Messages in this thread | | | Date | Sat, 30 May 2015 23:47:41 +0200 | From | Alexandre Belloni <> | Subject | Re: [PATCHv 1/1] RTC:driver for Cortina's SOC |
| |
Hi,
On 20/05/2015 at 17:49:31 +0200, Hans Ulli Kroll wrote : > Driver for the on chip RTC found on Cortina's SoC Gemini. > > Signed-off-by: Hans Ulli Kroll <ulli.kroll@googlemail.com>
I've applied it with a few modifications: https://git.kernel.org/cgit/linux/kernel/git/abelloni/linux.git/commit/?h=rtc-next&id=a0928c40a908d18f06c05c9db4bdc8e3f0761179
I've removed the need for goto and labels by using devm_request_irq().
> --- > MAINTAINERS | 1 + > drivers/rtc/Kconfig | 11 +++ > drivers/rtc/Makefile | 1 + > drivers/rtc/rtc-gemini.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 203 insertions(+) > create mode 100644 drivers/rtc/rtc-gemini.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index f0d922b..3cff87e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -977,6 +977,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > T: git git://github.com/ulli-kroll/linux.git > S: Maintained > F: arch/arm/mach-gemini/ > +F: drivers/rtc/rtc-gemini.c > > ARM/CSR SIRFPRIMA2 MACHINE SUPPORT > M: Barry Song <baohua@kernel.org> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > index 0fe4ad8..4a37931 100644 > --- a/drivers/rtc/Kconfig > +++ b/drivers/rtc/Kconfig > @@ -1372,6 +1372,17 @@ config RTC_DRV_ARMADA38X > This driver can also be built as a module. If so, the module > will be called armada38x-rtc. > > +config RTC_DRV_GEMINI > + tristate "Gemini SoC RTC" > + depends on ARCH_GEMINI || COMPILE_TEST > + depends on HAS_IOMEM > + help > + If you say Y here you will get support for the > + RTC found on Gemini SoC's. > + > + This driver can also be built as a module. If so, the module > + will be called rtc-gemini. > + > config RTC_DRV_PS3 > tristate "PS3 RTC" > depends on PPC_PS3 > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > index 2b82e2b..ed4057a 100644 > --- a/drivers/rtc/Makefile > +++ b/drivers/rtc/Makefile > @@ -65,6 +65,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o > obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o > obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o > obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o > +obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o > obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o > obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o > obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o > diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-gemini.c > new file mode 100644 > index 0000000..5b36450 > --- /dev/null > +++ b/drivers/rtc/rtc-gemini.c > @@ -0,0 +1,190 @@ > +/* > + * Gemini OnChip RTC > + * > + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.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. > + * > + * 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. > + * > + * Original code for older kernel 2.6.15 are from Stormlinksemi > + * first update from Janos Laube for > 2.6.29 kernels > + * > + * checkpatch fixes and usage of rtc-lib code > + * Hans Ulli Kroll <ulli.kroll@googlemail.com> > + */ > + > +#include <linux/rtc.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/platform_device.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > + > +#define DRV_NAME "rtc-gemini" > +#define DRV_VERSION "0.2" > + > +MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); > +MODULE_DESCRIPTION("RTC driver for Gemini SoC"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:" DRV_NAME); > + > +struct gemini_rtc { > + struct rtc_device *rtc_dev; > + void __iomem *rtc_base; > + int rtc_irq; > +}; > + > +enum gemini_rtc_offsets { > + GEMINI_RTC_SECOND = 0x00, > + GEMINI_RTC_MINUTE = 0x04, > + GEMINI_RTC_HOUR = 0x08, > + GEMINI_RTC_DAYS = 0x0C, > + GEMINI_RTC_ALARM_SECOND = 0x10, > + GEMINI_RTC_ALARM_MINUTE = 0x14, > + GEMINI_RTC_ALARM_HOUR = 0x18, > + GEMINI_RTC_RECORD = 0x1C, > + GEMINI_RTC_CR = 0x20 > +}; > + > +static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) > +{ > + return IRQ_HANDLED; > +} > + > +/* > + * Looks like the RTC in the Gemini SoC is (totaly) broken > + * We can't read/write directly the time from RTC registers. > + * We must do some "offset" calculation to get the real time > + * > + * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does > + * the same thing, without the rtc-lib.c calls. > + */ > + > +static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) > +{ > + struct gemini_rtc *rtc = dev_get_drvdata(dev); > + > + unsigned int days, hour, min, sec; > + unsigned long offset, time; > + > + sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); > + min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); > + hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); > + days = readl(rtc->rtc_base + GEMINI_RTC_DAYS); > + offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD); > + > + time = offset + days * 86400 + hour * 3600 + min * 60 + sec; > + > + rtc_time_to_tm(time, tm); > + > + return 0; > +} > + > +static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) > +{ > + struct gemini_rtc *rtc = dev_get_drvdata(dev); > + unsigned int sec, min, hour, day; > + unsigned long offset, time; > + > + if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ > + return -EINVAL; > + > + rtc_tm_to_time(tm, &time); > + > + sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); > + min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); > + hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); > + day = readl(rtc->rtc_base + GEMINI_RTC_DAYS); > + > + offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); > + > + writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD); > + writel(0x01, rtc->rtc_base + GEMINI_RTC_CR); > + > + return 0; > +} > + > +static struct rtc_class_ops gemini_rtc_ops = { > + .read_time = gemini_rtc_read_time, > + .set_time = gemini_rtc_set_time, > +}; > + > +static int gemini_rtc_probe(struct platform_device *pdev) > +{ > + struct gemini_rtc *rtc; > + struct device *dev = &pdev->dev; > + struct resource *res; > + int ret; > + > + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); > + if (unlikely(!rtc)) > + return -ENOMEM; > + platform_set_drvdata(pdev, rtc); > + > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + if (!res) { > + ret = -ENODEV; > + goto err_mem; > + } > + rtc->rtc_irq = res->start; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + ret = -ENODEV; > + goto err_mem; > + } > + rtc->rtc_base = devm_ioremap(&pdev->dev, res->start, > + res->end - res->start + 1); > + > + ret = request_irq(rtc->rtc_irq, gemini_rtc_interrupt, > + IRQF_SHARED, pdev->name, dev); > + if (unlikely(ret)) > + goto err_mem; > + > + rtc->rtc_dev = rtc_device_register(pdev->name, dev, > + &gemini_rtc_ops, THIS_MODULE); > + if (likely(IS_ERR(rtc->rtc_dev))) { > + ret = PTR_ERR(rtc->rtc_dev); > + goto err_irq; > + } > + return 0; > + > +err_irq: > + free_irq(rtc->rtc_irq, dev); > + > +err_mem: > + kfree(rtc); > + return ret; > +} > + > +static int gemini_rtc_remove(struct platform_device *pdev) > +{ > + struct gemini_rtc *rtc = platform_get_drvdata(pdev); > + struct device *dev = &pdev->dev; > + > + free_irq(rtc->rtc_irq, dev); > + rtc_device_unregister(rtc->rtc_dev); > + platform_set_drvdata(pdev, NULL); > + kfree(rtc); > + > + return 0; > +} > + > +static struct platform_driver gemini_rtc_driver = { > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + }, > + .probe = gemini_rtc_probe, > + .remove = gemini_rtc_remove, > +}; > + > +module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe); > + > -- > 2.4.0 >
-- Alexandre Belloni, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com
| |