ARM: SoC driver updates

- New SoC specific drivers
   - NVIDIA Tegra PM Domain support for newer SoCs (Tegra186 and later)
     based on the "BPMP" firmware
   - Clocksource and system controller drivers for the newly added
     Action Semi platforms (both arm and arm64).
 
 - Reset subsystem, merged through arm-soc by tradition:
   - New drivers for Altera Stratix10, TI Keystone and Cortina Gemini SoCs
   - Various subsystem-wide cleanups
 
 - Updates for existing SoC-specific drivers
   - TI GPMC (General Purpose Memory Controller)
   - Mediatek "scpsys" system controller support for MT6797
   - Broadcom "brcmstb_gisb" bus arbitrer
   - ARM SCPI firmware
   - Renesas "SYSC" system controller
 
 One more driver update was submitted for the Freescale/NXP DPAA
 data path acceleration that has previously been used on PowerPC
 chips. I ended up postponing the merge until some API questions
 for its unusual MMIO access are resolved.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIVAwUAWVpDFmCrR//JCVInAQKoihAAosgC3+3IHppOhHid+HepkOcp2teyKknw
 42fSXVpTdfWa8Oe7q80Kzmz2CPNfFq2SzHz6oXb9WCcDFqSGr0b9aSE7NnksRjTf
 2euHVJ4MnJpkRewvorRmcpK8dPXDcHwEw/8hU3yZtJUGI0IKtlrqXis+evgkz9cn
 YDynuVdAZgZiEfiNeSeadyNLcxaQCc3x9ovvsBXxBa1/x1pfeRnTbp+6hiHilCJu
 Szts/yAzZzZE9Jkf9dLKfNlsT6m2SgtjukqqOR+zHAhi7/BdTFSVUP6L8u7QjrR+
 +ijTICg8FMJGiWLAOe6ED2qZVByN92EJ2AGwriYlSles9ouoGfRkJ2rwxyjbete7
 avy0HP+PSBFXWdwbOcq8HX8CrbuBltagl5fkMokct6biWLLMshNZ33WWdQ6/DsM2
 b9mAAZuhbs0g1qWUBD3+q6qBytSuGme6Px6fMoVEc4GQ2YVFUQOoEfZOGKRv1U1o
 aMWGt/6qeF8SG288rYAnQ/TuYWpOLtksV6yhotA00HUHhkTCy0xVCdyWGZtNyKhG
 o/x4YnhWFzHsXmqKcR1sM7LzfZY/WNmbrOLvK6i83Z0P7QptqrdaLAylL3iSPEyX
 ZDUgExf6PYXkWIewc3KwC5sJjuD05z3ZCgIR+mCezwbuD+3Z2fOdjodY/VBZ74hq
 URcM/BqtuWE=
 =5L6n
 -----END PGP SIGNATURE-----

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

Pull ARM SoC driver updates from Arnd Bergmann:
 "New SoC specific drivers:

   - NVIDIA Tegra PM Domain support for newer SoCs (Tegra186 and later)
     based on the "BPMP" firmware

   - Clocksource and system controller drivers for the newly added
     Action Semi platforms (both arm and arm64).

  Reset subsystem, merged through arm-soc by tradition:

   - New drivers for Altera Stratix10, TI Keystone and Cortina Gemini
     SoCs

   - Various subsystem-wide cleanups

  Updates for existing SoC-specific drivers

   - TI GPMC (General Purpose Memory Controller)

   - Mediatek "scpsys" system controller support for MT6797

   - Broadcom "brcmstb_gisb" bus arbitrer

   - ARM SCPI firmware

   - Renesas "SYSC" system controller

  One more driver update was submitted for the Freescale/NXP DPAA data
  path acceleration that has previously been used on PowerPC chips. I
  ended up postponing the merge until some API questions for its unusual
  MMIO access are resolved"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (35 commits)
  clocksource: owl: Add S900 support
  clocksource: Add Owl timer
  soc: renesas: rcar-sysc: Use GENPD_FLAG_ALWAYS_ON
  firmware: tegra: Fix locking bugs in BPMP
  soc/tegra: flowctrl: Fix error handling
  soc/tegra: bpmp: Implement generic PM domains
  soc/tegra: bpmp: Update ABI header
  PM / Domains: Allow overriding the ->xlate() callback
  soc: brcmstb: enable drivers for ARM64 and BMIPS
  soc: renesas: Rework Kconfig and Makefile logic
  reset: Add the TI SCI reset driver
  dt-bindings: reset: Add TI SCI reset binding
  reset: use kref for reference counting
  soc: qcom: smsm: Improve error handling, quiesce probe deferral
  cpufreq: scpi: use new scpi_ops functions to remove duplicate code
  firmware: arm_scpi: add support to populate OPPs and get transition latency
  dt-bindings: reset: Add reset manager offsets for Stratix10
  memory: omap-gpmc: add error message if bank-width property is absent
  memory: omap-gpmc: make dts snippet include semicolon
  reset: Add a Gemini reset controller
  ...
