diff mbox

[2/2] rtc: mediatek: Add MT63xx RTC driver

Message ID 1422437276-41334-3-git-send-email-eddie.huang@mediatek.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eddie Huang (黃智傑) Jan. 28, 2015, 9:27 a.m. UTC
From: Tianping Fang <tianping.fang@mediatek.com>

Add Mediatek MT63xx RTC driver

Signed-off-by: Tianping Fang <tianping.fang@mediatek.com>
Signed-off-by: Eddie Huang <eddie.huang@mediatek.com>
---
 drivers/rtc/Kconfig      |  10 ++
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-mt6397.c | 352 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 363 insertions(+)
 create mode 100644 drivers/rtc/rtc-mt6397.c

Comments

Andrew Morton Feb. 23, 2015, 9:50 p.m. UTC | #1
On Wed, 28 Jan 2015 17:27:56 +0800 Eddie Huang <eddie.huang@mediatek.com> wrote:

> From: Tianping Fang <tianping.fang@mediatek.com>
> 
> Add Mediatek MT63xx RTC driver

There are a couple of checkpatch warnings which should be addressed,
please:

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#150: 
new file mode 100644

WARNING: DT compatible string "mediatek,mt6397-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/
#488: FILE: drivers/rtc/rtc-mt6397.c:334:
+	{ .compatible = "mediatek,mt6397-rtc", },




>
> ...
>
> +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
> +{
> +	u32 rdata = 0;
> +	u32 addr = rtc->addr_base + offset;
> +
> +	if (offset < rtc->addr_range)
> +		regmap_read(rtc->regmap, addr, &rdata);
> +
> +	return (u16)rdata;
> +}
> +
> +static void rtc_write(struct mt6397_rtc *rtc, u32 offset, u32 data)
> +{
> +	u32 addr;
> +
> +	addr = rtc->addr_base + offset;
> +
> +	if (offset < rtc->addr_range)
> +		regmap_write(rtc->regmap, addr, data);
> +}

regmap_read() and regmap_write() can return errors.  There is no
checking for this.

> +static void rtc_write_trigger(struct mt6397_rtc *rtc)
> +{
> +	rtc_write(rtc, RTC_WRTGR, 1);
> +	while (rtc_read(rtc, RTC_BBPU) & RTC_BBPU_CBUSY)
> +		cpu_relax();
> +}
> +
>
> ...
>
> +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +	struct rtc_time *tm = &alm->time;
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +	u16 irqen, pdn2;
> +
> +	mutex_lock(&rtc->lock);
> +	irqen = rtc_read(rtc, RTC_IRQ_EN);
> +	pdn2 = rtc_read(rtc, RTC_PDN2);
> +	tm->tm_sec  = rtc_read(rtc, RTC_AL_SEC);
> +	tm->tm_min  = rtc_read(rtc, RTC_AL_MIN);
> +	tm->tm_hour = rtc_read(rtc, RTC_AL_HOU) & RTC_AL_HOU_MASK;
> +	tm->tm_mday = rtc_read(rtc, RTC_AL_DOM) & RTC_AL_DOM_MASK;
> +	tm->tm_mon  = rtc_read(rtc, RTC_AL_MTH) & RTC_AL_MTH_MASK;
> +	tm->tm_year = rtc_read(rtc, RTC_AL_YEA);
> +	mutex_unlock(&rtc->lock);
> +
> +	alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
> +	alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
> +
> +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon--;
> +
> +	return 0;
> +}
> +
> +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +	struct rtc_time *tm = &alm->time;
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +	u16 irqen;
> +
> +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon++;
> +
> +	if (alm->enabled) {
> +		mutex_lock(&rtc->lock);
> +		rtc_write(rtc, RTC_AL_YEA, tm->tm_year);
> +		rtc_write(rtc, RTC_AL_MTH, (rtc_read(rtc, RTC_AL_MTH) &
> +				RTC_NEW_SPARE3) | tm->tm_mon);
> +		rtc_write(rtc, RTC_AL_DOM, (rtc_read(rtc, RTC_AL_DOM) &
> +				RTC_NEW_SPARE1) | tm->tm_mday);
> +		rtc_write(rtc, RTC_AL_HOU, (rtc_read(rtc, RTC_AL_HOU) &
> +				RTC_NEW_SPARE_FG_MASK) | tm->tm_hour);
> +		rtc_write(rtc, RTC_AL_MIN, tm->tm_min);
> +		rtc_write(rtc, RTC_AL_SEC, tm->tm_sec);
> +		rtc_write(rtc, RTC_AL_MASK, RTC_AL_MASK_DOW);
> +		rtc_write_trigger(rtc);
> +		irqen = rtc_read(rtc, RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
> +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> +		rtc_write_trigger(rtc);
> +		mutex_unlock(&rtc->lock);
> +	}
> +
> +	return 0;
> +}

This all looks a bit racy.  Wouldn't it be better if the testing of and
modification of ->enabled and ->pending were protected by the mutex?

>
> ...
>
> +static int mtk_rtc_probe(struct platform_device *pdev)
> +{
> +	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
> +	struct mt6397_rtc *rtc;
> +	u32 reg[2];
> +	int ret = 0;
> +
> +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
> +	if (!rtc)
> +		return -ENOMEM;
> +
> +	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2);
> +	if (ret) {
> +		dev_err(&pdev->dev, "couldn't read rtc base address!\n");
> +		return -EINVAL;
> +	}
> +	rtc->addr_base = reg[0];
> +	rtc->addr_range = reg[1];
> +	rtc->regmap = mt6397_chip->regmap;
> +	rtc->dev = &pdev->dev;
> +	mutex_init(&rtc->lock);
> +
> +	platform_set_drvdata(pdev, rtc);
> +
> +	rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
> +				&mtk_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc->rtc_dev)) {
> +		dev_err(&pdev->dev, "register rtc device failed\n");
> +		return PTR_ERR(rtc->rtc_dev);
> +	}
> +
> +	rtc->irq = platform_get_irq(pdev, 0);
> +	if (rtc->irq < 0) {
> +		ret = rtc->irq;
> +		goto out_rtc;
> +	}
> +
> +	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
> +			rtc_irq_handler_thread, IRQF_ONESHOT,
> +			"mt6397-rtc", rtc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
> +			rtc->irq, ret);
> +		goto out_rtc;
> +	}
> +
> +	device_init_wakeup(&pdev->dev, 1);
> +
> +	return 0;
> +
> +out_rtc:
> +	rtc_device_unregister(rtc->rtc_dev);
> +	return ret;
> +
> +}

It seems strange to request the IRQ after having registered the rtc. 
And possibly racy - I seem to recall another driver having issues with
this recently.

A lot of rtc drivers are requesting the IRQ after registration so
presumably it isn't a huge problem.  But still, wouldn't it be better
to defer registration until after the IRQ has been successfully
obtained?

>
> ...
>
Eddie Huang (黃智傑) March 2, 2015, 8:20 a.m. UTC | #2
Hi Andrew,

On Mon, 2015-02-23 at 13:50 -0800, Andrew Morton wrote:
> On Wed, 28 Jan 2015 17:27:56 +0800 Eddie Huang <eddie.huang@mediatek.com> wrote:
> 
> > From: Tianping Fang <tianping.fang@mediatek.com>
> > 
> > Add Mediatek MT63xx RTC driver
> 
> There are a couple of checkpatch warnings which should be addressed,
> please:
> 
> WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
> #150: 
> new file mode 100644
> 
OK, I will update MAINTAINERS file

> WARNING: DT compatible string "mediatek,mt6397-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/
> #488: FILE: drivers/rtc/rtc-mt6397.c:334:
> +	{ .compatible = "mediatek,mt6397-rtc", },
> 
> 
Do you include patch "[PATCH 1/2] dt: bindings: Add Mediatek RTC driver
binding document" in this series ?
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-January/320425.html

I think this warning shouldn't happen if include this patch.


