linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] ARM: sunxi: RTC support for A31/A23
@ 2014-07-14  7:32 Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver Chen-Yu Tsai
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This series adds support for the RTC in A31 and A23 SoCs. The series
applies to sunxi-next, with the sun8i patch based on my sun8i PRCM
series.

The A31 has a new RTC block that is separate from the timer block.
It has separate interrupts for each of the alarms, and a different
format for the counter alarm. The driver has been tested on the
A31 Hummingbird, and the A23 tablet I'm using to work on sun8i
support.

Patch 1 adds the driver for the RTC.

Patch 2 is a minor cleanup. This makes rtc-sunxi depend on sun4i or
sun7i, the 2 platforms the driver is actually used on.

Patch 3 adds the RTC device node to the sun6i DTSI.

Patch 4 adds the RTC device node to the sun8i DTSI.

Patch 5 adds enables rtc-sun6i in the sunxi defconfig.


Is there an RTC tree we need to merge the driver through?
Or can we merge all the patches through Maxime's sunxi tree?


Cheers
ChenYu


Chen-Yu Tsai (5):
  rtc: sun6i: Add sun6i RTC driver
  rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc
  ARM: dts: sun6i: add rtc device node
  ARM: dts: sun8i: add rtc device node
  ARM: sunxi: Add A31 RTC driver to sunxi_defconfig

 .../devicetree/bindings/rtc/sun6i-rtc.txt          |  17 +
 arch/arm/boot/dts/sun6i-a31.dtsi                   |   6 +
 arch/arm/boot/dts/sun8i-a23.dtsi                   |   6 +
 arch/arm/configs/sunxi_defconfig                   |   1 +
 drivers/rtc/Kconfig                                |   9 +-
 drivers/rtc/Makefile                               |   1 +
 drivers/rtc/rtc-sun6i.c                            | 466 +++++++++++++++++++++
 7 files changed, 505 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
 create mode 100644 drivers/rtc/rtc-sun6i.c

-- 
2.0.1

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver
  2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
@ 2014-07-14  7:32 ` Chen-Yu Tsai
  2014-07-18  8:07   ` Maxime Ripard
  2014-07-14  7:32 ` [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Chen-Yu Tsai
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

This patch introduces the driver for the RTC in the Allwinner A31 and
A23 SoCs.

Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
RTC in A31/A23 are a separate hardware block, which also contain a few
controls for the RTC block hardware (a regulator and RTC block GPIO pin
latches), while also having separate interrupts for the alarms.

The hardware is different enough to make a different driver for it.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 .../devicetree/bindings/rtc/sun6i-rtc.txt          |  17 +
 drivers/rtc/Kconfig                                |   7 +
 drivers/rtc/Makefile                               |   1 +
 drivers/rtc/rtc-sun6i.c                            | 466 +++++++++++++++++++++
 4 files changed, 491 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
 create mode 100644 drivers/rtc/rtc-sun6i.c

diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
new file mode 100644
index 0000000..b18927c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
@@ -0,0 +1,17 @@
+* sun6i Real Time Clock
+
+RTC controller for the Allwinner A31
+
+Required properties:
+- compatible : Should be "allwinner,sun6i-a31-rtc"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: IRQ line for the RTC alarm 0.
+
+Example:
+
+rtc: rtc at 01f00000 {
+	compatible = "allwinner,sun6i-a31-rtc";
+	reg = <0x01f00000 0x54>;
+	interrupts = <0 40 4>;
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0754f5c..5b3910a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1167,6 +1167,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_SUN6I
+	tristate "Allwinner sun6i/sun8i RTC"
+	depends on MACH_SUN6I || MACH_SUN8I
+	help
+	  If you say Y here you will get support for the RTC found on
+	  Allwinner A31/A23.
+
 config RTC_DRV_SUNXI
 	tristate "Allwinner sun4i/sun7i RTC"
 	depends on ARCH_SUNXI
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 70347d0..a47df29 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -123,6 +123,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_SUN6I)	+= rtc-sun6i.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
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
new file mode 100644
index 0000000..fabd019
--- /dev/null
+++ b/drivers/rtc/rtc-sun6i.c
@@ -0,0 +1,466 @@
+/*
+ * An RTC driver for Allwinner A31/A23
+ *
+ * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
+ *
+ * based on rtc-sunxi.c
+ *
+ * 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.
+ */
+
+#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>
+
+/* Control register */
+#define SUN6I_LOSC_CTRL				0x0000
+#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC		BIT(9)
+#define SUN6I_LOSC_CTRL_RTC_HMS_ACC		BIT(8)
+#define SUN6I_LOSC_CTRL_RTC_YMD_ACC		BIT(7)
+#define SUN6I_LOSC_CTRL_ACC_MASK		(BIT(9) | BIT(8) | BIT(7))
+
+/* RTC */
+#define SUN6I_RTC_YMD				0x0010
+#define SUN6I_RTC_HMS				0x0014
+
+/* Alarm 0 (counter) */
+#define SUN6I_ALRM_COUNTER			0x0020
+#define SUN6I_ALRM_CUR_VAL			0x0024
+#define SUN6I_ALRM_EN				0x0028
+#define SUN6I_ALRM_EN_CNT_EN			BIT(0)
+#define SUN6I_ALRM_IRQ_EN			0x002c
+#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN		BIT(0)
+#define SUN6I_ALRM_IRQ_STA			0x0030
+#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND		BIT(0)
+
+/* Alarm 1 (wall clock) */
+#define SUN6I_ALRM1_EN				0x0044
+#define SUN6I_ALRM1_IRQ_EN			0x0048
+#define SUN6I_ALRM1_IRQ_STA			0x004c
+#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND	BIT(0)
+
+/* Alarm config */
+#define SUN6I_ALARM_CONFIG			0x0050
+#define SUN6I_ALARM_CONFIG_WAKEUP		BIT(0)
+
+/* days / hours are 5 bit wide */
+#define SUN6I_MASK_DH				0x0000001f
+/* seconds / minutes / years are 6 bit wide */
+#define SUN6I_MASK_SMY				0x0000003f
+/* months are 4 bit wide */
+#define SUN6I_MASK_M				0x0000000f
+/* leap year is single bit */
+#define SUN6I_MASK_LY				0x00000001
+
+#define SUN6I_GET(x, mask, shift)		(((x) & ((mask) << (shift))) \
+							>> (shift))
+
+#define SUN6I_SET(x, mask, shift)		(((x) & (mask)) << (shift))
+
+/*
+ * Get date values
+ */
+#define SUN6I_DATE_GET_DAY_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_DH, 0)
+#define SUN6I_DATE_GET_MON_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_M, 8)
+#define SUN6I_DATE_GET_YEAR_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 16)
+
+/*
+ * Get time values
+ */
+#define SUN6I_TIME_GET_SEC_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 0)
+#define SUN6I_TIME_GET_MIN_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 8)
+#define SUN6I_TIME_GET_HOUR_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_DH, 16)
+
+/*
+ * Set date values
+ */
+#define SUN6I_DATE_SET_DAY_VALUE(x)		SUN6I_DATE_GET_DAY_VALUE(x)
+#define SUN6I_DATE_SET_MON_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_M, 8)
+#define SUN6I_DATE_SET_YEAR_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_SMY, 16)
+#define SUN6I_LEAP_SET_VALUE(x)			SUN6I_SET(x, SUN6I_MASK_LY, 22)
+
+/*
+ * Set time values
+ */
+#define SUN6I_TIME_SET_SEC_VALUE(x)		SUN6I_TIME_GET_SEC_VALUE(x)
+#define SUN6I_TIME_SET_MIN_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_SMY, 8)
+#define SUN6I_TIME_SET_HOUR_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_DH, 16)
+
+/*
+ * The year parameter passed to the driver is usually an offset relative to
+ * the year 1900. This macro is used to convert this offset to another one
+ * relative to the minimum year allowed by the hardware.
+ *
+ * The year range is 1970 - 2033. This range is selected to match Allwinner's
+ * driver, even though it is somewhat limited.
+ */
+#define SUN6I_YEAR_MIN				1970
+#define SUN6I_YEAR_MAX				2033
+#define SUN6I_YEAR_OFF				(SUN6I_YEAR_MIN - 1900)
+
+struct sun6i_rtc_dev {
+	struct rtc_device *rtc;
+	struct device *dev;
+	void __iomem *base;
+	int irq;
+	unsigned long alarm;
+};
+
+static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
+{
+	struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
+	u32 val;
+
+	val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+
+	if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
+		val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
+		writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
+
+		rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
+{
+	u32 alrm_val = 0;
+	u32 alrm_irq_val = 0;
+	u32 alrm_wake_val = 0;
+
+	if (to) {
+		alrm_val = SUN6I_ALRM_EN_CNT_EN;
+		alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
+		alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
+	} else {
+		writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
+				chip->base + SUN6I_ALRM_IRQ_STA);
+	}
+
+	writel(alrm_val, chip->base + SUN6I_ALRM_EN);
+	writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
+	writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
+}
+
+static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+	u32 date, time;
+
+	/*
+	 * read again in case it changes
+	 */
+	do {
+		date = readl(chip->base + SUN6I_RTC_YMD);
+		time = readl(chip->base + SUN6I_RTC_HMS);
+	} while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
+		 (time != readl(chip->base + SUN6I_RTC_HMS)));
+
+	rtc_tm->tm_sec  = SUN6I_TIME_GET_SEC_VALUE(time);
+	rtc_tm->tm_min  = SUN6I_TIME_GET_MIN_VALUE(time);
+	rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
+
+	rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
+	rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date);
+	rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
+
+	rtc_tm->tm_mon  -= 1;
+
+	/*
+	 * switch from (data_year->min)-relative offset to
+	 * a (1900)-relative one
+	 */
+	rtc_tm->tm_year += SUN6I_YEAR_OFF;
+
+	return rtc_valid_tm(rtc_tm);
+}
+
+static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+	u32 alrm_st;
+	u32 alrm_en;
+
+	alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
+	alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+	wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
+	wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
+	rtc_time_to_tm(chip->alarm, &wkalrm->time);
+
+	return 0;
+}
+
+static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+	struct rtc_time *alrm_tm = &wkalrm->time;
+	struct rtc_time tm_now;
+	unsigned long time_now = 0;
+	unsigned long time_set = 0;
+	unsigned long time_gap = 0;
+	int ret = 0;
+
+	ret = sun6i_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;
+
+	if (time_gap > U32_MAX) {
+		dev_err(dev, "Date too far in the future\n");
+		return -EINVAL;
+	}
+
+	sun6i_rtc_setaie(0, chip);
+	writel(0, chip->base + SUN6I_ALRM_COUNTER);
+	usleep_range(100, 300);
+
+	writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
+	chip->alarm = time_set;
+
+	sun6i_rtc_setaie(wkalrm->enabled, chip);
+
+	return 0;
+}
+
+static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
+			  unsigned int mask, unsigned int ms_timeout)
+{
+	const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
+	u32 reg;
+
+	do {
+		reg = readl(chip->base + offset);
+		reg &= mask;
+
+		if (!reg)
+			return 0;
+
+	} while (time_before(jiffies, timeout));
+
+	return -ETIMEDOUT;
+}
+
+static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+{
+	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+	u32 date = 0;
+	u32 time = 0;
+	int year;
+
+	/*
+	 * the input rtc_tm->tm_year is the offset relative to 1900. We use
+	 * the SUN6I_YEAR_OFF macro to rebase it with respect to the min year
+	 * allowed by the hardware
+	 */
+
+	year = rtc_tm->tm_year + 1900;
+	if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
+		dev_err(dev, "rtc only supports year in range %d - %d\n",
+				SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
+		return -EINVAL;
+	}
+
+	rtc_tm->tm_year -= SUN6I_YEAR_OFF;
+	rtc_tm->tm_mon += 1;
+
+	date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+		SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
+		SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
+
+	if (is_leap_year(year))
+		date |= SUN6I_LEAP_SET_VALUE(1);
+
+	time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |
+		SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |
+		SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
+
+	/* Check whether registers are writable */
+	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+				SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
+		dev_err(dev, "rtc is still busy.\n");
+		return -EBUSY;
+	}
+
+	writel(time, chip->base + SUN6I_RTC_HMS);
+
+	/*
+	 * After writing the RTC HH-MM-SS register, the
+	 * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
+	 * be cleared until the real writing operation is finished
+	 */
+
+	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+				SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
+		dev_err(dev, "Failed to set rtc time.\n");
+		return -ETIMEDOUT;
+	}
+
+	writel(date, chip->base + SUN6I_RTC_YMD);
+
+	/*
+	 * After writing the RTC YY-MM-DD register, the
+	 * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
+	 * be cleared until the real writing operation is finished
+	 */
+
+	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+				SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
+		dev_err(dev, "Failed to set rtc time.\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+	if (!enabled)
+		sun6i_rtc_setaie(enabled, chip);
+
+	return 0;
+}
+
+static const struct rtc_class_ops sun6i_rtc_ops = {
+	.read_time		= sun6i_rtc_gettime,
+	.set_time		= sun6i_rtc_settime,
+	.read_alarm		= sun6i_rtc_getalarm,
+	.set_alarm		= sun6i_rtc_setalarm,
+	.alarm_irq_enable	= sun6i_rtc_alarm_irq_enable
+};
+
+static const struct of_device_id sun6i_rtc_dt_ids[] = {
+	{ .compatible = "allwinner,sun6i-a31-rtc" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
+
+static int sun6i_rtc_probe(struct platform_device *pdev)
+{
+	struct sun6i_rtc_dev *chip;
+	struct resource *res;
+	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, sun6i_rtc_alarmirq,
+			0, dev_name(&pdev->dev), chip);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not request IRQ\n");
+		return ret;
+	}
+
+	/* clear the alarm counter value */
+	writel(0, chip->base + SUN6I_ALRM_COUNTER);
+
+	/* disable counter alarm */
+	writel(0, chip->base + SUN6I_ALRM_EN);
+
+	/* disable counter alarm interrupt */
+	writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
+
+	/* disable week alarm */
+	writel(0, chip->base + SUN6I_ALRM1_EN);
+
+	/* disable week alarm interrupt */
+	writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
+
+	/* clear counter alarm pending interrupts */
+	writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +
+			SUN6I_ALRM_IRQ_STA);
+
+	/* clear week alarm pending interrupts */
+	writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, chip->base +
+			SUN6I_ALRM1_IRQ_STA);
+
+	/* disable alarm wakeup */
+	writel(0, chip->base + SUN6I_ALARM_CONFIG);
+
+	chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
+			&sun6i_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 sun6i_rtc_remove(struct platform_device *pdev)
+{
+	struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(chip->rtc);
+
+	return 0;
+}
+
+static struct platform_driver sun6i_rtc_driver = {
+	.probe		= sun6i_rtc_probe,
+	.remove		= sun6i_rtc_remove,
+	.driver		= {
+		.name		= "sun6i-rtc",
+		.owner		= THIS_MODULE,
+		.of_match_table = sun6i_rtc_dt_ids,
+	},
+};
+
+module_platform_driver(sun6i_rtc_driver);
+
+MODULE_DESCRIPTION("sun6i RTC driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
-- 
2.0.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc
  2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver Chen-Yu Tsai
@ 2014-07-14  7:32 ` Chen-Yu Tsai
  2014-07-18  8:11   ` Maxime Ripard
  2014-07-14  7:32 ` [PATCH 3/5] ARM: dts: sun6i: add rtc device node Chen-Yu Tsai
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Now that we have Kconfig options for individual sunxi platforms, let
the rtc-sunxi driver depend on the platforms that actually have this
hardware, sun4i and sun7i.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/rtc/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5b3910a..09a8d01 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1176,7 +1176,7 @@ config RTC_DRV_SUN6I
 
 config RTC_DRV_SUNXI
 	tristate "Allwinner sun4i/sun7i RTC"
