diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-abx80x.c | 307 | ||||
-rw-r--r-- | drivers/rtc/rtc-armada38x.c | 24 |
4 files changed, 330 insertions, 12 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6149ae01e11f..0fe4ad8826b2 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -164,6 +164,16 @@ config RTC_DRV_ABB5ZES3 | |||
164 | This driver can also be built as a module. If so, the module | 164 | This driver can also be built as a module. If so, the module |
165 | will be called rtc-ab-b5ze-s3. | 165 | will be called rtc-ab-b5ze-s3. |
166 | 166 | ||
167 | config RTC_DRV_ABX80X | ||
168 | tristate "Abracon ABx80x" | ||
169 | help | ||
170 | If you say yes here you get support for Abracon AB080X and AB180X | ||
171 | families of ultra-low-power battery- and capacitor-backed real-time | ||
172 | clock chips. | ||
173 | |||
174 | This driver can also be built as a module. If so, the module | ||
175 | will be called rtc-abx80x. | ||
176 | |||
167 | config RTC_DRV_AS3722 | 177 | config RTC_DRV_AS3722 |
168 | tristate "ams AS3722 RTC driver" | 178 | tristate "ams AS3722 RTC driver" |
169 | depends on MFD_AS3722 | 179 | depends on MFD_AS3722 |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index c31731c29762..2b82e2b0311b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o | |||
25 | obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o | 25 | obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o |
26 | obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o | 26 | obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o |
27 | obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o | 27 | obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o |
28 | obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o | ||
28 | obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o | 29 | obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o |
29 | obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o | 30 | obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o |
30 | obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o | 31 | obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o |
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c new file mode 100644 index 000000000000..4337c3bc6ace --- /dev/null +++ b/drivers/rtc/rtc-abx80x.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /* | ||
2 | * A driver for the I2C members of the Abracon AB x8xx RTC family, | ||
3 | * and compatible: AB 1805 and AB 0805 | ||
4 | * | ||
5 | * Copyright 2014-2015 Macq S.A. | ||
6 | * | ||
7 | * Author: Philippe De Muyter <phdm@macqel.be> | ||
8 | * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/bcd.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/rtc.h> | ||
20 | |||
21 | #define ABX8XX_REG_HTH 0x00 | ||
22 | #define ABX8XX_REG_SC 0x01 | ||
23 | #define ABX8XX_REG_MN 0x02 | ||
24 | #define ABX8XX_REG_HR 0x03 | ||
25 | #define ABX8XX_REG_DA 0x04 | ||
26 | #define ABX8XX_REG_MO 0x05 | ||
27 | #define ABX8XX_REG_YR 0x06 | ||
28 | #define ABX8XX_REG_WD 0x07 | ||
29 | |||
30 | #define ABX8XX_REG_CTRL1 0x10 | ||
31 | #define ABX8XX_CTRL_WRITE BIT(1) | ||
32 | #define ABX8XX_CTRL_12_24 BIT(6) | ||
33 | |||
34 | #define ABX8XX_REG_CFG_KEY 0x1f | ||
35 | #define ABX8XX_CFG_KEY_MISC 0x9d | ||
36 | |||
37 | #define ABX8XX_REG_ID0 0x28 | ||
38 | |||
39 | #define ABX8XX_REG_TRICKLE 0x20 | ||
40 | #define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0 | ||
41 | #define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 | ||
42 | #define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4 | ||
43 | |||
44 | static u8 trickle_resistors[] = {0, 3, 6, 11}; | ||
45 | |||
46 | enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, | ||
47 | AB1801, AB1803, AB1804, AB1805, ABX80X}; | ||
48 | |||
49 | struct abx80x_cap { | ||
50 | u16 pn; | ||
51 | bool has_tc; | ||
52 | }; | ||
53 | |||
54 | static struct abx80x_cap abx80x_caps[] = { | ||
55 | [AB0801] = {.pn = 0x0801}, | ||
56 | [AB0803] = {.pn = 0x0803}, | ||
57 | [AB0804] = {.pn = 0x0804, .has_tc = true}, | ||
58 | [AB0805] = {.pn = 0x0805, .has_tc = true}, | ||
59 | [AB1801] = {.pn = 0x1801}, | ||
60 | [AB1803] = {.pn = 0x1803}, | ||
61 | [AB1804] = {.pn = 0x1804, .has_tc = true}, | ||
62 | [AB1805] = {.pn = 0x1805, .has_tc = true}, | ||
63 | [ABX80X] = {.pn = 0} | ||
64 | }; | ||
65 | |||
66 | static struct i2c_driver abx80x_driver; | ||
67 | |||
68 | static int abx80x_enable_trickle_charger(struct i2c_client *client, | ||
69 | u8 trickle_cfg) | ||
70 | { | ||
71 | int err; | ||
72 | |||
73 | /* | ||
74 | * Write the configuration key register to enable access to the Trickle | ||
75 | * register | ||
76 | */ | ||
77 | err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, | ||
78 | ABX8XX_CFG_KEY_MISC); | ||
79 | if (err < 0) { | ||
80 | dev_err(&client->dev, "Unable to write configuration key\n"); | ||
81 | return -EIO; | ||
82 | } | ||
83 | |||
84 | err = i2c_smbus_write_byte_data(client, ABX8XX_REG_TRICKLE, | ||
85 | ABX8XX_TRICKLE_CHARGE_ENABLE | | ||
86 | trickle_cfg); | ||
87 | if (err < 0) { | ||
88 | dev_err(&client->dev, "Unable to write trickle register\n"); | ||
89 | return -EIO; | ||
90 | } | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
96 | { | ||
97 | struct i2c_client *client = to_i2c_client(dev); | ||
98 | unsigned char buf[8]; | ||
99 | int err; | ||
100 | |||
101 | err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH, | ||
102 | sizeof(buf), buf); | ||
103 | if (err < 0) { | ||
104 | dev_err(&client->dev, "Unable to read date\n"); | ||
105 | return -EIO; | ||
106 | } | ||
107 | |||
108 | tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F); | ||
109 | tm->tm_min = bcd2bin(buf[ABX8XX_REG_MN] & 0x7F); | ||
110 | tm->tm_hour = bcd2bin(buf[ABX8XX_REG_HR] & 0x3F); | ||
111 | tm->tm_wday = buf[ABX8XX_REG_WD] & 0x7; | ||
112 | tm->tm_mday = bcd2bin(buf[ABX8XX_REG_DA] & 0x3F); | ||
113 | tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F) - 1; | ||
114 | tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 100; | ||
115 | |||
116 | err = rtc_valid_tm(tm); | ||
117 | if (err < 0) | ||
118 | dev_err(&client->dev, "retrieved date/time is not valid.\n"); | ||
119 | |||
120 | return err; | ||
121 | } | ||
122 | |||
123 | static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
124 | { | ||
125 | struct i2c_client *client = to_i2c_client(dev); | ||
126 | unsigned char buf[8]; | ||
127 | int err; | ||
128 | |||
129 | if (tm->tm_year < 100) | ||
130 | return -EINVAL; | ||
131 | |||
132 | buf[ABX8XX_REG_HTH] = 0; | ||
133 | buf[ABX8XX_REG_SC] = bin2bcd(tm->tm_sec); | ||
134 | buf[ABX8XX_REG_MN] = bin2bcd(tm->tm_min); | ||
135 | buf[ABX8XX_REG_HR] = bin2bcd(tm->tm_hour); | ||
136 | buf[ABX8XX_REG_DA] = bin2bcd(tm->tm_mday); | ||
137 | buf[ABX8XX_REG_MO] = bin2bcd(tm->tm_mon + 1); | ||
138 | buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100); | ||
139 | buf[ABX8XX_REG_WD] = tm->tm_wday; | ||
140 | |||
141 | err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH, | ||
142 | sizeof(buf), buf); | ||
143 | if (err < 0) { | ||
144 | dev_err(&client->dev, "Unable to write to date registers\n"); | ||
145 | return -EIO; | ||
146 | } | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static const struct rtc_class_ops abx80x_rtc_ops = { | ||
152 | .read_time = abx80x_rtc_read_time, | ||
153 | .set_time = abx80x_rtc_set_time, | ||
154 | }; | ||
155 | |||
156 | static int abx80x_dt_trickle_cfg(struct device_node *np) | ||
157 | { | ||
158 | const char *diode; | ||
159 | int trickle_cfg = 0; | ||
160 | int i, ret; | ||
161 | u32 tmp; | ||
162 | |||
163 | ret = of_property_read_string(np, "abracon,tc-diode", &diode); | ||
164 | if (ret) | ||
165 | return ret; | ||
166 | |||
167 | if (!strcmp(diode, "standard")) | ||
168 | trickle_cfg |= ABX8XX_TRICKLE_STANDARD_DIODE; | ||
169 | else if (!strcmp(diode, "schottky")) | ||
170 | trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE; | ||
171 | else | ||
172 | return -EINVAL; | ||
173 | |||
174 | ret = of_property_read_u32(np, "abracon,tc-resistor", &tmp); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | for (i = 0; i < sizeof(trickle_resistors); i++) | ||
179 | if (trickle_resistors[i] == tmp) | ||
180 | break; | ||
181 | |||
182 | if (i == sizeof(trickle_resistors)) | ||
183 | return -EINVAL; | ||
184 | |||
185 | return (trickle_cfg | i); | ||
186 | } | ||
187 | |||
188 | static int abx80x_probe(struct i2c_client *client, | ||
189 | const struct i2c_device_id *id) | ||
190 | { | ||
191 | struct device_node *np = client->dev.of_node; | ||
192 | struct rtc_device *rtc; | ||
193 | int i, data, err, trickle_cfg = -EINVAL; | ||
194 | char buf[7]; | ||
195 | unsigned int part = id->driver_data; | ||
196 | unsigned int partnumber; | ||
197 | unsigned int majrev, minrev; | ||
198 | unsigned int lot; | ||
199 | unsigned int wafer; | ||
200 | unsigned int uid; | ||
201 | |||
202 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
203 | return -ENODEV; | ||
204 | |||
205 | err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0, | ||
206 | sizeof(buf), buf); | ||
207 | if (err < 0) { | ||
208 | dev_err(&client->dev, "Unable to read partnumber\n"); | ||
209 | return -EIO; | ||
210 | } | ||
211 | |||
212 | partnumber = (buf[0] << 8) | buf[1]; | ||
213 | majrev = buf[2] >> 3; | ||
214 | minrev = buf[2] & 0x7; | ||
215 | lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3]; | ||
216 | uid = ((buf[4] & 0x7f) << 8) | buf[5]; | ||
217 | wafer = (buf[6] & 0x7c) >> 2; | ||
218 | dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n", | ||
219 | partnumber, majrev, minrev, lot, wafer, uid); | ||
220 | |||
221 | data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL1); | ||
222 | if (data < 0) { | ||
223 | dev_err(&client->dev, "Unable to read control register\n"); | ||
224 | return -EIO; | ||
225 | } | ||
226 | |||
227 | err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL1, | ||
228 | ((data & ~ABX8XX_CTRL_12_24) | | ||
229 | ABX8XX_CTRL_WRITE)); | ||
230 | if (err < 0) { | ||
231 | dev_err(&client->dev, "Unable to write control register\n"); | ||
232 | return -EIO; | ||
233 | } | ||
234 | |||
235 | /* part autodetection */ | ||
236 | if (part == ABX80X) { | ||
237 | for (i = 0; abx80x_caps[i].pn; i++) | ||
238 | if (partnumber == abx80x_caps[i].pn) | ||
239 | break; | ||
240 | if (abx80x_caps[i].pn == 0) { | ||
241 | dev_err(&client->dev, "Unknown part: %04x\n", | ||
242 | partnumber); | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | part = i; | ||
246 | } | ||
247 | |||
248 | if (partnumber != abx80x_caps[part].pn) { | ||
249 | dev_err(&client->dev, "partnumber mismatch %04x != %04x\n", | ||
250 | partnumber, abx80x_caps[part].pn); | ||
251 | return -EINVAL; | ||
252 | } | ||
253 | |||
254 | if (np && abx80x_caps[part].has_tc) | ||
255 | trickle_cfg = abx80x_dt_trickle_cfg(np); | ||
256 | |||
257 | if (trickle_cfg > 0) { | ||
258 | dev_info(&client->dev, "Enabling trickle charger: %02x\n", | ||
259 | trickle_cfg); | ||
260 | abx80x_enable_trickle_charger(client, trickle_cfg); | ||
261 | } | ||
262 | |||
263 | rtc = devm_rtc_device_register(&client->dev, abx80x_driver.driver.name, | ||
264 | &abx80x_rtc_ops, THIS_MODULE); | ||
265 | |||
266 | if (IS_ERR(rtc)) | ||
267 | return PTR_ERR(rtc); | ||
268 | |||
269 | i2c_set_clientdata(client, rtc); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int abx80x_remove(struct i2c_client *client) | ||
275 | { | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static const struct i2c_device_id abx80x_id[] = { | ||
280 | { "abx80x", ABX80X }, | ||
281 | { "ab0801", AB0801 }, | ||
282 | { "ab0803", AB0803 }, | ||
283 | { "ab0804", AB0804 }, | ||
284 | { "ab0805", AB0805 }, | ||
285 | { "ab1801", AB1801 }, | ||
286 | { "ab1803", AB1803 }, | ||
287 | { "ab1804", AB1804 }, | ||
288 | { "ab1805", AB1805 }, | ||
289 | { } | ||
290 | }; | ||
291 | MODULE_DEVICE_TABLE(i2c, abx80x_id); | ||
292 | |||
293 | static struct i2c_driver abx80x_driver = { | ||
294 | .driver = { | ||
295 | .name = "rtc-abx80x", | ||
296 | }, | ||
297 | .probe = abx80x_probe, | ||
298 | .remove = abx80x_remove, | ||
299 | .id_table = abx80x_id, | ||
300 | }; | ||
301 | |||
302 | module_i2c_driver(abx80x_driver); | ||
303 | |||
304 | MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>"); | ||
305 | MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); | ||
306 | MODULE_DESCRIPTION("Abracon ABX80X RTC driver"); | ||
307 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 43e04af39e09..cb70ced7e0db 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c | |||
@@ -40,6 +40,13 @@ struct armada38x_rtc { | |||
40 | void __iomem *regs; | 40 | void __iomem *regs; |
41 | void __iomem *regs_soc; | 41 | void __iomem *regs_soc; |
42 | spinlock_t lock; | 42 | spinlock_t lock; |
43 | /* | ||
44 | * While setting the time, the RTC TIME register should not be | ||
45 | * accessed. Setting the RTC time involves sleeping during | ||
46 | * 100ms, so a mutex instead of a spinlock is used to protect | ||
47 | * it | ||
48 | */ | ||
49 | struct mutex mutex_time; | ||
43 | int irq; | 50 | int irq; |
44 | }; | 51 | }; |
45 | 52 | ||
@@ -59,8 +66,7 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
59 | struct armada38x_rtc *rtc = dev_get_drvdata(dev); | 66 | struct armada38x_rtc *rtc = dev_get_drvdata(dev); |
60 | unsigned long time, time_check, flags; | 67 | unsigned long time, time_check, flags; |
61 | 68 | ||
62 | spin_lock_irqsave(&rtc->lock, flags); | 69 | mutex_lock(&rtc->mutex_time); |
63 | |||
64 | time = readl(rtc->regs + RTC_TIME); | 70 | time = readl(rtc->regs + RTC_TIME); |
65 | /* | 71 | /* |
66 | * WA for failing time set attempts. As stated in HW ERRATA if | 72 | * WA for failing time set attempts. As stated in HW ERRATA if |
@@ -71,7 +77,7 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
71 | if ((time_check - time) > 1) | 77 | if ((time_check - time) > 1) |
72 | time_check = readl(rtc->regs + RTC_TIME); | 78 | time_check = readl(rtc->regs + RTC_TIME); |
73 | 79 | ||
74 | spin_unlock_irqrestore(&rtc->lock, flags); | 80 | mutex_unlock(&rtc->mutex_time); |
75 | 81 | ||
76 | rtc_time_to_tm(time_check, tm); | 82 | rtc_time_to_tm(time_check, tm); |
77 | 83 | ||
@@ -94,19 +100,12 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
94 | * then wait for 100ms before writing to the time register to be | 100 | * then wait for 100ms before writing to the time register to be |
95 | * sure that the data will be taken into account. | 101 | * sure that the data will be taken into account. |
96 | */ | 102 | */ |
97 | spin_lock_irqsave(&rtc->lock, flags); | 103 | mutex_lock(&rtc->mutex_time); |
98 | |||
99 | rtc_delayed_write(0, rtc, RTC_STATUS); | 104 | rtc_delayed_write(0, rtc, RTC_STATUS); |
100 | |||
101 | spin_unlock_irqrestore(&rtc->lock, flags); | ||
102 | |||
103 | msleep(100); | 105 | msleep(100); |
104 | |||
105 | spin_lock_irqsave(&rtc->lock, flags); | ||
106 | |||
107 | rtc_delayed_write(time, rtc, RTC_TIME); | 106 | rtc_delayed_write(time, rtc, RTC_TIME); |
107 | mutex_unlock(&rtc->mutex_time); | ||
108 | 108 | ||
109 | spin_unlock_irqrestore(&rtc->lock, flags); | ||
110 | out: | 109 | out: |
111 | return ret; | 110 | return ret; |
112 | } | 111 | } |
@@ -230,6 +229,7 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) | |||
230 | return -ENOMEM; | 229 | return -ENOMEM; |
231 | 230 | ||
232 | spin_lock_init(&rtc->lock); | 231 | spin_lock_init(&rtc->lock); |
232 | mutex_init(&rtc->mutex_time); | ||
233 | 233 | ||
234 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); | 234 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); |
235 | rtc->regs = devm_ioremap_resource(&pdev->dev, res); | 235 | rtc->regs = devm_ioremap_resource(&pdev->dev, res); |