> >
> > ...
> >
> > +static void rtc_write(struct mt6397_rtc *rtc, u32 offset, u32 data)
> > +{
> > +	u32 addr;
> > +
> > +	addr = rtc->addr_base + offset;
> > +
> > +	if (offset < rtc->addr_range)
> > +		regmap_write(rtc->regmap, addr, data);
> > +}
> 
> regmap_read() and regmap_write() can return errors.  There is no
> checking for this.
Will fix it.

> > ...
> >
> > +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
> > +{
> > +	struct rtc_time *tm = &alm->time;
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +	u16 irqen, pdn2;
> > +
> > +	mutex_lock(&rtc->lock);
> > +	irqen = rtc_read(rtc, RTC_IRQ_EN);
> > +	pdn2 = rtc_read(rtc, RTC_PDN2);
> > +	tm->tm_sec  = rtc_read(rtc, RTC_AL_SEC);
> > +	tm->tm_min  = rtc_read(rtc, RTC_AL_MIN);
> > +	tm->tm_hour = rtc_read(rtc, RTC_AL_HOU) & RTC_AL_HOU_MASK;
> > +	tm->tm_mday = rtc_read(rtc, RTC_AL_DOM) & RTC_AL_DOM_MASK;
> > +	tm->tm_mon  = rtc_read(rtc, RTC_AL_MTH) & RTC_AL_MTH_MASK;
> > +	tm->tm_year = rtc_read(rtc, RTC_AL_YEA);
> > +	mutex_unlock(&rtc->lock);
> > +
> > +	alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
> > +	alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
> > +
> > +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon--;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
> > +{
> > +	struct rtc_time *tm = &alm->time;
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +	u16 irqen;
> > +
> > +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon++;
> > +
> > +	if (alm->enabled) {
> > +		mutex_lock(&rtc->lock);
> > +		rtc_write(rtc, RTC_AL_YEA, tm->tm_year);
> > +		rtc_write(rtc, RTC_AL_MTH, (rtc_read(rtc, RTC_AL_MTH) &
> > +				RTC_NEW_SPARE3) | tm->tm_mon);
> > +		rtc_write(rtc, RTC_AL_DOM, (rtc_read(rtc, RTC_AL_DOM) &
> > +				RTC_NEW_SPARE1) | tm->tm_mday);
> > +		rtc_write(rtc, RTC_AL_HOU, (rtc_read(rtc, RTC_AL_HOU) &
> > +				RTC_NEW_SPARE_FG_MASK) | tm->tm_hour);
> > +		rtc_write(rtc, RTC_AL_MIN, tm->tm_min);
> > +		rtc_write(rtc, RTC_AL_SEC, tm->tm_sec);
> > +		rtc_write(rtc, RTC_AL_MASK, RTC_AL_MASK_DOW);
> > +		rtc_write_trigger(rtc);
> > +		irqen = rtc_read(rtc, RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
> > +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> > +		rtc_write_trigger(rtc);
> > +		mutex_unlock(&rtc->lock);
> > +	}
> > +
> > +	return 0;
> > +}
> 
> This all looks a bit racy.  Wouldn't it be better if the testing of and
> modification of ->enabled and ->pending were protected by the mutex?
> 
Will fix it.

> >
> > ...
> >
> > +static int mtk_rtc_probe(struct platform_device *pdev)
> > +{
> > +	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
> > +	struct mt6397_rtc *rtc;
> > +	u32 reg[2];
> > +	int ret = 0;
> > +
> > +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
> > +	if (!rtc)
> > +		return -ENOMEM;
> > +
> > +	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "couldn't read rtc base address!\n");
> > +		return -EINVAL;
> > +	}
> > +	rtc->addr_base = reg[0];
> > +	rtc->addr_range = reg[1];
> > +	rtc->regmap = mt6397_chip->regmap;
> > +	rtc->dev = &pdev->dev;
> > +	mutex_init(&rtc->lock);
> > +
> > +	platform_set_drvdata(pdev, rtc);
> > +
> > +	rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
> > +				&mtk_rtc_ops, THIS_MODULE);
> > +	if (IS_ERR(rtc->rtc_dev)) {
> > +		dev_err(&pdev->dev, "register rtc device failed\n");
> > +		return PTR_ERR(rtc->rtc_dev);
> > +	}
> > +
> > +	rtc->irq = platform_get_irq(pdev, 0);
> > +	if (rtc->irq < 0) {
> > +		ret = rtc->irq;
> > +		goto out_rtc;
> > +	}
> > +
> > +	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
> > +			rtc_irq_handler_thread, IRQF_ONESHOT,
> > +			"mt6397-rtc", rtc);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
> > +			rtc->irq, ret);
> > +		goto out_rtc;
> > +	}
> > +
> > +	device_init_wakeup(&pdev->dev, 1);
> > +
> > +	return 0;
> > +
> > +out_rtc:
> > +	rtc_device_unregister(rtc->rtc_dev);
> > +	return ret;
> > +
> > +}
> 
> It seems strange to request the IRQ after having registered the rtc. 
> And possibly racy - I seem to recall another driver having issues with
> this recently.
> 
> A lot of rtc drivers are requesting the IRQ after registration so
> presumably it isn't a huge problem.  But still, wouldn't it be better
> to defer registration until after the IRQ has been successfully
> obtained?
> 
OK, will fix it.

Eddie
Andrew Morton March 2, 2015, 7:35 p.m. UTC | #3
On Mon, 2 Mar 2015 16:20:36 +0800 Eddie Huang <eddie.huang@mediatek.com> wrote:

> > WARNING: DT compatible string "mediatek,mt6397-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/
> > #488: FILE: drivers/rtc/rtc-mt6397.c:334:
> > +	{ .compatible = "mediatek,mt6397-rtc", },
> > 
> > 
> Do you include patch "[PATCH 1/2] dt: bindings: Add Mediatek RTC driver
> binding document" in this series ?
> http://lists.infradead.org/pipermail/linux-arm-kernel/2015-January/320425.html
> 
> I think this warning shouldn't happen if include this patch.

I can't find that patch on rtc-linux or linux-kernel.  Resend, please?
Eddie Huang (黃智傑) March 13, 2015, 10:29 a.m. UTC | #4
Hi,

On Mon, 2015-02-23 at 13:50 -0800, Andrew Morton wrote:
> On Wed, 28 Jan 2015 17:27:56 +0800 Eddie Huang <eddie.huang@mediatek.com> wrote:
> 
> > From: Tianping Fang <tianping.fang@mediatek.com>
> > 
> > Add Mediatek MT63xx RTC driver
> 
> There are a couple of checkpatch warnings which should be addressed,
> please:
> 
> WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
> #150: 
> new file mode 100644
> 
> WARNING: DT compatible string "mediatek,mt6397-rtc" appears un-documented -- check ./Documentation/devicetree/bindings/
> #488: FILE: drivers/rtc/rtc-mt6397.c:334:
> +	{ .compatible = "mediatek,mt6397-rtc", },
> 
> 
> 
> 
> >
> > ...
> >
> > +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
> > +{
> > +	u32 rdata = 0;
> > +	u32 addr = rtc->addr_base + offset;
> > +
> > +	if (offset < rtc->addr_range)
> > +		regmap_read(rtc->regmap, addr, &rdata);
> > +
> > +	return (u16)rdata;
> > +}
> > +
> > +static void rtc_write(struct mt6397_rtc *rtc, u32 offset, u32 data)
> > +{
> > +	u32 addr;
> > +
> > +	addr = rtc->addr_base + offset;
> > +
> > +	if (offset < rtc->addr_range)
> > +		regmap_write(rtc->regmap, addr, data);
> > +}
> 
> regmap_read() and regmap_write() can return errors.  There is no
> checking for this.
> 

I encounter some trouble when I add code to check return value of
regmap_read and regmap_write. Every RTC register access through regmap,
and there are many register read/write in this driver. If I check every
return value, the driver will become ugly. I try to make this driver
clean using following macro.

