| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Ricoh RS5C313 RTC device/driver | 
 | 3 |  *  Copyright (C) 2007 Nobuhiro Iwamatsu | 
 | 4 |  * | 
 | 5 |  *  2005-09-19 modifed by kogiidena | 
 | 6 |  * | 
 | 7 |  * Based on the old drivers/char/rs5c313_rtc.c  by: | 
 | 8 |  *  Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | 
 | 9 |  *  Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | 
 | 10 |  * | 
 | 11 |  * Based on code written by Paul Gortmaker. | 
 | 12 |  *  Copyright (C) 1996 Paul Gortmaker | 
 | 13 |  * | 
 | 14 |  * This file is subject to the terms and conditions of the GNU General Public | 
 | 15 |  * License.  See the file "COPYING" in the main directory of this archive | 
 | 16 |  * for more details. | 
 | 17 |  * | 
 | 18 |  * Based on other minimal char device drivers, like Alan's | 
 | 19 |  * watchdog, Ted's random, etc. etc. | 
 | 20 |  * | 
 | 21 |  *	1.07	Paul Gortmaker. | 
 | 22 |  *	1.08	Miquel van Smoorenburg: disallow certain things on the | 
 | 23 |  *		DEC Alpha as the CMOS clock is also used for other things. | 
 | 24 |  *	1.09	Nikita Schmidt: epoch support and some Alpha cleanup. | 
 | 25 |  *	1.09a	Pete Zaitcev: Sun SPARC | 
 | 26 |  *	1.09b	Jeff Garzik: Modularize, init cleanup | 
 | 27 |  *	1.09c	Jeff Garzik: SMP cleanup | 
 | 28 |  *	1.10    Paul Barton-Davis: add support for async I/O | 
 | 29 |  *	1.10a	Andrea Arcangeli: Alpha updates | 
 | 30 |  *	1.10b	Andrew Morton: SMP lock fix | 
 | 31 |  *	1.10c	Cesar Barros: SMP locking fixes and cleanup | 
 | 32 |  *	1.10d	Paul Gortmaker: delete paranoia check in rtc_exit | 
 | 33 |  *	1.10e	Maciej W. Rozycki: Handle DECstation's year weirdness. | 
 | 34 |  *      1.11    Takashi Iwai: Kernel access functions | 
 | 35 |  *			      rtc_register/rtc_unregister/rtc_control | 
 | 36 |  *      1.11a   Daniele Bellucci: Audit create_proc_read_entry in rtc_init | 
 | 37 |  *	1.12	Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer | 
 | 38 |  *		CONFIG_HPET_EMULATE_RTC | 
 | 39 |  *	1.13	Nobuhiro Iwamatsu: Updata driver. | 
 | 40 |  */ | 
 | 41 |  | 
 | 42 | #include <linux/module.h> | 
 | 43 | #include <linux/err.h> | 
 | 44 | #include <linux/rtc.h> | 
 | 45 | #include <linux/platform_device.h> | 
 | 46 | #include <linux/bcd.h> | 
 | 47 | #include <linux/delay.h> | 
 | 48 | #include <asm/io.h> | 
 | 49 |  | 
 | 50 | #define DRV_NAME	"rs5c313" | 
 | 51 | #define DRV_VERSION 	"1.13" | 
 | 52 |  | 
 | 53 | #ifdef CONFIG_SH_LANDISK | 
 | 54 | /*****************************************************/ | 
 | 55 | /* LANDISK dependence part of RS5C313                */ | 
 | 56 | /*****************************************************/ | 
 | 57 |  | 
 | 58 | #define SCSMR1		0xFFE00000 | 
 | 59 | #define SCSCR1		0xFFE00008 | 
 | 60 | #define SCSMR1_CA	0x80 | 
 | 61 | #define SCSCR1_CKE	0x03 | 
 | 62 | #define SCSPTR1		0xFFE0001C | 
 | 63 | #define SCSPTR1_EIO	0x80 | 
 | 64 | #define SCSPTR1_SPB1IO	0x08 | 
 | 65 | #define SCSPTR1_SPB1DT	0x04 | 
 | 66 | #define SCSPTR1_SPB0IO	0x02 | 
 | 67 | #define SCSPTR1_SPB0DT	0x01 | 
 | 68 |  | 
 | 69 | #define SDA_OEN		SCSPTR1_SPB1IO | 
 | 70 | #define SDA		SCSPTR1_SPB1DT | 
 | 71 | #define SCL_OEN		SCSPTR1_SPB0IO | 
 | 72 | #define SCL		SCSPTR1_SPB0DT | 
 | 73 |  | 
 | 74 | /* RICOH RS5C313 CE port */ | 
 | 75 | #define RS5C313_CE	0xB0000003 | 
 | 76 |  | 
 | 77 | /* RICOH RS5C313 CE port bit */ | 
 | 78 | #define RS5C313_CE_RTCCE	0x02 | 
 | 79 |  | 
 | 80 | /* SCSPTR1 data */ | 
 | 81 | unsigned char scsptr1_data; | 
 | 82 |  | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 83 | #define RS5C313_CEENABLE    __raw_writeb(RS5C313_CE_RTCCE, RS5C313_CE); | 
 | 84 | #define RS5C313_CEDISABLE   __raw_writeb(0x00, RS5C313_CE) | 
 | 85 | #define RS5C313_MISCOP      __raw_writeb(0x02, 0xB0000008) | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 86 |  | 
 | 87 | static void rs5c313_init_port(void) | 
 | 88 | { | 
 | 89 | 	/* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 90 | 	__raw_writeb(__raw_readb(SCSMR1) & ~SCSMR1_CA, SCSMR1); | 
 | 91 | 	__raw_writeb(__raw_readb(SCSCR1) & ~SCSCR1_CKE, SCSCR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 92 |  | 
 | 93 | 	/* And Initialize SCL for RS5C313 clock */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 94 | 	scsptr1_data = __raw_readb(SCSPTR1) | SCL;	/* SCL:H */ | 
 | 95 | 	__raw_writeb(scsptr1_data, SCSPTR1); | 
 | 96 | 	scsptr1_data = __raw_readb(SCSPTR1) | SCL_OEN;	/* SCL output enable */ | 
 | 97 | 	__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 98 | 	RS5C313_CEDISABLE;	/* CE:L */ | 
 | 99 | } | 
 | 100 |  | 
 | 101 | static void rs5c313_write_data(unsigned char data) | 
 | 102 | { | 
 | 103 | 	int i; | 
 | 104 |  | 
 | 105 | 	for (i = 0; i < 8; i++) { | 
 | 106 | 		/* SDA:Write Data */ | 
 | 107 | 		scsptr1_data = (scsptr1_data & ~SDA) | | 
 | 108 | 				((((0x80 >> i) & data) >> (7 - i)) << 2); | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 109 | 		__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 110 | 		if (i == 0) { | 
 | 111 | 			scsptr1_data |= SDA_OEN;	/* SDA:output enable */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 112 | 			__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 113 | 		} | 
 | 114 | 		ndelay(700); | 
 | 115 | 		scsptr1_data &= ~SCL;	/* SCL:L */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 116 | 		__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 117 | 		ndelay(700); | 
 | 118 | 		scsptr1_data |= SCL;	/* SCL:H */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 119 | 		__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 120 | 	} | 
 | 121 |  | 
 | 122 | 	scsptr1_data &= ~SDA_OEN;	/* SDA:output disable */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 123 | 	__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 124 | } | 
 | 125 |  | 
 | 126 | static unsigned char rs5c313_read_data(void) | 
 | 127 | { | 
 | 128 | 	int i; | 
| kogiidena | 9a3f1d5 | 2007-05-10 22:22:54 -0700 | [diff] [blame] | 129 | 	unsigned char data = 0; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 130 |  | 
 | 131 | 	for (i = 0; i < 8; i++) { | 
 | 132 | 		ndelay(700); | 
 | 133 | 		/* SDA:Read Data */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 134 | 		data |= ((__raw_readb(SCSPTR1) & SDA) >> 2) << (7 - i); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 135 | 		scsptr1_data &= ~SCL;	/* SCL:L */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 136 | 		__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 137 | 		ndelay(700); | 
 | 138 | 		scsptr1_data |= SCL;	/* SCL:H */ | 
| Paul Mundt | 071a1e3 | 2010-10-27 15:30:32 +0900 | [diff] [blame] | 139 | 		__raw_writeb(scsptr1_data, SCSPTR1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 140 | 	} | 
 | 141 | 	return data & 0x0F; | 
 | 142 | } | 
 | 143 |  | 
 | 144 | #endif /* CONFIG_SH_LANDISK */ | 
 | 145 |  | 
 | 146 | /*****************************************************/ | 
 | 147 | /* machine independence part of RS5C313              */ | 
 | 148 | /*****************************************************/ | 
 | 149 |  | 
 | 150 | /* RICOH RS5C313 address */ | 
 | 151 | #define RS5C313_ADDR_SEC	0x00 | 
 | 152 | #define RS5C313_ADDR_SEC10	0x01 | 
 | 153 | #define RS5C313_ADDR_MIN	0x02 | 
 | 154 | #define RS5C313_ADDR_MIN10	0x03 | 
 | 155 | #define RS5C313_ADDR_HOUR	0x04 | 
 | 156 | #define RS5C313_ADDR_HOUR10	0x05 | 
 | 157 | #define RS5C313_ADDR_WEEK	0x06 | 
 | 158 | #define RS5C313_ADDR_INTINTVREG	0x07 | 
 | 159 | #define RS5C313_ADDR_DAY	0x08 | 
 | 160 | #define RS5C313_ADDR_DAY10	0x09 | 
 | 161 | #define RS5C313_ADDR_MON	0x0A | 
 | 162 | #define RS5C313_ADDR_MON10	0x0B | 
 | 163 | #define RS5C313_ADDR_YEAR	0x0C | 
 | 164 | #define RS5C313_ADDR_YEAR10	0x0D | 
 | 165 | #define RS5C313_ADDR_CNTREG	0x0E | 
 | 166 | #define RS5C313_ADDR_TESTREG	0x0F | 
 | 167 |  | 
 | 168 | /* RICOH RS5C313 control register */ | 
 | 169 | #define RS5C313_CNTREG_ADJ_BSY	0x01 | 
 | 170 | #define RS5C313_CNTREG_WTEN_XSTP	0x02 | 
 | 171 | #define RS5C313_CNTREG_12_24	0x04 | 
 | 172 | #define RS5C313_CNTREG_CTFG	0x08 | 
 | 173 |  | 
 | 174 | /* RICOH RS5C313 test register */ | 
 | 175 | #define RS5C313_TESTREG_TEST	0x01 | 
 | 176 |  | 
 | 177 | /* RICOH RS5C313 control bit */ | 
 | 178 | #define RS5C313_CNTBIT_READ	0x40 | 
 | 179 | #define RS5C313_CNTBIT_AD	0x20 | 
 | 180 | #define RS5C313_CNTBIT_DT	0x10 | 
 | 181 |  | 
 | 182 | static unsigned char rs5c313_read_reg(unsigned char addr) | 
 | 183 | { | 
 | 184 |  | 
 | 185 | 	rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD); | 
 | 186 | 	return rs5c313_read_data(); | 
 | 187 | } | 
 | 188 |  | 
 | 189 | static void rs5c313_write_reg(unsigned char addr, unsigned char data) | 
 | 190 | { | 
 | 191 | 	data &= 0x0f; | 
 | 192 | 	rs5c313_write_data(addr | RS5C313_CNTBIT_AD); | 
 | 193 | 	rs5c313_write_data(data | RS5C313_CNTBIT_DT); | 
 | 194 | 	return; | 
 | 195 | } | 
 | 196 |  | 
