]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/clk/sunxi/clk-sunxi.c
clk: sunxi: fix thinko in comment
[karo-tx-linux.git] / drivers / clk / sunxi / clk-sunxi.c
index abb6c5ac8a10297505a8a7f1cb1cb0d5f8eccf16..bd7dc733c1ca52170c2a5fd6418770794630235b 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/clkdev.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/reset-controller.h>
 
 #include "clk-factors.h"
 
@@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
        if (!gate)
                goto err_free_fixed;
 
+       of_property_read_string(node, "clock-output-names", &clk_name);
+
        /* set up gate and fixed rate properties */
        gate->reg = of_iomap(node, 0);
        gate->bit_idx = SUNXI_OSC24M_GATE;
@@ -77,7 +80,7 @@ err_free_gate:
 err_free_fixed:
        kfree(fixed);
 }
-CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup);
+CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
 
 
 
@@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
        *n = DIV_ROUND_UP(div, (*k+1));
 }
 
+/**
+ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6
+ * PLL6 rate is calculated as follows
+ * rate = parent_rate * n * (k + 1) / 2
+ * parent_rate is always 24Mhz
+ */
+
+static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
+                                      u8 *n, u8 *k, u8 *m, u8 *p)
+{
+       u8 div;
+
+       /*
+        * We always have 24MHz / 2, so we can just say that our
+        * parent clock is 12MHz.
+        */
+       parent_rate = parent_rate / 2;
 
+       /* Normalize value to a parent_rate multiple (24M / 2) */
+       div = *freq / parent_rate;
+       *freq = parent_rate * div;
+
+       /* we were called to round the frequency, we can now return */
+       if (n == NULL)
+               return;
+
+       *k = div / 32;
+       if (*k > 3)
+               *k = 3;
+
+       *n = DIV_ROUND_UP(div, (*k+1));
+}
 
 /**
  * sun4i_get_apb1_factors() - calculates m, p factors for APB1
@@ -265,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
        if (parent_rate < *freq)
                *freq = parent_rate;
 
-       parent_rate = (parent_rate + (*freq - 1)) / *freq;
+       parent_rate = DIV_ROUND_UP(parent_rate, *freq);
 
        /* Invalid rate! */
        if (parent_rate > 32)
@@ -296,7 +330,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate,
 
 /**
  * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks
- * MMC rate is calculated as follows
+ * MOD0 rate is calculated as follows
  * rate = (parent_rate >> p) / (m + 1);
  */
 
@@ -310,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate,
        if (*freq > parent_rate)
                *freq = parent_rate;
 
-       div = parent_rate / *freq;
+       div = DIV_ROUND_UP(parent_rate, *freq);
 
        if (div < 16)
                calcp = 0;
@@ -351,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
        if (*freq > parent_rate)
                *freq = parent_rate;
 
-       div = parent_rate / *freq;
+       div = DIV_ROUND_UP(parent_rate, *freq);
 
        if (div < 32)
                calcp = 0;
@@ -376,6 +410,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
 
 
 
+/**
+ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
+ *
+ * This clock looks something like this
+ *                               ________________________
+ *  MII TX clock from PHY >-----|___________    _________|----> to GMAC core
+ *  GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
+ *  Ext. 125MHz RGMII TX clk >--|__divider__/            |
+ *                              |________________________|
+ *
+ * The external 125 MHz reference is optional, i.e. GMAC can use its
+ * internal TX clock just fine. The A31 GMAC clock module does not have
+ * the divider controls for the external reference.
+ *
+ * To keep it simple, let the GMAC use either the MII TX clock for MII mode,
+ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should
+ * select the appropriate source and gate/ungate the output to the PHY.
+ *
+ * Only the GMAC should use this clock. Altering the clock so that it doesn't
+ * match the GMAC's operation parameters will result in the GMAC not being
+ * able to send traffic out. The GMAC driver should set the clock rate and
+ * enable/disable this clock to configure the required state. The clock
+ * driver then responds by auto-reparenting the clock.
+ */
+
+#define SUN7I_A20_GMAC_GPIT    2
+#define SUN7I_A20_GMAC_MASK    0x3
+#define SUN7I_A20_GMAC_PARENTS 2
+
+static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       struct clk_mux *mux;
+       struct clk_gate *gate;
+       const char *clk_name = node->name;
+       const char *parents[SUN7I_A20_GMAC_PARENTS];
+       void *reg;
+
+       if (of_property_read_string(node, "clock-output-names", &clk_name))
+               return;
+
+       /* allocate mux and gate clock structs */
+       mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+       if (!mux)
+               return;
+
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+       if (!gate)
+               goto free_mux;
+
+       /* gmac clock requires exactly 2 parents */
+       parents[0] = of_clk_get_parent_name(node, 0);
+       parents[1] = of_clk_get_parent_name(node, 1);
+       if (!parents[0] || !parents[1])
+               goto free_gate;
+
+       reg = of_iomap(node, 0);
+       if (!reg)
+               goto free_gate;
+
+       /* set up gate and fixed rate properties */
+       gate->reg = reg;
+       gate->bit_idx = SUN7I_A20_GMAC_GPIT;
+       gate->lock = &clk_lock;
+       mux->reg = reg;
+       mux->mask = SUN7I_A20_GMAC_MASK;
+       mux->flags = CLK_MUX_INDEX_BIT;
+       mux->lock = &clk_lock;
+
+       clk = clk_register_composite(NULL, clk_name,
+                       parents, SUN7I_A20_GMAC_PARENTS,
+                       &mux->hw, &clk_mux_ops,
+                       NULL, NULL,
+                       &gate->hw, &clk_gate_ops,
+                       0);
+
+       if (IS_ERR(clk))
+               goto iounmap_reg;
+
+       of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       clk_register_clkdev(clk, clk_name, NULL);
+
+       return;
+
+iounmap_reg:
+       iounmap(reg);
+free_gate:
+       kfree(gate);
+free_mux:
+       kfree(mux);
+}
+CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
+               sun7i_a20_gmac_clk_setup);
+
+
+
 /**
  * sunxi_factors_clk_setup() - Setup function for factor clocks
  */
