--- /dev/null
+/*drivers/rtc/rtc-s35392a.h - driver for s35392a\r
+ *\r
+ * Copyright (C) 2010 ROCKCHIP, Inc.\r
+ *\r
+ * This software is licensed under the terms of the GNU General Public\r
+ * License version 2, as published by the Free Software Foundation, and\r
+ * may be copied, distributed, and modified under those terms.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ */\r
+\r
+#include <linux/module.h>\r
+#include <linux/rtc.h>\r
+#include <linux/i2c.h>\r
+#include <linux/bitrev.h>\r
+#include <linux/bcd.h>\r
+#include <linux/slab.h>\r
+#include <linux/delay.h>\r
+#include "rtc-s35392a.h"\r
+\r
+\r
+#define RTC_RATE 100 * 1000\r
+#define S35392_TEST 1\r
+\r
+struct s35392a {\r
+ struct i2c_client *client;\r
+ struct rtc_device *rtc;\r
+ int twentyfourhour;\r
+};\r
+\r
+static int s35392a_set_reg(struct s35392a *s35392a, const char reg, char *buf, int len)\r
+{\r
+ struct i2c_client *client = s35392a->client;\r
+ struct i2c_msg msg;\r
+ int ret;\r
+ \r
+ char *buff = buf;\r
+ msg.addr = client->addr | reg;\r
+ msg.flags = client->flags;\r
+ msg.len = len;\r
+ msg.buf = buff;\r
+ msg.scl_rate = RTC_RATE;\r
+ \r
+ ret = i2c_transfer(client->adapter,&msg,1);\r
+ return ret; \r
+ \r
+}\r
+\r
+static int s35392a_get_reg(struct s35392a *s35392a, const char reg, char *buf, int len)\r
+{\r
+ struct i2c_client *client = s35392a->client;\r
+ struct i2c_msg msg;\r
+ int ret;\r
+\r
+ msg.addr = client->addr | reg;\r
+ msg.flags = client->flags | I2C_M_RD;\r
+ msg.len = len;\r
+ msg.buf = buf;\r
+ msg.scl_rate = RTC_RATE;\r
+\r
+ ret = i2c_transfer(client->adapter,&msg,1);\r
+ return ret;\r
+ \r
+}\r
+\r
+#if S35392_TEST\r
+static int s35392_set_reg(struct s35392a *s35392a, const char reg, char *buf, int len,unsigned char head)\r
+{\r
+ struct i2c_client *client = s35392a->client;\r
+ struct i2c_msg msg;\r
+ int ret;\r
+ \r
+ char *buff = buf;\r
+ msg.addr = client->addr | reg | (head << 3);\r
+ msg.flags = client->flags;\r
+ msg.len = len;\r
+ msg.buf = buff;\r
+ msg.scl_rate = RTC_RATE; \r
+ ret = i2c_transfer(client->adapter,&msg,1);\r
+ return ret; \r
+ \r
+}\r
+\r
+static int s35392_get_reg(struct s35392a *s35392a, const char reg, char *buf, int len,unsigned char head)\r
+{\r
+ struct i2c_client *client = s35392a->client;\r
+ struct i2c_msg msg;\r
+ int ret;\r
+\r
+ msg.addr = client->addr | reg |(head << 3);\r
+ msg.flags = client->flags | I2C_M_RD;\r
+ msg.len = len;\r
+ msg.buf = buf;\r
+ msg.scl_rate = RTC_RATE;\r
+\r
+ ret = i2c_transfer(client->adapter,&msg,1);\r
+ return ret;\r
+ \r
+}\r
+static int s35392a_test(struct s35392a *s35392a)\r
+{\r
+ char buf[1];\r
+ char i;\r
+ \r
+ i = 50;\r
+ while(i--)\r
+ {\r
+ if (s35392_get_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf),6) < 0)\r
+ return -EIO; \r
+ if (!(buf[0] & (S35392A_FLAG_POC | S35392A_FLAG_BLD)))\r
+ return 0;\r
+ \r
+ buf[0] |= (S35392A_FLAG_RESET | S35392A_FLAG_24H);\r
+ buf[0] &= 0xf0;\r
+ s35392_set_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf),6);\r
+ \r
+ buf[0] = 0;\r
+ s35392_get_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf),6);\r
+ mdelay(10); \r
+ }\r
+ return 0;\r
+}\r
+#endif\r
+static int s35392a_init(struct s35392a *s35392a)\r
+{\r
+ char buf[1];\r
+\r
+ s35392a_get_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf)); \r
+ s35392a_get_reg(s35392a, S35392A_CMD_STATUS2, buf, sizeof(buf)); \r
+ s35392a_get_reg(s35392a, 4, buf, sizeof(buf));\r
+ s35392a_get_reg(s35392a, 5, buf, sizeof(buf));\r
+ s35392a_get_reg(s35392a, 6, buf, sizeof(buf));\r
+ s35392a_get_reg(s35392a, 7, buf, sizeof(buf));\r
+ \r
+ buf[0] |= (S35392A_FLAG_RESET | S35392A_FLAG_24H);\r
+ buf[0] &= 0xf0;\r
+ return s35392a_set_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf));\r
+\r
+}\r
+\r
+static int s35392a_reset(struct s35392a *s35392a)\r
+{\r
+ char buf[1];\r
+\r
+ if (s35392a_get_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf)) < 0)\r
+ return -EIO; \r
+ if (!(buf[0] & (S35392A_FLAG_POC | S35392A_FLAG_BLD)))\r
+ return 0;\r
+\r
+ buf[0] |= (S35392A_FLAG_RESET | S35392A_FLAG_24H);\r
+ buf[0] &= 0xf0;\r
+ return s35392a_set_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf));\r
+}\r
+\r
+\r
+static int s35392a_disable_test_mode(struct s35392a *s35392a)\r
+{\r
+ char buf[1];\r
+\r
+ if (s35392a_get_reg(s35392a, S35392A_CMD_STATUS2, buf, sizeof(buf)) < 0)\r
+ return -EIO;\r
+\r
+ if (!(buf[0] & S35392A_FLAG_TEST))\r
+ return 0;\r
+\r
+ buf[0] &= ~S35392A_FLAG_TEST;\r
+ return s35392a_set_reg(s35392a, S35392A_CMD_STATUS2, buf, sizeof(buf));\r
+}\r
+\r
+static char s35392a_hr2reg(struct s35392a *s35392a, int hour)\r
+{\r
+ if (s35392a->twentyfourhour)\r
+ return bin2bcd(hour);\r
+\r
+ if (hour < 12)\r
+ return bin2bcd(hour);\r
+\r
+ return 0x40 | bin2bcd(hour - 12);\r
+}\r
+\r
+static int s35392a_reg2hr(struct s35392a *s35392a, char reg)\r
+{\r
+ unsigned hour;\r
+\r
+ if (s35392a->twentyfourhour)\r
+ return bcd2bin(reg & 0x3f);\r
+\r
+ hour = bcd2bin(reg & 0x3f);\r
+ if (reg & 0x40)\r
+ hour += 12;\r
+\r
+ return hour;\r
+}\r
+\r
+static int s35392a_set_datetime(struct i2c_client *client, struct rtc_time *tm)\r
+{\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ int i, err;\r
+ char buf[7];\r
+\r
+ printk("%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "\r
+ "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,\r
+ tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,\r
+ tm->tm_wday);\r
+\r
+ buf[S35392A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100);\r
+ buf[S35392A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1);\r
+ buf[S35392A_BYTE_DAY] = bin2bcd(tm->tm_mday);\r
+ buf[S35392A_BYTE_WDAY] = bin2bcd(tm->tm_wday);\r
+ buf[S35392A_BYTE_HOURS] = s35392a_hr2reg(s35392a, tm->tm_hour);\r
+ buf[S35392A_BYTE_MINS] = bin2bcd(tm->tm_min);\r
+ buf[S35392A_BYTE_SECS] = bin2bcd(tm->tm_sec);\r
+\r
+ /* This chip expects the bits of each byte to be in reverse order */\r
+ for (i = 0; i < 7; ++i)\r
+ buf[i] = bitrev8(buf[i]);\r
+\r
+ err = s35392a_set_reg(s35392a, S35392A_CMD_TIME1, buf, sizeof(buf));\r
+\r
+ return err;\r
+}\r
+\r
+static int s35392a_get_datetime(struct i2c_client *client, struct rtc_time *tm)\r
+{\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ char buf[7];\r
+ int i, err;\r
+\r
+ err = s35392a_get_reg(s35392a, S35392A_CMD_TIME1, buf, sizeof(buf));\r
+ if (err < 0)\r
+ return err;\r
+\r
+ /* This chip returns the bits of each byte in reverse order */\r
+ for (i = 0; i < 7; ++i)\r
+ buf[i] = bitrev8(buf[i]);\r
+\r
+ tm->tm_sec = bcd2bin(buf[S35392A_BYTE_SECS]);\r
+ tm->tm_min = bcd2bin(buf[S35392A_BYTE_MINS]);\r
+ tm->tm_hour = s35392a_reg2hr(s35392a, buf[S35392A_BYTE_HOURS]);\r
+ tm->tm_wday = bcd2bin(buf[S35392A_BYTE_WDAY]);\r
+ tm->tm_mday = bcd2bin(buf[S35392A_BYTE_DAY]);\r
+ tm->tm_mon = bcd2bin(buf[S35392A_BYTE_MONTH]) - 1;\r
+ tm->tm_year = bcd2bin(buf[S35392A_BYTE_YEAR]) + 100;\r
+ //tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); \r
+\r
+ printk( "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, "\r
+ "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,\r
+ tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,\r
+ tm->tm_wday);\r
+\r
+ return rtc_valid_tm(tm);\r
+}\r
+static int s35392a_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm const *tm)\r
+{\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ \r
+}\r
+static int s35392a_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm const *tm)\r
+{\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ \r
+}\r
+static int s35392a_rtc_read_time(struct device *dev, struct rtc_time *tm)\r
+{\r
+ return s35392a_get_datetime(to_i2c_client(dev), tm);\r
+}\r
+\r
+static int s35392a_rtc_set_time(struct device *dev, struct rtc_time *tm)\r
+{\r
+ return s35392a_set_datetime(to_i2c_client(dev), tm);\r
+}\r
+static int s35392a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)\r
+{\r
+ return s35392a_i2c_read_alarm(to_i2c_client(dev),tm);\r
+}\r
+static int s35392a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)\r
+{ \r
+ return s35392a_i2c_set_alarm(to_i2c_client(dev),tm); \r
+}\r
+\r
+static int s35392a_i2c_open_alarm(struct i2c_client *client ) \r
+{\r
+ u8 data;\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ \r
+ s35392a_get_reg( s35392a, S35392A_CMD_STATUS2, &data, 1);\r
+ data = (data |S35392A_FLAG_INT1AE) & 0x3F;\r
+ s35392a_set_reg( s35392a, S35392A_CMD_STATUS2, &data, 1);\r
+\r
+ return 0;\r
+}\r
+static int s35392a_i2c_close_alarm(struct i2c_client *client )\r
+{\r
+ u8 data;\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+ \r
+ s35392a_get_reg( s35392a, S35392A_CMD_STATUS2, &data, 1);\r
+ data &= ~S35392A_FLAG_INT1AE;\r
+ s35392a_set_reg( s35392a, S35392A_CMD_STATUS2, &data, 1);\r
+\r
+ return 0;\r
+}\r
+\r
+static int s35392a_rtc_ioctl(struct device *dev,unsigned int cmd,unsigned long arg)\r
+{\r
+ struct i2c_client *client = to_i2c_client(dev);\r
+ \r
+ switch(cmd)\r
+ {\r
+ case RTC_AIE_OFF:\r
+ if(s35392a_i2c_close_alarm(client) < 0)\r
+ goto err;\r
+ break;\r
+ case RTC_AIE_ON:\r
+ if(s35392a_i2c_open_alarm(client) < 0)\r
+ goto err;\r
+ break;\r
+ default:\r
+ return -ENOIOCTLCMD;\r
+ }\r
+ return 0;\r
+err:\r
+ return -EIO;\r
+}\r
+static int s35392_rtc_proc(struct device *dev, unsigned int cmd, unsigned long arg)\r
+{\r
+ return 0;\r
+}\r
+\r
+static const struct rtc_class_ops s35392a_rtc_ops = {\r
+ .read_time = s35392a_rtc_read_time,\r
+ .set_time = s35392a_rtc_set_time,\r
+ .read_alarm = s35392a_rtc_read_alarm,\r
+ .set_alarm = s35392a_rtc_set_alarm,\r
+ .ioctl = s35392a_rtc_ioctl,\r
+ .proc = s35392_rtc_proc\r
+};\r
+\r
+static struct i2c_driver s35392a_driver;\r
+\r
+static int s35392a_probe(struct i2c_client *client,\r
+ const struct i2c_device_id *id)\r
+{\r
+ int err;\r
+ unsigned int i;\r
+ struct s35392a *s35392a;\r
+ struct rtc_time tm;\r
+ char buf[1];\r
+ printk("@@@@@%s:%d@@@@@\n",__FUNCTION__,__LINE__);\r
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {\r
+ err = -ENODEV;\r
+ goto exit;\r
+ }\r
+\r
+ s35392a = kzalloc(sizeof(struct s35392a), GFP_KERNEL);\r
+ if (!s35392a) {\r
+ err = -ENOMEM;\r
+ goto exit;\r
+ }\r
+\r
+ s35392a->client = client;\r
+ i2c_set_clientdata(client, s35392a);\r
+ mdelay(500);\r
+ //s35392a_init(s35392a);\r
+ err = s35392a_reset(s35392a); \r
+ if (err < 0) {\r
+ dev_err(&client->dev, "error resetting chip\n");\r
+ goto exit_dummy;\r
+ }\r
+ \r
+ err = s35392a_disable_test_mode(s35392a);\r
+ if (err < 0) {\r
+ dev_err(&client->dev, "error disabling test mode\n");\r
+ goto exit_dummy;\r
+ }\r
+\r
+ err = s35392a_get_reg(s35392a, S35392A_CMD_STATUS1, buf, sizeof(buf));\r
+ if (err < 0) {\r
+ dev_err(&client->dev, "error checking 12/24 hour mode\n");\r
+ goto exit_dummy;\r
+ }\r
+ if (buf[0] & S35392A_FLAG_24H)\r
+ s35392a->twentyfourhour = 1;\r
+ else\r
+ s35392a->twentyfourhour = 0;\r
+\r
+ if (s35392a_get_datetime(client, &tm) < 0)\r
+ dev_warn(&client->dev, "clock needs to be set\n");\r
+\r
+ s35392a->rtc = rtc_device_register(s35392a_driver.driver.name,\r
+ &client->dev, &s35392a_rtc_ops, THIS_MODULE);\r
+\r
+ if (IS_ERR(s35392a->rtc)) {\r
+ err = PTR_ERR(s35392a->rtc);\r
+ goto exit_dummy;\r
+ }\r
+ printk("@@@@@%s:%d@@@@@\n",__FUNCTION__,__LINE__);\r
+ return 0;\r
+\r
+exit_dummy:\r
+ rtc_device_unregister(s35392a->rtc);\r
+ kfree(s35392a);\r
+ i2c_set_clientdata(client, NULL);\r
+\r
+exit:\r
+ return err;\r
+}\r
+\r
+static int s35392a_remove(struct i2c_client *client)\r
+{\r
+ unsigned int i;\r
+\r
+ struct s35392a *s35392a = i2c_get_clientdata(client);\r
+\r
+ if (s35392a->client)\r
+ i2c_unregister_device(s35392a->client);\r
+\r
+ rtc_device_unregister(s35392a->rtc);\r
+ kfree(s35392a);\r
+ i2c_set_clientdata(client, NULL);\r
+\r
+ return 0;\r
+}\r
+\r
+static const struct i2c_device_id s35392a_id[] = {\r
+ { "rtc-s35392a", 0 },\r
+ { }\r
+};\r
+MODULE_DEVICE_TABLE(i2c, s35392a_id);\r
+\r
+static struct i2c_driver s35392a_driver = {\r
+ .driver = {\r
+ .name = "rtc-s35392a",\r
+ },\r
+ .probe = s35392a_probe,\r
+ .remove = s35392a_remove,\r
+ .id_table = s35392a_id,\r
+};\r
+\r
+static int __init s35392a_rtc_init(void)\r
+{\r
+ printk("@@@@@%s:%d@@@@@\n",__FUNCTION__,__LINE__);\r
+ return i2c_add_driver(&s35392a_driver);\r
+}\r
+\r
+static void __exit s35392a_rtc_exit(void)\r
+{\r
+ i2c_del_driver(&s35392a_driver);\r
+}\r
+\r
+MODULE_AUTHOR("swj@rock-chips.com>");\r
+MODULE_DESCRIPTION("S35392A RTC driver");\r
+MODULE_LICENSE("GPL");\r
+\r
+module_init(s35392a_rtc_init);\r
+module_exit(s35392a_rtc_exit);\r
+\r