diff options
Diffstat (limited to 'target/linux/sunxi/patches-3.12/175-sunxi-rtc.patch')
-rw-r--r-- | target/linux/sunxi/patches-3.12/175-sunxi-rtc.patch | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-3.12/175-sunxi-rtc.patch b/target/linux/sunxi/patches-3.12/175-sunxi-rtc.patch new file mode 100644 index 0000000000..615658aeef --- /dev/null +++ b/target/linux/sunxi/patches-3.12/175-sunxi-rtc.patch @@ -0,0 +1,542 @@ +From 9b6e3291426efc69d1e8bf257721997f3eeb3009 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <carlo.caione@gmail.com> +Date: Wed, 16 Oct 2013 20:30:27 +0200 +Subject: [PATCH] ARM: sun4i/sun7i: RTC driver + +This patch introduces the driver for the RTC in the Allwinner A10 and +A20 SoCs. + +Signed-off-by: Carlo Caione <carlo.caione@gmail.com> +--- + drivers/rtc/Kconfig | 7 + + drivers/rtc/Makefile | 1 + + drivers/rtc/rtc-sunxi.c | 487 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 495 insertions(+) + create mode 100644 drivers/rtc/rtc-sunxi.c + +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 9654aa3..ef45e0b 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1076,6 +1076,13 @@ config RTC_DRV_SUN4V + If you say Y here you will get support for the Hypervisor + based RTC on SUN4V systems. + ++config RTC_DRV_SUNXI ++ tristate "Allwinner sun4i/sun7i RTC" ++ depends on ARCH_SUNXI ++ help ++ If you say Y here you will get support for the RTC found on ++ Allwinner A10/A20. ++ + config RTC_DRV_STARFIRE + bool "Starfire RTC" + depends on SPARC64 +diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile +index 2dff3d2..8b52b5a 100644 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -115,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o + obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o + obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o + obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o ++obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o + obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o + obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o + obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o +diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c +new file mode 100644 +index 0000000..ccd48ae +--- /dev/null ++++ b/drivers/rtc/rtc-sunxi.c +@@ -0,0 +1,487 @@ ++/* ++ * An RTC driver for Allwinner A10/A20 ++ * ++ * Copyright (c) 2013, Carlo Caione <carlo.caione@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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/rtc.h> ++#include <linux/types.h> ++ ++#define SUNXI_LOSC_CTRL 0x0000 ++#define SUNXI_LOSC_CTRL_RTC_HMS_ACC BIT(8) ++#define SUNXI_LOSC_CTRL_RTC_YMD_ACC BIT(7) ++ ++#define SUNXI_RTC_YMD 0x0004 ++ ++#define SUNXI_RTC_HMS 0x0008 ++ ++#define SUNXI_ALRM_DHMS 0x000c ++ ++#define SUNXI_ALRM_EN 0x0014 ++#define SUNXI_ALRM_EN_CNT_EN BIT(8) ++ ++#define SUNXI_ALRM_IRQ_EN 0x0018 ++#define SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) ++ ++#define SUNXI_ALRM_IRQ_STA 0x001c ++#define SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) ++ ++#define SUNXI_LOSC_CTRL_RTC_ACC \ ++ (SUNXI_LOSC_CTRL_RTC_HMS_ACC | SUNXI_LOSC_CTRL_RTC_YMD_ACC) ++ ++#define SUNXI_MASK_DH 0x0000001f ++#define SUNXI_MASK_SM 0x0000003f ++#define SUNXI_MASK_M 0x0000000f ++#define SUNXI_MASK_LY 0x00000001 ++#define SUNXI_MASK_D 0x00000ffe ++#define SUNXI_MASK_M 0x0000000f ++ ++#define SUNXI_GET(x, mask, shift) (((x) & ((mask) << (shift))) \ ++ >> (shift)) ++ ++#define SUNXI_SET(x, mask, shift) (((x) & (mask)) << (shift)) ++ ++/* Get date values */ ++#define SUNXI_DATE_GET_DAY_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 0) ++#define SUNXI_DATE_GET_MON_VALUE(x) SUNXI_GET(x, SUNXI_MASK_M, 8) ++#define SUNXI_DATE_GET_YEAR_VALUE(x, mask) SUNXI_GET(x, mask, 16) ++ ++/* Get time values */ ++#define SUNXI_TIME_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) ++#define SUNXI_TIME_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) ++#define SUNXI_TIME_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) ++ ++/* Get alarm values */ ++#define SUNXI_ALRM_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) ++#define SUNXI_ALRM_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) ++#define SUNXI_ALRM_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) ++ ++/* Set date values */ ++#define SUNXI_DATE_SET_DAY_VALUE(x) SUNXI_DATE_GET_DAY_VALUE(x) ++#define SUNXI_DATE_SET_MON_VALUE(x) SUNXI_SET(x, SUNXI_MASK_M, 8) ++#define SUNXI_DATE_SET_YEAR_VALUE(x, mask) SUNXI_SET(x, mask, 16) ++#define SUNXI_LEAP_SET_VALUE(x, shift) SUNXI_SET(x, SUNXI_MASK_LY, shift) ++ ++/* Set time values */ ++#define SUNXI_TIME_SET_SEC_VALUE(x) SUNXI_TIME_GET_SEC_VALUE(x) ++#define SUNXI_TIME_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) ++#define SUNXI_TIME_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) ++ ++/* set alarm values */ ++#define SUNXI_ALRM_SET_SEC_VALUE(x) SUNXI_ALRM_GET_SEC_VALUE(x) ++#define SUNXI_ALRM_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) ++#define SUNXI_ALRM_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) ++#define SUNXI_ALRM_SET_DAY_VALUE(x) SUNXI_SET(x, SUNXI_MASK_D, 21) ++ ++/* time unit conversions */ ++#define SEC_IN_MIN 60 ++#define SEC_IN_HOUR (60 * SEC_IN_MIN) ++#define SEC_IN_DAY (24 * SEC_IN_HOUR) ++ ++struct sunxi_rtc_data_year { ++ unsigned int min; /* min year allowed */ ++ unsigned int max; /* max year allowed */ ++ unsigned int off; /* data year offset */ ++ unsigned int mask; ++ unsigned char leap_shift; /* bit shift to get the leap year */ ++}; ++ ++static struct sunxi_rtc_data_year data_year_param[] = { ++ [0] = { ++ .min = 1970, ++ .max = 2100, ++ .off = 0, ++ .mask = 0x000000ff, ++ .leap_shift = 24, ++ }, ++ [1] = { ++ .min = 2010, ++ .max = 2073, ++ .off = 110, ++ .mask = 0x0000003f, ++ .leap_shift = 22, ++ }, ++}; ++ ++struct sunxi_rtc_dev { ++ struct rtc_device *rtc; ++ struct device *dev; ++ struct sunxi_rtc_data_year *data_year; ++ void __iomem *base; ++ int irq; ++}; ++ ++static irqreturn_t sunxi_rtc_alarmirq(int irq, void *id) ++{ ++ struct sunxi_rtc_dev *chip = (struct sunxi_rtc_dev *) id; ++ u32 val; ++ ++ val = readl(chip->base + SUNXI_ALRM_IRQ_STA); ++ ++ if (val & SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND) { ++ val |= SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND; ++ writel(val, chip->base + SUNXI_ALRM_IRQ_STA); ++ ++ rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); ++ ++ return IRQ_HANDLED; ++ } ++ ++ return IRQ_NONE; ++} ++ ++static void sunxi_rtc_setaie(int to, struct sunxi_rtc_dev *chip) ++{ ++ u32 alarm_val = 0; ++ u32 alarm_irq_val = 0; ++ ++ if (to) { ++ alarm_val = readl(chip->base + SUNXI_ALRM_EN); ++ alarm_val |= SUNXI_ALRM_EN_CNT_EN; ++ ++ alarm_irq_val = readl(chip->base + SUNXI_ALRM_IRQ_EN); ++ alarm_irq_val |= SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN; ++ } else { ++ writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, ++ chip->base + SUNXI_ALRM_IRQ_STA); ++ } ++ ++ writel(alarm_val, chip->base + SUNXI_ALRM_EN); ++ writel(alarm_irq_val, chip->base + SUNXI_ALRM_IRQ_EN); ++} ++ ++static int sunxi_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); ++ struct rtc_time *alrm_tm = &alrm->time; ++ u32 alarm; ++ u32 alarm_en; ++ u32 date; ++ ++ alarm = readl(chip->base + SUNXI_ALRM_DHMS); ++ date = readl(chip->base + SUNXI_RTC_YMD); ++ ++ alrm_tm->tm_sec = SUNXI_ALRM_GET_SEC_VALUE(alarm); ++ alrm_tm->tm_min = SUNXI_ALRM_GET_MIN_VALUE(alarm); ++ alrm_tm->tm_hour = SUNXI_ALRM_GET_HOUR_VALUE(alarm); ++ ++ alrm_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); ++ alrm_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); ++ alrm_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, ++ chip->data_year->mask); ++ ++ alrm_tm->tm_year += chip->data_year->off; ++ alrm_tm->tm_mon -= 1; ++ ++ alarm_en = readl(chip->base + SUNXI_ALRM_IRQ_EN); ++ if (alarm_en & SUNXI_ALRM_EN_CNT_EN) ++ alrm->enabled = 1; ++ ++ return 0; ++} ++ ++static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) ++{ ++ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); ++ u32 date, time; ++ int t; ++ ++ /* read again if the system was mid-updated ++ */ ++ for (t = 0; t < 2; t++) { ++ date = readl(chip->base + SUNXI_RTC_YMD); ++ time = readl(chip->base + SUNXI_RTC_HMS); ++ ++ rtc_tm->tm_sec = SUNXI_TIME_GET_SEC_VALUE(time); ++ rtc_tm->tm_min = SUNXI_TIME_GET_MIN_VALUE(time); ++ rtc_tm->tm_hour = SUNXI_TIME_GET_HOUR_VALUE(time); ++ ++ rtc_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); ++ rtc_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); ++ rtc_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, ++ chip->data_year->mask); ++ ++ if (rtc_tm->tm_sec == 0) ++ msleep(500); ++ else ++ break; ++ } ++ ++ rtc_tm->tm_year += chip->data_year->off; ++ rtc_tm->tm_mon -= 1; ++ ++ return rtc_valid_tm(rtc_tm); ++} ++ ++static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); ++ struct rtc_time *alrm_tm = &alrm->time; ++ struct rtc_time tm_now; ++ u32 alarm = 0; ++ unsigned long time_now = 0; ++ unsigned long time_set = 0; ++ unsigned long time_gap = 0; ++ unsigned long time_gap_day = 0; ++ unsigned long time_gap_hour = 0; ++ unsigned long time_gap_min = 0; ++ int ret = 0; ++ ++ ret = sunxi_rtc_gettime(dev, &tm_now); ++ if (ret < 0) { ++ dev_err(dev, "Error in getting time\n"); ++ return -EINVAL; ++ } ++ ++ rtc_tm_to_time(alrm_tm, &time_set); ++ rtc_tm_to_time(&tm_now, &time_now); ++ if (time_set <= time_now) { ++ dev_err(dev, "Date to set in the past\n"); ++ return -EINVAL; ++ } ++ ++ time_gap = time_set - time_now; ++ time_gap_day = time_gap / SEC_IN_DAY; ++ time_gap -= time_gap_day * SEC_IN_DAY; ++ time_gap_hour = time_gap / SEC_IN_HOUR; ++ time_gap -= time_gap_hour * SEC_IN_HOUR; ++ time_gap_min = time_gap / SEC_IN_MIN; ++ time_gap -= time_gap_min * SEC_IN_MIN; ++ ++ if (time_gap_day > 255) { ++ dev_err(dev, "Day must be in the range 0 - 255\n"); ++ return -EINVAL; ++ } ++ ++ sunxi_rtc_setaie(0, chip); ++ writel(0, chip->base + SUNXI_ALRM_DHMS); ++ usleep_range(100, 300); ++ ++ alarm = SUNXI_ALRM_SET_SEC_VALUE(time_gap) | ++ SUNXI_ALRM_SET_MIN_VALUE(time_gap_min) | ++ SUNXI_ALRM_SET_HOUR_VALUE(time_gap_hour) | ++ SUNXI_ALRM_SET_DAY_VALUE(time_gap_day); ++ writel(alarm, chip->base + SUNXI_ALRM_DHMS); ++ ++ writel(0, chip->base + SUNXI_ALRM_IRQ_EN); ++ writel(SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN, chip->base + SUNXI_ALRM_IRQ_EN); ++ ++ sunxi_rtc_setaie(alrm->enabled, chip); ++ ++ return 0; ++} ++ ++static int sunxi_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) ++{ ++ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); ++ u32 date = 0; ++ u32 time = 0; ++ int year; ++ int t; ++ ++ year = rtc_tm->tm_year + 1900; ++ if (year < chip->data_year->min || year > chip->data_year->max) { ++ dev_err(dev, "rtc only supports year in range %d - %d\n", ++ chip->data_year->min, chip->data_year->max); ++ return -EINVAL; ++ } ++ ++ rtc_tm->tm_year -= chip->data_year->off; ++ rtc_tm->tm_mon += 1; ++ ++ date = SUNXI_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | ++ SUNXI_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | ++ SUNXI_DATE_SET_YEAR_VALUE(rtc_tm->tm_year, ++ chip->data_year->mask); ++ ++ if (is_leap_year(year)) ++ date |= SUNXI_LEAP_SET_VALUE(1, chip->data_year->leap_shift); ++ ++ time = SUNXI_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | ++ SUNXI_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | ++ SUNXI_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); ++ ++ writel(0, chip->base + SUNXI_RTC_HMS); ++ writel(0, chip->base + SUNXI_RTC_YMD); ++ ++ writel(time, chip->base + SUNXI_RTC_HMS); ++ ++ /* After writing the RCT HH-MM-SS register, the ++ * SUNXI_LOSC_CTRL_RTC_HMS_ACC bit is set and it will be cleared until ++ * the real writing operation is finished ++ */ ++ for (t = 0; t < 3; t++) { ++ if ((readl(chip->base + SUNXI_LOSC_CTRL) & ++ SUNXI_LOSC_CTRL_RTC_HMS_ACC) && --t) ++ break; ++ else ++ msleep(50); ++ } ++ if (t == 0) { ++ dev_err(dev, "Failed to set rtc time.\n"); ++ return -1; ++ } ++ ++ writel(date, chip->base + SUNXI_RTC_YMD); ++ ++ /* After writing the RCT YY-MM-DD register, the ++ * SUNXI_LOSC_CTRL_RTC_YMD_ACC bit is set and it will be cleared until ++ * the real writing operation is finished ++ */ ++ for (t = 0; t < 3; t++) { ++ if ((readl(chip->base + SUNXI_LOSC_CTRL) & ++ SUNXI_LOSC_CTRL_RTC_YMD_ACC) && --t) ++ break; ++ else ++ msleep(50); ++ } ++ if (t == 0) { ++ dev_err(dev, "Failed to set rtc date.\n"); ++ return -1; ++ } ++ ++ /* wait about 70us to make sure the the time is really written into ++ * target */ ++ usleep_range(70, 100); ++ ++ return 0; ++} ++ ++static int sunxi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) ++{ ++ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); ++ ++ if (!enabled) ++ sunxi_rtc_setaie(enabled, chip); ++ ++ return 0; ++} ++ ++static const struct rtc_class_ops sunxi_rtc_ops = { ++ .read_time = sunxi_rtc_gettime, ++ .set_time = sunxi_rtc_settime, ++ .read_alarm = sunxi_rtc_getalarm, ++ .set_alarm = sunxi_rtc_setalarm, ++ .alarm_irq_enable = sunxi_rtc_alarm_irq_enable ++}; ++ ++static const struct of_device_id sunxi_rtc_dt_ids[] = { ++ { .compatible = "allwinner,sun4i-rtc", .data = &data_year_param[0] }, ++ { .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, sunxi_rtc_dt_ids); ++ ++ ++static int sunxi_rtc_probe(struct platform_device *pdev) ++{ ++ struct sunxi_rtc_dev *chip; ++ struct resource *res; ++ const struct of_device_id *of_id; ++ int ret; ++ ++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, chip); ++ chip->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ chip->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(chip->base)) ++ return PTR_ERR(chip->base); ++ ++ chip->irq = platform_get_irq(pdev, 0); ++ if (chip->irq < 0) { ++ dev_err(&pdev->dev, "No IRQ resource\n"); ++ return chip->irq; ++ } ++ ret = devm_request_irq(&pdev->dev, chip->irq, sunxi_rtc_alarmirq, ++ 0, dev_name(&pdev->dev), chip); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not request IRQ\n"); ++ return ret; ++ } ++ ++ of_id = of_match_device(sunxi_rtc_dt_ids, &pdev->dev); ++ if (!of_id) { ++ dev_err(&pdev->dev, "Unable to setup RTC data\n"); ++ return -ENODEV; ++ } ++ chip->data_year = (struct sunxi_rtc_data_year *) of_id->data; ++ ++ /* clear the alarm count value */ ++ writel(0, chip->base + SUNXI_ALRM_DHMS); ++ ++ /* disable alarm, not generate irq pending */ ++ writel(0, chip->base + SUNXI_ALRM_EN); ++ ++ /* disable alarm week/cnt irq, unset to cpu */ ++ writel(0, chip->base + SUNXI_ALRM_IRQ_EN); ++ ++ /* clear alarm week/cnt irq pending */ ++ writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base + SUNXI_ALRM_IRQ_STA); ++ ++ chip->rtc = rtc_device_register("rtc-sunxi", &pdev->dev, ++ &sunxi_rtc_ops, THIS_MODULE); ++ if (IS_ERR(chip->rtc)) { ++ dev_err(&pdev->dev, "unable to register device\n"); ++ return PTR_ERR(chip->rtc); ++ } ++ ++ dev_info(&pdev->dev, "RTC enabled\n"); ++ ++ return 0; ++} ++ ++static int sunxi_rtc_remove(struct platform_device *pdev) ++{ ++ struct sunxi_rtc_dev *chip = platform_get_drvdata(pdev); ++ ++ rtc_device_unregister(chip->rtc); ++ ++ return 0; ++} ++ ++static struct platform_driver sunxi_rtc_driver = { ++ .probe = sunxi_rtc_probe, ++ .remove = sunxi_rtc_remove, ++ .driver = { ++ .name = "sunxi-rtc", ++ .owner = THIS_MODULE, ++ .of_match_table = sunxi_rtc_dt_ids, ++ }, ++}; ++ ++module_platform_driver(sunxi_rtc_driver); ++ ++MODULE_DESCRIPTION("sunxi RTC driver"); ++MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>"); ++MODULE_LICENSE("GPL"); +-- +1.8.4 + |