Commit 699eda28 authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-tegra', 'clk-imx', 'clk-sifive', 'clk-mediatek' and 'clk-summary' into clk-next

 - Support for SiFive FU740 PRCI
 - Add hardware enable information to clk_summary debugfs

* clk-tegra:
  clk: tegra: Fix duplicated SE clock entry
  clk: tegra: bpmp: Clamp clock rates on requests
  clk: tegra: Do not return 0 on failure

* clk-imx: (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
  ...

* clk-sifive:
  clk: sifive: Add clock enable and disable ops
  clk: sifive: Fix the wrong bit field shift
  clk: sifive: Add a driver for the SiFive FU740 PRCI IP block
  clk: sifive: Use common name for prci configuration
  clk: sifive: Extract prci core to common base
  dt-bindings: fu740: prci: add YAML documentation for the FU740 PRCI

* clk-mediatek:
  clk: mediatek: Make mtk_clk_register_mux() a static function

* clk-summary:
  clk: Add hardware-enable column to clk summary
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2020 SiFive, Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/sifive/fu740-prci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: SiFive FU740 Power Reset Clock Interrupt Controller (PRCI)
maintainers:
- Zong Li <zong.li@sifive.com>
- Paul Walmsley <paul.walmsley@sifive.com>
description:
On the FU740 family of SoCs, most system-wide clock and reset integration
is via the PRCI IP block.
The clock consumer should specify the desired clock via the clock ID
macros defined in include/dt-bindings/clock/sifive-fu740-prci.h.
These macros begin with PRCI_CLK_.
The hfclk and rtcclk nodes are required, and represent physical
crystals or resonators located on the PCB. These nodes should be present
underneath /, rather than /soc.
properties:
compatible:
const: sifive,fu740-c000-prci
reg:
maxItems: 1
clocks:
items:
- description: high frequency clock.
- description: RTL clock.
clock-names:
items:
- const: hfclk
- const: rtcclk
"#clock-cells":
const: 1
required:
- compatible
- reg
- clocks
- "#clock-cells"
additionalProperties: false
examples:
- |
prci: clock-controller@10000000 {
compatible = "sifive,fu740-c000-prci";
reg = <0x10000000 0x1000>;
clocks = <&hfclk>, <&rtcclk>;
#clock-cells = <1>;
};
...@@ -5,7 +5,7 @@ config SOC_SIFIVE ...@@ -5,7 +5,7 @@ config SOC_SIFIVE
select SERIAL_SIFIVE if TTY select SERIAL_SIFIVE if TTY
select SERIAL_SIFIVE_CONSOLE if TTY select SERIAL_SIFIVE_CONSOLE if TTY
select CLK_SIFIVE select CLK_SIFIVE
select CLK_SIFIVE_FU540_PRCI select CLK_SIFIVE_PRCI
select SIFIVE_PLIC select SIFIVE_PLIC
help help
This enables support for SiFive SoC platform hardware. This enables support for SiFive SoC platform hardware.
......
...@@ -2931,7 +2931,14 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, ...@@ -2931,7 +2931,14 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
else else
seq_puts(s, "-----"); seq_puts(s, "-----");
seq_printf(s, " %6d\n", clk_core_get_scaled_duty_cycle(c, 100000)); seq_printf(s, " %6d", clk_core_get_scaled_duty_cycle(c, 100000));
if (c->ops->is_enabled)
seq_printf(s, " %9c\n", clk_core_is_enabled(c) ? 'Y' : 'N');
else if (!c->ops->enable)
seq_printf(s, " %9c\n", 'Y');
else
seq_printf(s, " %9c\n", '?');
} }
static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
...@@ -2950,9 +2957,9 @@ static int clk_summary_show(struct seq_file *s, void *data) ...@@ -2950,9 +2957,9 @@ static int clk_summary_show(struct seq_file *s, void *data)
struct clk_core *c; struct clk_core *c;
struct hlist_head **lists = (struct hlist_head **)s->private; struct hlist_head **lists = (struct hlist_head **)s->private;
seq_puts(s, " enable prepare protect duty\n"); seq_puts(s, " enable prepare protect duty hardware\n");
seq_puts(s, " clock count count count rate accuracy phase cycle\n"); seq_puts(s, " clock count count count rate accuracy phase cycle enable\n");
seq_puts(s, "---------------------------------------------------------------------------------------------\n"); seq_puts(s, "-------------------------------------------------------------------------------------------------------\n");
clk_prepare_lock(); clk_prepare_lock();
......
...@@ -30,6 +30,7 @@ struct clk_gate2 { ...@@ -30,6 +30,7 @@ struct clk_gate2 {
void __iomem *reg; void __iomem *reg;
u8 bit_idx; u8 bit_idx;
u8 cgr_val; u8 cgr_val;
u8 cgr_mask;
u8 flags; u8 flags;
spinlock_t *lock; spinlock_t *lock;
unsigned int *share_count; unsigned int *share_count;
...@@ -37,37 +38,38 @@ struct clk_gate2 { ...@@ -37,37 +38,38 @@ struct clk_gate2 {
#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw) #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); struct clk_gate2 *gate = to_clk_gate2(hw);
u32 reg; 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; unsigned long flags;
int ret = 0;
spin_lock_irqsave(gate->lock, flags); spin_lock_irqsave(gate->lock, flags);
if (gate->share_count && (*gate->share_count)++ > 0) if (gate->share_count && (*gate->share_count)++ > 0)
goto out; goto out;
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) { clk_gate2_do_shared_clks(hw, true);
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);
}
out: out:
spin_unlock_irqrestore(gate->lock, flags); spin_unlock_irqrestore(gate->lock, flags);
return ret; return 0;
} }
static void clk_gate2_disable(struct clk_hw *hw) static void clk_gate2_disable(struct clk_hw *hw)
{ {
struct clk_gate2 *gate = to_clk_gate2(hw); struct clk_gate2 *gate = to_clk_gate2(hw);
u32 reg;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(gate->lock, flags); spin_lock_irqsave(gate->lock, flags);
...@@ -79,23 +81,17 @@ static void clk_gate2_disable(struct clk_hw *hw) ...@@ -79,23 +81,17 @@ static void clk_gate2_disable(struct clk_hw *hw)
goto out; goto out;
} }
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) { clk_gate2_do_shared_clks(hw, false);
clk_gate_ops.disable(hw);
} else {
reg = readl(gate->reg);
reg &= ~(3 << gate->bit_idx);
writel(reg, gate->reg);
}
out: out:
spin_unlock_irqrestore(gate->lock, flags); 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); u32 val = readl(reg);
if (((val >> bit_idx) & 1) == 1) if (((val >> bit_idx) & cgr_mask) == cgr_val)
return 1; return 1;
return 0; return 0;
...@@ -104,29 +100,28 @@ static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx) ...@@ -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) static int clk_gate2_is_enabled(struct clk_hw *hw)
{ {
struct clk_gate2 *gate = to_clk_gate2(hw); struct clk_gate2 *gate = to_clk_gate2(hw);
unsigned long flags;
int ret = 0;
spin_lock_irqsave(gate->lock, flags);
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx,
return clk_gate_ops.is_enabled(hw); gate->cgr_val, gate->cgr_mask);
return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx); spin_unlock_irqrestore(gate->lock, flags);
return ret;
} }
static void clk_gate2_disable_unused(struct clk_hw *hw) static void clk_gate2_disable_unused(struct clk_hw *hw)
{ {
struct clk_gate2 *gate = to_clk_gate2(hw); struct clk_gate2 *gate = to_clk_gate2(hw);
unsigned long flags; unsigned long flags;
u32 reg;
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT)
return;
spin_lock_irqsave(gate->lock, flags); spin_lock_irqsave(gate->lock, flags);
if (!gate->share_count || *gate->share_count == 0) { if (!gate->share_count || *gate->share_count == 0)
reg = readl(gate->reg); clk_gate2_do_shared_clks(hw, false);
reg &= ~(3 << gate->bit_idx);
writel(reg, gate->reg);
}
spin_unlock_irqrestore(gate->lock, flags); spin_unlock_irqrestore(gate->lock, flags);
} }
...@@ -140,7 +135,7 @@ static const struct clk_ops clk_gate2_ops = { ...@@ -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, struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags, 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, u8 clk_gate2_flags, spinlock_t *lock,
unsigned int *share_count) unsigned int *share_count)
{ {
...@@ -157,6 +152,7 @@ struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, ...@@ -157,6 +152,7 @@ struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
gate->reg = reg; gate->reg = reg;
gate->bit_idx = bit_idx; gate->bit_idx = bit_idx;
gate->cgr_val = cgr_val; gate->cgr_val = cgr_val;
gate->cgr_mask = cgr_mask;
gate->flags = clk_gate2_flags; gate->flags = clk_gate2_flags;
gate->lock = lock; gate->lock = lock;
gate->share_count = share_count; gate->share_count = share_count;
......
...@@ -653,7 +653,7 @@ static struct platform_driver imx8mm_clk_driver = { ...@@ -653,7 +653,7 @@ static struct platform_driver imx8mm_clk_driver = {
* reloading the driver will crash or break devices. * reloading the driver will crash or break devices.
*/ */
.suppress_bind_attrs = true, .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); module_platform_driver(imx8mm_clk_driver);
......
...@@ -604,7 +604,7 @@ static struct platform_driver imx8mn_clk_driver = { ...@@ -604,7 +604,7 @@ static struct platform_driver imx8mn_clk_driver = {
* reloading the driver will crash or break devices. * reloading the driver will crash or break devices.
*/ */
.suppress_bind_attrs = true, .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); module_platform_driver(imx8mn_clk_driver);
......
...@@ -425,7 +425,7 @@ static struct clk **uart_clks[ARRAY_SIZE(uart_clk_ids) + 1]; ...@@ -425,7 +425,7 @@ static struct clk **uart_clks[ARRAY_SIZE(uart_clk_ids) + 1];
static int imx8mp_clocks_probe(struct platform_device *pdev) static int imx8mp_clocks_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; struct device_node *np;
void __iomem *anatop_base, *ccm_base; void __iomem *anatop_base, *ccm_base;
int i; int i;
...@@ -763,7 +763,7 @@ static struct platform_driver imx8mp_clk_driver = { ...@@ -763,7 +763,7 @@ static struct platform_driver imx8mp_clk_driver = {
* reloading the driver will crash or break devices. * reloading the driver will crash or break devices.
*/ */
.suppress_bind_attrs = true, .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); module_platform_driver(imx8mp_clk_driver);
......
...@@ -639,7 +639,7 @@ static struct platform_driver imx8mq_clk_driver = { ...@@ -639,7 +639,7 @@ static struct platform_driver imx8mq_clk_driver = {
* reloading the driver will crash or break devices. * reloading the driver will crash or break devices.
*/ */
.suppress_bind_attrs = true, .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); module_platform_driver(imx8mq_clk_driver);
......
...@@ -9,8 +9,10 @@ ...@@ -9,8 +9,10 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "clk-scu.h" #include "clk-scu.h"
...@@ -157,6 +159,135 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = { ...@@ -157,6 +159,135 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
.num_max = IMX_LSIO_LPCG_CLK_END, .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) static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -167,8 +298,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) ...@@ -167,8 +298,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
struct clk_hw **clks; struct clk_hw **clks;
void __iomem *base; void __iomem *base;
int ret;
int i; 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); ss_lpcg = of_device_get_match_data(dev);
if (!ss_lpcg) if (!ss_lpcg)
return -ENODEV; return -ENODEV;
...@@ -219,6 +356,7 @@ static const struct of_device_id imx8qxp_lpcg_match[] = { ...@@ -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-adma", &imx8qxp_ss_adma, },
{ .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, }, { .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, },
{ .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, }, { .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, },
{ .compatible = "fsl,imx8qxp-lpcg", NULL },
{ /* sentinel */ } { /* sentinel */ }
}; };
...@@ -226,6 +364,7 @@ static struct platform_driver imx8qxp_lpcg_clk_driver = { ...@@ -226,6 +364,7 @@ static struct platform_driver imx8qxp_lpcg_clk_driver = {
.driver = { .driver = {
.name = "imx8qxp-lpcg-clk", .name = "imx8qxp-lpcg-clk",
.of_match_table = imx8qxp_lpcg_match, .of_match_table = imx8qxp_lpcg_match,
.pm = &imx_clk_lpcg_scu_pm_ops,
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
}, },
.probe = imx8qxp_lpcg_clk_probe, .probe = imx8qxp_lpcg_clk_probe,
......
This diff is collapsed.
...@@ -34,6 +34,9 @@ struct clk_lpcg_scu { ...@@ -34,6 +34,9 @@ struct clk_lpcg_scu {
void __iomem *reg; void __iomem *reg;
u8 bit_idx; u8 bit_idx;
bool hw_gate; bool hw_gate;
/* for state save&restore */
u32 state;
}; };
#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw) #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 = { ...@@ -81,9 +84,9 @@ static const struct clk_ops clk_lpcg_scu_ops = {
.disable = clk_lpcg_scu_disable, .disable = clk_lpcg_scu_disable,
}; };
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
unsigned long flags, void __iomem *reg, const char *parent_name, unsigned long flags,
u8 bit_idx, bool hw_gate) void __iomem *reg, u8 bit_idx, bool hw_gate)
{ {
struct clk_lpcg_scu *clk; struct clk_lpcg_scu *clk;
struct clk_init_data init; struct clk_init_data init;
...@@ -107,11 +110,53 @@ struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, ...@@ -107,11 +110,53 @@ struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
clk->hw.init = &init; clk->hw.init = &init;
hw = &clk->hw; hw = &clk->hw;
ret = clk_hw_register(NULL, hw); ret = clk_hw_register(dev, hw);
if (ret) { if (ret) {
kfree(clk); kfree(clk);
hw = ERR_PTR(ret); hw = ERR_PTR(ret);
} }
if (dev)
dev_set_drvdata(dev, clk);
return hw; 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, ...@@ -416,7 +416,7 @@ struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
__func__, name); __func__, name);
kfree(pll); kfree(pll);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
}; }
pll->base = base; pll->base = base;
pll->hw.init = &init; pll->hw.init = &init;
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/err.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 <linux/slab.h>
#include "clk-scu.h" #include "clk-scu.h"
...@@ -16,6 +20,21 @@ ...@@ -16,6 +20,21 @@
#define IMX_SIP_SET_CPUFREQ 0x00 #define IMX_SIP_SET_CPUFREQ 0x00
static struct imx_sc_ipc *ccm_ipc_handle; 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 * struct clk_scu - Description of one SCU clock
...@@ -27,6 +46,10 @@ struct clk_scu { ...@@ -27,6 +46,10 @@ struct clk_scu {
struct clk_hw hw; struct clk_hw hw;
u16 rsrc_id; u16 rsrc_id;
u8 clk_type; 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) ...@@ -128,9 +151,28 @@ static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
return container_of(hw, struct clk_scu, 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 = { ...@@ -344,8 +386,9 @@ static const struct clk_ops clk_scu_cpu_ops = {
.unprepare = clk_scu_unprepare, .unprepare = clk_scu_unprepare,
}; };
struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents, struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
int num_parents, u32 rsrc_id, u8 clk_type) const char * const *parents, int num_parents,
u32 rsrc_id, u8 clk_type)
{ {
struct clk_init_data init; struct clk_init_data init;
struct clk_scu *clk; struct clk_scu *clk;
...@@ -379,11 +422,185 @@ struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents, ...@@ -379,11 +422,185 @@ struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
clk->hw.init = &init; clk->hw.init = &init;
hw = &clk->hw; hw = &clk->hw;
ret = clk_hw_register(NULL, hw); ret = clk_hw_register(dev, hw);
if (ret) { if (ret) {
kfree(clk); kfree(clk);
hw = ERR_PTR(ret); hw = ERR_PTR(ret);
} }
if (dev)
dev_set_drvdata(dev, clk);
return hw; 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 @@ ...@@ -8,25 +8,61 @@
#define __IMX_CLK_SCU_H #define __IMX_CLK_SCU_H
#include <linux/firmware/imx/sci.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 imx_clk_scu_init(struct device_node *np);
int num_parents, u32 rsrc_id, u8 clk_type); 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, 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, 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)
{
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);
}
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_scu(name, parents, num_parents, rsrc_id, clk_type); return __imx_clk_lpcg_scu(dev, name, parent_name, flags, reg,
bit_idx, hw_gate);
} }
struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, static inline struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg, unsigned long flags, void __iomem *reg,
u8 bit_idx, bool hw_gate); u8 bit_idx, bool hw_gate)
{
return __imx_clk_lpcg_scu(NULL, name, parent_name, flags, reg,
bit_idx, hw_gate);
}
#endif #endif
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#define IMX_CLK_GATE2_SINGLE_BIT 1
extern spinlock_t imx_ccm_lock; extern spinlock_t imx_ccm_lock;
void imx_check_clocks(struct clk *clks[], unsigned int count); void imx_check_clocks(struct clk *clks[], unsigned int count);
...@@ -68,9 +66,9 @@ extern struct imx_pll14xx_clk imx_1443x_dram_pll; ...@@ -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)) 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, \ #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, \ 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) \ #define imx_clk_pllv3(type, name, parent_name, base, div_mask) \
to_clk(imx_clk_hw_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, ...@@ -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, struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags, 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, u8 clk_gate_flags, spinlock_t *lock,
unsigned int *share_count); unsigned int *share_count);
...@@ -351,14 +349,14 @@ static inline struct clk_hw *imx_clk_hw_gate2(const char *name, const char *pare ...@@ -351,14 +349,14 @@ static inline struct clk_hw *imx_clk_hw_gate2(const char *name, const char *pare
void __iomem *reg, u8 shift) void __iomem *reg, u8 shift)
{ {
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, 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, static inline struct clk_hw *imx_clk_hw_gate2_flags(const char *name, const char *parent,
void __iomem *reg, u8 shift, unsigned long flags) void __iomem *reg, u8 shift, unsigned long flags)
{ {
return clk_hw_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, 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, 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, ...@@ -366,7 +364,7 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared(const char *name,
unsigned int *share_count) unsigned int *share_count)
{ {
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, 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, 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, ...@@ -374,7 +372,7 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name,
unsigned int *share_count) unsigned int *share_count)
{ {
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | 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); &imx_ccm_lock, share_count);
} }
...@@ -384,16 +382,15 @@ static inline struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, ...@@ -384,16 +382,15 @@ static inline struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev,
unsigned int *share_count) unsigned int *share_count)
{ {
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, CLK_OPS_PARENT_ENABLE, reg, shift, 0x1,
IMX_CLK_GATE2_SINGLE_BIT, 0x1, 0, &imx_ccm_lock, share_count);
&imx_ccm_lock, share_count);
} }
static inline struct clk *imx_clk_gate2_cgr(const char *name, static inline struct clk *imx_clk_gate2_cgr(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 cgr_val) const char *parent, void __iomem *reg, u8 shift, u8 cgr_val)
{ {
return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, 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, 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 ...@@ -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, return clk_hw_register_gate2(NULL, name, parent,
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, 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, 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, ...@@ -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, return clk_hw_register_gate2(NULL, name, parent,
flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, 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) \ #define imx_clk_gate4_flags(name, parent, reg, shift, flags) \
......
...@@ -155,7 +155,7 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = { ...@@ -155,7 +155,7 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {
.set_parent = mtk_clk_mux_set_parent_setclr_lock, .set_parent = mtk_clk_mux_set_parent_setclr_lock,
}; };
struct clk *mtk_clk_register_mux(const struct mtk_mux *mux, static struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
struct regmap *regmap, struct regmap *regmap,
spinlock_t *lock) spinlock_t *lock)
{ {
......
...@@ -77,10 +77,6 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops; ...@@ -77,10 +77,6 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
_width, _gate, _upd_ofs, _upd, \ _width, _gate, _upd_ofs, _upd, \
CLK_SET_RATE_PARENT) CLK_SET_RATE_PARENT)
struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
struct regmap *regmap,
spinlock_t *lock);
int mtk_clk_register_muxes(const struct mtk_mux *muxes, int mtk_clk_register_muxes(const struct mtk_mux *muxes,
int num, struct device_node *node, int num, struct device_node *node,
spinlock_t *lock, spinlock_t *lock,
......
...@@ -8,12 +8,12 @@ menuconfig CLK_SIFIVE ...@@ -8,12 +8,12 @@ menuconfig CLK_SIFIVE
if CLK_SIFIVE if CLK_SIFIVE
config CLK_SIFIVE_FU540_PRCI config CLK_SIFIVE_PRCI
bool "PRCI driver for SiFive FU540 SoCs" bool "PRCI driver for SiFive SoCs"
select CLK_ANALOGBITS_WRPLL_CLN28HPC select CLK_ANALOGBITS_WRPLL_CLN28HPC
help help
Supports the Power Reset Clock interface (PRCI) IP block found in Supports the Power Reset Clock interface (PRCI) IP block found in
FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC, FU540/FU740 SoCs. If this kernel is meant to run on a SiFive FU540/
enable this driver. FU740 SoCs, enable this driver.
endif endif
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += fu540-prci.o obj-$(CONFIG_CLK_SIFIVE_PRCI) += sifive-prci.o fu540-prci.o fu740-prci.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 SiFive, Inc.
* Zong Li
*/
#ifndef __SIFIVE_CLK_FU540_PRCI_H
#define __SIFIVE_CLK_FU540_PRCI_H
#include "sifive-prci.h"
#define NUM_CLOCK_FU540 4
extern struct __prci_clock __prci_init_clocks_fu540[NUM_CLOCK_FU540];
static const struct prci_clk_desc prci_clk_fu540 = {
.clks = __prci_init_clocks_fu540,
.num_clks = ARRAY_SIZE(__prci_init_clocks_fu540),
};
#endif /* __SIFIVE_CLK_FU540_PRCI_H */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 SiFive, Inc.
* Copyright (C) 2020 Zong Li
*/
#include <linux/module.h>
#include <dt-bindings/clock/sifive-fu740-prci.h>
#include "fu540-prci.h"
#include "sifive-prci.h"
/* PRCI integration data for each WRPLL instance */
static struct __prci_wrpll_data __prci_corepll_data = {
.cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
.cfg1_offs = PRCI_COREPLLCFG1_OFFSET,
.enable_bypass = sifive_prci_coreclksel_use_hfclk,
.disable_bypass = sifive_prci_coreclksel_use_final_corepll,
};
static struct __prci_wrpll_data __prci_ddrpll_data = {
.cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
.cfg1_offs = PRCI_DDRPLLCFG1_OFFSET,
};
static struct __prci_wrpll_data __prci_gemgxlpll_data = {
.cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
.cfg1_offs = PRCI_GEMGXLPLLCFG1_OFFSET,
};
static struct __prci_wrpll_data __prci_dvfscorepll_data = {
.cfg0_offs = PRCI_DVFSCOREPLLCFG0_OFFSET,
.cfg1_offs = PRCI_DVFSCOREPLLCFG1_OFFSET,
.enable_bypass = sifive_prci_corepllsel_use_corepll,
.disable_bypass = sifive_prci_corepllsel_use_dvfscorepll,
};
static struct __prci_wrpll_data __prci_hfpclkpll_data = {
.cfg0_offs = PRCI_HFPCLKPLLCFG0_OFFSET,
.cfg1_offs = PRCI_HFPCLKPLLCFG1_OFFSET,
.enable_bypass = sifive_prci_hfpclkpllsel_use_hfclk,
.disable_bypass = sifive_prci_hfpclkpllsel_use_hfpclkpll,
};
static struct __prci_wrpll_data __prci_cltxpll_data = {
.cfg0_offs = PRCI_CLTXPLLCFG0_OFFSET,
.cfg1_offs = PRCI_CLTXPLLCFG1_OFFSET,
};
/* Linux clock framework integration */
static const struct clk_ops sifive_fu740_prci_wrpll_clk_ops = {
.set_rate = sifive_prci_wrpll_set_rate,
.round_rate = sifive_prci_wrpll_round_rate,
.recalc_rate = sifive_prci_wrpll_recalc_rate,
.enable = sifive_prci_clock_enable,
.disable = sifive_prci_clock_disable,
.is_enabled = sifive_clk_is_enabled,
};
static const struct clk_ops sifive_fu740_prci_wrpll_ro_clk_ops = {
.recalc_rate = sifive_prci_wrpll_recalc_rate,
};
static const struct clk_ops sifive_fu740_prci_tlclksel_clk_ops = {
.recalc_rate = sifive_prci_tlclksel_recalc_rate,
};
static const struct clk_ops sifive_fu740_prci_hfpclkplldiv_clk_ops = {
.recalc_rate = sifive_prci_hfpclkplldiv_recalc_rate,
};
/* List of clock controls provided by the PRCI */
struct __prci_clock __prci_init_clocks_fu740[] = {
[PRCI_CLK_COREPLL] = {
.name = "corepll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_clk_ops,
.pwd = &__prci_corepll_data,
},
[PRCI_CLK_DDRPLL] = {
.name = "ddrpll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_ro_clk_ops,
.pwd = &__prci_ddrpll_data,
},
[PRCI_CLK_GEMGXLPLL] = {
.name = "gemgxlpll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_clk_ops,
.pwd = &__prci_gemgxlpll_data,
},
[PRCI_CLK_DVFSCOREPLL] = {
.name = "dvfscorepll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_clk_ops,
.pwd = &__prci_dvfscorepll_data,
},
[PRCI_CLK_HFPCLKPLL] = {
.name = "hfpclkpll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_clk_ops,
.pwd = &__prci_hfpclkpll_data,
},
[PRCI_CLK_CLTXPLL] = {
.name = "cltxpll",
.parent_name = "hfclk",
.ops = &sifive_fu740_prci_wrpll_clk_ops,
.pwd = &__prci_cltxpll_data,
},
[PRCI_CLK_TLCLK] = {
.name = "tlclk",
.parent_name = "corepll",
.ops = &sifive_fu740_prci_tlclksel_clk_ops,
},
[PRCI_CLK_PCLK] = {
.name = "pclk",
.parent_name = "hfpclkpll",
.ops = &sifive_fu740_prci_hfpclkplldiv_clk_ops,
},
};
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 SiFive, Inc.
* Zong Li
*/
#ifndef __SIFIVE_CLK_FU740_PRCI_H
#define __SIFIVE_CLK_FU740_PRCI_H
#include "sifive-prci.h"
#define NUM_CLOCK_FU740 8
extern struct __prci_clock __prci_init_clocks_fu740[NUM_CLOCK_FU740];
static const struct prci_clk_desc prci_clk_fu740 = {
.clks = __prci_init_clocks_fu740,
.num_clks = ARRAY_SIZE(__prci_init_clocks_fu740),
};
#endif /* __SIFIVE_CLK_FU740_PRCI_H */
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (C) 2016 NVIDIA Corporation * Copyright (C) 2016-2020 NVIDIA Corporation
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
...@@ -174,7 +174,7 @@ static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate, ...@@ -174,7 +174,7 @@ static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
int err; int err;
memset(&request, 0, sizeof(request)); memset(&request, 0, sizeof(request));
request.rate = rate; request.rate = min_t(u64, rate, S64_MAX);
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.cmd = CMD_CLK_ROUND_RATE; msg.cmd = CMD_CLK_ROUND_RATE;
...@@ -256,7 +256,7 @@ static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -256,7 +256,7 @@ static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
struct tegra_bpmp_clk_message msg; struct tegra_bpmp_clk_message msg;
memset(&request, 0, sizeof(request)); memset(&request, 0, sizeof(request));
request.rate = rate; request.rate = min_t(u64, rate, S64_MAX);
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.cmd = CMD_CLK_SET_RATE; msg.cmd = CMD_CLK_SET_RATE;
......
...@@ -1856,13 +1856,13 @@ static int dfll_fetch_pwm_params(struct tegra_dfll *td) ...@@ -1856,13 +1856,13 @@ static int dfll_fetch_pwm_params(struct tegra_dfll *td)
&td->reg_init_uV); &td->reg_init_uV);
if (!ret) { if (!ret) {
dev_err(td->dev, "couldn't get initialized voltage\n"); dev_err(td->dev, "couldn't get initialized voltage\n");
return ret; return -EINVAL;
} }
ret = read_dt_param(td, "nvidia,pwm-period-nanoseconds", &pwm_period); ret = read_dt_param(td, "nvidia,pwm-period-nanoseconds", &pwm_period);
if (!ret) { if (!ret) {
dev_err(td->dev, "couldn't get PWM period\n"); dev_err(td->dev, "couldn't get PWM period\n");
return ret; return -EINVAL;
} }
td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1); td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
......
...@@ -227,6 +227,7 @@ enum clk_id { ...@@ -227,6 +227,7 @@ enum clk_id {
tegra_clk_sdmmc4, tegra_clk_sdmmc4,
tegra_clk_sdmmc4_8, tegra_clk_sdmmc4_8,
tegra_clk_se, tegra_clk_se,
tegra_clk_se_10,
tegra_clk_soc_therm, tegra_clk_soc_therm,
tegra_clk_soc_therm_8, tegra_clk_soc_therm_8,
tegra_clk_sor0, tegra_clk_sor0,
......
...@@ -630,7 +630,7 @@ static struct tegra_periph_init_data periph_clks[] = { ...@@ -630,7 +630,7 @@ static struct tegra_periph_init_data periph_clks[] = {
INT8("host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_8), INT8("host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_8),
INT8("host1x", mux_pllc4_out1_pllc_pllc4_out2_pllp_clkm_plla_pllc4_out0, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_9), INT8("host1x", mux_pllc4_out1_pllc_pllc4_out2_pllp_clkm_plla_pllc4_out0, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_9),
INT8("se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se), INT8("se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se),
INT8("se", mux_pllp_pllc2_c_c3_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se), INT8("se", mux_pllp_pllc2_c_c3_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se_10),
INT8("2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d_8), INT8("2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d_8),
INT8("3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d_8), INT8("3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d_8),
INT8("vic03", mux_pllm_pllc_pllp_plla_pllc2_c3_clkm, CLK_SOURCE_VIC03, 178, 0, tegra_clk_vic03), INT8("vic03", mux_pllm_pllc_pllp_plla_pllc2_c3_clkm, CLK_SOURCE_VIC03, 178, 0, tegra_clk_vic03),
......
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Copyright (C) 2019 SiFive, Inc.
* Wesley Terpstra
* Paul Walmsley
* Zong Li
*/
#ifndef __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H
#define __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H
/* Clock indexes for use by Device Tree data and the PRCI driver */
#define PRCI_CLK_COREPLL 0
#define PRCI_CLK_DDRPLL 1
#define PRCI_CLK_GEMGXLPLL 2
#define PRCI_CLK_DVFSCOREPLL 3
#define PRCI_CLK_HFPCLKPLL 4
#define PRCI_CLK_CLTXPLL 5
#define PRCI_CLK_TLCLK 6
#define PRCI_CLK_PCLK 7
#endif /* __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment