Re: [NEW DRIVER V3 5/8] DA9058 RTC driver

From: Lars-Peter Clausen
Date: Wed Aug 15 2012 - 14:36:27 EST


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 <anthony.olech.opensource@xxxxxxxxxxx>
> Signed-off-by: David Dajun Chen <david.chen@xxxxxxxxxxx>
> ---
> 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 <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/core.h>
> +
> +#include <linux/mfd/da9058/version.h>
> +#include <linux/mfd/da9058/registers.h>
> +#include <linux/mfd/da9058/core.h>
> +#include <linux/mfd/da9058/irq.h>
> +#include <linux/mfd/da9058/rtc.h>
> +
> +/*
> + * 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 <Anthony.Olech@xxxxxxxxxxx>");
> +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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/