ARM: SoC driver updates for v5.4

The branch contains driver changes that are tightly
 connected to SoC specific code. Aside from smaller
 cleanups and bug fixes, here is a list of the notable
 changes.
 
 New device drivers:
 
 - The Turris Mox router has a new "moxtet" bus driver
   for its on-board pluggable extension bus. The
   same platform also gains a firmware driver.
 
 - The Samsung Exynos family gains a new Chipid driver
   exporting using the soc device sysfs interface
 
 - A similar socinfo driver for Qualcomm Snapdragon
   chips.
 
 - A firmware driver for the NXP i.MX DSP IPC protocol
   using shared memory and a mailbox
 
 Other changes:
 
 - The i.MX reset controller driver now supports the
   NXP i.MX8MM chip
 
 - Amlogic SoC specific drivers gain support for
   the S905X3 and A311D chips
 
 - A rework of the TI Davinci framebuffer driver to
   allow important cleanups in the platform code
 
 - A couple of device drivers for removed ARM SoC
   platforms are removed. Most of the removals were
   picked up by other maintainers, this contains
   whatever was left.
 
 Signed-off-by: Arnd Bergmann <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJdf6SUAAoJEJpsee/mABjZAfwP/01bXBOlGVusNH2zuh8IUSHb
 //5sTdWpwa2ugRekLOJUOjo2p9Fu70yH6xr4RUHI0rcRjZA0xR3bZPx45gI8LRHQ
 tfb25LaKqfgZjWMCJ8due1Lh7B6ffOQukryMtM/LoiCtqsy7b6aThEKaLpM9/Owl
 t53o4wKaVQJK5He9JQom9NOZidkl7tYLHmDQTOXhX2UEA/i45vtfjdsEBvoFPbTx
 +bYvlqs+SWlpDJk29j+oBOeKadPF+TFboLDiUCxH44MC3OsH51zjtKVBRTtbNMkb
 ek/ci5x9hCeHcYSEigNq2EMzEln09Yxyvjk8U/jLiJ1h1kz3p5MjqJbVMF1rYXpe
 ALuAwinM8Zv2o5/UOCkiQTWq79PtpOKHZKpNBXkaJ8kyqBLMSy8Fs3hCvXrDnjnQ
 TC8jX7UBqHRV2rbQIYehAQAxTvcRgTbqusQGLkUJInlux6go57LoMYHPABpHftJV
 kRdVeT0KzdCz1pvQwyekIog5hPLNTBi4jw6eQcOgeENvAea1MJa8lMMfKcVbIdS0
 ZVvxLl+K6noEKAv5lSeHAzjXq+cQFr3zDCsWy351mJETDHmE8zjsaHN1SgbRYLEk
 ZqzNwUYaPYBis38g85qaY/TSsJrWJ+jP8u7s9HTw3Oywg8SRy5vtW177s00/9VOd
 PYZ2UpqUeX8cdvggqUUU
 =lxFi
 -----END PGP SIGNATURE-----

Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "This contains driver changes that are tightly connected to SoC
  specific code. Aside from smaller cleanups and bug fixes, here is a
  list of the notable changes.

  New device drivers:

   - The Turris Mox router has a new "moxtet" bus driver for its
     on-board pluggable extension bus. The same platform also gains a
     firmware driver.

   - The Samsung Exynos family gains a new Chipid driver exporting using
     the soc device sysfs interface

   - A similar socinfo driver for Qualcomm Snapdragon chips.

   - A firmware driver for the NXP i.MX DSP IPC protocol using shared
     memory and a mailbox

  Other changes:

   - The i.MX reset controller driver now supports the NXP i.MX8MM chip

   - Amlogic SoC specific drivers gain support for the S905X3 and A311D
     chips

   - A rework of the TI Davinci framebuffer driver to allow important
     cleanups in the platform code

   - A couple of device drivers for removed ARM SoC platforms are
     removed. Most of the removals were picked up by other maintainers,
     this contains whatever was left"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (123 commits)
  bus: uniphier-system-bus: use devm_platform_ioremap_resource()
  soc: ti: ti_sci_pm_domains: Add support for exclusive and shared access
  dt-bindings: ti_sci_pm_domains: Add support for exclusive and shared access
  firmware: ti_sci: Allow for device shared and exclusive requests
  bus: imx-weim: remove incorrect __init annotations
  fbdev: remove w90x900/nuc900 platform drivers
  spi: remove w90x900 driver
  net: remove w90p910-ether driver
  net: remove ks8695 driver
  firmware: turris-mox-rwtm: Add sysfs documentation
  firmware: Add Turris Mox rWTM firmware driver
  dt-bindings: firmware: Document cznic,turris-mox-rwtm binding
  bus: moxtet: fix unsigned comparison to less than zero
  bus: moxtet: remove set but not used variable 'dummy'
  ARM: scoop: Use the right include
  dt-bindings: power: add Amlogic Everything-Else power domains bindings
  soc: amlogic: Add support for Everything-Else power domains controller
  fbdev: da8xx: use resource management for dma
  fbdev: da8xx-fb: drop a redundant if
  fbdev: da8xx-fb: use devm_platform_ioremap_resource()
  ...
This commit is contained in:
Linus Torvalds 2019-09-16 15:52:38 -07:00
commit 399eb9b6cb
128 changed files with 5409 additions and 4954 deletions

View file

@ -37,6 +37,17 @@ config MESON_GX_PM_DOMAINS
Say yes to expose Amlogic Meson GX Power Domains as
Generic Power Domains.
config MESON_EE_PM_DOMAINS
bool "Amlogic Meson Everything-Else Power Domains driver"
depends on ARCH_MESON || COMPILE_TEST
depends on PM && OF
default ARCH_MESON
select PM_GENERIC_DOMAINS
select PM_GENERIC_DOMAINS_OF
help
Say yes to expose Amlogic Meson Everything-Else Power Domains as
Generic Power Domains.
config MESON_MX_SOCINFO
bool "Amlogic Meson MX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST

View file

@ -4,3 +4,4 @@ obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o

View file

@ -11,6 +11,8 @@
#include <linux/debugfs.h>
#include <linux/regmap.h>
static DEFINE_MUTEX(measure_lock);
#define MSR_CLK_DUTY 0x0
#define MSR_CLK_REG0 0x4
#define MSR_CLK_REG1 0x8
@ -322,6 +324,8 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
CLK_MSR_ID(84, "co_tx"),
CLK_MSR_ID(89, "hdmi_todig"),
CLK_MSR_ID(90, "hdmitx_sys"),
CLK_MSR_ID(91, "sys_cpub_div16"),
CLK_MSR_ID(92, "sys_pll_cpub_div16"),
CLK_MSR_ID(94, "eth_phy_rx"),
CLK_MSR_ID(95, "eth_phy_pll"),
CLK_MSR_ID(96, "vpu_b"),
@ -353,6 +357,136 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
CLK_MSR_ID(122, "audio_pdm_dclk"),
};
static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = {
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
CLK_MSR_ID(3, "ring_osc_out_ee_3"),
CLK_MSR_ID(4, "gp0_pll"),
CLK_MSR_ID(5, "gp1_pll"),
CLK_MSR_ID(6, "enci"),
CLK_MSR_ID(7, "clk81"),
CLK_MSR_ID(8, "encp"),
CLK_MSR_ID(9, "encl"),
CLK_MSR_ID(10, "vdac"),
CLK_MSR_ID(11, "eth_tx"),
CLK_MSR_ID(12, "hifi_pll"),
CLK_MSR_ID(13, "mod_tcon"),
CLK_MSR_ID(14, "fec_0"),
CLK_MSR_ID(15, "fec_1"),
CLK_MSR_ID(16, "fec_2"),
CLK_MSR_ID(17, "sys_pll_div16"),
CLK_MSR_ID(18, "sys_cpu_div16"),
CLK_MSR_ID(19, "lcd_an_ph2"),
CLK_MSR_ID(20, "rtc_osc_out"),
CLK_MSR_ID(21, "lcd_an_ph3"),
CLK_MSR_ID(22, "eth_phy_ref"),
CLK_MSR_ID(23, "mpll_50m"),
CLK_MSR_ID(24, "eth_125m"),
CLK_MSR_ID(25, "eth_rmii"),
CLK_MSR_ID(26, "sc_int"),
CLK_MSR_ID(27, "in_mac"),
CLK_MSR_ID(28, "sar_adc"),
CLK_MSR_ID(29, "pcie_inp"),
CLK_MSR_ID(30, "pcie_inn"),
CLK_MSR_ID(31, "mpll_test_out"),
CLK_MSR_ID(32, "vdec"),
CLK_MSR_ID(34, "eth_mpll_50m"),
CLK_MSR_ID(35, "mali"),
CLK_MSR_ID(36, "hdmi_tx_pixel"),
CLK_MSR_ID(37, "cdac"),
CLK_MSR_ID(38, "vdin_meas"),
CLK_MSR_ID(39, "bt656"),
CLK_MSR_ID(40, "arm_ring_osc_out_4"),
CLK_MSR_ID(41, "eth_rx_or_rmii"),
CLK_MSR_ID(42, "mp0_out"),
CLK_MSR_ID(43, "fclk_div5"),
CLK_MSR_ID(44, "pwm_b"),
CLK_MSR_ID(45, "pwm_a"),
CLK_MSR_ID(46, "vpu"),
CLK_MSR_ID(47, "ddr_dpll_pt"),
CLK_MSR_ID(48, "mp1_out"),
CLK_MSR_ID(49, "mp2_out"),
CLK_MSR_ID(50, "mp3_out"),
CLK_MSR_ID(51, "sd_emmc_c"),
CLK_MSR_ID(52, "sd_emmc_b"),
CLK_MSR_ID(53, "sd_emmc_a"),
CLK_MSR_ID(54, "vpu_clkc"),
CLK_MSR_ID(55, "vid_pll_div_out"),
CLK_MSR_ID(56, "wave420l_a"),
CLK_MSR_ID(57, "wave420l_c"),
CLK_MSR_ID(58, "wave420l_b"),
CLK_MSR_ID(59, "hcodec"),
CLK_MSR_ID(60, "arm_ring_osc_out_5"),
CLK_MSR_ID(61, "gpio_msr"),
CLK_MSR_ID(62, "hevcb"),
CLK_MSR_ID(63, "dsi_meas"),
CLK_MSR_ID(64, "spicc_1"),
CLK_MSR_ID(65, "spicc_0"),
CLK_MSR_ID(66, "vid_lock"),
CLK_MSR_ID(67, "dsi_phy"),
CLK_MSR_ID(68, "hdcp22_esm"),
CLK_MSR_ID(69, "hdcp22_skp"),
CLK_MSR_ID(70, "pwm_f"),
CLK_MSR_ID(71, "pwm_e"),
CLK_MSR_ID(72, "pwm_d"),
CLK_MSR_ID(73, "pwm_c"),
CLK_MSR_ID(74, "arm_ring_osc_out_6"),
CLK_MSR_ID(75, "hevcf"),
CLK_MSR_ID(76, "arm_ring_osc_out_7"),
CLK_MSR_ID(77, "rng_ring_osc_0"),
CLK_MSR_ID(78, "rng_ring_osc_1"),
CLK_MSR_ID(79, "rng_ring_osc_2"),
CLK_MSR_ID(80, "rng_ring_osc_3"),
CLK_MSR_ID(81, "vapb"),
CLK_MSR_ID(82, "ge2d"),
CLK_MSR_ID(83, "co_rx"),
CLK_MSR_ID(84, "co_tx"),
CLK_MSR_ID(85, "arm_ring_osc_out_8"),
CLK_MSR_ID(86, "arm_ring_osc_out_9"),
CLK_MSR_ID(87, "mipi_dsi_phy"),
CLK_MSR_ID(88, "cis2_adapt"),
CLK_MSR_ID(89, "hdmi_todig"),
CLK_MSR_ID(90, "hdmitx_sys"),
CLK_MSR_ID(91, "nna_core"),
CLK_MSR_ID(92, "nna_axi"),
CLK_MSR_ID(93, "vad"),
CLK_MSR_ID(94, "eth_phy_rx"),
CLK_MSR_ID(95, "eth_phy_pll"),
CLK_MSR_ID(96, "vpu_b"),
CLK_MSR_ID(97, "cpu_b_tmp"),
CLK_MSR_ID(98, "ts"),
CLK_MSR_ID(99, "arm_ring_osc_out_10"),
CLK_MSR_ID(100, "arm_ring_osc_out_11"),
CLK_MSR_ID(101, "arm_ring_osc_out_12"),
CLK_MSR_ID(102, "arm_ring_osc_out_13"),
CLK_MSR_ID(103, "arm_ring_osc_out_14"),
CLK_MSR_ID(104, "arm_ring_osc_out_15"),
CLK_MSR_ID(105, "arm_ring_osc_out_16"),
CLK_MSR_ID(106, "ephy_test"),
CLK_MSR_ID(107, "au_dac_g128x"),
CLK_MSR_ID(108, "audio_locker_out"),
CLK_MSR_ID(109, "audio_locker_in"),
CLK_MSR_ID(110, "audio_tdmout_c_sclk"),
CLK_MSR_ID(111, "audio_tdmout_b_sclk"),
CLK_MSR_ID(112, "audio_tdmout_a_sclk"),
CLK_MSR_ID(113, "audio_tdmin_lb_sclk"),
CLK_MSR_ID(114, "audio_tdmin_c_sclk"),
CLK_MSR_ID(115, "audio_tdmin_b_sclk"),
CLK_MSR_ID(116, "audio_tdmin_a_sclk"),
CLK_MSR_ID(117, "audio_resample"),
CLK_MSR_ID(118, "audio_pdm_sys"),
CLK_MSR_ID(119, "audio_spdifout_b"),
CLK_MSR_ID(120, "audio_spdifout"),
CLK_MSR_ID(121, "audio_spdifin"),
CLK_MSR_ID(122, "audio_pdm_dclk"),
CLK_MSR_ID(123, "audio_resampled"),
CLK_MSR_ID(124, "earcrx_pll"),
CLK_MSR_ID(125, "earcrx_pll_test"),
CLK_MSR_ID(126, "csi_phy0"),
CLK_MSR_ID(127, "csi2_data"),
};
static int meson_measure_id(struct meson_msr_id *clk_msr_id,
unsigned int duration)
{
@ -360,6 +494,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
unsigned int val;
int ret;
ret = mutex_lock_interruptible(&measure_lock);
if (ret)
return ret;
regmap_write(priv->regmap, MSR_CLK_REG0, 0);
/* Set measurement duration */
@ -377,8 +515,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0,
val, !(val & MSR_BUSY), 10, 10000);
if (ret)
if (ret) {
mutex_unlock(&measure_lock);
return ret;
}
/* Disable */
regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0);
@ -386,6 +526,8 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
/* Get the value in multiple of gate time counts */
regmap_read(priv->regmap, MSR_CLK_REG2, &val);
mutex_unlock(&measure_lock);
if (val >= MSR_VAL_MASK)
return -EINVAL;
@ -533,6 +675,10 @@ static const struct of_device_id meson_msr_match_table[] = {
.compatible = "amlogic,meson-g12a-clk-measure",
.data = (void *)clk_msr_g12a,
},
{
.compatible = "amlogic,meson-sm1-clk-measure",
.data = (void *)clk_msr_sm1,
},
{ /* sentinel */ }
};

