diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-30 20:11:04 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-30 20:11:04 +0300 |
commit | 95124339875c8d9c092eb2fa3993e4751e1be48d (patch) | |
tree | a977a1f03c4b8b488d3b239c18875c2f7972351b /drivers/clk/starfive | |
parent | ee96dd9614f1c139e719dd2f296acbed7f1ab4b8 (diff) | |
parent | cf683abd3913d5e6e51169de75d65ea193452fbd (diff) | |
download | linux-95124339875c8d9c092eb2fa3993e4751e1be48d.tar.xz |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd:
"There's one large change in the core clk framework here. We change how
clk_set_rate_range() works so that the frequency is re-evaulated each
time the rate is changed. Previously we wouldn't let clk providers see
a rate that was different if it was still within the range, which
could be bad for power if the clk could run slower when a range
expands. Now the clk provider can decide to do something differently
when the constraints change. This broke Nvidia's clk driver so we had
to wait for the fix for that to bake a little more in -next.
The rate range patch series also introduced a kunit suite for the clk
framework that we're going to extend in the next release. It already
made it easy to find corner cases in the rate range patches so I'm
excited to see it cover more clk code and increase our confidence in
core framework patches in the future. I also added a kunit test for
the basic clk gate code and that work will continue to cover more
basic clk types: muxes, dividers, etc.
Beyond the core code we have the usual set of clk driver updates and
additions. Qualcomm again dominates the diffstat here with lots more
SoCs being supported and i.MX follows afer that with a similar number
of SoCs gaining clk drivers. Beyond those large additions there's
drivers being modernized to use clk_parent_data so we can move away
from global string names for all the clks in an SoC. Finally there's
lots of little fixes all over the clk drivers for typos, warnings, and
missing clks that aren't critical and get batched up waiting for the
next merge window to open. Nothing super big stands out in the driver
pile. Full details are below.
Core:
- Make clk_set_rate_range() re-evaluate the limits each time
- Introduce various clk_set_rate_range() tests
- Add clk_drop_range() to drop a previously set range
New Drivers:
- i.MXRT1050 clock driver and bindings
- i.MX8DXL clock driver and bindings
- i.MX93 clock driver and bindings
- NCO blocks on Apple SoCs
- Audio clks on StarFive JH7100 RISC-V SoC
- Add support for the new Renesas RZ/V2L SoC
- Qualcomm SDX65 A7 PLL
- Qualcomm SM6350 GPU clks
- Qualcomm SM6125, SM6350, QCS2290 display clks
- Qualcomm MSM8226 multimedia clks
Updates:
- Kunit tests for clk-gate implementation
- Terminate arrays with sentinels and make that clearer
- Cleanup SPDX tags
- Fix typos in comments
- Mark mux table as const in clk-mux
- Make the all_lists array const
- Convert Cirrus Logic CS2000P driver to regmap, yamlify DT binding
and add support for dynamic mode
- Clock configuration on Microchip PolarFire SoCs
- Free allocations on probe error in Mediatek clk driver
- Modernize Mediatek clk driver by consolidating code
- Add watchdog (WDT), I2C, and pin function controller (PFC) clocks
on Renesas R-Car S4-8
- Improve the clocks for the Rockchip rk3568 display outputs
(parenting, pll-rates)
- Use of_device_get_match_data() instead of open-coding on Rockchip
rk3568
- Reintroduce the expected fractional-divider behaviour that
disappeared with the addition of CLK_FRAC_DIVIDER_POWER_OF_TWO_PS
- Remove SYS PLL 1/2 clock gates for i.MX8M*
- Remove AUDIO MCLK ROOT from i.MX7D
- Add fracn gppll clock type used by i.MX93
- Add new composite clock for i.MX93
- Add missing media mipi phy ref clock for i.MX8MP
- Fix off by one in imx_lpcg_parse_clks_from_dt()
- Rework for the imx pll14xx
- sama7g5: One low priority fix for GCLK of PDMC
- Add DMA engine (SYS-DMAC) clocks on Renesas R-Car S4-8
- Add MOST (MediaLB I/F) clocks on Renesas R-Car E3 and D3
- Add CAN-FD clocks on Renesas R-Car V3U
- Qualcomm SC8280XP RPMCC
- Add some missing clks on Qualcomm MSM8992/MSM8994/MSM8998 SoCs
- Rework Qualcomm GCC bindings and convert SDM845 camera bindig to
YAML
- Convert various Qualcomm drivers to use clk_parent_data
- Remove test clocks from various Qualcomm drivers
- Crypto engine clks on Qualcomm IPQ806x + more freqs for SDCC/NSS
- Qualcomm SM8150 EMAC, PCIe, UFS GDSCs
- Better pixel clk frequency support on Qualcomm RCG2 clks"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (227 commits)
clk: zynq: Update the parameters to zynq_clk_register_periph_clk
clk: zynq: trivial warning fix
clk: Drop the rate range on clk_put()
clk: test: Test clk_set_rate_range on orphan mux
clk: Initialize orphan req_rate
dt-bindings: clock: drop useless consumer example
dt-bindings: clock: renesas: Make example 'clocks' parsable
clk: qcom: gcc-msm8994: Fix gpll4 width
dt-bindings: clock: fix dt_binding_check error for qcom,gcc-other.yaml
clk: rs9: Add Renesas 9-series PCIe clock generator driver
clk: fixed-factor: Introduce devm_clk_hw_register_fixed_factor_index()
clk: visconti: prevent array overflow in visconti_clk_register_gates()
dt-bindings: clk: rs9: Add Renesas 9-series I2C PCIe clock generator
clk: sifive: Move all stuff into SoCs header files from C files
clk: sifive: Add SoCs prefix in each SoCs-dependent data
riscv: dts: Change the macro name of prci in each device node
dt-bindings: change the macro name of prci in header files and example
clk: sifive: duplicate the macro definitions for the time being
clk: qcom: sm6125-gcc: fix typos in comments
clk: ti: clkctrl: fix typos in comments
...
Diffstat (limited to 'drivers/clk/starfive')
-rw-r--r-- | drivers/clk/starfive/Kconfig | 8 | ||||
-rw-r--r-- | drivers/clk/starfive/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100-audio.c | 170 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100.c | 176 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100.h | 112 |
5 files changed, 380 insertions, 87 deletions
diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index c0fa9d5e641f..003bd2d56ce7 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -7,3 +7,11 @@ config CLK_STARFIVE_JH7100 help Say yes here to support the clock controller on the StarFive JH7100 SoC. + +config CLK_STARFIVE_JH7100_AUDIO + tristate "StarFive JH7100 audio clock support" + depends on CLK_STARFIVE_JH7100 + default m if SOC_STARFIVE + help + Say Y or M here to support the audio clocks on the StarFive JH7100 + SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 09759cc73530..0fa8ecb9ec1c 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # StarFive Clock obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o +obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c new file mode 100644 index 000000000000..8473a65e219b --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Audio Clock Driver + * + * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> + */ + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <dt-bindings/clock/starfive-jh7100-audio.h> + +#include "clk-starfive-jh7100.h" + +/* external clocks */ +#define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) +#define JH7100_AUDCLK_AUDIO_12288 (JH7100_AUDCLK_END + 1) +#define JH7100_AUDCLK_DOM7AHB_BUS (JH7100_AUDCLK_END + 2) +#define JH7100_AUDCLK_I2SADC_BCLK_IOPAD (JH7100_AUDCLK_END + 3) +#define JH7100_AUDCLK_I2SADC_LRCLK_IOPAD (JH7100_AUDCLK_END + 4) +#define JH7100_AUDCLK_I2SDAC_BCLK_IOPAD (JH7100_AUDCLK_END + 5) +#define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) +#define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) + +static const struct jh7100_clk_data jh7100_audclk_data[] = { + JH7100__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, + JH7100_AUDCLK_ADC_MCLK, + JH7100_AUDCLK_I2SADC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, + JH7100_AUDCLK_I2SADC_BCLK_N, + JH7100_AUDCLK_I2SADC_LRCLK_IOPAD, + JH7100_AUDCLK_I2SADC_BCLK), + JH7100_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, + JH7100_AUDCLK_AUDIO_SRC, + JH7100_AUDCLK_AUDIO_12288), + JH7100_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, + JH7100_AUDCLK_DAC_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, + JH7100_AUDCLK_I2S1_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, + JH7100_AUDCLK_I2S1_MCLK, + JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), + JH7100__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), + JH7100_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, + JH7100_AUDCLK_I2S1_BCLK_N, + JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), + JH7100_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH7100__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), + JH7100_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), + JH7100_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), + JH7100__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH7100__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, + JH7100_AUDCLK_VAD_INTMEM, + JH7100_AUDCLK_AUDIO_12288), +}; + +static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7100_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7100_AUDCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7100_audclk_probe(struct platform_device *pdev) +{ + struct jh7100_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_AUDCLK_END), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + for (idx = 0; idx < JH7100_AUDCLK_END; idx++) { + u32 max = jh7100_audclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7100_audclk_data[idx].name, + .ops = starfive_jh7100_clk_ops(max), + .parent_data = parents, + .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .flags = jh7100_audclk_data[idx].flags, + }; + struct jh7100_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7100_audclk_data[idx].parents[i]; + + if (pidx < JH7100_AUDCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7100_AUDCLK_AUDIO_SRC) + parents[i].fw_name = "audio_src"; + else if (pidx == JH7100_AUDCLK_AUDIO_12288) + parents[i].fw_name = "audio_12288"; + else if (pidx == JH7100_AUDCLK_DOM7AHB_BUS) + parents[i].fw_name = "dom7ahb_bus"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH7100_CLK_DIV_MASK; + + ret = devm_clk_hw_register(priv->dev, &clk->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(priv->dev, jh7100_audclk_get, priv); +} + +static const struct of_device_id jh7100_audclk_match[] = { + { .compatible = "starfive,jh7100-audclk" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7100_audclk_match); + +static struct platform_driver jh7100_audclk_driver = { + .probe = jh7100_audclk_probe, + .driver = { + .name = "clk-starfive-jh7100-audio", + .of_match_table = jh7100_audclk_match, + }, +}; +module_platform_driver(jh7100_audclk_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7100 audio clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 25d31afa0f87..691aeebc7092 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -20,69 +20,15 @@ #include <dt-bindings/clock/starfive-jh7100.h> +#include "clk-starfive-jh7100.h" + /* external clocks */ #define JH7100_CLK_OSC_SYS (JH7100_CLK_END + 0) #define JH7100_CLK_OSC_AUD (JH7100_CLK_END + 1) #define JH7100_CLK_GMAC_RMII_REF (JH7100_CLK_END + 2) #define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3) -/* register fields */ -#define JH7100_CLK_ENABLE BIT(31) -#define JH7100_CLK_INVERT BIT(30) -#define JH7100_CLK_MUX_MASK GENMASK(27, 24) -#define JH7100_CLK_MUX_SHIFT 24 -#define JH7100_CLK_DIV_MASK GENMASK(23, 0) - -/* clock data */ -#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT | (_flags), \ - .max = JH7100_CLK_ENABLE, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = _max, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | (_max), \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT, \ - .max = JH7100_CLK_INVERT, \ - .parents = { [0] = _parent }, \ -} - -static const struct { - const char *name; - unsigned long flags; - u32 max; - u8 parents[4]; -} jh7100_clk_data[] __initconst = { +static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, @@ -225,7 +171,7 @@ static const struct { JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_USBPHY_PLLDIV25M), - JH7100__DIV(JH7100_CLK_AUDIO_DIV, "audio_div", 131072, JH7100_CLK_AUDIO_ROOT), + JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), @@ -323,21 +269,6 @@ static const struct { JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), }; -struct jh7100_clk { - struct clk_hw hw; - unsigned int idx; - unsigned int max_div; -}; - -struct jh7100_clk_priv { - /* protect clk enable and set rate/parent from happening at the same time */ - spinlock_t rmw_lock; - struct device *dev; - void __iomem *base; - struct clk_hw *pll[3]; - struct jh7100_clk reg[JH7100_CLK_PLL0_OUT]; -}; - static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) { return container_of(hw, struct jh7100_clk, hw); @@ -399,22 +330,13 @@ static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, return div ? parent_rate / div : 0; } -static unsigned long jh7100_clk_bestdiv(struct jh7100_clk *clk, - unsigned long rate, unsigned long parent) -{ - unsigned long max = clk->max_div; - unsigned long div = DIV_ROUND_UP(parent, rate); - - return min(div, max); -} - static int jh7100_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct jh7100_clk *clk = jh7100_clk_from(hw); unsigned long parent = req->best_parent_rate; unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div = jh7100_clk_bestdiv(clk, rate, parent); + unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); unsigned long result = parent / div; /* @@ -442,12 +364,56 @@ static int jh7100_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div = jh7100_clk_bestdiv(clk, rate, parent_rate); + unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), + 1UL, (unsigned long)clk->max_div); jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); return 0; } +static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 reg = jh7100_clk_reg_get(clk); + unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + + ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); + + return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; +} + +static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long parent100 = 100 * req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + unsigned long result = parent100 / div100; + + /* clamp the result as in jh7100_clk_determine_rate() above */ + if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) + result = parent100 / (div100 + 1); + if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) + result = parent100 / (div100 - 1); + + req->rate = result; + return 0; +} + +static int jh7100_clk_frac_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); + return 0; +} + static u8 jh7100_clk_get_parent(struct clk_hw *hw) { struct jh7100_clk *clk = jh7100_clk_from(hw); @@ -534,6 +500,13 @@ static const struct clk_ops jh7100_clk_div_ops = { .debug_init = jh7100_clk_debug_init, }; +static const struct clk_ops jh7100_clk_fdiv_ops = { + .recalc_rate = jh7100_clk_frac_recalc_rate, + .determine_rate = jh7100_clk_frac_determine_rate, + .set_rate = jh7100_clk_frac_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + static const struct clk_ops jh7100_clk_gdiv_ops = { .enable = jh7100_clk_enable, .disable = jh7100_clk_disable, @@ -561,17 +534,45 @@ static const struct clk_ops jh7100_clk_gmux_ops = { .debug_init = jh7100_clk_debug_init, }; +static const struct clk_ops jh7100_clk_mdiv_ops = { + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gmd_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + static const struct clk_ops jh7100_clk_inv_ops = { .get_phase = jh7100_clk_get_phase, .set_phase = jh7100_clk_set_phase, .debug_init = jh7100_clk_debug_init, }; -static const struct clk_ops *__init jh7100_clk_ops(u32 max) +const struct clk_ops *starfive_jh7100_clk_ops(u32 max) { if (max & JH7100_CLK_DIV_MASK) { + if (max & JH7100_CLK_MUX_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gmd_ops; + return &jh7100_clk_mdiv_ops; + } if (max & JH7100_CLK_ENABLE) return &jh7100_clk_gdiv_ops; + if (max == JH7100_CLK_FRAC_MAX) + return &jh7100_clk_fdiv_ops; return &jh7100_clk_div_ops; } @@ -586,6 +587,7 @@ static const struct clk_ops *__init jh7100_clk_ops(u32 max) return &jh7100_clk_inv_ops; } +EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) { @@ -607,7 +609,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) unsigned int idx; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7100_CLK_PLL0_OUT), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -637,7 +639,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_clk_data[idx].name, - .ops = jh7100_clk_ops(max), + .ops = starfive_jh7100_clk_ops(max), .parent_data = parents, .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, .flags = jh7100_clk_data[idx].flags, diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h new file mode 100644 index 000000000000..f116be5740a5 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __CLK_STARFIVE_JH7100_H +#define __CLK_STARFIVE_JH7100_H + +#include <linux/bits.h> +#include <linux/clk-provider.h> + +/* register fields */ +#define JH7100_CLK_ENABLE BIT(31) +#define JH7100_CLK_INVERT BIT(30) +#define JH7100_CLK_MUX_MASK GENMASK(27, 24) +#define JH7100_CLK_MUX_SHIFT 24 +#define JH7100_CLK_DIV_MASK GENMASK(23, 0) +#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) +#define JH7100_CLK_FRAC_SHIFT 8 +#define JH7100_CLK_INT_MASK GENMASK(7, 0) + +/* fractional divider min/max */ +#define JH7100_CLK_FRAC_MIN 100UL +#define JH7100_CLK_FRAC_MAX 25599UL + +/* clock data */ +struct jh7100_clk_data { + const char *name; + unsigned long flags; + u32 max; + u8 parents[4]; +}; + +#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT | (_flags), \ + .max = JH7100_CLK_ENABLE, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = _max, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | (_max), \ + .parents = { [0] = _parent }, \ +} + +#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = JH7100_CLK_FRAC_MAX, \ + .parents = { [0] = _parent }, \ +} + +#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | \ + (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100_MDIV(_idx, _name, _max, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100__GMD(_idx, _name, _flags, _max, _nparents, ...) [_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH7100_CLK_ENABLE | \ + (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT, \ + .max = JH7100_CLK_INVERT, \ + .parents = { [0] = _parent }, \ +} + +struct jh7100_clk { + struct clk_hw hw; + unsigned int idx; + unsigned int max_div; +}; + +struct jh7100_clk_priv { + /* protect clk enable and set rate/parent from happening at the same time */ + spinlock_t rmw_lock; + struct device *dev; + void __iomem *base; + struct clk_hw *pll[3]; + struct jh7100_clk reg[]; +}; + +const struct clk_ops *starfive_jh7100_clk_ops(u32 max); + +#endif |