static int __rtc_read(struct mt6397_rtc *rtc, u32 offset, u32 *data)
{
        u32 addr = rtc->addr_base + offset;

        if (offset < rtc->addr_range)
                return regmap_read(rtc->regmap, addr, data);

        return -EINVAL;
}

#define rtc_read(ret, rtc, offset, data)                \
({                                                      \
        ret = __rtc_read(rtc, offset, data);            \
        if (ret < 0)                                    \
                goto rtc_exit;                          \
})                                                      \


And function call rtc_read, rtc_write looks like:

static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
        unsigned long time;
        struct mt6397_rtc *rtc = dev_get_drvdata(dev);
        int ret = 0;
        u32 sec;

        mutex_lock(&rtc->lock);
        do {
                rtc_read(ret, rtc, RTC_TC_SEC, &tm->tm_sec);
                rtc_read(ret, rtc, RTC_TC_MIN, &tm->tm_min);
                rtc_read(ret, rtc, RTC_TC_HOU, &tm->tm_hour);
                rtc_read(ret, rtc, RTC_TC_DOM, &tm->tm_mday);
                rtc_read(ret, rtc, RTC_TC_MTH, &tm->tm_mon);
                rtc_read(ret, rtc, RTC_TC_YEA, &tm->tm_year);
                rtc_read(ret, rtc, RTC_TC_SEC, &sec);
        } while (sec < tm->tm_sec);
        mutex_unlock(&rtc->lock);

        tm->tm_year += RTC_MIN_YEAR_OFFSET;
        tm->tm_mon--;
        rtc_tm_to_time(tm, &time);

        tm->tm_wday = (time / 86400 + 4) % 7;

        return 0;

rtc_exit:
        mutex_unlock(&rtc->lock);
        return ret;
}

It's a little tricky, does anyone have good suggestion ?

Eddie
Sascha Hauer March 13, 2015, 10:57 a.m. UTC | #5
Hi Eddie,

On Fri, Mar 13, 2015 at 06:29:23PM +0800, Eddie Huang wrote:
> > regmap_read() and regmap_write() can return errors.  There is no
> > checking for this.
> > 
> 
> I encounter some trouble when I add code to check return value of
> regmap_read and regmap_write. Every RTC register access through regmap,
> and there are many register read/write in this driver. If I check every
> return value, the driver will become ugly. I try to make this driver
> clean using following macro.
> 
> static int __rtc_read(struct mt6397_rtc *rtc, u32 offset, u32 *data)
> {
>         u32 addr = rtc->addr_base + offset;
> 
>         if (offset < rtc->addr_range)
>                 return regmap_read(rtc->regmap, addr, data);
> 
>         return -EINVAL;
> }
> 
> #define rtc_read(ret, rtc, offset, data)                \
> ({                                                      \
>         ret = __rtc_read(rtc, offset, data);            \
>         if (ret < 0)                                    \
>                 goto rtc_exit;                          \
> })                                                      \

Hiding a goto (or return) in a macro is a very bad idea.

what you can do is

	ret |= regmap_read(rtc->regmap, RTC_TC_SEC, &tm->tm_sec);
	ret |= regmap_read(rtc->regmap, RTC_TC_MIN, &tm->tm_min);

	if (ret)
		return -EIO;

(Don't return ret in this case though as it might contain different
error codes orred together)

Another possibilty at least for contiguous registers would be
regmap_bulk_read().

Sascha
Matthias Brugger March 13, 2015, 11:19 a.m. UTC | #6
On 13/03/15 11:29, Eddie Huang wrote:
> 
> I encounter some trouble when I add code to check return value of
> regmap_read and regmap_write. Every RTC register access through regmap,
> and there are many register read/write in this driver. If I check every
> return value, the driver will become ugly. I try to make this driver
> clean using following macro.
> 
> static int __rtc_read(struct mt6397_rtc *rtc, u32 offset, u32 *data)
> {
>         u32 addr = rtc->addr_base + offset;
> 
>         if (offset < rtc->addr_range)
>                 return regmap_read(rtc->regmap, addr, data);
> 
>         return -EINVAL;
> }
> 
> #define rtc_read(ret, rtc, offset, data)                \
> ({                                                      \
>         ret = __rtc_read(rtc, offset, data);            \
>         if (ret < 0)                                    \
>                 goto rtc_exit;                          \
> })                                                      \
> 

I agree with Sascha on hiding a goto statement in a macro is not a good idea.

> 
> And function call rtc_read, rtc_write looks like:
> 
> static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
> {
>         unsigned long time;
>         struct mt6397_rtc *rtc = dev_get_drvdata(dev);
>         int ret = 0;
>         u32 sec;
> 
>         mutex_lock(&rtc->lock);
>         do {
>                 rtc_read(ret, rtc, RTC_TC_SEC, &tm->tm_sec);
>                 rtc_read(ret, rtc, RTC_TC_MIN, &tm->tm_min);
>                 rtc_read(ret, rtc, RTC_TC_HOU, &tm->tm_hour);
>                 rtc_read(ret, rtc, RTC_TC_DOM, &tm->tm_mday);
>                 rtc_read(ret, rtc, RTC_TC_MTH, &tm->tm_mon);
>                 rtc_read(ret, rtc, RTC_TC_YEA, &tm->tm_year);
>                 rtc_read(ret, rtc, RTC_TC_SEC, &sec);
>         } while (sec < tm->tm_sec);

What about introducing
static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, struct rtc_time *tm, u32 *sec)
and hide the checks of return values from regmap_read and the offset check in there. You return the error code or 0.

This way the while loop would look like this:

do {
	ret = __mtk_rtc_read_time(rtc, &tm, &sec);
	if (ret < 0)
		goto rtc_exit;
} while (sec < tm->tm_sec);

Best regards,
Matthias
Eddie Huang (黃智傑) March 16, 2015, 9:52 a.m. UTC | #7
Hi Sascha,

On Fri, 2015-03-13 at 11:57 +0100, Sascha Hauer wrote:
> Hi Eddie,
> 
> On Fri, Mar 13, 2015 at 06:29:23PM +0800, Eddie Huang wrote:
> > > regmap_read() and regmap_write() can return errors.  There is no
> > > checking for this.
> > > 
> > 
> > I encounter some trouble when I add code to check return value of
> > regmap_read and regmap_write. Every RTC register access through regmap,
> > and there are many register read/write in this driver. If I check every
> > return value, the driver will become ugly. I try to make this driver
> > clean using following macro.
> > 
> > static int __rtc_read(struct mt6397_rtc *rtc, u32 offset, u32 *data)
> > {
> >         u32 addr = rtc->addr_base + offset;
> > 
> >         if (offset < rtc->addr_range)
> >                 return regmap_read(rtc->regmap, addr, data);
> > 
> >         return -EINVAL;
> > }
> > 
> > #define rtc_read(ret, rtc, offset, data)                \
> > ({                                                      \
> >         ret = __rtc_read(rtc, offset, data);            \
> >         if (ret < 0)                                    \
> >                 goto rtc_exit;                          \
> > })                                                      \
> 
> Hiding a goto (or return) in a macro is a very bad idea.
> 
> what you can do is
> 
> 	ret |= regmap_read(rtc->regmap, RTC_TC_SEC, &tm->tm_sec);
> 	ret |= regmap_read(rtc->regmap, RTC_TC_MIN, &tm->tm_min);
> 
> 	if (ret)
> 		return -EIO;
> 
> (Don't return ret in this case though as it might contain different
> error codes orred together)
> 

OK, I will drop macro, and check regmap_read, regmap_write return value
in each function.

> Another possibilty at least for contiguous registers would be
> regmap_bulk_read().
> 
> Sascha
> 

Contiguous registers access occurs in reading and writing time. I think
Matthias's suggestion is a good way: 

do {
	ret = __mtk_rtc_read_time(rtc, &tm, &sec);
	if (ret < 0)
		goto rtc_exit;
} while (sec < tm->tm_sec);

