From bed4af19486659bf51894e98fd98e6a585a57426 Mon Sep 17 00:00:00 2001 From: terry Date: Sat, 27 Apr 2019 22:41:13 +0800 Subject: [PATCH] RTC: VIM3/VIM3L: add khadas hym8563 rtc driver support --- arch/arm64/boot/dts/amlogic/kvim3_linux.dts | 5 + arch/arm64/boot/dts/amlogic/kvim3l_linux.dts | 5 + drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/khadas-rtc.c | 642 +++++++++++++++++++++++++++ include/linux/wakelock.h | 67 +++ 6 files changed, 731 insertions(+) create mode 100755 drivers/rtc/khadas-rtc.c create mode 100644 include/linux/wakelock.h diff --git a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts index 8e98dcf..d3ebb39 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3_linux.dts @@ -819,6 +819,11 @@ reg = <0x20>; status = "okay"; }; + + khadas-rtc { + compatible = "khadas-rtc"; + reg = <0x51>; + }; }; &audiobus { diff --git a/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts b/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts index 7b2eb9f..df4dcc7 100644 --- a/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts +++ b/arch/arm64/boot/dts/amlogic/kvim3l_linux.dts @@ -785,6 +785,11 @@ reg = <0x20>; status = "okay"; }; + + khadas-rtc { + compatible = "khadas-rtc"; + reg = <0x51>; + }; }; &audiobus { diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 0723c97..63756eb 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -286,6 +286,17 @@ config RTC_DRV_HYM8563 This driver can also be built as a module. If so, the module will be called rtc-hym8563. +config KHADAS_RTC + tristate "KHADAS RTC" + default y + help + Say Y to enable support for the HYM8563 I2C RTC chip. Apart + from the usual rtc functions it provides a clock output of + up to 32kHz. + + This driver can also be built as a module. If so, the module + will be called khadas-rtc. + config RTC_DRV_LP8788 tristate "TI LP8788 RTC driver" depends on MFD_LP8788 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 1ac694a..4d50a3d 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o +obj-$(CONFIG_KHADAS_RTC) += khadas-rtc.o obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o diff --git a/drivers/rtc/khadas-rtc.c b/drivers/rtc/khadas-rtc.c new file mode 100755 index 0000000..a377d05 --- /dev/null +++ b/drivers/rtc/khadas-rtc.c @@ -0,0 +1,642 @@ +#define pr_fmt(fmt) "rtc: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_CTL1 0x00 +#define RTC_CTL2 0x01 +#define RTC_SEC 0x02 +#define RTC_MIN 0x03 +#define RTC_HOUR 0x04 +#define RTC_DAY 0x05 +#define RTC_WEEK 0x06 +#define RTC_MON 0x07 +#define RTC_YEAR 0x08 +#define RTC_A_MIN 0x09 +#define RTC_A_HOUR 0x0A +#define RTC_A_DAY 0x0B +#define RTC_A_WEEK 0x0C +#define RTC_CLKOUT 0x0D +#define RTC_T_CTL 0x0E +#define RTC_T_COUNT 0x0F +#define CENTURY 0x80 +#define TI 0x10 +#define AF 0x08 +#define TF 0x04 +#define AIE 0x02 +#define TIE 0x01 +#define FE 0x80 +#define TE 0x80 +#define FD1 0x02 +#define FD0 0x01 +#define TD1 0x02 +#define TD0 0x01 +#define VL 0x80 + +#define RTC_SECTION_LEN 0x07 +#define ALM_BIT_DISABLE BIT(7) +#define MIN_MASK 0x7f +#define HOUR_MASK 0x3f +#define DAY_MASK 0x3f +#define MONTH_MASK 0x1f +#define WEEKDAY_MASK 0x07 +#define RTC_CTL2_AIE BIT(1) + +#define HY8563_DEBUG +#ifdef HY8563_DEBUG + #define debug_info(msg...) printk(msg); +#else + #define debug_info(msg...) +#endif + +struct hym8563 { + int irq; + struct i2c_client *client; + struct mutex mutex; + struct rtc_device *rtc; + struct rtc_wkalrm alarm; + struct wake_lock wake_lock; +}; +static struct i2c_client *gClient; +static int i2c_master_reg8_send(const struct i2c_client *client, + const char reg, const char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int ret; + char *tx_buf = kzalloc(count + 1, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + tx_buf[0] = reg; + memcpy(tx_buf+1, buf, count); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = count + 1; + msg.buf = (char *)tx_buf; + + ret = i2c_transfer(adap, &msg, 1); + kfree(tx_buf); + return (ret == 1) ? count : ret; +} + +static int i2c_master_reg8_recv(const struct i2c_client *client, + const char reg, char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msgs[2]; + int ret; + char reg_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = ®_buf; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = (char *)buf; + + ret = i2c_transfer(adap, msgs, 2); + + return (ret == 2) ? count : ret; +} + +static int hym8563_i2c_read_regs(struct i2c_client *client, + u8 reg, u8 buf[], unsigned len) +{ + int ret; + ret = i2c_master_reg8_recv(client, reg, buf, len); + printk("%s,ret=%d\n", __func__, ret); + return ret; +} + +static int hym8563_i2c_set_regs(struct i2c_client *client, + u8 reg, u8 const buf[], __u16 len) +{ + int ret; + ret = i2c_master_reg8_send(client, reg, buf, (int)len); + return ret; +} + +int hym8563_enable_count(struct i2c_client *client, int en) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + u8 regs[2]; + + if (!hym8563) + return -1; + + if (en) { + hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); + regs[0] |= TIE; + hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); + regs[0] = 0; + regs[0] |= (TE | TD1); + hym8563_i2c_set_regs(client, RTC_T_CTL, regs, 1); + } else { + hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); + regs[0] &= ~TIE; + hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); + regs[0] = 0; + regs[0] |= (TD0 | TD1); + hym8563_i2c_set_regs(client, RTC_T_CTL, regs, 1); + } + return 0; + +} + +int hym8563_set_count(struct i2c_client *client, int sec) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + u8 regs[2]; + + if (!hym8563) + return -1; + + if (sec >= 255) + regs[0] = 255; + else if (sec <= 1) + regs[0] = 1; + else + regs[0] = sec; + + hym8563_i2c_set_regs(client, RTC_T_COUNT, regs, 1); + + return 0; +} + +static int hym8563_init_device(struct i2c_client *client) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + u8 regs[2]; + int sr; + + mutex_lock(&hym8563->mutex); + regs[0] = 0; + sr = hym8563_i2c_set_regs(client, RTC_CTL1, regs, 1); + if (sr < 0) + goto exit; + + /*disable clkout*/ + regs[0] = 0x80; + sr = hym8563_i2c_set_regs(client, RTC_CLKOUT, regs, 1); + if (sr < 0) + goto exit; + + /*enable alarm && count interrupt*/ + sr = hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); + if (sr < 0) + goto exit; + regs[0] = 0x0; + regs[0] |= (AIE | TIE); + sr = hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); + if (sr < 0) + goto exit; + sr = hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); + if (sr < 0) + goto exit; + + sr = hym8563_i2c_read_regs(client, RTC_CTL2, regs, 1); + if (sr < 0) { + pr_err("read CTL2 err\n"); + goto exit; + } + + if (regs[0] & (AF|TF)) { + regs[0] &= ~(AF|TF); + sr = hym8563_i2c_set_regs(client, RTC_CTL2, regs, 1); + } +exit: + mutex_unlock(&hym8563->mutex); + return sr; +} + +static int hym8563_read_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + u8 regs[RTC_SECTION_LEN] = { 0, }; + debug_info("%s\n", __func__); + mutex_lock(&hym8563->mutex); + hym8563_i2c_read_regs(client, RTC_SEC, regs, RTC_SECTION_LEN); + mutex_unlock(&hym8563->mutex); + + tm->tm_sec = bcd2bin(regs[0x00] & 0x7F); + tm->tm_min = bcd2bin(regs[0x01] & 0x7F); + tm->tm_hour = bcd2bin(regs[0x02] & 0x3F); + tm->tm_mday = bcd2bin(regs[0x03] & 0x3F); + tm->tm_wday = bcd2bin(regs[0x04] & 0x07); + tm->tm_mon = bcd2bin(regs[0x05] & 0x1F); + tm->tm_mon -= 1; + tm->tm_year = bcd2bin(regs[0x06] & 0xFF); + + if (regs[5] & 0x80) + tm->tm_year += 1900; + else + tm->tm_year += 2000; + + tm->tm_year -= 1900; + if (tm->tm_year < 0) + tm->tm_year = 0; + tm->tm_isdst = 0; + + debug_info("%4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; + +} + +static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return hym8563_read_datetime(to_i2c_client(dev), tm); +} + +static int hym8563_set_time(struct i2c_client *client, struct rtc_time *tm) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + u8 regs[RTC_SECTION_LEN] = { 0, }; + u8 mon_day; + + debug_info("%4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + mon_day = rtc_month_days((tm->tm_mon), tm->tm_year + 1900); + + if (tm->tm_sec >= 60 || tm->tm_sec < 0) + regs[0x00] = bin2bcd(0x00); + else + regs[0x00] = bin2bcd(tm->tm_sec); + + if (tm->tm_min >= 60 || tm->tm_min < 0) + regs[0x01] = bin2bcd(0x00); + else + regs[0x01] = bin2bcd(tm->tm_min); + + if (tm->tm_hour >= 24 || tm->tm_hour < 0) + regs[0x02] = bin2bcd(0x00); + else + regs[0x02] = bin2bcd(tm->tm_hour); + + if ((tm->tm_mday) > mon_day) + regs[0x03] = bin2bcd(mon_day); + else if ((tm->tm_mday) > 0) + regs[0x03] = bin2bcd(tm->tm_mday); + else if ((tm->tm_mday) <= 0) + regs[0x03] = bin2bcd(0x01); + + if (tm->tm_year >= 200) + regs[0x06] = bin2bcd(99); + else if (tm->tm_year >= 100) + regs[0x06] = bin2bcd(tm->tm_year - 100); + else if (tm->tm_year >= 0) { + regs[0x06] = bin2bcd(tm->tm_year); + regs[0x05] |= 0x80; + } else { + regs[0x06] = bin2bcd(0); + regs[0x05] |= 0x80; + } + regs[0x04] = bin2bcd(tm->tm_wday); + regs[0x05] = (regs[0x05] & 0x80) | (bin2bcd(tm->tm_mon + 1) & 0x7F); + + mutex_lock(&hym8563->mutex); + hym8563_i2c_set_regs(client, RTC_SEC, regs, RTC_SECTION_LEN); + mutex_unlock(&hym8563->mutex); + + return 0; +} + +static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return hym8563_set_time(to_i2c_client(dev), tm); +} + +static int hym8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u8 buf[4]; + u8 regs[2]; + int ret; + + ret = hym8563_i2c_read_regs(gClient, RTC_A_MIN , buf, 4); + if (ret < 0) + return ret; + alm->time.tm_sec = -1; + alm->time.tm_min = (buf[0] & ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[0] & MIN_MASK); + alm->time.tm_hour = (buf[1] & ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[1] & HOUR_MASK); + alm->time.tm_mday = (buf[2] & ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[2] & DAY_MASK); + alm->time.tm_wday = (buf[3] & ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[3] & WEEKDAY_MASK); + + alm->time.tm_mon = -1; + alm->time.tm_year = -1; + ret = hym8563_i2c_read_regs(gClient, RTC_CTL2, regs, 1); + if (ret < 0) + return ret; + if (regs[0] & RTC_CTL2_AIE) + alm->enabled = 1; + + return 0; +} + +static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(gClient); + struct rtc_time now, *tm = &alarm->time; + u8 regs[4] = { 0, }; + u8 mon_day; + unsigned long alarm_sec, now_sec; + int diff_sec = 0; + + debug_info("%4d-%02d-%02d(%d) %02d:%02d:%02d enabled %d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, + tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, + tm->tm_sec, alarm->enabled); + + hym8563_read_datetime(gClient, &now); + mutex_lock(&hym8563->mutex); + rtc_tm_to_time(tm, &alarm_sec); + rtc_tm_to_time(&now, &now_sec); + + diff_sec = alarm_sec - now_sec; + if ((diff_sec > 0) && (diff_sec < 256)) { + if (alarm->enabled == 1) { + hym8563_set_count(gClient, diff_sec); + hym8563_enable_count(gClient, 1); + } else { + hym8563_enable_count(gClient, 0); + } + } else { + hym8563_enable_count(gClient, 0); + if (tm->tm_sec > 0) { + rtc_tm_to_time(tm, &alarm_sec); + rtc_time_to_tm(alarm_sec, tm); + } + hym8563->alarm = *alarm; + regs[0] = 0x0; + hym8563_i2c_set_regs(gClient, RTC_CTL2, regs, 1); + mon_day = rtc_month_days(tm->tm_mon, tm->tm_year + 1900); + hym8563_i2c_read_regs(gClient, RTC_A_MIN, regs, 4); + + if (tm->tm_min >= 60 || tm->tm_min < 0) + regs[0x00] = bin2bcd(0x00) & 0x7f; + else + regs[0x00] = bin2bcd(tm->tm_min) & 0x7f; + + if (tm->tm_hour >= 24 || tm->tm_hour < 0) + regs[0x01] = bin2bcd(0x00) & 0x7f; + else + regs[0x01] = bin2bcd(tm->tm_hour) & 0x7f; + + regs[0x03] = bin2bcd(tm->tm_wday) & 0x7f; + + if (tm->tm_mday > mon_day) + regs[0x02] = bin2bcd(mon_day) & 0x7f; + else if (tm->tm_mday > 0) + regs[0x02] = bin2bcd(tm->tm_mday) & 0x7f; + else if (tm->tm_mday <= 0) + regs[0x02] = bin2bcd(0x01) & 0x7f; + + hym8563_i2c_set_regs(gClient, RTC_A_MIN, regs, 4); + hym8563_i2c_read_regs(gClient, RTC_A_MIN, regs, 4); + hym8563_i2c_read_regs(gClient, RTC_CTL2, regs, 1); + + if (alarm->enabled == 1) + regs[0] |= AIE; + else + regs[0] &= 0x0; + + hym8563_i2c_set_regs(gClient, RTC_CTL2, regs, 1); + hym8563_i2c_read_regs(gClient, RTC_CTL2, regs, 1); + + if (diff_sec <= 0) + debug_info("alarm sec <= now sec\n"); + + } + + mutex_unlock(&hym8563->mutex); + + return 0; +} + +void hym8563_close_alarm(void) +{ + u8 data; + hym8563_i2c_read_regs(gClient, RTC_CTL2, &data, 1); + data &= ~AIE; + hym8563_i2c_set_regs(gClient, RTC_CTL2, &data, 1); + +} + +static const struct rtc_class_ops hym8563_rtc_ops = { + .read_time = hym8563_rtc_read_time, + .set_time = hym8563_rtc_set_time, + .read_alarm = hym8563_rtc_read_alarm, + .set_alarm = hym8563_rtc_set_alarm, + .ioctl = NULL, + .proc = NULL +}; + +static ssize_t rtc_sysfs_show_bootalarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t retval; + unsigned long alarm; + struct rtc_wkalrm alm; + + retval = hym8563_rtc_read_alarm(dev, &alm); + if (retval == 0 && alm.enabled) { + rtc_tm_to_time(&alm.time, &alarm); + retval = sprintf(buf, "%lu\n", alarm); + } + return retval; +} + +static ssize_t +rtc_sysfs_set_bootalarm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + unsigned long now, alarm; + struct rtc_wkalrm alm; + struct rtc_device *rtc = to_rtc_device(dev); + char *buf_ptr; + retval = rtc_read_time(rtc, &alm.time); + if (retval < 0) + return retval; + + rtc_tm_to_time(&alm.time, &now); + buf_ptr = (char *)buf; + + if (*buf_ptr == '+') + alm.enabled = 1; + else if (*buf_ptr == '-') + alm.enabled = 0; + else if (*buf_ptr == '=') + hym8563_close_alarm(); + + buf_ptr++; + if (kstrtoul(buf_ptr, 10, &alarm)) + return -1; + + rtc_time_to_tm(alarm, &alm.time); + retval = hym8563_rtc_set_alarm(dev, &alm); + return (retval < 0) ? retval : n; +} + +static DEVICE_ATTR(bootalarm, 0664, + rtc_sysfs_show_bootalarm, rtc_sysfs_set_bootalarm); + +void rtc_sysfs_add_bootalarm(struct rtc_device *rtc) +{ + int err; + err = device_create_file(&rtc->dev, &dev_attr_bootalarm); + if (err) + pr_err("failed to create boot alarm attribute"); + +} + +void rtc_sysfs_del_bootalarm(struct rtc_device *rtc) +{ + device_remove_file(&rtc->dev, &dev_attr_bootalarm); +} + +static int hym8563_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + u8 reg = 0; + struct hym8563 *hym8563; + struct rtc_device *rtc; + struct rtc_time tm_read, tm = { + .tm_wday = 6, + .tm_year = 111, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 12, + .tm_min = 0, + .tm_sec = 0, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL); + if (!hym8563) + return -ENOMEM; + + gClient = client; + hym8563->client = client; + hym8563->alarm.enabled = 0; + client->irq = 0; + mutex_init(&hym8563->mutex); + wake_lock_init(&hym8563->wake_lock, WAKE_LOCK_SUSPEND, "rtc_hym8563"); + i2c_set_clientdata(client, hym8563); + + hym8563_init_device(client); + hym8563_enable_count(client, 0); + + hym8563_i2c_read_regs(client, RTC_SEC, ®, 1); + if (reg&0x80) { + debug_info("clock/calendar information is no longer guaranteed\n"); + hym8563_set_time(client, &tm); + } + + hym8563_read_datetime(client, &tm_read); + + if (((tm_read.tm_year < 70) | (tm_read.tm_year > 137)) | + (tm_read.tm_mon == -1) | (rtc_valid_tm(&tm_read) != 0)) + hym8563_set_time(client, &tm); + + rtc = rtc_device_register(client->name, &client->dev, + &hym8563_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + rc = PTR_ERR(rtc); + rtc = NULL; + goto exit; + } + hym8563->rtc = rtc; + rtc_sysfs_add_bootalarm(rtc); + + return 0; + +exit: + if (hym8563) + wake_lock_destroy(&hym8563->wake_lock); + + return rc; +} + +static int hym8563_remove(struct i2c_client *client) +{ + struct hym8563 *hym8563 = i2c_get_clientdata(client); + wake_lock_destroy(&hym8563->wake_lock); + + return 0; +} + +void hym8563_shutdown(struct i2c_client *client) +{ + u8 regs[2]; + int ret; + regs[0] = 0x00; + ret = hym8563_i2c_set_regs(client, RTC_CLKOUT, regs, 1); + if (ret < 0) + pr_err("rtc shutdown is error\n"); +} + +static const struct i2c_device_id hym8563_id[] = { + { "khadas-hym8563", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hym8563_id); + +static struct of_device_id rtc_dt_ids[] = { + { .compatible = "khadas-rtc" }, + {}, +}; + +struct i2c_driver hym8563_driver = { + .driver = { + .name = "khadas-rtc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rtc_dt_ids), + }, + .probe = hym8563_probe, + .remove = hym8563_remove, + .id_table = hym8563_id, +}; + +static int __init hym8563_init(void) +{ + return i2c_add_driver(&hym8563_driver); +} + +static void __exit hym8563_exit(void) +{ + i2c_del_driver(&hym8563_driver); +} + +MODULE_AUTHOR("terry terry@szwesion.com"); +MODULE_DESCRIPTION("HYM8563 RTC driver"); +MODULE_LICENSE("GPL"); + +module_init(hym8563_init); +module_exit(hym8563_exit); diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h new file mode 100644 index 0000000..f4a698a --- /dev/null +++ b/include/linux/wakelock.h @@ -0,0 +1,67 @@ +/* include/linux/wakelock.h + * + * Copyright (C) 2007-2012 Google, 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 _LINUX_WAKELOCK_H +#define _LINUX_WAKELOCK_H + +#include +#include + +/* A wake_lock prevents the system from entering suspend or other low power + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock + * prevents a full system suspend. + */ + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init(&lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} + +static inline int wake_lock_active(struct wake_lock *lock) +{ + return lock->ws.active; +} + +#endif -- 2.7.4