| kogiidena | 9a3f1d5 | 2007-05-10 22:22:54 -0700 | [diff] [blame] | 197 | static inline unsigned char rs5c313_read_cntreg(void) | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 198 | { | 
 | 199 | 	return rs5c313_read_reg(RS5C313_ADDR_CNTREG); | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static inline void rs5c313_write_cntreg(unsigned char data) | 
 | 203 | { | 
 | 204 | 	rs5c313_write_reg(RS5C313_ADDR_CNTREG, data); | 
 | 205 | } | 
 | 206 |  | 
 | 207 | static inline void rs5c313_write_intintvreg(unsigned char data) | 
 | 208 | { | 
 | 209 | 	rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data); | 
 | 210 | } | 
 | 211 |  | 
 | 212 | static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) | 
 | 213 | { | 
 | 214 | 	int data; | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 215 | 	int cnt; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 216 |  | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 217 | 	cnt = 0; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 218 | 	while (1) { | 
 | 219 | 		RS5C313_CEENABLE;	/* CE:H */ | 
 | 220 |  | 
 | 221 | 		/* Initialize control reg. 24 hour */ | 
 | 222 | 		rs5c313_write_cntreg(0x04); | 
 | 223 |  | 
 | 224 | 		if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) | 
 | 225 | 			break; | 
 | 226 |  | 
 | 227 | 		RS5C313_CEDISABLE; | 
 | 228 | 		ndelay(700);	/* CE:L */ | 
 | 229 |  | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 230 | 		if (cnt++ > 100) { | 
| Harvey Harrison | 2a4e2b8 | 2008-04-28 02:12:00 -0700 | [diff] [blame] | 231 | 			dev_err(dev, "%s: timeout error\n", __func__); | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 232 | 			return -EIO; | 
 | 233 | 		} | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 234 | 	} | 
 | 235 |  | 
 | 236 | 	data = rs5c313_read_reg(RS5C313_ADDR_SEC); | 
 | 237 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 238 | 	tm->tm_sec = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 239 |  | 
 | 240 | 	data = rs5c313_read_reg(RS5C313_ADDR_MIN); | 
 | 241 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 242 | 	tm->tm_min = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 243 |  | 
 | 244 | 	data = rs5c313_read_reg(RS5C313_ADDR_HOUR); | 
 | 245 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 246 | 	tm->tm_hour = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 247 |  | 
 | 248 | 	data = rs5c313_read_reg(RS5C313_ADDR_DAY); | 
 | 249 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 250 | 	tm->tm_mday = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 251 |  | 
 | 252 | 	data = rs5c313_read_reg(RS5C313_ADDR_MON); | 
 | 253 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 254 | 	tm->tm_mon = bcd2bin(data) - 1; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 255 |  | 
 | 256 | 	data = rs5c313_read_reg(RS5C313_ADDR_YEAR); | 
 | 257 | 	data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 258 | 	tm->tm_year = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 259 |  | 
 | 260 | 	if (tm->tm_year < 70) | 
 | 261 | 		tm->tm_year += 100; | 
 | 262 |  | 
 | 263 | 	data = rs5c313_read_reg(RS5C313_ADDR_WEEK); | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 264 | 	tm->tm_wday = bcd2bin(data); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 265 |  | 
 | 266 | 	RS5C313_CEDISABLE; | 
 | 267 | 	ndelay(700);		/* CE:L */ | 
 | 268 |  | 
 | 269 | 	return 0; | 
 | 270 | } | 
 | 271 |  | 
 | 272 | static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) | 
 | 273 | { | 
 | 274 | 	int data; | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 275 | 	int cnt; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 276 |  | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 277 | 	cnt = 0; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 278 | 	/* busy check. */ | 
 | 279 | 	while (1) { | 
 | 280 | 		RS5C313_CEENABLE;	/* CE:H */ | 
 | 281 |  | 
 | 282 | 		/* Initiatlize control reg. 24 hour */ | 
 | 283 | 		rs5c313_write_cntreg(0x04); | 
 | 284 |  | 
 | 285 | 		if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) | 
 | 286 | 			break; | 
 | 287 | 		RS5C313_MISCOP; | 
 | 288 | 		RS5C313_CEDISABLE; | 
 | 289 | 		ndelay(700);	/* CE:L */ | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 290 |  | 
 | 291 | 		if (cnt++ > 100) { | 
| Harvey Harrison | 2a4e2b8 | 2008-04-28 02:12:00 -0700 | [diff] [blame] | 292 | 			dev_err(dev, "%s: timeout error\n", __func__); | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 293 | 			return -EIO; | 
 | 294 | 		} | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 295 | 	} | 
 | 296 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 297 | 	data = bin2bcd(tm->tm_sec); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 298 | 	rs5c313_write_reg(RS5C313_ADDR_SEC, data); | 
 | 299 | 	rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4)); | 
 | 300 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 301 | 	data = bin2bcd(tm->tm_min); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 302 | 	rs5c313_write_reg(RS5C313_ADDR_MIN, data ); | 
 | 303 | 	rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4)); | 
 | 304 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 305 | 	data = bin2bcd(tm->tm_hour); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 306 | 	rs5c313_write_reg(RS5C313_ADDR_HOUR, data); | 
 | 307 | 	rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4)); | 
 | 308 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 309 | 	data = bin2bcd(tm->tm_mday); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 310 | 	rs5c313_write_reg(RS5C313_ADDR_DAY, data); | 
 | 311 | 	rs5c313_write_reg(RS5C313_ADDR_DAY10, (data>> 4)); | 
 | 312 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 313 | 	data = bin2bcd(tm->tm_mon + 1); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 314 | 	rs5c313_write_reg(RS5C313_ADDR_MON, data); | 
 | 315 | 	rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4)); | 
 | 316 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 317 | 	data = bin2bcd(tm->tm_year % 100); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 318 | 	rs5c313_write_reg(RS5C313_ADDR_YEAR, data); | 
 | 319 | 	rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4)); | 
 | 320 |  | 
