Message ID | 20081209095708.21516.98999.stgit@i1501.lan.towertech.it |
---|---|
State | Accepted, archived |
Headers | show |
On Tue, 09 Dec 2008 10:57:08 +0100 Alessandro Zummo <a.zummo@towertech.it> wrote: > From: Saeed Bishara <saeed@marvell.com> > > Driver for the on-chip RTC found in some of Marvell's SoCs > such as the Kirkwood 88F6281 and 88F6192 devices. > > Signed-off-by: Saeed Bishara <saeed@marvell.com> > Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> > Signed-off-by: Nicolas Pitre <nico@marvell.com> > Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> > --- > > drivers/rtc/Kconfig | 11 +++ > drivers/rtc/Makefile | 1 > drivers/rtc/rtc-mv.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 175 insertions(+), 0 deletions(-) > create mode 100644 drivers/rtc/rtc-mv.c > > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig > index 5caa82d..b2cf166 100644 > --- a/drivers/rtc/Kconfig > +++ b/drivers/rtc/Kconfig > @@ -704,4 +704,15 @@ config RTC_DRV_STARFIRE > If you say Y here you will get support for the RTC found on > Starfire systems. > > +config RTC_DRV_MV > + tristate "Marvell SoC RTC" > + depends on ARCH_KIRKWOOD > + help > + If you say yes here you will get support for the in-chip RTC > + that can be found in some of Marvell's SoC devices, such as > + the Kirkwood 88F6281 and 88F6192. > + > + This driver can also be built as a module. If so, the module > + will be called rtc-mv. hm, I wonder if "rtc-mv" is a good name. It implies that this is the only RTC device which Marvell will ever make. > endif # RTC_CLASS > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile > index fca0c9b..87a3adc 100644 > --- a/drivers/rtc/Makefile > +++ b/drivers/rtc/Makefile > @@ -48,6 +48,7 @@ obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o > obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o > obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o > obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o > +obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o > obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o > obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o > obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o > diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c > new file mode 100644 > index 0000000..45f12dc > --- /dev/null > +++ b/drivers/rtc/rtc-mv.c > @@ -0,0 +1,163 @@ > +/* > + * Driver for the RTC in Marvell SoCs. > + * > + * This file is licensed under the terms of the GNU General Public > + * License version 2. This program is licensed "as is" without any > + * warranty of any kind, whether express or implied. > + */ > + > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/rtc.h> > +#include <linux/bcd.h> > +#include <linux/io.h> > +#include <linux/platform_device.h> > + > + > +#define RTC_TIME_REG_OFFS 0 > +#define RTC_SECONDS_OFFS 0 > +#define RTC_MINUTES_OFFS 8 > +#define RTC_HOURS_OFFS 16 > +#define RTC_WDAY_OFFS 24 > +#define RTC_HOURS_12H_MODE (1 << 22) /* 12 hours mode */ > + > +#define RTC_DATE_REG_OFFS 4 > +#define RTC_MDAY_OFFS 0 > +#define RTC_MONTH_OFFS 8 > +#define RTC_YEAR_OFFS 16 > + > + > +struct rtc_plat_data { > + struct rtc_device *rtc; > + void __iomem *ioaddr; > +}; > + > +static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm) > +{ > + struct rtc_plat_data *pdata = dev_get_drvdata(dev); > + void __iomem *ioaddr = pdata->ioaddr; > + u32 rtc_reg; > + > + rtc_reg = (bin2bcd(tm->tm_sec) << RTC_SECONDS_OFFS) | > + (bin2bcd(tm->tm_min) << RTC_MINUTES_OFFS) | > + (bin2bcd(tm->tm_hour) << RTC_HOURS_OFFS) | > + (bin2bcd(tm->tm_wday) << RTC_WDAY_OFFS); > + writel(rtc_reg, ioaddr + RTC_TIME_REG_OFFS); > + > + rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) | > + (bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) | > + (bin2bcd(tm->tm_year % 100) << RTC_YEAR_OFFS); > + writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS); > + > + return 0; > +} > + > +static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm) > +{ > + struct rtc_plat_data *pdata = dev_get_drvdata(dev); > + void __iomem *ioaddr = pdata->ioaddr; > + u32 rtc_time, rtc_date; > + unsigned int year, month, day, hour, minute, second, wday; > + > + rtc_time = readl(ioaddr + RTC_TIME_REG_OFFS); > + rtc_date = readl(ioaddr + RTC_DATE_REG_OFFS); > + > + second = rtc_time & 0x7f; > + minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f; > + hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */ > + wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7; > + > + day = rtc_date & 0x3f; > + month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f; > + year = (rtc_date >> RTC_YEAR_OFFS) & 0xff; > + > + tm->tm_sec = bcd2bin(second); > + tm->tm_min = bcd2bin(minute); > + tm->tm_hour = bcd2bin(hour); > + tm->tm_mday = bcd2bin(day); > + tm->tm_wday = bcd2bin(wday); > + tm->tm_mon = bcd2bin(month) - 1; > + /* hw counts from year 2000, but tm_year is relative to 1900 */ > + tm->tm_year = bcd2bin(year) + 100; > + > + return rtc_valid_tm(tm); > +} > + > +static const struct rtc_class_ops mv_rtc_ops = { > + .read_time = mv_rtc_read_time, > + .set_time = mv_rtc_set_time, > +}; > + > +static int __init mv_rtc_probe(struct platform_device *pdev) > +{ > + struct resource *res; > + struct rtc_plat_data *pdata; > + resource_size_t size; > + u32 rtc_time; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > + > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + size = resource_size(res); > + if (!devm_request_mem_region(&pdev->dev, res->start, size, > + pdev->name)) > + return -EBUSY; leak. > + pdata->ioaddr = devm_ioremap(&pdev->dev, res->start, size); > + if (!pdata->ioaddr) > + return -ENOMEM; double leak. > + /* make sure the 24 hours mode is enabled */ > + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); > + if (rtc_time & RTC_HOURS_12H_MODE) { > + dev_err(&pdev->dev, "24 Hours mode not supported.\n"); > + return -EINVAL; triple leak. > + } > + > + platform_set_drvdata(pdev, pdata); > + pdata->rtc = rtc_device_register(pdev->name, &pdev->dev, > + &mv_rtc_ops, THIS_MODULE); > + if (IS_ERR(pdata->rtc)) > + return PTR_ERR(pdata->rtc); > + > + return 0; > +} > + > +static int __exit mv_rtc_remove(struct platform_device *pdev) > +{ > + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); > + > + rtc_device_unregister(pdata->rtc); > + return 0; > +} > + > +static struct platform_driver mv_rtc_driver = { > + .remove = __exit_p(mv_rtc_remove), > + .driver = { > + .name = "rtc-mv", > + .owner = THIS_MODULE, > + }, > +}; > + > +static __init int mv_init(void) > +{ > + return platform_driver_probe(&mv_rtc_driver, mv_rtc_probe); > +} > + > +static __exit void mv_exit(void) > +{ > + platform_driver_unregister(&mv_rtc_driver); > +} > + > +module_init(mv_init); > +module_exit(mv_exit); > + > +MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>"); > +MODULE_DESCRIPTION("Marvell RTC driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:rtc-mv"); --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~---
On Tue, 9 Dec 2008, Andrew Morton wrote: > > +config RTC_DRV_MV > > + tristate "Marvell SoC RTC" > > + depends on ARCH_KIRKWOOD > > + help > > + If you say yes here you will get support for the in-chip RTC > > + that can be found in some of Marvell's SoC devices, such as > > + the Kirkwood 88F6281 and 88F6192. > > + > > + This driver can also be built as a module. If so, the module > > + will be called rtc-mv. > > hm, I wonder if "rtc-mv" is a good name. It implies that this is the > only RTC device which Marvell will ever make. Heh. This is the best name that came out internally within Marvell. So if we ever produce another RTC variant then maybe we'll have ground to rename things appropriately. > > + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); > > + if (!pdata) > > + return -ENOMEM; > > + > > + size = resource_size(res); > > + if (!devm_request_mem_region(&pdev->dev, res->start, size, > > + pdev->name)) > > + return -EBUSY; > > leak. Isn't "devm_" in devm_kzalloc() supposed to mean there is no leak? > > + pdata->ioaddr = devm_ioremap(&pdev->dev, res->start, size); > > + if (!pdata->ioaddr) > > + return -ENOMEM; > > double leak. Ditto. > > + /* make sure the 24 hours mode is enabled */ > > + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); > > + if (rtc_time & RTC_HOURS_12H_MODE) { > > + dev_err(&pdev->dev, "24 Hours mode not supported.\n"); > > + return -EINVAL; > > triple leak. Double ditto. Nicolas --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~---
On Tue, 9 Dec 2008 14:56:59 -0500 (EST) Nicolas Pitre <nico@marvell.com> wrote: > > leak. > > Isn't "devm_" in devm_kzalloc() supposed to mean there is no leak? Oh. That shows how much atention I paid when that stuff went it. wtf did we do that for? Sigh. --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~---
On Tue, 9 Dec 2008, Andrew Morton wrote: > On Tue, 9 Dec 2008 14:56:59 -0500 (EST) > Nicolas Pitre <nico@marvell.com> wrote: > > > > leak. > > > > Isn't "devm_" in devm_kzalloc() supposed to mean there is no leak? > > Oh. That shows how much atention I paid when that stuff went it. It's been in mainline for nearly 2 years now. Amazing you didn't come across it since then. > wtf did we do that for? Sigh. http://lwn.net/Articles/215996/ http://lwn.net/Articles/222860/ Nicolas --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~---
On Tue, 9 Dec 2008 15:25:18 -0500 (EST) Nicolas Pitre <nico@marvell.com> wrote: > On Tue, 9 Dec 2008, Andrew Morton wrote: > > > On Tue, 9 Dec 2008 14:56:59 -0500 (EST) > > Nicolas Pitre <nico@marvell.com> wrote: > > > > > > leak. > > > > > > Isn't "devm_" in devm_kzalloc() supposed to mean there is no leak? > > > > Oh. That shows how much atention I paid when that stuff went it. > > It's been in mainline for nearly 2 years now. Amazing you didn't come > across it since then. Oh, I saw it go past, but I didn't notice this aspect of it. I would have objected had I done so. > > wtf did we do that for? Sigh. > > http://lwn.net/Articles/215996/ > http://lwn.net/Articles/222860/ Yeah. We add permanent cost to every machine running this code, for all time. Just to save programmers and reviewers a once-off five minute effort. That is a stupid, stupid tradeoff decision. --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~---
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5caa82d..b2cf166 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -704,4 +704,15 @@ config RTC_DRV_STARFIRE If you say Y here you will get support for the RTC found on Starfire systems. +config RTC_DRV_MV + tristate "Marvell SoC RTC" + depends on ARCH_KIRKWOOD + help + If you say yes here you will get support for the in-chip RTC + that can be found in some of Marvell's SoC devices, such as + the Kirkwood 88F6281 and 88F6192. + + This driver can also be built as a module. If so, the module + will be called rtc-mv. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fca0c9b..87a3adc 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c new file mode 100644 index 0000000..45f12dc --- /dev/null +++ b/drivers/rtc/rtc-mv.c @@ -0,0 +1,163 @@ +/* + * Driver for the RTC in Marvell SoCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/io.h> +#include <linux/platform_device.h> + + +#define RTC_TIME_REG_OFFS 0 +#define RTC_SECONDS_OFFS 0 +#define RTC_MINUTES_OFFS 8 +#define RTC_HOURS_OFFS 16 +#define RTC_WDAY_OFFS 24 +#define RTC_HOURS_12H_MODE (1 << 22) /* 12 hours mode */ + +#define RTC_DATE_REG_OFFS 4 +#define RTC_MDAY_OFFS 0 +#define RTC_MONTH_OFFS 8 +#define RTC_YEAR_OFFS 16 + + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; +}; + +static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_reg; + + rtc_reg = (bin2bcd(tm->tm_sec) << RTC_SECONDS_OFFS) | + (bin2bcd(tm->tm_min) << RTC_MINUTES_OFFS) | + (bin2bcd(tm->tm_hour) << RTC_HOURS_OFFS) | + (bin2bcd(tm->tm_wday) << RTC_WDAY_OFFS); + writel(rtc_reg, ioaddr + RTC_TIME_REG_OFFS); + + rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) | + (bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) | + (bin2bcd(tm->tm_year % 100) << RTC_YEAR_OFFS); + writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS); + + return 0; +} + +static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_time, rtc_date; + unsigned int year, month, day, hour, minute, second, wday; + + rtc_time = readl(ioaddr + RTC_TIME_REG_OFFS); + rtc_date = readl(ioaddr + RTC_DATE_REG_OFFS); + + second = rtc_time & 0x7f; + minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f; + hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */ + wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7; + + day = rtc_date & 0x3f; + month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f; + year = (rtc_date >> RTC_YEAR_OFFS) & 0xff; + + tm->tm_sec = bcd2bin(second); + tm->tm_min = bcd2bin(minute); + tm->tm_hour = bcd2bin(hour); + tm->tm_mday = bcd2bin(day); + tm->tm_wday = bcd2bin(wday); + tm->tm_mon = bcd2bin(month) - 1; + /* hw counts from year 2000, but tm_year is relative to 1900 */ + tm->tm_year = bcd2bin(year) + 100; + + return rtc_valid_tm(tm); +} + +static const struct rtc_class_ops mv_rtc_ops = { + .read_time = mv_rtc_read_time, + .set_time = mv_rtc_set_time, +}; + +static int __init mv_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rtc_plat_data *pdata; + resource_size_t size; + u32 rtc_time; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + size = resource_size(res); + if (!devm_request_mem_region(&pdev->dev, res->start, size, + pdev->name)) + return -EBUSY; + + pdata->ioaddr = devm_ioremap(&pdev->dev, res->start, size); + if (!pdata->ioaddr) + return -ENOMEM; + + /* make sure the 24 hours mode is enabled */ + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); + if (rtc_time & RTC_HOURS_12H_MODE) { + dev_err(&pdev->dev, "24 Hours mode not supported.\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, pdata); + pdata->rtc = rtc_device_register(pdev->name, &pdev->dev, + &mv_rtc_ops, THIS_MODULE); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + return 0; +} + +static int __exit mv_rtc_remove(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + rtc_device_unregister(pdata->rtc); + return 0; +} + +static struct platform_driver mv_rtc_driver = { + .remove = __exit_p(mv_rtc_remove), + .driver = { + .name = "rtc-mv", + .owner = THIS_MODULE, + }, +}; + +static __init int mv_init(void) +{ + return platform_driver_probe(&mv_rtc_driver, mv_rtc_probe); +} + +static __exit void mv_exit(void) +{ + platform_driver_unregister(&mv_rtc_driver); +} + +module_init(mv_init); +module_exit(mv_exit); + +MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>"); +MODULE_DESCRIPTION("Marvell RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-mv");