mirror of
https://github.com/torvalds/linux.git
synced 2026-05-26 08:02:27 +02:00
i.MX clock update for 5.11:
- A series from Abel Vesa to improve clk-gate2 driver and make it more flexible. - A patch set from Dong Aisheng to add a new two cells binding for SCU clocks, so that IMX SCU based platforms like MX8QM and MX8QXP can be supported with SS (Subsystems). - Drop of_match_ptr from of_device_id table for i.MX8 clock drivers, as they can only be probed from device tree. - Other small cosmetic changes. -----BEGIN PGP SIGNATURE----- iQFIBAABCgAyFiEEFmJXigPl4LoGSz08UFdYWoewfM4FAl/GDiEUHHNoYXduZ3Vv QGtlcm5lbC5vcmcACgkQUFdYWoewfM4KaQf+LZOSdtzmWzjtvum3coOE+THVWYXz hueT2RQjR8pCdBc6IIQi+2vPak5cvy+fazDl0NAXWr/OryEBw4fNPbDzYkky2gdY HTslQYHrj8dufUN4tZ2yhofE0fqjgGkdcVczIzdbrodoj+cHjLKXc9GlwFy2hbi8 U9sUWYisrSFXCO+vQrheJrEcnUpnawfbKrl24Rpg0b1RVHC1s0rIYix3IyMeGm0P q7h+rPtIMmaX86hfuSLxo2GdoGcsLNzRVSglz1Csndt/lfhWCfniAvsTNHnKWRDH paEPMRUMwLSgOfPc4tpR8v0GjWRjOPmAk53lkncvW3ysa+o5ScIXycE1WQ== =TiqG -----END PGP SIGNATURE----- Merge tag 'clk-imx-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into clk-imx Pull i.MX clk driver updates from Shawn Guo: - A series from Abel Vesa to improve clk-gate2 driver and make it more flexible. - A patch set from Dong Aisheng to add a new two cells binding for SCU clocks, so that IMX SCU based platforms like MX8QM and MX8QXP can be supported with SS (Subsystems). - Drop of_match_ptr from of_device_id table for i.MX8 clock drivers, as they can only be probed from device tree. - Other small cosmetic changes. * tag 'clk-imx-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux: (24 commits) clk: imx: scu: remove the calling of device_is_bound clk: imx: scu: Make pd_np with static keyword clk: imx8mq: drop of_match_ptr from of_device_id table clk: imx8mp: drop of_match_ptr from of_device_id table clk: imx8mn: drop of_match_ptr from of_device_id table clk: imx8mm: drop of_match_ptr from of_device_id table clk: imx: gate2: Remove unused variable ret clk: imx: gate2: Add locking in is_enabled op clk: imx: gate2: Add cgr_mask for more flexible number of control bits clk: imx: gate2: Check if clock is enabled against cgr_val clk: imx: gate2: Keep the register writing in on place clk: imx: gate2: Remove the IMX_CLK_GATE2_SINGLE_BIT special case clk: imx: scu: fix build break when compiled as modules clk: imx: remove redundant assignment to pointer np clk: imx: remove unneeded semicolon clk: imx: lpcg: add suspend/resume support clk: imx: clk-imx8qxp-lpcg: add runtime pm support clk: imx: lpcg: allow lpcg clk to take device pointer clk: imx: imx8qxp-lpcg: add parsing clocks from device tree clk: imx: scu: add suspend/resume support ...
This commit is contained in:
commit
958879d0d7
|
|
@ -30,6 +30,7 @@ struct clk_gate2 {
|
|||
void __iomem *reg;
|
||||
u8 bit_idx;
|
||||
u8 cgr_val;
|
||||
u8 cgr_mask;
|
||||
u8 flags;
|
||||
spinlock_t *lock;
|
||||
unsigned int *share_count;
|
||||
|
|
@ -37,37 +38,38 @@ struct clk_gate2 {
|
|||
|
||||
#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw)
|
||||
|
||||
static int clk_gate2_enable(struct clk_hw *hw)
|
||||
static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(gate->cgr_mask << gate->bit_idx);
|
||||
if (enable)
|
||||
reg |= (gate->cgr_val & gate->cgr_mask) << gate->bit_idx;
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
static int clk_gate2_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
||||
if (gate->share_count && (*gate->share_count)++ > 0)
|
||||
goto out;
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) {
|
||||
ret = clk_gate_ops.enable(hw);
|
||||
} else {
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
reg |= gate->cgr_val << gate->bit_idx;
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
clk_gate2_do_shared_clks(hw, true);
|
||||
out:
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_gate2_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
|
@ -79,23 +81,17 @@ static void clk_gate2_disable(struct clk_hw *hw)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) {
|
||||
clk_gate_ops.disable(hw);
|
||||
} else {
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
clk_gate2_do_shared_clks(hw, false);
|
||||
out:
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
}
|
||||
|
||||
static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx)
|
||||
static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx,
|
||||
u8 cgr_val, u8 cgr_mask)
|
||||
{
|
||||
u32 val = readl(reg);
|
||||
|
||||
if (((val >> bit_idx) & 1) == 1)
|
||||
if (((val >> bit_idx) & cgr_mask) == cgr_val)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
|
@ -104,29 +100,28 @@ static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx)
|
|||
static int clk_gate2_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT)
|
||||
return clk_gate_ops.is_enabled(hw);
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
||||
return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx);
|
||||
ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx,
|
||||
gate->cgr_val, gate->cgr_mask);
|
||||
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clk_gate2_disable_unused(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
||||
if (!gate->share_count || *gate->share_count == 0) {
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
if (!gate->share_count || *gate->share_count == 0)
|
||||
clk_gate2_do_shared_clks(hw, false);
|
||||
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
}
|
||||
|
|
@ -140,7 +135,7 @@ static const struct clk_ops clk_gate2_ops = {
|
|||
|
||||
struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, u8 cgr_val,
|
||||
void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask,
|
||||
u8 clk_gate2_flags, spinlock_t *lock,
|
||||
unsigned int *share_count)
|
||||
{
|
||||
|
|
@ -157,6 +152,7 @@ struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
|
|||
gate->reg = reg;
|
||||
gate->bit_idx = bit_idx;
|
||||
gate->cgr_val = cgr_val;
|
||||
gate->cgr_mask = cgr_mask;
|
||||
gate->flags = clk_gate2_flags;
|
||||
gate->lock = lock;
|
||||
gate->share_count = share_count;
|
||||
|
|
|
|||
|
|
@ -653,7 +653,7 @@ static struct platform_driver imx8mm_clk_driver = {
|
|||
* reloading the driver will crash or break devices.
|
||||
*/
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = of_match_ptr(imx8mm_clk_of_match),
|
||||
.of_match_table = imx8mm_clk_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx8mm_clk_driver);
|
||||
|
|
|
|||
|
|
@ -604,7 +604,7 @@ static struct platform_driver imx8mn_clk_driver = {
|
|||
* reloading the driver will crash or break devices.
|
||||
*/
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = of_match_ptr(imx8mn_clk_of_match),
|
||||
.of_match_table = imx8mn_clk_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx8mn_clk_driver);
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ static struct clk **uart_clks[ARRAY_SIZE(uart_clk_ids) + 1];
|
|||
static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *np;
|
||||
void __iomem *anatop_base, *ccm_base;
|
||||
int i;
|
||||
|
||||
|
|
@ -763,7 +763,7 @@ static struct platform_driver imx8mp_clk_driver = {
|
|||
* reloading the driver will crash or break devices.
|
||||
*/
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = of_match_ptr(imx8mp_clk_of_match),
|
||||
.of_match_table = imx8mp_clk_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx8mp_clk_driver);
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ static struct platform_driver imx8mq_clk_driver = {
|
|||
* reloading the driver will crash or break devices.
|
||||
*/
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = of_match_ptr(imx8mq_clk_of_match),
|
||||
.of_match_table = imx8mq_clk_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx8mq_clk_driver);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
|
|
@ -157,6 +159,135 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
|
|||
.num_max = IMX_LSIO_LPCG_CLK_END,
|
||||
};
|
||||
|
||||
#define IMX_LPCG_MAX_CLKS 8
|
||||
|
||||
static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
struct clk_hw_onecell_data *hw_data = data;
|
||||
unsigned int idx = clkspec->args[0] / 4;
|
||||
|
||||
if (idx >= hw_data->num) {
|
||||
pr_err("%s: invalid index %u\n", __func__, idx);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return hw_data->hws[idx];
|
||||
}
|
||||
|
||||
static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev,
|
||||
struct device_node *np)
|
||||
{
|
||||
const char *output_names[IMX_LPCG_MAX_CLKS];
|
||||
const char *parent_names[IMX_LPCG_MAX_CLKS];
|
||||
unsigned int bit_offset[IMX_LPCG_MAX_CLKS];
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clk_hws;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int count;
|
||||
int idx;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg"))
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
count = of_property_count_u32_elems(np, "clock-indices");
|
||||
if (count < 0) {
|
||||
dev_err(&pdev->dev, "failed to count clocks\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* A trick here is that we set the num of clks to the MAX instead
|
||||
* of the count from clock-indices because one LPCG supports up to
|
||||
* 8 clock outputs which each of them is fixed to 4 bits. Then we can
|
||||
* easily get the clock by clk-indices (bit-offset) / 4.
|
||||
* And the cost is very limited few pointers.
|
||||
*/
|
||||
|
||||
clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws,
|
||||
IMX_LPCG_MAX_CLKS), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data->num = IMX_LPCG_MAX_CLKS;
|
||||
clk_hws = clk_data->hws;
|
||||
|
||||
ret = of_property_read_u32_array(np, "clock-indices", bit_offset,
|
||||
count);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to read clock-indices\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_clk_parent_fill(np, parent_names, count);
|
||||
if (ret != count) {
|
||||
dev_err(&pdev->dev, "failed to get clock parent names\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
ret = of_property_read_string_array(np, "clock-output-names",
|
||||
output_names, count);
|
||||
if (ret != count) {
|
||||
dev_err(&pdev->dev, "failed to read clock-output-names\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
idx = bit_offset[i] / 4;
|
||||
if (idx > IMX_LPCG_MAX_CLKS) {
|
||||
dev_warn(&pdev->dev, "invalid bit offset of clock %d\n",
|
||||
i);
|
||||
ret = -EINVAL;
|
||||
goto unreg;
|
||||
}
|
||||
|
||||
clk_hws[idx] = imx_clk_lpcg_scu_dev(&pdev->dev, output_names[i],
|
||||
parent_names[i], 0, base,
|
||||
bit_offset[i], false);
|
||||
if (IS_ERR(clk_hws[idx])) {
|
||||
dev_warn(&pdev->dev, "failed to register clock %d\n",
|
||||
idx);
|
||||
ret = PTR_ERR(clk_hws[idx]);
|
||||
goto unreg;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get,
|
||||
clk_data);
|
||||
if (ret)
|
||||
goto unreg;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
unreg:
|
||||
while (--i >= 0) {
|
||||
idx = bit_offset[i] / 4;
|
||||
if (clk_hws[idx])
|
||||
imx_clk_lpcg_scu_unregister(clk_hws[idx]);
|
||||
}
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
|
@ -167,8 +298,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct clk_hw **clks;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* try new binding to parse clocks from device tree first */
|
||||
ret = imx_lpcg_parse_clks_from_dt(pdev, np);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ss_lpcg = of_device_get_match_data(dev);
|
||||
if (!ss_lpcg)
|
||||
return -ENODEV;
|
||||
|
|
@ -219,6 +356,7 @@ static const struct of_device_id imx8qxp_lpcg_match[] = {
|
|||
{ .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, },
|
||||
{ .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, },
|
||||
{ .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, },
|
||||
{ .compatible = "fsl,imx8qxp-lpcg", NULL },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
@ -226,6 +364,7 @@ static struct platform_driver imx8qxp_lpcg_clk_driver = {
|
|||
.driver = {
|
||||
.name = "imx8qxp-lpcg-clk",
|
||||
.of_match_table = imx8qxp_lpcg_match,
|
||||
.pm = &imx_clk_lpcg_scu_pm_ops,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = imx8qxp_lpcg_clk_probe,
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
|
|||
struct device_node *ccm_node = pdev->dev.of_node;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct clk_hw **clks;
|
||||
u32 clk_cells;
|
||||
int ret, i;
|
||||
|
||||
ret = imx_clk_scu_init();
|
||||
ret = imx_clk_scu_init(ccm_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -33,6 +34,9 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
|
|||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u32(ccm_node, "#clock-cells", &clk_cells))
|
||||
return -EINVAL;
|
||||
|
||||
clk_data->num = IMX_SCU_CLK_END;
|
||||
clks = clk_data->hws;
|
||||
|
||||
|
|
@ -55,78 +59,78 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
|
|||
clks[IMX_LSIO_BUS_CLK] = clk_hw_register_fixed_rate(NULL, "lsio_bus_clk_root", NULL, 0, 100000000);
|
||||
|
||||
/* ARM core */
|
||||
clks[IMX_A35_CLK] = imx_clk_scu("a35_clk", IMX_SC_R_A35, IMX_SC_PM_CLK_CPU);
|
||||
clks[IMX_A35_CLK] = imx_clk_scu("a35_clk", IMX_SC_R_A35, IMX_SC_PM_CLK_CPU, clk_cells);
|
||||
|
||||
/* LSIO SS */
|
||||
clks[IMX_LSIO_PWM0_CLK] = imx_clk_scu("pwm0_clk", IMX_SC_R_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM1_CLK] = imx_clk_scu("pwm1_clk", IMX_SC_R_PWM_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM2_CLK] = imx_clk_scu("pwm2_clk", IMX_SC_R_PWM_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM3_CLK] = imx_clk_scu("pwm3_clk", IMX_SC_R_PWM_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM4_CLK] = imx_clk_scu("pwm4_clk", IMX_SC_R_PWM_4, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM5_CLK] = imx_clk_scu("pwm5_clk", IMX_SC_R_PWM_5, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM6_CLK] = imx_clk_scu("pwm6_clk", IMX_SC_R_PWM_6, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM7_CLK] = imx_clk_scu("pwm7_clk", IMX_SC_R_PWM_7, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_GPT0_CLK] = imx_clk_scu("gpt0_clk", IMX_SC_R_GPT_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_GPT1_CLK] = imx_clk_scu("gpt1_clk", IMX_SC_R_GPT_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_GPT2_CLK] = imx_clk_scu("gpt2_clk", IMX_SC_R_GPT_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_GPT3_CLK] = imx_clk_scu("gpt3_clk", IMX_SC_R_GPT_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_GPT4_CLK] = imx_clk_scu("gpt4_clk", IMX_SC_R_GPT_4, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_FSPI0_CLK] = imx_clk_scu("fspi0_clk", IMX_SC_R_FSPI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_FSPI1_CLK] = imx_clk_scu("fspi1_clk", IMX_SC_R_FSPI_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_LSIO_PWM0_CLK] = imx_clk_scu("pwm0_clk", IMX_SC_R_PWM_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM1_CLK] = imx_clk_scu("pwm1_clk", IMX_SC_R_PWM_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM2_CLK] = imx_clk_scu("pwm2_clk", IMX_SC_R_PWM_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM3_CLK] = imx_clk_scu("pwm3_clk", IMX_SC_R_PWM_3, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM4_CLK] = imx_clk_scu("pwm4_clk", IMX_SC_R_PWM_4, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM5_CLK] = imx_clk_scu("pwm5_clk", IMX_SC_R_PWM_5, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM6_CLK] = imx_clk_scu("pwm6_clk", IMX_SC_R_PWM_6, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_PWM7_CLK] = imx_clk_scu("pwm7_clk", IMX_SC_R_PWM_7, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_GPT0_CLK] = imx_clk_scu("gpt0_clk", IMX_SC_R_GPT_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_GPT1_CLK] = imx_clk_scu("gpt1_clk", IMX_SC_R_GPT_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_GPT2_CLK] = imx_clk_scu("gpt2_clk", IMX_SC_R_GPT_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_GPT3_CLK] = imx_clk_scu("gpt3_clk", IMX_SC_R_GPT_3, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_GPT4_CLK] = imx_clk_scu("gpt4_clk", IMX_SC_R_GPT_4, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_FSPI0_CLK] = imx_clk_scu("fspi0_clk", IMX_SC_R_FSPI_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_LSIO_FSPI1_CLK] = imx_clk_scu("fspi1_clk", IMX_SC_R_FSPI_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
|
||||
/* ADMA SS */
|
||||
clks[IMX_ADMA_UART0_CLK] = imx_clk_scu("uart0_clk", IMX_SC_R_UART_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_UART1_CLK] = imx_clk_scu("uart1_clk", IMX_SC_R_UART_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_UART2_CLK] = imx_clk_scu("uart2_clk", IMX_SC_R_UART_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_UART3_CLK] = imx_clk_scu("uart3_clk", IMX_SC_R_UART_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_SPI0_CLK] = imx_clk_scu("spi0_clk", IMX_SC_R_SPI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_SPI1_CLK] = imx_clk_scu("spi1_clk", IMX_SC_R_SPI_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_SPI2_CLK] = imx_clk_scu("spi2_clk", IMX_SC_R_SPI_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_SPI3_CLK] = imx_clk_scu("spi3_clk", IMX_SC_R_SPI_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_CAN0_CLK] = imx_clk_scu("can0_clk", IMX_SC_R_CAN_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_I2C0_CLK] = imx_clk_scu("i2c0_clk", IMX_SC_R_I2C_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_I2C1_CLK] = imx_clk_scu("i2c1_clk", IMX_SC_R_I2C_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_I2C2_CLK] = imx_clk_scu("i2c2_clk", IMX_SC_R_I2C_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_I2C3_CLK] = imx_clk_scu("i2c3_clk", IMX_SC_R_I2C_3, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_FTM0_CLK] = imx_clk_scu("ftm0_clk", IMX_SC_R_FTM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_FTM1_CLK] = imx_clk_scu("ftm1_clk", IMX_SC_R_FTM_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_ADC0_CLK] = imx_clk_scu("adc0_clk", IMX_SC_R_ADC_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_PWM_CLK] = imx_clk_scu("pwm_clk", IMX_SC_R_LCD_0_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_LCD_CLK] = imx_clk_scu("lcd_clk", IMX_SC_R_LCD_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_ADMA_UART0_CLK] = imx_clk_scu("uart0_clk", IMX_SC_R_UART_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_UART1_CLK] = imx_clk_scu("uart1_clk", IMX_SC_R_UART_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_UART2_CLK] = imx_clk_scu("uart2_clk", IMX_SC_R_UART_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_UART3_CLK] = imx_clk_scu("uart3_clk", IMX_SC_R_UART_3, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_SPI0_CLK] = imx_clk_scu("spi0_clk", IMX_SC_R_SPI_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_SPI1_CLK] = imx_clk_scu("spi1_clk", IMX_SC_R_SPI_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_SPI2_CLK] = imx_clk_scu("spi2_clk", IMX_SC_R_SPI_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_SPI3_CLK] = imx_clk_scu("spi3_clk", IMX_SC_R_SPI_3, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_CAN0_CLK] = imx_clk_scu("can0_clk", IMX_SC_R_CAN_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_I2C0_CLK] = imx_clk_scu("i2c0_clk", IMX_SC_R_I2C_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_I2C1_CLK] = imx_clk_scu("i2c1_clk", IMX_SC_R_I2C_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_I2C2_CLK] = imx_clk_scu("i2c2_clk", IMX_SC_R_I2C_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_I2C3_CLK] = imx_clk_scu("i2c3_clk", IMX_SC_R_I2C_3, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_FTM0_CLK] = imx_clk_scu("ftm0_clk", IMX_SC_R_FTM_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_FTM1_CLK] = imx_clk_scu("ftm1_clk", IMX_SC_R_FTM_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_ADC0_CLK] = imx_clk_scu("adc0_clk", IMX_SC_R_ADC_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_PWM_CLK] = imx_clk_scu("pwm_clk", IMX_SC_R_LCD_0_PWM_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_ADMA_LCD_CLK] = imx_clk_scu("lcd_clk", IMX_SC_R_LCD_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
|
||||
/* Connectivity */
|
||||
clks[IMX_CONN_SDHC0_CLK] = imx_clk_scu("sdhc0_clk", IMX_SC_R_SDHC_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_SDHC1_CLK] = imx_clk_scu("sdhc1_clk", IMX_SC_R_SDHC_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_SDHC2_CLK] = imx_clk_scu("sdhc2_clk", IMX_SC_R_SDHC_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_ENET0_ROOT_CLK] = imx_clk_scu("enet0_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_ENET0_BYPASS_CLK] = imx_clk_scu("enet0_bypass_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_BYPASS);
|
||||
clks[IMX_CONN_ENET0_RGMII_CLK] = imx_clk_scu("enet0_rgmii_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX_CONN_ENET1_ROOT_CLK] = imx_clk_scu("enet1_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_ENET1_BYPASS_CLK] = imx_clk_scu("enet1_bypass_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_BYPASS);
|
||||
clks[IMX_CONN_ENET1_RGMII_CLK] = imx_clk_scu("enet1_rgmii_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX_CONN_GPMI_BCH_IO_CLK] = imx_clk_scu("gpmi_io_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_MST_BUS);
|
||||
clks[IMX_CONN_GPMI_BCH_CLK] = imx_clk_scu("gpmi_bch_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_USB2_ACLK] = imx_clk_scu("usb3_aclk_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CONN_USB2_BUS_CLK] = imx_clk_scu("usb3_bus_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MST_BUS);
|
||||
clks[IMX_CONN_USB2_LPM_CLK] = imx_clk_scu("usb3_lpm_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MISC);
|
||||
clks[IMX_CONN_SDHC0_CLK] = imx_clk_scu("sdhc0_clk", IMX_SC_R_SDHC_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_SDHC1_CLK] = imx_clk_scu("sdhc1_clk", IMX_SC_R_SDHC_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_SDHC2_CLK] = imx_clk_scu("sdhc2_clk", IMX_SC_R_SDHC_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_ENET0_ROOT_CLK] = imx_clk_scu("enet0_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_ENET0_BYPASS_CLK] = imx_clk_scu("enet0_bypass_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_BYPASS, clk_cells);
|
||||
clks[IMX_CONN_ENET0_RGMII_CLK] = imx_clk_scu("enet0_rgmii_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0, clk_cells);
|
||||
clks[IMX_CONN_ENET1_ROOT_CLK] = imx_clk_scu("enet1_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_ENET1_BYPASS_CLK] = imx_clk_scu("enet1_bypass_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_BYPASS, clk_cells);
|
||||
clks[IMX_CONN_ENET1_RGMII_CLK] = imx_clk_scu("enet1_rgmii_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0, clk_cells);
|
||||
clks[IMX_CONN_GPMI_BCH_IO_CLK] = imx_clk_scu("gpmi_io_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_MST_BUS, clk_cells);
|
||||
clks[IMX_CONN_GPMI_BCH_CLK] = imx_clk_scu("gpmi_bch_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_USB2_ACLK] = imx_clk_scu("usb3_aclk_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CONN_USB2_BUS_CLK] = imx_clk_scu("usb3_bus_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MST_BUS, clk_cells);
|
||||
clks[IMX_CONN_USB2_LPM_CLK] = imx_clk_scu("usb3_lpm_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MISC, clk_cells);
|
||||
|
||||
/* Display controller SS */
|
||||
clks[IMX_DC0_DISP0_CLK] = imx_clk_scu("dc0_disp0_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0);
|
||||
clks[IMX_DC0_DISP1_CLK] = imx_clk_scu("dc0_disp1_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1);
|
||||
clks[IMX_DC0_DISP0_CLK] = imx_clk_scu("dc0_disp0_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0, clk_cells);
|
||||
clks[IMX_DC0_DISP1_CLK] = imx_clk_scu("dc0_disp1_clk", IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1, clk_cells);
|
||||
|
||||
/* MIPI-LVDS SS */
|
||||
clks[IMX_MIPI0_I2C0_CLK] = imx_clk_scu("mipi0_i2c0_clk", IMX_SC_R_MIPI_0_I2C_0, IMX_SC_PM_CLK_MISC2);
|
||||
clks[IMX_MIPI0_I2C1_CLK] = imx_clk_scu("mipi0_i2c1_clk", IMX_SC_R_MIPI_0_I2C_1, IMX_SC_PM_CLK_MISC2);
|
||||
clks[IMX_MIPI0_I2C0_CLK] = imx_clk_scu("mipi0_i2c0_clk", IMX_SC_R_MIPI_0_I2C_0, IMX_SC_PM_CLK_MISC2, clk_cells);
|
||||
clks[IMX_MIPI0_I2C1_CLK] = imx_clk_scu("mipi0_i2c1_clk", IMX_SC_R_MIPI_0_I2C_1, IMX_SC_PM_CLK_MISC2, clk_cells);
|
||||
|
||||
/* MIPI CSI SS */
|
||||
clks[IMX_CSI0_CORE_CLK] = imx_clk_scu("mipi_csi0_core_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CSI0_ESC_CLK] = imx_clk_scu("mipi_csi0_esc_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_MISC);
|
||||
clks[IMX_CSI0_I2C0_CLK] = imx_clk_scu("mipi_csi0_i2c0_clk", IMX_SC_R_CSI_0_I2C_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CSI0_PWM0_CLK] = imx_clk_scu("mipi_csi0_pwm0_clk", IMX_SC_R_CSI_0_PWM_0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_CSI0_CORE_CLK] = imx_clk_scu("mipi_csi0_core_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CSI0_ESC_CLK] = imx_clk_scu("mipi_csi0_esc_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_MISC, clk_cells);
|
||||
clks[IMX_CSI0_I2C0_CLK] = imx_clk_scu("mipi_csi0_i2c0_clk", IMX_SC_R_CSI_0_I2C_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_CSI0_PWM0_CLK] = imx_clk_scu("mipi_csi0_pwm0_clk", IMX_SC_R_CSI_0_PWM_0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
|
||||
/* GPU SS */
|
||||
clks[IMX_GPU0_CORE_CLK] = imx_clk_scu("gpu_core0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_PER);
|
||||
clks[IMX_GPU0_SHADER_CLK] = imx_clk_scu("gpu_shader0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_MISC);
|
||||
clks[IMX_GPU0_CORE_CLK] = imx_clk_scu("gpu_core0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_PER, clk_cells);
|
||||
clks[IMX_GPU0_SHADER_CLK] = imx_clk_scu("gpu_shader0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_MISC, clk_cells);
|
||||
|
||||
for (i = 0; i < clk_data->num; i++) {
|
||||
if (IS_ERR(clks[i]))
|
||||
|
|
@ -134,7 +138,19 @@ static int imx8qxp_clk_probe(struct platform_device *pdev)
|
|||
i, PTR_ERR(clks[i]));
|
||||
}
|
||||
|
||||
return of_clk_add_hw_provider(ccm_node, of_clk_hw_onecell_get, clk_data);
|
||||
if (clk_cells == 2) {
|
||||
ret = of_clk_add_hw_provider(ccm_node, imx_scu_of_clk_src_get, imx_scu_clks);
|
||||
if (ret)
|
||||
imx_clk_scu_unregister();
|
||||
} else {
|
||||
/*
|
||||
* legacy binding code path doesn't unregister here because
|
||||
* it will be removed later.
|
||||
*/
|
||||
ret = of_clk_add_hw_provider(ccm_node, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8qxp_match[] = {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ struct clk_lpcg_scu {
|
|||
void __iomem *reg;
|
||||
u8 bit_idx;
|
||||
bool hw_gate;
|
||||
|
||||
/* for state save&restore */
|
||||
u32 state;
|
||||
};
|
||||
|
||||
#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
|
||||
|
|
@ -81,9 +84,9 @@ static const struct clk_ops clk_lpcg_scu_ops = {
|
|||
.disable = clk_lpcg_scu_disable,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 bit_idx, bool hw_gate)
|
||||
struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, bool hw_gate)
|
||||
{
|
||||
struct clk_lpcg_scu *clk;
|
||||
struct clk_init_data init;
|
||||
|
|
@ -107,11 +110,53 @@ struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
|||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
kfree(clk);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (dev)
|
||||
dev_set_drvdata(dev, clk);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
|
||||
|
||||
clk_hw_unregister(&clk->hw);
|
||||
kfree(clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
|
||||
{
|
||||
struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
|
||||
|
||||
clk->state = readl_relaxed(clk->reg);
|
||||
dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
|
||||
{
|
||||
struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* FIXME: Sometimes writes don't work unless the CPU issues
|
||||
* them twice
|
||||
*/
|
||||
|
||||
writel(clk->state, clk->reg);
|
||||
writel(clk->state, clk->reg);
|
||||
dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
|
||||
imx_clk_lpcg_scu_resume)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
|
|||
__func__, name);
|
||||
kfree(pll);
|
||||
return ERR_PTR(-EINVAL);
|
||||
};
|
||||
}
|
||||
|
||||
pll->base = base;
|
||||
pll->hw.init = &init;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
#include <linux/arm-smccc.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-scu.h"
|
||||
|
|
@ -16,6 +20,21 @@
|
|||
#define IMX_SIP_SET_CPUFREQ 0x00
|
||||
|
||||
static struct imx_sc_ipc *ccm_ipc_handle;
|
||||
static struct device_node *pd_np;
|
||||
static struct platform_driver imx_clk_scu_driver;
|
||||
|
||||
struct imx_scu_clk_node {
|
||||
const char *name;
|
||||
u32 rsrc;
|
||||
u8 clk_type;
|
||||
const char * const *parents;
|
||||
int num_parents;
|
||||
|
||||
struct clk_hw *hw;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct list_head imx_scu_clks[IMX_SC_R_LAST];
|
||||
|
||||
/*
|
||||
* struct clk_scu - Description of one SCU clock
|
||||
|
|
@ -27,6 +46,10 @@ struct clk_scu {
|
|||
struct clk_hw hw;
|
||||
u16 rsrc_id;
|
||||
u8 clk_type;
|
||||
|
||||
/* for state save&restore */
|
||||
bool is_enabled;
|
||||
u32 rate;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -128,9 +151,28 @@ static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
|
|||
return container_of(hw, struct clk_scu, hw);
|
||||
}
|
||||
|
||||
int imx_clk_scu_init(void)
|
||||
int imx_clk_scu_init(struct device_node *np)
|
||||
{
|
||||
return imx_scu_get_handle(&ccm_ipc_handle);
|
||||
u32 clk_cells;
|
||||
int ret, i;
|
||||
|
||||
ret = imx_scu_get_handle(&ccm_ipc_handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
of_property_read_u32(np, "#clock-cells", &clk_cells);
|
||||
|
||||
if (clk_cells == 2) {
|
||||
for (i = 0; i < IMX_SC_R_LAST; i++)
|
||||
INIT_LIST_HEAD(&imx_scu_clks[i]);
|
||||
|
||||
/* pd_np will be used to attach power domains later */
|
||||
pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd");
|
||||
if (!pd_np)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return platform_driver_register(&imx_clk_scu_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -344,8 +386,9 @@ static const struct clk_ops clk_scu_cpu_ops = {
|
|||
.unprepare = clk_scu_unprepare,
|
||||
};
|
||||
|
||||
struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
|
||||
int num_parents, u32 rsrc_id, u8 clk_type)
|
||||
struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
|
||||
const char * const *parents, int num_parents,
|
||||
u32 rsrc_id, u8 clk_type)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_scu *clk;
|
||||
|
|
@ -379,11 +422,185 @@ struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
|
|||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
kfree(clk);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (dev)
|
||||
dev_set_drvdata(dev, clk);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
unsigned int rsrc = clkspec->args[0];
|
||||
unsigned int idx = clkspec->args[1];
|
||||
struct list_head *scu_clks = data;
|
||||
struct imx_scu_clk_node *clk;
|
||||
|
||||
list_for_each_entry(clk, &scu_clks[rsrc], node) {
|
||||
if (clk->clk_type == idx)
|
||||
return clk->hw;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static int imx_clk_scu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx_scu_clk_node *clk = dev_get_platdata(dev);
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret) {
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents,
|
||||
clk->rsrc, clk->clk_type);
|
||||
if (IS_ERR(hw)) {
|
||||
pm_runtime_disable(dev);
|
||||
return PTR_ERR(hw);
|
||||
}
|
||||
|
||||
clk->hw = hw;
|
||||
list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]);
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc,
|
||||
clk->clk_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
|
||||
{
|
||||
struct clk_scu *clk = dev_get_drvdata(dev);
|
||||
|
||||
clk->rate = clk_hw_get_rate(&clk->hw);
|
||||
clk->is_enabled = clk_hw_is_enabled(&clk->hw);
|
||||
|
||||
if (clk->rate)
|
||||
dev_dbg(dev, "save rate %d\n", clk->rate);
|
||||
|
||||
if (clk->is_enabled)
|
||||
dev_dbg(dev, "save enabled state\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx_clk_scu_resume(struct device *dev)
|
||||
{
|
||||
struct clk_scu *clk = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (clk->rate) {
|
||||
ret = clk_scu_set_rate(&clk->hw, clk->rate, 0);
|
||||
dev_dbg(dev, "restore rate %d %s\n", clk->rate,
|
||||
!ret ? "success" : "failed");
|
||||
}
|
||||
|
||||
if (clk->is_enabled) {
|
||||
ret = clk_scu_prepare(&clk->hw);
|
||||
dev_dbg(dev, "restore enabled state %s\n",
|
||||
!ret ? "success" : "failed");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops imx_clk_scu_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend,
|
||||
imx_clk_scu_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver imx_clk_scu_driver = {
|
||||
.driver = {
|
||||
.name = "imx-scu-clk",
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &imx_clk_scu_pm_ops,
|
||||
},
|
||||
.probe = imx_clk_scu_probe,
|
||||
};
|
||||
|
||||
static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
|
||||
{
|
||||
struct of_phandle_args genpdspec = {
|
||||
.np = pd_np,
|
||||
.args_count = 1,
|
||||
.args[0] = rsrc_id,
|
||||
};
|
||||
|
||||
if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 ||
|
||||
rsrc_id == IMX_SC_R_A72)
|
||||
return 0;
|
||||
|
||||
return of_genpd_add_device(&genpdspec, dev);
|
||||
}
|
||||
|
||||
struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
|
||||
const char * const *parents,
|
||||
int num_parents, u32 rsrc_id, u8 clk_type)
|
||||
{
|
||||
struct imx_scu_clk_node clk = {
|
||||
.name = name,
|
||||
.rsrc = rsrc_id,
|
||||
.clk_type = clk_type,
|
||||
.parents = parents,
|
||||
.num_parents = num_parents,
|
||||
};
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
|
||||
if (!pdev) {
|
||||
pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
|
||||
name, rsrc_id, clk_type);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(pdev, &clk, sizeof(clk));
|
||||
if (ret) {
|
||||
platform_device_put(pdev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
pdev->driver_override = "imx-scu-clk";
|
||||
|
||||
ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
|
||||
if (ret)
|
||||
pr_warn("%s: failed to attached the power domain %d\n",
|
||||
name, ret);
|
||||
|
||||
platform_device_add(pdev);
|
||||
|
||||
/* For API backwards compatiblilty, simply return NULL for success */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void imx_clk_scu_unregister(void)
|
||||
{
|
||||
struct imx_scu_clk_node *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMX_SC_R_LAST; i++) {
|
||||
list_for_each_entry(clk, &imx_scu_clks[i], node) {
|
||||
clk_hw_unregister(clk->hw);
|
||||
kfree(clk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,61 @@
|
|||
#define __IMX_CLK_SCU_H
|
||||
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
int imx_clk_scu_init(void);
|
||||
extern struct list_head imx_scu_clks[];
|
||||
extern const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops;
|
||||
|
||||
struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
|
||||
int num_parents, u32 rsrc_id, u8 clk_type);
|
||||
int imx_clk_scu_init(struct device_node *np);
|
||||
struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
|
||||
void *data);
|
||||
struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
|
||||
const char * const *parents,
|
||||
int num_parents, u32 rsrc_id, u8 clk_type);
|
||||
|
||||
struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
|
||||
const char * const *parents, int num_parents,
|
||||
u32 rsrc_id, u8 clk_type);
|
||||
|
||||
void imx_clk_scu_unregister(void);
|
||||
|
||||
struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, bool hw_gate);
|
||||
void imx_clk_lpcg_scu_unregister(struct clk_hw *hw);
|
||||
|
||||
static inline struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id,
|
||||
u8 clk_type)
|
||||
u8 clk_type, u8 clk_cells)
|
||||
{
|
||||
return __imx_clk_scu(name, NULL, 0, rsrc_id, clk_type);
|
||||
if (clk_cells == 2)
|
||||
return imx_clk_scu_alloc_dev(name, NULL, 0, rsrc_id, clk_type);
|
||||
else
|
||||
return __imx_clk_scu(NULL, name, NULL, 0, rsrc_id, clk_type);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const *parents,
|
||||
int num_parents, u32 rsrc_id, u8 clk_type)
|
||||
int num_parents, u32 rsrc_id, u8 clk_type,
|
||||
u8 clk_cells)
|
||||
{
|
||||
return __imx_clk_scu(name, parents, num_parents, rsrc_id, clk_type);
|
||||
if (clk_cells == 2)
|
||||
return imx_clk_scu_alloc_dev(name, parents, num_parents, rsrc_id, clk_type);
|
||||
else
|
||||
return __imx_clk_scu(NULL, name, parents, num_parents, rsrc_id, clk_type);
|
||||
}
|
||||
|
||||
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 bit_idx, bool hw_gate);
|
||||
static inline struct clk_hw *imx_clk_lpcg_scu_dev(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, bool hw_gate)
|
||||
{
|
||||
return __imx_clk_lpcg_scu(dev, name, parent_name, flags, reg,
|
||||
bit_idx, hw_gate);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 bit_idx, bool hw_gate)
|
||||
{
|
||||
return __imx_clk_lpcg_scu(NULL, name, parent_name, flags, reg,
|
||||
bit_idx, hw_gate);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define IMX_CLK_GATE2_SINGLE_BIT 1
|
||||
|
||||
extern spinlock_t imx_ccm_lock;
|
||||
|
||||
void imx_check_clocks(struct clk *clks[], unsigned int count);
|
||||
|
|
@ -68,9 +66,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll;
|
|||
to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step))
|
||||
|
||||
#define clk_register_gate2(dev, name, parent_name, flags, reg, bit_idx, \
|
||||
cgr_val, clk_gate_flags, lock, share_count) \
|
||||
cgr_val, cgr_mask, clk_gate_flags, lock, share_count) \
|
||||
to_clk(clk_hw_register_gate2(dev, name, parent_name, flags, reg, bit_idx, \
|
||||
cgr_val, clk_gate_flags, lock, share_count))
|
||||
cgr_val, cgr_mask, clk_gate_flags, lock, share_count))
|
||||
|
||||
#define imx_clk_pllv3(type, name, parent_name, base, div_mask) \
|
||||
to_clk(imx_clk_hw_pllv3(type, name, parent_name, base, div_mask))
|
||||
|
|
@ -198,7 +196,7 @@ struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name,
|
|||
|
||||
struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 bit_idx, u8 cgr_val,
|
||||
void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask,
|
||||
u8 clk_gate_flags, spinlock_t *lock,
|
||||
unsigned int *share_count);
|
||||
|
||||
|
|
@ -351,14 +349,14 @@ static inline struct clk_hw *imx_clk_hw_gate2(const char *name, const char *pare
|
|||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate2_flags(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift, unsigned long flags)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate2_shared(const char *name,
|
||||
|
|
@ -366,7 +364,7 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared(const char *name,
|
|||
unsigned int *share_count)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0x3, 0, &imx_ccm_lock, share_count);
|
||||
shift, 0x3, 0x3, 0, &imx_ccm_lock, share_count);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name,
|
||||
|
|
@ -374,7 +372,7 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name,
|
|||
unsigned int *share_count)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
|
||||
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0,
|
||||
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0x3, 0,
|
||||
&imx_ccm_lock, share_count);
|
||||
}
|
||||
|
||||
|
|
@ -384,16 +382,15 @@ static inline struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev,
|
|||
unsigned int *share_count)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
|
||||
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3,
|
||||
IMX_CLK_GATE2_SINGLE_BIT,
|
||||
&imx_ccm_lock, share_count);
|
||||
CLK_OPS_PARENT_ENABLE, reg, shift, 0x1,
|
||||
0x1, 0, &imx_ccm_lock, share_count);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate2_cgr(const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift, u8 cgr_val)
|
||||
{
|
||||
return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, cgr_val, 0, &imx_ccm_lock, NULL);
|
||||
shift, cgr_val, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate3(const char *name, const char *parent,
|
||||
|
|
@ -421,7 +418,7 @@ static inline struct clk_hw *imx_clk_hw_gate4(const char *name, const char *pare
|
|||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent,
|
||||
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
reg, shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate4_flags(const char *name,
|
||||
|
|
@ -430,7 +427,7 @@ static inline struct clk_hw *imx_clk_hw_gate4_flags(const char *name,
|
|||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent,
|
||||
flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
reg, shift, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
reg, shift, 0x3, 0x3, 0, &imx_ccm_lock, NULL);
|
||||
}
|
||||
|
||||
#define imx_clk_gate4_flags(name, parent, reg, shift, flags) \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user