-	depends on ARCH_SUNXI
+	depends on MACH_SUN4I || MACH_SUN7I
 	help
 	  If you say Y here you will get support for the RTC found on
 	  Allwinner A10/A20.
-- 
2.0.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/5] ARM: dts: sun6i: add rtc device node
  2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Chen-Yu Tsai
@ 2014-07-14  7:32 ` Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 4/5] ARM: dts: sun8i: " Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig Chen-Yu Tsai
  4 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Now that we have a driver for sun6i's rtc hardware, add a device node
for it so we can use it.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index a9dfa12..de351e1 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -691,6 +691,12 @@
 			interrupts = <1 9 0xf04>;
 		};
 
+		rtc: rtc at 01f00000 {
+			compatible = "allwinner,sun6i-a31-rtc";
+			reg = <0x01f00000 0x54>;
+			interrupts = <0 40 4>;
+		};
+
 		nmi_intc: interrupt-controller at 01f00c0c {
 			compatible = "allwinner,sun6i-a31-sc-nmi";
 			interrupt-controller;
-- 
2.0.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/5] ARM: dts: sun8i: add rtc device node
  2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
                   ` (2 preceding siblings ...)
  2014-07-14  7:32 ` [PATCH 3/5] ARM: dts: sun6i: add rtc device node Chen-Yu Tsai
@ 2014-07-14  7:32 ` Chen-Yu Tsai
  2014-07-14  7:32 ` [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig Chen-Yu Tsai
  4 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

sun8i shares the same rtc hardware as sun6i. Now that we have a driver
for it, add a device node to the DTSI for it so we can use it.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun8i-a23.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a23.dtsi b/arch/arm/boot/dts/sun8i-a23.dtsi
index 54ac0787..e0d1048 100644
--- a/arch/arm/boot/dts/sun8i-a23.dtsi
+++ b/arch/arm/boot/dts/sun8i-a23.dtsi
@@ -285,6 +285,12 @@
 			interrupts = <1 9 0xf04>;
 		};
 
+		rtc: rtc at 01f00000 {
+			compatible = "allwinner,sun6i-a31-rtc";
+			reg = <0x01f00000 0x54>;
+			interrupts = <0 40 4>;
+		};
+
 		prcm at 01f01400 {
 			compatible = "allwinner,sun8i-a23-prcm";
 			reg = <0x01f01400 0x200>;
-- 
2.0.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig
  2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
                   ` (3 preceding siblings ...)
  2014-07-14  7:32 ` [PATCH 4/5] ARM: dts: sun8i: " Chen-Yu Tsai
@ 2014-07-14  7:32 ` Chen-Yu Tsai
  2014-07-18  8:12   ` Maxime Ripard
  4 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-14  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

Now that we have a driver for A31's RTC, enable it
in the default sunxi config.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/configs/sunxi_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 7209bfd..0abc2ab 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -93,6 +93,7 @@ CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
 CONFIG_RTC_CLASS=y
 # CONFIG_RTC_INTF_SYSFS is not set
 # CONFIG_RTC_INTF_PROC is not set
+CONFIG_RTC_DRV_SUN6I=y
 CONFIG_RTC_DRV_SUNXI=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_PHY_SUN4I_USB=y
-- 
2.0.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver
  2014-07-14  7:32 ` [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver Chen-Yu Tsai
@ 2014-07-18  8:07   ` Maxime Ripard
  2014-07-21 14:46     ` Chen-Yu Tsai
  0 siblings, 1 reply; 13+ messages in thread
From: Maxime Ripard @ 2014-07-18  8:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Mon, Jul 14, 2014 at 03:32:13PM +0800, Chen-Yu Tsai wrote:
> This patch introduces the driver for the RTC in the Allwinner A31 and
> A23 SoCs.
> 
> Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
> RTC in A31/A23 are a separate hardware block, which also contain a few
> controls for the RTC block hardware (a regulator and RTC block GPIO pin
> latches), while also having separate interrupts for the alarms.

Do you plan on supporting those at some point?

It's also worth noting that the first registers are supposed to
control the source of the low frequency oscillator in the SoC, which
will probably be the most troublesome, since we need these clocks very
early on.

> 
> The hardware is different enough to make a different driver for it.
> 
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
>  .../devicetree/bindings/rtc/sun6i-rtc.txt          |  17 +
>  drivers/rtc/Kconfig                                |   7 +
>  drivers/rtc/Makefile                               |   1 +
>  drivers/rtc/rtc-sun6i.c                            | 466 +++++++++++++++++++++
>  4 files changed, 491 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
>  create mode 100644 drivers/rtc/rtc-sun6i.c
> 
> diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
> new file mode 100644
> index 0000000..b18927c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
> @@ -0,0 +1,17 @@
> +* sun6i Real Time Clock
> +
> +RTC controller for the Allwinner A31
> +
> +Required properties:
> +- compatible : Should be "allwinner,sun6i-a31-rtc"
> +- reg: physical base address of the controller and length of memory mapped
> +  region.
> +- interrupts: IRQ line for the RTC alarm 0.
> +
> +Example:
> +
> +rtc: rtc at 01f00000 {
> +	compatible = "allwinner,sun6i-a31-rtc";
> +	reg = <0x01f00000 0x54>;
> +	interrupts = <0 40 4>;
> +};
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 0754f5c..5b3910a 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1167,6 +1167,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_SUN6I
> +	tristate "Allwinner sun6i/sun8i RTC"

I'm half convinced about an exhaustive list here. That IP will also
probably be used by sun9i, and sun10i if it ever exists, etc. And you
exhaustive list won't be anymore.

I'd rather just mention the A31, like we do for the DT.

> +	depends on MACH_SUN6I || MACH_SUN8I
> +	help
> +	  If you say Y here you will get support for the RTC found on
> +	  Allwinner A31/A23.
> +
>  config RTC_DRV_SUNXI
>  	tristate "Allwinner sun4i/sun7i RTC"
>  	depends on ARCH_SUNXI
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 70347d0..a47df29 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -123,6 +123,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_SUN6I)	+= rtc-sun6i.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
> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> new file mode 100644
> index 0000000..fabd019
> --- /dev/null
> +++ b/drivers/rtc/rtc-sun6i.c
> @@ -0,0 +1,466 @@
> +/*
> + * An RTC driver for Allwinner A31/A23
> + *
> + * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
> + *
> + * based on rtc-sunxi.c
> + *
> + * 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.
> + */
> +
> +#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>
> +
> +/* Control register */
> +#define SUN6I_LOSC_CTRL				0x0000
> +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC		BIT(9)
> +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC		BIT(8)
> +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC		BIT(7)
> +#define SUN6I_LOSC_CTRL_ACC_MASK		(BIT(9) | BIT(8) | BIT(7))