View file

@ -0,0 +1,492 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2019 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*/
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
#include <linux/reset-controller.h>
#include <linux/reset.h>
#include <linux/clk.h>
#include <dt-bindings/power/meson-g12a-power.h>
#include <dt-bindings/power/meson-sm1-power.h>
/* AO Offsets */
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2)
/* HHI Offsets */
#define HHI_MEM_PD_REG0 (0x40 << 2)
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
#define HHI_VPU_MEM_PD_REG3 (0x43 << 2)
#define HHI_VPU_MEM_PD_REG4 (0x44 << 2)
#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2)
#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2)
#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2)
#define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
struct meson_ee_pwrc;
struct meson_ee_pwrc_domain;
struct meson_ee_pwrc_mem_domain {
unsigned int reg;
unsigned int mask;
};
struct meson_ee_pwrc_top_domain {
unsigned int sleep_reg;
unsigned int sleep_mask;
unsigned int iso_reg;
unsigned int iso_mask;
};
struct meson_ee_pwrc_domain_desc {
char *name;
unsigned int reset_names_count;
unsigned int clk_names_count;
struct meson_ee_pwrc_top_domain *top_pd;
unsigned int mem_pd_count;
struct meson_ee_pwrc_mem_domain *mem_pd;
bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain);
};
struct meson_ee_pwrc_domain_data {
unsigned int count;
struct meson_ee_pwrc_domain_desc *domains;
};
/* TOP Power Domains */
static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = {
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0,
.sleep_mask = BIT(8),
.iso_reg = AO_RTI_GEN_PWR_SLEEP0,
.iso_mask = BIT(9),
};
#define SM1_EE_PD(__bit) \
{ \
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \
.sleep_mask = BIT(__bit), \
.iso_reg = AO_RTI_GEN_PWR_ISO0, \
.iso_mask = BIT(__bit), \
}
static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8);
static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16);
static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17);
static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18);
static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19);
/* Memory PD Domains */
#define VPU_MEMPD(__reg) \
{ __reg, GENMASK(1, 0) }, \
{ __reg, GENMASK(3, 2) }, \
{ __reg, GENMASK(5, 4) }, \
{ __reg, GENMASK(7, 6) }, \
{ __reg, GENMASK(9, 8) }, \
{ __reg, GENMASK(11, 10) }, \
{ __reg, GENMASK(13, 12) }, \
{ __reg, GENMASK(15, 14) }, \
{ __reg, GENMASK(17, 16) }, \
{ __reg, GENMASK(19, 18) }, \
{ __reg, GENMASK(21, 20) }, \
{ __reg, GENMASK(23, 22) }, \
{ __reg, GENMASK(25, 24) }, \
{ __reg, GENMASK(27, 26) }, \
{ __reg, GENMASK(29, 28) }, \
{ __reg, GENMASK(31, 30) }
#define VPU_HHI_MEMPD(__reg) \
{ __reg, BIT(8) }, \
{ __reg, BIT(9) }, \
{ __reg, BIT(10) }, \
{ __reg, BIT(11) }, \
{ __reg, BIT(12) }, \
{ __reg, BIT(13) }, \
{ __reg, BIT(14) }, \
{ __reg, BIT(15) }
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = {
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
};
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = {
{ HHI_MEM_PD_REG0, GENMASK(3, 2) },
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = {
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
VPU_MEMPD(HHI_VPU_MEM_PD_REG3),
{ HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) },
{ HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) },
{ HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) },
{ HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) },
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = {
{ HHI_NANOQ_MEM_PD_REG0, 0xff },
{ HHI_NANOQ_MEM_PD_REG1, 0xff },
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = {
{ HHI_MEM_PD_REG0, GENMASK(31, 30) },
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = {
{ HHI_MEM_PD_REG0, GENMASK(29, 26) },
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = {
{ HHI_MEM_PD_REG0, GENMASK(25, 18) },
};
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = {
{ HHI_MEM_PD_REG0, GENMASK(5, 4) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) },
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) },
};
#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \
{ \
.name = __name, \
.reset_names_count = __resets, \
.clk_names_count = __clks, \
.top_pd = __top_pd, \
.mem_pd_count = ARRAY_SIZE(__mem), \
.mem_pd = __mem, \
.get_power = __get_power, \
}
#define TOP_PD(__name, __top_pd, __mem, __get_power) \
{ \
.name = __name, \
.top_pd = __top_pd, \
.mem_pd_count = ARRAY_SIZE(__mem), \
.mem_pd = __mem, \
.get_power = __get_power, \
}
#define MEM_PD(__name, __mem) \
TOP_PD(__name, NULL, __mem, NULL)
static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain);
static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = {
[PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu,
pwrc_ee_get_power, 11, 2),
[PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
};
static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = {
[PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu,
pwrc_ee_get_power, 11, 2),
[PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna,
pwrc_ee_get_power),
[PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb,
pwrc_ee_get_power),
[PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie,
pwrc_ee_get_power),
[PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d,
pwrc_ee_get_power),
[PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio),
[PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
};
struct meson_ee_pwrc_domain {
struct generic_pm_domain base;
bool enabled;
struct meson_ee_pwrc *pwrc;
struct meson_ee_pwrc_domain_desc desc;
struct clk_bulk_data *clks;
int num_clks;
struct reset_control *rstc;
int num_rstc;
};
struct meson_ee_pwrc {
struct regmap *regmap_ao;
struct regmap *regmap_hhi;
struct meson_ee_pwrc_domain *domains;
struct genpd_onecell_data xlate;
};
static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain)
{
u32 reg;
regmap_read(pwrc_domain->pwrc->regmap_ao,
pwrc_domain->desc.top_pd->sleep_reg, &reg);
return (reg & pwrc_domain->desc.top_pd->sleep_mask);
}
static int meson_ee_pwrc_off(struct generic_pm_domain *domain)
{
struct meson_ee_pwrc_domain *pwrc_domain =
container_of(domain, struct meson_ee_pwrc_domain, base);
int i;
if (pwrc_domain->desc.top_pd)
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
pwrc_domain->desc.top_pd->sleep_reg,
pwrc_domain->desc.top_pd->sleep_mask,
pwrc_domain->desc.top_pd->sleep_mask);
udelay(20);
for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
pwrc_domain->desc.mem_pd[i].reg,
pwrc_domain->desc.mem_pd[i].mask,
pwrc_domain->desc.mem_pd[i].mask);
udelay(20);
if (pwrc_domain->desc.top_pd)
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
pwrc_domain->desc.top_pd->iso_reg,
pwrc_domain->desc.top_pd->iso_mask,
pwrc_domain->desc.top_pd->iso_mask);
if (pwrc_domain->num_clks) {
msleep(20);
clk_bulk_disable_unprepare(pwrc_domain->num_clks,
pwrc_domain->clks);
}
return 0;
}
static int meson_ee_pwrc_on(struct generic_pm_domain *domain)
{
struct meson_ee_pwrc_domain *pwrc_domain =
container_of(domain, struct meson_ee_pwrc_domain, base);
int i, ret;
if (pwrc_domain->desc.top_pd)
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
pwrc_domain->desc.top_pd->sleep_reg,
pwrc_domain->desc.top_pd->sleep_mask, 0);
udelay(20);
for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
pwrc_domain->desc.mem_pd[i].reg,
pwrc_domain->desc.mem_pd[i].mask, 0);
udelay(20);
ret = reset_control_assert(pwrc_domain->rstc);
if (ret)
return ret;
if (pwrc_domain->desc.top_pd)
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
pwrc_domain->desc.top_pd->iso_reg,
pwrc_domain->desc.top_pd->iso_mask, 0);
ret = reset_control_deassert(pwrc_domain->rstc);
if (ret)
return ret;
return clk_bulk_prepare_enable(pwrc_domain->num_clks,
pwrc_domain->clks);
}
static int meson_ee_pwrc_init_domain(struct platform_device *pdev,
struct meson_ee_pwrc *pwrc,
struct meson_ee_pwrc_domain *dom)
{
dom->pwrc = pwrc;
dom->num_rstc = dom->desc.reset_names_count;
dom->num_clks = dom->desc.clk_names_count;
if (dom->num_rstc) {
int count = reset_control_get_count(&pdev->dev);
if (count != dom->num_rstc)
dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n",
count, dom->desc.name);
dom->rstc = devm_reset_control_array_get(&pdev->dev, false,
false);
if (IS_ERR(dom->rstc))
return PTR_ERR(dom->rstc);
}
if (dom->num_clks) {
int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks);
if (ret < 0)
return ret;
if (dom->num_clks != ret) {
dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n",
ret, dom->desc.name);
dom->num_clks = ret;
}
}
dom->base.name = dom->desc.name;
dom->base.power_on = meson_ee_pwrc_on;
dom->base.power_off = meson_ee_pwrc_off;
/*
* TOFIX: This is a special case for the VPU power domain, which can
* be enabled previously by the bootloader. In this case the VPU
* pipeline may be functional but no driver maybe never attach
* to this power domain, and if the domain is disabled it could
* cause system errors. This is why the pm_domain_always_on_gov
* is used here.
* For the same reason, the clocks should be enabled in case
* we need to power the domain off, otherwise the internal clocks
* prepare/enable counters won't be in sync.
*/
if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) {
int ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks);
if (ret)
return ret;
pm_genpd_init(&dom->base, &pm_domain_always_on_gov, false);
} else
pm_genpd_init(&dom->base, NULL,
(dom->desc.get_power ?
dom->desc.get_power(dom) : true));
return 0;
}
static int meson_ee_pwrc_probe(struct platform_device *pdev)
{
const struct meson_ee_pwrc_domain_data *match;
struct regmap *regmap_ao, *regmap_hhi;
struct meson_ee_pwrc *pwrc;
int i, ret;
match = of_device_get_match_data(&pdev->dev);
if (!match) {
dev_err(&pdev->dev, "failed to get match data\n");
return -ENODEV;
}
pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
if (!pwrc)
return -ENOMEM;
pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
sizeof(*pwrc->xlate.domains),
GFP_KERNEL);
if (!pwrc->xlate.domains)
return -ENOMEM;
pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
sizeof(*pwrc->domains), GFP_KERNEL);
if (!pwrc->domains)
return -ENOMEM;
pwrc->xlate.num_domains = match->count;
regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
if (IS_ERR(regmap_hhi)) {
dev_err(&pdev->dev, "failed to get HHI regmap\n");
return PTR_ERR(regmap_hhi);
}
regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"amlogic,ao-sysctrl");
if (IS_ERR(regmap_ao)) {
dev_err(&pdev->dev, "failed to get AO regmap\n");
return PTR_ERR(regmap_ao);
}
pwrc->regmap_ao = regmap_ao;
pwrc->regmap_hhi = regmap_hhi;
platform_set_drvdata(pdev, pwrc);
for (i = 0 ; i < match->count ; ++i) {
struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc));
ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom);
if (ret)
return ret;
pwrc->xlate.domains[i] = &dom->base;
}
of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
return 0;
}
static void meson_ee_pwrc_shutdown(struct platform_device *pdev)
{
struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev);
int i;
for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) {
struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
if (dom->desc.get_power && !dom->desc.get_power(dom))
meson_ee_pwrc_off(&dom->base);
}
}
static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = {
.count = ARRAY_SIZE(g12a_pwrc_domains),
.domains = g12a_pwrc_domains,
};
static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = {
.count = ARRAY_SIZE(sm1_pwrc_domains),
.domains = sm1_pwrc_domains,
};
static const struct of_device_id meson_ee_pwrc_match_table[] = {
{
.compatible = "amlogic,meson-g12a-pwrc",
.data = &meson_ee_g12a_pwrc_data,
},
{
.compatible = "amlogic,meson-sm1-pwrc",
.data = &meson_ee_sm1_pwrc_data,
},
{ /* sentinel */ }
};
static struct platform_driver meson_ee_pwrc_driver = {
.probe = meson_ee_pwrc_probe,
.shutdown = meson_ee_pwrc_shutdown,
.driver = {
.name = "meson_ee_pwrc",
.of_match_table = meson_ee_pwrc_match_table,
},
};
builtin_platform_driver(meson_ee_pwrc_driver);

