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