add rtc-s35392a for raho
authorswj <swj@rock-chips.com>
Wed, 11 Aug 2010 02:49:58 +0000 (19:49 -0700)
committerswj <swj@rock-chips.com>
Wed, 11 Aug 2010 02:49:58 +0000 (19:49 -0700)
drivers/rtc/rtc-s35392a.c [new file with mode: 0755]
drivers/rtc/rtc-s35392a.h [new file with mode: 0755]

diff --git a/drivers/rtc/rtc-s35392a.c b/drivers/rtc/rtc-s35392a.c
new file mode 100755 (executable)
index 0000000..ff1dbe8
--- /dev/null
@@ -0,0 +1,460 @@
+/*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
diff --git a/drivers/rtc/rtc-s35392a.h b/drivers/rtc/rtc-s35392a.h
new file mode 100755 (executable)
index 0000000..079df16
--- /dev/null
@@ -0,0 +1,49 @@
+/*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
+ #ifndef RTC_S35392A_H_\r
+ #define RTC_S35392A_H_\r
\r
+#define S35392A_CMD_STATUS1    0x0\r
+#define S35392A_CMD_STATUS2    0x1\r
+#define S35392A_CMD_TIME1              0x2\r
+#define S35392A_CMD_TIME2              0x3\r
+#define S35392A_CMD_INT1               0x4\r
+#define S35392A_CMD_INT2               0x5\r
+#define S35392A_CMD_CHECK      0x6\r
+#define S35392A_CMD_FREE               0x7\r
+\r
+#define S35392A_BYTE_YEAR              0\r
+#define S35392A_BYTE_MONTH     1\r
+#define S35392A_BYTE_DAY               2\r
+#define S35392A_BYTE_WDAY              3\r
+#define S35392A_BYTE_HOURS     4\r
+#define S35392A_BYTE_MINS              5\r
+#define S35392A_BYTE_SECS              6\r
+\r
+#define S35392A_FLAG_POC               0x01\r
+#define S35392A_FLAG_BLD               0x02\r
+#define S35392A_FLAG_INT2      0x04\r
+#define S35392A_FLAG_INT1      0x08\r
+#define S35392A_FLAG_24H               0x40\r
+\r
+#define S35392A_FLAG_TEST              0x01\r
+#define S35392A_FLAG_INT1AE      0x20\r
+#define S35392A_FLAG_RESET             0x80\r
+\r
+\r
+\r
+//#define S35392_STATUS_INT1       (~(0x3<<5))\r
+//#define S35392_STATUS_INT2       (~(0x3<<1))\r
+#endif\r
+\r