@@ -387,6 +517,7 @@ struct factors_data {
        int mux;
        struct clk_factors_config *table;
        void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
+       const char *name;
 };
 
 static struct clk_factors_config sun4i_pll1_config = {
@@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = {
        .kwidth = 2,
 };
 
+static struct clk_factors_config sun6i_a31_pll6_config = {
+       .nshift = 8,
+       .nwidth = 5,
+       .kshift = 4,
+       .kwidth = 2,
+};
+
 static struct clk_factors_config sun4i_apb1_config = {
        .mshift = 0,
        .mwidth = 5,
@@ -451,10 +589,30 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = {
        .getter = sun6i_a31_get_pll1_factors,
 };
 
+static const struct factors_data sun7i_a20_pll4_data __initconst = {
+       .enable = 31,
+       .table = &sun4i_pll5_config,
+       .getter = sun4i_get_pll5_factors,
+};
+
 static const struct factors_data sun4i_pll5_data __initconst = {
        .enable = 31,
        .table = &sun4i_pll5_config,
        .getter = sun4i_get_pll5_factors,
+       .name = "pll5",
+};
+
+static const struct factors_data sun4i_pll6_data __initconst = {
+       .enable = 31,
+       .table = &sun4i_pll5_config,
+       .getter = sun4i_get_pll5_factors,
+       .name = "pll6",
+};
+
+static const struct factors_data sun6i_a31_pll6_data __initconst = {
+       .enable = 31,
+       .table = &sun6i_a31_pll6_config,
+       .getter = sun6i_a31_get_pll6_factors,
 };
 
 static const struct factors_data sun4i_apb1_data __initconst = {
@@ -497,14 +655,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
               (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
                i++;
 
-       /* Nodes should be providing the name via clock-output-names
-        * but originally our dts didn't, and so we used node->name.
-        * The new, better nodes look like clk@deadbeef, so we pull the
-        * name just in this case */
-       if (!strcmp("clk", clk_name)) {
-               of_property_read_string_index(node, "clock-output-names",
-                                             0, &clk_name);
-       }
+       /*
+        * some factor clocks, such as pll5 and pll6, may have multiple
+        * outputs, and have their name designated in factors_data
+        */
+       if (data->name)
+               clk_name = data->name;
+       else
+               of_property_read_string(node, "clock-output-names", &clk_name);
 
        factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
        if (!factors)
@@ -601,6 +759,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
               (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
                i++;
 
+       of_property_read_string(node, "clock-output-names", &clk_name);
+
        clk = clk_register_mux(NULL, clk_name, parents, i,
                               CLK_SET_RATE_NO_REPARENT, reg,
                               data->shift, SUNXI_MUX_GATE_WIDTH,
@@ -660,6 +820,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
 
        clk_parent = of_clk_get_parent_name(node, 0);
 
+       of_property_read_string(node, "clock-output-names", &clk_name);
+
        clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
                                   reg, data->shift, data->width,
                                   data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
@@ -672,6 +834,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
 
 
 
+/**
+ * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
+ */
+
+struct gates_reset_data {
+       void __iomem                    *reg;
+       spinlock_t                      *lock;
+       struct reset_controller_dev     rcdev;
+};
+
+static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
+                             unsigned long id)
+{
+       struct gates_reset_data *data = container_of(rcdev,
+                                                    struct gates_reset_data,
+                                                    rcdev);
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(data->lock, flags);
+
+       reg = readl(data->reg);
+       writel(reg & ~BIT(id), data->reg);
+
+       spin_unlock_irqrestore(data->lock, flags);
+
+       return 0;
+}
+
+static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
+                               unsigned long id)
+{
+       struct gates_reset_data *data = container_of(rcdev,
+                                                    struct gates_reset_data,
+                                                    rcdev);
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(data->lock, flags);
+
+       reg = readl(data->reg);
+       writel(reg | BIT(id), data->reg);
+
+       spin_unlock_irqrestore(data->lock, flags);
+
+       return 0;
+}
+
+static struct reset_control_ops sunxi_gates_reset_ops = {
+       .assert         = sunxi_gates_reset_assert,
+       .deassert       = sunxi_gates_reset_deassert,
+};
+
 /**
  * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
  */
@@ -680,6 +895,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
 
 struct gates_data {
        DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+       u32 reset_mask;
 };
 
 static const struct gates_data sun4i_axi_gates_data __initconst = {
@@ -746,10 +962,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = {
        .mask = { 0xff80ff },
 };
 
+static const struct gates_data sun4i_a10_usb_gates_data __initconst = {
+       .mask = {0x1C0},
+       .reset_mask = 0x07,
+};
+
+static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
+       .mask = {0x140},
+       .reset_mask = 0x03,
+};
+
 static void __init sunxi_gates_clk_setup(struct device_node *node,
                                         struct gates_data *data)
 {
        struct clk_onecell_data *clk_data;
+       struct gates_reset_data *reset_data;
        const char *clk_parent;
        const char *clk_name;
        void *reg;
@@ -793,6 +1020,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
        clk_data->clk_num = i;
 
        of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+       /* Register a reset controler for gates with reset bits */
+       if (data->reset_mask == 0)
+               return;
+
+       reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+       if (!reset_data)
+               return;
+
+       reset_data->reg = reg;
+       reset_data->lock = &clk_lock;
+       reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
+       reset_data->rcdev.ops = &sunxi_gates_reset_ops;
+       reset_data->rcdev.of_node = node;
+       reset_controller_register(&reset_data->rcdev);
 }
 
 
@@ -832,7 +1074,7 @@ static const struct divs_data pll5_divs_data __initconst = {
 };
 
 static const struct divs_data pll6_divs_data __initconst = {
-       .factors = &sun4i_pll5_data,
+       .factors = &sun4i_pll6_data,
        .div = {
                { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
                { .fixed = 2 }, /* P, other */
@@ -854,7 +1096,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
                                        struct divs_data *data)
 {
        struct clk_onecell_data *clk_data;
-       const char *parent  = node->name;
+       const char *parent;
        const char *clk_name;
        struct clk **clks, *pclk;
        struct clk_hw *gate_hw, *rate_hw;
@@ -868,6 +1110,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
 
        /* Set up factor clock that we will be dividing */
        pclk = sunxi_factors_clk_setup(node, data->factors);
+       parent = __clk_get_name(pclk);
 
        reg = of_iomap(node, 0);
 
@@ -970,56 +1213,60 @@ free_clkdata:
 
 /* Matches for factors clocks */
 static const struct of_device_id clk_factors_match[] __initconst = {
-       {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,},
+       {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,},
        {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
-       {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,},
-       {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,},
+       {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,},
+       {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
+       {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,},
+       {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,},
        {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
        {}
 };
 
 /* Matches for divider clocks */
 static const struct of_device_id clk_div_match[] __initconst = {
-       {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,},
-       {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,},
-       {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,},
+       {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,},
+       {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,},
+       {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,},
        {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,},
        {}
 };
 
 /* Matches for divided outputs */
 static const struct of_device_id clk_divs_match[] __initconst = {
-       {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,},
-       {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,},
+       {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,},
+       {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,},
        {}
 };
 
 /* Matches for mux clocks */
 static const struct of_device_id clk_mux_match[] __initconst = {
-       {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,},
-       {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
+       {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,},
+       {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,},
        {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,},
        {}
 };
 
 /* Matches for gate clocks */
 static const struct of_device_id clk_gates_match[] __initconst = {
-       {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,},
-       {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
+       {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,},
+       {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,},
        {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,},
        {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,},
        {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,},
        {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,},
-       {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
+       {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,},
        {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,},
        {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,},
        {.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,},
-       {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
+       {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,},
        {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,},
        {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,},
        {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,},
        {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,},
        {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
+       {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,},
+       {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,},
        {}
 };