GENMASK maybe?

> +
> +/* RTC */
> +#define SUN6I_RTC_YMD				0x0010
> +#define SUN6I_RTC_HMS				0x0014
> +
> +/* Alarm 0 (counter) */
> +#define SUN6I_ALRM_COUNTER			0x0020
> +#define SUN6I_ALRM_CUR_VAL			0x0024
> +#define SUN6I_ALRM_EN				0x0028
> +#define SUN6I_ALRM_EN_CNT_EN			BIT(0)
> +#define SUN6I_ALRM_IRQ_EN			0x002c
> +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN		BIT(0)
> +#define SUN6I_ALRM_IRQ_STA			0x0030
> +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND		BIT(0)
> +
> +/* Alarm 1 (wall clock) */
> +#define SUN6I_ALRM1_EN				0x0044
> +#define SUN6I_ALRM1_IRQ_EN			0x0048
> +#define SUN6I_ALRM1_IRQ_STA			0x004c
> +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND	BIT(0)
> +
> +/* Alarm config */
> +#define SUN6I_ALARM_CONFIG			0x0050
> +#define SUN6I_ALARM_CONFIG_WAKEUP		BIT(0)
> +
> +/* days / hours are 5 bit wide */
> +#define SUN6I_MASK_DH				0x0000001f
> +/* seconds / minutes / years are 6 bit wide */
> +#define SUN6I_MASK_SMY				0x0000003f
> +/* months are 4 bit wide */
> +#define SUN6I_MASK_M				0x0000000f
> +/* leap year is single bit */
> +#define SUN6I_MASK_LY				0x00000001

Ditto

> +
> +#define SUN6I_GET(x, mask, shift)		(((x) & ((mask) << (shift))) \
> +							>> (shift))
> +
> +#define SUN6I_SET(x, mask, shift)		(((x) & (mask)) << (shift))

Wouldn't it be easier to have the mask already shifted?

> +
> +/*
> + * Get date values
> + */
> +#define SUN6I_DATE_GET_DAY_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_DH, 0)
> +#define SUN6I_DATE_GET_MON_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_M, 8)
> +#define SUN6I_DATE_GET_YEAR_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 16)
> +
> +/*
> + * Get time values
> + */
> +#define SUN6I_TIME_GET_SEC_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 0)
> +#define SUN6I_TIME_GET_MIN_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_SMY, 8)
> +#define SUN6I_TIME_GET_HOUR_VALUE(x)		SUN6I_GET(x, SUN6I_MASK_DH, 16)
> +
> +/*
> + * Set date values
> + */
> +#define SUN6I_DATE_SET_DAY_VALUE(x)		SUN6I_DATE_GET_DAY_VALUE(x)
> +#define SUN6I_DATE_SET_MON_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_M, 8)
> +#define SUN6I_DATE_SET_YEAR_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_SMY, 16)
> +#define SUN6I_LEAP_SET_VALUE(x)			SUN6I_SET(x, SUN6I_MASK_LY, 22)
> +
> +/*
> + * Set time values
> + */
> +#define SUN6I_TIME_SET_SEC_VALUE(x)		SUN6I_TIME_GET_SEC_VALUE(x)
> +#define SUN6I_TIME_SET_MIN_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_SMY, 8)
> +#define SUN6I_TIME_SET_HOUR_VALUE(x)		SUN6I_SET(x, SUN6I_MASK_DH, 16)
> +
> +/*
> + * The year parameter passed to the driver is usually an offset relative to
> + * the year 1900. This macro is used to convert this offset to another one
> + * relative to the minimum year allowed by the hardware.
> + *
> + * The year range is 1970 - 2033. This range is selected to match Allwinner's
> + * driver, even though it is somewhat limited.
> + */
> +#define SUN6I_YEAR_MIN				1970
> +#define SUN6I_YEAR_MAX				2033
> +#define SUN6I_YEAR_OFF				(SUN6I_YEAR_MIN - 1900)
> +
> +struct sun6i_rtc_dev {
> +	struct rtc_device *rtc;
> +	struct device *dev;
> +	void __iomem *base;
> +	int irq;
> +	unsigned long alarm;
> +};
> +
> +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
> +{
> +	struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
> +	u32 val;
> +
> +	val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
> +
> +	if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
> +		val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
> +		writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
> +
> +		rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
> +
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
> +{
> +	u32 alrm_val = 0;
> +	u32 alrm_irq_val = 0;
> +	u32 alrm_wake_val = 0;
> +
> +	if (to) {
> +		alrm_val = SUN6I_ALRM_EN_CNT_EN;
> +		alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
> +		alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
> +	} else {
> +		writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
> +				chip->base + SUN6I_ALRM_IRQ_STA);
> +	}
> +
> +	writel(alrm_val, chip->base + SUN6I_ALRM_EN);
> +	writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
> +	writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
> +}
> +
> +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
> +{
> +	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> +	u32 date, time;
> +
> +	/*
> +	 * read again in case it changes
> +	 */
> +	do {
> +		date = readl(chip->base + SUN6I_RTC_YMD);
> +		time = readl(chip->base + SUN6I_RTC_HMS);
> +	} while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
> +		 (time != readl(chip->base + SUN6I_RTC_HMS)));
> +
> +	rtc_tm->tm_sec  = SUN6I_TIME_GET_SEC_VALUE(time);
> +	rtc_tm->tm_min  = SUN6I_TIME_GET_MIN_VALUE(time);
> +	rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
> +
> +	rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
> +	rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date);
> +	rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
> +
> +	rtc_tm->tm_mon  -= 1;
> +
> +	/*
> +	 * switch from (data_year->min)-relative offset to
> +	 * a (1900)-relative one
> +	 */

I guess the reference to the structure field is not relevant anymore