View file

@ -39,6 +39,7 @@ static const struct meson_gx_soc_id {
{ "TXHD", 0x27 },
{ "G12A", 0x28 },
{ "G12B", 0x29 },
{ "SM1", 0x2b },
};
static const struct meson_gx_package_id {
@ -65,6 +66,8 @@ static const struct meson_gx_package_id {
{ "S905D2", 0x28, 0x10, 0xf0 },
{ "S905X2", 0x28, 0x40, 0xf0 },
{ "S922X", 0x29, 0x40, 0xf0 },
{ "A311D", 0x29, 0x10, 0xf0 },
{ "S905X3", 0x2b, 0x5, 0xf },
};
static inline unsigned int socinfo_to_major(u32 socinfo)
@ -138,8 +141,10 @@ static int __init meson_gx_socinfo_init(void)
}
/* check if chip-id is available */
if (!of_property_read_bool(np, "amlogic,has-chip-id"))
if (!of_property_read_bool(np, "amlogic,has-chip-id")) {
of_node_put(np);
return -ENODEV;
}
/* node should be a syscon */
regmap = syscon_node_to_regmap(np);

View file

@ -73,7 +73,7 @@ static u64 get_mc_fw_base_address(void)
mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr));
if (!mcfbaregs) {
pr_err("could not map MC Firmaware Base registers\n");
pr_err("could not map MC Firmware Base registers\n");
return 0;
}

View file

@ -305,8 +305,6 @@ void dpaa2_io_service_deregister(struct dpaa2_io *service,
list_del(&ctx->node);
spin_unlock_irqrestore(&d->lock_notifications, irqflags);
if (dev)
device_link_remove(dev, d->dev);
}
EXPORT_SYMBOL_GPL(dpaa2_io_service_deregister);

View file

@ -102,6 +102,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = {
.svr = 0x87360000,
.mask = 0xff3f0000,
},
/* Die: LS1028A, SoC: LS1028A */
{ .die = "LS1028A",
.svr = 0x870b0000,
.mask = 0xff3f0000,
},
{ },
};
@ -224,6 +229,7 @@ static const struct of_device_id fsl_guts_of_match[] = {
{ .compatible = "fsl,ls1012a-dcfg", },
{ .compatible = "fsl,ls1046a-dcfg", },
{ .compatible = "fsl,lx2160a-dcfg", },
{ .compatible = "fsl,ls1028a-dcfg", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_guts_of_match);

View file

@ -635,30 +635,31 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
return 0;
}
static int bm_shutdown_pool(u32 bpid)
int bm_shutdown_pool(u32 bpid)
{
int err = 0;
struct bm_mc_command *bm_cmd;
union bm_mc_result *bm_res;
struct bman_portal *p = get_affine_portal();
while (1) {
struct bman_portal *p = get_affine_portal();
/* Acquire buffers until empty */
bm_cmd = bm_mc_start(&p->p);
bm_cmd->bpid = bpid;
bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1);
if (!bm_mc_result_timeout(&p->p, &bm_res)) {
put_affine_portal();
pr_crit("BMan Acquire Command timedout\n");
return -ETIMEDOUT;
err = -ETIMEDOUT;
goto done;
}
if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
put_affine_portal();
/* Pool is empty */
return 0;
goto done;
}
put_affine_portal();
}
done:
put_affine_portal();
return 0;
}

View file

@ -97,17 +97,40 @@ static void bm_get_version(u16 *id, u8 *major, u8 *minor)
/* signal transactions for FBPRs with higher priority */
#define FBPR_AR_RPRIO_HI BIT(30)
static void bm_set_memory(u64 ba, u32 size)
/* Track if probe has occurred and if cleanup is required */
static int __bman_probed;
static int __bman_requires_cleanup;
static int bm_set_memory(u64 ba, u32 size)
{
u32 bar, bare;
u32 exp = ilog2(size);
/* choke if size isn't within range */
DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 &&
is_power_of_2(size));
/* choke if '[e]ba' has lower-alignment than 'size' */
DPAA_ASSERT(!(ba & (size - 1)));
/* Check to see if BMan has already been initialized */
bar = bm_ccsr_in(REG_FBPR_BAR);
if (bar) {
/* Maker sure ba == what was programmed) */
bare = bm_ccsr_in(REG_FBPR_BARE);
if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
ba, bare, bar);
return -ENOMEM;
}
pr_info("BMan BAR already configured\n");
__bman_requires_cleanup = 1;
return 1;
}
bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba));
bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba));
bm_ccsr_out(REG_FBPR_AR, exp - 1);
return 0;
}
/*
@ -120,7 +143,6 @@ static void bm_set_memory(u64 ba, u32 size)
*/
static dma_addr_t fbpr_a;
static size_t fbpr_sz;
static int __bman_probed;
static int bman_fbpr(struct reserved_mem *rmem)
{
@ -173,6 +195,16 @@ int bman_is_probed(void)
}
EXPORT_SYMBOL_GPL(bman_is_probed);
int bman_requires_cleanup(void)
{
return __bman_requires_cleanup;
}
void bman_done_cleanup(void)
{
__bman_requires_cleanup = 0;
}
static int fsl_bman_probe(struct platform_device *pdev)
{
int ret, err_irq;

View file

@ -100,7 +100,7 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg;
struct resource *addr_phys[2];
int irq, cpu, err;
int irq, cpu, err, i;
err = bman_is_probed();
if (!err)
@ -135,10 +135,8 @@ static int bman_portal_probe(struct platform_device *pdev)
pcfg->cpu = -1;
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "Can't get %pOF IRQ'\n", node);
if (irq <= 0)
goto err_ioremap1;
}
pcfg->irq = irq;
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
@ -178,6 +176,22 @@ static int bman_portal_probe(struct platform_device *pdev)
if (!cpu_online(cpu))
bman_offline_cpu(cpu);
if (__bman_portals_probed == 1 && bman_requires_cleanup()) {
/*
* BMan wasn't reset prior to boot (Kexec for example)
* Empty all the buffer pools so they are in reset state
*/
for (i = 0; i < BM_POOL_MAX; i++) {
err = bm_shutdown_pool(i);
if (err) {
dev_err(dev, "Failed to shutdown bpool %d\n",
i);
goto err_portal_init;
}
}
bman_done_cleanup();
}
return 0;
err_portal_init:

