upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / stopwatch.c
1 #include <linux/module.h>
2 #include <linux/clocksource.h>
3 #include <linux/platform_device.h>
4 #include <linux/ctype.h>
5 #include <linux/workqueue.h>
6 #include <linux/gpio.h>
7 #include <linux/err.h>
8 #include <linux/pwm.h>
9 #include <linux/timer.h>
10 #include <linux/kdev_t.h>
11
12 #include <mach/regs-clock.h>
13 #include <asm/div64.h>
14 #include <plat/pll.h>
15 #include <plat/regs-timer.h>
16 #include <plat/s5p-clock.h>
17
18 #include <linux/stopwatch.h>
19
20 static struct class *stw_class = NULL;
21 static struct device *stw_dev = NULL;
22
23 static struct stopwatch_data stopwatch_data = {
24         .name           = "stopwatch",
25         .pwm_timer      = 3,    /* Use PWM 3 */
26         .clk            = 0,
27         .res            = 0,
28         .t              = 0,
29 };
30
31 cycle_t get_stopwatch_tick(int timer)
32 {
33         return (cycle_t)~__raw_readl(S3C2410_TCNTO(timer));
34 }
35 EXPORT_SYMBOL(get_stopwatch_tick);
36
37 static ulong get_pclk(void)
38 {
39         struct clk *xtal_clk;
40         unsigned long xtal;
41         unsigned long apll;
42         unsigned long mpll;
43         unsigned long pclk;
44         u32 clkdiv0;
45
46         clkdiv0 = __raw_readl(S5P_CLKDIV_CPU);
47
48         xtal_clk = clk_get(NULL, "xtal");
49         BUG_ON(IS_ERR(xtal_clk));
50         xtal = clk_get_rate(xtal_clk);
51         clk_put(xtal_clk);
52
53         apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON0), pll_4508);
54         mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON0), pll_4502);
55
56         /* set pclk source */
57         if (__raw_readl(S5P_CLKSRC_CPU) & (1<<S5P_CLKSRC_CPU_MUXCORE_SHIFT))
58                 pclk = apll;
59         else
60                 pclk = mpll;
61
62         pclk = pclk / GET_DIV(clkdiv0, S5P_CLKDIV_CPU0_CORE);
63         pclk = pclk / GET_DIV(clkdiv0, S5P_CLKDIV_CPU0_CORE2);
64         pclk = pclk / GET_DIV(clkdiv0, S5P_CLKDIV_CPU0_PERIPH);
65
66         return pclk;
67 }
68
69 static int tcfg_divider[5] = {1,2,4,8,16};
70
71 ulong get_stopwatch_clk(int timer)
72 {
73         int prescaler, divider;
74
75         prescaler = __raw_readl(S3C2410_TCFG0);
76         divider = __raw_readl(S3C2410_TCFG1) &
77                         (0xf << (4 * timer));
78         divider = tcfg_divider[divider];
79
80         switch (timer) {
81                 case 0:
82                 case 1:
83                         prescaler = prescaler & S3C2410_TCFG_PRESCALER0_MASK;
84                         break;
85                 case 2:
86                 case 3:
87                 case 4:
88                         prescaler = (prescaler & S3C2410_TCFG_PRESCALER1_MASK) >> S3C2410_TCFG_PRESCALER1_SHIFT;
89                         break;
90         }
91
92         return get_pclk() / ((prescaler + 1) * divider);
93 }
94 EXPORT_SYMBOL(get_stopwatch_clk);
95
96 static void restart_stopwatch(int timer)
97 {
98         u32 val;
99         u32 count_value = -1;
100
101         val = __raw_readl(S3C2410_TCON);
102         writel( val & ~S3C2410_TCON_T3START, S3C2410_TCON);
103         val = __raw_readl(S3C2410_TCON);
104         pr_debug("restart stopwatch!!!, con = %x\n", val);
105
106         writel(count_value, S3C2410_TCNTB(timer));
107         writel(val | S3C2410_TCON_T3MANUALUPD, S3C2410_TCON);
108         writel(val | S3C2410_TCON_T3RELOAD, S3C2410_TCON);
109         writel(val | S3C2410_TCON_T3START, S3C2410_TCON);
110         pr_info("restart stopwatch!!!\n");
111 }
112
113 static ssize_t elapsed_show(struct device *dev, struct device_attribute *attr, char *buf)
114 {
115         ulong t_rem;
116         u64 sec;
117         ulong nanosec;
118         struct stopwatch_data *pdata = &stopwatch_data;
119
120         pdata->t = get_stopwatch_tick(pdata->pwm_timer);
121
122         if (unlikely(!pdata->clk)) {
123                 pdata->clk = get_stopwatch_clk(pdata->pwm_timer);
124                 pdata->res = 1000000000 / pdata->clk; /* ns, 1 sec / clock */
125         }
126
127         sec = pdata->t;
128         t_rem = do_div(sec, pdata->clk);
129         nanosec = t_rem * pdata->res;
130
131         return sprintf(buf, "%lu.%lu\n", (ulong)sec, nanosec / 1000);
132 }
133
134 static ssize_t sys_elapsed_show(struct device *dev, struct device_attribute *attr, char *buf)
135 {
136         ulong t_rem;
137         u64 sec;
138         ulong nanosec;
139         struct timeval systime;
140         struct stopwatch_data *pdata = &stopwatch_data;
141
142         do_gettimeofday(&systime);
143         pdata->t = get_stopwatch_tick(pdata->pwm_timer);
144
145         if (unlikely(!pdata->clk)) {
146                 pdata->clk = get_stopwatch_clk(pdata->pwm_timer);
147                 if (pdata->clk)
148                         pdata->res = 1000000000 / pdata->clk; /* ns, 1 sec / clock */
149                 else
150                         return (ssize_t) 0;
151         }
152
153         sec = pdata->t;
154         t_rem = do_div(sec, pdata->clk);
155         nanosec = t_rem * pdata->res;
156
157         return sprintf(buf, "%lu.%lu - %ld.%ld\n", (ulong)sec, nanosec / 1000, systime.tv_sec, systime.tv_usec);
158 }
159
160 static ssize_t elapsed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
161 {
162         return count;
163 }
164
165 static ssize_t sys_elapsed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
166 {
167         return count;
168 }
169
170 static ssize_t restart_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
171 {
172         struct stopwatch_data *pdata = &stopwatch_data;
173         restart_stopwatch(pdata->pwm_timer);
174         return count;
175 }
176
177 static DEVICE_ATTR(elapsed, S_IRUGO | S_IWUGO, elapsed_show, elapsed_store);
178 static DEVICE_ATTR(sys_elapsed, S_IRUGO | S_IWUGO, sys_elapsed_show, sys_elapsed_store);
179 static DEVICE_ATTR(restart, S_IWUGO, NULL, restart_store);
180
181 static struct attribute *stw_attributes[] = {
182         &dev_attr_elapsed.attr,
183         &dev_attr_sys_elapsed.attr,
184         &dev_attr_restart.attr,
185         NULL
186 };
187 static const struct attribute_group stw_group = {
188         .attrs = stw_attributes,
189 };
190
191 void stopwatch_put(const char * fmt, ...)
192 {
193         va_list args;
194         int r;
195         ulong t_rem;
196         u64 sec;
197         ulong nanosec;
198
199         if (stopwatch_data.pwm_timer == -1) {
200                 printk(KERN_ERR "%s: Failed to start stopwatch. You should set the pwm_timer. \n", __func__);
201                 return;
202         }
203
204         stopwatch_data.t = get_stopwatch_tick(stopwatch_data.pwm_timer);
205
206         if (unlikely(!stopwatch_data.clk)) {
207                 stopwatch_data.clk = get_stopwatch_clk(stopwatch_data.pwm_timer);
208                 stopwatch_data.res = 1000000000 / stopwatch_data.clk;
209         }
210
211         sec = stopwatch_data.t;
212         t_rem = do_div(sec, stopwatch_data.clk);
213         nanosec = t_rem * stopwatch_data.res;
214
215         printk("[STW:%5lu.%06lu] ", (ulong)sec, nanosec / 1000);
216
217         va_start(args, fmt);
218         r = vprintk(fmt, args);
219         va_end(args);
220
221         return;
222 }
223 EXPORT_SYMBOL(stopwatch_put);
224
225 static int stopwatch_probe(struct platform_device *pdev)
226 {
227         struct stopwatch_data *pdata = &stopwatch_data;
228         int r = 0;
229         platform_set_drvdata(pdev, pdata);
230
231         // ToDo : create class and sysfs node to reveal stopwatch time
232         stw_class = class_create(THIS_MODULE, "stopwatch");
233         if(IS_ERR(stw_class)) {
234                 dev_err(&pdev->dev, "failed to create sysfs class\n");
235                 r = PTR_ERR(stw_class);
236                 stw_class = NULL;
237                 goto err_class;
238         }
239
240         stw_dev = device_create(stw_class, &pdev->dev, MKDEV(0, 0), NULL, "%s", pdata->name);
241         if(IS_ERR(stw_dev)) {
242                 dev_err(&pdev->dev, "failed to create device\n");
243                 r = PTR_ERR(stw_dev);
244                 stw_dev = NULL;
245                 goto err_dev;
246         }
247
248         r = sysfs_create_group(&stw_dev->kobj, &stw_group);
249         if(r) {
250                 dev_err(&pdev->dev, "failed to create sysfs files\n");
251                 goto err;
252         }
253
254         dev_set_drvdata(stw_dev, pdata);
255
256         return 0;
257
258 err:
259         device_destroy(stw_class, stw_dev->devt);
260 err_dev:
261         class_destroy(stw_class);
262 err_class:
263         return r;
264 }
265
266 static int stopwatch_remove(struct platform_device *pdev)
267 {
268         sysfs_remove_group(&stw_dev->kobj, &stw_group);
269         device_destroy(stw_class, stw_dev->devt);
270         class_destroy(stw_class);
271         return 0;
272 }
273
274 static struct platform_driver stopwatch_driver = {
275         .probe          = stopwatch_probe,
276         .remove         = stopwatch_remove,
277         .driver         = {
278                 .name   = "stopwatch",
279                 .owner  = THIS_MODULE,
280         },
281 };
282
283 /* Boot StopWatch */
284 struct platform_device s5pv310_device_stopwatch = {
285         .name           = "stopwatch",
286         .id             = -1,
287 };
288
289 static int __init stopwatch_init(void)
290 {
291         return platform_driver_register(&stopwatch_driver);
292 }
293 module_init(stopwatch_init);
294
295 static void __exit stopwatch_exit(void)
296 {
297         platform_driver_unregister(&stopwatch_driver);
298 }
299 module_exit(stopwatch_exit);
300
301 MODULE_AUTHOR("MinhoBan <mhban@samsung.com>");
302 MODULE_DESCRIPTION("samsung Stopwatch driver");
303 MODULE_LICENSE("GPL");