18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * An SPI driver for the Philips PCF2123 RTC 48c2ecf20Sopenharmony_ci * Copyright 2009 Cyber Switching, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Chris Verges <chrisv@cyberswitching.com> 78c2ecf20Sopenharmony_ci * Maintainers: http://www.cyberswitching.com 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based on the RS5C348 driver in this same directory. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Thanks to Christian Pellegrin <chripell@fsfe.org> for 128c2ecf20Sopenharmony_ci * the sysfs contributions to this driver. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Please note that the CS is active high, so platform data 158c2ecf20Sopenharmony_ci * should look something like: 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * static struct spi_board_info ek_spi_devices[] = { 188c2ecf20Sopenharmony_ci * ... 198c2ecf20Sopenharmony_ci * { 208c2ecf20Sopenharmony_ci * .modalias = "rtc-pcf2123", 218c2ecf20Sopenharmony_ci * .chip_select = 1, 228c2ecf20Sopenharmony_ci * .controller_data = (void *)AT91_PIN_PA10, 238c2ecf20Sopenharmony_ci * .max_speed_hz = 1000 * 1000, 248c2ecf20Sopenharmony_ci * .mode = SPI_CS_HIGH, 258c2ecf20Sopenharmony_ci * .bus_num = 0, 268c2ecf20Sopenharmony_ci * }, 278c2ecf20Sopenharmony_ci * ... 288c2ecf20Sopenharmony_ci *}; 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/bcd.h> 328c2ecf20Sopenharmony_ci#include <linux/delay.h> 338c2ecf20Sopenharmony_ci#include <linux/device.h> 348c2ecf20Sopenharmony_ci#include <linux/errno.h> 358c2ecf20Sopenharmony_ci#include <linux/init.h> 368c2ecf20Sopenharmony_ci#include <linux/kernel.h> 378c2ecf20Sopenharmony_ci#include <linux/of.h> 388c2ecf20Sopenharmony_ci#include <linux/string.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci#include <linux/rtc.h> 418c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 428c2ecf20Sopenharmony_ci#include <linux/module.h> 438c2ecf20Sopenharmony_ci#include <linux/regmap.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* REGISTERS */ 468c2ecf20Sopenharmony_ci#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ 478c2ecf20Sopenharmony_ci#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ 488c2ecf20Sopenharmony_ci#define PCF2123_REG_SC (0x02) /* datetime */ 498c2ecf20Sopenharmony_ci#define PCF2123_REG_MN (0x03) 508c2ecf20Sopenharmony_ci#define PCF2123_REG_HR (0x04) 518c2ecf20Sopenharmony_ci#define PCF2123_REG_DM (0x05) 528c2ecf20Sopenharmony_ci#define PCF2123_REG_DW (0x06) 538c2ecf20Sopenharmony_ci#define PCF2123_REG_MO (0x07) 548c2ecf20Sopenharmony_ci#define PCF2123_REG_YR (0x08) 558c2ecf20Sopenharmony_ci#define PCF2123_REG_ALRM_MN (0x09) /* Alarm Registers */ 568c2ecf20Sopenharmony_ci#define PCF2123_REG_ALRM_HR (0x0a) 578c2ecf20Sopenharmony_ci#define PCF2123_REG_ALRM_DM (0x0b) 588c2ecf20Sopenharmony_ci#define PCF2123_REG_ALRM_DW (0x0c) 598c2ecf20Sopenharmony_ci#define PCF2123_REG_OFFSET (0x0d) /* Clock Rate Offset Register */ 608c2ecf20Sopenharmony_ci#define PCF2123_REG_TMR_CLKOUT (0x0e) /* Timer Registers */ 618c2ecf20Sopenharmony_ci#define PCF2123_REG_CTDWN_TMR (0x0f) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* PCF2123_REG_CTRL1 BITS */ 648c2ecf20Sopenharmony_ci#define CTRL1_CLEAR (0) /* Clear */ 658c2ecf20Sopenharmony_ci#define CTRL1_CORR_INT BIT(1) /* Correction irq enable */ 668c2ecf20Sopenharmony_ci#define CTRL1_12_HOUR BIT(2) /* 12 hour time */ 678c2ecf20Sopenharmony_ci#define CTRL1_SW_RESET (BIT(3) | BIT(4) | BIT(6)) /* Software reset */ 688c2ecf20Sopenharmony_ci#define CTRL1_STOP BIT(5) /* Stop the clock */ 698c2ecf20Sopenharmony_ci#define CTRL1_EXT_TEST BIT(7) /* External clock test mode */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* PCF2123_REG_CTRL2 BITS */ 728c2ecf20Sopenharmony_ci#define CTRL2_TIE BIT(0) /* Countdown timer irq enable */ 738c2ecf20Sopenharmony_ci#define CTRL2_AIE BIT(1) /* Alarm irq enable */ 748c2ecf20Sopenharmony_ci#define CTRL2_TF BIT(2) /* Countdown timer flag */ 758c2ecf20Sopenharmony_ci#define CTRL2_AF BIT(3) /* Alarm flag */ 768c2ecf20Sopenharmony_ci#define CTRL2_TI_TP BIT(4) /* Irq pin generates pulse */ 778c2ecf20Sopenharmony_ci#define CTRL2_MSF BIT(5) /* Minute or second irq flag */ 788c2ecf20Sopenharmony_ci#define CTRL2_SI BIT(6) /* Second irq enable */ 798c2ecf20Sopenharmony_ci#define CTRL2_MI BIT(7) /* Minute irq enable */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* PCF2123_REG_SC BITS */ 828c2ecf20Sopenharmony_ci#define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* PCF2123_REG_ALRM_XX BITS */ 858c2ecf20Sopenharmony_ci#define ALRM_DISABLE BIT(7) /* MN, HR, DM, or DW alarm matching */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* PCF2123_REG_TMR_CLKOUT BITS */ 888c2ecf20Sopenharmony_ci#define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */ 898c2ecf20Sopenharmony_ci#define CD_TMR_64HZ (1) /* 64 Hz countdown timer */ 908c2ecf20Sopenharmony_ci#define CD_TMR_1HZ (2) /* 1 Hz countdown timer */ 918c2ecf20Sopenharmony_ci#define CD_TMR_60th_HZ (3) /* 60th Hz countdown timer */ 928c2ecf20Sopenharmony_ci#define CD_TMR_TE BIT(3) /* Countdown timer enable */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* PCF2123_REG_OFFSET BITS */ 958c2ecf20Sopenharmony_ci#define OFFSET_SIGN_BIT 6 /* 2's complement sign bit */ 968c2ecf20Sopenharmony_ci#define OFFSET_COARSE BIT(7) /* Coarse mode offset */ 978c2ecf20Sopenharmony_ci#define OFFSET_STEP (2170) /* Offset step in parts per billion */ 988c2ecf20Sopenharmony_ci#define OFFSET_MASK GENMASK(6, 0) /* Offset value */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* READ/WRITE ADDRESS BITS */ 1018c2ecf20Sopenharmony_ci#define PCF2123_WRITE BIT(4) 1028c2ecf20Sopenharmony_ci#define PCF2123_READ (BIT(4) | BIT(7)) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic struct spi_driver pcf2123_driver; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct pcf2123_data { 1088c2ecf20Sopenharmony_ci struct rtc_device *rtc; 1098c2ecf20Sopenharmony_ci struct regmap *map; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct regmap_config pcf2123_regmap_config = { 1138c2ecf20Sopenharmony_ci .reg_bits = 8, 1148c2ecf20Sopenharmony_ci .val_bits = 8, 1158c2ecf20Sopenharmony_ci .read_flag_mask = PCF2123_READ, 1168c2ecf20Sopenharmony_ci .write_flag_mask = PCF2123_WRITE, 1178c2ecf20Sopenharmony_ci .max_register = PCF2123_REG_CTDWN_TMR, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int pcf2123_read_offset(struct device *dev, long *offset) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 1238c2ecf20Sopenharmony_ci int ret, val; 1248c2ecf20Sopenharmony_ci unsigned int reg; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = regmap_read(pcf2123->map, PCF2123_REG_OFFSET, ®); 1278c2ecf20Sopenharmony_ci if (ret) 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci val = sign_extend32((reg & OFFSET_MASK), OFFSET_SIGN_BIT); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (reg & OFFSET_COARSE) 1338c2ecf20Sopenharmony_ci val *= 2; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci *offset = ((long)val) * OFFSET_STEP; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * The offset register is a 7 bit signed value with a coarse bit in bit 7. 1428c2ecf20Sopenharmony_ci * The main difference between the two is normal offset adjusts the first 1438c2ecf20Sopenharmony_ci * second of n minutes every other hour, with 61, 62 and 63 being shoved 1448c2ecf20Sopenharmony_ci * into the 60th minute. 1458c2ecf20Sopenharmony_ci * The coarse adjustment does the same, but every hour. 1468c2ecf20Sopenharmony_ci * the two overlap, with every even normal offset value corresponding 1478c2ecf20Sopenharmony_ci * to a coarse offset. Based on this algorithm, it seems that despite the 1488c2ecf20Sopenharmony_ci * name, coarse offset is a better fit for overlapping values. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_cistatic int pcf2123_set_offset(struct device *dev, long offset) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 1538c2ecf20Sopenharmony_ci s8 reg; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (offset > OFFSET_STEP * 127) 1568c2ecf20Sopenharmony_ci reg = 127; 1578c2ecf20Sopenharmony_ci else if (offset < OFFSET_STEP * -128) 1588c2ecf20Sopenharmony_ci reg = -128; 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci reg = DIV_ROUND_CLOSEST(offset, OFFSET_STEP); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* choose fine offset only for odd values in the normal range */ 1638c2ecf20Sopenharmony_ci if (reg & 1 && reg <= 63 && reg >= -64) { 1648c2ecf20Sopenharmony_ci /* Normal offset. Clear the coarse bit */ 1658c2ecf20Sopenharmony_ci reg &= ~OFFSET_COARSE; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci /* Coarse offset. Divide by 2 and set the coarse bit */ 1688c2ecf20Sopenharmony_ci reg >>= 1; 1698c2ecf20Sopenharmony_ci reg |= OFFSET_COARSE; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return regmap_write(pcf2123->map, PCF2123_REG_OFFSET, (unsigned int)reg); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 1788c2ecf20Sopenharmony_ci u8 rxbuf[7]; 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_SC, rxbuf, 1828c2ecf20Sopenharmony_ci sizeof(rxbuf)); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (rxbuf[0] & OSC_HAS_STOPPED) { 1878c2ecf20Sopenharmony_ci dev_info(dev, "clock was stopped. Time is not valid\n"); 1888c2ecf20Sopenharmony_ci return -EINVAL; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); 1928c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); 1938c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ 1948c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); 1958c2ecf20Sopenharmony_ci tm->tm_wday = rxbuf[4] & 0x07; 1968c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ 1978c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(rxbuf[6]) + 100; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 2078c2ecf20Sopenharmony_ci u8 txbuf[7]; 2088c2ecf20Sopenharmony_ci int ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Stop the counter first */ 2138c2ecf20Sopenharmony_ci ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); 2148c2ecf20Sopenharmony_ci if (ret) 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Set the new time */ 2188c2ecf20Sopenharmony_ci txbuf[0] = bin2bcd(tm->tm_sec & 0x7F); 2198c2ecf20Sopenharmony_ci txbuf[1] = bin2bcd(tm->tm_min & 0x7F); 2208c2ecf20Sopenharmony_ci txbuf[2] = bin2bcd(tm->tm_hour & 0x3F); 2218c2ecf20Sopenharmony_ci txbuf[3] = bin2bcd(tm->tm_mday & 0x3F); 2228c2ecf20Sopenharmony_ci txbuf[4] = tm->tm_wday & 0x07; 2238c2ecf20Sopenharmony_ci txbuf[5] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ 2248c2ecf20Sopenharmony_ci txbuf[6] = bin2bcd(tm->tm_year - 100); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_SC, txbuf, 2278c2ecf20Sopenharmony_ci sizeof(txbuf)); 2288c2ecf20Sopenharmony_ci if (ret) 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Start the counter */ 2328c2ecf20Sopenharmony_ci ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int pcf2123_rtc_alarm_irq_enable(struct device *dev, unsigned int en) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 2448c2ecf20Sopenharmony_ci en ? CTRL2_AIE : 0); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 2508c2ecf20Sopenharmony_ci u8 rxbuf[4]; 2518c2ecf20Sopenharmony_ci int ret; 2528c2ecf20Sopenharmony_ci unsigned int val = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_ALRM_MN, rxbuf, 2558c2ecf20Sopenharmony_ci sizeof(rxbuf)); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F); 2608c2ecf20Sopenharmony_ci alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F); 2618c2ecf20Sopenharmony_ci alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F); 2628c2ecf20Sopenharmony_ci alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); 2678c2ecf20Sopenharmony_ci if (ret) 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci alm->enabled = !!(val & CTRL2_AIE); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 2788c2ecf20Sopenharmony_ci u8 txbuf[4]; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Disable alarm interrupt */ 2848c2ecf20Sopenharmony_ci ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0); 2858c2ecf20Sopenharmony_ci if (ret) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Ensure alarm flag is clear */ 2898c2ecf20Sopenharmony_ci ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); 2908c2ecf20Sopenharmony_ci if (ret) 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Set new alarm */ 2948c2ecf20Sopenharmony_ci txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F); 2958c2ecf20Sopenharmony_ci txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F); 2968c2ecf20Sopenharmony_ci txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F); 2978c2ecf20Sopenharmony_ci txbuf[3] = ALRM_DISABLE; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_ALRM_MN, txbuf, 3008c2ecf20Sopenharmony_ci sizeof(txbuf)); 3018c2ecf20Sopenharmony_ci if (ret) 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return pcf2123_rtc_alarm_irq_enable(dev, alm->enabled); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic irqreturn_t pcf2123_rtc_irq(int irq, void *dev) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 3108c2ecf20Sopenharmony_ci struct mutex *lock = &pcf2123->rtc->ops_lock; 3118c2ecf20Sopenharmony_ci unsigned int val = 0; 3128c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci mutex_lock(lock); 3158c2ecf20Sopenharmony_ci regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Alarm? */ 3188c2ecf20Sopenharmony_ci if (val & CTRL2_AF) { 3198c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Clear alarm flag */ 3228c2ecf20Sopenharmony_ci regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci mutex_unlock(lock); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int pcf2123_reset(struct device *dev) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci unsigned int val = 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_SW_RESET); 3398c2ecf20Sopenharmony_ci if (ret) 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Stop the counter */ 3438c2ecf20Sopenharmony_ci dev_dbg(dev, "stopping RTC\n"); 3448c2ecf20Sopenharmony_ci ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); 3458c2ecf20Sopenharmony_ci if (ret) 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* See if the counter was actually stopped */ 3498c2ecf20Sopenharmony_ci dev_dbg(dev, "checking for presence of RTC\n"); 3508c2ecf20Sopenharmony_ci ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL1, &val); 3518c2ecf20Sopenharmony_ci if (ret) 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci dev_dbg(dev, "received data from RTC (0x%08X)\n", val); 3558c2ecf20Sopenharmony_ci if (!(val & CTRL1_STOP)) 3568c2ecf20Sopenharmony_ci return -ENODEV; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Start the counter */ 3598c2ecf20Sopenharmony_ci ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); 3608c2ecf20Sopenharmony_ci if (ret) 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic const struct rtc_class_ops pcf2123_rtc_ops = { 3678c2ecf20Sopenharmony_ci .read_time = pcf2123_rtc_read_time, 3688c2ecf20Sopenharmony_ci .set_time = pcf2123_rtc_set_time, 3698c2ecf20Sopenharmony_ci .read_offset = pcf2123_read_offset, 3708c2ecf20Sopenharmony_ci .set_offset = pcf2123_set_offset, 3718c2ecf20Sopenharmony_ci .read_alarm = pcf2123_rtc_read_alarm, 3728c2ecf20Sopenharmony_ci .set_alarm = pcf2123_rtc_set_alarm, 3738c2ecf20Sopenharmony_ci .alarm_irq_enable = pcf2123_rtc_alarm_irq_enable, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int pcf2123_probe(struct spi_device *spi) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct rtc_device *rtc; 3798c2ecf20Sopenharmony_ci struct rtc_time tm; 3808c2ecf20Sopenharmony_ci struct pcf2123_data *pcf2123; 3818c2ecf20Sopenharmony_ci int ret = 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci pcf2123 = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_data), 3848c2ecf20Sopenharmony_ci GFP_KERNEL); 3858c2ecf20Sopenharmony_ci if (!pcf2123) 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev_set_drvdata(&spi->dev, pcf2123); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci pcf2123->map = devm_regmap_init_spi(spi, &pcf2123_regmap_config); 3918c2ecf20Sopenharmony_ci if (IS_ERR(pcf2123->map)) { 3928c2ecf20Sopenharmony_ci dev_err(&spi->dev, "regmap init failed.\n"); 3938c2ecf20Sopenharmony_ci return PTR_ERR(pcf2123->map); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = pcf2123_rtc_read_time(&spi->dev, &tm); 3978c2ecf20Sopenharmony_ci if (ret < 0) { 3988c2ecf20Sopenharmony_ci ret = pcf2123_reset(&spi->dev); 3998c2ecf20Sopenharmony_ci if (ret < 0) { 4008c2ecf20Sopenharmony_ci dev_err(&spi->dev, "chip not found\n"); 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dev_info(&spi->dev, "spiclk %u KHz.\n", 4068c2ecf20Sopenharmony_ci (spi->max_speed_hz + 500) / 1000); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Finalize the initialization */ 4098c2ecf20Sopenharmony_ci rtc = devm_rtc_allocate_device(&spi->dev); 4108c2ecf20Sopenharmony_ci if (IS_ERR(rtc)) 4118c2ecf20Sopenharmony_ci return PTR_ERR(rtc); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci pcf2123->rtc = rtc; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Register alarm irq */ 4168c2ecf20Sopenharmony_ci if (spi->irq > 0) { 4178c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, 4188c2ecf20Sopenharmony_ci pcf2123_rtc_irq, 4198c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 4208c2ecf20Sopenharmony_ci pcf2123_driver.driver.name, &spi->dev); 4218c2ecf20Sopenharmony_ci if (!ret) 4228c2ecf20Sopenharmony_ci device_init_wakeup(&spi->dev, true); 4238c2ecf20Sopenharmony_ci else 4248c2ecf20Sopenharmony_ci dev_err(&spi->dev, "could not request irq.\n"); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* The PCF2123's alarm only has minute accuracy. Must add timer 4288c2ecf20Sopenharmony_ci * support to this driver to generate interrupts more than once 4298c2ecf20Sopenharmony_ci * per minute. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci rtc->uie_unsupported = 1; 4328c2ecf20Sopenharmony_ci rtc->ops = &pcf2123_rtc_ops; 4338c2ecf20Sopenharmony_ci rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 4348c2ecf20Sopenharmony_ci rtc->range_max = RTC_TIMESTAMP_END_2099; 4358c2ecf20Sopenharmony_ci rtc->set_start_time = true; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = rtc_register_device(rtc); 4388c2ecf20Sopenharmony_ci if (ret) 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4458c2ecf20Sopenharmony_cistatic const struct of_device_id pcf2123_dt_ids[] = { 4468c2ecf20Sopenharmony_ci { .compatible = "nxp,pcf2123", }, 4478c2ecf20Sopenharmony_ci { .compatible = "microcrystal,rv2123", }, 4488c2ecf20Sopenharmony_ci /* Deprecated, do not use */ 4498c2ecf20Sopenharmony_ci { .compatible = "nxp,rtc-pcf2123", }, 4508c2ecf20Sopenharmony_ci { /* sentinel */ } 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pcf2123_dt_ids); 4538c2ecf20Sopenharmony_ci#endif 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic struct spi_driver pcf2123_driver = { 4568c2ecf20Sopenharmony_ci .driver = { 4578c2ecf20Sopenharmony_ci .name = "rtc-pcf2123", 4588c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(pcf2123_dt_ids), 4598c2ecf20Sopenharmony_ci }, 4608c2ecf20Sopenharmony_ci .probe = pcf2123_probe, 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cimodule_spi_driver(pcf2123_driver); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); 4668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NXP PCF2123 RTC driver"); 4678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 468