View file

@ -76,3 +76,8 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits);
const struct bm_portal_config *
bman_get_bm_portal_config(const struct bman_portal *portal);
int bman_requires_cleanup(void);
void bman_done_cleanup(void);
int bm_shutdown_pool(u32 bpid);

View file

@ -37,42 +37,53 @@
int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
size_t *size)
{
int ret;
struct device_node *mem_node;
u64 size64;
struct reserved_mem *rmem;
struct property *prop;
int len, err;
__be32 *res_array;
ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx);
if (ret) {
dev_err(dev,
"of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n",
idx, ret);
return -ENODEV;
}
mem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (mem_node) {
ret = of_property_read_u64(mem_node, "size", &size64);
if (ret) {
dev_err(dev, "of_address_to_resource fails 0x%x\n",
ret);
return -ENODEV;
}
*size = size64;
} else {
mem_node = of_parse_phandle(dev->of_node, "memory-region", idx);
if (!mem_node) {
dev_err(dev, "No memory-region found for index %d\n", idx);
return -ENODEV;
}
if (!dma_alloc_coherent(dev, *size, addr, 0)) {
dev_err(dev, "DMA Alloc memory failed\n");
rmem = of_reserved_mem_lookup(mem_node);
if (!rmem) {
dev_err(dev, "of_reserved_mem_lookup() returned NULL\n");
return -ENODEV;
}
*addr = rmem->base;
*size = rmem->size;
/*
* Disassociate the reserved memory area from the device
* because a device can only have one DMA memory area. This
* should be fine since the memory is allocated and initialized
* and only ever accessed by the QBMan device from now on
* Check if the reg property exists - if not insert the node
* so upon kexec() the same memory region address will be preserved.
* This is needed because QBMan HW does not allow the base address/
* size to be modified once set.
*/
of_reserved_mem_device_release(dev);
prop = of_find_property(mem_node, "reg", &len);
if (!prop) {
prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL);
if (!prop)
return -ENOMEM;
prop->value = res_array = devm_kzalloc(dev, sizeof(__be32) * 4,
GFP_KERNEL);
if (!prop->value)
return -ENOMEM;
res_array[0] = cpu_to_be32(upper_32_bits(*addr));
res_array[1] = cpu_to_be32(lower_32_bits(*addr));
res_array[2] = cpu_to_be32(upper_32_bits(*size));
res_array[3] = cpu_to_be32(lower_32_bits(*size));
prop->length = sizeof(__be32) * 4;
prop->name = devm_kstrdup(dev, "reg", GFP_KERNEL);
if (!prop->name)
return -ENOMEM;
err = of_add_property(mem_node, prop);
if (err)
return err;
}
return 0;
}

View file

@ -1018,6 +1018,20 @@ static inline void put_affine_portal(void)
put_cpu_var(qman_affine_portal);
}
static inline struct qman_portal *get_portal_for_channel(u16 channel)
{
int i;
for (i = 0; i < num_possible_cpus(); i++) {
if (affine_portals[i] &&
affine_portals[i]->config->channel == channel)
return affine_portals[i];
}
return NULL;
}
static struct workqueue_struct *qm_portal_wq;
int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
@ -1070,6 +1084,20 @@ int qman_wq_alloc(void)
return 0;
}
void qman_enable_irqs(void)
{
int i;
for (i = 0; i < num_possible_cpus(); i++) {
if (affine_portals[i]) {
qm_out(&affine_portals[i]->p, QM_REG_ISR, 0xffffffff);
qm_out(&affine_portals[i]->p, QM_REG_IIR, 0);
}
}
}
/*
* This is what everything can wait on, even if it migrates to a different cpu
* to the one whose affine portal it is waiting on.
@ -1164,6 +1192,7 @@ static int drain_mr_fqrni(struct qm_portal *p)
{
const union qm_mr_entry *msg;
loop:
qm_mr_pvb_update(p);
msg = qm_mr_current(p);
if (!msg) {
/*
@ -1180,7 +1209,8 @@ loop:
* entries well before the ring has been fully consumed, so
* we're being *really* paranoid here.
*/
msleep(1);
mdelay(1);
qm_mr_pvb_update(p);
msg = qm_mr_current(p);
if (!msg)
return 0;
@ -1267,8 +1297,8 @@ static int qman_create_portal(struct qman_portal *portal,
qm_out(p, QM_REG_ISDR, isdr);
portal->irq_sources = 0;
qm_out(p, QM_REG_IER, 0);
qm_out(p, QM_REG_ISR, 0xffffffff);
snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
qm_out(p, QM_REG_IIR, 1);
if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
dev_err(c->dev, "request_irq() failed\n");
goto fail_irq;
@ -1288,7 +1318,7 @@ static int qman_create_portal(struct qman_portal *portal,
isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI);
qm_out(p, QM_REG_ISDR, isdr);
if (qm_dqrr_current(p)) {
dev_err(c->dev, "DQRR unclean\n");
dev_dbg(c->dev, "DQRR unclean\n");
qm_dqrr_cdc_consume_n(p, 0xffff);
}
if (qm_mr_current(p) && drain_mr_fqrni(p)) {
@ -1301,8 +1331,10 @@ static int qman_create_portal(struct qman_portal *portal,
}
/* Success */
portal->config = c;
qm_out(p, QM_REG_ISR, 0xffffffff);
qm_out(p, QM_REG_ISDR, 0);
qm_out(p, QM_REG_IIR, 0);
if (!qman_requires_cleanup())
qm_out(p, QM_REG_IIR, 0);
/* Write a sane SDQCR */
qm_dqrr_sdqcr_set(p, portal->sdqcr);
return 0;
@ -2581,9 +2613,9 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
#define qm_dqrr_drain_nomatch(p) \
_qm_dqrr_consume_and_match(p, 0, 0, false)
static int qman_shutdown_fq(u32 fqid)
int qman_shutdown_fq(u32 fqid)
{
struct qman_portal *p;
struct qman_portal *p, *channel_portal;
struct device *dev;
union qm_mc_command *mcc;
union qm_mc_result *mcr;
@ -2623,17 +2655,28 @@ static int qman_shutdown_fq(u32 fqid)
channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
wq = qm_fqd_get_wq(&mcr->queryfq.fqd);
if (channel < qm_channel_pool1) {
channel_portal = get_portal_for_channel(channel);
if (channel_portal == NULL) {
dev_err(dev, "Can't find portal for dedicated channel 0x%x\n",
channel);
ret = -EIO;
goto out;
}
} else
channel_portal = p;
switch (state) {
case QM_MCR_NP_STATE_TEN_SCHED:
case QM_MCR_NP_STATE_TRU_SCHED:
case QM_MCR_NP_STATE_ACTIVE:
case QM_MCR_NP_STATE_PARKED:
orl_empty = 0;
mcc = qm_mc_start(&p->p);
mcc = qm_mc_start(&channel_portal->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
if (!qm_mc_result_timeout(&p->p, &mcr)) {
dev_err(dev, "QUERYFQ_NP timeout\n");
qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE);
if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) {
dev_err(dev, "ALTER_RETIRE timeout\n");
ret = -ETIMEDOUT;
goto out;
}
@ -2641,6 +2684,9 @@ static int qman_shutdown_fq(u32 fqid)
QM_MCR_VERB_ALTER_RETIRE);
res = mcr->result; /* Make a copy as we reuse MCR below */
if (res == QM_MCR_RESULT_OK)
drain_mr_fqrni(&channel_portal->p);
if (res == QM_MCR_RESULT_PENDING) {
/*
* Need to wait for the FQRN in the message ring, which
@ -2670,21 +2716,25 @@ static int qman_shutdown_fq(u32 fqid)
}
/* Set the sdqcr to drain this channel */
if (channel < qm_channel_pool1)
qm_dqrr_sdqcr_set(&p->p,
qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_DEDICATED);
else
qm_dqrr_sdqcr_set(&p->p,
qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_POOL_CONV
(channel));
do {
/* Keep draining DQRR while checking the MR*/
qm_dqrr_drain_nomatch(&p->p);
qm_dqrr_drain_nomatch(&channel_portal->p);
/* Process message ring too */
found_fqrn = qm_mr_drain(&p->p, FQRN);
found_fqrn = qm_mr_drain(&channel_portal->p,
FQRN);
cpu_relax();
} while (!found_fqrn);
/* Restore SDQCR */
qm_dqrr_sdqcr_set(&channel_portal->p,
channel_portal->sdqcr);
}
if (res != QM_MCR_RESULT_OK &&
@ -2715,9 +2765,8 @@ static int qman_shutdown_fq(u32 fqid)
* Wait for a dequeue and process the dequeues,
* making sure to empty the ring completely
*/
} while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
} while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
}
qm_dqrr_sdqcr_set(&p->p, 0);
while (!orl_empty) {
/* Wait for the ORL to have been completely drained */
@ -2754,7 +2803,7 @@ static int qman_shutdown_fq(u32 fqid)
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
QM_MCR_VERB_ALTER_OOS);
if (mcr->result) {
if (mcr->result != QM_MCR_RESULT_OK) {
dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
fqid, mcr->result);
ret = -EIO;

View file

@ -274,6 +274,7 @@ static u32 __iomem *qm_ccsr_start;
/* A SDQCR mask comprising all the available/visible pool channels */
static u32 qm_pools_sdqcr;
static int __qman_probed;
static int __qman_requires_cleanup;
static inline u32 qm_ccsr_in(u32 offset)
{
@ -340,19 +341,55 @@ static void qm_get_version(u16 *id, u8 *major, u8 *minor)
}
#define PFDR_AR_EN BIT(31)
static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
static int qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
{
void *ptr;
u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
u32 exp = ilog2(size);
u32 bar, bare;
/* choke if size isn't within range */
DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
is_power_of_2(size));
/* choke if 'ba' has lower-alignment than 'size' */
DPAA_ASSERT(!(ba & (size - 1)));
/* Check to see if QMan has already been initialized */
bar = qm_ccsr_in(offset + REG_offset_BAR);
if (bar) {
/* Maker sure ba == what was programmed) */
bare = qm_ccsr_in(offset);
if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
pr_err("Attempted to reinitialize QMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
ba, bare, bar);
return -ENOMEM;
}
__qman_requires_cleanup = 1;
/* Return 1 to indicate memory was previously programmed */
return 1;
}
/* Need to temporarily map the area to make sure it is zeroed */
ptr = memremap(ba, size, MEMREMAP_WB);
if (!ptr) {
pr_crit("memremap() of QMan private memory failed\n");
return -ENOMEM;
}
memset(ptr, 0, size);
#ifdef CONFIG_PPC
/*
* PPC doesn't appear to flush the cache on memunmap() but the
* cache must be flushed since QMan does non coherent accesses
* to this memory
*/
flush_dcache_range((unsigned long) ptr, (unsigned long) ptr+size);
#endif
memunmap(ptr);
qm_ccsr_out(offset, upper_32_bits(ba));
qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
return 0;
}
static void qm_set_pfdr_threshold(u32 th, u8 k)
@ -455,7 +492,7 @@ RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
#endif
static unsigned int qm_get_fqid_maxcnt(void)
unsigned int qm_get_fqid_maxcnt(void)
{
return fqd_sz / 64;
}
@ -571,12 +608,19 @@ static int qman_init_ccsr(struct device *dev)
int i, err;
/* FQD memory */
qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
/* PFDR memory */
qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
if (err)
err = qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
if (err < 0)
return err;
/* PFDR memory */
err = qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
if (err < 0)
return err;
/* Only initialize PFDRs if the QMan was not initialized before */
if (err == 0) {
err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
if (err)
return err;
}
/* thresholds */
qm_set_pfdr_threshold(512, 64);
qm_set_sfdr_threshold(128);
@ -693,6 +737,18 @@ int qman_is_probed(void)
}
EXPORT_SYMBOL_GPL(qman_is_probed);
int qman_requires_cleanup(void)
{
return __qman_requires_cleanup;
}
void qman_done_cleanup(void)
{
qman_enable_irqs();
__qman_requires_cleanup = 0;
}
static int fsl_qman_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;

