Blame view
drivers/rtc/rtc-rs5c348.c
6.56 KB
e0ac4761f [PATCH] RTC: add ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * A SPI driver for the Ricoh RS5C348 RTC * * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The board specific init code should provide characteristics of this * device: * Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS */ #include <linux/bcd.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> |
5a0e3ad6a include cleanup: ... |
22 |
#include <linux/slab.h> |
e0ac4761f [PATCH] RTC: add ... |
23 24 25 |
#include <linux/rtc.h> #include <linux/workqueue.h> #include <linux/spi/spi.h> |
2113852b2 rtc: Add module.h... |
26 |
#include <linux/module.h> |
e0ac4761f [PATCH] RTC: add ... |
27 |
|
e0ac4761f [PATCH] RTC: add ... |
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#define RS5C348_REG_SECS 0 #define RS5C348_REG_MINS 1 #define RS5C348_REG_HOURS 2 #define RS5C348_REG_WDAY 3 #define RS5C348_REG_DAY 4 #define RS5C348_REG_MONTH 5 #define RS5C348_REG_YEAR 6 #define RS5C348_REG_CTL1 14 #define RS5C348_REG_CTL2 15 #define RS5C348_SECS_MASK 0x7f #define RS5C348_MINS_MASK 0x7f #define RS5C348_HOURS_MASK 0x3f #define RS5C348_WDAY_MASK 0x03 #define RS5C348_DAY_MASK 0x3f #define RS5C348_MONTH_MASK 0x1f #define RS5C348_BIT_PM 0x20 /* REG_HOURS */ #define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */ #define RS5C348_BIT_24H 0x20 /* REG_CTL1 */ #define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */ #define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */ #define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */ #define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */ #define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */ #define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */ struct rs5c348_plat_data { struct rtc_device *rtc; int rtc_24h; }; static int rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct spi_device *spi = to_spi_device(dev); |
f6405afe2 drivers/rtc/rtc-r... |
65 |
struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev); |
e0ac4761f [PATCH] RTC: add ... |
66 67 68 69 70 71 72 73 74 75 76 |
u8 txbuf[5+7], *txp; int ret; /* Transfer 5 bytes before writing SEC. This gives 31us for carry. */ txp = txbuf; txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ txbuf[1] = 0; /* dummy */ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ txbuf[3] = 0; /* dummy */ txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */ txp = &txbuf[5]; |
fe20ba70a drivers/rtc/: use... |
77 78 |
txp[RS5C348_REG_SECS] = bin2bcd(tm->tm_sec); txp[RS5C348_REG_MINS] = bin2bcd(tm->tm_min); |
e0ac4761f [PATCH] RTC: add ... |
79 |
if (pdata->rtc_24h) { |
fe20ba70a drivers/rtc/: use... |
80 |
txp[RS5C348_REG_HOURS] = bin2bcd(tm->tm_hour); |
e0ac4761f [PATCH] RTC: add ... |
81 82 |
} else { /* hour 0 is AM12, noon is PM12 */ |
fe20ba70a drivers/rtc/: use... |
83 |
txp[RS5C348_REG_HOURS] = bin2bcd((tm->tm_hour + 11) % 12 + 1) | |
e0ac4761f [PATCH] RTC: add ... |
84 85 |
(tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0); } |
fe20ba70a drivers/rtc/: use... |
86 87 88 |
txp[RS5C348_REG_WDAY] = bin2bcd(tm->tm_wday); txp[RS5C348_REG_DAY] = bin2bcd(tm->tm_mday); txp[RS5C348_REG_MONTH] = bin2bcd(tm->tm_mon + 1) | |
e0ac4761f [PATCH] RTC: add ... |
89 |
(tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0); |
fe20ba70a drivers/rtc/: use... |
90 |
txp[RS5C348_REG_YEAR] = bin2bcd(tm->tm_year % 100); |
e0ac4761f [PATCH] RTC: add ... |
91 92 93 94 95 96 97 98 99 100 |
/* write in one transfer to avoid data inconsistency */ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0); udelay(62); /* Tcsr 62us */ return ret; } static int rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct spi_device *spi = to_spi_device(dev); |
f6405afe2 drivers/rtc/rtc-r... |
101 |
struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev); |
e0ac4761f [PATCH] RTC: add ... |
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
u8 txbuf[5], rxbuf[7]; int ret; /* Transfer 5 byte befores reading SEC. This gives 31us for carry. */ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ txbuf[1] = 0; /* dummy */ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ txbuf[3] = 0; /* dummy */ txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */ /* read in one transfer to avoid data inconsistency */ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), rxbuf, sizeof(rxbuf)); udelay(62); /* Tcsr 62us */ if (ret < 0) return ret; |
fe20ba70a drivers/rtc/: use... |
118 119 120 |
tm->tm_sec = bcd2bin(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK); tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK); tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK); |
e0ac4761f [PATCH] RTC: add ... |
121 |
if (!pdata->rtc_24h) { |
7dbfb315b drivers/rtc/rtc-r... |
122 123 124 |
if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) { tm->tm_hour -= 20; tm->tm_hour %= 12; |
e0ac4761f [PATCH] RTC: add ... |
125 |
tm->tm_hour += 12; |
7dbfb315b drivers/rtc/rtc-r... |
126 127 |
} else tm->tm_hour %= 12; |
e0ac4761f [PATCH] RTC: add ... |
128 |
} |
fe20ba70a drivers/rtc/: use... |
129 130 |
tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK); tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK); |
e0ac4761f [PATCH] RTC: add ... |
131 |
tm->tm_mon = |
fe20ba70a drivers/rtc/: use... |
132 |
bcd2bin(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1; |
e0ac4761f [PATCH] RTC: add ... |
133 |
/* year is 1900 + tm->tm_year */ |
fe20ba70a drivers/rtc/: use... |
134 |
tm->tm_year = bcd2bin(rxbuf[RS5C348_REG_YEAR]) + |
e0ac4761f [PATCH] RTC: add ... |
135 136 137 138 139 140 141 142 143 144 |
((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0); if (rtc_valid_tm(tm) < 0) { dev_err(&spi->dev, "retrieved date/time is not valid. "); rtc_time_to_tm(0, tm); } return 0; } |
ff8371ac9 [PATCH] constify ... |
145 |
static const struct rtc_class_ops rs5c348_rtc_ops = { |
e0ac4761f [PATCH] RTC: add ... |
146 147 148 149 150 |
.read_time = rs5c348_rtc_read_time, .set_time = rs5c348_rtc_set_time, }; static struct spi_driver rs5c348_driver; |
5a167f454 Drivers: rtc: rem... |
151 |
static int rs5c348_probe(struct spi_device *spi) |
e0ac4761f [PATCH] RTC: add ... |
152 153 154 155 |
{ int ret; struct rtc_device *rtc; struct rs5c348_plat_data *pdata; |
8fb1ecb36 rtc: rtc-rs5c348:... |
156 157 |
pdata = devm_kzalloc(&spi->dev, sizeof(struct rs5c348_plat_data), GFP_KERNEL); |
e0ac4761f [PATCH] RTC: add ... |
158 159 160 161 162 163 164 165 166 167 168 |
if (!pdata) return -ENOMEM; spi->dev.platform_data = pdata; /* Check D7 of SECOND register */ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS)); if (ret < 0 || (ret & 0x80)) { dev_err(&spi->dev, "not found. "); goto kfree_exit; } |
e0ac4761f [PATCH] RTC: add ... |
169 170 171 172 173 174 175 176 177 178 |
dev_info(&spi->dev, "spiclk %u KHz. ", (spi->max_speed_hz + 500) / 1000); /* turn RTC on if it was not on */ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2)); if (ret < 0) goto kfree_exit; if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) { u8 buf[2]; |
13c73f045 [PATCH] RTC: more... |
179 |
struct rtc_time tm; |
e0ac4761f [PATCH] RTC: add ... |
180 181 182 |
if (ret & RS5C348_BIT_VDET) dev_warn(&spi->dev, "voltage-low detected. "); |
13c73f045 [PATCH] RTC: more... |
183 184 185 186 187 188 189 |
if (ret & RS5C348_BIT_XSTP) dev_warn(&spi->dev, "oscillator-stop detected. "); rtc_time_to_tm(0, &tm); /* 1970/1/1 */ ret = rs5c348_rtc_set_time(&spi->dev, &tm); if (ret < 0) goto kfree_exit; |
e0ac4761f [PATCH] RTC: add ... |
190 191 192 193 194 195 196 197 198 199 200 201 |
buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2); buf[1] = 0; ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); if (ret < 0) goto kfree_exit; } ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1)); if (ret < 0) goto kfree_exit; if (ret & RS5C348_BIT_24H) pdata->rtc_24h = 1; |
8fb1ecb36 rtc: rtc-rs5c348:... |
202 |
rtc = devm_rtc_device_register(&spi->dev, rs5c348_driver.driver.name, |
e0ac4761f [PATCH] RTC: add ... |
203 204 205 206 207 208 209 210 211 212 213 |
&rs5c348_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { ret = PTR_ERR(rtc); goto kfree_exit; } pdata->rtc = rtc; return 0; kfree_exit: |
e0ac4761f [PATCH] RTC: add ... |
214 215 |
return ret; } |
e0ac4761f [PATCH] RTC: add ... |
216 217 |
static struct spi_driver rs5c348_driver = { .driver = { |
9f90a03a7 [PATCH] rtc: Make... |
218 |
.name = "rtc-rs5c348", |
e0ac4761f [PATCH] RTC: add ... |
219 220 |
}, .probe = rs5c348_probe, |
e0ac4761f [PATCH] RTC: add ... |
221 |
}; |
109e94184 rtc: convert rtc ... |
222 |
module_spi_driver(rs5c348_driver); |
e0ac4761f [PATCH] RTC: add ... |
223 224 225 226 |
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); MODULE_LICENSE("GPL"); |
e0626e384 spi: prefix modal... |
227 |
MODULE_ALIAS("spi:rtc-rs5c348"); |