| Adrian Bunk | fe20ba7 | 2008-10-18 20:28:41 -0700 | [diff] [blame] | 321 | 	data = bin2bcd(tm->tm_wday); | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 322 | 	rs5c313_write_reg(RS5C313_ADDR_WEEK, data); | 
 | 323 |  | 
 | 324 | 	RS5C313_CEDISABLE;	/* CE:H */ | 
 | 325 | 	ndelay(700); | 
 | 326 |  | 
 | 327 | 	return 0; | 
 | 328 | } | 
 | 329 |  | 
 | 330 | static void rs5c313_check_xstp_bit(void) | 
 | 331 | { | 
 | 332 | 	struct rtc_time tm; | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 333 | 	int cnt; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 334 |  | 
 | 335 | 	RS5C313_CEENABLE;	/* CE:H */ | 
 | 336 | 	if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { | 
 | 337 | 		/* INT interval reg. OFF */ | 
 | 338 | 		rs5c313_write_intintvreg(0x00); | 
 | 339 | 		/* Initialize control reg. 24 hour & adjust */ | 
 | 340 | 		rs5c313_write_cntreg(0x07); | 
 | 341 |  | 
 | 342 | 		/* busy check. */ | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 343 | 		for (cnt = 0; cnt < 100; cnt++) { | 
 | 344 | 			if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) | 
 | 345 | 				break; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 346 | 			RS5C313_MISCOP; | 
| kogiidena | 4ac24b3 | 2007-05-10 22:22:57 -0700 | [diff] [blame] | 347 | 		} | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 348 |  | 
 | 349 | 		memset(&tm, 0, sizeof(struct rtc_time)); | 
 | 350 | 		tm.tm_mday 	= 1; | 
| kogiidena | 5a6a078 | 2007-05-10 22:22:56 -0700 | [diff] [blame] | 351 | 		tm.tm_mon 	= 1 - 1; | 
 | 352 | 		tm.tm_year 	= 2000 - 1900; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 353 |  | 
 | 354 | 		rs5c313_rtc_set_time(NULL, &tm); | 
 | 355 | 		printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " | 
 | 356 | 				"1 Jan 2000\n"); | 
 | 357 | 	} | 
 | 358 | 	RS5C313_CEDISABLE; | 
 | 359 | 	ndelay(700);		/* CE:L */ | 
 | 360 | } | 
 | 361 |  | 
 | 362 | static const struct rtc_class_ops rs5c313_rtc_ops = { | 
 | 363 | 	.read_time = rs5c313_rtc_read_time, | 
 | 364 | 	.set_time = rs5c313_rtc_set_time, | 
 | 365 | }; | 
 | 366 |  | 
 | 367 | static int rs5c313_rtc_probe(struct platform_device *pdev) | 
 | 368 | { | 
 | 369 | 	struct rtc_device *rtc = rtc_device_register("rs5c313", &pdev->dev, | 
 | 370 | 				&rs5c313_rtc_ops, THIS_MODULE); | 
 | 371 |  | 
 | 372 | 	if (IS_ERR(rtc)) | 
 | 373 | 		return PTR_ERR(rtc); | 
 | 374 |  | 
 | 375 | 	platform_set_drvdata(pdev, rtc); | 
 | 376 |  | 
| kogiidena | 9a3f1d5 | 2007-05-10 22:22:54 -0700 | [diff] [blame] | 377 | 	return 0; | 
| Nobuhiro Iwamatsu | e9f2bd8 | 2007-05-08 00:26:37 -0700 | [diff] [blame] | 378 | } | 
 | 379 |  | 
 | 380 | static int __devexit rs5c313_rtc_remove(struct platform_device *pdev) | 
 | 381 | { | 
 | 382 | 	struct rtc_device *rtc = platform_get_drvdata( pdev ); | 
 | 383 |  | 
 | 384 | 	rtc_device_unregister(rtc); | 
 | 385 |  | 
 | 386 | 	return 0; | 
 | 387 | } | 
 | 388 |  | 
 | 389 | static struct platform_driver rs5c313_rtc_platform_driver = { | 
 | 390 | 	.driver         = { | 
 | 391 | 		.name   = DRV_NAME, | 
 | 392 | 		.owner  = THIS_MODULE, | 
 | 393 | 	}, | 
 | 394 | 	.probe 	= rs5c313_rtc_probe, | 
 | 395 | 	.remove = __devexit_p( rs5c313_rtc_remove ), | 
 | 396 | }; | 
 | 397 |  | 
 | 398 | static int __init rs5c313_rtc_init(void) | 
 | 399 | { | 
 | 400 | 	int err; | 
 | 401 |  | 
 | 402 | 	err = platform_driver_register(&rs5c313_rtc_platform_driver); | 
 | 403 | 	if (err) | 
 | 404 | 		return err; | 
 | 405 |  | 
 | 406 | 	rs5c313_init_port(); | 
 | 407 | 	rs5c313_check_xstp_bit(); | 
 | 408 |  | 
 | 409 | 	return 0; | 
 | 410 | } | 
 | 411 |  | 
 | 412 | static void __exit rs5c313_rtc_exit(void) | 
 | 413 | { | 
 | 414 | 	platform_driver_unregister( &rs5c313_rtc_platform_driver ); | 
 | 415 | } | 
 | 416 |  | 
 | 417 | module_init(rs5c313_rtc_init); | 
 | 418 | module_exit(rs5c313_rtc_exit); | 
 | 419 |  | 
 | 420 | MODULE_VERSION(DRV_VERSION); | 
 | 421 | MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>"); | 
 | 422 | MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver"); | 
 | 423 | MODULE_LICENSE("GPL"); | 
| Kay Sievers | ad28a07 | 2008-04-10 21:29:25 -0700 | [diff] [blame] | 424 | MODULE_ALIAS("platform:" DRV_NAME); |