View file

@ -233,7 +233,7 @@ static int qman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct qm_portal_config *pcfg;
struct resource *addr_phys[2];
int irq, cpu, err;
int irq, cpu, err, i;
u32 val;
err = qman_is_probed();
@ -275,10 +275,8 @@ static int qman_portal_probe(struct platform_device *pdev)
pcfg->channel = val;
pcfg->cpu = -1;
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "Can't get %pOF IRQ\n", node);
if (irq <= 0)
goto err_ioremap1;
}
pcfg->irq = irq;
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
@ -325,6 +323,22 @@ static int qman_portal_probe(struct platform_device *pdev)
if (!cpu_online(cpu))
qman_offline_cpu(cpu);
if (__qman_portals_probed == 1 && qman_requires_cleanup()) {
/*
* QMan wasn't reset prior to boot (Kexec for example)
* Empty all the frame queues so they are in reset state
*/
for (i = 0; i < qm_get_fqid_maxcnt(); i++) {
err = qman_shutdown_fq(i);
if (err) {
dev_err(dev, "Failed to shutdown frame queue %d\n",
i);
goto err_portal_init;
}
}
qman_done_cleanup();
}
return 0;
err_portal_init:

View file

@ -272,3 +272,11 @@ extern struct qman_portal *affine_portals[NR_CPUS];
extern struct qman_portal *qman_dma_portal;
const struct qm_portal_config *qman_get_qm_portal_config(
struct qman_portal *portal);
unsigned int qm_get_fqid_maxcnt(void);
int qman_shutdown_fq(u32 fqid);
int qman_requires_cleanup(void);
void qman_done_cleanup(void);
void qman_enable_irqs(void);

View file

@ -10,6 +10,7 @@
* General Purpose functions for the global management of the
* QUICC Engine (QE).
*/
#include <linux/bitmap.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@ -39,29 +40,32 @@ static DEFINE_SPINLOCK(qe_lock);
DEFINE_SPINLOCK(cmxgcr_lock);
EXPORT_SYMBOL(cmxgcr_lock);
/* QE snum state */
enum qe_snum_state {
QE_SNUM_STATE_USED,
QE_SNUM_STATE_FREE
};
/* QE snum */
struct qe_snum {
u8 num;
enum qe_snum_state state;
};
/* We allocate this here because it is used almost exclusively for
* the communication processor devices.
*/
struct qe_immap __iomem *qe_immr;
EXPORT_SYMBOL(qe_immr);
static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
static u8 snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
static DECLARE_BITMAP(snum_state, QE_NUM_OF_SNUM);
static unsigned int qe_num_of_snum;
static phys_addr_t qebase = -1;
static struct device_node *qe_get_device_node(void)
{
struct device_node *qe;
/*
* Newer device trees have an "fsl,qe" compatible property for the QE
* node, but we still need to support older device trees.
*/
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (qe)
return qe;
return of_find_node_by_type(NULL, "qe");
}
static phys_addr_t get_qe_base(void)
{
struct device_node *qe;
@ -71,12 +75,9 @@ static phys_addr_t get_qe_base(void)
if (qebase != -1)
return qebase;
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!qe) {
qe = of_find_node_by_type(NULL, "qe");
if (!qe)
return qebase;
}
qe = qe_get_device_node();
if (!qe)
return qebase;
ret = of_address_to_resource(qe, 0, &res);
if (!ret)
@ -170,12 +171,9 @@ unsigned int qe_get_brg_clk(void)
if (brg_clk)
return brg_clk;
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!qe) {
qe = of_find_node_by_type(NULL, "qe");
if (!qe)
return brg_clk;
}
qe = qe_get_device_node();
if (!qe)
return brg_clk;
prop = of_get_property(qe, "brg-frequency", &size);
if (prop && size == sizeof(*prop))
@ -281,7 +279,6 @@ EXPORT_SYMBOL(qe_clock_source);
*/
static void qe_snums_init(void)
{
int i;
static const u8 snum_init_76[] = {
0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
@ -302,19 +299,39 @@ static void qe_snums_init(void)
0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59,
0x68, 0x69, 0x78, 0x79, 0x80, 0x81,
};
static const u8 *snum_init;
struct device_node *qe;
const u8 *snum_init;
int i;
qe_num_of_snum = qe_get_num_of_snums();
if (qe_num_of_snum == 76)
snum_init = snum_init_76;
else
snum_init = snum_init_46;
for (i = 0; i < qe_num_of_snum; i++) {
snums[i].num = snum_init[i];
snums[i].state = QE_SNUM_STATE_FREE;
bitmap_zero(snum_state, QE_NUM_OF_SNUM);
qe_num_of_snum = 28; /* The default number of snum for threads is 28 */
qe = qe_get_device_node();
if (qe) {
i = of_property_read_variable_u8_array(qe, "fsl,qe-snums",
snums, 1, QE_NUM_OF_SNUM);
if (i > 0) {
of_node_put(qe);
qe_num_of_snum = i;
return;
}
/*
* Fall back to legacy binding of using the value of
* fsl,qe-num-snums to choose one of the static arrays
* above.
*/
of_property_read_u32(qe, "fsl,qe-num-snums", &qe_num_of_snum);
of_node_put(qe);
}
if (qe_num_of_snum == 76) {
snum_init = snum_init_76;
} else if (qe_num_of_snum == 28 || qe_num_of_snum == 46) {
snum_init = snum_init_46;
} else {
pr_err("QE: unsupported value of fsl,qe-num-snums: %u\n", qe_num_of_snum);
return;
}
memcpy(snums, snum_init, qe_num_of_snum);
}
int qe_get_snum(void)
@ -324,12 +341,10 @@ int qe_get_snum(void)
int i;
spin_lock_irqsave(&qe_lock, flags);
for (i = 0; i < qe_num_of_snum; i++) {
if (snums[i].state == QE_SNUM_STATE_FREE) {
snums[i].state = QE_SNUM_STATE_USED;
snum = snums[i].num;
break;
}
i = find_first_zero_bit(snum_state, qe_num_of_snum);
if (i < qe_num_of_snum) {
set_bit(i, snum_state);
snum = snums[i];
}
spin_unlock_irqrestore(&qe_lock, flags);
@ -339,14 +354,10 @@ EXPORT_SYMBOL(qe_get_snum);
void qe_put_snum(u8 snum)
{
int i;
const u8 *p = memchr(snums, snum, qe_num_of_snum);
for (i = 0; i < qe_num_of_snum; i++) {
if (snums[i].num == snum) {
snums[i].state = QE_SNUM_STATE_FREE;
break;
}
}
if (p)
clear_bit(p - snums, snum_state);
}
EXPORT_SYMBOL(qe_put_snum);
@ -572,16 +583,9 @@ struct qe_firmware_info *qe_get_firmware_info(void)
initialized = 1;
/*
* Newer device trees have an "fsl,qe" compatible property for the QE
* node, but we still need to support older device trees.
*/
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!qe) {
qe = of_find_node_by_type(NULL, "qe");
if (!qe)
return NULL;
}
qe = qe_get_device_node();
if (!qe)
return NULL;
/* Find the 'firmware' child node */
fw = of_get_child_by_name(qe, "firmware");
@ -627,16 +631,9 @@ unsigned int qe_get_num_of_risc(void)
unsigned int num_of_risc = 0;
const u32 *prop;
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!qe) {
/* Older devices trees did not have an "fsl,qe"
* compatible property, so we need to look for
* the QE node by name.
*/
qe = of_find_node_by_type(NULL, "qe");
if (!qe)
return num_of_risc;
}
qe = qe_get_device_node();
if (!qe)
return num_of_risc;
prop = of_get_property(qe, "fsl,qe-num-riscs", &size);
if (prop && size == sizeof(*prop))
@ -650,37 +647,7 @@ EXPORT_SYMBOL(qe_get_num_of_risc);
unsigned int qe_get_num_of_snums(void)
{
struct device_node *qe;
int size;
unsigned int num_of_snums;
const u32 *prop;
num_of_snums = 28; /* The default number of snum for threads is 28 */
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
if (!qe) {
/* Older devices trees did not have an "fsl,qe"
* compatible property, so we need to look for
* the QE node by name.
*/
qe = of_find_node_by_type(NULL, "qe");
if (!qe)
return num_of_snums;
}
prop = of_get_property(qe, "fsl,qe-num-snums", &size);
if (prop && size == sizeof(*prop)) {
num_of_snums = *prop;
if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) {
/* No QE ever has fewer than 28 SNUMs */
pr_err("QE: number of snum is invalid\n");
of_node_put(qe);
return -EINVAL;
}
}
of_node_put(qe);
return num_of_snums;
return qe_num_of_snum;
}
EXPORT_SYMBOL(qe_get_num_of_snums);

