ARM: mach-shmobile: clock-r8a7740: add USB clock
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tue, 24 Apr 2012 09:07:47 +0000 (02:07 -0700)
committerRafael J. Wysocki <rjw@sisk.pl>
Sat, 12 May 2012 20:24:21 +0000 (22:24 +0200)
R8A7740 USB needs many clocks for workaround,
and it has confusing name "usb24s" and "usb24".
This "usb24s" will be used by other clocks.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Simon Horman <horms@verge.net.au>
Acked-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
arch/arm/mach-shmobile/clock-r8a7740.c

index b6fa1b7..b9b1d73 100644 (file)
@@ -47,6 +47,7 @@
 #define PLLC01CR       0xe6150028
 
 #define SUBCKCR                0xe6150080
+#define USBCKCR                0xe615008c
 
 #define MSTPSR0                0xe6150030
 #define MSTPSR1                0xe6150038
@@ -181,6 +182,100 @@ static struct clk pllc1_div2_clk = {
        .parent         = &pllc1_clk,
 };
 
+/* USB clock */
+static struct clk *usb24s_parents[] = {
+       [0] = &system_clk,
+       [1] = &extal2_clk
+};
+
+static unsigned long usb24s_recalc(struct clk *clk)
+{
+       return clk->parent->rate;
+};
+
+static int usb24s_enable(struct clk *clk)
+{
+       __raw_writel(__raw_readl(USBCKCR) & ~(1 << 8), USBCKCR);
+
+       return 0;
+}
+
+static void usb24s_disable(struct clk *clk)
+{
+       __raw_writel(__raw_readl(USBCKCR) | (1 << 8), USBCKCR);
+}
+
+static int usb24s_set_parent(struct clk *clk, struct clk *parent)
+{
+       int i, ret;
+       u32 val;
+
+       if (!clk->parent_table || !clk->parent_num)
+               return -EINVAL;
+
+       /* Search the parent */
+       for (i = 0; i < clk->parent_num; i++)
+               if (clk->parent_table[i] == parent)
+                       break;
+
+       if (i == clk->parent_num)
+               return -ENODEV;
+
+       ret = clk_reparent(clk, parent);
+       if (ret < 0)
+               return ret;
+
+       val = __raw_readl(USBCKCR);
+       val &= ~(1 << 7);
+       val |= i << 7;
+       __raw_writel(val, USBCKCR);
+
+       return 0;
+}
+
+static struct sh_clk_ops usb24s_clk_ops = {
+       .recalc         = usb24s_recalc,
+       .enable         = usb24s_enable,
+       .disable        = usb24s_disable,
+       .set_parent     = usb24s_set_parent,
+};
+
+static struct clk usb24s_clk = {
+       .ops            = &usb24s_clk_ops,
+       .parent_table   = usb24s_parents,
+       .parent_num     = ARRAY_SIZE(usb24s_parents),
+       .parent         = &system_clk,
+};
+
+static unsigned long usb24_recalc(struct clk *clk)
+{
+       return clk->parent->rate /
+               ((__raw_readl(USBCKCR) & (1 << 6)) ? 1 : 2);
+};
+
+static int usb24_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 val;
+
+       /* closer to which ? parent->rate or parent->rate/2 */
+       val = __raw_readl(USBCKCR);
+       val &= ~(1 << 6);
+       val |= (rate > (clk->parent->rate / 4) * 3) << 6;
+       __raw_writel(val, USBCKCR);
+
+       return 0;
+}
+
+static struct sh_clk_ops usb24_clk_ops = {
+       .recalc         = usb24_recalc,
+       .set_rate       = usb24_set_rate,
+};
+
+static struct clk usb24_clk = {
+       .ops            = &usb24_clk_ops,
+       .parent         = &usb24s_clk,
+};
+
 struct clk *main_clks[] = {
        &extalr_clk,
        &extal1_clk,
@@ -196,6 +291,8 @@ struct clk *main_clks[] = {
        &pllc0_clk,
        &pllc1_clk,
        &pllc1_div2_clk,
+       &usb24s_clk,
+       &usb24_clk,
 };
 
 static void div4_kick(struct clk *clk)
@@ -223,7 +320,7 @@ static struct clk_div4_table div4_table = {
 
 enum {
        DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_HP,
-       DIV4_HPP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP,
+       DIV4_HPP, DIV4_USBP, DIV4_S, DIV4_ZB, DIV4_M3, DIV4_CP,
        DIV4_NR
 };
 
@@ -234,6 +331,7 @@ struct clk div4_clks[DIV4_NR] = {
        [DIV4_M1]       = SH_CLK_DIV4(&pllc1_clk, FRQCRA,  4, 0x6fff, CLK_ENABLE_ON_INIT),
        [DIV4_HP]       = SH_CLK_DIV4(&pllc1_clk, FRQCRB,  4, 0x6fff, 0),
        [DIV4_HPP]      = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 20, 0x6fff, 0),
+       [DIV4_USBP]     = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 16, 0x6fff, 0),
        [DIV4_S]        = SH_CLK_DIV4(&pllc1_clk, FRQCRC, 12, 0x6fff, 0),
        [DIV4_ZB]       = SH_CLK_DIV4(&pllc1_clk, FRQCRC,  8, 0x6fff, 0),
        [DIV4_M3]       = SH_CLK_DIV4(&pllc1_clk, FRQCRC,  4, 0x6fff, 0),
@@ -257,7 +355,9 @@ enum {
        MSTP222,
        MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
 
-       MSTP329, MSTP328, MSTP323,
+       MSTP329, MSTP328, MSTP323, MSTP320,
+
+       MSTP416, MSTP407, MSTP406,
 
        MSTP_NR
 };
@@ -282,6 +382,11 @@ static struct clk mstp_clks[MSTP_NR] = {
        [MSTP329] = SH_CLK_MSTP32(&r_clk,               SMSTPCR3, 29, 0), /* CMT10 */
        [MSTP328] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR3, 28, 0), /* FSI */
        [MSTP323] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */
+       [MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR3, 20, 0), /* USBF */
+
+       [MSTP416] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR4, 16, 0), /* USBHOST */
+       [MSTP407] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR4,  7, 0), /* USB-Func */
+       [MSTP406] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR4,  6, 0), /* USB Phy */
 };
 
 static struct clk_lookup lookups[] = {
@@ -300,6 +405,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_CON_ID("pllc0_clk",              &pllc0_clk),
        CLKDEV_CON_ID("pllc1_clk",              &pllc1_clk),
        CLKDEV_CON_ID("pllc1_div2_clk",         &pllc1_div2_clk),
+       CLKDEV_CON_ID("usb24s",                 &usb24s_clk),
 
        /* DIV4 clocks */
        CLKDEV_CON_ID("i_clk",                  &div4_clks[DIV4_I]),
@@ -337,6 +443,14 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh_cmt.10",              &mstp_clks[MSTP329]),
        CLKDEV_DEV_ID("sh_fsi2",                &mstp_clks[MSTP328]),
        CLKDEV_DEV_ID("i2c-sh_mobile.1",        &mstp_clks[MSTP323]),
+       CLKDEV_DEV_ID("renesas_usbhs",          &mstp_clks[MSTP320]),
+
+       /* ICK */
+       CLKDEV_ICK_ID("host",   "renesas_usbhs",        &mstp_clks[MSTP416]),
+       CLKDEV_ICK_ID("func",   "renesas_usbhs",        &mstp_clks[MSTP407]),
+       CLKDEV_ICK_ID("phy",    "renesas_usbhs",        &mstp_clks[MSTP406]),
+       CLKDEV_ICK_ID("pci",    "renesas_usbhs",        &div4_clks[DIV4_USBP]),
+       CLKDEV_ICK_ID("usb24",  "renesas_usbhs",        &usb24_clk),
 };
 
 void __init r8a7740_clock_init(u8 md_ck)