Blame view
drivers/rtc/rtc-v3020.c
8.75 KB
d2912cb15
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
362600fe6
|
2 3 4 5 6 |
/* drivers/rtc/rtc-v3020.c * * Copyright (C) 2006 8D Technologies inc. * Copyright (C) 2004 Compulab Ltd. * |
362600fe6
|
7 8 9 10 11 12 13 14 15 |
* Driver for the V3020 RTC * * Changelog: * * 10-May-2006: Raphael Assenat <raph@8d.com> * - Converted to platform driver * - Use the generic rtc class * * ??-???-2004: Someone at Compulab |
de2edf32f
|
16 |
* - Initial driver creation. |
362600fe6
|
17 18 19 20 21 22 23 |
*/ #include <linux/platform_device.h> #include <linux/module.h> #include <linux/init.h> #include <linux/rtc.h> #include <linux/types.h> #include <linux/bcd.h> |
cd9b518b9
|
24 |
#include <linux/platform_data/rtc-v3020.h> |
f3d79b20d
|
25 |
#include <linux/delay.h> |
96615841e
|
26 |
#include <linux/gpio.h> |
5a0e3ad6a
|
27 |
#include <linux/slab.h> |
362600fe6
|
28 |
|
c08cf9daf
|
29 |
#include <linux/io.h> |
362600fe6
|
30 31 |
#undef DEBUG |
96615841e
|
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
struct v3020; struct v3020_chip_ops { int (*map_io)(struct v3020 *chip, struct platform_device *pdev, struct v3020_platform_data *pdata); void (*unmap_io)(struct v3020 *chip); unsigned char (*read_bit)(struct v3020 *chip); void (*write_bit)(struct v3020 *chip, unsigned char bit); }; #define V3020_CS 0 #define V3020_WR 1 #define V3020_RD 2 #define V3020_IO 3 |
362600fe6
|
46 |
struct v3020 { |
96615841e
|
47 |
/* MMIO access */ |
362600fe6
|
48 49 |
void __iomem *ioaddress; int leftshift; |
96615841e
|
50 51 |
/* GPIO access */ |
6c95fa80e
|
52 |
struct gpio *gpio; |
96615841e
|
53 |
|
7432a850b
|
54 |
const struct v3020_chip_ops *ops; |
96615841e
|
55 |
|
362600fe6
|
56 57 |
struct rtc_device *rtc; }; |
96615841e
|
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev, struct v3020_platform_data *pdata) { if (pdev->num_resources != 1) return -EBUSY; if (pdev->resource[0].flags != IORESOURCE_MEM) return -EBUSY; chip->leftshift = pdata->leftshift; chip->ioaddress = ioremap(pdev->resource[0].start, 1); if (chip->ioaddress == NULL) return -EBUSY; return 0; } static void v3020_mmio_unmap(struct v3020 *chip) { iounmap(chip->ioaddress); } static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) { writel(bit << chip->leftshift, chip->ioaddress); } static unsigned char v3020_mmio_read_bit(struct v3020 *chip) { |
bcb3a1676
|
88 |
return !!(readl(chip->ioaddress) & (1 << chip->leftshift)); |
96615841e
|
89 |
} |
7432a850b
|
90 |
static const struct v3020_chip_ops v3020_mmio_ops = { |
96615841e
|
91 92 93 94 95 |
.map_io = v3020_mmio_map, .unmap_io = v3020_mmio_unmap, .read_bit = v3020_mmio_read_bit, .write_bit = v3020_mmio_write_bit, }; |
6c95fa80e
|
96 97 98 99 100 |
static struct gpio v3020_gpio[] = { { 0, GPIOF_OUT_INIT_HIGH, "RTC CS"}, { 0, GPIOF_OUT_INIT_HIGH, "RTC WR"}, { 0, GPIOF_OUT_INIT_HIGH, "RTC RD"}, { 0, GPIOF_OUT_INIT_HIGH, "RTC IO"}, |
96615841e
|
101 102 103 104 105 |
}; static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev, struct v3020_platform_data *pdata) { |
6c95fa80e
|
106 |
int err; |
96615841e
|
107 108 109 110 111 |
v3020_gpio[V3020_CS].gpio = pdata->gpio_cs; v3020_gpio[V3020_WR].gpio = pdata->gpio_wr; v3020_gpio[V3020_RD].gpio = pdata->gpio_rd; v3020_gpio[V3020_IO].gpio = pdata->gpio_io; |
6c95fa80e
|
112 |
err = gpio_request_array(v3020_gpio, ARRAY_SIZE(v3020_gpio)); |
96615841e
|
113 |
|
6c95fa80e
|
114 115 |
if (!err) chip->gpio = v3020_gpio; |
96615841e
|
116 117 118 119 120 121 |
return err; } static void v3020_gpio_unmap(struct v3020 *chip) { |
6c95fa80e
|
122 |
gpio_free_array(v3020_gpio, ARRAY_SIZE(v3020_gpio)); |
96615841e
|
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
} static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit) { gpio_direction_output(chip->gpio[V3020_IO].gpio, bit); gpio_set_value(chip->gpio[V3020_CS].gpio, 0); gpio_set_value(chip->gpio[V3020_WR].gpio, 0); udelay(1); gpio_set_value(chip->gpio[V3020_WR].gpio, 1); gpio_set_value(chip->gpio[V3020_CS].gpio, 1); } static unsigned char v3020_gpio_read_bit(struct v3020 *chip) { int bit; gpio_direction_input(chip->gpio[V3020_IO].gpio); gpio_set_value(chip->gpio[V3020_CS].gpio, 0); gpio_set_value(chip->gpio[V3020_RD].gpio, 0); udelay(1); bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio); udelay(1); gpio_set_value(chip->gpio[V3020_RD].gpio, 1); gpio_set_value(chip->gpio[V3020_CS].gpio, 1); return bit; } |
7432a850b
|
150 |
static const struct v3020_chip_ops v3020_gpio_ops = { |
96615841e
|
151 152 153 154 155 |
.map_io = v3020_gpio_map, .unmap_io = v3020_gpio_unmap, .read_bit = v3020_gpio_read_bit, .write_bit = v3020_gpio_write_bit, }; |
362600fe6
|
156 157 158 159 160 161 162 163 |
static void v3020_set_reg(struct v3020 *chip, unsigned char address, unsigned char data) { int i; unsigned char tmp; tmp = address; for (i = 0; i < 4; i++) { |
96615841e
|
164 |
chip->ops->write_bit(chip, (tmp & 1)); |
362600fe6
|
165 |
tmp >>= 1; |
f3d79b20d
|
166 |
udelay(1); |
362600fe6
|
167 168 169 170 171 |
} /* Commands dont have data */ if (!V3020_IS_COMMAND(address)) { for (i = 0; i < 8; i++) { |
96615841e
|
172 |
chip->ops->write_bit(chip, (data & 1)); |
362600fe6
|
173 |
data >>= 1; |
f3d79b20d
|
174 |
udelay(1); |
362600fe6
|
175 176 177 178 179 180 |
} } } static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) { |
c08cf9daf
|
181 |
unsigned int data = 0; |
362600fe6
|
182 183 184 |
int i; for (i = 0; i < 4; i++) { |
96615841e
|
185 |
chip->ops->write_bit(chip, (address & 1)); |
362600fe6
|
186 |
address >>= 1; |
f3d79b20d
|
187 |
udelay(1); |
362600fe6
|
188 189 190 191 |
} for (i = 0; i < 8; i++) { data >>= 1; |
96615841e
|
192 |
if (chip->ops->read_bit(chip)) |
362600fe6
|
193 |
data |= 0x80; |
f3d79b20d
|
194 |
udelay(1); |
362600fe6
|
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
} return data; } static int v3020_read_time(struct device *dev, struct rtc_time *dt) { struct v3020 *chip = dev_get_drvdata(dev); int tmp; /* Copy the current time to ram... */ v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0); /* ...and then read constant values. */ tmp = v3020_get_reg(chip, V3020_SECONDS); |
fe20ba70a
|
210 |
dt->tm_sec = bcd2bin(tmp); |
362600fe6
|
211 |
tmp = v3020_get_reg(chip, V3020_MINUTES); |
fe20ba70a
|
212 |
dt->tm_min = bcd2bin(tmp); |
362600fe6
|
213 |
tmp = v3020_get_reg(chip, V3020_HOURS); |
fe20ba70a
|
214 |
dt->tm_hour = bcd2bin(tmp); |
362600fe6
|
215 |
tmp = v3020_get_reg(chip, V3020_MONTH_DAY); |
fe20ba70a
|
216 |
dt->tm_mday = bcd2bin(tmp); |
362600fe6
|
217 |
tmp = v3020_get_reg(chip, V3020_MONTH); |
fe20ba70a
|
218 |
dt->tm_mon = bcd2bin(tmp) - 1; |
362600fe6
|
219 |
tmp = v3020_get_reg(chip, V3020_WEEK_DAY); |
fe20ba70a
|
220 |
dt->tm_wday = bcd2bin(tmp); |
362600fe6
|
221 |
tmp = v3020_get_reg(chip, V3020_YEAR); |
fe20ba70a
|
222 |
dt->tm_year = bcd2bin(tmp)+100; |
362600fe6
|
223 |
|
c08cf9daf
|
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
dev_dbg(dev, " %s : Read RTC values ", __func__); dev_dbg(dev, "tm_hour: %i ", dt->tm_hour); dev_dbg(dev, "tm_min : %i ", dt->tm_min); dev_dbg(dev, "tm_sec : %i ", dt->tm_sec); dev_dbg(dev, "tm_year: %i ", dt->tm_year); dev_dbg(dev, "tm_mon : %i ", dt->tm_mon); dev_dbg(dev, "tm_mday: %i ", dt->tm_mday); dev_dbg(dev, "tm_wday: %i ", dt->tm_wday); |
362600fe6
|
241 242 243 244 245 246 247 248 |
return 0; } static int v3020_set_time(struct device *dev, struct rtc_time *dt) { struct v3020 *chip = dev_get_drvdata(dev); |
c08cf9daf
|
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
dev_dbg(dev, " %s : Setting RTC values ", __func__); dev_dbg(dev, "tm_sec : %i ", dt->tm_sec); dev_dbg(dev, "tm_min : %i ", dt->tm_min); dev_dbg(dev, "tm_hour: %i ", dt->tm_hour); dev_dbg(dev, "tm_mday: %i ", dt->tm_mday); dev_dbg(dev, "tm_wday: %i ", dt->tm_wday); dev_dbg(dev, "tm_year: %i ", dt->tm_year); |
362600fe6
|
264 265 |
/* Write all the values to ram... */ |
de2edf32f
|
266 267 268 |
v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); |
fe20ba70a
|
269 |
v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday)); |
de2edf32f
|
270 271 272 |
v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); |
362600fe6
|
273 274 275 276 277 278 279 280 281 282 |
/* ...and set the clock. */ v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0); /* Compulab used this delay here. I dont know why, * the datasheet does not specify a delay. */ /*mdelay(5);*/ return 0; } |
ff8371ac9
|
283 |
static const struct rtc_class_ops v3020_rtc_ops = { |
362600fe6
|
284 285 286 287 288 289 |
.read_time = v3020_read_time, .set_time = v3020_set_time, }; static int rtc_probe(struct platform_device *pdev) { |
a65ddceba
|
290 |
struct v3020_platform_data *pdata = dev_get_platdata(&pdev->dev); |
362600fe6
|
291 |
struct v3020 *chip; |
362600fe6
|
292 293 |
int retval = -EBUSY; int i; |
362600fe6
|
294 |
|
431c6c1df
|
295 |
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); |
362600fe6
|
296 297 |
if (!chip) return -ENOMEM; |
96615841e
|
298 299 300 301 302 303 304 |
if (pdata->use_gpio) chip->ops = &v3020_gpio_ops; else chip->ops = &v3020_mmio_ops; retval = chip->ops->map_io(chip, pdev, pdata); if (retval) |
d8d5290a3
|
305 |
return retval; |
362600fe6
|
306 307 308 309 |
/* Make sure the v3020 expects a communication cycle * by reading 8 times */ for (i = 0; i < 8; i++) |
42397492f
|
310 |
chip->ops->read_bit(chip); |
362600fe6
|
311 312 313 314 |
/* Test chip by doing a write/read sequence * to the chip ram */ v3020_set_reg(chip, V3020_SECONDS, 0x33); |
c08cf9daf
|
315 |
if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { |
362600fe6
|
316 317 318 |
retval = -ENODEV; goto err_io; } |
af901ca18
|
319 |
/* Make sure frequency measurement mode, test modes, and lock |
362600fe6
|
320 321 |
* are all disabled */ v3020_set_reg(chip, V3020_STATUS_0, 0x0); |
96615841e
|
322 323 324 325 326 327 328 329 330 331 332 333 334 |
if (pdata->use_gpio) dev_info(&pdev->dev, "Chip available at GPIOs " "%d, %d, %d, %d ", chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio, chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio); else dev_info(&pdev->dev, "Chip available at " "physical address 0x%llx," "data connected to D%d ", (unsigned long long)pdev->resource[0].start, chip->leftshift); |
362600fe6
|
335 336 |
platform_set_drvdata(pdev, chip); |
431c6c1df
|
337 338 |
chip->rtc = devm_rtc_device_register(&pdev->dev, "v3020", &v3020_rtc_ops, THIS_MODULE); |
b74d2caa6
|
339 340 |
if (IS_ERR(chip->rtc)) { retval = PTR_ERR(chip->rtc); |
362600fe6
|
341 342 |
goto err_io; } |
362600fe6
|
343 344 345 346 |
return 0; err_io: |
96615841e
|
347 |
chip->ops->unmap_io(chip); |
d8d5290a3
|
348 |
|
362600fe6
|
349 350 351 352 353 354 |
return retval; } static int rtc_remove(struct platform_device *dev) { struct v3020 *chip = platform_get_drvdata(dev); |
362600fe6
|
355 |
|
96615841e
|
356 |
chip->ops->unmap_io(chip); |
362600fe6
|
357 358 359 360 361 362 363 364 365 |
return 0; } static struct platform_driver rtc_device_driver = { .probe = rtc_probe, .remove = rtc_remove, .driver = { .name = "v3020", |
362600fe6
|
366 367 |
}, }; |
0c4eae665
|
368 |
module_platform_driver(rtc_device_driver); |
362600fe6
|
369 370 371 372 |
MODULE_DESCRIPTION("V3020 RTC"); MODULE_AUTHOR("Raphael Assenat"); MODULE_LICENSE("GPL"); |
ad28a07bc
|
373 |
MODULE_ALIAS("platform:v3020"); |