clk: stm32f4: Use CLK_OF_DECLARE_DRIVER initialization method
[sfrench/cifs-2.6.git] / drivers / clk / clk-stm32f4.c
index 02d6810084017212c04cf83ae7b27b586fd74ca7..fc585f370549a336ea12cc5c323dbc77aec69825 100644 (file)
 #include <linux/clk-provider.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
 
 #define STM32F4_RCC_PLLCFGR            0x04
 #define STM32F4_RCC_CFGR               0x08
@@ -31,6 +35,8 @@
 #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
 
 struct stm32f4_gate_data {
        u8      offset;
@@ -40,7 +46,7 @@ struct stm32f4_gate_data {
        unsigned long flags;
 };
 
-static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
+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" },
@@ -120,26 +126,113 @@ static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
        { STM32F4_RCC_APB2ENR, 26,      "ltdc",         "apb2_div" },
 };
 
-/*
- * MAX_CLKS is the maximum value in the enumeration below plus the combined
- * hweight of stm32f42xx_gate_map (plus one).
- */
-#define MAX_CLKS 74
+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",         "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" },
+       { STM32F4_RCC_APB2ENR, 26,      "ltdc",         "apb2_div" },
+};
 
-enum { SYSTICK, FCLK };
+enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK };
 
 /*
  * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
  * have gate bits associated with them. Its combined hweight is 71.
  */
-static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull,
-                                          0x0000000000000001ull,
-                                          0x04777f33f6fec9ffull };
+#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 *stm32f4_gate_map;
+
+static struct clk_hw **clks;
 
-static struct clk_hw *clks[MAX_CLKS];
 static DEFINE_SPINLOCK(stm32f4_clk_lock);
 static void __iomem *base;
 
+static struct regmap *pdrm;
+
 /*
  * "Multiplier" device for APBx clocks.
  *
@@ -256,15 +349,15 @@ static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk)
  */
 static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 {
-       u64 table[ARRAY_SIZE(stm32f42xx_gate_map)];
+       u64 table[MAX_GATE_MAP];
 
        if (primary == 1) {
-               if (WARN_ON(secondary > FCLK))
+               if (WARN_ON(secondary >= END_PRIMARY_CLK))
                        return -EINVAL;
                return secondary;
        }
 
-       memcpy(table, stm32f42xx_gate_map, sizeof(table));
+       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) ||
@@ -276,7 +369,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
        table[BIT_ULL_WORD(secondary)] &=
            GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0);
 
-       return FCLK + hweight64(table[0]) +
+       return 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);
 }
@@ -292,6 +385,212 @@ stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data)
        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 RTC_TIMEOUT 1000000
+
+static int rgclk_enable(struct clk_hw *hw)
+{
+       struct clk_gate *gate = to_clk_gate(hw);
+       struct stm32_rgate *rgate = to_rgclk(gate);
+       u32 reg;
+       int ret;
+
+       disable_power_domain_write_protection();
+
+       clk_gate_ops.enable(hw);
+
+       ret = readl_relaxed_poll_timeout_atomic(gate->reg, reg,
+                       reg & rgate->bit_rdy_idx, 1000, RTC_TIMEOUT);
+
+       enable_power_domain_write_protection();
+       return ret;
+}
+
+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 *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.bit_idx = 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 u8 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 *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->bit_idx = bit_idx;
+       gate->flags = 0;
+       gate->lock = lock;
+
+       mux->reg = reg;
+       mux->shift = shift;
+       mux->mask = 3;
+       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[] = {
@@ -308,10 +607,46 @@ static const struct clk_div_table apb_div_table[] = {
        { 0 },
 };
 
+static const char *rtc_parents[4] = {
+       "no-clock", "lse", "lsi", "hse-rtc"
+};
+
+struct stm32f4_clk_data {
+       const struct stm32f4_gate_data *gates_data;
+       const u64 *gates_map;
+       int gates_num;
+};
+
+static const struct stm32f4_clk_data stm32f429_clk_data = {
+       .gates_data     = stm32f429_gates,
+       .gates_map      = stm32f42xx_gate_map,
+       .gates_num      = ARRAY_SIZE(stm32f429_gates),
+};
+
+static const struct stm32f4_clk_data stm32f469_clk_data = {
+       .gates_data     = stm32f469_gates,
+       .gates_map      = stm32f46xx_gate_map,
+       .gates_num      = ARRAY_SIZE(stm32f469_gates),
+};
+
+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
+       },
+       {}
+};
+
 static void __init stm32f4_rcc_init(struct device_node *np)
 {
        const char *hse_clk;
        int n;
+       const struct of_device_id *match;
+       const struct stm32f4_clk_data *data;
 
        base = of_iomap(np, 0);
        if (!base) {
@@ -319,6 +654,25 @@ static void __init stm32f4_rcc_init(struct device_node *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;
+
+       clks = kmalloc_array(data->gates_num + 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);
 
        clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
@@ -351,11 +705,15 @@ static void __init stm32f4_rcc_init(struct device_node *np)
        clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div",
                                               0, 1, 1);
 
-       for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) {
-               const struct stm32f4_gate_data *gd = &stm32f4_gates[n];
-               unsigned int secondary =
-                   8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx;
-               int idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
+       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;
@@ -371,9 +729,44 @@ static void __init stm32f4_rcc_init(struct device_node *np)
                }
        }
 
+       clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0,
+                       base + STM32F4_RCC_CSR, 0, 2, 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, 2, 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;
+       }
+
        of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
        return;
 fail:
+       kfree(clks);
        iounmap(base);
 }
-CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);