clk: X1000: Add support for calculat REFCLK of USB PHY.
author周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
Tue, 30 Jun 2020 16:38:52 +0000 (00:38 +0800)
committerStephen Boyd <sboyd@kernel.org>
Tue, 28 Jul 2020 01:18:14 +0000 (18:18 -0700)
Add functions for calculat the rate of REFCLK, which is needed by
USB PHY in Ingenic X1000 SoC.

Tested-by: 周正 (Zhou Zheng) <sernia.zhou@foxmail.com>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
Link: https://lore.kernel.org/r/20200630163852.47267-4-zhouyanjie@wanyeetech.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/ingenic/x1000-cgu.c

index 3cc3746..9aa20b5 100644 (file)
 #define USBPCR_SIDDQ           BIT(21)
 #define USBPCR_OTG_DISABLE     BIT(20)
 
+/* bits within the USBPCR1 register */
+#define USBPCR1_REFCLKSEL_SHIFT        26
+#define USBPCR1_REFCLKSEL_MASK (0x3 << USBPCR1_REFCLKSEL_SHIFT)
+#define USBPCR1_REFCLKSEL_CORE (0x2 << USBPCR1_REFCLKSEL_SHIFT)
+#define USBPCR1_REFCLKDIV_SHIFT        24
+#define USBPCR1_REFCLKDIV_MASK (0x3 << USBPCR1_REFCLKDIV_SHIFT)
+#define USBPCR1_REFCLKDIV_48   (0x2 << USBPCR1_REFCLKDIV_SHIFT)
+#define USBPCR1_REFCLKDIV_24   (0x1 << USBPCR1_REFCLKDIV_SHIFT)
+#define USBPCR1_REFCLKDIV_12   (0x0 << USBPCR1_REFCLKDIV_SHIFT)
+
 static struct ingenic_cgu *cgu;
 
+static unsigned long x1000_otg_phy_recalc_rate(struct clk_hw *hw,
+                                               unsigned long parent_rate)
+{
+       u32 usbpcr1;
+       unsigned refclk_div;
+
+       usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1);
+       refclk_div = usbpcr1 & USBPCR1_REFCLKDIV_MASK;
+
+       switch (refclk_div) {
+       case USBPCR1_REFCLKDIV_12:
+               return 12000000;
+
+       case USBPCR1_REFCLKDIV_24:
+               return 24000000;
+
+       case USBPCR1_REFCLKDIV_48:
+               return 48000000;
+       }
+
+       return parent_rate;
+}
+
+static long x1000_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate,
+                                     unsigned long *parent_rate)
+{
+       if (req_rate < 18000000)
+               return 12000000;
+
+       if (req_rate < 36000000)
+               return 24000000;
+
+       return 48000000;
+}
+
+static int x1000_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate,
+                                  unsigned long parent_rate)
+{
+       unsigned long flags;
+       u32 usbpcr1, div_bits;
+
+       switch (req_rate) {
+       case 12000000:
+               div_bits = USBPCR1_REFCLKDIV_12;
+               break;
+
+       case 24000000:
+               div_bits = USBPCR1_REFCLKDIV_24;
+               break;
+
+       case 48000000:
+               div_bits = USBPCR1_REFCLKDIV_48;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&cgu->lock, flags);
+
+       usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1);
+       usbpcr1 &= ~USBPCR1_REFCLKDIV_MASK;
+       usbpcr1 |= div_bits;
+       writel(usbpcr1, cgu->base + CGU_REG_USBPCR1);
+
+       spin_unlock_irqrestore(&cgu->lock, flags);
+       return 0;
+}
+
 static int x1000_usb_phy_enable(struct clk_hw *hw)
 {
        void __iomem *reg_opcr          = cgu->base + CGU_REG_OPCR;
@@ -80,6 +159,10 @@ static int x1000_usb_phy_is_enabled(struct clk_hw *hw)
 }
 
 static const struct clk_ops x1000_otg_phy_ops = {
+       .recalc_rate = x1000_otg_phy_recalc_rate,
+       .round_rate = x1000_otg_phy_round_rate,
+       .set_rate = x1000_otg_phy_set_rate,
+
        .enable         = x1000_usb_phy_enable,
        .disable        = x1000_usb_phy_disable,
        .is_enabled     = x1000_usb_phy_is_enabled,
@@ -144,7 +227,6 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
                },
        },
 
-
        /* Custom (SoC-specific) OTG PHY */
 
        [X1000_CLK_OTGPHY] = {