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, ®); 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, ®); 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