clk: at91: clk-utmi: add utmi support for sama7g5
authorClaudiu Beznea <claudiu.beznea@microchip.com>
Wed, 22 Jul 2020 07:38:25 +0000 (10:38 +0300)
committerStephen Boyd <sboyd@kernel.org>
Fri, 24 Jul 2020 09:19:08 +0000 (02:19 -0700)
Add UTMI support for SAMA7G5. SAMA7G5's UTMI control is done via
XTALF register. Values written at bits 2..0 in this register
correspond to the on board crystal oscillator frequency.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/1595403506-8209-18-git-send-email-claudiu.beznea@microchip.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/at91/clk-utmi.c
drivers/clk/at91/pmc.h
include/linux/clk/at91_pmc.h

index f1ef4e1..df9f3fc 100644 (file)
@@ -120,9 +120,11 @@ static const struct clk_ops utmi_ops = {
        .recalc_rate = clk_utmi_recalc_rate,
 };
 
-struct clk_hw * __init
-at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
-                      const char *name, const char *parent_name)
+static struct clk_hw * __init
+at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
+                               struct regmap *regmap_sfr,
+                               const char *name, const char *parent_name,
+                               const struct clk_ops *ops, unsigned long flags)
 {
        struct clk_utmi *utmi;
        struct clk_hw *hw;
@@ -134,10 +136,10 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
                return ERR_PTR(-ENOMEM);
 
        init.name = name;
-       init.ops = &utmi_ops;
+       init.ops = ops;
        init.parent_names = parent_name ? &parent_name : NULL;
        init.num_parents = parent_name ? 1 : 0;
-       init.flags = CLK_SET_RATE_GATE;
+       init.flags = flags;
 
        utmi->hw.init = &init;
        utmi->regmap_pmc = regmap_pmc;
@@ -152,3 +154,94 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
 
        return hw;
 }
+
+struct clk_hw * __init
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
+                      const char *name, const char *parent_name)
+{
+       return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
+                       parent_name, &utmi_ops, CLK_SET_RATE_GATE);
+}
+
+static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
+{
+       struct clk_utmi *utmi = to_clk_utmi(hw);
+       struct clk_hw *hw_parent;
+       unsigned long parent_rate;
+       unsigned int val;
+
+       hw_parent = clk_hw_get_parent(hw);
+       parent_rate = clk_hw_get_rate(hw_parent);
+
+       switch (parent_rate) {
+       case 16000000:
+               val = 0;
+               break;
+       case 20000000:
+               val = 2;
+               break;
+       case 24000000:
+               val = 3;
+               break;
+       case 32000000:
+               val = 5;
+               break;
+       default:
+               pr_err("UTMICK: unsupported main_xtal rate\n");
+               return -EINVAL;
+       }
+
+       regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
+
+       return 0;
+
+}
+
+static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
+{
+       struct clk_utmi *utmi = to_clk_utmi(hw);
+       struct clk_hw *hw_parent;
+       unsigned long parent_rate;
+       unsigned int val;
+
+       hw_parent = clk_hw_get_parent(hw);
+       parent_rate = clk_hw_get_rate(hw_parent);
+
+       regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
+       switch (val & 0x7) {
+       case 0:
+               if (parent_rate == 16000000)
+                       return 1;
+               break;
+       case 2:
+               if (parent_rate == 20000000)
+                       return 1;
+               break;
+       case 3:
+               if (parent_rate == 24000000)
+                       return 1;
+               break;
+       case 5:
+               if (parent_rate == 32000000)
+                       return 1;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct clk_ops sama7g5_utmi_ops = {
+       .prepare = clk_utmi_sama7g5_prepare,
+       .is_prepared = clk_utmi_sama7g5_is_prepared,
+       .recalc_rate = clk_utmi_recalc_rate,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
+                              const char *parent_name)
+{
+       return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
+                       parent_name, &sama7g5_utmi_ops, 0);
+}
index 6340b9b..7b86aff 100644 (file)
@@ -236,6 +236,10 @@ struct clk_hw * __init
 at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
                       const char *name, const char *parent_name);
 
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
+                              const char *parent_name);
+
 #ifdef CONFIG_PM
 void pmc_register_id(u8 id);
 void pmc_register_pck(u8 pck);
index dc5e85f..a4f82e8 100644 (file)
 #define                        AT91_PMC_PLLADIV2_ON            (1 << 12)
 #define                AT91_PMC_H32MXDIV       BIT(24)
 
+#define AT91_PMC_XTALF         0x34                    /* Main XTAL Frequency Register [SAMA7G5 only] */
+
 #define        AT91_PMC_USB            0x38                    /* USB Clock Register [some SAM9 only] */
 #define                AT91_PMC_USBS           (0x1 <<  0)             /* USB OHCI Input clock selection */
 #define                        AT91_PMC_USBS_PLLA              (0 << 0)