lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 10 Aug 2009 20:55:35 +0200
From:	Alessandro Zummo <alessandro.zummo@...ertech.it>
To:	rtc-linux@...glegroups.com
Cc:	broonie@...nsource.wolfsonmicro.com,
	Samuel Ortiz <sameo@...ux.intel.com>,
	linux-kernel@...r.kernel.org,
	Alessandro Zummo <a.zummo@...ertech.it>
Subject: Re: [rtc-linux] [PATCH 4/5] RTC: Add support for RTCs on Wolfson
 WM831x devices

On Mon, 10 Aug 2009 17:43:54 +0100
Mark Brown <broonie@...nsource.wolfsonmicro.com> wrote:

> 
> The WM831x series of PMICs contain RTC functionality. The hardware
> provides a 32 bit counter incrementing at 1Hz together with a per
> tick interrupt and an alarm value. For simplicity the driver chooses
> to define the epoch for the counter as the Unix epoch - if required
> platform data can be used in future to customise this.

 [...]

 HI, comments below:


> Signed-off-by: Mark Brown <broonie@...nsource.wolfsonmicro.com>
> Cc: Alessandro Zummo <a.zummo@...ertech.it>
> Cc: rtc-linux@...glegroups.com
> ---
>  drivers/rtc/Kconfig      |   10 +
>  drivers/rtc/Makefile     |    1 +
>  drivers/rtc/rtc-wm831x.c |  538 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 549 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-wm831x.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 139b783..f595113 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -537,6 +537,16 @@ config RTC_DRV_V3020
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-v3020.
>  
> +config RTC_DRV_WM831X
> +	tristate "Wolfson Microelectronics WM831x RTC"
> +	depends on MFD_WM831X
> +	help
> +	  If you say yes here you will get support for the RTC subsystem
> +	  of the Wolfson Microelectronics WM831X series PMICs.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called "rtc-wm831x".
> +
>  config RTC_DRV_WM8350
>  	tristate "Wolfson Microelectronics WM8350 RTC"
>  	depends on MFD_WM8350
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 2a565f8..61d5600 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030)	+= rtc-twl4030.o
>  obj-$(CONFIG_RTC_DRV_TX4939)	+= rtc-tx4939.o
>  obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
> +obj-$(CONFIG_RTC_DRV_WM831X)	+= rtc-wm831x.o
>  obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
>  obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
> diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
> new file mode 100644
> index 0000000..99e7845
> --- /dev/null
> +++ b/drivers/rtc/rtc-wm831x.c
> @@ -0,0 +1,538 @@
> +/*
> + *	Real Time Clock driver for Wolfson Microelectronics WM831x
> + *
> + *	Copyright (C) 2009 Wolfson Microelectronics PLC.
> + *
> + *  Author: Mark Brown <broonie@...nsource.wolfsonmicro.com>
> + *
> + *  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/kernel.h>
> +#include <linux/time.h>
> +#include <linux/rtc.h>
> +#include <linux/bcd.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioctl.h>
> +#include <linux/completion.h>
> +#include <linux/mfd/wm831x/core.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +
> +
> +/*
> + * R16416 (0x4020) - RTC Write Counter
> + */
> +#define WM831X_RTC_WR_CNT_MASK                  0xFFFF  /* RTC_WR_CNT - [15:0] */
> +#define WM831X_RTC_WR_CNT_SHIFT                      0  /* RTC_WR_CNT - [15:0] */
> +#define WM831X_RTC_WR_CNT_WIDTH                     16  /* RTC_WR_CNT - [15:0] */
> +
> +/*
> + * R16417 (0x4021) - RTC Time 1
> + */
> +#define WM831X_RTC_TIME_MASK                    0xFFFF  /* RTC_TIME - [15:0] */
> +#define WM831X_RTC_TIME_SHIFT                        0  /* RTC_TIME - [15:0] */
> +#define WM831X_RTC_TIME_WIDTH                       16  /* RTC_TIME - [15:0] */
> +
> +/*
> + * R16418 (0x4022) - RTC Time 2
> + */
> +#define WM831X_RTC_TIME_MASK                    0xFFFF  /* RTC_TIME - [15:0] */
> +#define WM831X_RTC_TIME_SHIFT                        0  /* RTC_TIME - [15:0] */
> +#define WM831X_RTC_TIME_WIDTH                       16  /* RTC_TIME - [15:0] */
> +
> +/*
> + * R16419 (0x4023) - RTC Alarm 1
> + */
> +#define WM831X_RTC_ALM_MASK                     0xFFFF  /* RTC_ALM - [15:0] */
> +#define WM831X_RTC_ALM_SHIFT                         0  /* RTC_ALM - [15:0] */
> +#define WM831X_RTC_ALM_WIDTH                        16  /* RTC_ALM - [15:0] */
> +
> +/*
> + * R16420 (0x4024) - RTC Alarm 2
> + */
> +#define WM831X_RTC_ALM_MASK                     0xFFFF  /* RTC_ALM - [15:0] */
> +#define WM831X_RTC_ALM_SHIFT                         0  /* RTC_ALM - [15:0] */
> +#define WM831X_RTC_ALM_WIDTH                        16  /* RTC_ALM - [15:0] */
> +
> +/*
> + * R16421 (0x4025) - RTC Control
> + */
> +#define WM831X_RTC_VALID                        0x8000  /* RTC_VALID */
> +#define WM831X_RTC_VALID_MASK                   0x8000  /* RTC_VALID */
> +#define WM831X_RTC_VALID_SHIFT                      15  /* RTC_VALID */
> +#define WM831X_RTC_VALID_WIDTH                       1  /* RTC_VALID */
> +#define WM831X_RTC_SYNC_BUSY                    0x4000  /* RTC_SYNC_BUSY */
> +#define WM831X_RTC_SYNC_BUSY_MASK               0x4000  /* RTC_SYNC_BUSY */
> +#define WM831X_RTC_SYNC_BUSY_SHIFT                  14  /* RTC_SYNC_BUSY */
> +#define WM831X_RTC_SYNC_BUSY_WIDTH                   1  /* RTC_SYNC_BUSY */
> +#define WM831X_RTC_ALM_ENA                      0x0400  /* RTC_ALM_ENA */
> +#define WM831X_RTC_ALM_ENA_MASK                 0x0400  /* RTC_ALM_ENA */
> +#define WM831X_RTC_ALM_ENA_SHIFT                    10  /* RTC_ALM_ENA */
> +#define WM831X_RTC_ALM_ENA_WIDTH                     1  /* RTC_ALM_ENA */
> +#define WM831X_RTC_PINT_FREQ_MASK               0x0070  /* RTC_PINT_FREQ - [6:4] */
> +#define WM831X_RTC_PINT_FREQ_SHIFT                   4  /* RTC_PINT_FREQ - [6:4] */
> +#define WM831X_RTC_PINT_FREQ_WIDTH                   3  /* RTC_PINT_FREQ - [6:4] */
> +
> +/*
> + * R16422 (0x4026) - RTC Trim
> + */
> +#define WM831X_RTC_TRIM_MASK                    0x03FF  /* RTC_TRIM - [9:0] */
> +#define WM831X_RTC_TRIM_SHIFT                        0  /* RTC_TRIM - [9:0] */
> +#define WM831X_RTC_TRIM_WIDTH                       10  /* RTC_TRIM - [9:0] */
> +
> +#define WM831X_SET_TIME_RETRIES	5
> +#define WM831X_GET_TIME_RETRIES	5

 given you have your own include directory undef mfd/
 you might want to move those #defines there