View file

@ -198,7 +198,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
err = regulator_disable(domain->regulator);
if (err)
dev_err(domain->dev,
"failed to disable regulator: %d\n", ret);
"failed to disable regulator: %d\n", err);
/* Preserve earlier error code */
ret = ret ?: err;
}

View file

@ -27,6 +27,40 @@ struct imx_sc_msg_misc_get_soc_id {
} data;
} __packed;
struct imx_sc_msg_misc_get_soc_uid {
struct imx_sc_rpc_msg hdr;
u32 uid_low;
u32 uid_high;
} __packed;
static ssize_t soc_uid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct imx_sc_msg_misc_get_soc_uid msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
u64 soc_uid;
int ret;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = IMX_SC_RPC_SVC_MISC;
hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
hdr->size = 1;
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false);
if (ret) {
pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
return ret;
}
soc_uid = msg.uid_high;
soc_uid <<= 32;
soc_uid |= msg.uid_low;
return sprintf(buf, "%016llX\n", soc_uid);
}
static DEVICE_ATTR_RO(soc_uid);
static int imx_scu_soc_id(void)
{
struct imx_sc_msg_misc_get_soc_id msg;
@ -102,6 +136,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
goto free_revision;
}
ret = device_create_file(soc_device_to_device(soc_dev),
&dev_attr_soc_uid);
if (ret)
goto free_revision;
return 0;
free_revision:

View file

@ -16,6 +16,9 @@
#define IMX8MQ_SW_INFO_B1 0x40
#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
#define OCOTP_UID_LOW 0x410
#define OCOTP_UID_HIGH 0x420
/* Same as ANADIG_DIGPROG_IMX7D */
#define ANADIG_DIGPROG_IMX8MM 0x800
@ -24,6 +27,16 @@ struct imx8_soc_data {
u32 (*soc_revision)(void);
};
static u64 soc_uid;
static ssize_t soc_uid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%016llX\n", soc_uid);
}
static DEVICE_ATTR_RO(soc_uid);
static u32 __init imx8mq_soc_revision(void)
{
struct device_node *np;
@ -42,6 +55,10 @@ static u32 __init imx8mq_soc_revision(void)
if (magic == IMX8MQ_SW_MAGIC_B1)
rev = REV_B1;
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
soc_uid <<= 32;
soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
iounmap(ocotp_base);
out:
@ -49,6 +66,26 @@ out:
return rev;
}
static void __init imx8mm_soc_uid(void)
{
void __iomem *ocotp_base;
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
if (!np)
return;
ocotp_base = of_iomap(np, 0);
WARN_ON(!ocotp_base);
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
soc_uid <<= 32;
soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
iounmap(ocotp_base);
of_node_put(np);
}
static u32 __init imx8mm_soc_revision(void)
{
struct device_node *np;
@ -66,6 +103,9 @@ static u32 __init imx8mm_soc_revision(void)
iounmap(anatop_base);
of_node_put(np);
imx8mm_soc_uid();
return rev;
}
@ -140,6 +180,11 @@ static int __init imx8_soc_init(void)
goto free_rev;
}
ret = device_create_file(soc_device_to_device(soc_dev),
&dev_attr_soc_uid);
if (ret)
goto free_rev;
if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);

View file

@ -136,7 +136,7 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
return 0;
}
int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset)
int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
{
u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) |
(subsys << CMDQ_SUBSYS_SHIFT);
@ -145,8 +145,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset)
}
EXPORT_SYMBOL(cmdq_pkt_write);
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
u32 subsys, u32 offset, u32 mask)
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
u16 offset, u32 value, u32 mask)
{
u32 offset_mask = offset;
int err = 0;
@ -161,7 +161,7 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
}
EXPORT_SYMBOL(cmdq_pkt_write_mask);
int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event)
int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event)
{
u32 arg_b;
@ -181,7 +181,7 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event)
}
EXPORT_SYMBOL(cmdq_pkt_wfe);
int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event)
int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
{
if (event >= CMDQ_MAX_EVENT)
return -EINVAL;

View file

@ -175,6 +175,14 @@ config QCOM_SMSM
Say yes here to support the Qualcomm Shared Memory State Machine.
The state machine is represented by bits in shared memory.
config QCOM_SOCINFO
tristate "Qualcomm socinfo driver"
depends on QCOM_SMEM
select SOC_BUS
help
Say yes here to support the Qualcomm socinfo driver, providing
information about the SoC to user space.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on ARCH_QCOM || COMPILE_TEST

View file

@ -18,6 +18,7 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o

View file

@ -10,6 +10,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/thermal.h>
#include <linux/slab.h>
#define QMP_DESC_MAGIC 0x0
#define QMP_DESC_VERSION 0x4
@ -40,6 +42,17 @@
/* 64 bytes is enough to store the requests and provides padding to 4 bytes */
#define QMP_MSG_LEN 64
#define QMP_NUM_COOLING_RESOURCES 2
static bool qmp_cdev_init_state = 1;
struct qmp_cooling_device {
struct thermal_cooling_device *cdev;
struct qmp *qmp;
char *name;
bool state;
};
/**
* struct qmp - driver state for QMP implementation
* @msgram: iomem referencing the message RAM used for communication
@ -69,6 +82,7 @@ struct qmp {
struct clk_hw qdss_clk;
struct genpd_onecell_data pd_data;
struct qmp_cooling_device *cooling_devs;
};
struct qmp_pd {
@ -385,6 +399,118 @@ static void qmp_pd_remove(struct qmp *qmp)
pm_genpd_remove(data->domains[i]);
}
static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = qmp_cdev_init_state;
return 0;
}
static int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct qmp_cooling_device *qmp_cdev = cdev->devdata;
*state = qmp_cdev->state;
return 0;
}
static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct qmp_cooling_device *qmp_cdev = cdev->devdata;
char buf[QMP_MSG_LEN] = {};
bool cdev_state;
int ret;
/* Normalize state */
cdev_state = !!state;
if (qmp_cdev->state == state)
return 0;
snprintf(buf, sizeof(buf),
"{class: volt_flr, event:zero_temp, res:%s, value:%s}",
qmp_cdev->name,
cdev_state ? "off" : "on");
ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf));
if (!ret)
qmp_cdev->state = cdev_state;
return ret;
}
static struct thermal_cooling_device_ops qmp_cooling_device_ops = {
.get_max_state = qmp_cdev_get_max_state,
.get_cur_state = qmp_cdev_get_cur_state,
.set_cur_state = qmp_cdev_set_cur_state,
};
static int qmp_cooling_device_add(struct qmp *qmp,
struct qmp_cooling_device *qmp_cdev,
struct device_node *node)
{
char *cdev_name = (char *)node->name;
qmp_cdev->qmp = qmp;
qmp_cdev->state = qmp_cdev_init_state;
qmp_cdev->name = cdev_name;
qmp_cdev->cdev = devm_thermal_of_cooling_device_register
(qmp->dev, node,
cdev_name,
qmp_cdev, &qmp_cooling_device_ops);
if (IS_ERR(qmp_cdev->cdev))
dev_err(qmp->dev, "unable to register %s cooling device\n",
cdev_name);
return PTR_ERR_OR_ZERO(qmp_cdev->cdev);
}
static int qmp_cooling_devices_register(struct qmp *qmp)
{
struct device_node *np, *child;
int count = QMP_NUM_COOLING_RESOURCES;
int ret;
np = qmp->dev->of_node;
qmp->cooling_devs = devm_kcalloc(qmp->dev, count,
sizeof(*qmp->cooling_devs),
GFP_KERNEL);
if (!qmp->cooling_devs)
return -ENOMEM;
for_each_available_child_of_node(np, child) {
if (!of_find_property(child, "#cooling-cells", NULL))
continue;
ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++],
child);
if (ret)
goto unroll;
}
return 0;
unroll:
while (--count >= 0)
thermal_cooling_device_unregister
(qmp->cooling_devs[count].cdev);
return ret;
}
static void qmp_cooling_devices_remove(struct qmp *qmp)
{
int i;
for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++)
thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
}
static int qmp_probe(struct platform_device *pdev)
{
struct resource *res;
@ -433,6 +559,10 @@ static int qmp_probe(struct platform_device *pdev)
if (ret)
goto err_remove_qdss_clk;
ret = qmp_cooling_devices_register(qmp);
if (ret)
dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
platform_set_drvdata(pdev, qmp);
return 0;
@ -453,6 +583,7 @@ static int qmp_remove(struct platform_device *pdev)
qmp_qdss_clk_remove(qmp);
qmp_pd_remove(qmp);
qmp_cooling_devices_remove(qmp);
qmp_close(qmp);
mbox_free_channel(qmp->mbox_chan);
@ -461,7 +592,9 @@ static int qmp_remove(struct platform_device *pdev)
}
static const struct of_device_id qmp_dt_match[] = {
{ .compatible = "qcom,sc7180-aoss-qmp", },
{ .compatible = "qcom,sdm845-aoss-qmp", },
{ .compatible = "qcom,sm8150-aoss-qmp", },
{}
};
MODULE_DEVICE_TABLE(of, qmp_dt_match);

View file