Eddie
Uwe Kleine-König March 16, 2015, 3:30 p.m. UTC | #8
Hello Eddie,

On Wed, Jan 28, 2015 at 05:27:56PM +0800, Eddie Huang wrote:
> From: Tianping Fang <tianping.fang@mediatek.com>
> 
> Add Mediatek MT63xx RTC driver
MT6397?

> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index f15cddf..8ac52d8 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1427,6 +1427,16 @@ config RTC_DRV_MOXART
>  	   This driver can also be built as a module. If so, the module
>  	   will be called rtc-moxart
>  
> +config RTC_DRV_MT63XX
> +	tristate "Mediatek Real Time Clock driver"
> +	depends on MFD_MT6397
I suggest:

	depends on MFD_MT6397 || COMPILE_TEST

(maybe + any hard dependencies you need for compilation).

> +	help
> +	  This selects the Mediatek(R) RTC driver, you should add support
> +	  for Mediatek MT6397 PMIC before select Mediatek(R) RTC driver.
> +
> +	  If you want to use Mediatek(R) RTC interface, select Y or M here.
> +	  If unsure, Please select N.
Given the dependency above I'd say choosing y here is fine. Instead of
recommending that I'd just drop this line.

> [...]
> +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
rtc_read is a bad name for a driver. There are already 6 functions with
this name in the kernel. Better use a unique prefix.

> [...]
> +static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
> +{
> +	struct mt6397_rtc *rtc = data;
> +	u16 irqsta, irqen;
> +
> +	mutex_lock(&rtc->lock);
> +	irqsta = rtc_read(rtc, RTC_IRQ_STA);
Do you really need to lock for a single read access?

> +	mutex_unlock(&rtc->lock);
> +
> +	if (irqsta & RTC_IRQ_STA_AL) {
> +		rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
> +		irqen = irqsta & ~RTC_IRQ_EN_AL;
> +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> +		rtc_write_trigger(rtc);
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	unsigned long time;
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +
> +	mutex_lock(&rtc->lock);
> +	do {
> +		tm->tm_sec = rtc_read(rtc, RTC_TC_SEC);
> +		tm->tm_min = rtc_read(rtc, RTC_TC_MIN);
> +		tm->tm_hour = rtc_read(rtc, RTC_TC_HOU);
> +		tm->tm_mday = rtc_read(rtc, RTC_TC_DOM);
> +		tm->tm_mon = rtc_read(rtc, RTC_TC_MTH);
> +		tm->tm_year = rtc_read(rtc, RTC_TC_YEA);
> +	} while (rtc_read(rtc, RTC_TC_SEC) < tm->tm_sec);
> +	mutex_unlock(&rtc->lock);
> +
> +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon--;
> +	rtc_tm_to_time(tm, &time);
rtc_tm_to_time is deprecated, better use rtc_tm_to_time64.

> +	tm->tm_wday = (time / 86400 + 4) % 7;
> +
> +	return 0;
> +}
> +
> +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +
> +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon++;
> +	mutex_lock(&rtc->lock);
> +	rtc_write(rtc, RTC_TC_YEA, tm->tm_year);
> +	rtc_write(rtc, RTC_TC_MTH, tm->tm_mon);
> +	rtc_write(rtc, RTC_TC_DOM, tm->tm_mday);
> +	rtc_write(rtc, RTC_TC_HOU, tm->tm_hour);
> +	rtc_write(rtc, RTC_TC_MIN, tm->tm_min);
> +	rtc_write(rtc, RTC_TC_SEC, tm->tm_sec);
Is this racy? I.e. what happens if RTC_TC_SEC overflows just before you
write to it but after you wrote RTC_TC_MIN?

> +	rtc_write_trigger(rtc);
> +	mutex_unlock(&rtc->lock);
> +
> +	return 0;
> +}
> +
> +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +	struct rtc_time *tm = &alm->time;
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +	u16 irqen, pdn2;
> +
> +	mutex_lock(&rtc->lock);
> +	irqen = rtc_read(rtc, RTC_IRQ_EN);
> +	pdn2 = rtc_read(rtc, RTC_PDN2);
> +	tm->tm_sec  = rtc_read(rtc, RTC_AL_SEC);
> +	tm->tm_min  = rtc_read(rtc, RTC_AL_MIN);
> +	tm->tm_hour = rtc_read(rtc, RTC_AL_HOU) & RTC_AL_HOU_MASK;
> +	tm->tm_mday = rtc_read(rtc, RTC_AL_DOM) & RTC_AL_DOM_MASK;
> +	tm->tm_mon  = rtc_read(rtc, RTC_AL_MTH) & RTC_AL_MTH_MASK;
> +	tm->tm_year = rtc_read(rtc, RTC_AL_YEA);
> +	mutex_unlock(&rtc->lock);
> +
> +	alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
> +	alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
> +
> +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon--;
> +
> +	return 0;
> +}
> +
> +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +	struct rtc_time *tm = &alm->time;
> +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> +	u16 irqen;
> +
> +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> +	tm->tm_mon++;
> +
> +	if (alm->enabled) {
> +		mutex_lock(&rtc->lock);
> +		rtc_write(rtc, RTC_AL_YEA, tm->tm_year);
> +		rtc_write(rtc, RTC_AL_MTH, (rtc_read(rtc, RTC_AL_MTH) &
> +				RTC_NEW_SPARE3) | tm->tm_mon);
This looks strange. Why doesn't RTC_NEW_SPARE3 contain the register
name? I would have expected:

	(rtc_read(rtc, RTC_AL_MTH) & ~RTC_AL_MTH_MASK) | tm->tm_mon;


> +		rtc_write(rtc, RTC_AL_DOM, (rtc_read(rtc, RTC_AL_DOM) &
> +				RTC_NEW_SPARE1) | tm->tm_mday);
> +		rtc_write(rtc, RTC_AL_HOU, (rtc_read(rtc, RTC_AL_HOU) &
> +				RTC_NEW_SPARE_FG_MASK) | tm->tm_hour);
> +		rtc_write(rtc, RTC_AL_MIN, tm->tm_min);
> +		rtc_write(rtc, RTC_AL_SEC, tm->tm_sec);
> +		rtc_write(rtc, RTC_AL_MASK, RTC_AL_MASK_DOW);
Is this racy? I.e. if the previous set alarm is

	2015-03-13 14:15:00

and you write

	2015-03.14 17:17:00

is it possible that this triggers an alarm if the update happens at

	2015-03-14 14:15:00

?

> +		rtc_write_trigger(rtc);
> +		irqen = rtc_read(rtc, RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
> +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> +		rtc_write_trigger(rtc);
> +		mutex_unlock(&rtc->lock);
	} else {
		/* disable alarm here */

> +	}
> +
> +	return 0;
> +}
> +
> +static struct rtc_class_ops mtk_rtc_ops = {
> +	.read_time  = mtk_rtc_read_time,
> +	.set_time   = mtk_rtc_set_time,
> +	.read_alarm = mtk_rtc_read_alarm,
> +	.set_alarm  = mtk_rtc_set_alarm,
> +};
> +
> +static int mtk_rtc_probe(struct platform_device *pdev)
> +{
> +	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
> +	struct mt6397_rtc *rtc;
> +	u32 reg[2];
> +	int ret = 0;
> +
> +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
> +	if (!rtc)
> +		return -ENOMEM;
> +
> +	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2);
> +	if (ret) {
> +		dev_err(&pdev->dev, "couldn't read rtc base address!\n");
> +		return -EINVAL;
> +	}
> +	rtc->addr_base = reg[0];
> +	rtc->addr_range = reg[1];
This looks strange, but maybe that's right as you reuse the parent's
regmap.

