1/* 2 * TI LP8788 MFD - rtc driver 3 * 4 * Copyright 2012 Texas Instruments 5 * 6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14#include <linux/err.h> 15#include <linux/irqdomain.h> 16#include <linux/mfd/lp8788.h> 17#include <linux/module.h> 18#include <linux/platform_device.h> 19#include <linux/rtc.h> 20#include <linux/slab.h> 21 22/* register address */ 23#define LP8788_INTEN_3 0x05 24#define LP8788_RTC_UNLOCK 0x64 25#define LP8788_RTC_SEC 0x70 26#define LP8788_ALM1_SEC 0x77 27#define LP8788_ALM1_EN 0x7D 28#define LP8788_ALM2_SEC 0x7E 29#define LP8788_ALM2_EN 0x84 30 31/* mask/shift bits */ 32#define LP8788_INT_RTC_ALM1_M BIT(1) /* Addr 05h */ 33#define LP8788_INT_RTC_ALM1_S 1 34#define LP8788_INT_RTC_ALM2_M BIT(2) /* Addr 05h */ 35#define LP8788_INT_RTC_ALM2_S 2 36#define LP8788_ALM_EN_M BIT(7) /* Addr 7Dh or 84h */ 37#define LP8788_ALM_EN_S 7 38 39#define DEFAULT_ALARM_SEL LP8788_ALARM_1 40#define LP8788_MONTH_OFFSET 1 41#define LP8788_BASE_YEAR 2000 42#define MAX_WDAY_BITS 7 43#define LP8788_WDAY_SET 1 44#define RTC_UNLOCK 0x1 45#define RTC_LATCH 0x2 46#define ALARM_IRQ_FLAG (RTC_IRQF | RTC_AF) 47 48enum lp8788_time { 49 LPTIME_SEC, 50 LPTIME_MIN, 51 LPTIME_HOUR, 52 LPTIME_MDAY, 53 LPTIME_MON, 54 LPTIME_YEAR, 55 LPTIME_WDAY, 56 LPTIME_MAX, 57}; 58 59struct lp8788_rtc { 60 struct lp8788 *lp; 61 struct rtc_device *rdev; 62 enum lp8788_alarm_sel alarm; 63 int irq; 64}; 65 66static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = { 67 LP8788_ALM1_SEC, 68 LP8788_ALM2_SEC, 69}; 70 71static const u8 addr_alarm_en[LP8788_ALARM_MAX] = { 72 LP8788_ALM1_EN, 73 LP8788_ALM2_EN, 74}; 75 76static const u8 mask_alarm_en[LP8788_ALARM_MAX] = { 77 LP8788_INT_RTC_ALM1_M, 78 LP8788_INT_RTC_ALM2_M, 79}; 80 81static const u8 shift_alarm_en[LP8788_ALARM_MAX] = { 82 LP8788_INT_RTC_ALM1_S, 83 LP8788_INT_RTC_ALM2_S, 84}; 85 86static int _to_tm_wday(u8 lp8788_wday) 87{ 88 int i; 89 90 if (lp8788_wday == 0) 91 return 0; 92 93 /* lookup defined weekday from read register value */ 94 for (i = 0; i < MAX_WDAY_BITS; i++) { 95 if ((lp8788_wday >> i) == LP8788_WDAY_SET) 96 break; 97 } 98 99 return i + 1; 100} 101 102static inline int _to_lp8788_wday(int tm_wday) 103{ 104 return LP8788_WDAY_SET << (tm_wday - 1); 105} 106 107static void lp8788_rtc_unlock(struct lp8788 *lp) 108{ 109 lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK); 110 lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH); 111} 112 113static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm) 114{ 115 struct lp8788_rtc *rtc = dev_get_drvdata(dev); 116 struct lp8788 *lp = rtc->lp; 117 u8 data[LPTIME_MAX]; 118 int ret; 119 120 lp8788_rtc_unlock(lp); 121 122 ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX); 123 if (ret) 124 return ret; 125 126 tm->tm_sec = data[LPTIME_SEC]; 127 tm->tm_min = data[LPTIME_MIN]; 128 tm->tm_hour = data[LPTIME_HOUR]; 129 tm->tm_mday = data[LPTIME_MDAY]; 130 tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; 131 tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; 132 tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); 133 134 return 0; 135} 136 137static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm) 138{ 139 struct lp8788_rtc *rtc = dev_get_drvdata(dev); 140 struct lp8788 *lp = rtc->lp; 141 u8 data[LPTIME_MAX - 1]; 142 int ret, i, year; 143 144 year = tm->tm_year + 1900 - LP8788_BASE_YEAR; 145 if (year < 0) { 146 dev_err(lp->dev, "invalid year: %d\n", year); 147 return -EINVAL; 148 } 149 150 /* because rtc weekday is a readonly register, do not update */ 151 data[LPTIME_SEC] = tm->tm_sec; 152 data[LPTIME_MIN] = tm->tm_min; 153 data[LPTIME_HOUR] = tm->tm_hour; 154 data[LPTIME_MDAY] = tm->tm_mday; 155 data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; 156 data[LPTIME_YEAR] = year; 157 158 for (i = 0; i < ARRAY_SIZE(data); i++) { 159 ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]); 160 if (ret) 161 return ret; 162 } 163 164 return 0; 165} 166 167static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) 168{ 169 struct lp8788_rtc *rtc = dev_get_drvdata(dev); 170 struct lp8788 *lp = rtc->lp; 171 struct rtc_time *tm = &alarm->time; 172 u8 addr, data[LPTIME_MAX]; 173 int ret; 174 175 addr = addr_alarm_sec[rtc->alarm]; 176 ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX); 177 if (ret) 178 return ret; 179 180 tm->tm_sec = data[LPTIME_SEC]; 181 tm->tm_min = data[LPTIME_MIN]; 182 tm->tm_hour = data[LPTIME_HOUR]; 183 tm->tm_mday = data[LPTIME_MDAY]; 184 tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; 185 tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; 186 tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); 187 alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M; 188 189 return 0; 190} 191 192static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) 193{ 194 struct lp8788_rtc *rtc = dev_get_drvdata(dev); 195 struct lp8788 *lp = rtc->lp; 196 struct rtc_time *tm = &alarm->time; 197 u8 addr, data[LPTIME_MAX]; 198 int ret, i, year; 199 200 year = tm->tm_year + 1900 - LP8788_BASE_YEAR; 201 if (year < 0) { 202 dev_err(lp->dev, "invalid year: %d\n", year); 203 return -EINVAL; 204 } 205 206 data[LPTIME_SEC] = tm->tm_sec; 207 data[LPTIME_MIN] = tm->tm_min; 208 data[LPTIME_HOUR] = tm->tm_hour; 209 data[LPTIME_MDAY] = tm->tm_mday; 210 data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; 211 data[LPTIME_YEAR] = year; 212 data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday); 213 214 for (i = 0; i < ARRAY_SIZE(data); i++) { 215 addr = addr_alarm_sec[rtc->alarm] + i; 216 ret = lp8788_write_byte(lp, addr, data[i]); 217 if (ret) 218 return ret; 219 } 220 221 alarm->enabled = 1; 222 addr = addr_alarm_en[rtc->alarm]; 223 224 return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M, 225 alarm->enabled << LP8788_ALM_EN_S); 226} 227 228static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable) 229{ 230 struct lp8788_rtc *rtc = dev_get_drvdata(dev); 231 struct lp8788 *lp = rtc->lp; 232 u8 mask, shift; 233 234 if (!rtc->irq) 235 return -EIO; 236 237 mask = mask_alarm_en[rtc->alarm]; 238 shift = shift_alarm_en[rtc->alarm]; 239 240 return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift); 241} 242 243static const struct rtc_class_ops lp8788_rtc_ops = { 244 .read_time = lp8788_rtc_read_time, 245 .set_time = lp8788_rtc_set_time, 246 .read_alarm = lp8788_read_alarm, 247 .set_alarm = lp8788_set_alarm, 248 .alarm_irq_enable = lp8788_alarm_irq_enable, 249}; 250 251static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr) 252{ 253 struct lp8788_rtc *rtc = ptr; 254 255 rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG); 256 return IRQ_HANDLED; 257} 258 259static int lp8788_alarm_irq_register(struct platform_device *pdev, 260 struct lp8788_rtc *rtc) 261{ 262 struct resource *r; 263 struct lp8788 *lp = rtc->lp; 264 struct irq_domain *irqdm = lp->irqdm; 265 int irq; 266 267 rtc->irq = 0; 268 269 /* even the alarm IRQ number is not specified, rtc time should work */ 270 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ); 271 if (!r) 272 return 0; 273 274 if (rtc->alarm == LP8788_ALARM_1) 275 irq = r->start; 276 else 277 irq = r->end; 278 279 rtc->irq = irq_create_mapping(irqdm, irq); 280 281 return devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, 282 lp8788_alarm_irq_handler, 283 0, LP8788_ALM_IRQ, rtc); 284} 285 286static int lp8788_rtc_probe(struct platform_device *pdev) 287{ 288 struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); 289 struct lp8788_rtc *rtc; 290 struct device *dev = &pdev->dev; 291 292 rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL); 293 if (!rtc) 294 return -ENOMEM; 295 296 rtc->lp = lp; 297 rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL; 298 platform_set_drvdata(pdev, rtc); 299 300 device_init_wakeup(dev, 1); 301 302 rtc->rdev = devm_rtc_device_register(dev, "lp8788_rtc", 303 &lp8788_rtc_ops, THIS_MODULE); 304 if (IS_ERR(rtc->rdev)) { 305 dev_err(dev, "can not register rtc device\n"); 306 return PTR_ERR(rtc->rdev); 307 } 308 309 if (lp8788_alarm_irq_register(pdev, rtc)) 310 dev_warn(lp->dev, "no rtc irq handler\n"); 311 312 return 0; 313} 314 315static struct platform_driver lp8788_rtc_driver = { 316 .probe = lp8788_rtc_probe, 317 .driver = { 318 .name = LP8788_DEV_RTC, 319 }, 320}; 321module_platform_driver(lp8788_rtc_driver); 322 323MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver"); 324MODULE_AUTHOR("Milo Kim"); 325MODULE_LICENSE("GPL"); 326MODULE_ALIAS("platform:lp8788-rtc"); 327