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>
9 #include <linux/timer.h>
10 #include <linux/kdev_t.h>
12 #include <mach/regs-clock.h>
13 #include <asm/div64.h>
15 #include <plat/regs-timer.h>
16 #include <plat/s5p-clock.h>
18 #include <linux/stopwatch.h>
20 static struct class *stw_class = NULL;
21 static struct device *stw_dev = NULL;
23 static struct stopwatch_data stopwatch_data = {
25 .pwm_timer = 3, /* Use PWM 3 */
31 cycle_t get_stopwatch_tick(int timer)
33 return (cycle_t)~__raw_readl(S3C2410_TCNTO(timer));
35 EXPORT_SYMBOL(get_stopwatch_tick);
37 static ulong get_pclk(void)
46 clkdiv0 = __raw_readl(S5P_CLKDIV_CPU);
48 xtal_clk = clk_get(NULL, "xtal");
49 BUG_ON(IS_ERR(xtal_clk));
50 xtal = clk_get_rate(xtal_clk);
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);
57 if (__raw_readl(S5P_CLKSRC_CPU) & (1<<S5P_CLKSRC_CPU_MUXCORE_SHIFT))
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);
69 static int tcfg_divider[5] = {1,2,4,8,16};
71 ulong get_stopwatch_clk(int timer)
73 int prescaler, divider;
75 prescaler = __raw_readl(S3C2410_TCFG0);
76 divider = __raw_readl(S3C2410_TCFG1) &
78 divider = tcfg_divider[divider];
83 prescaler = prescaler & S3C2410_TCFG_PRESCALER0_MASK;
88 prescaler = (prescaler & S3C2410_TCFG_PRESCALER1_MASK) >> S3C2410_TCFG_PRESCALER1_SHIFT;
92 return get_pclk() / ((prescaler + 1) * divider);
94 EXPORT_SYMBOL(get_stopwatch_clk);
96 static void restart_stopwatch(int timer)
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);
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");
113 static ssize_t elapsed_show(struct device *dev, struct device_attribute *attr, char *buf)
118 struct stopwatch_data *pdata = &stopwatch_data;
120 pdata->t = get_stopwatch_tick(pdata->pwm_timer);
122 if (unlikely(!pdata->clk)) {
123 pdata->clk = get_stopwatch_clk(pdata->pwm_timer);
124 pdata->res = 1000000000 / pdata->clk; /* ns, 1 sec / clock */
128 t_rem = do_div(sec, pdata->clk);
129 nanosec = t_rem * pdata->res;
131 return sprintf(buf, "%lu.%lu\n", (ulong)sec, nanosec / 1000);
134 static ssize_t sys_elapsed_show(struct device *dev, struct device_attribute *attr, char *buf)
139 struct timeval systime;
140 struct stopwatch_data *pdata = &stopwatch_data;
142 do_gettimeofday(&systime);
143 pdata->t = get_stopwatch_tick(pdata->pwm_timer);
145 if (unlikely(!pdata->clk)) {
146 pdata->clk = get_stopwatch_clk(pdata->pwm_timer);
148 pdata->res = 1000000000 / pdata->clk; /* ns, 1 sec / clock */
154 t_rem = do_div(sec, pdata->clk);
155 nanosec = t_rem * pdata->res;
157 return sprintf(buf, "%lu.%lu - %ld.%ld\n", (ulong)sec, nanosec / 1000, systime.tv_sec, systime.tv_usec);
160 static ssize_t elapsed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
165 static ssize_t sys_elapsed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
170 static ssize_t restart_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
172 struct stopwatch_data *pdata = &stopwatch_data;
173 restart_stopwatch(pdata->pwm_timer);
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);
181 static struct attribute *stw_attributes[] = {
182 &dev_attr_elapsed.attr,
183 &dev_attr_sys_elapsed.attr,
184 &dev_attr_restart.attr,
187 static const struct attribute_group stw_group = {
188 .attrs = stw_attributes,
191 void stopwatch_put(const char * fmt, ...)
199 if (stopwatch_data.pwm_timer == -1) {
200 printk(KERN_ERR "%s: Failed to start stopwatch. You should set the pwm_timer. \n", __func__);
204 stopwatch_data.t = get_stopwatch_tick(stopwatch_data.pwm_timer);
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;
211 sec = stopwatch_data.t;
212 t_rem = do_div(sec, stopwatch_data.clk);
213 nanosec = t_rem * stopwatch_data.res;
215 printk("[STW:%5lu.%06lu] ", (ulong)sec, nanosec / 1000);
218 r = vprintk(fmt, args);
223 EXPORT_SYMBOL(stopwatch_put);
225 static int stopwatch_probe(struct platform_device *pdev)
227 struct stopwatch_data *pdata = &stopwatch_data;
229 platform_set_drvdata(pdev, pdata);
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);
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);
248 r = sysfs_create_group(&stw_dev->kobj, &stw_group);
250 dev_err(&pdev->dev, "failed to create sysfs files\n");
254 dev_set_drvdata(stw_dev, pdata);
259 device_destroy(stw_class, stw_dev->devt);
261 class_destroy(stw_class);
266 static int stopwatch_remove(struct platform_device *pdev)
268 sysfs_remove_group(&stw_dev->kobj, &stw_group);
269 device_destroy(stw_class, stw_dev->devt);
270 class_destroy(stw_class);
274 static struct platform_driver stopwatch_driver = {
275 .probe = stopwatch_probe,
276 .remove = stopwatch_remove,
279 .owner = THIS_MODULE,
284 struct platform_device s5pv310_device_stopwatch = {
289 static int __init stopwatch_init(void)
291 return platform_driver_register(&stopwatch_driver);
293 module_init(stopwatch_init);
295 static void __exit stopwatch_exit(void)
297 platform_driver_unregister(&stopwatch_driver);
299 module_exit(stopwatch_exit);
301 MODULE_AUTHOR("MinhoBan <mhban@samsung.com>");
302 MODULE_DESCRIPTION("samsung Stopwatch driver");
303 MODULE_LICENSE("GPL");