1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22e774c7cSMark Zhan /*
32e774c7cSMark Zhan * ST M48T59 RTC driver
42e774c7cSMark Zhan *
52e774c7cSMark Zhan * Copyright (c) 2007 Wind River Systems, Inc.
62e774c7cSMark Zhan *
72e774c7cSMark Zhan * Author: Mark Zhan <rongkai.zhan@windriver.com>
82e774c7cSMark Zhan */
92e774c7cSMark Zhan
102e774c7cSMark Zhan #include <linux/kernel.h>
112e774c7cSMark Zhan #include <linux/module.h>
122e774c7cSMark Zhan #include <linux/init.h>
132e774c7cSMark Zhan #include <linux/io.h>
142e774c7cSMark Zhan #include <linux/device.h>
152e774c7cSMark Zhan #include <linux/platform_device.h>
162e774c7cSMark Zhan #include <linux/rtc.h>
172e774c7cSMark Zhan #include <linux/rtc/m48t59.h>
182e774c7cSMark Zhan #include <linux/bcd.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
202e774c7cSMark Zhan
212e774c7cSMark Zhan #ifndef NO_IRQ
222e774c7cSMark Zhan #define NO_IRQ (-1)
232e774c7cSMark Zhan #endif
242e774c7cSMark Zhan
2594fe7424SKrzysztof Helt #define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg))
2694fe7424SKrzysztof Helt #define M48T59_WRITE(val, reg) \
2794fe7424SKrzysztof Helt (pdata->write_byte(dev, pdata->offset + reg, val))
282e774c7cSMark Zhan
292e774c7cSMark Zhan #define M48T59_SET_BITS(mask, reg) \
302e774c7cSMark Zhan M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg))
312e774c7cSMark Zhan #define M48T59_CLEAR_BITS(mask, reg) \
322e774c7cSMark Zhan M48T59_WRITE((M48T59_READ(reg) & ~(mask)), (reg))
332e774c7cSMark Zhan
342e774c7cSMark Zhan struct m48t59_private {
352e774c7cSMark Zhan void __iomem *ioaddr;
360439208aSMark Zhan int irq;
372e774c7cSMark Zhan struct rtc_device *rtc;
382e774c7cSMark Zhan spinlock_t lock; /* serialize the NVRAM and RTC access */
392e774c7cSMark Zhan };
402e774c7cSMark Zhan
412e774c7cSMark Zhan /*
422e774c7cSMark Zhan * This is the generic access method when the chip is memory-mapped
432e774c7cSMark Zhan */
442e774c7cSMark Zhan static void
m48t59_mem_writeb(struct device * dev,u32 ofs,u8 val)452e774c7cSMark Zhan m48t59_mem_writeb(struct device *dev, u32 ofs, u8 val)
462e774c7cSMark Zhan {
4785368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
482e774c7cSMark Zhan
492e774c7cSMark Zhan writeb(val, m48t59->ioaddr+ofs);
502e774c7cSMark Zhan }
512e774c7cSMark Zhan
522e774c7cSMark Zhan static u8
m48t59_mem_readb(struct device * dev,u32 ofs)532e774c7cSMark Zhan m48t59_mem_readb(struct device *dev, u32 ofs)
542e774c7cSMark Zhan {
5585368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
562e774c7cSMark Zhan
572e774c7cSMark Zhan return readb(m48t59->ioaddr+ofs);
582e774c7cSMark Zhan }
592e774c7cSMark Zhan
602e774c7cSMark Zhan /*
612e774c7cSMark Zhan * NOTE: M48T59 only uses BCD mode
622e774c7cSMark Zhan */
m48t59_rtc_read_time(struct device * dev,struct rtc_time * tm)632e774c7cSMark Zhan static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
642e774c7cSMark Zhan {
6585368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
6685368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
672e774c7cSMark Zhan unsigned long flags;
682e774c7cSMark Zhan u8 val;
692e774c7cSMark Zhan
702e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
712e774c7cSMark Zhan /* Issue the READ command */
722e774c7cSMark Zhan M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
732e774c7cSMark Zhan
74fe20ba70SAdrian Bunk tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
752e774c7cSMark Zhan /* tm_mon is 0-11 */
76fe20ba70SAdrian Bunk tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
77fe20ba70SAdrian Bunk tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY));
782e774c7cSMark Zhan
792e774c7cSMark Zhan val = M48T59_READ(M48T59_WDAY);
80833be4e1SRobert Reif if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
81833be4e1SRobert Reif (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
822e774c7cSMark Zhan dev_dbg(dev, "Century bit is enabled\n");
832e774c7cSMark Zhan tm->tm_year += 100; /* one century */
842e774c7cSMark Zhan }
8512a9ee3cSKrzysztof Helt #ifdef CONFIG_SPARC
8612a9ee3cSKrzysztof Helt /* Sun SPARC machines count years since 1968 */
8712a9ee3cSKrzysztof Helt tm->tm_year += 68;
8812a9ee3cSKrzysztof Helt #endif
892e774c7cSMark Zhan
90fe20ba70SAdrian Bunk tm->tm_wday = bcd2bin(val & 0x07);
91fe20ba70SAdrian Bunk tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F);
92fe20ba70SAdrian Bunk tm->tm_min = bcd2bin(M48T59_READ(M48T59_MIN) & 0x7F);
93fe20ba70SAdrian Bunk tm->tm_sec = bcd2bin(M48T59_READ(M48T59_SEC) & 0x7F);
942e774c7cSMark Zhan
952e774c7cSMark Zhan /* Clear the READ bit */
962e774c7cSMark Zhan M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
972e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
982e774c7cSMark Zhan
99ad78343eSAndy Shevchenko dev_dbg(dev, "RTC read time %ptR\n", tm);
10022652ba7SAlexandre Belloni return 0;
1012e774c7cSMark Zhan }
1022e774c7cSMark Zhan
m48t59_rtc_set_time(struct device * dev,struct rtc_time * tm)1032e774c7cSMark Zhan static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
1042e774c7cSMark Zhan {
10585368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
10685368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
1072e774c7cSMark Zhan unsigned long flags;
1082e774c7cSMark Zhan u8 val = 0;
10912a9ee3cSKrzysztof Helt int year = tm->tm_year;
11012a9ee3cSKrzysztof Helt
11112a9ee3cSKrzysztof Helt #ifdef CONFIG_SPARC
11212a9ee3cSKrzysztof Helt /* Sun SPARC machines count years since 1968 */
11312a9ee3cSKrzysztof Helt year -= 68;
11412a9ee3cSKrzysztof Helt #endif
1152e774c7cSMark Zhan
1162e774c7cSMark Zhan dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n",
11712a9ee3cSKrzysztof Helt year + 1900, tm->tm_mon, tm->tm_mday,
1182e774c7cSMark Zhan tm->tm_hour, tm->tm_min, tm->tm_sec);
1192e774c7cSMark Zhan
12012a9ee3cSKrzysztof Helt if (year < 0)
12112a9ee3cSKrzysztof Helt return -EINVAL;
12212a9ee3cSKrzysztof Helt
1232e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
1242e774c7cSMark Zhan /* Issue the WRITE command */
1252e774c7cSMark Zhan M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
1262e774c7cSMark Zhan
127fe20ba70SAdrian Bunk M48T59_WRITE((bin2bcd(tm->tm_sec) & 0x7F), M48T59_SEC);
128fe20ba70SAdrian Bunk M48T59_WRITE((bin2bcd(tm->tm_min) & 0x7F), M48T59_MIN);
129fe20ba70SAdrian Bunk M48T59_WRITE((bin2bcd(tm->tm_hour) & 0x3F), M48T59_HOUR);
130fe20ba70SAdrian Bunk M48T59_WRITE((bin2bcd(tm->tm_mday) & 0x3F), M48T59_MDAY);
1312e774c7cSMark Zhan /* tm_mon is 0-11 */
132fe20ba70SAdrian Bunk M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
13312a9ee3cSKrzysztof Helt M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR);
1342e774c7cSMark Zhan
13512a9ee3cSKrzysztof Helt if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100))
1362e774c7cSMark Zhan val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
137fe20ba70SAdrian Bunk val |= (bin2bcd(tm->tm_wday) & 0x07);
1382e774c7cSMark Zhan M48T59_WRITE(val, M48T59_WDAY);
1392e774c7cSMark Zhan
1402e774c7cSMark Zhan /* Clear the WRITE bit */
1412e774c7cSMark Zhan M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
1422e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
1432e774c7cSMark Zhan return 0;
1442e774c7cSMark Zhan }
1452e774c7cSMark Zhan
1462e774c7cSMark Zhan /*
1472e774c7cSMark Zhan * Read alarm time and date in RTC
1482e774c7cSMark Zhan */
m48t59_rtc_readalarm(struct device * dev,struct rtc_wkalrm * alrm)1492e774c7cSMark Zhan static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
1502e774c7cSMark Zhan {
15185368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
15285368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
1532e774c7cSMark Zhan struct rtc_time *tm = &alrm->time;
1542e774c7cSMark Zhan unsigned long flags;
1552e774c7cSMark Zhan u8 val;
1562e774c7cSMark Zhan
1572e774c7cSMark Zhan /* If no irq, we don't support ALARM */
1582e774c7cSMark Zhan if (m48t59->irq == NO_IRQ)
1592e774c7cSMark Zhan return -EIO;
1602e774c7cSMark Zhan
1612e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
1622e774c7cSMark Zhan /* Issue the READ command */
1632e774c7cSMark Zhan M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
1642e774c7cSMark Zhan
165fe20ba70SAdrian Bunk tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
16612a9ee3cSKrzysztof Helt #ifdef CONFIG_SPARC
16712a9ee3cSKrzysztof Helt /* Sun SPARC machines count years since 1968 */
16812a9ee3cSKrzysztof Helt tm->tm_year += 68;
16912a9ee3cSKrzysztof Helt #endif
1702e774c7cSMark Zhan /* tm_mon is 0-11 */
171fe20ba70SAdrian Bunk tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
1722e774c7cSMark Zhan
1732e774c7cSMark Zhan val = M48T59_READ(M48T59_WDAY);
1742e774c7cSMark Zhan if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
1752e774c7cSMark Zhan tm->tm_year += 100; /* one century */
1762e774c7cSMark Zhan
177fe20ba70SAdrian Bunk tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE));
178fe20ba70SAdrian Bunk tm->tm_hour = bcd2bin(M48T59_READ(M48T59_ALARM_HOUR));
179fe20ba70SAdrian Bunk tm->tm_min = bcd2bin(M48T59_READ(M48T59_ALARM_MIN));
180fe20ba70SAdrian Bunk tm->tm_sec = bcd2bin(M48T59_READ(M48T59_ALARM_SEC));
1812e774c7cSMark Zhan
1822e774c7cSMark Zhan /* Clear the READ bit */
1832e774c7cSMark Zhan M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
1842e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
1852e774c7cSMark Zhan
186ad78343eSAndy Shevchenko dev_dbg(dev, "RTC read alarm time %ptR\n", tm);
187caf1e106SWan ZongShun return rtc_valid_tm(tm);
1882e774c7cSMark Zhan }
1892e774c7cSMark Zhan
1902e774c7cSMark Zhan /*
1912e774c7cSMark Zhan * Set alarm time and date in RTC
1922e774c7cSMark Zhan */
m48t59_rtc_setalarm(struct device * dev,struct rtc_wkalrm * alrm)1932e774c7cSMark Zhan static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
1942e774c7cSMark Zhan {
19585368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
19685368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
1972e774c7cSMark Zhan struct rtc_time *tm = &alrm->time;
1982e774c7cSMark Zhan u8 mday, hour, min, sec;
1992e774c7cSMark Zhan unsigned long flags;
20012a9ee3cSKrzysztof Helt int year = tm->tm_year;
20112a9ee3cSKrzysztof Helt
20212a9ee3cSKrzysztof Helt #ifdef CONFIG_SPARC
20312a9ee3cSKrzysztof Helt /* Sun SPARC machines count years since 1968 */
20412a9ee3cSKrzysztof Helt year -= 68;
20512a9ee3cSKrzysztof Helt #endif
2062e774c7cSMark Zhan
2072e774c7cSMark Zhan /* If no irq, we don't support ALARM */
2082e774c7cSMark Zhan if (m48t59->irq == NO_IRQ)
2092e774c7cSMark Zhan return -EIO;
2102e774c7cSMark Zhan
21112a9ee3cSKrzysztof Helt if (year < 0)
21212a9ee3cSKrzysztof Helt return -EINVAL;
21312a9ee3cSKrzysztof Helt
2142e774c7cSMark Zhan /*
2152e774c7cSMark Zhan * 0xff means "always match"
2162e774c7cSMark Zhan */
2172e774c7cSMark Zhan mday = tm->tm_mday;
218fe20ba70SAdrian Bunk mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
2192e774c7cSMark Zhan if (mday == 0xff)
2202e774c7cSMark Zhan mday = M48T59_READ(M48T59_MDAY);
2212e774c7cSMark Zhan
2222e774c7cSMark Zhan hour = tm->tm_hour;
223fe20ba70SAdrian Bunk hour = (hour < 24) ? bin2bcd(hour) : 0x00;
2242e774c7cSMark Zhan
2252e774c7cSMark Zhan min = tm->tm_min;
226fe20ba70SAdrian Bunk min = (min < 60) ? bin2bcd(min) : 0x00;
2272e774c7cSMark Zhan
2282e774c7cSMark Zhan sec = tm->tm_sec;
229fe20ba70SAdrian Bunk sec = (sec < 60) ? bin2bcd(sec) : 0x00;
2302e774c7cSMark Zhan
2312e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
2322e774c7cSMark Zhan /* Issue the WRITE command */
2332e774c7cSMark Zhan M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
2342e774c7cSMark Zhan
2352e774c7cSMark Zhan M48T59_WRITE(mday, M48T59_ALARM_DATE);
2362e774c7cSMark Zhan M48T59_WRITE(hour, M48T59_ALARM_HOUR);
2372e774c7cSMark Zhan M48T59_WRITE(min, M48T59_ALARM_MIN);
2382e774c7cSMark Zhan M48T59_WRITE(sec, M48T59_ALARM_SEC);
2392e774c7cSMark Zhan
2402e774c7cSMark Zhan /* Clear the WRITE bit */
2412e774c7cSMark Zhan M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
2422e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
2432e774c7cSMark Zhan
2442e774c7cSMark Zhan dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
24512a9ee3cSKrzysztof Helt year + 1900, tm->tm_mon, tm->tm_mday,
2462e774c7cSMark Zhan tm->tm_hour, tm->tm_min, tm->tm_sec);
2472e774c7cSMark Zhan return 0;
2482e774c7cSMark Zhan }
2492e774c7cSMark Zhan
2502e774c7cSMark Zhan /*
2512e774c7cSMark Zhan * Handle commands from user-space
2522e774c7cSMark Zhan */
m48t59_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)25316380c15SJohn Stultz static int m48t59_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
2542e774c7cSMark Zhan {
25585368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
25685368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
2572e774c7cSMark Zhan unsigned long flags;
2582e774c7cSMark Zhan
2592e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
26016380c15SJohn Stultz if (enabled)
2612e774c7cSMark Zhan M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR);
26216380c15SJohn Stultz else
26316380c15SJohn Stultz M48T59_WRITE(0x00, M48T59_INTR);
2642e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
2652e774c7cSMark Zhan
26616380c15SJohn Stultz return 0;
2672e774c7cSMark Zhan }
2682e774c7cSMark Zhan
m48t59_rtc_proc(struct device * dev,struct seq_file * seq)2692e774c7cSMark Zhan static int m48t59_rtc_proc(struct device *dev, struct seq_file *seq)
2702e774c7cSMark Zhan {
27185368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
27285368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
2732e774c7cSMark Zhan unsigned long flags;
2742e774c7cSMark Zhan u8 val;
2752e774c7cSMark Zhan
2762e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
2772e774c7cSMark Zhan val = M48T59_READ(M48T59_FLAGS);
2782e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
2792e774c7cSMark Zhan
2802e774c7cSMark Zhan seq_printf(seq, "battery\t\t: %s\n",
2812e774c7cSMark Zhan (val & M48T59_FLAGS_BF) ? "low" : "normal");
2822e774c7cSMark Zhan return 0;
2832e774c7cSMark Zhan }
2842e774c7cSMark Zhan
2852e774c7cSMark Zhan /*
2862e774c7cSMark Zhan * IRQ handler for the RTC
2872e774c7cSMark Zhan */
m48t59_rtc_interrupt(int irq,void * dev_id)2882e774c7cSMark Zhan static irqreturn_t m48t59_rtc_interrupt(int irq, void *dev_id)
2892e774c7cSMark Zhan {
2902e774c7cSMark Zhan struct device *dev = (struct device *)dev_id;
29185368bb9SWolfram Sang struct m48t59_plat_data *pdata = dev_get_platdata(dev);
29285368bb9SWolfram Sang struct m48t59_private *m48t59 = dev_get_drvdata(dev);
2932e774c7cSMark Zhan u8 event;
2942e774c7cSMark Zhan
2952e774c7cSMark Zhan spin_lock(&m48t59->lock);
2962e774c7cSMark Zhan event = M48T59_READ(M48T59_FLAGS);
2972e774c7cSMark Zhan spin_unlock(&m48t59->lock);
2982e774c7cSMark Zhan
2992e774c7cSMark Zhan if (event & M48T59_FLAGS_AF) {
3002e774c7cSMark Zhan rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF));
3012e774c7cSMark Zhan return IRQ_HANDLED;
3022e774c7cSMark Zhan }
3032e774c7cSMark Zhan
3042e774c7cSMark Zhan return IRQ_NONE;
3052e774c7cSMark Zhan }
3062e774c7cSMark Zhan
3072e774c7cSMark Zhan static const struct rtc_class_ops m48t59_rtc_ops = {
3082e774c7cSMark Zhan .read_time = m48t59_rtc_read_time,
3092e774c7cSMark Zhan .set_time = m48t59_rtc_set_time,
3102e774c7cSMark Zhan .read_alarm = m48t59_rtc_readalarm,
3112e774c7cSMark Zhan .set_alarm = m48t59_rtc_setalarm,
3122e774c7cSMark Zhan .proc = m48t59_rtc_proc,
31316380c15SJohn Stultz .alarm_irq_enable = m48t59_rtc_alarm_irq_enable,
3142e774c7cSMark Zhan };
3152e774c7cSMark Zhan
m48t59_nvram_read(void * priv,unsigned int offset,void * val,size_t size)3160ff3565dSAlexandre Belloni static int m48t59_nvram_read(void *priv, unsigned int offset, void *val,
3170ff3565dSAlexandre Belloni size_t size)
3182e774c7cSMark Zhan {
3190ff3565dSAlexandre Belloni struct platform_device *pdev = priv;
3200ff3565dSAlexandre Belloni struct device *dev = &pdev->dev;
3218136032bSJingoo Han struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
3222e774c7cSMark Zhan struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
3232e774c7cSMark Zhan ssize_t cnt = 0;
3242e774c7cSMark Zhan unsigned long flags;
3250ff3565dSAlexandre Belloni u8 *buf = val;
3262e774c7cSMark Zhan
3272e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
32899be3e37SVladimir Zapolskiy
32999be3e37SVladimir Zapolskiy for (; cnt < size; cnt++)
3302e774c7cSMark Zhan *buf++ = M48T59_READ(cnt);
33199be3e37SVladimir Zapolskiy
3322e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
3332e774c7cSMark Zhan
3340ff3565dSAlexandre Belloni return 0;
3352e774c7cSMark Zhan }
3362e774c7cSMark Zhan
m48t59_nvram_write(void * priv,unsigned int offset,void * val,size_t size)3370ff3565dSAlexandre Belloni static int m48t59_nvram_write(void *priv, unsigned int offset, void *val,
3380ff3565dSAlexandre Belloni size_t size)
3392e774c7cSMark Zhan {
3400ff3565dSAlexandre Belloni struct platform_device *pdev = priv;
3410ff3565dSAlexandre Belloni struct device *dev = &pdev->dev;
3428136032bSJingoo Han struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
3432e774c7cSMark Zhan struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
3442e774c7cSMark Zhan ssize_t cnt = 0;
3452e774c7cSMark Zhan unsigned long flags;
3460ff3565dSAlexandre Belloni u8 *buf = val;
3472e774c7cSMark Zhan
3482e774c7cSMark Zhan spin_lock_irqsave(&m48t59->lock, flags);
34999be3e37SVladimir Zapolskiy
35099be3e37SVladimir Zapolskiy for (; cnt < size; cnt++)
3512e774c7cSMark Zhan M48T59_WRITE(*buf++, cnt);
35299be3e37SVladimir Zapolskiy
3532e774c7cSMark Zhan spin_unlock_irqrestore(&m48t59->lock, flags);
3542e774c7cSMark Zhan
3550ff3565dSAlexandre Belloni return 0;
3562e774c7cSMark Zhan }
3572e774c7cSMark Zhan
m48t59_rtc_probe(struct platform_device * pdev)3585a167f45SGreg Kroah-Hartman static int m48t59_rtc_probe(struct platform_device *pdev)
3592e774c7cSMark Zhan {
3608136032bSJingoo Han struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
3612e774c7cSMark Zhan struct m48t59_private *m48t59 = NULL;
3622e774c7cSMark Zhan struct resource *res;
3632e774c7cSMark Zhan int ret = -ENOMEM;
3640ff3565dSAlexandre Belloni struct nvmem_config nvmem_cfg = {
3650ff3565dSAlexandre Belloni .name = "m48t59-",
3660ff3565dSAlexandre Belloni .word_size = 1,
3670ff3565dSAlexandre Belloni .stride = 1,
3680ff3565dSAlexandre Belloni .reg_read = m48t59_nvram_read,
3690ff3565dSAlexandre Belloni .reg_write = m48t59_nvram_write,
3700ff3565dSAlexandre Belloni .priv = pdev,
3710ff3565dSAlexandre Belloni };
3722e774c7cSMark Zhan
3732e774c7cSMark Zhan /* This chip could be memory-mapped or I/O-mapped */
3742e774c7cSMark Zhan res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3752e774c7cSMark Zhan if (!res) {
3762e774c7cSMark Zhan res = platform_get_resource(pdev, IORESOURCE_IO, 0);
3772e774c7cSMark Zhan if (!res)
3782e774c7cSMark Zhan return -EINVAL;
3792e774c7cSMark Zhan }
3802e774c7cSMark Zhan
3812e774c7cSMark Zhan if (res->flags & IORESOURCE_IO) {
3822e774c7cSMark Zhan /* If we are I/O-mapped, the platform should provide
3832e774c7cSMark Zhan * the operations accessing chip registers.
3842e774c7cSMark Zhan */
3852e774c7cSMark Zhan if (!pdata || !pdata->write_byte || !pdata->read_byte)
3862e774c7cSMark Zhan return -EINVAL;
3872e774c7cSMark Zhan } else if (res->flags & IORESOURCE_MEM) {
3882e774c7cSMark Zhan /* we are memory-mapped */
3892e774c7cSMark Zhan if (!pdata) {
39019b8d887SJingoo Han pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata),
39119b8d887SJingoo Han GFP_KERNEL);
3922e774c7cSMark Zhan if (!pdata)
3932e774c7cSMark Zhan return -ENOMEM;
3942e774c7cSMark Zhan /* Ensure we only kmalloc platform data once */
3952e774c7cSMark Zhan pdev->dev.platform_data = pdata;
3962e774c7cSMark Zhan }
39794fe7424SKrzysztof Helt if (!pdata->type)
39894fe7424SKrzysztof Helt pdata->type = M48T59RTC_TYPE_M48T59;
3992e774c7cSMark Zhan
4002e774c7cSMark Zhan /* Try to use the generic memory read/write ops */
4012e774c7cSMark Zhan if (!pdata->write_byte)
4022e774c7cSMark Zhan pdata->write_byte = m48t59_mem_writeb;
4032e774c7cSMark Zhan if (!pdata->read_byte)
4042e774c7cSMark Zhan pdata->read_byte = m48t59_mem_readb;
4052e774c7cSMark Zhan }
4062e774c7cSMark Zhan
40719b8d887SJingoo Han m48t59 = devm_kzalloc(&pdev->dev, sizeof(*m48t59), GFP_KERNEL);
4082e774c7cSMark Zhan if (!m48t59)
4092e774c7cSMark Zhan return -ENOMEM;
4102e774c7cSMark Zhan
41164151ad5SKrzysztof Helt m48t59->ioaddr = pdata->ioaddr;
41264151ad5SKrzysztof Helt
41364151ad5SKrzysztof Helt if (!m48t59->ioaddr) {
41464151ad5SKrzysztof Helt /* ioaddr not mapped externally */
41519b8d887SJingoo Han m48t59->ioaddr = devm_ioremap(&pdev->dev, res->start,
41619b8d887SJingoo Han resource_size(res));
4172e774c7cSMark Zhan if (!m48t59->ioaddr)
41819b8d887SJingoo Han return ret;
41964151ad5SKrzysztof Helt }
4202e774c7cSMark Zhan
4212e774c7cSMark Zhan /* Try to get irq number. We also can work in
4222e774c7cSMark Zhan * the mode without IRQ.
4232e774c7cSMark Zhan */
424*55cc33faSAlexandre Belloni m48t59->irq = platform_get_irq_optional(pdev, 0);
4252fac6674SAnton Vorontsov if (m48t59->irq <= 0)
4262e774c7cSMark Zhan m48t59->irq = NO_IRQ;
4272e774c7cSMark Zhan
4282e774c7cSMark Zhan if (m48t59->irq != NO_IRQ) {
42919b8d887SJingoo Han ret = devm_request_irq(&pdev->dev, m48t59->irq,
43019b8d887SJingoo Han m48t59_rtc_interrupt, IRQF_SHARED,
43119b8d887SJingoo Han "rtc-m48t59", &pdev->dev);
4322e774c7cSMark Zhan if (ret)
43319b8d887SJingoo Han return ret;
4342e774c7cSMark Zhan }
435e53ad084SAlexandre Belloni
436e53ad084SAlexandre Belloni m48t59->rtc = devm_rtc_allocate_device(&pdev->dev);
437e53ad084SAlexandre Belloni if (IS_ERR(m48t59->rtc))
438e53ad084SAlexandre Belloni return PTR_ERR(m48t59->rtc);
439e53ad084SAlexandre Belloni
44094fe7424SKrzysztof Helt switch (pdata->type) {
44194fe7424SKrzysztof Helt case M48T59RTC_TYPE_M48T59:
44294fe7424SKrzysztof Helt pdata->offset = 0x1ff0;
44394fe7424SKrzysztof Helt break;
44494fe7424SKrzysztof Helt case M48T59RTC_TYPE_M48T02:
445e53ad084SAlexandre Belloni clear_bit(RTC_FEATURE_ALARM, m48t59->rtc->features);
44694fe7424SKrzysztof Helt pdata->offset = 0x7f0;
44794fe7424SKrzysztof Helt break;
44894fe7424SKrzysztof Helt case M48T59RTC_TYPE_M48T08:
449e53ad084SAlexandre Belloni clear_bit(RTC_FEATURE_ALARM, m48t59->rtc->features);
45094fe7424SKrzysztof Helt pdata->offset = 0x1ff0;
45194fe7424SKrzysztof Helt break;
45294fe7424SKrzysztof Helt default:
45394fe7424SKrzysztof Helt dev_err(&pdev->dev, "Unknown RTC type\n");
45419b8d887SJingoo Han return -ENODEV;
45594fe7424SKrzysztof Helt }
4562e774c7cSMark Zhan
457b74d2caaSAlessandro Zummo spin_lock_init(&m48t59->lock);
458b74d2caaSAlessandro Zummo platform_set_drvdata(pdev, m48t59);
459b74d2caaSAlessandro Zummo
460e53ad084SAlexandre Belloni m48t59->rtc->ops = &m48t59_rtc_ops;
461affb842bSAlexandre Belloni
4620ff3565dSAlexandre Belloni nvmem_cfg.size = pdata->offset;
4633a905c2dSBartosz Golaszewski ret = devm_rtc_nvmem_register(m48t59->rtc, &nvmem_cfg);
4640ff3565dSAlexandre Belloni if (ret)
4650ff3565dSAlexandre Belloni return ret;
4660ff3565dSAlexandre Belloni
467fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(m48t59->rtc);
468affb842bSAlexandre Belloni if (ret)
469affb842bSAlexandre Belloni return ret;
470affb842bSAlexandre Belloni
4712e774c7cSMark Zhan return 0;
4722e774c7cSMark Zhan }
4732e774c7cSMark Zhan
474ad28a07bSKay Sievers /* work with hotplug and coldplug */
475ad28a07bSKay Sievers MODULE_ALIAS("platform:rtc-m48t59");
476ad28a07bSKay Sievers
47712730926SRandy Dunlap static struct platform_driver m48t59_rtc_driver = {
4782e774c7cSMark Zhan .driver = {
4792e774c7cSMark Zhan .name = "rtc-m48t59",
4802e774c7cSMark Zhan },
4812e774c7cSMark Zhan .probe = m48t59_rtc_probe,
4822e774c7cSMark Zhan };
4832e774c7cSMark Zhan
4840c4eae66SAxel Lin module_platform_driver(m48t59_rtc_driver);
4852e774c7cSMark Zhan
4862e774c7cSMark Zhan MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>");
48794fe7424SKrzysztof Helt MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver");
4882e774c7cSMark Zhan MODULE_LICENSE("GPL");
489