@ -84,7 +84,7 @@
#define SMEM_GLOBAL_HOST 0xfffe
/* Max number of processors/hosts in a system */
#define SMEM_HOST_COUNT 10
#define SMEM_HOST_COUNT 11
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
@ -268,6 +268,7 @@ struct qcom_smem {
struct smem_partition_header *partitions[SMEM_HOST_COUNT];
size_t cacheline[SMEM_HOST_COUNT];
u32 item_count;
struct platform_device *socinfo;
unsigned num_regions;
struct smem_region regions[];
@ -963,11 +964,19 @@ static int qcom_smem_probe(struct platform_device *pdev)
__smem = smem;
smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
PLATFORM_DEVID_NONE, NULL,
0);
if (IS_ERR(smem->socinfo))
dev_dbg(&pdev->dev, "failed to register socinfo device\n");
return 0;
}
static int qcom_smem_remove(struct platform_device *pdev)
{
platform_device_unregister(__smem->socinfo);
hwspin_lock_free(__smem->hwlock);
__smem = NULL;

476
drivers/soc/qcom/socinfo.c Normal file
View file

@ -0,0 +1,476 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2017-2019, Linaro Ltd.
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem.h>
#include <linux/string.h>
#include <linux/sys_soc.h>
#include <linux/types.h>
/*
* SoC version type with major number in the upper 16 bits and minor
* number in the lower 16 bits.
*/
#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
/*
* SMEM item id, used to acquire handles to respective
* SMEM region.
*/
#define SMEM_HW_SW_BUILD_ID 137
#ifdef CONFIG_DEBUG_FS
#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
#define SMEM_IMAGE_VERSION_SIZE 4096
#define SMEM_IMAGE_VERSION_NAME_SIZE 75
#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20
#define SMEM_IMAGE_VERSION_OEM_SIZE 32
/*
* SMEM Image table indices
*/
#define SMEM_IMAGE_TABLE_BOOT_INDEX 0
#define SMEM_IMAGE_TABLE_TZ_INDEX 1
#define SMEM_IMAGE_TABLE_RPM_INDEX 3
#define SMEM_IMAGE_TABLE_APPS_INDEX 10
#define SMEM_IMAGE_TABLE_MPSS_INDEX 11
#define SMEM_IMAGE_TABLE_ADSP_INDEX 12
#define SMEM_IMAGE_TABLE_CNSS_INDEX 13
#define SMEM_IMAGE_TABLE_VIDEO_INDEX 14
#define SMEM_IMAGE_VERSION_TABLE 469
/*
* SMEM Image table names
*/
static const char *const socinfo_image_names[] = {
[SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp",
[SMEM_IMAGE_TABLE_APPS_INDEX] = "apps",
[SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot",
[SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss",
[SMEM_IMAGE_TABLE_MPSS_INDEX] = "mpss",
[SMEM_IMAGE_TABLE_RPM_INDEX] = "rpm",
[SMEM_IMAGE_TABLE_TZ_INDEX] = "tz",
[SMEM_IMAGE_TABLE_VIDEO_INDEX] = "video",
};
static const char *const pmic_models[] = {
[0] = "Unknown PMIC model",
[9] = "PM8994",
[11] = "PM8916",
[13] = "PM8058",
[14] = "PM8028",
[15] = "PM8901",
[16] = "PM8027",
[17] = "ISL9519",
[18] = "PM8921",
[19] = "PM8018",
[20] = "PM8015",
[21] = "PM8014",
[22] = "PM8821",
[23] = "PM8038",
[24] = "PM8922",
[25] = "PM8917",
};
#endif /* CONFIG_DEBUG_FS */
/* Socinfo SMEM item structure */
struct socinfo {
__le32 fmt;
__le32 id;
__le32 ver;
char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
/* Version 2 */
__le32 raw_id;
__le32 raw_ver;
/* Version 3 */
__le32 hw_plat;
/* Version 4 */
__le32 plat_ver;
/* Version 5 */
__le32 accessory_chip;
/* Version 6 */
__le32 hw_plat_subtype;
/* Version 7 */
__le32 pmic_model;
__le32 pmic_die_rev;
/* Version 8 */
__le32 pmic_model_1;
__le32 pmic_die_rev_1;
__le32 pmic_model_2;
__le32 pmic_die_rev_2;
/* Version 9 */
__le32 foundry_id;
/* Version 10 */
__le32 serial_num;
/* Version 11 */
__le32 num_pmics;
__le32 pmic_array_offset;
/* Version 12 */
__le32 chip_family;
__le32 raw_device_family;
__le32 raw_device_num;
};
#ifdef CONFIG_DEBUG_FS
struct socinfo_params {
u32 raw_device_family;
u32 hw_plat_subtype;
u32 accessory_chip;
u32 raw_device_num;
u32 chip_family;
u32 foundry_id;
u32 plat_ver;
u32 raw_ver;
u32 hw_plat;
u32 fmt;
};
struct smem_image_version {
char name[SMEM_IMAGE_VERSION_NAME_SIZE];
char variant[SMEM_IMAGE_VERSION_VARIANT_SIZE];
char pad;
char oem[SMEM_IMAGE_VERSION_OEM_SIZE];
};
#endif /* CONFIG_DEBUG_FS */
struct qcom_socinfo {
struct soc_device *soc_dev;
struct soc_device_attribute attr;
#ifdef CONFIG_DEBUG_FS
struct dentry *dbg_root;
struct socinfo_params info;
#endif /* CONFIG_DEBUG_FS */
};
struct soc_id {
unsigned int id;
const char *name;
};
static const struct soc_id soc_id[] = {
{ 87, "MSM8960" },
{ 109, "APQ8064" },
{ 122, "MSM8660A" },
{ 123, "MSM8260A" },
{ 124, "APQ8060A" },
{ 126, "MSM8974" },
{ 130, "MPQ8064" },
{ 138, "MSM8960AB" },
{ 139, "APQ8060AB" },
{ 140, "MSM8260AB" },
{ 141, "MSM8660AB" },
{ 178, "APQ8084" },
{ 184, "APQ8074" },
{ 185, "MSM8274" },
{ 186, "MSM8674" },
{ 194, "MSM8974PRO" },
{ 206, "MSM8916" },
{ 208, "APQ8074-AA" },
{ 209, "APQ8074-AB" },
{ 210, "APQ8074PRO" },
{ 211, "MSM8274-AA" },
{ 212, "MSM8274-AB" },
{ 213, "MSM8274PRO" },
{ 214, "MSM8674-AA" },
{ 215, "MSM8674-AB" },
{ 216, "MSM8674PRO" },
{ 217, "MSM8974-AA" },
{ 218, "MSM8974-AB" },
{ 246, "MSM8996" },
{ 247, "APQ8016" },
{ 248, "MSM8216" },
{ 249, "MSM8116" },
{ 250, "MSM8616" },
{ 291, "APQ8096" },
{ 305, "MSM8996SG" },
{ 310, "MSM8996AU" },
{ 311, "APQ8096AU" },
{ 312, "APQ8096SG" },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)
{
int idx;
for (idx = 0; idx < ARRAY_SIZE(soc_id); idx++) {
if (soc_id[idx].id == id)
return soc_id[idx].name;
}
return NULL;
}
#ifdef CONFIG_DEBUG_FS
#define QCOM_OPEN(name, _func) \
static int qcom_open_##name(struct inode *inode, struct file *file) \
{ \
return single_open(file, _func, inode->i_private); \
} \
\
static const struct file_operations qcom_ ##name## _ops = { \
.open = qcom_open_##name, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define DEBUGFS_ADD(info, name) \
debugfs_create_file(__stringify(name), 0400, \
qcom_socinfo->dbg_root, \
info, &qcom_ ##name## _ops)
static int qcom_show_build_id(struct seq_file *seq, void *p)
{
struct socinfo *socinfo = seq->private;
seq_printf(seq, "%s\n", socinfo->build_id);
return 0;
}
static int qcom_show_pmic_model(struct seq_file *seq, void *p)
{
struct socinfo *socinfo = seq->private;
int model = SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_model));
if (model < 0)
return -EINVAL;
seq_printf(seq, "%s\n", pmic_models[model]);
return 0;
}
static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p)
{
struct socinfo *socinfo = seq->private;
seq_printf(seq, "%u.%u\n",
SOCINFO_MAJOR(le32_to_cpu(socinfo->pmic_die_rev)),
SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_die_rev)));
return 0;
}
QCOM_OPEN(build_id, qcom_show_build_id);
QCOM_OPEN(pmic_model, qcom_show_pmic_model);
QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision);
#define DEFINE_IMAGE_OPS(type) \
static int show_image_##type(struct seq_file *seq, void *p) \
{ \
struct smem_image_version *image_version = seq->private; \
seq_puts(seq, image_version->type); \
seq_puts(seq, "\n"); \
return 0; \
} \
static int open_image_##type(struct inode *inode, struct file *file) \
{ \
return single_open(file, show_image_##type, inode->i_private); \
} \
\
static const struct file_operations qcom_image_##type##_ops = { \
.open = open_image_##type, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
DEFINE_IMAGE_OPS(name);
DEFINE_IMAGE_OPS(variant);
DEFINE_IMAGE_OPS(oem);
static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
struct socinfo *info)
{
struct smem_image_version *versions;
struct dentry *dentry;
size_t size;
int i;
qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL);
qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt);
switch (qcom_socinfo->info.fmt) {
case SOCINFO_VERSION(0, 12):
qcom_socinfo->info.chip_family =
__le32_to_cpu(info->chip_family);
qcom_socinfo->info.raw_device_family =
__le32_to_cpu(info->raw_device_family);
qcom_socinfo->info.raw_device_num =
__le32_to_cpu(info->raw_device_num);
debugfs_create_x32("chip_family", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.chip_family);
debugfs_create_x32("raw_device_family", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.raw_device_family);
debugfs_create_x32("raw_device_number", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.raw_device_num);
/* Fall through */
case SOCINFO_VERSION(0, 11):
case SOCINFO_VERSION(0, 10):
case SOCINFO_VERSION(0, 9):
qcom_socinfo->info.foundry_id = __le32_to_cpu(info->foundry_id);
debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.foundry_id);
/* Fall through */
case SOCINFO_VERSION(0, 8):
case SOCINFO_VERSION(0, 7):
DEBUGFS_ADD(info, pmic_model);
DEBUGFS_ADD(info, pmic_die_rev);
/* Fall through */
case SOCINFO_VERSION(0, 6):
qcom_socinfo->info.hw_plat_subtype =
__le32_to_cpu(info->hw_plat_subtype);
debugfs_create_u32("hardware_platform_subtype", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.hw_plat_subtype);
/* Fall through */
case SOCINFO_VERSION(0, 5):
qcom_socinfo->info.accessory_chip =
__le32_to_cpu(info->accessory_chip);
debugfs_create_u32("accessory_chip", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.accessory_chip);
/* Fall through */
case SOCINFO_VERSION(0, 4):
qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver);
debugfs_create_u32("platform_version", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.plat_ver);
/* Fall through */
case SOCINFO_VERSION(0, 3):
qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat);
debugfs_create_u32("hardware_platform", 0400,
qcom_socinfo->dbg_root,
&qcom_socinfo->info.hw_plat);
/* Fall through */
case SOCINFO_VERSION(0, 2):
qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver);
debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.raw_ver);
/* Fall through */
case SOCINFO_VERSION(0, 1):
DEBUGFS_ADD(info, build_id);
break;
}
versions = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE,
&size);
for (i = 0; i < ARRAY_SIZE(socinfo_image_names); i++) {
if (!socinfo_image_names[i])
continue;
dentry = debugfs_create_dir(socinfo_image_names[i],
qcom_socinfo->dbg_root);
debugfs_create_file("name", 0400, dentry, &versions[i],
&qcom_image_name_ops);
debugfs_create_file("variant", 0400, dentry, &versions[i],
&qcom_image_variant_ops);
debugfs_create_file("oem", 0400, dentry, &versions[i],
&qcom_image_oem_ops);
}
}
static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo)
{
debugfs_remove_recursive(qcom_socinfo->dbg_root);
}
#else
static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
struct socinfo *info)
{
}
static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) { }
#endif /* CONFIG_DEBUG_FS */
static int qcom_socinfo_probe(struct platform_device *pdev)
{
struct qcom_socinfo *qs;
struct socinfo *info;
size_t item_size;
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
&item_size);
if (IS_ERR(info)) {
dev_err(&pdev->dev, "Couldn't find socinfo\n");
return PTR_ERR(info);
}
qs = devm_kzalloc(&pdev->dev, sizeof(*qs), GFP_KERNEL);
if (!qs)
return -ENOMEM;
qs->attr.family = "Snapdragon";
qs->attr.machine = socinfo_machine(&pdev->dev,
le32_to_cpu(info->id));
qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u",
SOCINFO_MAJOR(le32_to_cpu(info->ver)),
SOCINFO_MINOR(le32_to_cpu(info->ver)));
if (offsetof(struct socinfo, serial_num) <= item_size)
qs->attr.serial_number = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"%u",
le32_to_cpu(info->serial_num));
qs->soc_dev = soc_device_register(&qs->attr);
if (IS_ERR(qs->soc_dev))
return PTR_ERR(qs->soc_dev);
socinfo_debugfs_init(qs, info);
/* Feed the soc specific unique data into entropy pool */
add_device_randomness(info, item_size);
platform_set_drvdata(pdev, qs->soc_dev);
return 0;
}
static int qcom_socinfo_remove(struct platform_device *pdev)
{
struct qcom_socinfo *qs = platform_get_drvdata(pdev);
soc_device_unregister(qs->soc_dev);
socinfo_debugfs_exit(qs);
return 0;
}
static struct platform_driver qcom_socinfo_driver = {
.probe = qcom_socinfo_probe,
.remove = qcom_socinfo_remove,
.driver = {
.name = "qcom-socinfo",
},
};
module_platform_driver(qcom_socinfo_driver);
MODULE_DESCRIPTION("Qualcomm SoCinfo driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:qcom-socinfo");