> +	rtc->regmap = mt6397_chip->regmap;
> +	rtc->dev = &pdev->dev;
> +	mutex_init(&rtc->lock);
> +
> +	platform_set_drvdata(pdev, rtc);
> +
> +	rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
> +				&mtk_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc->rtc_dev)) {
> +		dev_err(&pdev->dev, "register rtc device failed\n");
> +		return PTR_ERR(rtc->rtc_dev);
> +	}
> +
> +	rtc->irq = platform_get_irq(pdev, 0);
> +	if (rtc->irq < 0) {
platform_get_irq(pdev, 0) = 0 should be treated as error, too.

> +		ret = rtc->irq;
> +		goto out_rtc;
> +	}

Best regards
Uwe
Eddie Huang (黃智傑) March 17, 2015, 12:31 p.m. UTC | #9
Hi Uwe,

Thanks your review.

On Mon, 2015-03-16 at 16:30 +0100, Uwe Kleine-König wrote:
> Hello Eddie,
> 
> On Wed, Jan 28, 2015 at 05:27:56PM +0800, Eddie Huang wrote:
> > From: Tianping Fang <tianping.fang@mediatek.com>
> > 
> > Add Mediatek MT63xx RTC driver
> MT6397?

Yes, it is better to use MT6397

> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index f15cddf..8ac52d8 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -1427,6 +1427,16 @@ config RTC_DRV_MOXART
> >  	   This driver can also be built as a module. If so, the module
> >  	   will be called rtc-moxart
> >  
> > +config RTC_DRV_MT63XX
> > +	tristate "Mediatek Real Time Clock driver"
> > +	depends on MFD_MT6397
> I suggest:
> 
> 	depends on MFD_MT6397 || COMPILE_TEST
> 
> (maybe + any hard dependencies you need for compilation).

OK, will fix it

> 
> > +	help
> > +	  This selects the Mediatek(R) RTC driver, you should add support
> > +	  for Mediatek MT6397 PMIC before select Mediatek(R) RTC driver.
> > +
> > +	  If you want to use Mediatek(R) RTC interface, select Y or M here.
> > +	  If unsure, Please select N.
> Given the dependency above I'd say choosing y here is fine. Instead of
> recommending that I'd just drop this line.

ok, will drop.

> 
> > [...]
> > +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
> rtc_read is a bad name for a driver. There are already 6 functions with
> this name in the kernel. Better use a unique prefix.

I will use prefix mtk_

