|
@@ -240,3 +240,277 @@ static void __init hi3620_clk_init(struct device_node *np)
|
|
|
base);
|
|
|
}
|
|
|
CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
|
|
|
+
|
|
|
+struct hisi_mmc_clock {
|
|
|
+ unsigned int id;
|
|
|
+ const char *name;
|
|
|
+ const char *parent_name;
|
|
|
+ unsigned long flags;
|
|
|
+ u32 clken_reg;
|
|
|
+ u32 clken_bit;
|
|
|
+ u32 div_reg;
|
|
|
+ u32 div_off;
|
|
|
+ u32 div_bits;
|
|
|
+ u32 drv_reg;
|
|
|
+ u32 drv_off;
|
|
|
+ u32 drv_bits;
|
|
|
+ u32 sam_reg;
|
|
|
+ u32 sam_off;
|
|
|
+ u32 sam_bits;
|
|
|
+};
|
|
|
+
|
|
|
+struct clk_mmc {
|
|
|
+ struct clk_hw hw;
|
|
|
+ u32 id;
|
|
|
+ void __iomem *clken_reg;
|
|
|
+ u32 clken_bit;
|
|
|
+ void __iomem *div_reg;
|
|
|
+ u32 div_off;
|
|
|
+ u32 div_bits;
|
|
|
+ void __iomem *drv_reg;
|
|
|
+ u32 drv_off;
|
|
|
+ u32 drv_bits;
|
|
|
+ void __iomem *sam_reg;
|
|
|
+ u32 sam_off;
|
|
|
+ u32 sam_bits;
|
|
|
+};
|
|
|
+
|
|
|
+#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
|
|
|
+
|
|
|
+static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
|
|
|
+ { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
|
|
|
+ { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
|
|
|
+ { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
|
|
|
+ { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
|
|
|
+};
|
|
|
+
|
|
|
+static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
|
|
|
+ unsigned long parent_rate)
|
|
|
+{
|
|
|
+ switch (parent_rate) {
|
|
|
+ case 26000000:
|
|
|
+ return 13000000;
|
|
|
+ case 180000000:
|
|
|
+ return 25000000;
|
|
|
+ case 360000000:
|
|
|
+ return 50000000;
|
|
|
+ case 720000000:
|
|
|
+ return 100000000;
|
|
|
+ case 1440000000:
|
|
|
+ return 180000000;
|
|
|
+ default:
|
|
|
+ return parent_rate;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
+ unsigned long *best_parent_rate,
|
|
|
+ struct clk **best_parent_p)
|
|
|
+{
|
|
|
+ struct clk_mmc *mclk = to_mmc(hw);
|
|
|
+ unsigned long best = 0;
|
|
|
+
|
|
|
+ if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
|
|
|
+ rate = 13000000;
|
|
|
+ best = 26000000;
|
|
|
+ } else if (rate <= 26000000) {
|
|
|
+ rate = 25000000;
|
|
|
+ best = 180000000;
|
|
|
+ } else if (rate <= 52000000) {
|
|
|
+ rate = 50000000;
|
|
|
+ best = 360000000;
|
|
|
+ } else if (rate <= 100000000) {
|
|
|
+ rate = 100000000;
|
|
|
+ best = 720000000;
|
|
|
+ } else {
|
|
|
+ /* max is 180M */
|
|
|
+ rate = 180000000;
|
|
|
+ best = 1440000000;
|
|
|
+ }
|
|
|
+ *best_parent_rate = best;
|
|
|
+ return rate;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
|
|
|
+{
|
|
|
+ u32 i;
|
|
|
+
|
|
|
+ if (para >= 0) {
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
+ if (para % 2)
|
|
|
+ val |= 1 << (off + i);
|
|
|
+ else
|
|
|
+ val &= ~(1 << (off + i));
|
|
|
+ para = para >> 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
|
|
|
+{
|
|
|
+ struct clk_mmc *mclk = to_mmc(hw);
|
|
|
+ unsigned long flags;
|
|
|
+ u32 sam, drv, div, val;
|
|
|
+ static DEFINE_SPINLOCK(mmc_clk_lock);
|
|
|
+
|
|
|
+ switch (rate) {
|
|
|
+ case 13000000:
|
|
|
+ sam = 3;
|
|
|
+ drv = 1;
|
|
|
+ div = 1;
|
|
|
+ break;
|
|
|
+ case 25000000:
|
|
|
+ sam = 13;
|
|
|
+ drv = 6;
|
|
|
+ div = 6;
|
|
|
+ break;
|
|
|
+ case 50000000:
|
|
|
+ sam = 3;
|
|
|
+ drv = 6;
|
|
|
+ div = 6;
|
|
|
+ break;
|
|
|
+ case 100000000:
|
|
|
+ sam = 6;
|
|
|
+ drv = 4;
|
|
|
+ div = 6;
|
|
|
+ break;
|
|
|
+ case 180000000:
|
|
|
+ sam = 6;
|
|
|
+ drv = 4;
|
|
|
+ div = 7;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_irqsave(&mmc_clk_lock, flags);
|
|
|
+
|
|
|
+ val = readl_relaxed(mclk->clken_reg);
|
|
|
+ val &= ~(1 << mclk->clken_bit);
|
|
|
+ writel_relaxed(val, mclk->clken_reg);
|
|
|
+
|
|
|
+ val = readl_relaxed(mclk->sam_reg);
|
|
|
+ val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
|
|
|
+ writel_relaxed(val, mclk->sam_reg);
|
|
|
+
|
|
|
+ val = readl_relaxed(mclk->drv_reg);
|
|
|
+ val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
|
|
|
+ writel_relaxed(val, mclk->drv_reg);
|
|
|
+
|
|
|
+ val = readl_relaxed(mclk->div_reg);
|
|
|
+ val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
|
|
|
+ writel_relaxed(val, mclk->div_reg);
|
|
|
+
|
|
|
+ val = readl_relaxed(mclk->clken_reg);
|
|
|
+ val |= 1 << mclk->clken_bit;
|
|
|
+ writel_relaxed(val, mclk->clken_reg);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&mmc_clk_lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mmc_clk_prepare(struct clk_hw *hw)
|
|
|
+{
|
|
|
+ struct clk_mmc *mclk = to_mmc(hw);
|
|
|
+ unsigned long rate;
|
|
|
+
|
|
|
+ if (mclk->id == HI3620_MMC_CIUCLK1)
|
|
|
+ rate = 13000000;
|
|
|
+ else
|
|
|
+ rate = 25000000;
|
|
|
+
|
|
|
+ return mmc_clk_set_timing(hw, rate);
|
|
|
+}
|
|
|
+
|
|
|
+static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
+ unsigned long parent_rate)
|
|
|
+{
|
|
|
+ return mmc_clk_set_timing(hw, rate);
|
|
|
+}
|
|
|
+
|
|
|
+static struct clk_ops clk_mmc_ops = {
|
|
|
+ .prepare = mmc_clk_prepare,
|
|
|
+ .determine_rate = mmc_clk_determine_rate,
|
|
|
+ .set_rate = mmc_clk_set_rate,
|
|
|
+ .recalc_rate = mmc_clk_recalc_rate,
|
|
|
+};
|
|
|
+
|
|
|
+static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
|
|
|
+ void __iomem *base, struct device_node *np)
|
|
|
+{
|
|
|
+ struct clk_mmc *mclk;
|
|
|
+ struct clk *clk;
|
|
|
+ struct clk_init_data init;
|
|
|
+
|
|
|
+ mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
|
|
|
+ if (!mclk) {
|
|
|
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+ }
|
|
|
+
|
|
|
+ init.name = mmc_clk->name;
|
|
|
+ init.ops = &clk_mmc_ops;
|
|
|
+ init.flags = mmc_clk->flags | CLK_IS_BASIC;
|
|
|
+ init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
|
|
|
+ init.num_parents = (mmc_clk->parent_name ? 1 : 0);
|
|
|
+ mclk->hw.init = &init;
|
|
|
+
|
|
|
+ mclk->id = mmc_clk->id;
|
|
|
+ mclk->clken_reg = base + mmc_clk->clken_reg;
|
|
|
+ mclk->clken_bit = mmc_clk->clken_bit;
|
|
|
+ mclk->div_reg = base + mmc_clk->div_reg;
|
|
|
+ mclk->div_off = mmc_clk->div_off;
|
|
|
+ mclk->div_bits = mmc_clk->div_bits;
|
|
|
+ mclk->drv_reg = base + mmc_clk->drv_reg;
|
|
|
+ mclk->drv_off = mmc_clk->drv_off;
|
|
|
+ mclk->drv_bits = mmc_clk->drv_bits;
|
|
|
+ mclk->sam_reg = base + mmc_clk->sam_reg;
|
|
|
+ mclk->sam_off = mmc_clk->sam_off;
|
|
|
+ mclk->sam_bits = mmc_clk->sam_bits;
|
|
|
+
|
|
|
+ clk = clk_register(NULL, &mclk->hw);
|
|
|
+ if (WARN_ON(IS_ERR(clk)))
|
|
|
+ kfree(mclk);
|
|
|
+ return clk;
|
|
|
+}
|
|
|
+
|
|
|
+static void __init hi3620_mmc_clk_init(struct device_node *node)
|
|
|
+{
|
|
|
+ void __iomem *base;
|
|
|
+ int i, num = ARRAY_SIZE(hi3620_mmc_clks);
|
|
|
+ struct clk_onecell_data *clk_data;
|
|
|
+
|
|
|
+ if (!node) {
|
|
|
+ pr_err("failed to find pctrl node in DTS\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ base = of_iomap(node, 0);
|
|
|
+ if (!base) {
|
|
|
+ pr_err("failed to map pctrl\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
|
|
+ if (WARN_ON(!clk_data))
|
|
|
+ return;
|
|
|
+
|
|
|
+ clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
|
|
|
+ if (!clk_data->clks) {
|
|
|
+ pr_err("%s: fail to allocate mmc clk\n", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < num; i++) {
|
|
|
+ struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
|
|
|
+ clk_data->clks[mmc_clk->id] =
|
|
|
+ hisi_register_clk_mmc(mmc_clk, base, node);
|
|
|
+ }
|
|
|
+
|
|
|
+ clk_data->clk_num = num;
|
|
|
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
|
|
+}
|
|
|
+
|
|
|
+CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
|