View file

@ -55,6 +55,7 @@ config ARCH_EMEV2
config ARCH_R7S72100
bool "RZ/A1H (R7S72100)"
select ARM_ERRATA_754322
select PM
select PM_GENERIC_DOMAINS
select RENESAS_OSTM
@ -72,12 +73,14 @@ config ARCH_R8A73A4
bool "R-Mobile APE6 (R8A73A40)"
select ARCH_RMOBILE
select ARM_ERRATA_798181 if SMP
select ARM_ERRATA_814220
select HAVE_ARM_ARCH_TIMER
select RENESAS_IRQC
config ARCH_R8A7740
bool "R-Mobile A1 (R8A77400)"
select ARCH_RMOBILE
select ARM_ERRATA_754322
select RENESAS_INTC_IRQPIN
config ARCH_R8A7743
@ -95,20 +98,24 @@ config ARCH_R8A7744
config ARCH_R8A7745
bool "RZ/G1E (R8A77450)"
select ARCH_RCAR_GEN2
select ARM_ERRATA_814220
select SYSC_R8A7745
config ARCH_R8A77470
bool "RZ/G1C (R8A77470)"
select ARCH_RCAR_GEN2
select ARM_ERRATA_814220
select SYSC_R8A77470
config ARCH_R8A7778
bool "R-Car M1A (R8A77781)"
select ARCH_RCAR_GEN1
select ARM_ERRATA_754322
config ARCH_R8A7779
bool "R-Car H1 (R8A77790)"
select ARCH_RCAR_GEN1
select ARM_ERRATA_754322
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select SYSC_R8A7779
@ -117,6 +124,7 @@ config ARCH_R8A7790
bool "R-Car H2 (R8A77900)"
select ARCH_RCAR_GEN2
select ARM_ERRATA_798181 if SMP
select ARM_ERRATA_814220
select I2C
select SYSC_R8A7790
@ -143,15 +151,18 @@ config ARCH_R8A7793
config ARCH_R8A7794
bool "R-Car E2 (R8A77940)"
select ARCH_RCAR_GEN2
select ARM_ERRATA_814220
select SYSC_R8A7794
config ARCH_R9A06G032
bool "RZ/N1D (R9A06G032)"
select ARCH_RZN1
select ARM_ERRATA_814220
config ARCH_SH73A0
bool "SH-Mobile AG5 (R8A73A00)"
select ARCH_RMOBILE
select ARM_ERRATA_754322
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select RENESAS_INTC_IRQPIN

View file

@ -170,7 +170,7 @@ struct rcar_sysc_pd {
struct generic_pm_domain genpd;
struct rcar_sysc_ch ch;
unsigned int flags;
char name[0];
char name[];
};
static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d)
@ -200,7 +200,6 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
{
struct generic_pm_domain *genpd = &pd->genpd;
const char *name = pd->genpd.name;
struct dev_power_governor *gov = &simple_qos_governor;
int error;
if (pd->flags & PD_CPU) {
@ -254,7 +253,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
rcar_sysc_power(&pd->ch, true);
finalize:
error = pm_genpd_init(genpd, gov, false);
error = pm_genpd_init(genpd, &simple_qos_governor, false);
if (error)
pr_err("Failed to init PM domain %s: %d\n", name, error);
@ -346,7 +345,7 @@ static int __init rcar_sysc_pd_init(void)
if (info->init) {
error = info->init();
if (error)
return error;
goto out_put;
}
has_cpg_mstp = of_find_compatible_node(NULL, NULL,

View file

@ -48,12 +48,8 @@ struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
{
struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
unsigned int mask;
unsigned int mask = BIT(rmobile_pd->bit_shift);
if (rmobile_pd->bit_shift == ~0)
return -EBUSY;
mask = BIT(rmobile_pd->bit_shift);
if (rmobile_pd->suspend) {
int ret = rmobile_pd->suspend();
@ -80,14 +76,10 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd)
{
unsigned int mask;
unsigned int mask = BIT(rmobile_pd->bit_shift);
unsigned int retry_count;
int ret = 0;
if (rmobile_pd->bit_shift == ~0)
return 0;
mask = BIT(rmobile_pd->bit_shift);
if (__raw_readl(rmobile_pd->base + PSTR) & mask)
return ret;
@ -122,11 +114,15 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
struct dev_power_governor *gov = rmobile_pd->gov;
genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
genpd->power_off = rmobile_pd_power_down;
genpd->power_on = rmobile_pd_power_up;
genpd->attach_dev = cpg_mstp_attach_dev;
genpd->detach_dev = cpg_mstp_detach_dev;
__rmobile_pd_power_up(rmobile_pd);
genpd->attach_dev = cpg_mstp_attach_dev;
genpd->detach_dev = cpg_mstp_detach_dev;
if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) {
genpd->power_off = rmobile_pd_power_down;
genpd->power_on = rmobile_pd_power_up;
__rmobile_pd_power_up(rmobile_pd);
}
pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
}
@ -270,6 +266,11 @@ static void __init rmobile_setup_pm_domain(struct device_node *np,
break;
case PD_NORMAL:
if (pd->bit_shift == ~0) {
/* Top-level always-on domain */
pr_debug("PM domain %s is always-on domain\n", name);
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
}
break;
}

View file

@ -7,6 +7,12 @@ menuconfig SOC_SAMSUNG
if SOC_SAMSUNG
config EXYNOS_CHIPID
bool "Exynos Chipid controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || COMPILE_TEST
select MFD_SYSCON
select SOC_BUS
config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)

View file

@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \

View file

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS - CHIP ID support
* Author: Pankaj Dubey <pankaj.dubey@samsung.com>
* Author: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
*/
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/soc/samsung/exynos-chipid.h>
#include <linux/sys_soc.h>
static const struct exynos_soc_id {
const char *name;
unsigned int id;
} soc_ids[] = {
{ "EXYNOS3250", 0xE3472000 },
{ "EXYNOS4210", 0x43200000 }, /* EVT0 revision */
{ "EXYNOS4210", 0x43210000 },
{ "EXYNOS4212", 0x43220000 },
{ "EXYNOS4412", 0xE4412000 },
{ "EXYNOS5250", 0x43520000 },
{ "EXYNOS5260", 0xE5260000 },
{ "EXYNOS5410", 0xE5410000 },
{ "EXYNOS5420", 0xE5420000 },
{ "EXYNOS5440", 0xE5440000 },
{ "EXYNOS5800", 0xE5422000 },
{ "EXYNOS7420", 0xE7420000 },
{ "EXYNOS5433", 0xE5433000 },
};
static const char * __init product_id_to_soc_id(unsigned int product_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(soc_ids); i++)
if ((product_id & EXYNOS_MASK) == soc_ids[i].id)
return soc_ids[i].name;
return NULL;
}
int __init exynos_chipid_early_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *root;
struct regmap *regmap;
u32 product_id;
u32 revision;
int ret;
regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid");
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id);
if (ret < 0)
return ret;
revision = product_id & EXYNOS_REV_MASK;
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENOMEM;
soc_dev_attr->family = "Samsung Exynos";
root = of_find_node_by_path("/");
of_property_read_string(root, "model", &soc_dev_attr->machine);
of_node_put(root);
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision);
soc_dev_attr->soc_id = product_id_to_soc_id(product_id);
if (!soc_dev_attr->soc_id) {
pr_err("Unknown SoC\n");
ret = -ENODEV;
goto err;
}
/* please note that the actual registration will be deferred */
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
ret = PTR_ERR(soc_dev);
goto err;
}
/* it is too early to use dev_info() here (soc_dev is NULL) */
pr_info("soc soc0: Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
soc_dev_attr->soc_id, product_id, revision);
return 0;
err:
kfree(soc_dev_attr->revision);
kfree(soc_dev_attr);
return ret;
}
early_initcall(exynos_chipid_early_init);

View file

@ -15,15 +15,19 @@
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>
/**
* struct ti_sci_genpd_dev_data: holds data needed for every device attached
* to this genpd
* @idx: index of the device that identifies it with the system
* control processor.
* @exclusive: Permissions for exclusive request or shared request of the
* device.
*/
struct ti_sci_genpd_dev_data {
int idx;
u8 exclusive;
};
/**
@ -55,6 +59,14 @@ static int ti_sci_dev_id(struct device *dev)
return sci_dev_data->idx;
}
static u8 is_ti_sci_dev_exclusive(struct device *dev)
{
struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
return sci_dev_data->exclusive;
}
/**
* ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
* @dev: pointer to device associated with this genpd
@ -79,7 +91,10 @@ static int ti_sci_dev_start(struct device *dev)
const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
int idx = ti_sci_dev_id(dev);
return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
if (is_ti_sci_dev_exclusive(dev))
return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx);
else
return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
}
/**
@ -110,7 +125,7 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
if (ret < 0)
return ret;
if (pd_args.args_count != 1)
if (pd_args.args_count != 1 && pd_args.args_count != 2)
return -EINVAL;
idx = pd_args.args[0];
@ -128,6 +143,10 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
return -ENOMEM;
sci_dev_data->idx = idx;
/* Enable the exclusive permissions by default */
sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE;
if (pd_args.args_count == 2)
sci_dev_data->exclusive = pd_args.args[1] & 0x1;
genpd_data = dev_gpd_data(dev);
genpd_data->data = sci_dev_data;