> 
> > [...]
> > +static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
> > +{
> > +	struct mt6397_rtc *rtc = data;
> > +	u16 irqsta, irqen;
> > +
> > +	mutex_lock(&rtc->lock);
> > +	irqsta = rtc_read(rtc, RTC_IRQ_STA);
> Do you really need to lock for a single read access?

I think this lock is necessary, because other thread may access rtc
register at the same time, for example, call mtk_rtc_set_alarm to modify
alarm time.

> 
> > +	mutex_unlock(&rtc->lock);
> > +
> > +	if (irqsta & RTC_IRQ_STA_AL) {
> > +		rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
> > +		irqen = irqsta & ~RTC_IRQ_EN_AL;
> > +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> > +		rtc_write_trigger(rtc);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	return IRQ_NONE;
> > +}
> > +
> > +static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
> > +{
> > +	unsigned long time;
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +
> > +	mutex_lock(&rtc->lock);
> > +	do {
> > +		tm->tm_sec = rtc_read(rtc, RTC_TC_SEC);
> > +		tm->tm_min = rtc_read(rtc, RTC_TC_MIN);
> > +		tm->tm_hour = rtc_read(rtc, RTC_TC_HOU);
> > +		tm->tm_mday = rtc_read(rtc, RTC_TC_DOM);
> > +		tm->tm_mon = rtc_read(rtc, RTC_TC_MTH);
> > +		tm->tm_year = rtc_read(rtc, RTC_TC_YEA);
> > +	} while (rtc_read(rtc, RTC_TC_SEC) < tm->tm_sec);
> > +	mutex_unlock(&rtc->lock);
> > +
> > +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon--;
> > +	rtc_tm_to_time(tm, &time);
> rtc_tm_to_time is deprecated, better use rtc_tm_to_time64.

OK, will change to rtc_tm_to_time64

> 
> > +	tm->tm_wday = (time / 86400 + 4) % 7;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > +{
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +
> > +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon++;
> > +	mutex_lock(&rtc->lock);
> > +	rtc_write(rtc, RTC_TC_YEA, tm->tm_year);
> > +	rtc_write(rtc, RTC_TC_MTH, tm->tm_mon);
> > +	rtc_write(rtc, RTC_TC_DOM, tm->tm_mday);
> > +	rtc_write(rtc, RTC_TC_HOU, tm->tm_hour);
> > +	rtc_write(rtc, RTC_TC_MIN, tm->tm_min);
> > +	rtc_write(rtc, RTC_TC_SEC, tm->tm_sec);
> Is this racy? I.e. what happens if RTC_TC_SEC overflows just before you
> write to it but after you wrote RTC_TC_MIN?

register value will write to hardware after rtc_write_trigger, so the
racy condition not exist.

> 
> > +	rtc_write_trigger(rtc);
> > +	mutex_unlock(&rtc->lock);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
> > +{
> > +	struct rtc_time *tm = &alm->time;
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +	u16 irqen, pdn2;
> > +
> > +	mutex_lock(&rtc->lock);
> > +	irqen = rtc_read(rtc, RTC_IRQ_EN);
> > +	pdn2 = rtc_read(rtc, RTC_PDN2);
> > +	tm->tm_sec  = rtc_read(rtc, RTC_AL_SEC);
> > +	tm->tm_min  = rtc_read(rtc, RTC_AL_MIN);
> > +	tm->tm_hour = rtc_read(rtc, RTC_AL_HOU) & RTC_AL_HOU_MASK;
> > +	tm->tm_mday = rtc_read(rtc, RTC_AL_DOM) & RTC_AL_DOM_MASK;
> > +	tm->tm_mon  = rtc_read(rtc, RTC_AL_MTH) & RTC_AL_MTH_MASK;
> > +	tm->tm_year = rtc_read(rtc, RTC_AL_YEA);
> > +	mutex_unlock(&rtc->lock);
> > +
> > +	alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
> > +	alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
> > +
> > +	tm->tm_year += RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon--;
> > +
> > +	return 0;
> > +}
> > +
> > +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
> > +{
> > +	struct rtc_time *tm = &alm->time;
> > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > +	u16 irqen;
> > +
> > +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> > +	tm->tm_mon++;
> > +
> > +	if (alm->enabled) {
> > +		mutex_lock(&rtc->lock);
> > +		rtc_write(rtc, RTC_AL_YEA, tm->tm_year);
> > +		rtc_write(rtc, RTC_AL_MTH, (rtc_read(rtc, RTC_AL_MTH) &
> > +				RTC_NEW_SPARE3) | tm->tm_mon);
> This looks strange. Why doesn't RTC_NEW_SPARE3 contain the register
> name? I would have expected:
> 
> 	(rtc_read(rtc, RTC_AL_MTH) & ~RTC_AL_MTH_MASK) | tm->tm_mon;

I will remove RTC_NEW_SPARE3. Hardware will return 0 if register bit is
useless. So the bit clear is redundant. 

> 
> > +		rtc_write(rtc, RTC_AL_DOM, (rtc_read(rtc, RTC_AL_DOM) &
> > +				RTC_NEW_SPARE1) | tm->tm_mday);
> > +		rtc_write(rtc, RTC_AL_HOU, (rtc_read(rtc, RTC_AL_HOU) &
> > +				RTC_NEW_SPARE_FG_MASK) | tm->tm_hour);
> > +		rtc_write(rtc, RTC_AL_MIN, tm->tm_min);
> > +		rtc_write(rtc, RTC_AL_SEC, tm->tm_sec);
> > +		rtc_write(rtc, RTC_AL_MASK, RTC_AL_MASK_DOW);
> Is this racy? I.e. if the previous set alarm is
> 
> 	2015-03-13 14:15:00
> 
> and you write
> 
> 	2015-03.14 17:17:00
> 
> is it possible that this triggers an alarm if the update happens at
> 
> 	2015-03-14 14:15:00
> 
> ?
> 
All rtc register value write to RTC hardware after rtc_write_trigger.
Race condition should not happen. 


> > +		rtc_write_trigger(rtc);
> > +		irqen = rtc_read(rtc, RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
> > +		rtc_write(rtc, RTC_IRQ_EN, irqen);
> > +		rtc_write_trigger(rtc);
> > +		mutex_unlock(&rtc->lock);
> 	} else {
> 		/* disable alarm here */
> 
OK, should clear RTC_IRQ_EN

> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct rtc_class_ops mtk_rtc_ops = {
> > +	.read_time  = mtk_rtc_read_time,
> > +	.set_time   = mtk_rtc_set_time,
> > +	.read_alarm = mtk_rtc_read_alarm,
> > +	.set_alarm  = mtk_rtc_set_alarm,
> > +};
> > +
> > +static int mtk_rtc_probe(struct platform_device *pdev)
> > +{
> > +	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
> > +	struct mt6397_rtc *rtc;
> > +	u32 reg[2];
> > +	int ret = 0;
> > +
> > +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
> > +	if (!rtc)
> > +		return -ENOMEM;
> > +
> > +	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "couldn't read rtc base address!\n");
> > +		return -EINVAL;
> > +	}
> > +	rtc->addr_base = reg[0];
> > +	rtc->addr_range = reg[1];
> This looks strange, but maybe that's right as you reuse the parent's
> regmap.

According Sascha and Mark Brown's discussion:
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-February/323239.html

Address and interrupt will move from device tree to mfd_cell in
mt6397-core.c

> 
> > +	rtc->regmap = mt6397_chip->regmap;
> > +	rtc->dev = &pdev->dev;
> > +	mutex_init(&rtc->lock);
> > +
> > +	platform_set_drvdata(pdev, rtc);
> > +
> > +	rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
> > +				&mtk_rtc_ops, THIS_MODULE);
> > +	if (IS_ERR(rtc->rtc_dev)) {
> > +		dev_err(&pdev->dev, "register rtc device failed\n");
> > +		return PTR_ERR(rtc->rtc_dev);
> > +	}
> > +
> > +	rtc->irq = platform_get_irq(pdev, 0);
> > +	if (rtc->irq < 0) {
> platform_get_irq(pdev, 0) = 0 should be treated as error, too.

OK, will fix it

> 
> > +		ret = rtc->irq;
> > +		goto out_rtc;
> > +	}
> 
> Best regards
> Uwe
>
Uwe Kleine-König March 17, 2015, 1:43 p.m. UTC | #10
Hello Eddie,

On Tue, Mar 17, 2015 at 08:31:14PM +0800, Eddie Huang wrote:
> On Mon, 2015-03-16 at 16:30 +0100, Uwe Kleine-König wrote:
> > On Wed, Jan 28, 2015 at 05:27:56PM +0800, Eddie Huang wrote:
> > > [...]
> > > +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
> > rtc_read is a bad name for a driver. There are already 6 functions with
> > this name in the kernel. Better use a unique prefix.
> 
> I will use prefix mtk_
I would prefer a prefix that is unique to the driver. "mtk_" doesn't
work to distinguish between the rtc and a (say) spi driver. What you
want here is that if someone reports a bug on any mailinglist with a
backtrace you are able to immediately see which driver is affected.

> > > [...]
> > > +static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
> > > +{
> > > +	struct mt6397_rtc *rtc = data;
> > > +	u16 irqsta, irqen;
> > > +
> > > +	mutex_lock(&rtc->lock);
> > > +	irqsta = rtc_read(rtc, RTC_IRQ_STA);
> > Do you really need to lock for a single read access?
> 
> I think this lock is necessary, because other thread may access rtc
> register at the same time, for example, call mtk_rtc_set_alarm to modify
> alarm time.
That would be a valid reason if mtk_rtc_set_alarm touched that register
twice in a single critical section and the handler must not read the
value of the first write. Otherwise it should be fine, shouldn't it?

> > > +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > > +{
> > > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > > +
> > > +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> > > +	tm->tm_mon++;
> > > +	mutex_lock(&rtc->lock);
> > > +	rtc_write(rtc, RTC_TC_YEA, tm->tm_year);
> > > +	rtc_write(rtc, RTC_TC_MTH, tm->tm_mon);
> > > +	rtc_write(rtc, RTC_TC_DOM, tm->tm_mday);
> > > +	rtc_write(rtc, RTC_TC_HOU, tm->tm_hour);
> > > +	rtc_write(rtc, RTC_TC_MIN, tm->tm_min);
> > > +	rtc_write(rtc, RTC_TC_SEC, tm->tm_sec);
> > Is this racy? I.e. what happens if RTC_TC_SEC overflows just before you
> > write to it but after you wrote RTC_TC_MIN?
> 
> register value will write to hardware after rtc_write_trigger, so the
> racy condition not exist.
Ah, it seems the hardware guys did their job. Nice.

Best regards
Uwe
Eddie Huang (黃智傑) March 18, 2015, 3:27 a.m. UTC | #11
Hi Uwe,

On Tue, 2015-03-17 at 14:43 +0100, Uwe Kleine-König wrote:
> Hello Eddie,
> 
> On Tue, Mar 17, 2015 at 08:31:14PM +0800, Eddie Huang wrote:
> > On Mon, 2015-03-16 at 16:30 +0100, Uwe Kleine-König wrote:
> > > On Wed, Jan 28, 2015 at 05:27:56PM +0800, Eddie Huang wrote:
> > > > [...]
> > > > +static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
> > > rtc_read is a bad name for a driver. There are already 6 functions with
> > > this name in the kernel. Better use a unique prefix.
> > 
> > I will use prefix mtk_
> I would prefer a prefix that is unique to the driver. "mtk_" doesn't
> work to distinguish between the rtc and a (say) spi driver. What you
> want here is that if someone reports a bug on any mailinglist with a
> backtrace you are able to immediately see which driver is affected.
> 

My meaning is mtk_rtc_read, mtk_rtc_write.

> > > > [...]
> > > > +static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
> > > > +{
> > > > +	struct mt6397_rtc *rtc = data;
> > > > +	u16 irqsta, irqen;
> > > > +
> > > > +	mutex_lock(&rtc->lock);
> > > > +	irqsta = rtc_read(rtc, RTC_IRQ_STA);
> > > Do you really need to lock for a single read access?
> > 
> > I think this lock is necessary, because other thread may access rtc
> > register at the same time, for example, call mtk_rtc_set_alarm to modify
> > alarm time.
> That would be a valid reason if mtk_rtc_set_alarm touched that register
> twice in a single critical section and the handler must not read the
> value of the first write. Otherwise it should be fine, shouldn't it?
> 

My original though is if disable alarm in mtk_rtc_set_alarm function,
RTC_IRQ_STA may be affected, this is why I add mutex. After checking
with designer, RTC_IRQ_STA will not be affected. I will remove the
mutex.

> > > > +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > > > +{
> > > > +	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
> > > > +
> > > > +	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
> > > > +	tm->tm_mon++;
> > > > +	mutex_lock(&rtc->lock);
> > > > +	rtc_write(rtc, RTC_TC_YEA, tm->tm_year);
> > > > +	rtc_write(rtc, RTC_TC_MTH, tm->tm_mon);
> > > > +	rtc_write(rtc, RTC_TC_DOM, tm->tm_mday);
> > > > +	rtc_write(rtc, RTC_TC_HOU, tm->tm_hour);
> > > > +	rtc_write(rtc, RTC_TC_MIN, tm->tm_min);
> > > > +	rtc_write(rtc, RTC_TC_SEC, tm->tm_sec);
> > > Is this racy? I.e. what happens if RTC_TC_SEC overflows just before you
> > > write to it but after you wrote RTC_TC_MIN?
> > 
> > register value will write to hardware after rtc_write_trigger, so the
> > racy condition not exist.
> Ah, it seems the hardware guys did their job. Nice.
> 
> Best regards
> Uwe
>
Uwe Kleine-König March 18, 2015, 7:42 a.m. UTC | #12
Hello Eddie,

On Wed, Mar 18, 2015 at 11:27:40AM +0800, Eddie Huang wrote:
> On Tue, 2015-03-17 at 14:43 +0100, Uwe Kleine-König wrote:
> > On Tue, Mar 17, 2015 at 08:31:14PM +0800, Eddie Huang wrote:
> > > On Mon, 2015-03-16 at 16:30 +0100, Uwe Kleine-König wrote:
> > > > On Wed, Jan 28, 2015 at 05:27:56PM +0800, Eddie Huang wrote:
> > > > > [...]
> > > > > +static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
> > > > > +{
> > > > > +	struct mt6397_rtc *rtc = data;
> > > > > +	u16 irqsta, irqen;
> > > > > +
> > > > > +	mutex_lock(&rtc->lock);
> > > > > +	irqsta = rtc_read(rtc, RTC_IRQ_STA);
> > > > Do you really need to lock for a single read access?
> > > 
> > > I think this lock is necessary, because other thread may access rtc
> > > register at the same time, for example, call mtk_rtc_set_alarm to modify
> > > alarm time.
> > That would be a valid reason if mtk_rtc_set_alarm touched that register
> > twice in a single critical section and the handler must not read the
> > value of the first write. Otherwise it should be fine, shouldn't it?
> > 
> 
> My original though is if disable alarm in mtk_rtc_set_alarm function,
> RTC_IRQ_STA may be affected, this is why I add mutex. After checking
> with designer, RTC_IRQ_STA will not be affected. I will remove the
> mutex.
Even if IRQ_STA is changed there is no problem. With the lock the
following can happen:

Either the irq thread comes first:

	thread1			thread2

	lock
	rtc_read
	unlock   ------------>  lock
				dosomething with IRQ_STA
				unlock

or the other thread comes first:

	thread1			thread2

				lock
				dosomething with IRQ_STA
	lock   <-------------	unlock
	rtc_read
	unlock

So thread1 either reads the old or the new value of IRQ_STA.

Without locking you have the same! Either the write in thread2 comes
first or not. And depending on that you either read the new or the old
value respectively.

Of course this assumes that a write is atomic in the sense that if you
update a value from 0x01 to 0x10 there is no point in time a reader can
see 0x00 or 0x11. But this is usually given. Also reading the register
in question must not have side effects that affect the other threads.

Best regards
Uwe
diff mbox

Patch

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index f15cddf..8ac52d8 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1427,6 +1427,16 @@  config RTC_DRV_MOXART
 	   This driver can also be built as a module. If so, the module
 	   will be called rtc-moxart
 
+config RTC_DRV_MT63XX
+	tristate "Mediatek Real Time Clock driver"
+	depends on MFD_MT6397
+	help
+	  This selects the Mediatek(R) RTC driver, you should add support
+	  for Mediatek MT6397 PMIC before select Mediatek(R) RTC driver.
+
+	  If you want to use Mediatek(R) RTC interface, select Y or M here.
+	  If unsure, Please select N.
+
 config RTC_DRV_XGENE
 	tristate "APM X-Gene RTC"
 	depends on HAS_IOMEM
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index c8ef3e1..53e0085 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -150,3 +150,4 @@  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
+obj-$(CONFIG_RTC_DRV_MT63XX)	+= rtc-mt6397.o
diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
new file mode 100644
index 0000000..b54d605
--- /dev/null
+++ b/drivers/rtc/rtc-mt6397.c
@@ -0,0 +1,352 @@ 
+/*
+* Copyright (c) 2014-2015 MediaTek Inc.
+* Author: Tianping.Fang <tianping.fang@mediatek.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* 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.
+*/
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/irqdomain.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/mfd/mt6397/core.h>
+
+#define RTC_BBPU		0x0000
+#define RTC_WRTGR		0x003c
+#define RTC_IRQ_EN		0x0004
+#define RTC_IRQ_STA		0x0002
+
+#define RTC_BBPU_CBUSY		(1 << 6)
+#define RTC_BBPU_KEY		(0x43 << 8)
+#define RTC_BBPU_AUTO		(1 << 3)
+#define RTC_IRQ_STA_AL		(1 << 0)
+#define RTC_IRQ_STA_LP		(1 << 3)
+
+#define RTC_TC_SEC		0x000a
+#define RTC_TC_MIN		0x000c
+#define RTC_TC_HOU		0x000e
+#define RTC_TC_DOM		0x0010
+#define RTC_TC_MTH		0x0014
+#define RTC_TC_YEA		0x0016
+#define RTC_AL_SEC		0x0018
+#define RTC_AL_MIN		0x001a
+
+#define RTC_IRQ_EN_AL		(1 << 0)
+#define RTC_IRQ_EN_ONESHOT	(1 << 2)
+#define RTC_IRQ_EN_LP		(1 << 3)
+#define RTC_IRQ_EN_ONESHOT_AL	(RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL)
+
+#define RTC_TC_MIN_MASK		0x003f
+#define RTC_TC_SEC_MASK		0x003f
+#define RTC_TC_HOU_MASK		0x001f
+#define RTC_TC_DOM_MASK		0x001f
+#define RTC_TC_MTH_MASK		0x000f
+#define RTC_TC_YEA_MASK		0x007f
+
+#define RTC_AL_SEC_MASK		0x003f
+#define RTC_AL_MIN_MASK		0x003f
+#define RTC_AL_MASK_DOW		(1 << 4)
+
+#define RTC_AL_HOU		0x001c
+#define RTC_NEW_SPARE_FG_MASK	0xff00
+#define RTC_NEW_SPARE_FG_SHIFT	8
+#define RTC_AL_HOU_MASK		0x001f
+
+#define RTC_AL_DOM		0x001e
+#define RTC_NEW_SPARE1		0xff00
+#define RTC_AL_DOM_MASK		0x001f
+#define RTC_AL_MASK		0x0008
+
+#define RTC_AL_MTH		0x0022
+#define RTC_NEW_SPARE3		0xff00
+#define RTC_AL_MTH_MASK		0x000f
+
+#define RTC_AL_YEA		0x0024
+#define RTC_AL_YEA_MASK		0x007f
+
+#define RTC_PDN1		0x002c
+#define RTC_PDN1_PWRON_TIME	(1 << 7)
+
+#define RTC_PDN2			0x002e
+#define RTC_PDN2_PWRON_MTH_MASK		0x000f
+#define RTC_PDN2_PWRON_MTH_SHIFT	0
+#define RTC_PDN2_PWRON_ALARM		(1 << 4)
+#define RTC_PDN2_UART_MASK		0x0060
+#define RTC_PDN2_UART_SHIFT		5
+#define RTC_PDN2_PWRON_YEA_MASK		0x7f00
+#define RTC_PDN2_PWRON_YEA_SHIFT	8
+#define RTC_PDN2_PWRON_LOGO		(1 << 15)
+
+#define RTC_MIN_YEAR		1968
+#define RTC_BASE_YEAR		1900
+#define RTC_NUM_YEARS		128
+#define RTC_MIN_YEAR_OFFSET	(RTC_MIN_YEAR - RTC_BASE_YEAR)
+#define RTC_RELPWR_WHEN_XRST	1
+
+struct mt6397_rtc {
+	struct device		*dev;
+	struct rtc_device	*rtc_dev;
+	struct mutex		lock;
+	struct regmap		*regmap;
+	int			irq;
+	u32			addr_base;
+	u32			addr_range;
+};
+
+static u16 rtc_read(struct mt6397_rtc *rtc, u32 offset)
+{
+	u32 rdata = 0;
+	u32 addr = rtc->addr_base + offset;
+
+	if (offset < rtc->addr_range)
+		regmap_read(rtc->regmap, addr, &rdata);
+
+	return (u16)rdata;
+}
+
+static void rtc_write(struct mt6397_rtc *rtc, u32 offset, u32 data)
+{
+	u32 addr;
+
+	addr = rtc->addr_base + offset;
+
+	if (offset < rtc->addr_range)
+		regmap_write(rtc->regmap, addr, data);
+}
+
+static void rtc_write_trigger(struct mt6397_rtc *rtc)
+{
+	rtc_write(rtc, RTC_WRTGR, 1);
+	while (rtc_read(rtc, RTC_BBPU) & RTC_BBPU_CBUSY)
+		cpu_relax();
+}
+
+static irqreturn_t rtc_irq_handler_thread(int irq, void *data)
+{
+	struct mt6397_rtc *rtc = data;
+	u16 irqsta, irqen;
+
+	mutex_lock(&rtc->lock);
+	irqsta = rtc_read(rtc, RTC_IRQ_STA);
+	mutex_unlock(&rtc->lock);
+
+	if (irqsta & RTC_IRQ_STA_AL) {
+		rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+		irqen = irqsta & ~RTC_IRQ_EN_AL;
+		rtc_write(rtc, RTC_IRQ_EN, irqen);
+		rtc_write_trigger(rtc);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned long time;
+	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+
+	mutex_lock(&rtc->lock);
+	do {
+		tm->tm_sec = rtc_read(rtc, RTC_TC_SEC);
+		tm->tm_min = rtc_read(rtc, RTC_TC_MIN);
+		tm->tm_hour = rtc_read(rtc, RTC_TC_HOU);
+		tm->tm_mday = rtc_read(rtc, RTC_TC_DOM);
+		tm->tm_mon = rtc_read(rtc, RTC_TC_MTH);
+		tm->tm_year = rtc_read(rtc, RTC_TC_YEA);
+	} while (rtc_read(rtc, RTC_TC_SEC) < tm->tm_sec);
+	mutex_unlock(&rtc->lock);
+
+	tm->tm_year += RTC_MIN_YEAR_OFFSET;
+	tm->tm_mon--;
+	rtc_tm_to_time(tm, &time);
+
+	tm->tm_wday = (time / 86400 + 4) % 7;
+
+	return 0;
+}
+
+static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+
+	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
+	tm->tm_mon++;
+	mutex_lock(&rtc->lock);
+	rtc_write(rtc, RTC_TC_YEA, tm->tm_year);
+	rtc_write(rtc, RTC_TC_MTH, tm->tm_mon);
+	rtc_write(rtc, RTC_TC_DOM, tm->tm_mday);
+	rtc_write(rtc, RTC_TC_HOU, tm->tm_hour);
+	rtc_write(rtc, RTC_TC_MIN, tm->tm_min);
+	rtc_write(rtc, RTC_TC_SEC, tm->tm_sec);
+	rtc_write_trigger(rtc);
+	mutex_unlock(&rtc->lock);
+
+	return 0;
+}
+
+static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+	struct rtc_time *tm = &alm->time;
+	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+	u16 irqen, pdn2;
+
+	mutex_lock(&rtc->lock);
+	irqen = rtc_read(rtc, RTC_IRQ_EN);
+	pdn2 = rtc_read(rtc, RTC_PDN2);
+	tm->tm_sec  = rtc_read(rtc, RTC_AL_SEC);
+	tm->tm_min  = rtc_read(rtc, RTC_AL_MIN);
+	tm->tm_hour = rtc_read(rtc, RTC_AL_HOU) & RTC_AL_HOU_MASK;
+	tm->tm_mday = rtc_read(rtc, RTC_AL_DOM) & RTC_AL_DOM_MASK;
+	tm->tm_mon  = rtc_read(rtc, RTC_AL_MTH) & RTC_AL_MTH_MASK;
+	tm->tm_year = rtc_read(rtc, RTC_AL_YEA);
+	mutex_unlock(&rtc->lock);
+
+	alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
+	alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
+
+	tm->tm_year += RTC_MIN_YEAR_OFFSET;
+	tm->tm_mon--;
+
+	return 0;
+}
+
+static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+	struct rtc_time *tm = &alm->time;
+	struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+	u16 irqen;
+
+	tm->tm_year -= RTC_MIN_YEAR_OFFSET;
+	tm->tm_mon++;
+
+	if (alm->enabled) {
+		mutex_lock(&rtc->lock);
+		rtc_write(rtc, RTC_AL_YEA, tm->tm_year);
+		rtc_write(rtc, RTC_AL_MTH, (rtc_read(rtc, RTC_AL_MTH) &
+				RTC_NEW_SPARE3) | tm->tm_mon);
+		rtc_write(rtc, RTC_AL_DOM, (rtc_read(rtc, RTC_AL_DOM) &
+				RTC_NEW_SPARE1) | tm->tm_mday);
+		rtc_write(rtc, RTC_AL_HOU, (rtc_read(rtc, RTC_AL_HOU) &
+				RTC_NEW_SPARE_FG_MASK) | tm->tm_hour);
+		rtc_write(rtc, RTC_AL_MIN, tm->tm_min);
+		rtc_write(rtc, RTC_AL_SEC, tm->tm_sec);
+		rtc_write(rtc, RTC_AL_MASK, RTC_AL_MASK_DOW);
+		rtc_write_trigger(rtc);
+		irqen = rtc_read(rtc, RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
+		rtc_write(rtc, RTC_IRQ_EN, irqen);
+		rtc_write_trigger(rtc);
+		mutex_unlock(&rtc->lock);
+	}
+
+	return 0;
+}
+
+static struct rtc_class_ops mtk_rtc_ops = {
+	.read_time  = mtk_rtc_read_time,
+	.set_time   = mtk_rtc_set_time,
+	.read_alarm = mtk_rtc_read_alarm,
+	.set_alarm  = mtk_rtc_set_alarm,
+};
+
+static int mtk_rtc_probe(struct platform_device *pdev)
+{
+	struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
+	struct mt6397_rtc *rtc;
+	u32 reg[2];
+	int ret = 0;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't read rtc base address!\n");
+		return -EINVAL;
+	}
+	rtc->addr_base = reg[0];
+	rtc->addr_range = reg[1];
+	rtc->regmap = mt6397_chip->regmap;
+	rtc->dev = &pdev->dev;
+	mutex_init(&rtc->lock);
+
+	platform_set_drvdata(pdev, rtc);
+
+	rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
+				&mtk_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc_dev)) {
+		dev_err(&pdev->dev, "register rtc device failed\n");
+		return PTR_ERR(rtc->rtc_dev);
+	}
+
+	rtc->irq = platform_get_irq(pdev, 0);
+	if (rtc->irq < 0) {
+		ret = rtc->irq;
+		goto out_rtc;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+			rtc_irq_handler_thread, IRQF_ONESHOT,
+			"mt6397-rtc", rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+			rtc->irq, ret);
+		goto out_rtc;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+
+out_rtc:
+	rtc_device_unregister(rtc->rtc_dev);
+	return ret;
+
+}
+
+static int mtk_rtc_remove(struct platform_device *pdev)
+{
+	struct mt6397_rtc *rtc = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(rtc->rtc_dev);
+	return 0;
+}
+
+static const struct of_device_id mt6397_rtc_of_match[] = {
+	{ .compatible = "mediatek,mt6397-rtc", },
+	{ }
+};
+
+static struct platform_driver mtk_rtc_driver = {
+	.driver = {
+		.name = "mt6397-rtc",
+		.of_match_table = mt6397_rtc_of_match,
+	},
+	.probe	= mtk_rtc_probe,
+	.remove = mtk_rtc_remove,
+};
+
+module_platform_driver(mtk_rtc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Tianping Fang <tianping.fang@mediatek.com>");
+MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC");
+MODULE_ALIAS("platform:mt6397-rtc");