rk3188 plus: add delay line support
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rk3188 / delayline.c
1 /*
2  * Copyright (C) 2013 ROCKCHIP, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/device.h>
13 #include <linux/platform_device.h>
14 #include <linux/err.h>
15 #include <linux/clk.h>
16 #include <linux/interrupt.h>
17 #include <linux/io.h>
18 #include <linux/delay.h>
19 #include <linux/slab.h>
20
21 #include <mach/clock.h>
22
23 #define KHZ                             (1000UL)
24 #define MHZ                             (1000UL * 1000UL)
25 #define RK30_DELAYLINE_BASE             (RK30_TIMER0_BASE + 0x1000)
26 #define delayline_readl(offset)         readl_relaxed(RK30_DELAYLINE_BASE + offset)
27 #define delayline_writel(val, offset)   writel_relaxed(val, RK30_DELAYLINE_BASE + offset)
28
29 #define get_delayline_bits(con, mask, shift)\
30         ((delayline_readl((con)) >> (shift)) & (mask))
31 #define set_delayline_bits(val, mask, shift, con)\
32         delayline_writel(((delayline_readl(con) & (~(mask << shift))) | (val << shift)), con)
33 #define set_delayline_bits_w_msk(val, mask, shift, con)\
34         delayline_writel(((mask) << (shift + 16)) | ((val) << (shift)), (con))
35
36 #define DELAYLINE_CON0                  (0x0000)
37 #define DELAYLINE_CON1                  (0x0004)
38 #define DELAYLINE_STATUS                (0x0008)
39
40 #if 1
41 #define DELAYLINE_DBG(fmt, args...) printk(KERN_DEBUG "DELAYLINE_DBG:\t"fmt, ##args)
42 #else
43 #define DELAYLINE_DBG(fmt, args...) do {} while(0)
44 #endif
45
46 #define DELAYLINE_ERR(fmt, args...) printk("DELAYLINE_ERR:\t"fmt, ##args)
47
48 static struct clk *clk_delayline = NULL, *clk_xin24m = NULL, *clk_aclk_cpu = NULL;
49 static struct clk *clk_delayline_parents[2];
50
51 static unsigned long clk_delayline_recalc_freediv(struct clk *clk)
52 {
53         u32 div = get_delayline_bits(clk->clksel_con, clk->div_mask, clk->div_shift) + 1;
54         unsigned long rate = clk->parent->rate / div;
55
56         DELAYLINE_DBG("%s new clock rate is %lu (div %u)\n", clk->name, rate, div);
57         return rate;
58 }
59
60 static int clk_delayline_set_rate_freediv(struct clk *clk, unsigned long rate)
61 {
62         u32 div;
63         for (div = 0; div < clk->div_max; div++) {
64                 u32 new_rate = clk->parent->rate / (div + 1);
65                 if (new_rate <= rate) {
66                         set_delayline_bits(div, clk->div_mask, clk->div_shift, clk->clksel_con);
67                         DELAYLINE_DBG("clksel_set_rate_freediv for clock %s to rate %ld (div %d)\n", 
68                                         clk->name, rate, div + 1);
69                         return 0;
70                 }
71         }
72         return -ENOENT;
73 }
74
75 static int clk_delayline_set_parent(struct clk *clk, struct clk *parent)
76 {
77         u32 i;
78         if (unlikely(!clk->parents))
79                 return -EINVAL;
80         for (i = 0; (i < clk->parents_num); i++) {
81                 if (clk->parents[i] != parent)
82                         continue;
83                 set_delayline_bits(i, clk->src_mask, clk->src_shift, clk->clksel_con);
84                 return 0;
85         }
86         return -EINVAL;
87 }
88
89 static struct clk *clk_delayline_get_parent(struct clk *clk) {
90         return clk->parents[(delayline_readl(clk->clksel_con) >> clk->src_shift) & clk->src_mask];
91 }
92
93 int rk3188_get_delayline_value(void)
94 {
95         struct clk *clk = clk_delayline;
96         clk->set_parent(clk, clk_aclk_cpu);
97         clk->set_rate(clk, 75 * MHZ);
98
99         set_delayline_bits(0x4, 0xff, 8, DELAYLINE_CON1);
100         set_delayline_bits(0xff, 0xff, 0, DELAYLINE_CON1);
101         set_delayline_bits(0x1, 0x1, 5, clk->clksel_con);
102         mdelay(1);
103         set_delayline_bits(0x0, 0x1, 5, clk->clksel_con);
104         mdelay(1);
105         
106         while(get_delayline_bits(DELAYLINE_STATUS, 0x1, 8) == 0);
107
108         return delayline_readl(DELAYLINE_STATUS) & 0xff;
109 }
110 EXPORT_SYMBOL(rk3188_get_delayline_value);
111
112 static int __init rk3188_delayline_init(void)
113 {
114         DELAYLINE_DBG("%s......\n", __func__);
115
116         clk_delayline = clk_get(NULL, "delayline");
117         if (IS_ERR_OR_NULL(clk_delayline)) {
118                 DELAYLINE_ERR("%s: can not get clock 'delay_line'\n"
119                                 "\tplease check 'mach-rk3188/clock_data.c' had 'clk_delayline' or not\n", __func__);
120                 return -EINVAL;
121         }
122
123         clk_xin24m = clk_get(NULL, "xin24m");
124         if (IS_ERR_OR_NULL(clk_xin24m)) {
125                 DELAYLINE_ERR("%s: can not get parent clock 'xin24m'\n", __func__);
126                 return -EINVAL;
127         }
128         clk_delayline_parents[0] = clk_xin24m;
129
130         clk_aclk_cpu = clk_get(NULL, "aclk_cpu");
131         if (IS_ERR_OR_NULL(clk_aclk_cpu)) {
132                 DELAYLINE_ERR("%s: can not get parent clock 'aclk_cpu'\n", __func__);
133                 return -EINVAL;
134         }
135         clk_delayline_parents[1] = clk_aclk_cpu;
136
137         clk_delayline->clksel_con       = DELAYLINE_CON0;
138         clk_delayline->recalc           = clk_delayline_recalc_freediv;
139         clk_delayline->set_rate         = clk_delayline_set_rate_freediv;
140         clk_delayline->set_parent       = clk_delayline_set_parent;
141         clk_delayline->get_parent       = clk_delayline_get_parent;
142         clk_delayline->parents          = clk_delayline_parents;
143         clk_delayline->parents_num      = ARRAY_SIZE(clk_delayline_parents);
144
145         clk_delayline->parent = clk_delayline->get_parent(clk_delayline);
146         clk_delayline->rate = clk_delayline->recalc(clk_delayline);
147 #if 1
148         DELAYLINE_DBG("clksel_con=%08x\n", clk_delayline->clksel_con);
149         DELAYLINE_DBG("current parent=%s\n", clk_delayline->parent->name);
150         DELAYLINE_DBG("parents[0]=%s\n", clk_delayline->parents[0]->name);
151         DELAYLINE_DBG("parents[1]=%s\n", clk_delayline->parents[1]->name);
152         DELAYLINE_DBG("parents_num=%d\n", clk_delayline->parents_num);
153 #endif
154         return 0;
155 }
156
157 late_initcall(rk3188_delayline_init);
158
159 MODULE_DESCRIPTION("Driver for delayline");
160 MODULE_AUTHOR("chenxing, chenxing@rock-chips.com");
161 MODULE_LICENSE("GPL");