string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / drivers / rtc / rtc-tx4939.c
1 /*
2  * TX4939 internal RTC driver
3  * Based on RBTX49xx patch from CELF patch archive.
4  *
5  * This file is subject to the terms and conditions of the GNU General Public
6  * License.  See the file "COPYING" in the main directory of this archive
7  * for more details.
8  *
9  * (C) Copyright TOSHIBA CORPORATION 2005-2007
10  */
11 #include <linux/rtc.h>
12 #include <linux/platform_device.h>
13 #include <linux/interrupt.h>
14 #include <linux/io.h>
15 #include <asm/txx9/tx4939.h>
16
17 struct tx4939rtc_plat_data {
18         struct rtc_device *rtc;
19         struct tx4939_rtc_reg __iomem *rtcreg;
20 };
21
22 static struct tx4939rtc_plat_data *get_tx4939rtc_plat_data(struct device *dev)
23 {
24         return platform_get_drvdata(to_platform_device(dev));
25 }
26
27 static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
28 {
29         int i = 0;
30
31         __raw_writel(cmd, &rtcreg->ctl);
32         /* This might take 30us (next 32.768KHz clock) */
33         while (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_BUSY) {
34                 /* timeout on approx. 100us (@ GBUS200MHz) */
35                 if (i++ > 200 * 100)
36                         return -EBUSY;
37                 cpu_relax();
38         }
39         return 0;
40 }
41
42 static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
43 {
44         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
45         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
46         int i, ret;
47         unsigned char buf[6];
48
49         buf[0] = 0;
50         buf[1] = 0;
51         buf[2] = secs;
52         buf[3] = secs >> 8;
53         buf[4] = secs >> 16;
54         buf[5] = secs >> 24;
55         spin_lock_irq(&pdata->rtc->irq_lock);
56         __raw_writel(0, &rtcreg->adr);
57         for (i = 0; i < 6; i++)
58                 __raw_writel(buf[i], &rtcreg->dat);
59         ret = tx4939_rtc_cmd(rtcreg,
60                              TX4939_RTCCTL_COMMAND_SETTIME |
61                              (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
62         spin_unlock_irq(&pdata->rtc->irq_lock);
63         return ret;
64 }
65
66 static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
67 {
68         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
69         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
70         int i, ret;
71         unsigned long sec;
72         unsigned char buf[6];
73
74         spin_lock_irq(&pdata->rtc->irq_lock);
75         ret = tx4939_rtc_cmd(rtcreg,
76                              TX4939_RTCCTL_COMMAND_GETTIME |
77                              (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
78         if (ret) {
79                 spin_unlock_irq(&pdata->rtc->irq_lock);
80                 return ret;
81         }
82         __raw_writel(2, &rtcreg->adr);
83         for (i = 2; i < 6; i++)
84                 buf[i] = __raw_readl(&rtcreg->dat);
85         spin_unlock_irq(&pdata->rtc->irq_lock);
86         sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
87         rtc_time_to_tm(sec, tm);
88         return rtc_valid_tm(tm);
89 }
90
91 static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
92 {
93         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
94         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
95         int i, ret;
96         unsigned long sec;
97         unsigned char buf[6];
98
99         if (alrm->time.tm_sec < 0 ||
100             alrm->time.tm_min < 0 ||
101             alrm->time.tm_hour < 0 ||
102             alrm->time.tm_mday < 0 ||
103             alrm->time.tm_mon < 0 ||
104             alrm->time.tm_year < 0)
105                 return -EINVAL;
106         rtc_tm_to_time(&alrm->time, &sec);
107         buf[0] = 0;
108         buf[1] = 0;
109         buf[2] = sec;
110         buf[3] = sec >> 8;
111         buf[4] = sec >> 16;
112         buf[5] = sec >> 24;
113         spin_lock_irq(&pdata->rtc->irq_lock);
114         __raw_writel(0, &rtcreg->adr);
115         for (i = 0; i < 6; i++)
116                 __raw_writel(buf[i], &rtcreg->dat);
117         ret = tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_SETALARM |
118                              (alrm->enabled ? TX4939_RTCCTL_ALME : 0));
119         spin_unlock_irq(&pdata->rtc->irq_lock);
120         return ret;
121 }
122
123 static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
124 {
125         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
126         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
127         int i, ret;
128         unsigned long sec;
129         unsigned char buf[6];
130         u32 ctl;
131
132         spin_lock_irq(&pdata->rtc->irq_lock);
133         ret = tx4939_rtc_cmd(rtcreg,
134                              TX4939_RTCCTL_COMMAND_GETALARM |
135                              (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
136         if (ret) {
137                 spin_unlock_irq(&pdata->rtc->irq_lock);
138                 return ret;
139         }
140         __raw_writel(2, &rtcreg->adr);
141         for (i = 2; i < 6; i++)
142                 buf[i] = __raw_readl(&rtcreg->dat);
143         ctl = __raw_readl(&rtcreg->ctl);
144         alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
145         alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
146         spin_unlock_irq(&pdata->rtc->irq_lock);
147         sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
148         rtc_time_to_tm(sec, &alrm->time);
149         return rtc_valid_tm(&alrm->time);
150 }
151
152 static int tx4939_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
153 {
154         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
155
156         spin_lock_irq(&pdata->rtc->irq_lock);
157         tx4939_rtc_cmd(pdata->rtcreg,
158                        TX4939_RTCCTL_COMMAND_NOP |
159                        (enabled ? TX4939_RTCCTL_ALME : 0));
160         spin_unlock_irq(&pdata->rtc->irq_lock);
161         return 0;
162 }
163
164 static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id)
165 {
166         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev_id);
167         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
168         unsigned long events = RTC_IRQF;
169
170         spin_lock(&pdata->rtc->irq_lock);
171         if (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALMD) {
172                 events |= RTC_AF;
173                 tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_NOP);
174         }
175         spin_unlock(&pdata->rtc->irq_lock);
176         rtc_update_irq(pdata->rtc, 1, events);
177         return IRQ_HANDLED;
178 }
179
180 static const struct rtc_class_ops tx4939_rtc_ops = {
181         .read_time              = tx4939_rtc_read_time,
182         .read_alarm             = tx4939_rtc_read_alarm,
183         .set_alarm              = tx4939_rtc_set_alarm,
184         .set_mmss               = tx4939_rtc_set_mmss,
185         .alarm_irq_enable       = tx4939_rtc_alarm_irq_enable,
186 };
187
188 static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj,
189                                      struct bin_attribute *bin_attr,
190                                      char *buf, loff_t pos, size_t size)
191 {
192         struct device *dev = container_of(kobj, struct device, kobj);
193         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
194         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
195         ssize_t count;
196
197         spin_lock_irq(&pdata->rtc->irq_lock);
198         for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
199              count++, size--) {
200                 __raw_writel(pos++, &rtcreg->adr);
201                 *buf++ = __raw_readl(&rtcreg->dat);
202         }
203         spin_unlock_irq(&pdata->rtc->irq_lock);
204         return count;
205 }
206
207 static ssize_t tx4939_rtc_nvram_write(struct kobject *kobj,
208                                       struct bin_attribute *bin_attr,
209                                       char *buf, loff_t pos, size_t size)
210 {
211         struct device *dev = container_of(kobj, struct device, kobj);
212         struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
213         struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
214         ssize_t count;
215
216         spin_lock_irq(&pdata->rtc->irq_lock);
217         for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
218              count++, size--) {
219                 __raw_writel(pos++, &rtcreg->adr);
220                 __raw_writel(*buf++, &rtcreg->dat);
221         }
222         spin_unlock_irq(&pdata->rtc->irq_lock);
223         return count;
224 }
225
226 static struct bin_attribute tx4939_rtc_nvram_attr = {
227         .attr = {
228                 .name = "nvram",
229                 .mode = S_IRUGO | S_IWUSR,
230         },
231         .size = TX4939_RTC_REG_RAMSIZE,
232         .read = tx4939_rtc_nvram_read,
233         .write = tx4939_rtc_nvram_write,
234 };
235
236 static int __init tx4939_rtc_probe(struct platform_device *pdev)
237 {
238         struct rtc_device *rtc;
239         struct tx4939rtc_plat_data *pdata;
240         struct resource *res;
241         int irq, ret;
242
243         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
244         if (!res)
245                 return -ENODEV;
246         irq = platform_get_irq(pdev, 0);
247         if (irq < 0)
248                 return -ENODEV;
249         pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
250         if (!pdata)
251                 return -ENOMEM;
252         platform_set_drvdata(pdev, pdata);
253
254         if (!devm_request_mem_region(&pdev->dev, res->start,
255                                      resource_size(res), pdev->name))
256                 return -EBUSY;
257         pdata->rtcreg = devm_ioremap(&pdev->dev, res->start,
258                                      resource_size(res));
259         if (!pdata->rtcreg)
260                 return -EBUSY;
261
262         tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
263         if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt,
264                              IRQF_DISABLED, pdev->name, &pdev->dev) < 0)
265                 return -EBUSY;
266         rtc = rtc_device_register(pdev->name, &pdev->dev,
267                                   &tx4939_rtc_ops, THIS_MODULE);
268         if (IS_ERR(rtc))
269                 return PTR_ERR(rtc);
270         pdata->rtc = rtc;
271         ret = sysfs_create_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
272         if (ret)
273                 rtc_device_unregister(rtc);
274         return ret;
275 }
276
277 static int __exit tx4939_rtc_remove(struct platform_device *pdev)
278 {
279         struct tx4939rtc_plat_data *pdata = platform_get_drvdata(pdev);
280         struct rtc_device *rtc = pdata->rtc;
281
282         spin_lock_irq(&rtc->irq_lock);
283         tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
284         spin_unlock_irq(&rtc->irq_lock);
285         sysfs_remove_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
286         rtc_device_unregister(rtc);
287         platform_set_drvdata(pdev, NULL);
288         return 0;
289 }
290
291 static struct platform_driver tx4939_rtc_driver = {
292         .remove         = __exit_p(tx4939_rtc_remove),
293         .driver         = {
294                 .name   = "tx4939rtc",
295                 .owner  = THIS_MODULE,
296         },
297 };
298
299 static int __init tx4939rtc_init(void)
300 {
301         return platform_driver_probe(&tx4939_rtc_driver, tx4939_rtc_probe);
302 }
303
304 static void __exit tx4939rtc_exit(void)
305 {
306         platform_driver_unregister(&tx4939_rtc_driver);
307 }
308
309 module_init(tx4939rtc_init);
310 module_exit(tx4939rtc_exit);
311
312 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
313 MODULE_DESCRIPTION("TX4939 internal RTC driver");
314 MODULE_LICENSE("GPL");
315 MODULE_ALIAS("platform:tx4939rtc");