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