> +	rtc_tm->tm_year += SUN6I_YEAR_OFF;
> +
> +	return rtc_valid_tm(rtc_tm);
> +}
> +
> +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> +	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> +	u32 alrm_st;
> +	u32 alrm_en;
> +
> +	alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
> +	alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
> +	wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
> +	wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
> +	rtc_time_to_tm(chip->alarm, &wkalrm->time);
> +
> +	return 0;
> +}
> +
> +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> +	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> +	struct rtc_time *alrm_tm = &wkalrm->time;
> +	struct rtc_time tm_now;
> +	unsigned long time_now = 0;
> +	unsigned long time_set = 0;
> +	unsigned long time_gap = 0;
> +	int ret = 0;
> +
> +	ret = sun6i_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;
> +
> +	if (time_gap > U32_MAX) {
> +		dev_err(dev, "Date too far in the future\n");
> +		return -EINVAL;
> +	}
> +
> +	sun6i_rtc_setaie(0, chip);
> +	writel(0, chip->base + SUN6I_ALRM_COUNTER);
> +	usleep_range(100, 300);
> +
> +	writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
> +	chip->alarm = time_set;
> +
> +	sun6i_rtc_setaie(wkalrm->enabled, chip);
> +
> +	return 0;
> +}
> +
> +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
> +			  unsigned int mask, unsigned int ms_timeout)
> +{
> +	const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
> +	u32 reg;
> +
> +	do {
> +		reg = readl(chip->base + offset);
> +		reg &= mask;
> +
> +		if (!reg)
> +			return 0;
> +
> +	} while (time_before(jiffies, timeout));
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
> +{
> +	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> +	u32 date = 0;
> +	u32 time = 0;
> +	int year;
> +
> +	/*
> +	 * the input rtc_tm->tm_year is the offset relative to 1900. We use
> +	 * the SUN6I_YEAR_OFF macro to rebase it with respect to the min year
> +	 * allowed by the hardware
> +	 */
> +
> +	year = rtc_tm->tm_year + 1900;
> +	if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
> +		dev_err(dev, "rtc only supports year in range %d - %d\n",
> +				SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
> +		return -EINVAL;
> +	}
> +
> +	rtc_tm->tm_year -= SUN6I_YEAR_OFF;
> +	rtc_tm->tm_mon += 1;
> +
> +	date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
> +		SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
> +		SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
> +
> +	if (is_leap_year(year))
> +		date |= SUN6I_LEAP_SET_VALUE(1);
> +
> +	time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |
> +		SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |
> +		SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
> +
> +	/* Check whether registers are writable */
> +	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> +				SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
> +		dev_err(dev, "rtc is still busy.\n");
> +		return -EBUSY;
> +	}
> +
> +	writel(time, chip->base + SUN6I_RTC_HMS);
> +
> +	/*
> +	 * After writing the RTC HH-MM-SS register, the
> +	 * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
> +	 * be cleared until the real writing operation is finished
> +	 */
> +
> +	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> +				SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
> +		dev_err(dev, "Failed to set rtc time.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	writel(date, chip->base + SUN6I_RTC_YMD);
> +
> +	/*
> +	 * After writing the RTC YY-MM-DD register, the
> +	 * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
> +	 * be cleared until the real writing operation is finished
> +	 */
> +
> +	if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> +				SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
> +		dev_err(dev, "Failed to set rtc time.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> +
> +	if (!enabled)
> +		sun6i_rtc_setaie(enabled, chip);
> +
> +	return 0;
> +}
> +
> +static const struct rtc_class_ops sun6i_rtc_ops = {
> +	.read_time		= sun6i_rtc_gettime,
> +	.set_time		= sun6i_rtc_settime,
> +	.read_alarm		= sun6i_rtc_getalarm,
> +	.set_alarm		= sun6i_rtc_setalarm,
> +	.alarm_irq_enable	= sun6i_rtc_alarm_irq_enable
> +};
> +
> +static const struct of_device_id sun6i_rtc_dt_ids[] = {
> +	{ .compatible = "allwinner,sun6i-a31-rtc" },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);

I guess you can move this down just before the platform_driver
declaration if you don't need it in probe.

> +
> +static int sun6i_rtc_probe(struct platform_device *pdev)
> +{
> +	struct sun6i_rtc_dev *chip;
> +	struct resource *res;
> +	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;
> +	}

Newline

> +	ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
> +			0, dev_name(&pdev->dev), chip);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not request IRQ\n");
> +		return ret;
> +	}
> +
> +	/* clear the alarm counter value */
> +	writel(0, chip->base + SUN6I_ALRM_COUNTER);
> +
> +	/* disable counter alarm */
> +	writel(0, chip->base + SUN6I_ALRM_EN);
> +
> +	/* disable counter alarm interrupt */
> +	writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
> +
> +	/* disable week alarm */
> +	writel(0, chip->base + SUN6I_ALRM1_EN);
> +
> +	/* disable week alarm interrupt */
> +	writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
> +
> +	/* clear counter alarm pending interrupts */
> +	writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +
> +			SUN6I_ALRM_IRQ_STA);
> +
> +	/* clear week alarm pending interrupts */
> +	writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, chip->base +
> +			SUN6I_ALRM1_IRQ_STA);
> +
> +	/* disable alarm wakeup */
> +	writel(0, chip->base + SUN6I_ALARM_CONFIG);
> +
> +	chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
> +			&sun6i_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 sun6i_rtc_remove(struct platform_device *pdev)
> +{
> +	struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
> +
> +	rtc_device_unregister(chip->rtc);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver sun6i_rtc_driver = {
> +	.probe		= sun6i_rtc_probe,
> +	.remove		= sun6i_rtc_remove,
> +	.driver		= {
> +		.name		= "sun6i-rtc",
> +		.owner		= THIS_MODULE,
> +		.of_match_table = sun6i_rtc_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(sun6i_rtc_driver);
> +
> +MODULE_DESCRIPTION("sun6i RTC driver");
> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.0.1
> 

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140718/8fc8491b/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc
  2014-07-14  7:32 ` [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Chen-Yu Tsai
@ 2014-07-18  8:11   ` Maxime Ripard
  0 siblings, 0 replies; 13+ messages in thread
From: Maxime Ripard @ 2014-07-18  8:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jul 14, 2014 at 03:32:14PM +0800, Chen-Yu Tsai wrote:
> Now that we have Kconfig options for individual sunxi platforms, let
> the rtc-sunxi driver depend on the platforms that actually have this
> hardware, sun4i and sun7i.
> 
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>

Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140718/0a7c12ae/attachment.sig>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig
  2014-07-14  7:32 ` [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig Chen-Yu Tsai
@ 2014-07-18  8:12   ` Maxime Ripard
  2014-07-18  9:25     ` Chen-Yu Tsai
  0 siblings, 1 reply; 13+ messages in thread
From: Maxime Ripard @ 2014-07-18  8:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jul 14, 2014 at 03:32:17PM +0800, Chen-Yu Tsai wrote:
> Now that we have a driver for A31's RTC, enable it
> in the default sunxi config.

It would be great if you could do this for multi_v7 as well.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140718/7b00b43d/attachment.sig>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig
  2014-07-18  8:12   ` Maxime Ripard
@ 2014-07-18  9:25     ` Chen-Yu Tsai
  0 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-18  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 18, 2014 at 4:12 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Mon, Jul 14, 2014 at 03:32:17PM +0800, Chen-Yu Tsai wrote:
>> Now that we have a driver for A31's RTC, enable it
>> in the default sunxi config.
>
> It would be great if you could do this for multi_v7 as well.

No problem. I'll add it in the next version.


ChenYu

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver
  2014-07-18  8:07   ` Maxime Ripard
@ 2014-07-21 14:46     ` Chen-Yu Tsai
  2014-07-23  9:55       ` Maxime Ripard
  0 siblings, 1 reply; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-21 14:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Fri, Jul 18, 2014 at 4:07 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Mon, Jul 14, 2014 at 03:32:13PM +0800, Chen-Yu Tsai wrote:
>> This patch introduces the driver for the RTC in the Allwinner A31 and
>> A23 SoCs.
>>
>> Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
>> RTC in A31/A23 are a separate hardware block, which also contain a few
>> controls for the RTC block hardware (a regulator and RTC block GPIO pin
>> latches), while also having separate interrupts for the alarms.
>
> Do you plan on supporting those at some point?

I haven't seen any devices use the regulator (which has an output pin).
I suppose we shouldn't add drivers for things we can't verify.
As for the GPIO pin latches, I'll have to experiment some more to figure
out what they do exactly.

> It's also worth noting that the first registers are supposed to
> control the source of the low frequency oscillator in the SoC, which
> will probably be the most troublesome, since we need these clocks very
> early on.

That's true. I suppose the bootloader configures this. IIRC I've seen
code for this in boot0 or boot1 from Allwinner. I can't find the equivalent
for our sun4i u-boot though.

>>
>> The hardware is different enough to make a different driver for it.
>>
>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
>> ---
>>  .../devicetree/bindings/rtc/sun6i-rtc.txt          |  17 +
>>  drivers/rtc/Kconfig                                |   7 +
>>  drivers/rtc/Makefile                               |   1 +
>>  drivers/rtc/rtc-sun6i.c                            | 466 +++++++++++++++++++++
>>  4 files changed, 491 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
>>  create mode 100644 drivers/rtc/rtc-sun6i.c
>>
>> diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
>> new file mode 100644
>> index 0000000..b18927c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
>> @@ -0,0 +1,17 @@
>> +* sun6i Real Time Clock
>> +
>> +RTC controller for the Allwinner A31
>> +
>> +Required properties:
>> +- compatible : Should be "allwinner,sun6i-a31-rtc"
>> +- reg: physical base address of the controller and length of memory mapped
>> +  region.
>> +- interrupts: IRQ line for the RTC alarm 0.
>> +
>> +Example:
>> +
>> +rtc: rtc at 01f00000 {
>> +     compatible = "allwinner,sun6i-a31-rtc";
>> +     reg = <0x01f00000 0x54>;
>> +     interrupts = <0 40 4>;
>> +};
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index 0754f5c..5b3910a 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -1167,6 +1167,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_SUN6I
>> +     tristate "Allwinner sun6i/sun8i RTC"
>
> I'm half convinced about an exhaustive list here. That IP will also
> probably be used by sun9i, and sun10i if it ever exists, etc. And you
> exhaustive list won't be anymore.
>
> I'd rather just mention the A31, like we do for the DT.

Fixed.

>> +     depends on MACH_SUN6I || MACH_SUN8I
>> +     help
>> +       If you say Y here you will get support for the RTC found on
>> +       Allwinner A31/A23.
>> +
>>  config RTC_DRV_SUNXI
>>       tristate "Allwinner sun4i/sun7i RTC"
>>       depends on ARCH_SUNXI
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 70347d0..a47df29 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -123,6 +123,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_SUN6I)  += rtc-sun6i.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
>> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
>> new file mode 100644
>> index 0000000..fabd019
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-sun6i.c
>> @@ -0,0 +1,466 @@
>> +/*
>> + * An RTC driver for Allwinner A31/A23
>> + *
>> + * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
>> + *
>> + * based on rtc-sunxi.c
>> + *
>> + * 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.
>> + */
>> +
>> +#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>
>> +
>> +/* Control register */
>> +#define SUN6I_LOSC_CTRL                              0x0000
>> +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC         BIT(9)
>> +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC          BIT(8)
>> +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC          BIT(7)
>> +#define SUN6I_LOSC_CTRL_ACC_MASK             (BIT(9) | BIT(8) | BIT(7))
>
> GENMASK maybe?

Fixed

>> +
>> +/* RTC */
>> +#define SUN6I_RTC_YMD                                0x0010
>> +#define SUN6I_RTC_HMS                                0x0014
>> +
>> +/* Alarm 0 (counter) */
>> +#define SUN6I_ALRM_COUNTER                   0x0020
>> +#define SUN6I_ALRM_CUR_VAL                   0x0024
>> +#define SUN6I_ALRM_EN                                0x0028
>> +#define SUN6I_ALRM_EN_CNT_EN                 BIT(0)
>> +#define SUN6I_ALRM_IRQ_EN                    0x002c
>> +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN         BIT(0)
>> +#define SUN6I_ALRM_IRQ_STA                   0x0030
>> +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND              BIT(0)
>> +
>> +/* Alarm 1 (wall clock) */
>> +#define SUN6I_ALRM1_EN                               0x0044
>> +#define SUN6I_ALRM1_IRQ_EN                   0x0048
>> +#define SUN6I_ALRM1_IRQ_STA                  0x004c
>> +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND    BIT(0)
>> +
>> +/* Alarm config */
>> +#define SUN6I_ALARM_CONFIG                   0x0050
>> +#define SUN6I_ALARM_CONFIG_WAKEUP            BIT(0)
>> +
>> +/* days / hours are 5 bit wide */
>> +#define SUN6I_MASK_DH                                0x0000001f
>> +/* seconds / minutes / years are 6 bit wide */
>> +#define SUN6I_MASK_SMY                               0x0000003f
>> +/* months are 4 bit wide */
>> +#define SUN6I_MASK_M                         0x0000000f
>> +/* leap year is single bit */
>> +#define SUN6I_MASK_LY                                0x00000001
>
> Ditto

See below.

>> +
>> +#define SUN6I_GET(x, mask, shift)            (((x) & ((mask) << (shift))) \
>> +                                                     >> (shift))
>> +
>> +#define SUN6I_SET(x, mask, shift)            (((x) & (mask)) << (shift))
>
> Wouldn't it be easier to have the mask already shifted?

I'll just get rid of these 2 and the masks above, and inline them
(in hex format) in the GET/SET macros below. How does that sound?

>> +
>> +/*
>> + * Get date values
>> + */
>> +#define SUN6I_DATE_GET_DAY_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_DH, 0)
>> +#define SUN6I_DATE_GET_MON_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_M, 8)
>> +#define SUN6I_DATE_GET_YEAR_VALUE(x)         SUN6I_GET(x, SUN6I_MASK_SMY, 16)
>> +
>> +/*
>> + * Get time values
>> + */
>> +#define SUN6I_TIME_GET_SEC_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_SMY, 0)
>> +#define SUN6I_TIME_GET_MIN_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_SMY, 8)
>> +#define SUN6I_TIME_GET_HOUR_VALUE(x)         SUN6I_GET(x, SUN6I_MASK_DH, 16)
>> +
>> +/*
>> + * Set date values
>> + */
>> +#define SUN6I_DATE_SET_DAY_VALUE(x)          SUN6I_DATE_GET_DAY_VALUE(x)
>> +#define SUN6I_DATE_SET_MON_VALUE(x)          SUN6I_SET(x, SUN6I_MASK_M, 8)
>> +#define SUN6I_DATE_SET_YEAR_VALUE(x)         SUN6I_SET(x, SUN6I_MASK_SMY, 16)
>> +#define SUN6I_LEAP_SET_VALUE(x)                      SUN6I_SET(x, SUN6I_MASK_LY, 22)
>> +
>> +/*
>> + * Set time values
>> + */
>> +#define SUN6I_TIME_SET_SEC_VALUE(x)          SUN6I_TIME_GET_SEC_VALUE(x)
>> +#define SUN6I_TIME_SET_MIN_VALUE(x)          SUN6I_SET(x, SUN6I_MASK_SMY, 8)
>> +#define SUN6I_TIME_SET_HOUR_VALUE(x)         SUN6I_SET(x, SUN6I_MASK_DH, 16)
>> +
>> +/*
>> + * The year parameter passed to the driver is usually an offset relative to
>> + * the year 1900. This macro is used to convert this offset to another one
>> + * relative to the minimum year allowed by the hardware.
>> + *
>> + * The year range is 1970 - 2033. This range is selected to match Allwinner's
>> + * driver, even though it is somewhat limited.
>> + */
>> +#define SUN6I_YEAR_MIN                               1970
>> +#define SUN6I_YEAR_MAX                               2033
>> +#define SUN6I_YEAR_OFF                               (SUN6I_YEAR_MIN - 1900)
>> +
>> +struct sun6i_rtc_dev {
>> +     struct rtc_device *rtc;
>> +     struct device *dev;
>> +     void __iomem *base;
>> +     int irq;
>> +     unsigned long alarm;
>> +};
>> +
>> +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
>> +{
>> +     struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
>> +     u32 val;
>> +
>> +     val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
>> +
>> +     if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
>> +             val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
>> +             writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
>> +
>> +             rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
>> +
>> +             return IRQ_HANDLED;
>> +     }
>> +
>> +     return IRQ_NONE;
>> +}
>> +
>> +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
>> +{
>> +     u32 alrm_val = 0;
>> +     u32 alrm_irq_val = 0;
>> +     u32 alrm_wake_val = 0;
>> +
>> +     if (to) {
>> +             alrm_val = SUN6I_ALRM_EN_CNT_EN;
>> +             alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
>> +             alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
>> +     } else {
>> +             writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
>> +                             chip->base + SUN6I_ALRM_IRQ_STA);
>> +     }
>> +
>> +     writel(alrm_val, chip->base + SUN6I_ALRM_EN);
>> +     writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
>> +     writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
>> +}
>> +
>> +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
>> +{
>> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
>> +     u32 date, time;
>> +
>> +     /*
>> +      * read again in case it changes
>> +      */
>> +     do {
>> +             date = readl(chip->base + SUN6I_RTC_YMD);
>> +             time = readl(chip->base + SUN6I_RTC_HMS);
>> +     } while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
>> +              (time != readl(chip->base + SUN6I_RTC_HMS)));
>> +
>> +     rtc_tm->tm_sec  = SUN6I_TIME_GET_SEC_VALUE(time);
>> +     rtc_tm->tm_min  = SUN6I_TIME_GET_MIN_VALUE(time);
>> +     rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
>> +
>> +     rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
>> +     rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date);
>> +     rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
>> +
>> +     rtc_tm->tm_mon  -= 1;
>> +
>> +     /*
>> +      * switch from (data_year->min)-relative offset to
>> +      * a (1900)-relative one
>> +      */
>
> I guess the reference to the structure field is not relevant anymore

