From 2489007e7d740ccbc3e0a202914e243ad5178787 Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sat, 22 Sep 2012 09:48:20 +0200 Subject: merge opensource jb u5 Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2 --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/alarm-dev.c | 11 +- drivers/rtc/alarm.c | 53 +++++++++ drivers/rtc/interface.c | 23 ++++ drivers/rtc/rtc-max77686.c | 2 + drivers/rtc/rtc-s5m.c | 268 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 366 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index fdfd85d..2a564ca 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -133,6 +133,17 @@ config RTC_ALARM_BOOT time with checking auto power up menu, the handset must be powered on in time automatically. +config RTC_POWER_OFF + bool "Power off alarm function" + depends on RTC_INTF_ALARM + default n + help + This is only for S project power off alarm. When the handset going to + fake power off state, all wakeup sources are blocked, + except pwr-key and RTC_POWER_OFF. + At the time you will set power off time, rtc wakeup the device + and run the power off process. + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c index 78d80d7..55f4896 100644 --- a/drivers/rtc/alarm-dev.c +++ b/drivers/rtc/alarm-dev.c @@ -68,8 +68,9 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) uint32_t alarm_type_mask = 1U << alarm_type; #if defined(CONFIG_RTC_ALARM_BOOT) char bootalarm_data[14]; +#elif defined(CONFIG_RTC_POWER_OFF) + char pwroffalarm_data[14]; #endif - if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) return -EINVAL; @@ -171,6 +172,14 @@ from_old_alarm_set: } rv = alarm_set_alarm_boot(bootalarm_data); break; +#elif defined(CONFIG_RTC_POWER_OFF) + case ANDROID_ALARM_SET_ALARM_POWEROFF: + if (copy_from_user(pwroffalarm_data, (void __user *)arg, 14)) { + rv = -EFAULT; + goto err1; + } + rv = alarm_set_alarm_poweroff(pwroffalarm_data); + break; #endif case ANDROID_ALARM_GET_TIME(0): switch (alarm_type) { diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index 94ff21d..5b82b52 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -347,6 +347,59 @@ int alarm_set_alarm_boot(char *alarm_data) } #endif +#if defined(CONFIG_RTC_POWER_OFF) +#define PWROFFALM_BIT_EN 0 +#define PWROFFALM_BIT_YEAR 1 +#define PWROFFALM_BIT_MONTH 5 +#define PWROFFALM_BIT_DAY 7 +#define PWROFFALM_BIT_HOUR 9 +#define PWROFFALM_BIT_MIN 11 +#define PWROFFALM_BIT_TOTAL 13 + +int alarm_set_alarm_poweroff(char *alarm_data) +{ + struct rtc_wkalrm alm; + int ret; + char buf_ptr[PWROFFALM_BIT_TOTAL + 1]; + + if (!alarm_rtc_dev) { + pr_alarm(ERROR, "alarm_set_alarm_poweroff: " + "no RTC, time will be lost on reboot\n"); + return -1; + } + + strlcpy(buf_ptr, alarm_data, PWROFFALM_BIT_TOTAL + 1); + + alm.time.tm_sec = 0; + + alm.time.tm_min = (buf_ptr[PWROFFALM_BIT_MIN] - '0') * 10 + + (buf_ptr[PWROFFALM_BIT_MIN + 1] - '0'); + alm.time.tm_hour = (buf_ptr[PWROFFALM_BIT_HOUR] - '0') * 10 + + (buf_ptr[PWROFFALM_BIT_HOUR + 1] - '0'); + alm.time.tm_mday = (buf_ptr[PWROFFALM_BIT_DAY] - '0') * 10 + + (buf_ptr[PWROFFALM_BIT_DAY + 1] - '0'); + alm.time.tm_mon = (buf_ptr[PWROFFALM_BIT_MONTH] - '0') * 10 + + (buf_ptr[PWROFFALM_BIT_MONTH + 1] - '0'); + alm.time.tm_year = (buf_ptr[PWROFFALM_BIT_YEAR] - '0') * 1000 + + (buf_ptr[PWROFFALM_BIT_YEAR + 1] - '0') * 100 + + (buf_ptr[PWROFFALM_BIT_YEAR + 2] - '0') * 10 + + (buf_ptr[PWROFFALM_BIT_YEAR + 3] - '0'); + alm.enabled = (*buf_ptr == '1'); + + alm.time.tm_mon -= 1; + alm.time.tm_year -= 1900; + + printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, + 1900 + alm.time.tm_year, 1 + alm.time.tm_mon, + alm.time.tm_mday, alm.time.tm_hour, alm.time.tm_min, + alm.time.tm_sec, alm.time.tm_wday); + + ret = rtc_set_alarm_poweroff(alarm_rtc_dev, &alm); + + return ret; +} +#endif + /** * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format * diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index ccf498c..7cf1429 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -397,6 +397,29 @@ int rtc_set_alarm_boot(struct rtc_device *rtc, struct rtc_wkalrm *alarm) EXPORT_SYMBOL_GPL(rtc_set_alarm_boot); #endif +#if defined(CONFIG_RTC_POWER_OFF) +int rtc_set_alarm_poweroff(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->set_alarm) + err = -EINVAL; + else + err = rtc->ops->set_alarm_poweroff(rtc->dev.parent, alarm); + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_alarm_poweroff); +#endif + + /* Called once per device from rtc_device_register */ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 4bf32eb..a5c1c78 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -806,9 +806,11 @@ static int __devinit max77686_rtc_probe(struct platform_device *pdev) #ifdef MAX77686_RTC_WTSR_SMPL if (max77686->wtsr_smpl & MAX77686_WTSR_ENABLE) max77686_rtc_enable_wtsr(info, true); +#if !defined(CONFIG_TARGET_LOCALE_KOR) if (max77686->wtsr_smpl & MAX77686_SMPL_ENABLE) max77686_rtc_enable_smpl(info, true); #endif +#endif device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 22c4ff7..fdea136 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -29,6 +29,9 @@ struct s5m_rtc_info { struct rtc_device *rtc_dev; struct mutex lock; int irq; +#if defined(CONFIG_RTC_POWER_OFF) + int irq2; +#endif int device_type; int rtc_24hr_mode; bool wtsr_smpl; @@ -322,7 +325,7 @@ static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) data[i] &= ~ALARM_ENABLE_MASK; ret = s5m_bulk_write(info->rtc, S5M87XX_ALARM0_SEC, 8, data); - if (ret <0) + if (ret < 0) return ret; ret = s5m8767_rtc_set_alarm_reg(info); @@ -336,6 +339,50 @@ static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) return ret; } +#if defined(CONFIG_RTC_POWER_OFF) +static int s5m_rtc_stop_alarm_poweroff(struct s5m_rtc_info *info) +{ + u8 data[8]; + int ret, i; + struct rtc_time tm; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = s5m_bulk_read(info->rtc, S5M87XX_ALARM1_SEC, 8, data); + if (ret < 0) + return ret; + + s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); + printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, + 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday); + + switch (info->device_type) { + case S5M8763X: + ret = s5m_reg_write(info->rtc, S5M87XX_ALARM1_CONF, 0); + break; + + case S5M8767X: + for (i = 0; i < 7; i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = s5m_bulk_write(info->rtc, S5M87XX_ALARM1_SEC, 8, data); + if (ret < 0) + return ret; + + ret = s5m8767_rtc_set_alarm_reg(info); + + break; + + default: + return -EINVAL; + } + + return ret; +} +#endif + static int s5m_rtc_start_alarm(struct s5m_rtc_info *info) { int ret; @@ -387,6 +434,60 @@ static int s5m_rtc_start_alarm(struct s5m_rtc_info *info) return ret; } +#if defined(CONFIG_RTC_POWER_OFF) +static int s5m_rtc_start_alarm_poweroff(struct s5m_rtc_info *info) +{ + int ret; + u8 data[8]; + u8 alarm0_conf; + struct rtc_time tm; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = s5m_bulk_read(info->rtc, S5M87XX_ALARM1_SEC, 8, data); + if (ret < 0) + return ret; + + s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); + printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, + 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday); + + switch (info->device_type) { + case S5M8763X: + alarm0_conf = 0x77; + ret = s5m_reg_write(info->rtc, S5M87XX_ALARM1_CONF, + alarm0_conf); + break; + + case S5M8767X: + data[RTC_SEC] |= ALARM_ENABLE_MASK; + data[RTC_MIN] |= ALARM_ENABLE_MASK; + data[RTC_HOUR] |= ALARM_ENABLE_MASK; + data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; + if (data[RTC_DATE] & 0x1f) + data[RTC_DATE] |= ALARM_ENABLE_MASK; + if (data[RTC_MONTH] & 0xf) + data[RTC_MONTH] |= ALARM_ENABLE_MASK; + if (data[RTC_YEAR1] & 0x7f) + data[RTC_YEAR1] |= ALARM_ENABLE_MASK; + + ret = s5m_bulk_write(info->rtc, S5M87XX_ALARM1_SEC, 8, data); + if (ret < 0) + return ret; + ret = s5m8767_rtc_set_alarm_reg(info); + + break; + + default: + return -EINVAL; + } + + return ret; +} +#endif + static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct s5m_rtc_info *info = dev_get_drvdata(dev); @@ -432,6 +533,97 @@ out: return ret; } +#if defined(CONFIG_RTC_POWER_OFF) +static int s5m_rtc_set_alarm_poweroff(struct device *dev, + struct rtc_wkalrm *alrm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + switch (info->device_type) { + case S5M8763X: + if (alrm->enabled) { + data[RTC_SEC] = bin2bcd(alrm->time.tm_sec); + data[RTC_MIN] = bin2bcd(alrm->time.tm_min); + data[RTC_HOUR] = bin2bcd(alrm->time.tm_hour); + data[RTC_WEEKDAY] = 0; + data[RTC_DATE] = bin2bcd(alrm->time.tm_mday); + data[RTC_MONTH] = bin2bcd(alrm->time.tm_mon); + data[RTC_YEAR1] = bin2bcd(alrm->time.tm_year % 100); + data[RTC_YEAR2] = + bin2bcd((alrm->time.tm_year + 1900) / 100); + } else { + data[RTC_SEC] = 0; + data[RTC_MIN] = 0; + data[RTC_HOUR] = 0; + data[RTC_WEEKDAY] = 0; + data[RTC_DATE] = 1; + data[RTC_MONTH] = 0; + data[RTC_YEAR1] = 0; + data[RTC_YEAR2] = 0; + } + break; + + case S5M8767X: + if (alrm->enabled) { + data[RTC_SEC] = alrm->time.tm_sec; + data[RTC_MIN] = alrm->time.tm_min; + if (alrm->time.tm_hour >= 12) + data[RTC_HOUR] = + alrm->time.tm_hour | HOUR_PM_MASK; + else + data[RTC_HOUR] = + alrm->time.tm_hour & ~HOUR_PM_MASK; + data[RTC_WEEKDAY] = 1 << alrm->time.tm_wday; + data[RTC_DATE] = alrm->time.tm_mday; + data[RTC_MONTH] = alrm->time.tm_mon + 1; + data[RTC_YEAR1] = alrm->time.tm_year % 100; + data[RTC_YEAR2] = + bin2bcd((alrm->time.tm_year + 1900) / 100); + } else { + data[RTC_SEC] = 0; + data[RTC_MIN] = 0; + data[RTC_HOUR] = 0; + data[RTC_WEEKDAY] = 0; + data[RTC_DATE] = 1; + data[RTC_MONTH] = 0; + data[RTC_YEAR1] = 0; + data[RTC_YEAR2] = 0; + } + break; + + default: + return -EINVAL; + } + + printk(KERN_INFO "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, + 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon, + alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min, + alrm->time.tm_sec, alrm->time.tm_wday); + + mutex_lock(&info->lock); + + ret = s5m_rtc_stop_alarm_poweroff(info); + if (ret < 0) + goto out; + + ret = s5m_bulk_write(info->rtc, S5M87XX_ALARM1_SEC, 8, data); + if (ret < 0) + goto out; + + ret = s5m8767_rtc_set_alarm_reg(info); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = s5m_rtc_start_alarm_poweroff(info); +out: + mutex_unlock(&info->lock); + return ret; +} +#endif + static int s5m_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { @@ -458,11 +650,27 @@ static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data) return IRQ_HANDLED; } +#if defined(CONFIG_RTC_POWER_OFF) +static irqreturn_t s5m_rtc_alarm2_irq(int irq, void *data) +{ + struct s5m_rtc_info *info = data; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + pr_info("%s called", __func__); + + return IRQ_HANDLED; +} +#endif + static const struct rtc_class_ops s5m_rtc_ops = { .read_time = s5m_rtc_read_time, .set_time = s5m_rtc_set_time, .read_alarm = s5m_rtc_read_alarm, .set_alarm = s5m_rtc_set_alarm, +#if defined(CONFIG_RTC_POWER_OFF) + .set_alarm_poweroff = s5m_rtc_set_alarm_poweroff, +#endif .alarm_irq_enable = s5m_rtc_alarm_irq_enable, }; @@ -487,6 +695,7 @@ static void s5m_rtc_enable_wtsr(struct s5m_rtc_info *info, bool enable) __func__, ret); return; } + ret = s5m8767_rtc_set_alarm_reg(info); } static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable) @@ -510,6 +719,7 @@ static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable) __func__, ret); return; } + ret = s5m8767_rtc_set_alarm_reg(info); val = 0; s5m_reg_read(info->rtc, S5M87XX_WTSR_SMPL_CNTL, &val); @@ -521,7 +731,16 @@ static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) u8 data[2], tp_read; int ret; struct rtc_time tm; +#if defined(CONFIG_RTC_POWER_OFF) + u8 data_alm2[8]; + ret = s5m_bulk_read(info->rtc, S5M87XX_ALARM1_SEC, 8, data_alm2); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read control reg(%d)\n", + __func__, ret); + return ret; + } +#endif ret = s5m_reg_read(info->rtc, S5M87XX_RTC_UDR_CON, &tp_read); if (ret < 0) { dev_err(info->dev, "%s: fail to read control reg(%d)\n", @@ -588,10 +807,16 @@ static int __devinit s5m_rtc_probe(struct platform_device *pdev) switch (pdata->device_type) { case S5M8763X: info->irq = s5m87xx->irq_base + S5M8763_IRQ_ALARM0; +#if defined(CONFIG_RTC_POWER_OFF) + info->irq2 = s5m87xx->irq_base + S5M8763_IRQ_ALARM1; +#endif break; case S5M8767X: info->irq = s5m87xx->irq_base + S5M8767_IRQ_RTCA1; +#if defined(CONFIG_RTC_POWER_OFF) + info->irq2 = s5m87xx->irq_base + S5M8767_IRQ_RTCA2; +#endif break; default: @@ -626,7 +851,14 @@ static int __devinit s5m_rtc_probe(struct platform_device *pdev) if (ret < 0) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->irq, ret); +#if defined(CONFIG_RTC_POWER_OFF) + ret = request_threaded_irq(info->irq2, NULL, s5m_rtc_alarm2_irq, 0, + "rtc-alarm0", info); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm2 IRQ: %d: %d\n", + info->irq, ret); +#endif dev_info(&pdev->dev, "RTC CHIP NAME: %s\n", pdev->id_entry->name); return 0; @@ -643,6 +875,9 @@ static int __devexit s5m_rtc_remove(struct platform_device *pdev) if (info) { free_irq(info->irq, info); +#if defined(CONFIG_RTC_POWER_OFF) + free_irq(info->irq2, info); +#endif rtc_device_unregister(info->rtc_dev); kfree(info); } @@ -676,10 +911,41 @@ static const struct platform_device_id s5m_rtc_id[] = { { "s5m-rtc", 0 }, }; +#if defined(CONFIG_RTC_POWER_OFF) +extern bool fake_shut_down; + +static int s5m_rtc_resume(struct device *dev) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + int ret; + ret = s5m_rtc_stop_alarm_poweroff(info); + + return ret; +} + +static int s5m_rtc_suspend(struct device *dev) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + + if (fake_shut_down) + disable_irq(info->irq); + + return 0; +} + +static const struct dev_pm_ops s5m_rtc_pm_ops = { + .resume = s5m_rtc_resume, + .suspend = s5m_rtc_suspend, +}; +#endif + static struct platform_driver s5m_rtc_driver = { .driver = { .name = "s5m-rtc", .owner = THIS_MODULE, +#if defined(CONFIG_RTC_POWER_OFF) + .pm = &s5m_rtc_pm_ops, +#endif }, .probe = s5m_rtc_probe, .remove = __devexit_p(s5m_rtc_remove), -- cgit v1.1