> +struct wm831x_rtc {
> +	struct wm831x *wm831x;
> +	struct rtc_device *rtc;
> +	int alarm_enabled;
> +	int per_irq;

 are those tows int or unsigned int? 
 or maybe alarm_enabled could be :1 ?

> +};
> +
> +/*
> + * Read current time and date in RTC
> + */
> +static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
> +{
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +	struct wm831x *wm831x = wm831x_rtc->wm831x;
> +	u16 time1[2], time2[2];
> +	int ret;
> +	int count = 0;
> +
> +	/* Has the RTC been programmed? */
> +	ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read RTC control: %d\n", ret);
> +		return ret;
> +	}
> +	if (!(ret & WM831X_RTC_VALID)) {
> +		dev_dbg(dev, "RTC not yet configured\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Read twice to make sure we don't read a corrupt, partially
> +	 * incremented, value.
> +	 */
> +	do {
> +		ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
> +				       2, time1);
> +		if (ret != 0)
> +			continue;
> +
> +		ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
> +				       2, time2);
> +		if (ret != 0)
> +			continue;
> +
> +		if (memcmp(time1, time2, sizeof(time1)) == 0) {
> +			u32 time = (time1[0] << 16) | time1[1];
> +
> +			rtc_time_to_tm(time, tm);

 use return rtc_valid_tm(..

> +			return 0;
> +		}
> +
> +	} while (++count < WM831X_GET_TIME_RETRIES);
> +
> +	dev_err(dev, "Timed out reading current time\n");
> +
> +	return -EIO;
> +}
> +
> +/*
> + * Set current time and date in RTC
> + */
> +static int wm831x_rtc_settime(struct device *dev, struct rtc_time *tm)

 isn't rtc_set_mmss more appropriate?

> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +	struct wm831x *wm831x = wm831x_rtc->wm831x;
> +	struct rtc_time new_tm;
> +	unsigned long time, new_time;
> +	int ret;
> +	int count = 0;
> +
> +	ret = rtc_tm_to_time(tm, &time);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to convert time: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
> +			       (time >> 16) & 0xffff);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write TIME_1: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write TIME_2: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Wait for the update to complete - should happen first time
> +	 * round but be conservative.
> +	 */
> +	do {
> +		msleep(1);
> +
> +		ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
> +		if (ret < 0)
> +			ret = WM831X_RTC_SYNC_BUSY;
> +	} while (!(ret & WM831X_RTC_SYNC_BUSY) &&
> +		 ++count < WM831X_SET_TIME_RETRIES);
> +
> +	if (ret & WM831X_RTC_SYNC_BUSY) {
> +		dev_err(dev, "Timed out writing RTC update\n");
> +		return -EIO;
> +	}
> +
> +	/* Check that the update was accepted; security features may
> +	 * have caused the update to be ignored.
> +	 */
> +	ret = wm831x_rtc_readtime(dev, &new_tm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = rtc_tm_to_time(&new_tm, &new_time);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to convert time: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Allow a second of change in case of tick */
> +	if (new_time - time > 1) {
> +		dev_err(dev, "RTC update not permitted by hardware\n");
> +		return -EPERM;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Read alarm time and date in RTC
> + */
> +static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +	int ret;
> +	u16 data[2];
> +	u32 time;
> +
> +	ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1,
> +			       2, data);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to read alarm time: %d\n", ret);
> +		return ret;
> +	}
> +
> +	time = (data[0] << 16) | data[1];
> +
> +	rtc_time_to_tm(time, &alrm->time);
> +
> +	ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to read RTC control: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (ret & WM831X_RTC_ALM_ENA)
> +		alrm->enabled = 1;
> +	else
> +		alrm->enabled = 0;
> +
> +	return 0;
> +}
> +
> +static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc)
> +{
> +	wm831x_rtc->alarm_enabled = 0;
> +
> +	return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
> +			       WM831X_RTC_ALM_ENA, 0);
> +}
> +
> +static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc)
> +{
> +	wm831x_rtc->alarm_enabled = 1;
> +
> +	return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
> +			       WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA);
> +}
> +
> +static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +	struct wm831x *wm831x = wm831x_rtc->wm831x;
> +	int ret;
> +	unsigned long time;
> +
> +	ret = rtc_tm_to_time(&alrm->time, &time);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to convert time: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wm831x_rtc_stop_alarm(wm831x_rtc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to stop alarm: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1,
> +			       (time >> 16) & 0xffff);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write ALARM_1: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to write ALARM_2: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (alrm->enabled) {
> +		ret = wm831x_rtc_start_alarm(wm831x_rtc);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to start alarm: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int wm831x_rtc_alarm_irq_enable(struct device *dev,
> +				       unsigned int enabled)
> +{
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +
> +	if (enabled)
> +		return wm831x_rtc_start_alarm(wm831x_rtc);
> +	else
> +		return wm831x_rtc_stop_alarm(wm831x_rtc);
> +}
> +
> +static int wm831x_rtc_update_irq_enable(struct device *dev,
> +					unsigned int enabled)
> +{
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
> +	int val;
> +
> +	if (enabled)
> +		val = 1 << WM831X_RTC_PINT_FREQ_SHIFT;
> +	else
> +		val = 0;
> +
> +	return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
> +			       WM831X_RTC_PINT_FREQ_MASK, val);
> +}
> +
> +static irqreturn_t wm831x_alm_irq(int irq, void *data)
> +{
> +	struct wm831x_rtc *wm831x_rtc = data;
> +
> +	rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t wm831x_per_irq(int irq, void *data)
> +{
> +	struct wm831x_rtc *wm831x_rtc = data;
> +
> +	rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_UF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct rtc_class_ops wm831x_rtc_ops = {
> +	.read_time = wm831x_rtc_readtime,
> +	.set_time = wm831x_rtc_settime,
> +	.read_alarm = wm831x_rtc_readalarm,
> +	.set_alarm = wm831x_rtc_setalarm,
> +	.alarm_irq_enable = wm831x_rtc_alarm_irq_enable,
> +	.update_irq_enable = wm831x_rtc_update_irq_enable,
> +};
> +
> +#ifdef CONFIG_PM
> +/* Turn off the alarm if it should not be a wake source. */
> +static int wm831x_rtc_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
> +	int ret, enable;
> +
> +	if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
> +		enable = WM831X_RTC_ALM_ENA;
> +	else
> +		enable = 0;
> +
> +	ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
> +			      WM831X_RTC_ALM_ENA, enable);
> +	if (ret != 0)
> +		dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
> +
> +	return 0;

 always 0 ? (also below..)

> +}
> +
> +/* Enable the alarm if it should be enabled (in case it was disabled to
> + * prevent use as a wake source).
> + */
> +static int wm831x_rtc_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +
> +	if (wm831x_rtc->alarm_enabled) {
> +		ret = wm831x_rtc_start_alarm(wm831x_rtc);
> +		if (ret != 0)
> +			dev_err(&pdev->dev,
> +				"Failed to restart RTC alarm: %d\n", ret);
> +	}
> +
> +	return 0;
> +}
> +
> +/* Unconditionally disable the alarm */
> +static int wm831x_rtc_freeze(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +
> +	ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
> +			      WM831X_RTC_ALM_ENA, 0);
> +	if (ret != 0)
> +		dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
> +
> +	return 0;
> +}
> +#else
> +#define wm831x_rtc_suspend NULL
> +#define wm831x_rtc_resume NULL
> +#define wm831x_rtc_freeze NULL
> +#endif
> +
> +static int wm831x_rtc_probe(struct platform_device *pdev)
> +{
> +	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
> +	struct wm831x_rtc *wm831x_rtc;
> +	int per_irq = platform_get_irq_byname(pdev, "PER");
> +	int alm_irq = platform_get_irq_byname(pdev, "ALM");
> +	int ret = 0;
> +
> +	wm831x_rtc = kzalloc(sizeof(*wm831x_rtc), GFP_KERNEL);
> +	if (wm831x_rtc == NULL)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, wm831x_rtc);
> +	wm831x_rtc->wm831x = wm831x;
> +	wm831x_rtc->per_irq = per_irq;
> +
> +	ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
> +		goto err;
> +	}
> +	if (ret & WM831X_RTC_ALM_ENA)
> +		wm831x_rtc->alarm_enabled = 1;
> +
> +	device_init_wakeup(&pdev->dev, 1);
> +
> +	wm831x_rtc->rtc = rtc_device_register("wm831x", &pdev->dev,
> +					      &wm831x_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(wm831x_rtc->rtc)) {
> +		ret = PTR_ERR(wm831x_rtc->rtc);
> +		dev_err(&pdev->dev, "Failed to register RTC: %d\n", ret);

 useless, rtc core will emit his own message.

> +		goto err;
> +	}
> +
> +	ret = wm831x_request_irq(wm831x, per_irq, wm831x_per_irq,
> +				 IRQF_TRIGGER_RISING, "wm831x_rtc_per",
> +				 wm831x_rtc);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n",
> +			per_irq, ret);

 can't the rtc work without the periodic irq?

