17f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges/*
27f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * An SPI driver for the Philips PCF2123 RTC
37f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Copyright 2009 Cyber Switching, Inc.
47f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
57f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Author: Chris Verges <chrisv@cyberswitching.com>
67f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Maintainers: http://www.cyberswitching.com
77f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
87f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * based on the RS5C348 driver in this same directory.
97f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
107f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Thanks to Christian Pellegrin <chripell@fsfe.org> for
117f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * the sysfs contributions to this driver.
127f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
137f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * This program is free software; you can redistribute it and/or modify
147f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * it under the terms of the GNU General Public License version 2 as
157f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * published by the Free Software Foundation.
167f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
177f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Please note that the CS is active high, so platform data
187f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * should look something like:
197f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
207f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * static struct spi_board_info ek_spi_devices[] = {
21369015fbd40655f86384a3e7d5452f9c61eb26e9Sachin Kamat *	...
22369015fbd40655f86384a3e7d5452f9c61eb26e9Sachin Kamat *	{
23369015fbd40655f86384a3e7d5452f9c61eb26e9Sachin Kamat *		.modalias		= "rtc-pcf2123",
24369015fbd40655f86384a3e7d5452f9c61eb26e9Sachin Kamat *		.chip_select		= 1,
25369015fbd40655f86384a3e7d5452f9c61eb26e9Sachin Kamat *		.controller_data	= (void *)AT91_PIN_PA10,
267f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *		.max_speed_hz		= 1000 * 1000,
277f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *		.mode			= SPI_CS_HIGH,
287f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *		.bus_num		= 0,
297f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *	},
307f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *	...
317f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *};
327f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges *
337f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges */
347f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
357f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/bcd.h>
367f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/delay.h>
377f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/device.h>
387f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/errno.h>
397f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/init.h>
407f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/kernel.h>
417f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/string.h>
425a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
437f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/rtc.h>
447f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#include <linux/spi/spi.h>
452113852b239ed4a93d04135372162252f9342bb6Paul Gortmaker#include <linux/module.h>
465ed12f12825c6c0451d703bfe918a7fc190e2738Ilya Shchepetkov#include <linux/sysfs.h>
477f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
48f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges#define DRV_VERSION "0.6"
497f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
507f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_CTRL1	(0x00)	/* Control Register 1 */
517f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_CTRL2	(0x01)	/* Control Register 2 */
527f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_SC		(0x02)	/* datetime */
537f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_MN		(0x03)
547f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_HR		(0x04)
557f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_DM		(0x05)
567f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_DW		(0x06)
577f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_MO		(0x07)
587f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_REG_YR		(0x08)
597f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
607f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_SUBADDR		(1 << 4)
617f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_WRITE		((0 << 7) | PCF2123_SUBADDR)
627f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges#define PCF2123_READ		((1 << 7) | PCF2123_SUBADDR)
637f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
647f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic struct spi_driver pcf2123_driver;
657f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
667f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstruct pcf2123_sysfs_reg {
67f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	struct device_attribute attr;
687f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	char name[2];
697f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges};
707f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
717f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstruct pcf2123_plat_data {
727f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct rtc_device *rtc;
737f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct pcf2123_sysfs_reg regs[16];
747f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges};
757f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
767f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges/*
777f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select
787f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * is released properly after an SPI write.  This function should be
797f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges * called after EVERY read/write call over SPI.
807f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges */
817f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic inline void pcf2123_delay_trec(void)
827f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
837f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ndelay(30);
847f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
857f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
867f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
877f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			    char *buffer)
887f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
897f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct spi_device *spi = to_spi_device(dev);
90f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	struct pcf2123_sysfs_reg *r;
917f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	u8 txbuf[1], rxbuf[1];
92f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	unsigned long reg;
937f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int ret;
947f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
95f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	r = container_of(attr, struct pcf2123_sysfs_reg, attr);
96f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
974c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	ret = kstrtoul(r->name, 16, &reg);
984c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	if (ret)
994c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han		return ret;
100f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
101f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	txbuf[0] = PCF2123_READ | reg;
1027f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
1037f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
104f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges		return -EIO;
1057f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
106f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	return sprintf(buffer, "0x%x\n", rxbuf[0]);
1077f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
1087f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1097f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
1107f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			     const char *buffer, size_t count) {
1117f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct spi_device *spi = to_spi_device(dev);
112f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	struct pcf2123_sysfs_reg *r;
1137f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	u8 txbuf[2];
114f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	unsigned long reg;
115f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	unsigned long val;
116f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
1177f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int ret;
1187f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
119f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	r = container_of(attr, struct pcf2123_sysfs_reg, attr);
120f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
1214c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	ret = kstrtoul(r->name, 16, &reg);
1224c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	if (ret)
1234c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han		return ret;
1244c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han
1254c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	ret = kstrtoul(buffer, 10, &val);
1264c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han	if (ret)
1274c5591c1eee54b7775ea63635ae310a29f0207bbJingoo Han		return ret;
128f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
129f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	txbuf[0] = PCF2123_WRITE | reg;
130f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	txbuf[1] = val;
1317f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, sizeof(txbuf));
1327f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
1337f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return -EIO;
1347f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
1357f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return count;
1367f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
1377f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1387f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
1397f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
1407f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct spi_device *spi = to_spi_device(dev);
1417f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	u8 txbuf[1], rxbuf[7];
1427f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int ret;
1437f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1447f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_READ | PCF2123_REG_SC;
1457f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
1467f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			rxbuf, sizeof(rxbuf));
1477f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
1487f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return ret;
1497f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
1507f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1517f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F);
1527f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_min = bcd2bin(rxbuf[1] & 0x7F);
1537f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */
1547f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F);
1557f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_wday = rxbuf[4] & 0x07;
1567f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */
1577f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	tm->tm_year = bcd2bin(rxbuf[6]);
1587f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (tm->tm_year < 70)
1597f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		tm->tm_year += 100;	/* assume we are in 1970...2069 */
1607f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1617f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
1627f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			"mday=%d, mon=%d, year=%d, wday=%d\n",
1637f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			__func__,
1647f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			tm->tm_sec, tm->tm_min, tm->tm_hour,
1657f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
1667f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1677f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* the clock can give out invalid datetime, but we cannot return
1687f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	 * -EINVAL otherwise hwclock will refuse to set the time on bootup.
1697f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	 */
1707f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (rtc_valid_tm(tm) < 0)
1717f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		dev_err(dev, "retrieved date/time is not valid.\n");
1727f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1737f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return 0;
1747f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
1757f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1767f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
1777f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
1787f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct spi_device *spi = to_spi_device(dev);
1797f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	u8 txbuf[8];
1807f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int ret;
1817f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1827f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
1837f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			"mday=%d, mon=%d, year=%d, wday=%d\n",
1847f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			__func__,
1857f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			tm->tm_sec, tm->tm_min, tm->tm_hour,
1867f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
1877f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1887f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Stop the counter first */
1897f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
1907f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = 0x20;
1917f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, 2);
1927f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
1937f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return ret;
1947f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
1957f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
1967f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Set the new time */
1977f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC;
1987f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = bin2bcd(tm->tm_sec & 0x7F);
1997f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[2] = bin2bcd(tm->tm_min & 0x7F);
2007f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[3] = bin2bcd(tm->tm_hour & 0x3F);
2017f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[4] = bin2bcd(tm->tm_mday & 0x3F);
2027f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[5] = tm->tm_wday & 0x07;
2037f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
2047f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100);
2057f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2067f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, sizeof(txbuf));
2077f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
2087f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return ret;
2097f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2107f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2117f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Start the counter */
2127f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
2137f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = 0x00;
2147f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, 2);
2157f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
2167f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return ret;
2177f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2187f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2197f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return 0;
2207f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
2217f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2227f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic const struct rtc_class_ops pcf2123_rtc_ops = {
2237f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	.read_time	= pcf2123_rtc_read_time,
2247f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	.set_time	= pcf2123_rtc_set_time,
2257f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges};
2267f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2275a167f4543e45d45c5672a5cd6cb8ba5ddf4f3eaGreg Kroah-Hartmanstatic int pcf2123_probe(struct spi_device *spi)
2287f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
2297f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct rtc_device *rtc;
2307f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	struct pcf2123_plat_data *pdata;
2317f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	u8 txbuf[2], rxbuf[2];
2327f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int ret, i;
2337f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
234dd48ccc491c3c327bef12e4d07bfa4d4266be3b9Jingoo Han	pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data),
235dd48ccc491c3c327bef12e4d07bfa4d4266be3b9Jingoo Han				GFP_KERNEL);
2367f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (!pdata)
2377f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		return -ENOMEM;
2387f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	spi->dev.platform_data = pdata;
2397f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2407f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Send a software reset command */
2417f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
2427f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = 0x58;
2437f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n",
2447f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			txbuf[0], txbuf[1]);
2457f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, 2 * sizeof(u8));
2467f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
247f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges		goto kfree_exit;
2487f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2497f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2507f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Stop the counter */
2517f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
2527f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = 0x20;
2537f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n",
2547f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			txbuf[0], txbuf[1]);
2557f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, 2 * sizeof(u8));
2567f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
257f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges		goto kfree_exit;
2587f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2597f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2607f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* See if the counter was actually stopped */
2617f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1;
2627f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n",
2637f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			txbuf[0]);
2647f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8),
2657f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges					rxbuf, 2 * sizeof(u8));
2667f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n",
2677f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			rxbuf[0], rxbuf[1]);
2687f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
2697f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		goto kfree_exit;
2707f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2717f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2727f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (!(rxbuf[0] & 0x20)) {
2737f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		dev_err(&spi->dev, "chip not found\n");
27435623715818dfa720cccf99cd280dcbb4b78da23Wei Yongjun		ret = -ENODEV;
2757f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		goto kfree_exit;
2767f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	}
2777f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2787f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
2797f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	dev_info(&spi->dev, "spiclk %u KHz.\n",
2807f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			(spi->max_speed_hz + 500) / 1000);
2817f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2827f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Start the counter */
2837f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
2847f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	txbuf[1] = 0x00;
2857f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	ret = spi_write(spi, txbuf, sizeof(txbuf));
2867f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (ret < 0)
2877f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		goto kfree_exit;
2887f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pcf2123_delay_trec();
2897f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2907f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	/* Finalize the initialization */
291dd48ccc491c3c327bef12e4d07bfa4d4266be3b9Jingoo Han	rtc = devm_rtc_device_register(&spi->dev, pcf2123_driver.driver.name,
2927f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			&pcf2123_rtc_ops, THIS_MODULE);
2937f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
2947f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (IS_ERR(rtc)) {
2957f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		dev_err(&spi->dev, "failed to register.\n");
2967f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		ret = PTR_ERR(rtc);
2977f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		goto kfree_exit;
2987f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	}
2997f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3007f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	pdata->rtc = rtc;
3017f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3027f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	for (i = 0; i < 16; i++) {
3035ed12f12825c6c0451d703bfe918a7fc190e2738Ilya Shchepetkov		sysfs_attr_init(&pdata->regs[i].attr.attr);
3047f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		sprintf(pdata->regs[i].name, "%1x", i);
3057f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
3067f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		pdata->regs[i].attr.attr.name = pdata->regs[i].name;
3077f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		pdata->regs[i].attr.show = pcf2123_show;
3087f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		pdata->regs[i].attr.store = pcf2123_store;
3097f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		ret = device_create_file(&spi->dev, &pdata->regs[i].attr);
3107f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		if (ret) {
3117f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			dev_err(&spi->dev, "Unable to create sysfs %s\n",
3127f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges				pdata->regs[i].name);
313f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges			goto sysfs_exit;
3147f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		}
3157f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	}
3167f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3177f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return 0;
318f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
319f3d2570a1482a6d897ba29276964965f6fe970d8Chris Vergessysfs_exit:
320f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges	for (i--; i >= 0; i--)
321f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges		device_remove_file(&spi->dev, &pdata->regs[i].attr);
322f3d2570a1482a6d897ba29276964965f6fe970d8Chris Verges
3237f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergeskfree_exit:
3247f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	spi->dev.platform_data = NULL;
3257f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return ret;
3267f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
3277f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3285a167f4543e45d45c5672a5cd6cb8ba5ddf4f3eaGreg Kroah-Hartmanstatic int pcf2123_remove(struct spi_device *spi)
3297f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges{
330ffc75bb8f240b65ecd4e23d7f87863d9f7aeeeffJingoo Han	struct pcf2123_plat_data *pdata = dev_get_platdata(&spi->dev);
3317f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	int i;
3327f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3337f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	if (pdata) {
3347f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges		for (i = 0; i < 16; i++)
3357f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			if (pdata->regs[i].name[0])
3367f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges				device_remove_file(&spi->dev,
3377f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges						   &pdata->regs[i].attr);
3387f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	}
3397f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3407f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	return 0;
3417f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges}
3427f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3437f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Vergesstatic struct spi_driver pcf2123_driver = {
3447f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	.driver	= {
3457f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			.name	= "rtc-pcf2123",
3467f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges			.owner	= THIS_MODULE,
3477f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	},
3487f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges	.probe	= pcf2123_probe,
3495a167f4543e45d45c5672a5cd6cb8ba5ddf4f3eaGreg Kroah-Hartman	.remove	= pcf2123_remove,
3507f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges};
3517f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
352109e941843893cb1b4f7bed24c615ba84ce00ff5Axel Linmodule_spi_driver(pcf2123_driver);
3537f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris Verges
3547f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris VergesMODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
3557f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris VergesMODULE_DESCRIPTION("NXP PCF2123 RTC driver");
3567f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris VergesMODULE_LICENSE("GPL");
3577f3923a184bb8e7ede5e5f58f1114bf7b8c611eaChris VergesMODULE_VERSION(DRV_VERSION);
358