This commit is contained in:
Linus Torvalds 2017-07-04 14:47:47 -07:00
commit e854711291
42 changed files with 2059 additions and 198 deletions

View file

@ -7,6 +7,7 @@ source "drivers/soc/fsl/Kconfig"
source "drivers/soc/imx/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/renesas/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/samsung/Kconfig"
source "drivers/soc/sunxi/Kconfig"

View file

@ -11,7 +11,7 @@ obj-y += fsl/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/

View file

@ -11,7 +11,7 @@ config RASPBERRYPI_POWER
config SOC_BRCMSTB
bool "Broadcom STB SoC drivers"
depends on ARM
depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST
select SOC_BUS
help
Enables drivers for the Broadcom Set-Top Box (STB) series of chips.

View file

@ -1,2 +1,2 @@
obj-y += gpc.o
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o

View file

@ -1117,6 +1117,11 @@ static int pwrap_probe(struct platform_device *pdev)
const struct of_device_id *of_slave_id = NULL;
struct resource *res;
if (!of_id) {
dev_err(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
}
if (pdev->dev.of_node->child)
of_slave_id = of_match_node(of_slave_match_tbl,
pdev->dev.of_node->child);
@ -1200,7 +1205,8 @@ static int pwrap_probe(struct platform_device *pdev)
if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_INIT_DONE0)) {
dev_dbg(wrp->dev, "initialization isn't finished\n");
return -ENODEV;
ret = -ENODEV;
goto err_out2;
}
/* Initialize watchdog, may not be done by the bootloader */
@ -1220,8 +1226,10 @@ static int pwrap_probe(struct platform_device *pdev)
goto err_out2;
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, &pwrap_regmap_config);
if (IS_ERR(wrp->regmap))
return PTR_ERR(wrp->regmap);
if (IS_ERR(wrp->regmap)) {
ret = PTR_ERR(wrp->regmap);
goto err_out2;
}
ret = of_platform_populate(np, NULL, NULL, wrp->dev);
if (ret) {

View file

@ -21,6 +21,7 @@
#include <linux/soc/mediatek/infracfg.h>
#include <dt-bindings/power/mt2701-power.h>
#include <dt-bindings/power/mt6797-power.h>
#include <dt-bindings/power/mt8173-power.h>
#define SPM_VDE_PWR_CON 0x0210
@ -71,6 +72,7 @@ enum clk_id {
CLK_VENC,
CLK_VENC_LT,
CLK_ETHIF,
CLK_VDEC,
CLK_MAX,
};
@ -81,6 +83,7 @@ static const char * const clk_names[] = {
"venc",
"venc_lt",
"ethif",
"vdec",
NULL,
};
@ -107,21 +110,28 @@ struct scp_domain {
struct regulator *supply;
};
struct scp_ctrl_reg {
int pwr_sta_offs;
int pwr_sta2nd_offs;
};
struct scp {
struct scp_domain *domains;
struct genpd_onecell_data pd_data;
struct device *dev;
void __iomem *base;
struct regmap *infracfg;
struct scp_ctrl_reg ctrl_reg;
};
static int scpsys_domain_is_on(struct scp_domain *scpd)
{
struct scp *scp = scpd->scp;
u32 status = readl(scp->base + SPM_PWR_STATUS) & scpd->data->sta_mask;
u32 status2 = readl(scp->base + SPM_PWR_STATUS_2ND) &
scpd->data->sta_mask;
u32 status = readl(scp->base + scp->ctrl_reg.pwr_sta_offs) &
scpd->data->sta_mask;
u32 status2 = readl(scp->base + scp->ctrl_reg.pwr_sta2nd_offs) &
scpd->data->sta_mask;
/*
* A domain is on when both status bits are set. If only one is set
@ -346,7 +356,8 @@ static void init_clks(struct platform_device *pdev, struct clk **clk)
}
static struct scp *init_scp(struct platform_device *pdev,
const struct scp_domain_data *scp_domain_data, int num)
const struct scp_domain_data *scp_domain_data, int num,
struct scp_ctrl_reg *scp_ctrl_reg)
{
struct genpd_onecell_data *pd_data;
struct resource *res;
@ -358,6 +369,9 @@ static struct scp *init_scp(struct platform_device *pdev,
if (!scp)
return ERR_PTR(-ENOMEM);
scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs;
scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs;
scp->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -556,8 +570,13 @@ static const struct scp_domain_data scp_domain_data_mt2701[] = {
static int __init scpsys_probe_mt2701(struct platform_device *pdev)
{
struct scp *scp;
struct scp_ctrl_reg scp_reg;
scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701);
scp_reg.pwr_sta_offs = SPM_PWR_STATUS;
scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND;
scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701,
&scp_reg);
if (IS_ERR(scp))
return PTR_ERR(scp);
@ -566,6 +585,116 @@ static int __init scpsys_probe_mt2701(struct platform_device *pdev)
return 0;
}
/*
* MT6797 power domain support
*/
static const struct scp_domain_data scp_domain_data_mt6797[] = {
[MT6797_POWER_DOMAIN_VDEC] = {
.name = "vdec",
.sta_mask = BIT(7),
.ctl_offs = 0x300,
.sram_pdn_bits = GENMASK(8, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
.clk_id = {CLK_VDEC},
},
[MT6797_POWER_DOMAIN_VENC] = {
.name = "venc",
.sta_mask = BIT(21),
.ctl_offs = 0x304,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
.clk_id = {CLK_NONE},
},
[MT6797_POWER_DOMAIN_ISP] = {
.name = "isp",
.sta_mask = BIT(5),
.ctl_offs = 0x308,
.sram_pdn_bits = GENMASK(9, 8),
.sram_pdn_ack_bits = GENMASK(13, 12),
.clk_id = {CLK_NONE},
},
[MT6797_POWER_DOMAIN_MM] = {
.name = "mm",
.sta_mask = BIT(3),
.ctl_offs = 0x30C,
.sram_pdn_bits = GENMASK(8, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
.clk_id = {CLK_MM},
.bus_prot_mask = (BIT(1) | BIT(2)),
},
[MT6797_POWER_DOMAIN_AUDIO] = {
.name = "audio",
.sta_mask = BIT(24),
.ctl_offs = 0x314,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
.clk_id = {CLK_NONE},
},
[MT6797_POWER_DOMAIN_MFG_ASYNC] = {
.name = "mfg_async",
.sta_mask = BIT(13),
.ctl_offs = 0x334,
.sram_pdn_bits = 0,
.sram_pdn_ack_bits = 0,
.clk_id = {CLK_MFG},
},
[MT6797_POWER_DOMAIN_MJC] = {
.name = "mjc",
.sta_mask = BIT(20),
.ctl_offs = 0x310,
.sram_pdn_bits = GENMASK(8, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
.clk_id = {CLK_NONE},
},
};
#define NUM_DOMAINS_MT6797 ARRAY_SIZE(scp_domain_data_mt6797)
#define SPM_PWR_STATUS_MT6797 0x0180
#define SPM_PWR_STATUS_2ND_MT6797 0x0184
static int __init scpsys_probe_mt6797(struct platform_device *pdev)
{
struct scp *scp;
struct genpd_onecell_data *pd_data;
int ret;
struct scp_ctrl_reg scp_reg;
scp_reg.pwr_sta_offs = SPM_PWR_STATUS_MT6797;
scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797;
scp = init_scp(pdev, scp_domain_data_mt6797, NUM_DOMAINS_MT6797,
&scp_reg);
if (IS_ERR(scp))
return PTR_ERR(scp);
mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT6797);
pd_data = &scp->pd_data;
ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
pd_data->domains[MT6797_POWER_DOMAIN_VDEC]);
if (ret && IS_ENABLED(CONFIG_PM))
dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
pd_data->domains[MT6797_POWER_DOMAIN_ISP]);
if (ret && IS_ENABLED(CONFIG_PM))
dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
pd_data->domains[MT6797_POWER_DOMAIN_VENC]);
if (ret && IS_ENABLED(CONFIG_PM))
dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
pd_data->domains[MT6797_POWER_DOMAIN_MJC]);
if (ret && IS_ENABLED(CONFIG_PM))
dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
return 0;
}
/*
* MT8173 power domain support
*/
@ -667,8 +796,13 @@ static int __init scpsys_probe_mt8173(struct platform_device *pdev)
struct scp *scp;
struct genpd_onecell_data *pd_data;
int ret;
struct scp_ctrl_reg scp_reg;
scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173);
scp_reg.pwr_sta_offs = SPM_PWR_STATUS;
scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND;
scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173,
&scp_reg);
if (IS_ERR(scp))
return PTR_ERR(scp);
@ -697,6 +831,9 @@ static const struct of_device_id of_scpsys_match_tbl[] = {
{
.compatible = "mediatek,mt2701-scpsys",
.data = scpsys_probe_mt2701,
}, {
.compatible = "mediatek,mt6797-scpsys",
.data = scpsys_probe_mt6797,
}, {
.compatible = "mediatek,mt8173-scpsys",
.data = scpsys_probe_mt8173,

View file

@ -439,14 +439,15 @@ static int smsm_get_size_info(struct qcom_smsm *smsm)
} *info;
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO, &size);
if (PTR_ERR(info) == -ENOENT || size != sizeof(*info)) {
if (IS_ERR(info) && PTR_ERR(info) != -ENOENT) {
if (PTR_ERR(info) != -EPROBE_DEFER)
dev_err(smsm->dev, "unable to retrieve smsm size info\n");
return PTR_ERR(info);
} else if (IS_ERR(info) || size != sizeof(*info)) {
dev_warn(smsm->dev, "no smsm size info, using defaults\n");
smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
return 0;
} else if (IS_ERR(info)) {
dev_err(smsm->dev, "unable to retrieve smsm size info\n");
return PTR_ERR(info);
}
smsm->num_entries = info->num_entries;

View file

@ -0,0 +1,63 @@
config SOC_RENESAS
bool "Renesas SoC driver support" if COMPILE_TEST && !ARCH_RENESAS
default y if ARCH_RENESAS
select SOC_BUS
select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \
ARCH_R8A7795 || ARCH_R8A7796
select SYSC_R8A7743 if ARCH_R8A7743
select SYSC_R8A7745 if ARCH_R8A7745
select SYSC_R8A7779 if ARCH_R8A7779
select SYSC_R8A7790 if ARCH_R8A7790
select SYSC_R8A7791 if ARCH_R8A7791 || ARCH_R8A7793
select SYSC_R8A7792 if ARCH_R8A7792
select SYSC_R8A7794 if ARCH_R8A7794
select SYSC_R8A7795 if ARCH_R8A7795
select SYSC_R8A7796 if ARCH_R8A7796
if SOC_RENESAS
# SoC
config SYSC_R8A7743
bool "RZ/G1M System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7745
bool "RZ/G1E System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7779
bool "R-Car H1 System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7790
bool "R-Car H2 System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7791
bool "R-Car M2-W/N System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7792
bool "R-Car V2H System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7794
bool "R-Car E2 System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7795
bool "R-Car H3 System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A7796
bool "R-Car M3-W System Controller support" if COMPILE_TEST
select SYSC_RCAR
# Family
config RST_RCAR
bool "R-Car Reset Controller support" if COMPILE_TEST
config SYSC_RCAR
bool "R-Car System Controller support" if COMPILE_TEST
endif # SOC_RENESAS

View file

@ -1,18 +1,17 @@
obj-$(CONFIG_SOC_BUS) += renesas-soc.o
# Generic, must be first because of soc_device_register()
obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o
obj-$(CONFIG_ARCH_RCAR_GEN1) += rcar-rst.o
obj-$(CONFIG_ARCH_RCAR_GEN2) += rcar-rst.o
obj-$(CONFIG_ARCH_R8A7795) += rcar-rst.o
obj-$(CONFIG_ARCH_R8A7796) += rcar-rst.o
# SoC
obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o
obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o
obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o
obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o
obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o
obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o
obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o
obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o
obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o
obj-$(CONFIG_ARCH_R8A7743) += rcar-sysc.o r8a7743-sysc.o
obj-$(CONFIG_ARCH_R8A7745) += rcar-sysc.o r8a7745-sysc.o
obj-$(CONFIG_ARCH_R8A7779) += rcar-sysc.o r8a7779-sysc.o
obj-$(CONFIG_ARCH_R8A7790) += rcar-sysc.o r8a7790-sysc.o
obj-$(CONFIG_ARCH_R8A7791) += rcar-sysc.o r8a7791-sysc.o
obj-$(CONFIG_ARCH_R8A7792) += rcar-sysc.o r8a7792-sysc.o
# R-Car M2-N is identical to R-Car M2-W w.r.t. power domains.
obj-$(CONFIG_ARCH_R8A7793) += rcar-sysc.o r8a7791-sysc.o
obj-$(CONFIG_ARCH_R8A7794) += rcar-sysc.o r8a7794-sysc.o
obj-$(CONFIG_ARCH_R8A7795) += rcar-sysc.o r8a7795-sysc.o
obj-$(CONFIG_ARCH_R8A7796) += rcar-sysc.o r8a7796-sysc.o
# Family
obj-$(CONFIG_RST_RCAR) += rcar-rst.o
obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o

View file

@ -181,17 +181,6 @@ static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd)
struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
if (pd->flags & PD_NO_CR) {
pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
return -EBUSY;
}
if (pd->flags & PD_BUSY) {
pr_debug("%s: %s busy\n", __func__, genpd->name);
return -EBUSY;
}
return rcar_sysc_power_down(&pd->ch);
}
@ -200,12 +189,6 @@ static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd)
struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
if (pd->flags & PD_NO_CR) {
pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
return 0;
}
return rcar_sysc_power_up(&pd->ch);
}
@ -223,8 +206,7 @@ static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
* only be turned off if the CPU is not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "CPU");
pd->flags |= PD_BUSY;
gov = &pm_domain_always_on_gov;
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_SCU) {
/*
* This domain contains an SCU and cache-controller, and
@ -232,19 +214,17 @@ static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
* not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "SCU");
pd->flags |= PD_BUSY;
gov = &pm_domain_always_on_gov;
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_NO_CR) {
/*
* This domain cannot be turned off.
*/
pd->flags |= PD_BUSY;
gov = &pm_domain_always_on_gov;
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
}
if (!(pd->flags & (PD_CPU | PD_SCU))) {
/* Enable Clock Domain for I/O devices */
genpd->flags = GENPD_FLAG_PM_CLK;
genpd->flags |= GENPD_FLAG_PM_CLK;
if (has_cpg_mstp) {
genpd->attach_dev = cpg_mstp_attach_dev;
genpd->detach_dev = cpg_mstp_detach_dev;
@ -275,35 +255,33 @@ finalize:
}
static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_ARCH_R8A7743
#ifdef CONFIG_SYSC_R8A7743
{ .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7745
#ifdef CONFIG_SYSC_R8A7745
{ .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7779
#ifdef CONFIG_SYSC_R8A7779
{ .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7790
#ifdef CONFIG_SYSC_R8A7790
{ .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7791
#ifdef CONFIG_SYSC_R8A7791
{ .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7792
{ .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7793
/* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */
{ .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7794
#ifdef CONFIG_SYSC_R8A7792
{ .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A7794
{ .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7795
#ifdef CONFIG_SYSC_R8A7795
{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },
#endif
#ifdef CONFIG_ARCH_R8A7796
#ifdef CONFIG_SYSC_R8A7796
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
#endif
{ /* sentinel */ }

View file

@ -20,8 +20,6 @@
#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */
#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */
#define PD_BUSY BIT(3) /* Busy, for internal use only */
#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */
#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */
#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */

View file

@ -115,3 +115,8 @@ config SOC_TEGRA_PMC
config SOC_TEGRA_PMC_TEGRA186
bool
config SOC_TEGRA_POWERGATE_BPMP
def_bool y
depends on PM_GENERIC_DOMAINS
depends on TEGRA_BPMP

View file

@ -4,3 +4,4 @@ obj-y += common.o
obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o

View file

@ -157,7 +157,7 @@ static int tegra_flowctrl_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra_flowctrl_base))
return PTR_ERR(base);
return PTR_ERR(tegra_flowctrl_base);
iounmap(base);

View file

@ -0,0 +1,359 @@
/*
* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
struct tegra_powergate_info {
unsigned int id;
char *name;
};
struct tegra_powergate {
struct generic_pm_domain genpd;
struct tegra_bpmp *bpmp;
unsigned int id;
};
static inline struct tegra_powergate *
to_tegra_powergate(struct generic_pm_domain *genpd)
{
return container_of(genpd, struct tegra_powergate, genpd);
}
static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
unsigned int id, u32 state)
{
struct mrq_pg_request request;
struct tegra_bpmp_message msg;
memset(&request, 0, sizeof(request));
request.cmd = CMD_PG_SET_STATE;
request.id = id;
request.set_state.state = state;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_PG;
msg.tx.data = &request;
msg.tx.size = sizeof(request);
return tegra_bpmp_transfer(bpmp, &msg);
}
static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
unsigned int id)
{
struct mrq_pg_response response;
struct mrq_pg_request request;
struct tegra_bpmp_message msg;
int err;
memset(&request, 0, sizeof(request));
request.cmd = CMD_PG_GET_STATE;
request.id = id;
memset(&response, 0, sizeof(response));
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_PG;
msg.tx.data = &request;
msg.tx.size = sizeof(request);
msg.rx.data = &response;
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return PG_STATE_OFF;
return response.get_state.state;
}
static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
{
struct mrq_pg_response response;
struct mrq_pg_request request;
struct tegra_bpmp_message msg;
int err;
memset(&request, 0, sizeof(request));
request.cmd = CMD_PG_GET_MAX_ID;
memset(&response, 0, sizeof(response));
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_PG;
msg.tx.data = &request;
msg.tx.size = sizeof(request);
msg.rx.data = &response;
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return err;
return response.get_max_id.max_id;
}
static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
unsigned int id)
{
struct mrq_pg_response response;
struct mrq_pg_request request;
struct tegra_bpmp_message msg;
int err;
memset(&request, 0, sizeof(request));
request.cmd = CMD_PG_GET_NAME;
request.id = id;
memset(&response, 0, sizeof(response));
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_PG;
msg.tx.data = &request;
msg.tx.size = sizeof(request);
msg.rx.data = &response;
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return NULL;
return kstrdup(response.get_name.name, GFP_KERNEL);
}
static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
unsigned int id)
{
return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
}
static int tegra_powergate_power_on(struct generic_pm_domain *domain)
{
struct tegra_powergate *powergate = to_tegra_powergate(domain);
struct tegra_bpmp *bpmp = powergate->bpmp;
return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
PG_STATE_ON);
}
static int tegra_powergate_power_off(struct generic_pm_domain *domain)
{
struct tegra_powergate *powergate = to_tegra_powergate(domain);
struct tegra_bpmp *bpmp = powergate->bpmp;
return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
PG_STATE_OFF);
}
static struct tegra_powergate *
tegra_powergate_add(struct tegra_bpmp *bpmp,
const struct tegra_powergate_info *info)
{
struct tegra_powergate *powergate;
bool off;
int err;
off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
if (!powergate)
return ERR_PTR(-ENOMEM);
powergate->id = info->id;
powergate->bpmp = bpmp;
powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
powergate->genpd.power_on = tegra_powergate_power_on;
powergate->genpd.power_off = tegra_powergate_power_off;
err = pm_genpd_init(&powergate->genpd, NULL, off);
if (err < 0) {
kfree(powergate->genpd.name);
return ERR_PTR(err);
}
return powergate;
}
static void tegra_powergate_remove(struct tegra_powergate *powergate)
{
struct generic_pm_domain *genpd = &powergate->genpd;
struct tegra_bpmp *bpmp = powergate->bpmp;
int err;
err = pm_genpd_remove(genpd);
if (err < 0)
dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
genpd->name, err);
kfree(genpd->name);
}
static int
tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
struct tegra_powergate_info **powergatesp)
{
struct tegra_powergate_info *powergates;
unsigned int max_id, id, count = 0;
unsigned int num_holes = 0;
int err;
err = tegra_bpmp_powergate_get_max_id(bpmp);
if (err < 0)
return err;
max_id = err;
dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
if (!powergates)
return -ENOMEM;
for (id = 0; id <= max_id; id++) {
struct tegra_powergate_info *info = &powergates[count];
info->name = tegra_bpmp_powergate_get_name(bpmp, id);
if (!info->name || info->name[0] == '\0') {
num_holes++;
continue;
}
info->id = id;
count++;
}
dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
*powergatesp = powergates;
return count;
}
static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
struct tegra_powergate_info *powergates,
unsigned int count)
{
struct genpd_onecell_data *genpd = &bpmp->genpd;
struct generic_pm_domain **domains;
struct tegra_powergate *powergate;
unsigned int i;
int err;
domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
for (i = 0; i < count; i++) {
powergate = tegra_powergate_add(bpmp, &powergates[i]);
if (IS_ERR(powergate)) {
err = PTR_ERR(powergate);
goto remove;
}
dev_dbg(bpmp->dev, "added power domain %s\n",
powergate->genpd.name);
domains[i] = &powergate->genpd;
}
genpd->num_domains = count;
genpd->domains = domains;
return 0;
remove:
while (i--) {
powergate = to_tegra_powergate(domains[i]);
tegra_powergate_remove(powergate);
}
kfree(genpd->domains);
return err;
}
static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
{
struct genpd_onecell_data *genpd = &bpmp->genpd;
unsigned int i = genpd->num_domains;
struct tegra_powergate *powergate;
while (i--) {
dev_dbg(bpmp->dev, "removing power domain %s\n",
genpd->domains[i]->name);
powergate = to_tegra_powergate(genpd->domains[i]);
tegra_powergate_remove(powergate);
}
}
static struct generic_pm_domain *
tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
{
struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
struct genpd_onecell_data *genpd = data;
unsigned int i;
for (i = 0; i < genpd->num_domains; i++) {
struct tegra_powergate *powergate;
powergate = to_tegra_powergate(genpd->domains[i]);
if (powergate->id == spec->args[0]) {
domain = &powergate->genpd;
break;
}
}
return domain;
}
int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
{
struct device_node *np = bpmp->dev->of_node;
struct tegra_powergate_info *powergates;
struct device *dev = bpmp->dev;
unsigned int count, i;
int err;
err = tegra_bpmp_probe_powergates(bpmp, &powergates);
if (err < 0)
return err;
count = err;
dev_dbg(dev, "%u power domains probed\n", count);
err = tegra_bpmp_add_powergates(bpmp, powergates, count);
if (err < 0)
goto free;
bpmp->genpd.xlate = tegra_powergate_xlate;
err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
if (err < 0) {
dev_err(dev, "failed to add power domain provider: %d\n", err);
tegra_bpmp_remove_powergates(bpmp);
}
free:
for (i = 0; i < count; i++)
kfree(powergates[i].name);
kfree(powergates);
return err;
}