> +		goto err_rtc;
> +	}
> +
> +	ret = wm831x_request_irq(wm831x, alm_irq, wm831x_alm_irq,
> +				 IRQF_TRIGGER_RISING, "wm831x_rtc_alm",
> +				 wm831x_rtc);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
> +			alm_irq, ret);

 ditto.

> +		goto err_per;
> +	}
> +
> +	return 0;
> +
> +err_per:
> +	wm831x_free_irq(wm831x, per_irq, wm831x_rtc);
> +err_rtc:
> +	rtc_device_unregister(wm831x_rtc->rtc);
> +err:
> +	kfree(wm831x_rtc);
> +	return ret;
> +}
> +
> +static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
> +{
> +	struct wm831x_rtc *wm831x_rtc = platform_get_drvdata(pdev);
> +	int per_irq = platform_get_irq(pdev, 0);
> +	int alm_irq = platform_get_irq(pdev, 1);

 why not _byname() ?

> +
> +	wm831x_free_irq(wm831x_rtc->wm831x, alm_irq, wm831x_rtc);
> +	wm831x_free_irq(wm831x_rtc->wm831x, per_irq, wm831x_rtc);
> +	rtc_device_unregister(wm831x_rtc->rtc);
> +	kfree(wm831x_rtc);
> +
> +	return 0;
> +}
> +
> +static struct dev_pm_ops wm831x_rtc_pm_ops = {
> +	.suspend = wm831x_rtc_suspend,
> +	.resume = wm831x_rtc_resume,
> +
> +	.freeze = wm831x_rtc_freeze,
> +	.thaw = wm831x_rtc_resume,
> +	.restore = wm831x_rtc_resume,
> +
> +	.poweroff = wm831x_rtc_suspend,
> +};
> +
> +static struct platform_driver wm831x_rtc_driver = {
> +	.probe = wm831x_rtc_probe,
> +	.remove = __devexit_p(wm831x_rtc_remove),
> +	.driver = {
> +		.name = "wm831x-rtc",
> +		.pm = &wm831x_rtc_pm_ops,
> +	},
> +};
> +
> +static int __init wm831x_rtc_init(void)
> +{
> +	return platform_driver_register(&wm831x_rtc_driver);

 can you use platform_driver_probe() ?

> +}
> +module_init(wm831x_rtc_init);
> +
> +static void __exit wm831x_rtc_exit(void)
> +{
> +	platform_driver_unregister(&wm831x_rtc_driver);
> +}
> +module_exit(wm831x_rtc_exit);
> +
> +MODULE_AUTHOR("Mark Brown <broonie@...nsource.wolfsonmicro.com>");
> +MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:wm831x-rtc");
> -- 
> 1.6.3.3
> 
> 
> --~--~---------~--~----~------------~-------~--~----~
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
> -~----------~----~----~----~------~----~------~--~---
> 


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