Commit | Line | Data |
---|---|---|
362600fe RA |
1 | /* drivers/rtc/rtc-v3020.c |
2 | * | |
3 | * Copyright (C) 2006 8D Technologies inc. | |
4 | * Copyright (C) 2004 Compulab Ltd. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * Driver for the V3020 RTC | |
11 | * | |
12 | * Changelog: | |
13 | * | |
14 | * 10-May-2006: Raphael Assenat <raph@8d.com> | |
15 | * - Converted to platform driver | |
16 | * - Use the generic rtc class | |
17 | * | |
18 | * ??-???-2004: Someone at Compulab | |
19 | * - Initial driver creation. | |
20 | * | |
21 | */ | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/rtc.h> | |
26 | #include <linux/types.h> | |
27 | #include <linux/bcd.h> | |
28 | #include <linux/rtc-v3020.h> | |
f3d79b20 | 29 | #include <linux/delay.h> |
362600fe | 30 | |
c08cf9da | 31 | #include <linux/io.h> |
362600fe RA |
32 | |
33 | #undef DEBUG | |
34 | ||
35 | struct v3020 { | |
36 | void __iomem *ioaddress; | |
37 | int leftshift; | |
38 | struct rtc_device *rtc; | |
39 | }; | |
40 | ||
41 | static void v3020_set_reg(struct v3020 *chip, unsigned char address, | |
42 | unsigned char data) | |
43 | { | |
44 | int i; | |
45 | unsigned char tmp; | |
46 | ||
47 | tmp = address; | |
48 | for (i = 0; i < 4; i++) { | |
49 | writel((tmp & 1) << chip->leftshift, chip->ioaddress); | |
50 | tmp >>= 1; | |
f3d79b20 | 51 | udelay(1); |
362600fe RA |
52 | } |
53 | ||
54 | /* Commands dont have data */ | |
55 | if (!V3020_IS_COMMAND(address)) { | |
56 | for (i = 0; i < 8; i++) { | |
57 | writel((data & 1) << chip->leftshift, chip->ioaddress); | |
58 | data >>= 1; | |
f3d79b20 | 59 | udelay(1); |
362600fe RA |
60 | } |
61 | } | |
62 | } | |
63 | ||
64 | static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) | |
65 | { | |
c08cf9da | 66 | unsigned int data = 0; |
362600fe RA |
67 | int i; |
68 | ||
69 | for (i = 0; i < 4; i++) { | |
70 | writel((address & 1) << chip->leftshift, chip->ioaddress); | |
71 | address >>= 1; | |
f3d79b20 | 72 | udelay(1); |
362600fe RA |
73 | } |
74 | ||
75 | for (i = 0; i < 8; i++) { | |
76 | data >>= 1; | |
77 | if (readl(chip->ioaddress) & (1 << chip->leftshift)) | |
78 | data |= 0x80; | |
f3d79b20 | 79 | udelay(1); |
362600fe RA |
80 | } |
81 | ||
82 | return data; | |
83 | } | |
84 | ||
85 | static int v3020_read_time(struct device *dev, struct rtc_time *dt) | |
86 | { | |
87 | struct v3020 *chip = dev_get_drvdata(dev); | |
88 | int tmp; | |
89 | ||
90 | /* Copy the current time to ram... */ | |
91 | v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0); | |
92 | ||
93 | /* ...and then read constant values. */ | |
94 | tmp = v3020_get_reg(chip, V3020_SECONDS); | |
fe20ba70 | 95 | dt->tm_sec = bcd2bin(tmp); |
362600fe | 96 | tmp = v3020_get_reg(chip, V3020_MINUTES); |
fe20ba70 | 97 | dt->tm_min = bcd2bin(tmp); |
362600fe | 98 | tmp = v3020_get_reg(chip, V3020_HOURS); |
fe20ba70 | 99 | dt->tm_hour = bcd2bin(tmp); |
362600fe | 100 | tmp = v3020_get_reg(chip, V3020_MONTH_DAY); |
fe20ba70 | 101 | dt->tm_mday = bcd2bin(tmp); |
362600fe | 102 | tmp = v3020_get_reg(chip, V3020_MONTH); |
fe20ba70 | 103 | dt->tm_mon = bcd2bin(tmp) - 1; |
362600fe | 104 | tmp = v3020_get_reg(chip, V3020_WEEK_DAY); |
fe20ba70 | 105 | dt->tm_wday = bcd2bin(tmp); |
362600fe | 106 | tmp = v3020_get_reg(chip, V3020_YEAR); |
fe20ba70 | 107 | dt->tm_year = bcd2bin(tmp)+100; |
362600fe | 108 | |
c08cf9da MR |
109 | dev_dbg(dev, "\n%s : Read RTC values\n", __func__); |
110 | dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); | |
111 | dev_dbg(dev, "tm_min : %i\n", dt->tm_min); | |
112 | dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); | |
113 | dev_dbg(dev, "tm_year: %i\n", dt->tm_year); | |
114 | dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); | |
115 | dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); | |
116 | dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); | |
362600fe RA |
117 | |
118 | return 0; | |
119 | } | |
120 | ||
121 | ||
122 | static int v3020_set_time(struct device *dev, struct rtc_time *dt) | |
123 | { | |
124 | struct v3020 *chip = dev_get_drvdata(dev); | |
125 | ||
c08cf9da MR |
126 | dev_dbg(dev, "\n%s : Setting RTC values\n", __func__); |
127 | dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); | |
128 | dev_dbg(dev, "tm_min : %i\n", dt->tm_min); | |
129 | dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); | |
130 | dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); | |
131 | dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); | |
132 | dev_dbg(dev, "tm_year: %i\n", dt->tm_year); | |
362600fe RA |
133 | |
134 | /* Write all the values to ram... */ | |
fe20ba70 AB |
135 | v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); |
136 | v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); | |
137 | v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); | |
138 | v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday)); | |
139 | v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); | |
140 | v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); | |
141 | v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); | |
362600fe RA |
142 | |
143 | /* ...and set the clock. */ | |
144 | v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0); | |
145 | ||
146 | /* Compulab used this delay here. I dont know why, | |
147 | * the datasheet does not specify a delay. */ | |
148 | /*mdelay(5);*/ | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
ff8371ac | 153 | static const struct rtc_class_ops v3020_rtc_ops = { |
362600fe RA |
154 | .read_time = v3020_read_time, |
155 | .set_time = v3020_set_time, | |
156 | }; | |
157 | ||
158 | static int rtc_probe(struct platform_device *pdev) | |
159 | { | |
160 | struct v3020_platform_data *pdata = pdev->dev.platform_data; | |
161 | struct v3020 *chip; | |
162 | struct rtc_device *rtc; | |
163 | int retval = -EBUSY; | |
164 | int i; | |
165 | int temp; | |
166 | ||
167 | if (pdev->num_resources != 1) | |
168 | return -EBUSY; | |
169 | ||
170 | if (pdev->resource[0].flags != IORESOURCE_MEM) | |
171 | return -EBUSY; | |
172 | ||
362600fe RA |
173 | chip = kzalloc(sizeof *chip, GFP_KERNEL); |
174 | if (!chip) | |
175 | return -ENOMEM; | |
176 | ||
177 | chip->leftshift = pdata->leftshift; | |
178 | chip->ioaddress = ioremap(pdev->resource[0].start, 1); | |
179 | if (chip->ioaddress == NULL) | |
180 | goto err_chip; | |
181 | ||
182 | /* Make sure the v3020 expects a communication cycle | |
183 | * by reading 8 times */ | |
184 | for (i = 0; i < 8; i++) | |
185 | temp = readl(chip->ioaddress); | |
186 | ||
187 | /* Test chip by doing a write/read sequence | |
188 | * to the chip ram */ | |
189 | v3020_set_reg(chip, V3020_SECONDS, 0x33); | |
c08cf9da | 190 | if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { |
362600fe RA |
191 | retval = -ENODEV; |
192 | goto err_io; | |
193 | } | |
194 | ||
195 | /* Make sure frequency measurment mode, test modes, and lock | |
196 | * are all disabled */ | |
197 | v3020_set_reg(chip, V3020_STATUS_0, 0x0); | |
198 | ||
6a15f46c | 199 | dev_info(&pdev->dev, "Chip available at physical address 0x%llx," |
362600fe | 200 | "data connected to D%d\n", |
6a15f46c | 201 | (unsigned long long)pdev->resource[0].start, |
362600fe RA |
202 | chip->leftshift); |
203 | ||
204 | platform_set_drvdata(pdev, chip); | |
205 | ||
206 | rtc = rtc_device_register("v3020", | |
207 | &pdev->dev, &v3020_rtc_ops, THIS_MODULE); | |
208 | if (IS_ERR(rtc)) { | |
209 | retval = PTR_ERR(rtc); | |
210 | goto err_io; | |
211 | } | |
212 | chip->rtc = rtc; | |
213 | ||
214 | return 0; | |
215 | ||
216 | err_io: | |
217 | iounmap(chip->ioaddress); | |
218 | err_chip: | |
219 | kfree(chip); | |
220 | ||
221 | return retval; | |
222 | } | |
223 | ||
224 | static int rtc_remove(struct platform_device *dev) | |
225 | { | |
226 | struct v3020 *chip = platform_get_drvdata(dev); | |
227 | struct rtc_device *rtc = chip->rtc; | |
228 | ||
229 | if (rtc) | |
230 | rtc_device_unregister(rtc); | |
231 | ||
232 | iounmap(chip->ioaddress); | |
233 | kfree(chip); | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | static struct platform_driver rtc_device_driver = { | |
239 | .probe = rtc_probe, | |
240 | .remove = rtc_remove, | |
241 | .driver = { | |
242 | .name = "v3020", | |
243 | .owner = THIS_MODULE, | |
244 | }, | |
245 | }; | |
246 | ||
247 | static __init int v3020_init(void) | |
248 | { | |
249 | return platform_driver_register(&rtc_device_driver); | |
250 | } | |
251 | ||
252 | static __exit void v3020_exit(void) | |
253 | { | |
254 | platform_driver_unregister(&rtc_device_driver); | |
255 | } | |
256 | ||
257 | module_init(v3020_init); | |
258 | module_exit(v3020_exit); | |
259 | ||
260 | MODULE_DESCRIPTION("V3020 RTC"); | |
261 | MODULE_AUTHOR("Raphael Assenat"); | |
262 | MODULE_LICENSE("GPL"); | |
ad28a07b | 263 | MODULE_ALIAS("platform:v3020"); |