Removed.

>> +     rtc_tm->tm_year += SUN6I_YEAR_OFF;
>> +
>> +     return rtc_valid_tm(rtc_tm);
>> +}
>> +
>> +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
>> +{
>> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
>> +     u32 alrm_st;
>> +     u32 alrm_en;
>> +
>> +     alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
>> +     alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
>> +     wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
>> +     wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
>> +     rtc_time_to_tm(chip->alarm, &wkalrm->time);
>> +
>> +     return 0;
>> +}
>> +
>> +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
>> +{
>> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
>> +     struct rtc_time *alrm_tm = &wkalrm->time;
>> +     struct rtc_time tm_now;
>> +     unsigned long time_now = 0;
>> +     unsigned long time_set = 0;
>> +     unsigned long time_gap = 0;
>> +     int ret = 0;
>> +
>> +     ret = sun6i_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;
>> +
>> +     if (time_gap > U32_MAX) {
>> +             dev_err(dev, "Date too far in the future\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     sun6i_rtc_setaie(0, chip);
>> +     writel(0, chip->base + SUN6I_ALRM_COUNTER);
>> +     usleep_range(100, 300);
>> +
>> +     writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
>> +     chip->alarm = time_set;
>> +
>> +     sun6i_rtc_setaie(wkalrm->enabled, chip);
>> +
>> +     return 0;
>> +}
>> +
>> +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
>> +                       unsigned int mask, unsigned int ms_timeout)
>> +{
>> +     const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
>> +     u32 reg;
>> +
>> +     do {
>> +             reg = readl(chip->base + offset);
>> +             reg &= mask;
>> +
>> +             if (!reg)
>> +                     return 0;
>> +
>> +     } while (time_before(jiffies, timeout));
>> +
>> +     return -ETIMEDOUT;
>> +}
>> +
>> +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
>> +{
>> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
>> +     u32 date = 0;
>> +     u32 time = 0;
>> +     int year;
>> +
>> +     /*
>> +      * the input rtc_tm->tm_year is the offset relative to 1900. We use
>> +      * the SUN6I_YEAR_OFF macro to rebase it with respect to the min year
>> +      * allowed by the hardware
>> +      */
>> +
>> +     year = rtc_tm->tm_year + 1900;
>> +     if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
>> +             dev_err(dev, "rtc only supports year in range %d - %d\n",
>> +                             SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
>> +             return -EINVAL;
>> +     }
>> +
>> +     rtc_tm->tm_year -= SUN6I_YEAR_OFF;
>> +     rtc_tm->tm_mon += 1;
>> +
>> +     date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
>> +             SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
>> +             SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
>> +
>> +     if (is_leap_year(year))
>> +             date |= SUN6I_LEAP_SET_VALUE(1);
>> +
>> +     time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |
>> +             SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |
>> +             SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
>> +
>> +     /* Check whether registers are writable */
>> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
>> +                             SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
>> +             dev_err(dev, "rtc is still busy.\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     writel(time, chip->base + SUN6I_RTC_HMS);
>> +
>> +     /*
>> +      * After writing the RTC HH-MM-SS register, the
>> +      * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
>> +      * be cleared until the real writing operation is finished
>> +      */
>> +
>> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
>> +                             SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
>> +             dev_err(dev, "Failed to set rtc time.\n");
>> +             return -ETIMEDOUT;
>> +     }
>> +
>> +     writel(date, chip->base + SUN6I_RTC_YMD);
>> +
>> +     /*
>> +      * After writing the RTC YY-MM-DD register, the
>> +      * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
>> +      * be cleared until the real writing operation is finished
>> +      */
>> +
>> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
>> +                             SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
>> +             dev_err(dev, "Failed to set rtc time.\n");
>> +             return -ETIMEDOUT;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
>> +{
>> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
>> +
>> +     if (!enabled)
>> +             sun6i_rtc_setaie(enabled, chip);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct rtc_class_ops sun6i_rtc_ops = {
>> +     .read_time              = sun6i_rtc_gettime,
>> +     .set_time               = sun6i_rtc_settime,
>> +     .read_alarm             = sun6i_rtc_getalarm,
>> +     .set_alarm              = sun6i_rtc_setalarm,
>> +     .alarm_irq_enable       = sun6i_rtc_alarm_irq_enable
>> +};
>> +
>> +static const struct of_device_id sun6i_rtc_dt_ids[] = {
>> +     { .compatible = "allwinner,sun6i-a31-rtc" },
>> +     { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
>
> I guess you can move this down just before the platform_driver
> declaration if you don't need it in probe.

Moved.

>> +
>> +static int sun6i_rtc_probe(struct platform_device *pdev)
>> +{
>> +     struct sun6i_rtc_dev *chip;
>> +     struct resource *res;
>> +     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;
>> +     }
>
> Newline

Added.

>> +     ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
>> +                     0, dev_name(&pdev->dev), chip);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Could not request IRQ\n");
>> +             return ret;
>> +     }
>> +
>> +     /* clear the alarm counter value */
>> +     writel(0, chip->base + SUN6I_ALRM_COUNTER);
>> +
>> +     /* disable counter alarm */
>> +     writel(0, chip->base + SUN6I_ALRM_EN);
>> +
>> +     /* disable counter alarm interrupt */
>> +     writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
>> +
>> +     /* disable week alarm */
>> +     writel(0, chip->base + SUN6I_ALRM1_EN);
>> +
>> +     /* disable week alarm interrupt */
>> +     writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
>> +
>> +     /* clear counter alarm pending interrupts */
>> +     writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +
>> +                     SUN6I_ALRM_IRQ_STA);
>> +
>> +     /* clear week alarm pending interrupts */
>> +     writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, chip->base +
>> +                     SUN6I_ALRM1_IRQ_STA);
>> +
>> +     /* disable alarm wakeup */
>> +     writel(0, chip->base + SUN6I_ALARM_CONFIG);
>> +
>> +     chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
>> +                     &sun6i_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 sun6i_rtc_remove(struct platform_device *pdev)
>> +{
>> +     struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
>> +
>> +     rtc_device_unregister(chip->rtc);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver sun6i_rtc_driver = {
>> +     .probe          = sun6i_rtc_probe,
>> +     .remove         = sun6i_rtc_remove,
>> +     .driver         = {
>> +             .name           = "sun6i-rtc",
>> +             .owner          = THIS_MODULE,
>> +             .of_match_table = sun6i_rtc_dt_ids,
>> +     },
>> +};
>> +
>> +module_platform_driver(sun6i_rtc_driver);
>> +
>> +MODULE_DESCRIPTION("sun6i RTC driver");
>> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.0.1
>>
>
> Thanks!
> Maxime

Thanks for the review!
ChenYu

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver
  2014-07-21 14:46     ` Chen-Yu Tsai
@ 2014-07-23  9:55       ` Maxime Ripard
  2014-07-23 14:37         ` Chen-Yu Tsai
  0 siblings, 1 reply; 13+ messages in thread
From: Maxime Ripard @ 2014-07-23  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Mon, Jul 21, 2014 at 10:46:06PM +0800, Chen-Yu Tsai wrote:
> Hi,
> 
> On Fri, Jul 18, 2014 at 4:07 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > Hi,
> >
> > On Mon, Jul 14, 2014 at 03:32:13PM +0800, Chen-Yu Tsai wrote:
> >> This patch introduces the driver for the RTC in the Allwinner A31 and
> >> A23 SoCs.
> >>
> >> Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
> >> RTC in A31/A23 are a separate hardware block, which also contain a few
> >> controls for the RTC block hardware (a regulator and RTC block GPIO pin
> >> latches), while also having separate interrupts for the alarms.
> >
> > Do you plan on supporting those at some point?
> 
> I haven't seen any devices use the regulator (which has an output pin).
> I suppose we shouldn't add drivers for things we can't verify.
> As for the GPIO pin latches, I'll have to experiment some more to figure
> out what they do exactly.

Ok.

> > It's also worth noting that the first registers are supposed to
> > control the source of the low frequency oscillator in the SoC, which
> > will probably be the most troublesome, since we need these clocks very
> > early on.
> 
> That's true. I suppose the bootloader configures this. IIRC I've seen
> code for this in boot0 or boot1 from Allwinner. I can't find the equivalent
> for our sun4i u-boot though.

Do you know what was the bootloader configuring it to?

I don't really know how we can deal with this in a nice way, but I
guess it's not so urgent.

> >>
> >> The hardware is different enough to make a different driver for it.
> >>
> >> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> >> ---
> >>  .../devicetree/bindings/rtc/sun6i-rtc.txt          |  17 +
> >>  drivers/rtc/Kconfig                                |   7 +
> >>  drivers/rtc/Makefile                               |   1 +
> >>  drivers/rtc/rtc-sun6i.c                            | 466 +++++++++++++++++++++
> >>  4 files changed, 491 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
> >>  create mode 100644 drivers/rtc/rtc-sun6i.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
> >> new file mode 100644
> >> index 0000000..b18927c
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt
> >> @@ -0,0 +1,17 @@
> >> +* sun6i Real Time Clock
> >> +
> >> +RTC controller for the Allwinner A31
> >> +
> >> +Required properties:
> >> +- compatible : Should be "allwinner,sun6i-a31-rtc"
> >> +- reg: physical base address of the controller and length of memory mapped
> >> +  region.
> >> +- interrupts: IRQ line for the RTC alarm 0.
> >> +
> >> +Example:
> >> +
> >> +rtc: rtc at 01f00000 {
> >> +     compatible = "allwinner,sun6i-a31-rtc";
> >> +     reg = <0x01f00000 0x54>;
> >> +     interrupts = <0 40 4>;
> >> +};
> >> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> >> index 0754f5c..5b3910a 100644
> >> --- a/drivers/rtc/Kconfig
> >> +++ b/drivers/rtc/Kconfig
> >> @@ -1167,6 +1167,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_SUN6I
> >> +     tristate "Allwinner sun6i/sun8i RTC"
> >
> > I'm half convinced about an exhaustive list here. That IP will also
> > probably be used by sun9i, and sun10i if it ever exists, etc. And you
> > exhaustive list won't be anymore.
> >
> > I'd rather just mention the A31, like we do for the DT.
> 
> Fixed.
> 
> >> +     depends on MACH_SUN6I || MACH_SUN8I
> >> +     help
> >> +       If you say Y here you will get support for the RTC found on
> >> +       Allwinner A31/A23.
> >> +
> >>  config RTC_DRV_SUNXI
> >>       tristate "Allwinner sun4i/sun7i RTC"
> >>       depends on ARCH_SUNXI
> >> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> >> index 70347d0..a47df29 100644
> >> --- a/drivers/rtc/Makefile
> >> +++ b/drivers/rtc/Makefile
> >> @@ -123,6 +123,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_SUN6I)  += rtc-sun6i.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
> >> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> >> new file mode 100644
> >> index 0000000..fabd019
> >> --- /dev/null
> >> +++ b/drivers/rtc/rtc-sun6i.c
> >> @@ -0,0 +1,466 @@
> >> +/*
> >> + * An RTC driver for Allwinner A31/A23
> >> + *
> >> + * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
> >> + *
> >> + * based on rtc-sunxi.c
> >> + *
> >> + * 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.
> >> + */
> >> +
> >> +#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>
> >> +
> >> +/* Control register */
> >> +#define SUN6I_LOSC_CTRL                              0x0000
> >> +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC         BIT(9)
> >> +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC          BIT(8)
> >> +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC          BIT(7)
> >> +#define SUN6I_LOSC_CTRL_ACC_MASK             (BIT(9) | BIT(8) | BIT(7))
> >
> > GENMASK maybe?
> 
> Fixed
> 
> >> +
> >> +/* RTC */
> >> +#define SUN6I_RTC_YMD                                0x0010
> >> +#define SUN6I_RTC_HMS                                0x0014
> >> +
> >> +/* Alarm 0 (counter) */
> >> +#define SUN6I_ALRM_COUNTER                   0x0020
> >> +#define SUN6I_ALRM_CUR_VAL                   0x0024
> >> +#define SUN6I_ALRM_EN                                0x0028
> >> +#define SUN6I_ALRM_EN_CNT_EN                 BIT(0)
> >> +#define SUN6I_ALRM_IRQ_EN                    0x002c
> >> +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN         BIT(0)
> >> +#define SUN6I_ALRM_IRQ_STA                   0x0030
> >> +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND              BIT(0)
> >> +
> >> +/* Alarm 1 (wall clock) */
> >> +#define SUN6I_ALRM1_EN                               0x0044
> >> +#define SUN6I_ALRM1_IRQ_EN                   0x0048
> >> +#define SUN6I_ALRM1_IRQ_STA                  0x004c
> >> +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND    BIT(0)
> >> +
> >> +/* Alarm config */
> >> +#define SUN6I_ALARM_CONFIG                   0x0050
> >> +#define SUN6I_ALARM_CONFIG_WAKEUP            BIT(0)
> >> +
> >> +/* days / hours are 5 bit wide */
> >> +#define SUN6I_MASK_DH                                0x0000001f
> >> +/* seconds / minutes / years are 6 bit wide */
> >> +#define SUN6I_MASK_SMY                               0x0000003f
> >> +/* months are 4 bit wide */
> >> +#define SUN6I_MASK_M                         0x0000000f
> >> +/* leap year is single bit */
> >> +#define SUN6I_MASK_LY                                0x00000001
> >
> > Ditto
> 
> See below.
> 
> >> +
> >> +#define SUN6I_GET(x, mask, shift)            (((x) & ((mask) << (shift))) \
> >> +                                                     >> (shift))
> >> +
> >> +#define SUN6I_SET(x, mask, shift)            (((x) & (mask)) << (shift))
> >
> > Wouldn't it be easier to have the mask already shifted?
> 
> I'll just get rid of these 2 and the masks above, and inline them
> (in hex format) in the GET/SET macros below. How does that sound?

Sounds good to me.

> 
> >> +
> >> +/*
> >> + * Get date values
> >> + */
> >> +#define SUN6I_DATE_GET_DAY_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_DH, 0)
> >> +#define SUN6I_DATE_GET_MON_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_M, 8)
> >> +#define SUN6I_DATE_GET_YEAR_VALUE(x)         SUN6I_GET(x, SUN6I_MASK_SMY, 16)
> >> +
> >> +/*
> >> + * Get time values
> >> + */
> >> +#define SUN6I_TIME_GET_SEC_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_SMY, 0)
> >> +#define SUN6I_TIME_GET_MIN_VALUE(x)          SUN6I_GET(x, SUN6I_MASK_SMY, 8)
> >> +#define SUN6I_TIME_GET_HOUR_VALUE(x)         SUN6I_GET(x, SUN6I_MASK_DH, 16)
> >> +
> >> +/*
> >> + * Set date values
> >> + */
> >> +#define SUN6I_DATE_SET_DAY_VALUE(x)          SUN6I_DATE_GET_DAY_VALUE(x)
> >> +#define SUN6I_DATE_SET_MON_VALUE(x)          SUN6I_SET(x, SUN6I_MASK_M, 8)
> >> +#define SUN6I_DATE_SET_YEAR_VALUE(x)         SUN6I_SET(x, SUN6I_MASK_SMY, 16)
> >> +#define SUN6I_LEAP_SET_VALUE(x)                      SUN6I_SET(x, SUN6I_MASK_LY, 22)
> >> +
> >> +/*
> >> + * Set time values
> >> + */
> >> +#define SUN6I_TIME_SET_SEC_VALUE(x)          SUN6I_TIME_GET_SEC_VALUE(x)
> >> +#define SUN6I_TIME_SET_MIN_VALUE(x)          SUN6I_SET(x, SUN6I_MASK_SMY, 8)
> >> +#define SUN6I_TIME_SET_HOUR_VALUE(x)         SUN6I_SET(x, SUN6I_MASK_DH, 16)
> >> +
> >> +/*
> >> + * The year parameter passed to the driver is usually an offset relative to
> >> + * the year 1900. This macro is used to convert this offset to another one
> >> + * relative to the minimum year allowed by the hardware.
> >> + *
> >> + * The year range is 1970 - 2033. This range is selected to match Allwinner's
> >> + * driver, even though it is somewhat limited.
> >> + */
> >> +#define SUN6I_YEAR_MIN                               1970
> >> +#define SUN6I_YEAR_MAX                               2033
> >> +#define SUN6I_YEAR_OFF                               (SUN6I_YEAR_MIN - 1900)
> >> +
> >> +struct sun6i_rtc_dev {
> >> +     struct rtc_device *rtc;
> >> +     struct device *dev;
> >> +     void __iomem *base;
> >> +     int irq;
> >> +     unsigned long alarm;
> >> +};
> >> +
> >> +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
> >> +     u32 val;
> >> +
> >> +     val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
> >> +
> >> +     if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
> >> +             val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
> >> +             writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
> >> +
> >> +             rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
> >> +
> >> +             return IRQ_HANDLED;
> >> +     }
> >> +
> >> +     return IRQ_NONE;
> >> +}
> >> +
> >> +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
> >> +{
> >> +     u32 alrm_val = 0;
> >> +     u32 alrm_irq_val = 0;
> >> +     u32 alrm_wake_val = 0;
> >> +
> >> +     if (to) {
> >> +             alrm_val = SUN6I_ALRM_EN_CNT_EN;
> >> +             alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
> >> +             alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
> >> +     } else {
> >> +             writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
> >> +                             chip->base + SUN6I_ALRM_IRQ_STA);
> >> +     }
> >> +
> >> +     writel(alrm_val, chip->base + SUN6I_ALRM_EN);
> >> +     writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
> >> +     writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
> >> +}
> >> +
> >> +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> >> +     u32 date, time;
> >> +
> >> +     /*
> >> +      * read again in case it changes
> >> +      */
> >> +     do {
> >> +             date = readl(chip->base + SUN6I_RTC_YMD);
> >> +             time = readl(chip->base + SUN6I_RTC_HMS);
> >> +     } while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
> >> +              (time != readl(chip->base + SUN6I_RTC_HMS)));
> >> +
> >> +     rtc_tm->tm_sec  = SUN6I_TIME_GET_SEC_VALUE(time);
> >> +     rtc_tm->tm_min  = SUN6I_TIME_GET_MIN_VALUE(time);
> >> +     rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
> >> +
> >> +     rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
> >> +     rtc_tm->tm_mon  = SUN6I_DATE_GET_MON_VALUE(date);
> >> +     rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
> >> +
> >> +     rtc_tm->tm_mon  -= 1;
> >> +
> >> +     /*
> >> +      * switch from (data_year->min)-relative offset to
> >> +      * a (1900)-relative one
> >> +      */
> >
> > I guess the reference to the structure field is not relevant anymore
> 
> Removed.
> 
> >> +     rtc_tm->tm_year += SUN6I_YEAR_OFF;
> >> +
> >> +     return rtc_valid_tm(rtc_tm);
> >> +}
> >> +
> >> +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> >> +     u32 alrm_st;
> >> +     u32 alrm_en;
> >> +
> >> +     alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
> >> +     alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
> >> +     wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
> >> +     wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
> >> +     rtc_time_to_tm(chip->alarm, &wkalrm->time);
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> >> +     struct rtc_time *alrm_tm = &wkalrm->time;
> >> +     struct rtc_time tm_now;
> >> +     unsigned long time_now = 0;
> >> +     unsigned long time_set = 0;
> >> +     unsigned long time_gap = 0;
> >> +     int ret = 0;
> >> +
> >> +     ret = sun6i_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;
> >> +
> >> +     if (time_gap > U32_MAX) {
> >> +             dev_err(dev, "Date too far in the future\n");
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     sun6i_rtc_setaie(0, chip);
> >> +     writel(0, chip->base + SUN6I_ALRM_COUNTER);
> >> +     usleep_range(100, 300);
> >> +
> >> +     writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
> >> +     chip->alarm = time_set;
> >> +
> >> +     sun6i_rtc_setaie(wkalrm->enabled, chip);
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
> >> +                       unsigned int mask, unsigned int ms_timeout)
> >> +{
> >> +     const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
> >> +     u32 reg;
> >> +
> >> +     do {
> >> +             reg = readl(chip->base + offset);
> >> +             reg &= mask;
> >> +
> >> +             if (!reg)
> >> +                     return 0;
> >> +
> >> +     } while (time_before(jiffies, timeout));
> >> +
> >> +     return -ETIMEDOUT;
> >> +}
> >> +
> >> +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> >> +     u32 date = 0;
> >> +     u32 time = 0;
> >> +     int year;
> >> +
> >> +     /*
> >> +      * the input rtc_tm->tm_year is the offset relative to 1900. We use
> >> +      * the SUN6I_YEAR_OFF macro to rebase it with respect to the min year
> >> +      * allowed by the hardware
> >> +      */
> >> +
> >> +     year = rtc_tm->tm_year + 1900;
> >> +     if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
> >> +             dev_err(dev, "rtc only supports year in range %d - %d\n",
> >> +                             SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     rtc_tm->tm_year -= SUN6I_YEAR_OFF;
> >> +     rtc_tm->tm_mon += 1;
> >> +
> >> +     date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
> >> +             SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon)  |
> >> +             SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
> >> +
> >> +     if (is_leap_year(year))
> >> +             date |= SUN6I_LEAP_SET_VALUE(1);
> >> +
> >> +     time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec)  |
> >> +             SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min)  |
> >> +             SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
> >> +
> >> +     /* Check whether registers are writable */
> >> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> >> +                             SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
> >> +             dev_err(dev, "rtc is still busy.\n");
> >> +             return -EBUSY;
> >> +     }
> >> +
> >> +     writel(time, chip->base + SUN6I_RTC_HMS);
> >> +
> >> +     /*
> >> +      * After writing the RTC HH-MM-SS register, the
> >> +      * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
> >> +      * be cleared until the real writing operation is finished
> >> +      */
> >> +
> >> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> >> +                             SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
> >> +             dev_err(dev, "Failed to set rtc time.\n");
> >> +             return -ETIMEDOUT;
> >> +     }
> >> +
> >> +     writel(date, chip->base + SUN6I_RTC_YMD);
> >> +
> >> +     /*
> >> +      * After writing the RTC YY-MM-DD register, the
> >> +      * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
> >> +      * be cleared until the real writing operation is finished
> >> +      */
> >> +
> >> +     if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
> >> +                             SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
> >> +             dev_err(dev, "Failed to set rtc time.\n");
> >> +             return -ETIMEDOUT;
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
> >> +
> >> +     if (!enabled)
> >> +             sun6i_rtc_setaie(enabled, chip);
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static const struct rtc_class_ops sun6i_rtc_ops = {
> >> +     .read_time              = sun6i_rtc_gettime,
> >> +     .set_time               = sun6i_rtc_settime,
> >> +     .read_alarm             = sun6i_rtc_getalarm,
> >> +     .set_alarm              = sun6i_rtc_setalarm,
> >> +     .alarm_irq_enable       = sun6i_rtc_alarm_irq_enable
> >> +};
> >> +
> >> +static const struct of_device_id sun6i_rtc_dt_ids[] = {
> >> +     { .compatible = "allwinner,sun6i-a31-rtc" },
> >> +     { /* sentinel */ },
> >> +};
> >> +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
> >
> > I guess you can move this down just before the platform_driver
> > declaration if you don't need it in probe.
> 
> Moved.
> 
> >> +
> >> +static int sun6i_rtc_probe(struct platform_device *pdev)
> >> +{
> >> +     struct sun6i_rtc_dev *chip;
> >> +     struct resource *res;
> >> +     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;
> >> +     }
> >
> > Newline
> 
> Added.
> 
> >> +     ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
> >> +                     0, dev_name(&pdev->dev), chip);
> >> +     if (ret) {
> >> +             dev_err(&pdev->dev, "Could not request IRQ\n");
> >> +             return ret;
> >> +     }
> >> +
> >> +     /* clear the alarm counter value */
> >> +     writel(0, chip->base + SUN6I_ALRM_COUNTER);
> >> +
> >> +     /* disable counter alarm */
> >> +     writel(0, chip->base + SUN6I_ALRM_EN);
> >> +
> >> +     /* disable counter alarm interrupt */
> >> +     writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
> >> +
> >> +     /* disable week alarm */
> >> +     writel(0, chip->base + SUN6I_ALRM1_EN);
> >> +
> >> +     /* disable week alarm interrupt */
> >> +     writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
> >> +
> >> +     /* clear counter alarm pending interrupts */
> >> +     writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +
> >> +                     SUN6I_ALRM_IRQ_STA);
> >> +
> >> +     /* clear week alarm pending interrupts */
> >> +     writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, chip->base +
> >> +                     SUN6I_ALRM1_IRQ_STA);
> >> +
> >> +     /* disable alarm wakeup */
> >> +     writel(0, chip->base + SUN6I_ALARM_CONFIG);
> >> +
> >> +     chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
> >> +                     &sun6i_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 sun6i_rtc_remove(struct platform_device *pdev)
> >> +{
> >> +     struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
> >> +
> >> +     rtc_device_unregister(chip->rtc);
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static struct platform_driver sun6i_rtc_driver = {
> >> +     .probe          = sun6i_rtc_probe,
> >> +     .remove         = sun6i_rtc_remove,
> >> +     .driver         = {
> >> +             .name           = "sun6i-rtc",
> >> +             .owner          = THIS_MODULE,
> >> +             .of_match_table = sun6i_rtc_dt_ids,
> >> +     },
> >> +};
> >> +
> >> +module_platform_driver(sun6i_rtc_driver);
> >> +
> >> +MODULE_DESCRIPTION("sun6i RTC driver");
> >> +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
> >> +MODULE_LICENSE("GPL");
> >> --
> >> 2.0.1
> >>
> >
> > Thanks!
> > Maxime
> 
> Thanks for the review!
> ChenYu

Thanks for your efforts on this,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140723/136b332c/attachment.sig>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver
  2014-07-23  9:55       ` Maxime Ripard
@ 2014-07-23 14:37         ` Chen-Yu Tsai
  0 siblings, 0 replies; 13+ messages in thread
From: Chen-Yu Tsai @ 2014-07-23 14:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jul 23, 2014 at 5:55 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Mon, Jul 21, 2014 at 10:46:06PM +0800, Chen-Yu Tsai wrote:
>> Hi,
>>
>> On Fri, Jul 18, 2014 at 4:07 PM, Maxime Ripard
>> <maxime.ripard@free-electrons.com> wrote:
>> > Hi,
>> >
>> > On Mon, Jul 14, 2014 at 03:32:13PM +0800, Chen-Yu Tsai wrote:
>> >> This patch introduces the driver for the RTC in the Allwinner A31 and
>> >> A23 SoCs.
>> >>
>> >> Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
>> >> RTC in A31/A23 are a separate hardware block, which also contain a few
>> >> controls for the RTC block hardware (a regulator and RTC block GPIO pin
>> >> latches), while also having separate interrupts for the alarms.
>> >
>> > Do you plan on supporting those at some point?
>>
>> I haven't seen any devices use the regulator (which has an output pin).
>> I suppose we shouldn't add drivers for things we can't verify.
>> As for the GPIO pin latches, I'll have to experiment some more to figure
>> out what they do exactly.
>
> Ok.
>
>> > It's also worth noting that the first registers are supposed to
>> > control the source of the low frequency oscillator in the SoC, which
>> > will probably be the most troublesome, since we need these clocks very
>> > early on.
>>
>> That's true. I suppose the bootloader configures this. IIRC I've seen
>> code for this in boot0 or boot1 from Allwinner. I can't find the equivalent
>> for our sun4i u-boot though.
>
> Do you know what was the bootloader configuring it to?

I checked this some time ago, when I started to look at the sunxi clock code.
For all the devices (sun6/7/8i) I've checked, the system is configured to use
the external 32.768k oscillator.

> I don't really know how we can deal with this in a nice way, but I
> guess it's not so urgent.

I agree. I will look at the BROM (for sun4/7i) and boot0 (sun6i) for signs of
when it is configured. Then we can determine if we need to implement it
somewhere.

[...]

ChenYu

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2014-07-23 14:37 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-14  7:32 [PATCH 0/5] ARM: sunxi: RTC support for A31/A23 Chen-Yu Tsai
2014-07-14  7:32 ` [PATCH 1/5] rtc: sun6i: Add sun6i RTC driver Chen-Yu Tsai
2014-07-18  8:07   ` Maxime Ripard
2014-07-21 14:46     ` Chen-Yu Tsai
2014-07-23  9:55       ` Maxime Ripard
2014-07-23 14:37         ` Chen-Yu Tsai
2014-07-14  7:32 ` [PATCH 2/5] rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Chen-Yu Tsai
2014-07-18  8:11   ` Maxime Ripard
2014-07-14  7:32 ` [PATCH 3/5] ARM: dts: sun6i: add rtc device node Chen-Yu Tsai
2014-07-14  7:32 ` [PATCH 4/5] ARM: dts: sun8i: " Chen-Yu Tsai
2014-07-14  7:32 ` [PATCH 5/5] ARM: sunxi: Add A31 RTC driver to sunxi_defconfig Chen-Yu Tsai
2014-07-18  8:12   ` Maxime Ripard
2014-07-18  9:25     ` Chen-Yu Tsai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).