i.MX drivers update for 5.1:
- Do not get GPCv2 driver depend on SOC_IMX8MQ since the driver is going to be used on more SoCs than just i.MX8MQ. - Add power domain information into SCU bindings document. - Add support of start/stop a CPU into imx firmware driver. - Support multiple address ranges per child node for imx-weim bus driver. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJcYi+0AAoJEFBXWFqHsHzOtdYH/jX/y0r7OvX5GVSrrnW8PlkC 3i2EQ0sGOxWOMqBegZIpFe9W7IddAetMf6praPGz/efHPnoVC4jO8Yqe4TZwQXLB vOxoN4G7NH9Al9RX11ce96kd/tUgVK/JHuQJ9fu+ogrprLpAS3w1sbIkidOMSF3M /5VrmFCxUNOPwTHRnyjw8QsyypKnKEBu0jA8sgyUmg99ii6rqQ5OKh1vgKf62J0f 1BbJLalI0CxGcYVQxYBy2ML37+XxCcN9Vl8FxiI4tVRtZox8/YkU9V5vEgVWN2fS ZWfgT9w7cJxI5x8Em8Cxe4zcyVdbn5w26lXc6WbXazIncm206Ps+DIeHkQI9jPA= =8C2m -----END PGP SIGNATURE----- Merge tag 'imx-drivers-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into arm/drivers i.MX drivers update for 5.1: - Do not get GPCv2 driver depend on SOC_IMX8MQ since the driver is going to be used on more SoCs than just i.MX8MQ. - Add power domain information into SCU bindings document. - Add support of start/stop a CPU into imx firmware driver. - Support multiple address ranges per child node for imx-weim bus driver. * tag 'imx-drivers-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux: firmware: imx: Add support to start/stop a CPU soc: imx: Break dependency on SOC_IMX8MQ for GPCv2 firmware: imx: scu-pd: add fallback compatible string support dt-bindings: fsl: scu: add imx8qm scu power domain support dt-bindings: fsl: scu: add fallback compatible string for power domain bus: imx-weim: guard against timing configuration conflicts bus: imx-weim: support multiple address ranges per child node dt-bindings: bus: imx-weim: document multiple address ranges per child node soc: imx: gpcv2: handle reset clocks soc: imx: gpcv2: handle additional power-down bits in handshake register Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
c9235d9996
|
@ -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>;
|
||||
};
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */ }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue