1 /* 2 * ST M48T86 / Dallas DS12887 RTC driver 3 * Copyright (c) 2006 Tower Technologies 4 * 5 * Author: Alessandro Zummo <a.zummo@towertech.it> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This drivers only supports the clock running in BCD and 24H mode. 12 * If it will be ever adapted to binary and 12H mode, care must be taken 13 * to not introduce bugs. 14 */ 15 16 #include <linux/module.h> 17 #include <linux/rtc.h> 18 #include <linux/platform_device.h> 19 #include <linux/platform_data/rtc-m48t86.h> 20 #include <linux/bcd.h> 21 22 #define M48T86_REG_SEC 0x00 23 #define M48T86_REG_SECALRM 0x01 24 #define M48T86_REG_MIN 0x02 25 #define M48T86_REG_MINALRM 0x03 26 #define M48T86_REG_HOUR 0x04 27 #define M48T86_REG_HOURALRM 0x05 28 #define M48T86_REG_DOW 0x06 /* 1 = sunday */ 29 #define M48T86_REG_DOM 0x07 30 #define M48T86_REG_MONTH 0x08 /* 1 - 12 */ 31 #define M48T86_REG_YEAR 0x09 /* 0 - 99 */ 32 #define M48T86_REG_A 0x0A 33 #define M48T86_REG_B 0x0B 34 #define M48T86_REG_C 0x0C 35 #define M48T86_REG_D 0x0D 36 37 #define M48T86_REG_B_H24 (1 << 1) 38 #define M48T86_REG_B_DM (1 << 2) 39 #define M48T86_REG_B_SET (1 << 7) 40 #define M48T86_REG_D_VRT (1 << 7) 41 42 static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) 43 { 44 unsigned char reg; 45 struct platform_device *pdev = to_platform_device(dev); 46 struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); 47 48 reg = ops->readbyte(M48T86_REG_B); 49 50 if (reg & M48T86_REG_B_DM) { 51 /* data (binary) mode */ 52 tm->tm_sec = ops->readbyte(M48T86_REG_SEC); 53 tm->tm_min = ops->readbyte(M48T86_REG_MIN); 54 tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F; 55 tm->tm_mday = ops->readbyte(M48T86_REG_DOM); 56 /* tm_mon is 0-11 */ 57 tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1; 58 tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100; 59 tm->tm_wday = ops->readbyte(M48T86_REG_DOW); 60 } else { 61 /* bcd mode */ 62 tm->tm_sec = bcd2bin(ops->readbyte(M48T86_REG_SEC)); 63 tm->tm_min = bcd2bin(ops->readbyte(M48T86_REG_MIN)); 64 tm->tm_hour = bcd2bin(ops->readbyte(M48T86_REG_HOUR) & 0x3F); 65 tm->tm_mday = bcd2bin(ops->readbyte(M48T86_REG_DOM)); 66 /* tm_mon is 0-11 */ 67 tm->tm_mon = bcd2bin(ops->readbyte(M48T86_REG_MONTH)) - 1; 68 tm->tm_year = bcd2bin(ops->readbyte(M48T86_REG_YEAR)) + 100; 69 tm->tm_wday = bcd2bin(ops->readbyte(M48T86_REG_DOW)); 70 } 71 72 /* correct the hour if the clock is in 12h mode */ 73 if (!(reg & M48T86_REG_B_H24)) 74 if (ops->readbyte(M48T86_REG_HOUR) & 0x80) 75 tm->tm_hour += 12; 76 77 return rtc_valid_tm(tm); 78 } 79 80 static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) 81 { 82 unsigned char reg; 83 struct platform_device *pdev = to_platform_device(dev); 84 struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); 85 86 reg = ops->readbyte(M48T86_REG_B); 87 88 /* update flag and 24h mode */ 89 reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; 90 ops->writebyte(reg, M48T86_REG_B); 91 92 if (reg & M48T86_REG_B_DM) { 93 /* data (binary) mode */ 94 ops->writebyte(tm->tm_sec, M48T86_REG_SEC); 95 ops->writebyte(tm->tm_min, M48T86_REG_MIN); 96 ops->writebyte(tm->tm_hour, M48T86_REG_HOUR); 97 ops->writebyte(tm->tm_mday, M48T86_REG_DOM); 98 ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH); 99 ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR); 100 ops->writebyte(tm->tm_wday, M48T86_REG_DOW); 101 } else { 102 /* bcd mode */ 103 ops->writebyte(bin2bcd(tm->tm_sec), M48T86_REG_SEC); 104 ops->writebyte(bin2bcd(tm->tm_min), M48T86_REG_MIN); 105 ops->writebyte(bin2bcd(tm->tm_hour), M48T86_REG_HOUR); 106 ops->writebyte(bin2bcd(tm->tm_mday), M48T86_REG_DOM); 107 ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_REG_MONTH); 108 ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_REG_YEAR); 109 ops->writebyte(bin2bcd(tm->tm_wday), M48T86_REG_DOW); 110 } 111 112 /* update ended */ 113 reg &= ~M48T86_REG_B_SET; 114 ops->writebyte(reg, M48T86_REG_B); 115 116 return 0; 117 } 118 119 static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) 120 { 121 unsigned char reg; 122 struct platform_device *pdev = to_platform_device(dev); 123 struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); 124 125 reg = ops->readbyte(M48T86_REG_B); 126 127 seq_printf(seq, "mode\t\t: %s\n", 128 (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); 129 130 reg = ops->readbyte(M48T86_REG_D); 131 132 seq_printf(seq, "battery\t\t: %s\n", 133 (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); 134 135 return 0; 136 } 137 138 static const struct rtc_class_ops m48t86_rtc_ops = { 139 .read_time = m48t86_rtc_read_time, 140 .set_time = m48t86_rtc_set_time, 141 .proc = m48t86_rtc_proc, 142 }; 143 144 static int m48t86_rtc_probe(struct platform_device *dev) 145 { 146 unsigned char reg; 147 struct m48t86_ops *ops = dev_get_platdata(&dev->dev); 148 struct rtc_device *rtc; 149 150 rtc = devm_rtc_device_register(&dev->dev, "m48t86", 151 &m48t86_rtc_ops, THIS_MODULE); 152 153 if (IS_ERR(rtc)) 154 return PTR_ERR(rtc); 155 156 platform_set_drvdata(dev, rtc); 157 158 /* read battery status */ 159 reg = ops->readbyte(M48T86_REG_D); 160 dev_info(&dev->dev, "battery %s\n", 161 (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); 162 163 return 0; 164 } 165 166 static struct platform_driver m48t86_rtc_platform_driver = { 167 .driver = { 168 .name = "rtc-m48t86", 169 }, 170 .probe = m48t86_rtc_probe, 171 }; 172 173 module_platform_driver(m48t86_rtc_platform_driver); 174 175 MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); 176 MODULE_DESCRIPTION("M48T86 RTC driver"); 177 MODULE_LICENSE("GPL"); 178 MODULE_ALIAS("platform:rtc-m48t86"); 179