diff options
-rw-r--r-- | Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt | 8 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/bus/imx-weim.txt | 32 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt | 3 | ||||
-rw-r--r-- | drivers/bus/imx-weim.c | 70 | ||||
-rw-r--r-- | drivers/firmware/imx/misc.c | 38 | ||||
-rw-r--r-- | drivers/firmware/imx/scu-pd.c | 1 | ||||
-rw-r--r-- | drivers/soc/imx/Kconfig | 2 | ||||
-rw-r--r-- | drivers/soc/imx/gpcv2.c | 76 | ||||
-rw-r--r-- | include/linux/firmware/imx/svc/misc.h | 3 |
9 files changed, 212 insertions, 21 deletions
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt index 27784b6edfed..fd2bed23e0e3 100644 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt @@ -58,7 +58,11 @@ This binding for the SCU power domain providers uses the generic power domain binding[2]. Required properties: -- compatible: Should be "fsl,imx8qxp-scu-pd". +- compatible: Should be one of: + "fsl,imx8qm-scu-pd", + "fsl,imx8qxp-scu-pd" + followed by "fsl,scu-pd" + - #power-domain-cells: Must be 1. Contains the Resource ID used by SCU commands. See detailed Resource ID list from: @@ -154,7 +158,7 @@ firmware { }; pd: imx8qx-pd { - compatible = "fsl,imx8qxp-scu-pd"; + compatible = "fsl,imx8qxp-scu-pd", "fsl,scu-pd"; #power-domain-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt index 683eaf3aed79..dda7d6d66479 100644 --- a/Documentation/devicetree/bindings/bus/imx-weim.txt +++ b/Documentation/devicetree/bindings/bus/imx-weim.txt @@ -47,9 +47,9 @@ Optional properties: Timing property for child nodes. It is mandatory, not optional. - fsl,weim-cs-timing: The timing array, contains timing values for the - child node. We can get the CS index from the child - node's "reg" property. The number of registers depends - on the selected chip. + child node. We get the CS indexes from the address + ranges in the child node's "reg" property. + The number of registers depends on the selected chip: For i.MX1, i.MX21 ("fsl,imx1-weim") there are two registers: CSxU, CSxL. For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim") @@ -80,3 +80,29 @@ Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM: 0x0000c000 0x1404a38e 0x00000000>; }; }; + +Example for an imx6q-based board, a multi-chipselect device connected to WEIM: + +In this case, both chip select 0 and 1 will be configured with the same timing +array values. + + weim: weim@21b8000 { + compatible = "fsl,imx6q-weim"; + reg = <0x021b8000 0x4000>; + clocks = <&clks 196>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x02000000 + 1 0 0x0a000000 0x02000000 + 2 0 0x0c000000 0x02000000 + 3 0 0x0e000000 0x02000000>; + fsl,weim-cs-gpr = <&gpr>; + + acme@0 { + compatible = "acme,whatever"; + reg = <0 0 0x100>, <0 0x400000 0x800>, + <1 0x400000 0x800>; + fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100 + 0x00000000 0xa0000240 0x00000000>; + }; + }; diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt index 7c947a996df1..7c7e972aaa42 100644 --- a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt @@ -32,6 +32,9 @@ Required properties: Optional properties: - power-supply: Power supply used to power the domain +- clocks: a number of phandles to clocks that need to be enabled during + domain power-up sequencing to ensure reset propagation into devices + located inside this power domain Example: diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index d84996a4528e..db74334ca5ef 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -46,6 +46,17 @@ static const struct imx_weim_devtype imx51_weim_devtype = { }; #define MAX_CS_REGS_COUNT 6 +#define MAX_CS_COUNT 6 +#define OF_REG_SIZE 3 + +struct cs_timing { + bool is_applied; + u32 regs[MAX_CS_REGS_COUNT]; +}; + +struct cs_timing_state { + struct cs_timing cs[MAX_CS_COUNT]; +}; static const struct of_device_id weim_id_table[] = { /* i.MX1/21 */ @@ -111,21 +122,19 @@ err: } /* Parse and set the timing for this device. */ -static int __init weim_timing_setup(struct device_node *np, void __iomem *base, - const struct imx_weim_devtype *devtype) +static int __init weim_timing_setup(struct device *dev, + struct device_node *np, void __iomem *base, + const struct imx_weim_devtype *devtype, + struct cs_timing_state *ts) { u32 cs_idx, value[MAX_CS_REGS_COUNT]; int i, ret; + int reg_idx, num_regs; + struct cs_timing *cst; if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) return -EINVAL; - - /* get the CS index from this child node's "reg" property. */ - ret = of_property_read_u32(np, "reg", &cs_idx); - if (ret) - return ret; - - if (cs_idx >= devtype->cs_count) + if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) return -EINVAL; ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", @@ -133,9 +142,43 @@ static int __init weim_timing_setup(struct device_node *np, void __iomem *base, if (ret) return ret; - /* set the timing for WEIM */ - for (i = 0; i < devtype->cs_regs_count; i++) - writel(value[i], base + cs_idx * devtype->cs_stride + i * 4); + /* + * the child node's "reg" property may contain multiple address ranges, + * extract the chip select for each. + */ + num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); + if (num_regs < 0) + return num_regs; + if (!num_regs) + return -EINVAL; + for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { + /* get the CS index from this child node's "reg" property. */ + ret = of_property_read_u32_index(np, "reg", + reg_idx * OF_REG_SIZE, &cs_idx); + if (ret) + break; + + if (cs_idx >= devtype->cs_count) + return -EINVAL; + + /* prevent re-configuring a CS that's already been configured */ + cst = &ts->cs[cs_idx]; + if (cst->is_applied && memcmp(value, cst->regs, + devtype->cs_regs_count * sizeof(u32))) { + dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); + return -EINVAL; + } + + /* set the timing for WEIM */ + for (i = 0; i < devtype->cs_regs_count; i++) + writel(value[i], + base + cs_idx * devtype->cs_stride + i * 4); + if (!cst->is_applied) { + cst->is_applied = true; + memcpy(cst->regs, value, + devtype->cs_regs_count * sizeof(u32)); + } + } return 0; } @@ -148,6 +191,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, const struct imx_weim_devtype *devtype = of_id->data; struct device_node *child; int ret, have_child = 0; + struct cs_timing_state ts = {}; if (devtype == &imx50_weim_devtype) { ret = imx_weim_gpr_setup(pdev); @@ -156,7 +200,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, } for_each_available_child_of_node(pdev->dev.of_node, child) { - ret = weim_timing_setup(child, base, devtype); + ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); if (ret) dev_warn(&pdev->dev, "%pOF set timing failed.\n", child); diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c index 97f5424dbac9..4b56a587dacd 100644 --- a/drivers/firmware/imx/misc.c +++ b/drivers/firmware/imx/misc.c @@ -18,6 +18,14 @@ struct imx_sc_msg_req_misc_set_ctrl { u16 resource; } __packed; +struct imx_sc_msg_req_cpu_start { + struct imx_sc_rpc_msg hdr; + u32 address_hi; + u32 address_lo; + u16 resource; + u8 enable; +} __packed; + struct imx_sc_msg_req_misc_get_ctrl { struct imx_sc_rpc_msg hdr; u32 ctrl; @@ -97,3 +105,33 @@ int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, return 0; } EXPORT_SYMBOL(imx_sc_misc_get_control); + +/* + * This function starts/stops a CPU identified by @resource + * + * @param[in] ipc IPC handle + * @param[in] resource resource the control is associated with + * @param[in] enable true for start, false for stop + * @param[in] phys_addr initial instruction address to be executed + * + * @return Returns 0 for success and < 0 for errors. + */ +int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, + bool enable, u64 phys_addr) +{ + struct imx_sc_msg_req_cpu_start msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_CPU_START; + hdr->size = 4; + + msg.address_hi = phys_addr >> 32; + msg.address_lo = phys_addr; + msg.resource = resource; + msg.enable = enable; + + return imx_scu_call_rpc(ipc, &msg, true); +} +EXPORT_SYMBOL(imx_sc_pm_cpu_start); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 407245f2efd0..39a94c7177fc 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -322,6 +322,7 @@ static int imx_sc_pd_probe(struct platform_device *pdev) static const struct of_device_id imx_sc_pd_match[] = { { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, + { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, { /* sentinel */ } }; diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index 2112d18dbb7b..d80f899d22f9 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig @@ -2,7 +2,7 @@ menu "i.MX SoC drivers" config IMX_GPCV2_PM_DOMAINS bool "i.MX GPCv2 PM domains" - depends on SOC_IMX7D || SOC_IMX8MQ || (COMPILE_TEST && OF) + depends on ARCH_MXC || (COMPILE_TEST && OF) depends on PM select PM_GENERIC_DOMAINS default y if SOC_IMX7D diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 8b4f48a2ca57..176f473127b6 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -8,6 +8,7 @@ * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> */ +#include <linux/clk.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> @@ -65,6 +66,12 @@ #define GPC_M4_PU_PDN_FLG 0x1bc +#define GPC_PU_PWRHSK 0x1fc + +#define IMX8M_GPU_HSK_PWRDNREQN BIT(6) +#define IMX8M_VPU_HSK_PWRDNREQN BIT(5) +#define IMX8M_DISP_HSK_PWRDNREQN BIT(4) + /* * The PGC offset values in Reference Manual * (Rev. 1, 01/2018 and the older ones) GPC chapter's @@ -92,16 +99,21 @@ #define GPC_PGC_CTRL_PCR BIT(0) +#define GPC_CLK_MAX 6 + struct imx_pgc_domain { struct generic_pm_domain genpd; struct regmap *regmap; struct regulator *regulator; + struct clk *clk[GPC_CLK_MAX]; + int num_clks; unsigned int pgc; const struct { u32 pxx; u32 map; + u32 hsk; } bits; const int voltage; @@ -125,7 +137,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, const bool enable_power_control = !on; const bool has_regulator = !IS_ERR(domain->regulator); unsigned long deadline; - int ret = 0; + int i, ret = 0; regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, domain->bits.map, domain->bits.map); @@ -138,10 +150,18 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, } } + /* Enable reset clocks for all devices in the domain */ + for (i = 0; i < domain->num_clks; i++) + clk_prepare_enable(domain->clk[i]); + if (enable_power_control) regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); + if (domain->bits.hsk) + regmap_update_bits(domain->regmap, GPC_PU_PWRHSK, + domain->bits.hsk, on ? domain->bits.hsk : 0); + regmap_update_bits(domain->regmap, offset, domain->bits.pxx, domain->bits.pxx); @@ -179,6 +199,10 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), GPC_PGC_CTRL_PCR, 0); + /* Disable reset clocks for all devices in the domain */ + for (i = 0; i < domain->num_clks; i++) + clk_disable_unprepare(domain->clk[i]); + if (has_regulator && !on) { int err; @@ -328,6 +352,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { .bits = { .pxx = IMX8M_GPU_SW_Pxx_REQ, .map = IMX8M_GPU_A53_DOMAIN, + .hsk = IMX8M_GPU_HSK_PWRDNREQN, }, .pgc = IMX8M_PGC_GPU, }, @@ -339,6 +364,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { .bits = { .pxx = IMX8M_VPU_SW_Pxx_REQ, .map = IMX8M_VPU_A53_DOMAIN, + .hsk = IMX8M_VPU_HSK_PWRDNREQN, }, .pgc = IMX8M_PGC_VPU, }, @@ -350,6 +376,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { .bits = { .pxx = IMX8M_DISP_SW_Pxx_REQ, .map = IMX8M_DISP_A53_DOMAIN, + .hsk = IMX8M_DISP_HSK_PWRDNREQN, }, .pgc = IMX8M_PGC_DISP, }, @@ -390,7 +417,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { static const struct regmap_range imx8m_yes_ranges[] = { regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_M4_PU_PDN_FLG), + GPC_PU_PWRHSK), regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), GPC_PGC_SR(IMX8M_PGC_MIPI)), regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), @@ -426,6 +453,41 @@ static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { .reg_access_table = &imx8m_access_table, }; +static int imx_pgc_get_clocks(struct imx_pgc_domain *domain) +{ + int i, ret; + + for (i = 0; ; i++) { + struct clk *clk = of_clk_get(domain->dev->of_node, i); + if (IS_ERR(clk)) + break; + if (i >= GPC_CLK_MAX) { + dev_err(domain->dev, "more than %d clocks\n", + GPC_CLK_MAX); + ret = -EINVAL; + goto clk_err; + } + domain->clk[i] = clk; + } + domain->num_clks = i; + + return 0; + +clk_err: + while (i--) + clk_put(domain->clk[i]); + + return ret; +} + +static void imx_pgc_put_clocks(struct imx_pgc_domain *domain) +{ + int i; + + for (i = domain->num_clks - 1; i >= 0; i--) + clk_put(domain->clk[i]); +} + static int imx_pgc_domain_probe(struct platform_device *pdev) { struct imx_pgc_domain *domain = pdev->dev.platform_data; @@ -445,9 +507,17 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) domain->voltage, domain->voltage); } + ret = imx_pgc_get_clocks(domain); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(domain->dev, "Failed to get domain's clocks\n"); + return ret; + } + ret = pm_genpd_init(&domain->genpd, NULL, true); if (ret) { dev_err(domain->dev, "Failed to init power domain\n"); + imx_pgc_put_clocks(domain); return ret; } @@ -456,6 +526,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) if (ret) { dev_err(domain->dev, "Failed to add genpd provider\n"); pm_genpd_remove(&domain->genpd); + imx_pgc_put_clocks(domain); } return ret; @@ -467,6 +538,7 @@ static int imx_pgc_domain_remove(struct platform_device *pdev) of_genpd_del_provider(domain->dev->of_node); pm_genpd_remove(&domain->genpd); + imx_pgc_put_clocks(domain); return 0; } diff --git a/include/linux/firmware/imx/svc/misc.h b/include/linux/firmware/imx/svc/misc.h index e21c49aba92f..031dd4d3c766 100644 --- a/include/linux/firmware/imx/svc/misc.h +++ b/include/linux/firmware/imx/svc/misc.h @@ -52,4 +52,7 @@ int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource, int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, u8 ctrl, u32 *val); +int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, + bool enable, u64 phys_addr); + #endif /* _SC_MISC_API_H */ |