Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753748Ab2HOSgf (ORCPT ); Wed, 15 Aug 2012 14:36:35 -0400 Received: from mailhost.informatik.uni-hamburg.de ([134.100.9.70]:58996 "EHLO mailhost.informatik.uni-hamburg.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751457Ab2HOSgd (ORCPT ); Wed, 15 Aug 2012 14:36:33 -0400 Message-ID: <502BEC1D.5040406@metafoo.de> Date: Wed, 15 Aug 2012 20:36:13 +0200 From: Lars-Peter Clausen User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20120613 Icedove/3.0.11 MIME-Version: 1.0 To: Anthony Olech CC: Andrew Morton , Mark Brown , Paul Gortmaker , Samuel Ortiz , Alessandro Zummo , rtc-linux@googlegroups.com, LKML , David Dajun Chen Subject: Re: [NEW DRIVER V3 5/8] DA9058 RTC driver References: <201208151518.q7FFINX1011619@latitude.olech.com> In-Reply-To: <201208151518.q7FFINX1011619@latitude.olech.com> X-Enigmail-Version: 1.0.1 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 14706 Lines: 521 On 08/15/2012 05:05 PM, Anthony Olech wrote: > This is the RTC component driver of the Dialog DA9058 PMIC. > This driver is just one component of the whole DA9058 PMIC driver. > It depends on the CORE component driver of the DA9058 MFD. How much is this one actually different from the da9052 rtc core? I just had a quick glance at them, but they seem rather similar. > > Signed-off-by: Anthony Olech > Signed-off-by: David Dajun Chen > --- > drivers/rtc/Kconfig | 10 + > drivers/rtc/Makefile | 1 + > drivers/rtc/rtc-da9058.c | 458 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 469 insertions(+), 0 deletions(-) > create mode 100644 drivers/rtc/rtc-da9058.c > > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > index 08cbdb9..21f5630 100644 > --- a/drivers/rtc/Kconfig > +++ b/drivers/rtc/Kconfig > @@ -135,6 +135,16 @@ config RTC_DRV_88PM860X > This driver can also be built as a module. If so, the module > will be called rtc-88pm860x. > > +config RTC_DRV_DA9058 > + tristate "Dialog DA9058" > + depends on MFD_DA9058 > + help > + If you say yes here you will get support for the > + RTC of the Dialog DA9058 PMIC. > + > + This driver can also be built as a module. If so, the module > + will be called rtc-da9058. > + > config RTC_DRV_DS1307 > tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" > help > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > index 2973921..b772f05 100644 > --- a/drivers/rtc/Makefile > +++ b/drivers/rtc/Makefile > @@ -29,6 +29,7 @@ obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o > obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o > obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o > obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o > +obj-$(CONFIG_RTC_DRV_DA9058) += rtc-da9058.o > obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o > obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o > obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o > diff --git a/drivers/rtc/rtc-da9058.c b/drivers/rtc/rtc-da9058.c > new file mode 100644 > index 0000000..1b4e05b > --- /dev/null > +++ b/drivers/rtc/rtc-da9058.c > @@ -0,0 +1,458 @@ > +/* > + * Copyright (C) 2012 Dialog Semiconductor Ltd. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + */ > + > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +/* > + * Limit values > + */ > +#define DA9058_RTC_SECONDS_LIMIT 59 > +#define DA9058_RTC_MINUTES_LIMIT 59 > +#define DA9058_RTC_HOURS_LIMIT 23 > +#define DA9058_RTC_DAYS_LIMIT 31 > +#define DA9058_RTC_MONTHS_LIMIT 12 > +#define DA9058_RTC_YEARS_LIMIT 63 > + > +struct da9058_rtc { > + struct da9058 *da9058; > + struct platform_device *pdev; > + struct rtc_device *rtc_dev; > + int alarm_irq; > + int tick_irq; > + int alarm_enabled; /* used over suspend/resume */ > +}; > + > +static int da9058_rtc_check_param(struct rtc_time *rtc_tm) > +{ > + if ((rtc_tm->tm_sec > DA9058_RTC_SECONDS_LIMIT) || (rtc_tm->tm_sec < 0)) > + return -EIO; > + > + if ((rtc_tm->tm_min > DA9058_RTC_MINUTES_LIMIT) || (rtc_tm->tm_min < 0)) > + return -EIO; > + > + if ((rtc_tm->tm_hour > DA9058_RTC_HOURS_LIMIT) || (rtc_tm->tm_hour < 0)) > + return -EIO; > + > + if ((rtc_tm->tm_mday > DA9058_RTC_DAYS_LIMIT) || (rtc_tm->tm_mday <= 0)) > + return -EIO; > + > + if ((rtc_tm->tm_mon > DA9058_RTC_MONTHS_LIMIT) || (rtc_tm->tm_mon <= 0)) > + return -EIO; > + > + if ((rtc_tm->tm_year > DA9058_RTC_YEARS_LIMIT) || (rtc_tm->tm_year < 0)) > + return -EIO; > + > + return 0; > +} > + > +static int da9058_rtc_readtime(struct device *dev, struct rtc_time *tm) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + struct da9058 *da9058 = rtc->da9058; > + u8 rtc_time[6]; > + int ret; > + > + ret = da9058_bulk_read(da9058, DA9058_COUNTS_REG, rtc_time, 6); > + if (ret) > + return ret; > + > + tm->tm_sec = rtc_time[0] & DA9058_RTC_SECS_MASK; > + > + tm->tm_min = rtc_time[1] & DA9058_RTC_MINS_MASK; > + > + tm->tm_hour = rtc_time[2] & DA9058_RTC_HRS_MASK; > + > + tm->tm_mday = (rtc_time[3] & DA9058_RTC_DAY_MASK); > + > + tm->tm_mon = (rtc_time[4] & DA9058_RTC_MTH_MASK); > + > + tm->tm_year = (rtc_time[5] & DA9058_RTC_YRS_MASK); > + > + ret = da9058_rtc_check_param(tm); > + > + if (ret) > + return ret; > + > + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, > + tm->tm_year); > + tm->tm_year += 100; > + tm->tm_mon -= 1; > + > + return 0; > +} > + > +static int da9058_rtc_settime(struct device *dev, struct rtc_time *tm) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + struct da9058 *da9058 = rtc->da9058; > + unsigned int rtc_ctrl, val; > + u8 rtc_time[6]; > + int ret; > + > + tm->tm_year -= 100; > + tm->tm_mon += 1; > + > + ret = da9058_rtc_check_param(tm); > + if (ret < 0) > + return ret; > + > + ret = da9058_reg_read(da9058, DA9058_COUNTS_REG, &rtc_ctrl); > + if (ret) > + return ret; > + rtc_ctrl &= ~DA9058_RTC_SECS_MASK; > + > + rtc_time[0] = rtc_ctrl | tm->tm_sec; > + rtc_time[1] = tm->tm_min; > + rtc_time[2] = tm->tm_hour; > + rtc_time[3] = tm->tm_mday; > + rtc_time[4] = tm->tm_mon; > + rtc_time[5] = tm->tm_year; > + > + ret = da9058_bulk_write(da9058, DA9058_COUNTS_REG, rtc_time, 6); > + if (ret) { > + dev_dbg(dev, "failed %d to write to RTC\n", ret); > + return ret; > + } > + ret = da9058_reg_read(da9058, DA9058_COUNTY_REG, &val); > + if (ret) > + return ret; > + > + val &= DA9058_COUNTY_MONITOR; > + if (val) > + return 0; > + > + ret = da9058_set_bits(da9058, DA9058_COUNTY_REG, DA9058_COUNTY_MONITOR); > + > + return ret; > +} > + > +static int da9058_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + struct da9058 *da9058 = rtc->da9058; > + struct rtc_time *tm = &alrm->time; > + unsigned int val; > + u8 alm_time[6]; > + int ret; > + > + ret = da9058_bulk_read(da9058, DA9058_ALARMS_REG, alm_time, 6); > + if (ret) > + return ret; > + > + tm->tm_min = alm_time[0] & DA9058_RTC_ALMSECS_MASK; > + > + tm->tm_min = alm_time[1] & DA9058_RTC_ALMMINS_MASK; > + > + tm->tm_hour = alm_time[2] & DA9058_RTC_ALMHRS_MASK; > + > + tm->tm_mday = alm_time[3] & DA9058_RTC_ALMDAY_MASK; > + > + tm->tm_mon = alm_time[4] & DA9058_RTC_ALMMTH_MASK; > + > + tm->tm_year = alm_time[5] & DA9058_RTC_ALMYRS_MASK; > + > + ret = da9058_rtc_check_param(tm); > + if (ret < 0) > + return ret; > + > + ret = da9058_reg_read(da9058, DA9058_ALARMY_REG, &val); > + if (ret) > + return ret; > + > + alrm->enabled = val & DA9058_ALARMY_ALARMON; > + > + tm->tm_year += 100; > + tm->tm_mon -= 1; > + > + return 0; > +} > + > +static int da9058_rtc_stop_alarm(struct da9058_rtc *rtc) > +{ > + return da9058_clear_bits(rtc->da9058, DA9058_ALARMY_REG, > + DA9058_ALARMY_ALARMON); > +} > + > +static int da9058_rtc_start_alarm(struct da9058_rtc *rtc) > +{ > + return da9058_set_bits(rtc->da9058, DA9058_ALARMY_REG, > + DA9058_ALARMY_ALARMON); > +} > + > +static int da9058_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + struct da9058 *da9058 = rtc->da9058; > + struct rtc_time *tm = &alrm->time; > + unsigned int rtc_ctrl; > + u8 alm_time[6]; > + int ret; > + > + tm->tm_year -= 100; > + tm->tm_mon += 1; > + > + ret = da9058_rtc_check_param(tm); > + if (ret < 0) > + return ret; > + > + memset(alm_time, 0, sizeof(alm_time)); > + > + if (tm->tm_sec != -1) > + alm_time[0] |= tm->tm_sec; > + else > + alm_time[0] |= DA9058_RTC_ALMSECS_MASK; > + > + ret = da9058_reg_read(da9058, DA9058_ALARMMI_REG, &rtc_ctrl); > + if (ret) > + return ret; > + rtc_ctrl &= ~DA9058_RTC_ALMMINS_MASK; > + > + if (tm->tm_min != -1) > + alm_time[1] = rtc_ctrl | tm->tm_min; > + else > + alm_time[1] = rtc_ctrl | DA9058_RTC_ALMMINS_MASK; > + > + if (tm->tm_hour != -1) > + alm_time[2] |= tm->tm_hour; > + else > + alm_time[2] |= DA9058_RTC_ALMHRS_MASK; > + > + if (tm->tm_mday != -1) > + alm_time[3] |= tm->tm_mday; > + else > + alm_time[3] |= DA9058_RTC_ALMDAY_MASK; > + > + if (tm->tm_mon != -1) > + alm_time[4] |= tm->tm_mon; > + else > + alm_time[4] |= DA9058_RTC_ALMMTH_MASK; > + > + ret = da9058_reg_read(da9058, DA9058_ALARMY_REG, &rtc_ctrl); > + if (ret) > + return ret; > + > + rtc_ctrl &= ~DA9058_RTC_ALMYRS_MASK; > + > + if (tm->tm_year != -1) > + alm_time[5] = rtc_ctrl | tm->tm_year; > + else > + alm_time[5] = rtc_ctrl | DA9058_RTC_ALMYRS_MASK; > + > + ret = da9058_rtc_stop_alarm(rtc); > + if (ret < 0) > + return ret; > + > + ret = da9058_bulk_write(da9058, DA9058_ALARMS_REG, alm_time, 6); > + if (ret) > + return ret; > + > + if (alrm->enabled) > + ret = da9058_rtc_start_alarm(rtc); > + > + return ret; > +} > + > +static int da9058_rtc_alarm_irq_enable(struct device *dev, > + unsigned int enabled) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + > + if (enabled) > + return da9058_rtc_start_alarm(rtc); > + else > + return da9058_rtc_stop_alarm(rtc); > +} > + > +static irqreturn_t da9058_rtc_timer_alarm_handler(int irq, void *data) > +{ > + struct da9058_rtc *rtc = data; > + > + da9058_rtc_stop_alarm(rtc); > + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t da9058_rtc_tick_alarm_handler(int irq, void *data) > +{ > + struct da9058_rtc *rtc = data; > + > + rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); > + > + return IRQ_HANDLED; > +} > +static int da9058_rtc_proc(struct device *dev, struct seq_file *seq) > +{ > + struct da9058_rtc *rtc = dev_get_drvdata(dev); > + struct da9058 *da9058 = rtc->da9058; > + unsigned int rtc_ctrl; > + int ret; > + > + ret = da9058_reg_read(da9058, DA9058_ALARMY_REG, &rtc_ctrl); > + > + seq_printf(seq, rtc_ctrl & DA9058_ALARMY_ALARMON ? > + "ALRM is running\n" : "ALRM is not running\n"); > + > + return 0; > +} > + > +static const struct rtc_class_ops da9058_rtc_ops = { > + .read_time = da9058_rtc_readtime, > + .set_time = da9058_rtc_settime, > + .read_alarm = da9058_rtc_readalarm, > + .set_alarm = da9058_rtc_setalarm, > + .proc = da9058_rtc_proc, > + .alarm_irq_enable = da9058_rtc_alarm_irq_enable, > +}; > + > +static int da9058_rtc_probe(struct platform_device *pdev) > +{ > + struct da9058 *da9058 = dev_get_drvdata(pdev->dev.parent); > + const struct mfd_cell *cell = mfd_get_cell(pdev); > + struct da9058_rtc_pdata *rtc_pdata; > + struct da9058_rtc *rtc; > + int ret; > + > + if (cell == NULL) { > + ret = -ENODEV; > + goto exit; > + } > + > + rtc_pdata = cell->platform_data; > + > + if (rtc_pdata == NULL) { > + ret = -EINVAL; > + goto exit; > + } > + > + rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9058_rtc), GFP_KERNEL); > + if (!rtc) { > + ret = -ENOMEM; > + goto exit; > + } > + > + platform_set_drvdata(pdev, rtc); > + > + rtc->da9058 = da9058; > + rtc->pdev = pdev; > + ret = da9058_clear_bits(da9058, DA9058_WAITCONT_REG, > + DA9058_WAITCONT_RTCCLOCK); > + if (ret) { > + dev_err(&pdev->dev, "Failed to set RTC running: %d\n", ret); > + goto unable_to_init_device; > + } > + > + ret = da9058_set_bits(da9058, DA9058_COUNTY_REG, DA9058_COUNTY_MONITOR); > + if (ret) { > + dev_err(&pdev->dev, "Failed No gating RTC: %d\n", ret); > + goto unable_to_init_device; > + } > + > + device_init_wakeup(&pdev->dev, 1); > + > + rtc->rtc_dev = rtc_device_register("da9058", &pdev->dev, > + &da9058_rtc_ops, THIS_MODULE); > + if (IS_ERR(rtc->rtc_dev)) { > + ret = PTR_ERR(rtc->rtc_dev); > + dev_err(&pdev->dev, "failed to register RTC: %d\n", ret); > + goto unable_to_register_device; > + } > + > + rtc->alarm_irq = platform_get_irq(pdev, 0); > + if (rtc->alarm_irq < 0) { > + dev_err(&pdev->dev, "can not get RTC ALARM IRQ error=%d\n", > + rtc->alarm_irq); > + ret = -ENODEV; > + goto failed_to_get_alarm_irq; > + } > + > + ret = request_threaded_irq(da9058_to_virt_irq_num(da9058, > + rtc->alarm_irq), > + NULL, da9058_rtc_timer_alarm_handler, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + "DA9058 RTC Timer Alarm", rtc); > + > + if (ret) { > + dev_err(&pdev->dev, > + "Failed to get rtc timer alarm IRQ %d: %d\n", > + rtc->alarm_irq, ret); > + goto unable_to_setup_timer_irq; > + } > + > + rtc->tick_irq = platform_get_irq(pdev, 1); > + if (rtc->tick_irq < 0) { > + dev_err(&pdev->dev, "can not get RTC TICK IRQ error=%d\n", > + rtc->tick_irq); > + ret = -ENODEV; > + goto failed_to_get_tick_irq; > + } > + ret = request_threaded_irq(da9058_to_virt_irq_num(da9058, > + rtc->tick_irq), > + NULL, da9058_rtc_tick_alarm_handler, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + "DA9058 RTC Tick Alarm", rtc); > + if (ret) { > + dev_err(&pdev->dev, > + "Failed to get rtc timer alarm IRQ %d: %d\n", > + DA9058_IRQ_ETICK, ret); > + goto unable_to_setup_alarm_irq; > + } > + goto exit; > + > +failed_to_get_tick_irq: > +unable_to_setup_alarm_irq: > + free_irq(da9058_to_virt_irq_num(da9058, rtc->alarm_irq), rtc); > +failed_to_get_alarm_irq: > +unable_to_setup_timer_irq: > + rtc_device_unregister(rtc->rtc_dev); > +unable_to_register_device: > +unable_to_init_device: > + platform_set_drvdata(pdev, NULL); > +exit: > + return ret; > +} > + > +static int __devexit da9058_rtc_remove(struct platform_device *pdev) > +{ > + struct da9058_rtc *rtc = platform_get_drvdata(pdev); > + struct da9058 *da9058 = rtc->da9058; > + > + free_irq(da9058_to_virt_irq_num(da9058, rtc->alarm_irq), rtc); > + free_irq(da9058_to_virt_irq_num(da9058, rtc->tick_irq), rtc); > + > + rtc_device_unregister(rtc->rtc_dev); > + > + return 0; > +} > + > +static struct platform_driver da9058_rtc_driver = { > + .probe = da9058_rtc_probe, > + .remove = __devexit_p(da9058_rtc_remove), > + .driver = { > + .name = "da9058-rtc", > + .owner = THIS_MODULE, > + }, > +}; > + > +module_platform_driver(da9058_rtc_driver); > + > +MODULE_DESCRIPTION("Dialog DA9058 PMIC Real Time Clock Driver"); > +MODULE_AUTHOR("Anthony Olech "); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:da9058-rtc"); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/