From a756af03562dcd2fb0ed9c6cdbbefc9983e108c3 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:53:37 +0100 Subject: reset: add simple reset controller support Incoming STM32 MCU support will leverage this driver, so port it over from Linux v5.16. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075338.1869305-1-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/reset/Kconfig | 17 ++++ drivers/reset/Makefile | 1 + drivers/reset/reset-simple.c | 189 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 drivers/reset/reset-simple.c (limited to 'drivers') diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6c70c10269..82c8516253 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -15,6 +15,23 @@ menuconfig RESET_CONTROLLER if RESET_CONTROLLER +config RESET_SIMPLE + bool "Simple Reset Controller Driver" if COMPILE_TEST + help + This enables a simple reset controller driver for reset lines that + that can be asserted and deasserted by toggling bits in a contiguous, + exclusive register space. + + Currently this driver supports: + - Altera 64-Bit SoCFPGAs + - ASPEED BMC SoCs + - Bitmain BM1880 SoC + - Realtek SoCs + - RCC reset controller in STM32 MCUs + - Allwinner SoCs + - SiFive FU740 SoCs + + config RESET_IMX7 bool "i.MX7 Reset Driver" default ARCH_IMX7 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index d884a83aa3..b4270411fd 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_STM32) += reset-stm32.o diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c new file mode 100644 index 0000000000..082956d94d --- /dev/null +++ b/drivers/reset/reset-simple.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Simple Reset Controller Driver + * + * Copyright (C) 2017 Pengutronix, Philipp Zabel + * + * Based on Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline struct reset_simple_data * +to_reset_simple_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_simple_data, rcdev); +} + +static int reset_simple_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int reg_width = sizeof(u32); + int bank = id / (reg_width * BITS_PER_BYTE); + int offset = id % (reg_width * BITS_PER_BYTE); + u32 reg; + + reg = readl(data->membase + (bank * reg_width)); + if (assert ^ data->active_low) + reg |= BIT(offset); + else + reg &= ~BIT(offset); + writel(reg, data->membase + (bank * reg_width)); + + return 0; +} + +static int reset_simple_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return reset_simple_update(rcdev, id, true); +} + +static int reset_simple_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return reset_simple_update(rcdev, id, false); +} + +static int reset_simple_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int ret; + + if (!data->reset_us) + return -ENOTSUPP; + + ret = reset_simple_assert(rcdev, id); + if (ret) + return ret; + + udelay(data->reset_us); + + return reset_simple_deassert(rcdev, id); +} + +static int __maybe_unused reset_simple_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int reg_width = sizeof(u32); + int bank = id / (reg_width * BITS_PER_BYTE); + int offset = id % (reg_width * BITS_PER_BYTE); + u32 reg; + + reg = readl(data->membase + (bank * reg_width)); + + return !(reg & BIT(offset)) ^ !data->status_active_low; +} + +const struct reset_control_ops reset_simple_ops = { + .assert = reset_simple_assert, + .deassert = reset_simple_deassert, + .reset = reset_simple_reset, +}; +EXPORT_SYMBOL_GPL(reset_simple_ops); + +/** + * struct reset_simple_devdata - simple reset controller properties + * @reg_offset: offset between base address and first reset register. + * @nr_resets: number of resets. If not set, default to resource size in bits. + * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits + * are set to assert the reset. + * @status_active_low: if true, bits read back as cleared while the reset is + * asserted. Otherwise, bits read back as set while the + * reset is asserted. + */ +struct reset_simple_devdata { + u32 reg_offset; + u32 nr_resets; + bool active_low; + bool status_active_low; +}; + +#define SOCFPGA_NR_BANKS 8 + +static const struct reset_simple_devdata reset_simple_socfpga = { + .reg_offset = 0x20, + .nr_resets = SOCFPGA_NR_BANKS * 32, + .status_active_low = true, +}; + +static const struct reset_simple_devdata reset_simple_active_low = { + .active_low = true, + .status_active_low = true, +}; + +static const struct of_device_id reset_simple_dt_ids[] = { + { .compatible = "altr,stratix10-rst-mgr", + .data = &reset_simple_socfpga }, + { .compatible = "st,stm32-rcc", }, + { .compatible = "allwinner,sun6i-a31-clock-reset", + .data = &reset_simple_active_low }, + { .compatible = "zte,zx296718-reset", + .data = &reset_simple_active_low }, + { .compatible = "aspeed,ast2400-lpc-reset" }, + { .compatible = "aspeed,ast2500-lpc-reset" }, + { .compatible = "bitmain,bm1880-reset", + .data = &reset_simple_active_low }, + { .compatible = "brcm,bcm4908-misc-pcie-reset", + .data = &reset_simple_active_low }, + { .compatible = "snps,dw-high-reset" }, + { .compatible = "snps,dw-low-reset", + .data = &reset_simple_active_low }, + { /* sentinel */ }, +}; + +static int reset_simple_probe(struct device_d *dev) +{ + const struct reset_simple_devdata *devdata; + struct reset_simple_data *data; + struct resource *res; + u32 reg_offset = 0; + + devdata = device_get_match_data(dev); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + data->membase = IOMEM(res->start); + data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE; + data->rcdev.ops = &reset_simple_ops; + data->rcdev.of_node = dev->device_node; + + if (devdata) { + reg_offset = devdata->reg_offset; + if (devdata->nr_resets) + data->rcdev.nr_resets = devdata->nr_resets; + data->active_low = devdata->active_low; + data->status_active_low = devdata->status_active_low; + } + + data->membase += reg_offset; + + return reset_controller_register(&data->rcdev); +} + +static struct driver_d reset_simple_driver = { + .probe = reset_simple_probe, + .name = "simple-reset", + .of_compatible = reset_simple_dt_ids, +}; +postcore_platform_driver(reset_simple_driver); -- cgit v1.2.3 From 65683428c113c9fe114e2f89aa5e9b8909534a43 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:53:38 +0100 Subject: reset: stm32: drop STM32 MCU support in favor of simple reset driver RCC reset will eventually get more involved when we add SCMI support. Linux already has reset and clock control in the same driver. As we now have a simple driver that can toggle resets on the STM32 MCUs as well, we can drop the now duplicate support from the dedicated STM32 reset driver. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075338.1869305-2-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/reset/Kconfig | 2 +- drivers/reset/reset-stm32.c | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 82c8516253..b12159094d 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -43,7 +43,7 @@ config RESET_STM32 bool "STM32 Reset Driver" depends on ARCH_STM32MP || COMPILE_TEST help - This enables the reset controller driver for STM32MP and STM32 MCUs. + This enables the reset controller driver for STM32MP1. config RESET_STARFIVE bool "StarFive Controller Driver" if COMPILE_TEST diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c index 703ba1f072..186b2a8bc6 100644 --- a/drivers/reset/reset-stm32.c +++ b/drivers/reset/reset-stm32.c @@ -66,14 +66,6 @@ static void stm32mp_reset(void __iomem *reg, unsigned offset, bool assert) writel(BIT(offset), reg); } -static void stm32mcu_reset(void __iomem *reg, unsigned offset, bool assert) -{ - if (assert) - setbits_le32(reg, BIT(offset)); - else - clrbits_le32(reg, BIT(offset)); -} - static u32 stm32_reset_status(struct stm32_reset *priv, unsigned long bank) { return readl(priv->base + bank); @@ -195,18 +187,13 @@ static const struct stm32_reset_ops stm32mp1_reset_ops = { .reset_reasons = stm32mp_reset_reasons, }; -static const struct stm32_reset_ops stm32mcu_reset_ops = { - .reset = stm32mcu_reset, -}; - static const struct of_device_id stm32_rcc_reset_dt_ids[] = { { .compatible = "st,stm32mp1-rcc", .data = &stm32mp1_reset_ops }, - { .compatible = "st,stm32-rcc", .data = &stm32mcu_reset_ops }, { /* sentinel */ }, }; static struct driver_d stm32_rcc_reset_driver = { - .name = "stm32_rcc_reset", + .name = "stm32mp_rcc_reset", .probe = stm32_reset_probe, .of_compatible = DRV_OF_COMPAT(stm32_rcc_reset_dt_ids), }; -- cgit v1.2.3 From 8b0ca7a885eaa244b7cf23895f4ee9c264efcfbd Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:15 +0100 Subject: clk: composite: add clk_hw registration functions Save users the hassle of opencoding by providing wrappers with the same Linux semantics: names are duplicated, same arguments and struct clk_hw is returned. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-3-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-composite.c | 25 +++++++++++++++++++++++++ include/linux/clk.h | 8 ++++++++ 2 files changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 479ac5e8ef..63056b7696 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -171,3 +171,28 @@ err: kfree(composite); return 0; } + +struct clk_hw *clk_hw_register_composite(struct device_d *dev, + const char *name, const char * const *parent_names, + int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + mux_hw->clk.ops = mux_ops; + rate_hw->clk.ops = rate_ops; + gate_hw->clk.ops = gate_ops; + + parent_names = memdup_array(parent_names, num_parents); + if (!parent_names) + return ERR_PTR(-ENOMEM); + + clk = clk_register_composite(xstrdup(name), parent_names, num_parents, + mux_hw ? &mux_hw->clk : NULL, + rate_hw ? &rate_hw->clk : NULL, + gate_hw ? &gate_hw->clk : NULL, + flags); + return clk_to_clk_hw(clk); +} diff --git a/include/linux/clk.h b/include/linux/clk.h index 7242f1d3ad..2643dd1c30 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -676,6 +676,14 @@ struct clk *clk_register_composite(const char *name, struct clk *gate_clk, unsigned long flags); +struct clk_hw *clk_hw_register_composite(struct device_d *dev, + const char *name, const char * const *parent_names, + int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags); + static inline const char *clk_hw_get_name(struct clk_hw *hw) { return hw->clk.name; -- cgit v1.2.3 From 450d00f58e80956177fd117dd95d4908e0b4ffae Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:16 +0100 Subject: clk: divider: add clk_hw registration functions Save users the hassle of opencoding by providing wrappers with the same Linux semantics: names are duplicated, same arguments and struct clk_hw is returned. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-4-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-divider.c | 21 +++++++++++++++++++++ include/linux/clk.h | 11 +++++++++++ 2 files changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 856b8a0648..fed9ad9d24 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -428,3 +428,24 @@ struct clk *clk_register_divider(struct device_d *dev, const char *name, return clk_divider(name, parent_name, flags, reg, shift, width, clk_divider_flags); } + +struct clk_hw *clk_hw_register_divider_table(struct device_d *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return clk_to_clk_hw(clk_register_divider_table(dev, xstrdup(name), + xstrdup(parent_name), flags, reg, shift, width, + clk_divider_flags, table, lock)); +} + +struct clk_hw *clk_hw_register_divider(struct device_d *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock) +{ + return clk_to_clk_hw(clk_register_divider(dev, xstrdup(name), + xstrdup(parent_name), flags, reg, shift, width, + clk_divider_flags, lock)); +} diff --git a/include/linux/clk.h b/include/linux/clk.h index 2643dd1c30..7237911c6c 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -519,6 +519,17 @@ struct clk *clk_register_divider_table(struct device_d *dev, const char *name, u8 clk_divider_flags, const struct clk_div_table *table, spinlock_t *lock); +struct clk_hw *clk_hw_register_divider_table(struct device_d *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); + +struct clk_hw *clk_hw_register_divider(struct device_d *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock); + struct clk_fixed_factor { struct clk_hw hw; int mult; -- cgit v1.2.3 From 9cf9eb8bd03666c01023dd8e15fdb539bc3bc74c Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:17 +0100 Subject: clk: fixed-factor: add clk_hw registration functions Save users the hassle of opencoding by providing wrappers with the same Linux semantics: names are duplicated, same arguments and struct clk_hw is returned. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-5-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-fixed-factor.c | 9 +++++++++ include/linux/clk.h | 4 ++++ 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index fd4a3805f1..a6d3fdc6a1 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -84,6 +84,15 @@ struct clk *clk_register_fixed_factor(struct device_d *dev, const char *name, return clk_fixed_factor(name, parent_name, mult, div, flags); } +struct clk_hw *clk_hw_register_fixed_factor(struct device_d *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + return clk_to_clk_hw(clk_register_fixed_factor(dev, xstrdup(name), + xstrdup(parent_name), + flags, mult, div)); +} + /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock */ diff --git a/include/linux/clk.h b/include/linux/clk.h index 7237911c6c..e28d4afb56 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -551,6 +551,10 @@ struct clk *clk_register_fixed_factor(struct device_d *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); +struct clk_hw *clk_hw_register_fixed_factor(struct device_d *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div); + /** * struct clk_fractional_divider - adjustable fractional divider clock * -- cgit v1.2.3 From 917d7d7a94f7dc1a086270981a356a50e633839a Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:18 +0100 Subject: clk: clk-fixed: add clk_hw registration functions Save users the hassle of opencoding by providing wrappers with the same Linux semantics: names are duplicated, same arguments and struct clk_hw is returned. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-6-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-fixed.c | 8 ++++++++ include/linux/clk.h | 4 ++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-fixed.c b/drivers/clk/clk-fixed.c index e813f31d76..9e5a07817b 100644 --- a/drivers/clk/clk-fixed.c +++ b/drivers/clk/clk-fixed.c @@ -63,6 +63,14 @@ struct clk *clk_register_fixed_rate(const char *name, return &fix->hw.clk; } +struct clk_hw *clk_hw_register_fixed_rate(struct device_d *dev, + const char *name, const char *parent_name, + unsigned long flags, unsigned long rate) +{ + return clk_to_clk_hw(clk_register_fixed_rate(xstrdup(name), parent_name, + flags, rate)); +} + /** * of_fixed_clk_setup() - Setup function for simple fixed rate clock */ diff --git a/include/linux/clk.h b/include/linux/clk.h index e28d4afb56..390bead99a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -449,6 +449,10 @@ struct clk *clk_register_fixed_rate(const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate); +struct clk_hw *clk_hw_register_fixed_rate(struct device_d *dev, const char *name, + const char *parent_name, unsigned long flags, + unsigned long rate); + static inline struct clk *clk_fixed(const char *name, int rate) { return clk_register_fixed_rate(name, NULL, 0, rate); -- cgit v1.2.3 From d437d2cfee4530f3c372a673cff90ee751afe679 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:20 +0100 Subject: clk: mux: add clk_hw registration functions Save users the hassle of opencoding by providing wrappers with the same Linux semantics: names are duplicated, same arguments and struct clk_hw is returned. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-8-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-mux.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/clk.h | 27 +++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index d8c09e4a75..d608e0e2c5 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -10,12 +10,36 @@ #include #include +int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags, + unsigned int val) +{ + int num_parents = clk_hw_get_num_parents(hw); + + if (table) { + int i; + + for (i = 0; i < num_parents; i++) + if (table[i] == val) + return i; + return -EINVAL; + } + + return val; +} +EXPORT_SYMBOL_GPL(clk_mux_val_to_index); + +unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index) +{ + return table ? table[index] : index; +} +EXPORT_SYMBOL_GPL(clk_mux_index_to_val); + static int clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *m = to_clk_mux(hw); int idx = readl(m->reg) >> m->shift & ((1 << m->width) - 1); - return idx; + return clk_mux_val_to_index(hw, m->table, m->flags, idx); } static int clk_mux_set_parent(struct clk_hw *hw, u8 idx) @@ -30,6 +54,8 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 idx) return 0; } + idx = clk_mux_index_to_val(m->table, m->flags, idx); + val = readl(m->reg); val &= ~(((1 << m->width) - 1) << m->shift); val |= idx << m->shift; @@ -186,3 +212,57 @@ struct clk *clk_register_mux(struct device_d *dev, const char *name, return clk_mux(name, flags, reg, shift, width, parent_names, num_parents, clk_mux_flags); } + +struct clk_hw *__clk_hw_register_mux(struct device_d *dev, + const char *name, u8 num_parents, + const char * const *parent_names, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) +{ + struct clk_mux *mux; + struct clk_hw *hw; + struct clk_init_data init = {}; + u8 width = 0; + int ret = -EINVAL; + + width = fls(mask) - ffs(mask) + 1; + + if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { + if (width + shift > 16) { + pr_err("mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + + /* allocate the mux */ + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + if (clk_mux_flags & CLK_MUX_READ_ONLY) + init.ops = &clk_mux_ro_ops; + else + init.ops = &clk_mux_ops; + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; + + /* struct clk_mux assignments */ + mux->reg = reg; + mux->shift = shift; + mux->width = width; + mux->flags = clk_mux_flags; + mux->lock = lock; + mux->table = table; + mux->hw.init = &init; + + hw = &mux->hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(mux); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/include/linux/clk.h b/include/linux/clk.h index 9f2069dc9e..44c0cc7b1f 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -619,6 +619,7 @@ struct clk_mux { int shift; int width; unsigned flags; + u32 *table; spinlock_t *lock; }; @@ -641,6 +642,32 @@ struct clk *clk_register_mux(struct device_d *dev, const char *name, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); +struct clk_hw *__clk_hw_register_mux(struct device_d *dev, + const char *name, u8 num_parents, + const char * const *parent_names, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock); + +#define clk_hw_register_mux(dev, name, parent_names, \ + num_parents, flags, reg, shift, mask, \ + clk_mux_flags, lock) \ + __clk_hw_register_mux((dev), (name), (num_parents), \ + (parent_names), \ + (flags), (reg), (shift), (mask), \ + (clk_mux_flags), NULL, (lock)) + +#define clk_hw_register_mux_table(dev, name, parent_names, num_parents, \ + flags, reg, shift, mask, clk_mux_flags, \ + table, lock) \ + __clk_hw_register_mux((dev), (name), (num_parents), \ + (parent_names), (flags), (reg), \ + (shift), (mask), (clk_mux_flags), (table), \ + (lock)) + +int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags, + unsigned int val); +unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index); + struct clk_gate { struct clk_hw hw; void __iomem *reg; -- cgit v1.2.3 From 14a113b4b9bdfb1d84f62cea695c8097a6031fbd Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:21 +0100 Subject: clk: mux: export clk_mux_round_rate Clock drivers may want to implement round rate for their custom clocks in term of clk_mux_round_rate. Export the function to facilitate this. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-9-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk-mux.c | 5 +++-- include/linux/clk.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index d608e0e2c5..8463f1ee82 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -110,8 +110,8 @@ static struct clk *clk_mux_best_parent(struct clk *mux, unsigned long rate, return bestparent; } -static long clk_mux_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +long clk_mux_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) { struct clk *clk = clk_hw_to_clk(hw); unsigned long rrate; @@ -124,6 +124,7 @@ static long clk_mux_round_rate(struct clk_hw *hw, unsigned long rate, return rrate; } +EXPORT_SYMBOL_GPL(clk_mux_round_rate); static int clk_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) diff --git a/include/linux/clk.h b/include/linux/clk.h index 44c0cc7b1f..073c919802 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -668,6 +668,9 @@ int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags, unsigned int val); unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index); +long clk_mux_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate); + struct clk_gate { struct clk_hw hw; void __iomem *reg; -- cgit v1.2.3 From 61db7299af30c56dd35eede858e8be701d1ee376 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:22 +0100 Subject: clk: implement of_clk_add_hw_provider New Linux drivers rather use of_clk_add_hw_provider, so port it over. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-10-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/clk.c | 43 ++++++++++++++++++++++++++++++++++--------- include/linux/clk.h | 13 +++++++++++++ 2 files changed, 47 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 18373edbe4..227ab75ed6 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -561,6 +561,7 @@ struct of_clk_provider { struct device_node *node; struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data); void *data; }; @@ -591,15 +592,11 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) } EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); -/** - * of_clk_add_provider() - Register a clock provider for a node - * @np: Device node pointer associated with clock provider - * @clk_src_get: callback for decoding clock - * @data: context pointer for @clk_src_get callback. - */ -int of_clk_add_provider(struct device_node *np, +static int __of_clk_add_provider(struct device_node *np, struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, void *data), + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), void *data) { struct of_clk_provider *cp; @@ -611,6 +608,7 @@ int of_clk_add_provider(struct device_node *np, cp->node = np; cp->data = data; cp->get = clk_src_get; + cp->get_hw = clk_hw_src_get; list_add(&cp->link, &of_clk_providers); pr_debug("Added clock from %s\n", np ? np->full_name : ""); @@ -619,8 +617,31 @@ int of_clk_add_provider(struct device_node *np, return 0; } + +/** + * of_clk_add_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @clk_src_get: callback for decoding clock + * @data: context pointer for @clk_src_get callback. + */ +int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return __of_clk_add_provider(np, clk_src_get, NULL, data); +} EXPORT_SYMBOL_GPL(of_clk_add_provider); +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return __of_clk_add_provider(np, NULL, clk_hw_src_get, data); +} +EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); + /** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider @@ -649,8 +670,12 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { - if (provider->node == clkspec->np) - clk = provider->get(clkspec, provider->data); + if (provider->node == clkspec->np) { + if (provider->get) + clk = provider->get(clkspec, provider->data); + else + clk = clk_hw_to_clk(provider->get_hw(clkspec, provider->data)); + } if (!IS_ERR(clk)) break; } diff --git a/include/linux/clk.h b/include/linux/clk.h index 073c919802..dad0a9decc 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -788,6 +788,11 @@ int of_clk_add_provider(struct device_node *np, void *data), void *data); +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data); + #else @@ -835,6 +840,14 @@ static inline int of_clk_add_provider(struct device_node *np, { return 0; } + +static inline int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return 0; +} #endif #define CLK_OF_DECLARE_DRIVER(name, compat, fn) CLK_OF_DECLARE(name, compat, fn) -- cgit v1.2.3 From 314dcbc09ae7f3274583b55df1a61a0f8f830417 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:24 +0100 Subject: ARM: stm32mp: allow driver reuse for STM32 MCUs Most peripheral driver are usable for both STM32 MPUs and MCUs, but so far we they were only used for STM32MP1. In preparation for adding MCU support, introduce a new ARCH_STM32 selected by ARCH_STM32MP and migrate common drivers to it. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-12-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- arch/arm/Kconfig | 7 +++++++ drivers/aiodev/Kconfig | 2 +- drivers/hw_random/Kconfig | 2 +- drivers/i2c/busses/Kconfig | 2 +- drivers/mci/Kconfig | 2 +- drivers/mfd/Kconfig | 2 +- drivers/pinctrl/Kconfig | 2 +- drivers/pwm/Kconfig | 2 +- drivers/regulator/Kconfig | 2 +- drivers/serial/Kconfig | 4 ++-- drivers/watchdog/Kconfig | 2 +- 11 files changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index fecbe39233..299e0ab080 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -29,6 +29,12 @@ config TEXT_BASE menu "System Type" +config ARCH_STM32 + bool + help + Selected by both STM32 MCUs and MPUs to restrict driver + visibility. + choice prompt "ARM system type" @@ -185,6 +191,7 @@ config ARCH_S3C64xx config ARCH_STM32MP bool "STMicroelectronics STM32MP" + select ARCH_STM32 select CPU_V7 select HAVE_PBL_MULTI_IMAGES select CLKDEV_LOOKUP diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig index b0af2ebfd3..88a3b9a343 100644 --- a/drivers/aiodev/Kconfig +++ b/drivers/aiodev/Kconfig @@ -46,7 +46,7 @@ config AM335X_ADC config STM32_ADC tristate "STM32 ADC driver" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help Support for ADC on STM32. Supports simple one-shot readings rather than continuous sampling with DMA, etc. ADC channels should be diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig index 4b4200b5db..32b84b028b 100644 --- a/drivers/hw_random/Kconfig +++ b/drivers/hw_random/Kconfig @@ -17,7 +17,7 @@ config HWRNG_MXC_RNGC config HWRNG_STM32 tristate "STM32 Random Number Generator" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help This driver provides barebox support for the Random Number Generator hardware found on the STM32 family of MPUs and MCUs. diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a551df537a..d4e74552b7 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -60,7 +60,7 @@ config I2C_STM32 bool "STM32 I2C master driver" select RESET_CONTROLLER depends on HAVE_CLK - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST config I2C_RK3X tristate "Rockchip RK3xxx I2C adapter" diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 95a73a761c..21d53c0c3f 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -206,7 +206,7 @@ config MCI_STM32_SDMMC2 bool "STMicroelectronics STM32H7 SD/MMC Host Controller support" depends on ARM_AMBA depends on RESET_CONTROLLER - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help This selects support for the SD/MMC controller on STM32H7 SoCs. If you have a board based on such a SoC and with a SD/MMC slot, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8468a2d1d1..4285280602 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -87,7 +87,7 @@ config SMSC_SUPERIO config MFD_STM32_TIMERS bool "STM32 Timers" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help Select this to get regmap support for the timer blocks on STM32 MCUs and MPUs. diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index d3d5c7be23..2ff99a39c8 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -102,7 +102,7 @@ config PINCTRL_VF610 config PINCTRL_STM32 bool "STM32 pinctrl support" if COMPILE_TEST - default y if ARCH_STM32MP + default y if ARCH_STM32 help Pinmux and GPIO controller found on STM32 family endif diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 8219c1eaff..0b12278e80 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -38,7 +38,7 @@ config PWM_MXS config PWM_STM32 bool "STM32 PWM Support" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help This enables PWM support for STM32 MCUs and MPUs. diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 02cea362eb..c468e45915 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -31,7 +31,7 @@ config REGULATOR_STM32_PWR config REGULATOR_STM32_VREFBUF tristate "STMicroelectronics STM32 VREFBUF" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help This driver supports STMicroelectronics STM32 VREFBUF (voltage reference buffer) which can be used as voltage reference for diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8bf9fcd07e..9d4a534580 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -39,8 +39,8 @@ config DRIVER_SERIAL_IMX bool "i.MX serial driver" config DRIVER_SERIAL_STM32 - depends on ARCH_STM32MP - bool "stm32mp serial driver" + depends on ARCH_STM32 + bool "stm32 serial driver" config DRIVER_SERIAL_STM378X depends on ARCH_MXS diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 70c8be7ca9..2d194fab79 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -97,7 +97,7 @@ config RAVE_SP_WATCHDOG config STM32_IWDG_WATCHDOG bool "STM32 IWDG" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST select MFD_SYSCON help Enable to support configuration of the STM32's on-SoC IWDG watchdog. -- cgit v1.2.3 From 27923adcd1b7f5aa42a5ee8b797ddfa8c2e8e5a9 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 08:57:25 +0100 Subject: clk: add clock driver for stm32f4 and stm32f7 Port over the Linux v5.16 state of the clock driver. Tested on a STM32F429. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131075725.1873026-13-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-stm32f4.c | 1901 +++++++++++++++++++++++++++++++++++++ include/asm-generic/bitsperlong.h | 4 + include/linux/clk.h | 5 + 5 files changed, 1917 insertions(+) create mode 100644 drivers/clk/clk-stm32f4.c (limited to 'drivers') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index d8649c3f9b..e01ffe10f2 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,4 +20,10 @@ config CLK_SOCFPGA select COMMON_CLK_OF_PROVIDER default y if ARCH_SOCFPGA && OFDEVICE +config COMMON_CLK_STM32F + bool "STM32F4 and STM32F7 clock driver" if COMPILE_TEST + depends on COMMON_CLK && ARCH_STM32 + help + Support for stm32f4 and stm32f7 SoC families clocks + source "drivers/clk/sifive/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 499df2fe39..bab0498d7c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_ARCH_LAYERSCAPE) += clk-qoric.o obj-y += analogbits/ obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_SOC_STARFIVE) += starfive/ +obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c new file mode 100644 index 0000000000..4611038f4b --- /dev/null +++ b/drivers/clk/clk-stm32f4.c @@ -0,0 +1,1901 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Author: Daniel Thompson + * + * Inspired by clk-asm9260.c . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Include list of clocks wich are not derived from system clock (SYSCLOCK) + * The index of these clocks is the secondary index of DT bindings + * + */ +#include + +#define STM32F4_RCC_CR 0x00 +#define STM32F4_RCC_PLLCFGR 0x04 +#define STM32F4_RCC_CFGR 0x08 +#define STM32F4_RCC_AHB1ENR 0x30 +#define STM32F4_RCC_AHB2ENR 0x34 +#define STM32F4_RCC_AHB3ENR 0x38 +#define STM32F4_RCC_APB1ENR 0x40 +#define STM32F4_RCC_APB2ENR 0x44 +#define STM32F4_RCC_BDCR 0x70 +#define STM32F4_RCC_CSR 0x74 +#define STM32F4_RCC_PLLI2SCFGR 0x84 +#define STM32F4_RCC_PLLSAICFGR 0x88 +#define STM32F4_RCC_DCKCFGR 0x8c +#define STM32F7_RCC_DCKCFGR2 0x90 + +#define NONE -1 +#define NO_IDX NONE +#define NO_MUX NONE +#define NO_GATE NONE + +struct stm32f4_gate_data { + u8 offset; + u8 bit_idx; + const char *name; + const char *parent_name; + unsigned long flags; +}; + +static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, +}; + +static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "sdmux" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, +}; + +static const struct stm32f4_gate_data stm32f746_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 7, "sdmmc2", "sdmux" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdmmc", "sdmux" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" }, +}; + +static const struct stm32f4_gate_data stm32f769_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 1, "jpeg", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 10, "rtcapb", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 13, "can3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 7, "sdmmc2", "sdmux2" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdmmc1", "sdmux1" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" }, +}; + +/* + * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx + * have gate bits associated with them. Its combined hweight is 71. + */ +#define MAX_GATE_MAP 3 + +static const u64 stm32f42xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000001ull, + 0x04777f33f6fec9ffull }; + +static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000003ull, + 0x0c777f33f6fec9ffull }; + +static const u64 stm32f746_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000003ull, + 0x04f77f833e01c9ffull }; + +static const u64 stm32f769_gate_map[MAX_GATE_MAP] = { 0x000000f37ef417ffull, + 0x0000000000000003ull, + 0x44F77F833E01EDFFull }; + +static const u64 *stm32f4_gate_map; + +static struct clk_hw **clks; + +static DEFINE_SPINLOCK(stm32f4_clk_lock); +static void __iomem *base; + +static struct regmap *pdrm; + +static int stm32fx_end_primary_clk; + +/* + * "Multiplier" device for APBx clocks. + * + * The APBx dividers are power-of-two dividers and, if *not* running in 1:1 + * mode, they also tap out the one of the low order state bits to run the + * timers. ST datasheets represent this feature as a (conditional) clock + * multiplier. + */ +struct clk_apb_mul { + struct clk_hw hw; + u8 bit_idx; +}; + +#define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw) + +static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + return parent_rate * 2; + + return parent_rate; +} + +static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + unsigned long mult = 1; + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + mult = 2; + + if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { + unsigned long best_parent = rate / mult; + + *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); + } + + return *prate * mult; +} + +static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * clk_apb_mul_round_rate returns values that ensure this call is a + * nop. + */ + + return 0; +} + +static const struct clk_ops clk_apb_mul_factor_ops = { + .round_rate = clk_apb_mul_round_rate, + .set_rate = clk_apb_mul_set_rate, + .recalc_rate = clk_apb_mul_recalc_rate, +}; + +static struct clk *clk_register_apb_mul(struct device_d *dev, const char *name, + const char *parent_name, + unsigned long flags, u8 bit_idx) +{ + struct clk_apb_mul *am; + struct clk_init_data init; + struct clk *clk; + + am = kzalloc(sizeof(*am), GFP_KERNEL); + if (!am) + return ERR_PTR(-ENOMEM); + + am->bit_idx = bit_idx; + am->hw.init = &init; + + init.name = name; + init.ops = &clk_apb_mul_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &am->hw); + + if (IS_ERR(clk)) + kfree(am); + + return clk; +} + +enum { + PLL, + PLL_I2S, + PLL_SAI, +}; + +static const struct clk_div_table pll_divp_table[] = { + { 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 } +}; + +static const struct clk_div_table pll_divq_table[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, + { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 13, 13 }, + { 14, 14 }, { 15, 15 }, + { 0 } +}; + +static const struct clk_div_table pll_divr_table[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 } +}; + +struct stm32f4_pll { + spinlock_t *lock; + struct clk_gate gate; + u8 offset; + u8 bit_rdy_idx; + u8 status; + u8 n_start; +}; + +#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate) + +struct stm32f4_pll_post_div_data { + int idx; + int pll_idx; + const char *name; + const char *parent; + u8 flag; + u8 offset; + u8 shift; + u8 width; + u8 flag_div; + const struct clk_div_table *div_table; +}; + +struct stm32f4_vco_data { + const char *vco_name; + u8 offset; + u8 bit_idx; + u8 bit_rdy_idx; +}; + +static const struct stm32f4_vco_data vco_data[] = { + { "vco", STM32F4_RCC_PLLCFGR, 24, 25 }, + { "vco-i2s", STM32F4_RCC_PLLI2SCFGR, 26, 27 }, + { "vco-sai", STM32F4_RCC_PLLSAICFGR, 28, 29 }, +}; + + +static const struct clk_div_table post_divr_table[] = { + { 0, 2 }, { 1, 4 }, { 2, 8 }, { 3, 16 }, { 0 } +}; + +#define MAX_POST_DIV 3 +static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = { + { CLK_I2SQ_PDIV, PLL_VCO_I2S, "plli2s-q-div", "plli2s-q", + CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL}, + + { CLK_SAIQ_PDIV, PLL_VCO_SAI, "pllsai-q-div", "pllsai-q", + CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL }, + + { NO_IDX, PLL_VCO_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT, + STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table }, +}; + +struct stm32f4_div_data { + u8 shift; + u8 width; + u8 flag_div; + const struct clk_div_table *div_table; +}; + +#define MAX_PLL_DIV 3 +static const struct stm32f4_div_data div_data[MAX_PLL_DIV] = { + { 16, 2, 0, pll_divp_table }, + { 24, 4, 0, pll_divq_table }, + { 28, 3, 0, pll_divr_table }, +}; + +struct stm32f4_pll_data { + u8 pll_num; + u8 n_start; + const char *div_name[MAX_PLL_DIV]; +}; + +static const struct stm32f4_pll_data stm32f429_pll[MAX_PLL_DIV] = { + { PLL, 192, { "pll", "pll48", NULL } }, + { PLL_I2S, 192, { NULL, "plli2s-q", "plli2s-r" } }, + { PLL_SAI, 49, { NULL, "pllsai-q", "pllsai-r" } }, +}; + +static const struct stm32f4_pll_data stm32f469_pll[MAX_PLL_DIV] = { + { PLL, 50, { "pll", "pll-q", "pll-r" } }, + { PLL_I2S, 50, { "plli2s-p", "plli2s-q", "plli2s-r" } }, + { PLL_SAI, 50, { "pllsai-p", "pllsai-q", "pllsai-r" } }, +}; + +static int stm32f4_pll_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +#define PLL_TIMEOUT 10000 + +static int stm32f4_pll_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + int bit_status; + unsigned int timeout = PLL_TIMEOUT; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + clk_gate_ops.enable(hw); + + do { + bit_status = !(readl(gate->reg) & BIT(pll->bit_rdy_idx)); + + } while (bit_status && --timeout); + + return bit_status; +} + +static void stm32f4_pll_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static unsigned long stm32f4_pll_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + unsigned long n; + + n = (readl(base + pll->offset) >> 6) & 0x1ff; + + return parent_rate * n; +} + +static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + unsigned long n; + + n = rate / *prate; + + if (n < pll->n_start) + n = pll->n_start; + else if (n > 432) + n = 432; + + return *prate * n; +} + +static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + + unsigned long n; + unsigned long val; + int pll_state; + + pll_state = stm32f4_pll_is_enabled(hw); + + if (pll_state) + stm32f4_pll_disable(hw); + + n = rate / parent_rate; + + val = readl(base + pll->offset) & ~(0x1ff << 6); + + writel(val | ((n & 0x1ff) << 6), base + pll->offset); + + if (pll_state) + stm32f4_pll_enable(hw); + + return 0; +} + +static const struct clk_ops stm32f4_pll_gate_ops = { + .enable = stm32f4_pll_enable, + .disable = stm32f4_pll_disable, + .is_enabled = stm32f4_pll_is_enabled, + .recalc_rate = stm32f4_pll_recalc, + .round_rate = stm32f4_pll_round_rate, + .set_rate = stm32f4_pll_set_rate, +}; + +struct stm32f4_pll_div { + struct clk_divider div; + struct clk_hw *hw_pll; +}; + +#define to_pll_div_clk(_div) container_of(_div, struct stm32f4_pll_div, div) + +static unsigned long stm32f4_pll_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long stm32f4_pll_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int stm32f4_pll_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int pll_state, ret; + + struct clk_divider *div = to_clk_divider(hw); + struct stm32f4_pll_div *pll_div = to_pll_div_clk(div); + + pll_state = stm32f4_pll_is_enabled(pll_div->hw_pll); + + if (pll_state) + stm32f4_pll_disable(pll_div->hw_pll); + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + + if (pll_state) + stm32f4_pll_enable(pll_div->hw_pll); + + return ret; +} + +static const struct clk_ops stm32f4_pll_div_ops = { + .recalc_rate = stm32f4_pll_div_recalc_rate, + .round_rate = stm32f4_pll_div_round_rate, + .set_rate = stm32f4_pll_div_set_rate, +}; + +static struct clk_hw *clk_register_pll_div(const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + struct clk_hw *pll_hw, spinlock_t *lock) +{ + struct stm32f4_pll_div *pll_div; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + /* allocate the divider */ + pll_div = kzalloc(sizeof(*pll_div), GFP_KERNEL); + if (!pll_div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &stm32f4_pll_div_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_divider assignments */ + pll_div->div.reg = reg; + pll_div->div.shift = shift; + pll_div->div.width = width; + pll_div->div.flags = clk_divider_flags; + pll_div->div.lock = lock; + pll_div->div.table = table; + pll_div->div.hw.init = &init; + + pll_div->hw_pll = pll_hw; + + /* register the clock */ + hw = &pll_div->div.hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll_div); + hw = ERR_PTR(ret); + } + + return hw; +} + +static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc, + const struct stm32f4_pll_data *data, spinlock_t *lock) +{ + struct stm32f4_pll *pll; + struct clk_init_data init = { NULL }; + void __iomem *reg; + struct clk_hw *pll_hw; + int ret; + int i; + const struct stm32f4_vco_data *vco; + + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + vco = &vco_data[data->pll_num]; + + init.name = vco->vco_name; + init.ops = &stm32f4_pll_gate_ops; + /* init.flags = CLK_SET_RATE_GATE; */ + init.parent_names = &pllsrc; + init.num_parents = 1; + + pll->gate.lock = lock; + pll->gate.reg = base + STM32F4_RCC_CR; + pll->gate.shift = vco->bit_idx; + pll->gate.hw.init = &init; + + pll->offset = vco->offset; + pll->n_start = data->n_start; + pll->bit_rdy_idx = vco->bit_rdy_idx; + pll->status = (readl(base + STM32F4_RCC_CR) >> vco->bit_idx) & 0x1; + + reg = base + pll->offset; + + pll_hw = &pll->gate.hw; + ret = clk_hw_register(NULL, pll_hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + for (i = 0; i < MAX_PLL_DIV; i++) + if (data->div_name[i]) + clk_register_pll_div(data->div_name[i], + vco->vco_name, + 0, + reg, + div_data[i].shift, + div_data[i].width, + div_data[i].flag_div, + div_data[i].div_table, + pll_hw, + lock); + return pll_hw; +} + +/* + * Converts the primary and secondary indices (as they appear in DT) to an + * offset into our struct clock array. + */ +static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) +{ + u64 table[MAX_GATE_MAP]; + + if (primary == 1) { + if (WARN_ON(secondary >= stm32fx_end_primary_clk)) + return -EINVAL; + return secondary; + } + + memcpy(table, stm32f4_gate_map, sizeof(table)); + + /* only bits set in table can be used as indices */ + if (WARN_ON(secondary >= BITS_PER_BYTE * sizeof(table) || + 0 == (table[BIT_ULL_WORD(secondary)] & + BIT_ULL_MASK(secondary)))) + return -EINVAL; + + /* mask out bits above our current index */ + table[BIT_ULL_WORD(secondary)] &= + GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); + + return stm32fx_end_primary_clk - 1 + hweight64(table[0]) + + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); +} + +static struct clk_hw * +stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) +{ + int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]); + + if (i < 0) + return ERR_PTR(-EINVAL); + + return clks[i]; +} + +#define to_rgclk(_rgate) container_of(_rgate, struct stm32_rgate, gate) + +static inline void disable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8)); +} + +static inline void enable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8)); +} + +static inline void sofware_reset_backup_domain(void) +{ + unsigned long val; + + val = readl(base + STM32F4_RCC_BDCR); + writel(val | BIT(16), base + STM32F4_RCC_BDCR); + writel(val & ~BIT(16), base + STM32F4_RCC_BDCR); +} + +struct stm32_rgate { + struct clk_gate gate; + u8 bit_rdy_idx; +}; + +#define RGATE_TIMEOUT 50000 + +static int rgclk_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_rgate *rgate = to_rgclk(gate); + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + disable_power_domain_write_protection(); + + clk_gate_ops.enable(hw); + + do { + bit_status = !(readl(gate->reg) & BIT(rgate->bit_rdy_idx)); + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); + + enable_power_domain_write_protection(); + + return bit_status; +} + +static void rgclk_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int rgclk_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops rgclk_ops = { + .enable = rgclk_enable, + .disable = rgclk_disable, + .is_enabled = rgclk_is_enabled, +}; + +static struct clk_hw *clk_register_rgate(struct device_d *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct stm32_rgate *rgate; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); + if (!rgate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &rgclk_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + rgate->bit_rdy_idx = bit_rdy_idx; + + rgate->gate.lock = lock; + rgate->gate.reg = reg; + rgate->gate.shift = bit_idx; + rgate->gate.hw.init = &init; + + hw = &rgate->gate.hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(rgate); + hw = ERR_PTR(ret); + } + + return hw; +} + +static int cclk_gate_enable(struct clk_hw *hw) +{ + int ret; + + disable_power_domain_write_protection(); + + ret = clk_gate_ops.enable(hw); + + enable_power_domain_write_protection(); + + return ret; +} + +static void cclk_gate_disable(struct clk_hw *hw) +{ + disable_power_domain_write_protection(); + + clk_gate_ops.disable(hw); + + enable_power_domain_write_protection(); +} + +static int cclk_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops cclk_gate_ops = { + .enable = cclk_gate_enable, + .disable = cclk_gate_disable, + .is_enabled = cclk_gate_is_enabled, +}; + +static int cclk_mux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int cclk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + int ret; + + disable_power_domain_write_protection(); + + sofware_reset_backup_domain(); + + ret = clk_mux_ops.set_parent(hw, index); + + enable_power_domain_write_protection(); + + return ret; +} + +static const struct clk_ops cclk_mux_ops = { + .get_parent = cclk_mux_get_parent, + .set_parent = cclk_mux_set_parent, +}; + +static struct clk_hw *stm32_register_cclk(struct device_d *dev, const char *name, + const char * const *parent_names, int num_parents, + void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags, + spinlock_t *lock) +{ + struct clk_hw *hw; + struct clk_gate *gate; + struct clk_mux *mux; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + kfree(gate); + hw = ERR_PTR(-EINVAL); + goto fail; + } + + gate->reg = reg; + gate->shift = bit_idx; + gate->flags = 0; + gate->lock = lock; + + mux->reg = reg; + mux->shift = shift; + mux->width = 2; + mux->flags = 0; + + hw = clk_hw_register_composite(dev, name, parent_names, num_parents, + &mux->hw, &cclk_mux_ops, + NULL, NULL, + &gate->hw, &cclk_gate_ops, + flags); + + if (IS_ERR(hw)) { + kfree(gate); + kfree(mux); + } + +fail: + return hw; +} + +static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; + +static const struct clk_div_table ahb_div_table[] = { + { 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 }, + { 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 }, + { 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 }, + { 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 }, + { 0 }, +}; + +static const struct clk_div_table apb_div_table[] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static const char *rtc_parents[4] = { + "no-clock", "lse", "lsi", "hse-rtc" +}; + +static const char *pll_src = "pll-src"; + +static const char *pllsrc_parent[2] = { "hsi", NULL }; + +static const char *dsi_parent[2] = { NULL, "pll-r" }; + +static const char *lcd_parent[1] = { "pllsai-r-div" }; + +static const char *i2s_parents[2] = { "plli2s-r", NULL }; + +static const char *sai_parents[4] = { "pllsai-q-div", "plli2s-q-div", NULL, + "no-clock" }; + +static const char *pll48_parents[2] = { "pll-q", "pllsai-p" }; + +static const char *sdmux_parents[2] = { "pll48", "sys" }; + +static const char *hdmi_parents[2] = { "lse", "hsi_div488" }; + +static const char *spdif_parent[1] = { "plli2s-p" }; + +static const char *lptim_parent[4] = { "apb1_mul", "lsi", "hsi", "lse" }; + +static const char *uart_parents1[4] = { "apb2_div", "sys", "hsi", "lse" }; +static const char *uart_parents2[4] = { "apb1_div", "sys", "hsi", "lse" }; + +static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" }; + +static const char * const dfsdm1_src[] = { "apb2_div", "sys" }; +static const char * const adsfdm1_parent[] = { "sai1_clk", "sai2_clk" }; + +struct stm32_aux_clk { + int idx; + const char *name; + const char * const *parent_names; + int num_parents; + int offset_mux; + u8 shift; + u8 mask; + int offset_gate; + u8 bit_idx; + unsigned long flags; +}; + +struct stm32f4_clk_data { + const struct stm32f4_gate_data *gates_data; + const u64 *gates_map; + int gates_num; + const struct stm32f4_pll_data *pll_data; + const struct stm32_aux_clk *aux_clk; + int aux_clk_num; + int end_primary; +}; + +static const struct stm32_aux_clk stm32f429_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, +}; + +static const struct stm32_aux_clk stm32f469_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), + STM32F4_RCC_DCKCFGR, 27, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F4_RCC_DCKCFGR, 28, 1, + NO_GATE, 0, + 0 + }, + { + CLK_F469_DSI, "dsi", dsi_parent, ARRAY_SIZE(dsi_parent), + STM32F4_RCC_DCKCFGR, 29, 1, + STM32F4_RCC_APB2ENR, 27, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT + }, +}; + +static const struct stm32_aux_clk stm32f746_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), + STM32F7_RCC_DCKCFGR2, 27, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F7_RCC_DCKCFGR2, 28, 1, + NO_GATE, 0, + 0 + }, + { + CLK_HDMI_CEC, "hdmi-cec", + hdmi_parents, ARRAY_SIZE(hdmi_parents), + STM32F7_RCC_DCKCFGR2, 26, 1, + NO_GATE, 0, + 0 + }, + { + CLK_SPDIF, "spdif-rx", + spdif_parent, ARRAY_SIZE(spdif_parent), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + CLK_USART1, "usart1", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 0, 3, + STM32F4_RCC_APB2ENR, 4, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART2, "usart2", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 2, 3, + STM32F4_RCC_APB1ENR, 17, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART3, "usart3", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 4, 3, + STM32F4_RCC_APB1ENR, 18, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART4, "uart4", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 6, 3, + STM32F4_RCC_APB1ENR, 19, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART5, "uart5", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 8, 3, + STM32F4_RCC_APB1ENR, 20, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART6, "usart6", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 10, 3, + STM32F4_RCC_APB2ENR, 5, + CLK_SET_RATE_PARENT, + }, + + { + CLK_UART7, "uart7", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 12, 3, + STM32F4_RCC_APB1ENR, 30, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART8, "uart8", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 14, 3, + STM32F4_RCC_APB1ENR, 31, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C1, "i2c1", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 16, 3, + STM32F4_RCC_APB1ENR, 21, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C2, "i2c2", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 18, 3, + STM32F4_RCC_APB1ENR, 22, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C3, "i2c3", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 20, 3, + STM32F4_RCC_APB1ENR, 23, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C4, "i2c4", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB1ENR, 24, + CLK_SET_RATE_PARENT, + }, + + { + CLK_LPTIMER, "lptim1", + lptim_parent, ARRAY_SIZE(lptim_parent), + STM32F7_RCC_DCKCFGR2, 24, 3, + STM32F4_RCC_APB1ENR, 9, + CLK_SET_RATE_PARENT + }, +}; + +static const struct stm32_aux_clk stm32f769_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), + STM32F7_RCC_DCKCFGR2, 27, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux1", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F7_RCC_DCKCFGR2, 28, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux2", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F7_RCC_DCKCFGR2, 29, 1, + NO_GATE, 0, + 0 + }, + { + CLK_HDMI_CEC, "hdmi-cec", + hdmi_parents, ARRAY_SIZE(hdmi_parents), + STM32F7_RCC_DCKCFGR2, 26, 1, + NO_GATE, 0, + 0 + }, + { + CLK_SPDIF, "spdif-rx", + spdif_parent, ARRAY_SIZE(spdif_parent), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + CLK_USART1, "usart1", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 0, 3, + STM32F4_RCC_APB2ENR, 4, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART2, "usart2", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 2, 3, + STM32F4_RCC_APB1ENR, 17, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART3, "usart3", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 4, 3, + STM32F4_RCC_APB1ENR, 18, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART4, "uart4", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 6, 3, + STM32F4_RCC_APB1ENR, 19, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART5, "uart5", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 8, 3, + STM32F4_RCC_APB1ENR, 20, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART6, "usart6", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 10, 3, + STM32F4_RCC_APB2ENR, 5, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART7, "uart7", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 12, 3, + STM32F4_RCC_APB1ENR, 30, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART8, "uart8", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 14, 3, + STM32F4_RCC_APB1ENR, 31, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C1, "i2c1", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 16, 3, + STM32F4_RCC_APB1ENR, 21, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C2, "i2c2", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 18, 3, + STM32F4_RCC_APB1ENR, 22, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C3, "i2c3", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 20, 3, + STM32F4_RCC_APB1ENR, 23, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C4, "i2c4", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB1ENR, 24, + CLK_SET_RATE_PARENT, + }, + { + CLK_LPTIMER, "lptim1", + lptim_parent, ARRAY_SIZE(lptim_parent), + STM32F7_RCC_DCKCFGR2, 24, 3, + STM32F4_RCC_APB1ENR, 9, + CLK_SET_RATE_PARENT + }, + { + CLK_F769_DSI, "dsi", + dsi_parent, ARRAY_SIZE(dsi_parent), + STM32F7_RCC_DCKCFGR2, 0, 1, + STM32F4_RCC_APB2ENR, 27, + CLK_SET_RATE_PARENT + }, + { + CLK_DFSDM1, "dfsdm1", + dfsdm1_src, ARRAY_SIZE(dfsdm1_src), + STM32F4_RCC_DCKCFGR, 25, 1, + STM32F4_RCC_APB2ENR, 29, + CLK_SET_RATE_PARENT + }, + { + CLK_ADFSDM1, "adfsdm1", + adsfdm1_parent, ARRAY_SIZE(adsfdm1_parent), + STM32F4_RCC_DCKCFGR, 26, 1, + STM32F4_RCC_APB2ENR, 29, + CLK_SET_RATE_PARENT + }, +}; + +static const struct stm32f4_clk_data stm32f429_clk_data = { + .end_primary = END_PRIMARY_CLK, + .gates_data = stm32f429_gates, + .gates_map = stm32f42xx_gate_map, + .gates_num = ARRAY_SIZE(stm32f429_gates), + .pll_data = stm32f429_pll, + .aux_clk = stm32f429_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f429_aux_clk), +}; + +static const struct stm32f4_clk_data stm32f469_clk_data = { + .end_primary = END_PRIMARY_CLK, + .gates_data = stm32f469_gates, + .gates_map = stm32f46xx_gate_map, + .gates_num = ARRAY_SIZE(stm32f469_gates), + .pll_data = stm32f469_pll, + .aux_clk = stm32f469_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f469_aux_clk), +}; + +static const struct stm32f4_clk_data stm32f746_clk_data = { + .end_primary = END_PRIMARY_CLK_F7, + .gates_data = stm32f746_gates, + .gates_map = stm32f746_gate_map, + .gates_num = ARRAY_SIZE(stm32f746_gates), + .pll_data = stm32f469_pll, + .aux_clk = stm32f746_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk), +}; + +static const struct stm32f4_clk_data stm32f769_clk_data = { + .end_primary = END_PRIMARY_CLK_F7, + .gates_data = stm32f769_gates, + .gates_map = stm32f769_gate_map, + .gates_num = ARRAY_SIZE(stm32f769_gates), + .pll_data = stm32f469_pll, + .aux_clk = stm32f769_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f769_aux_clk), +}; + +static const struct of_device_id stm32f4_of_match[] = { + { + .compatible = "st,stm32f42xx-rcc", + .data = &stm32f429_clk_data + }, + { + .compatible = "st,stm32f469-rcc", + .data = &stm32f469_clk_data + }, + { + .compatible = "st,stm32f746-rcc", + .data = &stm32f746_clk_data + }, + { + .compatible = "st,stm32f769-rcc", + .data = &stm32f769_clk_data + }, + {} +}; + +static struct clk_hw *stm32_register_aux_clk(const char *name, + const char * const *parent_names, int num_parents, + int offset_mux, u8 shift, u8 mask, + int offset_gate, u8 bit_idx, + unsigned long flags, spinlock_t *lock) +{ + struct clk_hw *hw; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL; + + if (offset_gate != NO_GATE) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + gate->reg = base + offset_gate; + gate->shift = bit_idx; + gate->flags = 0; + gate->lock = lock; + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (offset_mux != NO_MUX) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + mux->reg = base + offset_mux; + mux->shift = shift; + mux->width = hweight8(mask); + mux->flags = 0; + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + } + + if (mux_hw == NULL && gate_hw == NULL) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, mux_ops, + NULL, NULL, + gate_hw, gate_ops, + flags); + +fail: + if (IS_ERR(hw)) { + kfree(gate); + kfree(mux); + } + + return hw; +} + +static void __init stm32f4_rcc_init(struct device_node *np) +{ + const char *hse_clk, *i2s_in_clk; + int n; + const struct of_device_id *match; + const struct stm32f4_clk_data *data; + unsigned long pllm; + struct clk_hw *pll_src_hw; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%pOFn: unable to map resource\n", np); + return; + } + + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pdrm)) { + pdrm = NULL; + pr_warn("%s: Unable to get syscfg\n", __func__); + } + + match = of_match_node(stm32f4_of_match, np); + if (WARN_ON(!match)) + return; + + data = match->data; + + stm32fx_end_primary_clk = data->end_primary; + + clks = kmalloc_array(data->gates_num + stm32fx_end_primary_clk, + sizeof(*clks), GFP_KERNEL); + if (!clks) + goto fail; + + stm32f4_gate_map = data->gates_map; + + hse_clk = of_clk_get_parent_name(np, 0); + dsi_parent[0] = hse_clk; + pllsrc_parent[1] = hse_clk; + + i2s_in_clk = of_clk_get_parent_name(np, 1); + + i2s_parents[1] = i2s_in_clk; + sai_parents[2] = i2s_in_clk; + + if (of_device_is_compatible(np, "st,stm32f769-rcc")) { + clk_hw_register_gate(NULL, "dfsdm1_apb", "apb2_div", 0, + base + STM32F4_RCC_APB2ENR, 29, + CLK_IGNORE_UNUSED, &stm32f4_clk_lock); + dsi_parent[0] = pll_src; + sai_parents[3] = pll_src; + } + + clks[CLK_HSI] = clk_hw_register_fixed_rate(NULL, "hsi", + NULL, 0, 16000000); + + pll_src_hw = clk_hw_register_mux(NULL, pll_src, pllsrc_parent, + ARRAY_SIZE(pllsrc_parent), 0, + base + STM32F4_RCC_PLLCFGR, 22, 1, 0, + &stm32f4_clk_lock); + + pllm = readl(base + STM32F4_RCC_PLLCFGR) & 0x3f; + + clk_hw_register_fixed_factor(NULL, "vco_in", pll_src, + 0, 1, pllm); + + stm32f4_rcc_register_pll("vco_in", &data->pll_data[0], + &stm32f4_clk_lock); + + clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", + &data->pll_data[1], &stm32f4_clk_lock); + + clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in", + &data->pll_data[2], &stm32f4_clk_lock); + + for (n = 0; n < MAX_POST_DIV; n++) { + const struct stm32f4_pll_post_div_data *post_div; + struct clk_hw *hw; + + post_div = &post_div_data[n]; + + hw = clk_register_pll_div(post_div->name, + post_div->parent, + post_div->flag, + base + post_div->offset, + post_div->shift, + post_div->width, + post_div->flag_div, + post_div->div_table, + clks[post_div->pll_idx], + &stm32f4_clk_lock); + + if (post_div->idx != NO_IDX) + clks[post_div->idx] = hw; + } + + sys_parents[1] = hse_clk; + + clks[CLK_SYSCLK] = clk_hw_register_mux_table( + NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, + base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "ahb_div", "sys", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 4, 4, 0, ahb_div_table, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "apb1_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 10, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb1_mul", "apb1_div", + CLK_SET_RATE_PARENT, 12); + + clk_register_divider_table(NULL, "apb2_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 13, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb2_mul", "apb2_div", + CLK_SET_RATE_PARENT, 15); + + clks[SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", "ahb_div", + 0, 1, 8); + clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div", + 0, 1, 1); + + for (n = 0; n < data->gates_num; n++) { + const struct stm32f4_gate_data *gd; + unsigned int secondary; + int idx; + + gd = &data->gates_data[n]; + secondary = 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + + gd->bit_idx; + idx = stm32f4_rcc_lookup_clk_idx(0, secondary); + + if (idx < 0) + goto fail; + + clks[idx] = clk_hw_register_gate( + NULL, gd->name, gd->parent_name, gd->flags, + base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[idx])) { + pr_err("%pOF: Unable to register leaf clock %s\n", + np, gd->name); + goto fail; + } + } + + clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0, + base + STM32F4_RCC_CSR, 0, 1, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_LSI])) { + pr_err("Unable to register lsi clock\n"); + goto fail; + } + + clks[CLK_LSE] = clk_register_rgate(NULL, "lse", "clk-lse", 0, + base + STM32F4_RCC_BDCR, 0, 1, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_LSE])) { + pr_err("Unable to register lse clock\n"); + goto fail; + } + + clks[CLK_HSE_RTC] = clk_hw_register_divider(NULL, "hse-rtc", "clk-hse", + 0, base + STM32F4_RCC_CFGR, 16, 5, 0, + &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_HSE_RTC])) { + pr_err("Unable to register hse-rtc clock\n"); + goto fail; + } + + clks[CLK_RTC] = stm32_register_cclk(NULL, "rtc", rtc_parents, 4, + base + STM32F4_RCC_BDCR, 15, 8, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_RTC])) { + pr_err("Unable to register rtc clock\n"); + goto fail; + } + + for (n = 0; n < data->aux_clk_num; n++) { + const struct stm32_aux_clk *aux_clk; + struct clk_hw *hw; + + aux_clk = &data->aux_clk[n]; + + hw = stm32_register_aux_clk(aux_clk->name, + aux_clk->parent_names, aux_clk->num_parents, + aux_clk->offset_mux, aux_clk->shift, + aux_clk->mask, aux_clk->offset_gate, + aux_clk->bit_idx, aux_clk->flags, + &stm32f4_clk_lock); + + if (IS_ERR(hw)) { + pr_warn("Unable to register %s clk\n", aux_clk->name); + continue; + } + + if (aux_clk->idx != NO_IDX) + clks[aux_clk->idx] = hw; + } + + if (of_device_is_compatible(np, "st,stm32f746-rcc")) { + + clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0, + 1, 488); + + clks[CLK_PLL_SRC] = pll_src_hw; + } + + of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); + + return; +fail: + kfree(clks); +} +CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE_DRIVER(stm32f769_rcc, "st,stm32f769-rcc", stm32f4_rcc_init); diff --git a/include/asm-generic/bitsperlong.h b/include/asm-generic/bitsperlong.h index 836a1d4c83..20c055c6bd 100644 --- a/include/asm-generic/bitsperlong.h +++ b/include/asm-generic/bitsperlong.h @@ -9,4 +9,8 @@ #define BITS_PER_LONG 32 #endif /* CONFIG_64BIT */ +#ifndef BITS_PER_LONG_LONG +#define BITS_PER_LONG_LONG 64 +#endif + #endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/include/linux/clk.h b/include/linux/clk.h index 887cd79009..9bee204652 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -759,6 +759,11 @@ static inline unsigned int clk_hw_get_num_parents(const struct clk_hw *hw) return hw->clk.num_parents; } +static inline unsigned long clk_hw_get_flags(const struct clk_hw *hw) +{ + return hw->clk.flags; +} + int clk_name_set_parent(const char *clkname, const char *clkparentname); int clk_name_set_rate(const char *clkname, unsigned long rate); -- cgit v1.2.3 From 8f0c6557d027692a8bad5b2820fa99254ce2f89b Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 09:11:45 +0100 Subject: clocksource: add ARMv7-M SysTick driver The SysTick is a simple 24-bit system timer that's required for ARMv7-M implementations. Add a clocksource driver for it for Cortex-M system support. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131081146.1883859-1-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clocksource/Kconfig | 5 +++ drivers/clocksource/Makefile | 1 + drivers/clocksource/armv7m_systick.c | 87 ++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 drivers/clocksource/armv7m_systick.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index c6ca72d4e9..9fae1f2d35 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -115,4 +115,9 @@ config CLINT_TIMER This option enables the CLINT timer for RISC-V systems. The CLINT driver is usually used for NoMMU RISC-V systems. +config ARMV7M_SYSTICK + bool "Support for the ARMv7M system timer" if COMPILE_TEST + help + This option enables support for the ARMv7M system timer unit. + endmenu diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 21fd83a093..a4a7b84fae 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_CLOCKSOURCE_TI_DM) += timer-ti-dm.o obj-$(CONFIG_CLOCKSOURCE_TI_32K) += timer-ti-32k.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o +obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c new file mode 100644 index 0000000000..5f9222c50b --- /dev/null +++ b/drivers/clocksource/armv7m_systick.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) Maxime Coquelin 2015 + * Author: Maxime Coquelin + */ + +#include +#include +#include +#include +#include +#include + +#define SYST_CSR 0x00 +#define SYST_RVR 0x04 +#define SYST_CVR 0x08 +#define SYST_CALIB 0x0c + +#define SYSTICK_CTRL_EN BIT(0) +/* Clock source: 0 = Ref clock, 1 = CPU clock */ +#define SYSTICK_CTRL_CPU_CLK BIT(2) +#define SYSTICK_CAL_NOREF BIT(31) + +#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF + +static __iomem void *systick_base; + +static u64 armv7m_systick_clocksource_read(void) +{ + return SYSTICK_LOAD_RELOAD_MASK - readl(systick_base + SYST_CVR); +} + +static struct clocksource cs = { + .read = armv7m_systick_clocksource_read, + .mask = CLOCKSOURCE_MASK(24), + .shift = 0, +}; + +static int armv7m_systick_probe(struct device_d *dev) +{ + struct clk *clk = NULL; + u32 rate, cal; + int ret; + + systick_base = dev_request_mem_region_err_null(dev, 0); + if (!systick_base) + return -ENOENT; + + ret = of_property_read_u32(dev->device_node, "clock-frequency", &rate); + if (ret) { + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_enable(clk); + if (ret) + return ret; + + rate = clk_get_rate(clk); + if (!rate) + return -EINVAL; + } + + writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, systick_base + SYST_RVR); + + cal = readl(&systick_base + SYST_CALIB); + if (cal & SYSTICK_CAL_NOREF) + writel(SYSTICK_CTRL_EN | SYSTICK_CTRL_CPU_CLK, systick_base + SYST_CSR); + else + writel(SYSTICK_CTRL_EN, systick_base + SYST_CSR); + + cs.mult = clocksource_hz2mult(rate, cs.shift); + + return init_clock(&cs); +} + +static struct of_device_id armv7m_systick_dt_ids[] = { + { .compatible = "arm,armv7m-systick", }, + { } +}; + +static struct driver_d armv7m_systick_driver = { + .name = "armv7m-systick-timer", + .probe = armv7m_systick_probe, + .of_compatible = DRV_OF_COMPAT(armv7m_systick_dt_ids), +}; +postcore_platform_driver(armv7m_systick_driver); -- cgit v1.2.3 From 398557fe59bb91113059b091ab86731fd97c49fc Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 31 Jan 2022 09:11:46 +0100 Subject: clocksource: add STM32 Timer driver The STM32 timer is 32-bit and thus takes longer to wrap around than the 24-bit SysTick timer. Add a driver for it at a higher priority. Signed-off-by: Ahmad Fatoum Link: https://lore.barebox.org/20220131081146.1883859-2-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer --- drivers/clocksource/Kconfig | 4 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-stm32.c | 122 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 drivers/clocksource/timer-stm32.c (limited to 'drivers') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 9fae1f2d35..e1bff23320 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -120,4 +120,8 @@ config ARMV7M_SYSTICK help This option enables support for the ARMv7M system timer unit. +config CLKSRC_STM32 + bool "Clocksource for STM32 SoCs" + depends on OFDEVICE && (ARCH_STM32 || COMPILE_TEST) + endmenu diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index a4a7b84fae..eceaa990d4 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_CLOCKSOURCE_TI_32K) += timer-ti-32k.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o +obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c new file mode 100644 index 0000000000..dec48fccf5 --- /dev/null +++ b/drivers/clocksource/timer-stm32.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author(s): Patrice Chotard, for STMicroelectronics. + */ + +#include +#include +#include +#include +#include + +/* Timer control1 register */ +#define CR1_CEN BIT(0) +#define CR1_ARPE BIT(7) + +/* Event Generation Register register */ +#define EGR_UG BIT(0) + +/* Auto reload register for free running config */ +#define GPT_FREE_RUNNING 0xFFFFFFFF + +#define MHZ_1 1000000 + +struct stm32_timer_regs { + u32 cr1; + u32 cr2; + u32 smcr; + u32 dier; + u32 sr; + u32 egr; + u32 ccmr1; + u32 ccmr2; + u32 ccer; + u32 cnt; + u32 psc; + u32 arr; + u32 reserved; + u32 ccr1; + u32 ccr2; + u32 ccr3; + u32 ccr4; + u32 reserved1; + u32 dcr; + u32 dmar; + u32 tim2_5_or; +}; + +static struct stm32_timer_regs *timer_base; + +static u64 stm32_timer_read(void) +{ + return readl(&timer_base->cnt); +} + +/* A bit obvious isn't it? */ +static struct clocksource cs = { + .read = stm32_timer_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 0, + .priority = 100, +}; + +static int stm32_timer_probe(struct device_d *dev) +{ + struct resource *iores; + struct clk *clk; + u32 rate, psc; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + timer_base = IOMEM(iores->start); + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_enable(clk); + if (ret) + return ret; + + /* Stop the timer */ + clrbits_le32(&timer_base->cr1, CR1_CEN); + + /* get timer clock */ + rate = clk_get_rate(clk); + + /* we set timer prescaler to obtain a 1MHz timer counter frequency */ + psc = (rate / MHZ_1) - 1; + writel(psc, &timer_base->psc); + + /* Configure timer for auto-reload */ + setbits_le32(&timer_base->cr1, CR1_ARPE); + + /* load value for auto reload */ + writel(GPT_FREE_RUNNING, &timer_base->arr); + + /* start timer */ + setbits_le32(&timer_base->cr1, CR1_CEN); + + /* Update generation */ + setbits_le32(&timer_base->egr, EGR_UG); + + cs.mult = clocksource_hz2mult(MHZ_1, cs.shift); + + return init_clock(&cs); +} + +static struct of_device_id stm32_timer_dt_ids[] = { + { .compatible = "st,stm32-timer" }, + { /* sentinel */ } +}; + +static struct driver_d stm32_timer_driver = { + .name = "stm32-timer", + .probe = stm32_timer_probe, + .of_compatible = stm32_timer_dt_ids, +}; +postcore_platform_driver(stm32_timer_driver); -- cgit v1.2.3