linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC
@ 2016-06-14  2:17 Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting Chen-Yu Tsai
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Hi everyone,

This series adds support for X-Powers' AC100 audio codec / RTC combo IC.
This chip is found on Allwinner A80 SoC based boards, and is also part
of the AXP813/AXP818 PMIC found with Allwinner A83T SoCs.

The series focuses on the RTC side of the chip. The audio codec will
be done later, once the digital audio interface of the SoC is supported.

Patch 1 adds bulk write support for regmaps without raw formatting.
This is used in the RTC driver to write out the date and time in one
call.

Patch 2 adds device tree bindings for the AC100.

Patch 3 adds the mfd driver for the AC100.

Patch 4 adds the RTC driver for the AC100.

Patch 5 adds RTC clk output support to the RTC driver.
This is a separate patch as the clk code is over 300 LoC. Having a
separate patch should make it easier for the clk maintainers to review.

Patch 6 adds the AC100 device nodes to the A80 Optimus board dts.

Patch 7 adds the AC100 device nodes to the Cubieboard4 board dts.

Patch 8 fixes the order of device nodes in the A80 Optimus board dts.

Patch 9 fixes the order of device nodes in the Cubieboard4 board dts.

Patch 10 changes the fixed osc_32k clk to a fixed factor clk,
representing the 32k clk input pin on the SoC, and also hooks up the
ac100 rtc clk output to this clk in the board dts files.

I'm hoping we can merge the driver bits (patches 2~5) through the
mfd tree, with acks from the rtc and clk maintainers for patches
4 and 5.

Regards
ChenYu


Chen-Yu Tsai (10):
  regmap: Support bulk writes for devices without raw formatting
  mfd: ac100: Add device tree bindings for X-Powers AC100 codec/RTC
    combo IC
  mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC combo IC
  rtc: ac100: Add RTC driver for X-Powers AC100
  rtc: ac100: Add clk output support
  ARM: dts: sun9i: a80-optimus: Add device node for AC100
  ARM: dts: sun9i: cubieboard4: Add device node for AC100
  ARM: dts: sun9i: cubieboard4: Order nodes by alphabetical order
  ARM: dts: sun9i: a80-optimus: Order nodes by alphabetical order
  ARM: dts: sun9i: Switch to the AC100 RTC clock outputs for osc32k

 Documentation/devicetree/bindings/mfd/ac100.txt |  42 ++
 arch/arm/boot/dts/sun9i-a80-cubieboard4.dts     |  58 +-
 arch/arm/boot/dts/sun9i-a80-optimus.dts         |  76 ++-
 arch/arm/boot/dts/sun9i-a80.dtsi                |   9 +-
 drivers/base/regmap/regmap.c                    |  31 +-
 drivers/mfd/Kconfig                             |  10 +
 drivers/mfd/Makefile                            |   2 +
 drivers/mfd/ac100.c                             | 135 +++++
 drivers/rtc/Kconfig                             |  10 +
 drivers/rtc/Makefile                            |   1 +
 drivers/rtc/rtc-ac100.c                         | 686 ++++++++++++++++++++++++
 include/linux/mfd/ac100.h                       | 176 ++++++
 12 files changed, 1186 insertions(+), 50 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/ac100.txt
 create mode 100644 drivers/mfd/ac100.c
 create mode 100644 drivers/rtc/rtc-ac100.c
 create mode 100644 include/linux/mfd/ac100.h

-- 
2.8.1

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

* [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-29 18:48   ` Applied "regmap: Support bulk writes for devices without raw formatting" to the regmap tree Mark Brown
  2016-06-14  2:17 ` [PATCH 02/10] mfd: ac100: Add device tree bindings for X-Powers AC100 codec/RTC combo IC Chen-Yu Tsai
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

When doing a bulk writes from a device which lacks raw I/O support we
fall back to doing register at a time reads but we still use the raw
formatters in order to render the data into the word size used by the
device (since bulk reads still operate on the device word size rather
than unsigned ints).  This means that devices without raw formatting
such as those that provide reg_read() are not supported.  Provide
handling for them by copying the values read into native endian values
of the appropriate size.

This complements commit d5b98eb12420 ("regmap: Support bulk reads for
devices without raw formatting").

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/base/regmap/regmap.c | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index df2d2ef5d6b3..51fa7d66a393 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1777,8 +1777,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 	size_t val_bytes = map->format.val_bytes;
 	size_t total_size = val_bytes * val_count;
 
-	if (map->bus && !map->format.parse_inplace)
-		return -EINVAL;
 	if (!IS_ALIGNED(reg, map->reg_stride))
 		return -EINVAL;
 
@@ -1789,7 +1787,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 	 *
 	 * The first if block is used for memory mapped io. It does not allow
 	 * val_bytes of 3 for example.
-	 * The second one is used for busses which do not have this limitation
+	 * The second one is for busses that do not provide raw I/O.
+	 * The third one is used for busses which do not have these limitations
 	 * and can write arbitrary value lengths.
 	 */
 	if (!map->bus) {
@@ -1825,6 +1824,32 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 		}
 out:
 		map->unlock(map->lock_arg);
+	} else if (map->bus && !map->format.parse_inplace) {
+		const u8 *u8 = val;
+		const u16 *u16 = val;
+		const u32 *u32 = val;
+		unsigned int ival;
+
+		for (i = 0; i < val_count; i++) {
+			switch (map->format.val_bytes) {
+			case 4:
+				ival = u32[i];
+				break;
+			case 2:
+				ival = u16[i];
+				break;
+			case 1:
+				ival = u8[i];
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = regmap_write(map, reg + (i * map->reg_stride),
+					   ival);
+			if (ret)
+				return ret;
+		}
 	} else if (map->use_single_write ||
 		   (map->max_raw_write && map->max_raw_write < total_size)) {
 		int chunk_stride = map->reg_stride;
-- 
2.8.1

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

* [PATCH 02/10] mfd: ac100: Add device tree bindings for X-Powers AC100 codec/RTC combo IC
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 03/10] mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC " Chen-Yu Tsai
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 Documentation/devicetree/bindings/mfd/ac100.txt | 42 +++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/ac100.txt

diff --git a/Documentation/devicetree/bindings/mfd/ac100.txt b/Documentation/devicetree/bindings/mfd/ac100.txt
new file mode 100644
index 000000000000..fbab623e6fba
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ac100.txt
@@ -0,0 +1,42 @@
+X-Powers AC100 Codec/RTC IC device tree bindings
+
+AC100 is a audio codec and RTC subsystem combo IC. The 2 parts are
+separated, including power supplies and interrupt lines, but share
+a common register address space and host interface.
+
+Required properties:
+- compatible: "x-powers,ac100"
+- reg: The I2C slave address or RSB hardware address for the chip
+- sub-nodes:
+  - codec
+    - compatible: "x-powers,ac100-codec"
+    - interrupt-parent: The parent interrupt controller
+    - interrupts: SoC NMI / GPIO interrupt connected to the IRQ_AUDIO pin
+  - rtc
+    - compatible: "x-powers,ac100-rtc"
+    - interrupt-parent: The parent interrupt controller
+    - interrupts: SoC NMI / GPIO interrupt connected to the IRQ_RTC pin
+    - #clock-cells: shall be 1
+    - clock-output-names: "cko1_rtc", "cko2_rtc", "cko3_rtc"
+    - see clock/clock-bindings.txt for common clock bindings
+
+Example:
+
+ac100: codec@e89 {
+	compatible = "x-powers,ac100";
+	reg = <0xe89>;
+
+	ac100_codec {
+		compatible = "x-powers,ac100-codec";
+		interrupt-parent = <&nmi_intc>;
+		interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+	};
+
+	ac100_rtc {
+		compatible = "x-powers,ac100-rtc";
+		interrupt-parent = <&nmi_intc>;
+		interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+		#clock-cells = <1>;
+		clock-output-names = "cko1_rtc", "cko2_rtc", "cko3_rtc";
+	};
+};
-- 
2.8.1

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

* [PATCH 03/10] mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC combo IC
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 02/10] mfd: ac100: Add device tree bindings for X-Powers AC100 codec/RTC combo IC Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 03/10] mfd: " Chen-Yu Tsai
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

The AC100 is a multifunction device with an audio codec subsystem and
an RTC subsystem. These two subsystems share a common register space
and host interface.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/mfd/Kconfig       |  10 +++
 drivers/mfd/Makefile      |   2 +
 drivers/mfd/ac100.c       | 135 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/ac100.h | 176 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 323 insertions(+)
 create mode 100644 drivers/mfd/ac100.c
 create mode 100644 include/linux/mfd/ac100.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1bcf601de5bc..bd83849a0c8d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -101,6 +101,16 @@ config MFD_BCM590XX
 	help
 	  Support for the BCM590xx PMUs from Broadcom
 
+config MFD_AC100
+	tristate "X-Powers AC100"
+	select MFD_CORE
+	depends on SUNXI_RSB
+	help
+	  If you say Y here you get support for the X-Powers AC100 audio codec
+	  IC.
+	  This driver include only the core APIs. You have to select individual
+	  components like codecs or RTC under the corresponding menus.
+
 config MFD_AXP20X
 	tristate
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 42a66e19e191..60f9a6b0557c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -113,6 +113,8 @@ obj-$(CONFIG_PMIC_DA9052)	+= da9052-irq.o
 obj-$(CONFIG_PMIC_DA9052)	+= da9052-core.o
 obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o
+
+obj-$(CONFIG_MFD_AC100)		+= ac100.o
 obj-$(CONFIG_MFD_AXP20X)	+= axp20x.o
 obj-$(CONFIG_MFD_AXP20X_I2C)	+= axp20x-i2c.o
 obj-$(CONFIG_MFD_AXP20X_RSB)	+= axp20x-rsb.o
diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c
new file mode 100644
index 000000000000..be7fe6e52b90
--- /dev/null
+++ b/drivers/mfd/ac100.c
@@ -0,0 +1,135 @@
+/*
+ * ac100.c - MFD core driver for X-Powers' AC100 Audio Codec IC
+ *
+ * The AC100 is a highly integrated audio codec and RTC subsystem designed
+ * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC,
+ * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has
+ * 3 clock outputs.
+ *
+ * The audio codec and RTC parts are completely separate, sharing only the
+ * host interface for access to its registers.
+ *
+ * Author: Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ac100.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/sunxi-rsb.h>
+
+static const struct regmap_range ac100_writeable_ranges[] = {
+	regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL),
+	regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN),
+	regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN),
+	regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL),
+	regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL),
+	regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS),
+	regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN),
+	regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL),
+	regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT),
+	regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT),
+	regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA),
+	regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2),
+	regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2),
+	regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLK32K_OUT_CTRL3),
+	regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD),
+	regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA),
+	regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)),
+};
+
+static const struct regmap_range ac100_volatile_ranges[] = {
+	regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2),
+	regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS)
+	regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA),
+	regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1),
+	regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1),
+	regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4),
+	regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST),
+	regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA),
+	regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD),
+};
+
+static const struct regmap_access_table ac100_writeable_table = {
+	.yes_ranges	= ac100_writeable_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(ac100_writeable_ranges),
+};
+
+static const struct regmap_access_table ac100_volatile_table = {
+	.yes_ranges	= ac100_volatile_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(ac100_volatile_ranges),
+};
+
+static const struct regmap_config ac100_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 16,
+	.wr_table	= &ac100_writeable_table,
+	.volatile_table	= &ac100_volatile_table,
+	.max_register	= AC100_RTC_GP(15),
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static struct mfd_cell ac100_cells[] = {
+	{
+		.name		= "ac100-codec",
+		.of_compatible	= "x-powers,ac100-codec",
+	}, {
+		.name		= "ac100-rtc",
+		.of_compatible	= "x-powers,ac100-rtc",
+	},
+};
+
+static int ac100_rsb_probe(struct sunxi_rsb_device *rdev)
+{
+	struct ac100_dev *ac100;
+	int ret;
+
+	ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL);
+	if (!ac100)
+		return -ENOMEM;
+
+	ac100->dev = &rdev->dev;
+	sunxi_rsb_device_set_drvdata(rdev, ac100);
+
+	ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config);
+	if (IS_ERR(ac100->regmap)) {
+		ret = PTR_ERR(ac100->regmap);
+		dev_err(ac100->dev, "regmap init failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells,
+				   ARRAY_SIZE(ac100_cells), NULL, 0, NULL);
+	if (ret) {
+		dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id ac100_of_match[] = {
+	{ .compatible = "x-powers,ac100" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ac100_of_match);
+
+static struct sunxi_rsb_driver ac100_rsb_driver = {
+	.driver = {
+		.name	= "ac100",
+		.of_match_table	= of_match_ptr(ac100_of_match),
+	},
+	.probe	= ac100_rsb_probe,
+};
+module_sunxi_rsb_driver(ac100_rsb_driver);
+
+MODULE_DESCRIPTION("Audio codec MFD core driver for AC100");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h
new file mode 100644
index 000000000000..460b4fd03df1
--- /dev/null
+++ b/include/linux/mfd/ac100.h
@@ -0,0 +1,176 @@
+/*
+ * Functions and registers to access AC100 codec / RTC combo IC.
+ *
+ * Copyright (C) 2013, Carlo Caione <carlo@caione.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_AC100_H
+#define __LINUX_MFD_AC100_H
+
+#include <linux/regmap.h>
+
+struct ac100_dev {
+	struct device			*dev;
+	struct regmap			*regmap;
+};
+
+/* Audio codec related registers */
+#define AC100_CHIP_AUDIO_RST		0x00
+#define AC100_PLL_CTRL1			0x01
+#define AC100_PLL_CTRL2			0x02
+#define AC100_SYSCLK_CTRL		0x03
+#define AC100_MOD_CLK_ENA		0x04
+#define AC100_MOD_RST_CTRL		0x05
+#define AC100_I2S_SR_CTRL		0x06
+
+/* I2S1 interface */
+#define AC100_I2S1_CLK_CTRL		0x10
+#define AC100_I2S1_SND_OUT_CTRL		0x11
+#define AC100_I2S1_SND_IN_CTRL		0x12
+#define AC100_I2S1_MXR_SRC		0x13
+#define AC100_I2S1_VOL_CTRL1		0x14
+#define AC100_I2S1_VOL_CTRL2		0x15
+#define AC100_I2S1_VOL_CTRL3		0x16
+#define AC100_I2S1_VOL_CTRL4		0x17
+#define AC100_I2S1_MXR_GAIN		0x18
+
+/* I2S2 interface */
+#define AC100_I2S2_CLK_CTRL		0x20
+#define AC100_I2S2_SND_OUT_CTRL		0x21
+#define AC100_I2S2_SND_IN_CTRL		0x22
+#define AC100_I2S2_MXR_SRC		0x23
+#define AC100_I2S2_VOL_CTRL1		0x24
+#define AC100_I2S2_VOL_CTRL2		0x25
+#define AC100_I2S2_VOL_CTRL3		0x26
+#define AC100_I2S2_VOL_CTRL4		0x27
+#define AC100_I2S2_MXR_GAIN		0x28
+
+/* I2S3 interface */
+#define AC100_I2S3_CLK_CTRL		0x30
+#define AC100_I2S3_SND_OUT_CTRL		0x31
+#define AC100_I2S3_SND_IN_CTRL		0x32
+#define AC100_I2S3_SIG_PATH_CTRL	0x33
+
+/* ADC digital controls */
+#define AC100_ADC_DIG_CTRL		0x40
+#define AC100_ADC_VOL_CTRL		0x41
+
+/* HMIC plug sensing / key detection */
+#define AC100_HMIC_CTRL1		0x44
+#define AC100_HMIC_CTRL2		0x45
+#define AC100_HMIC_STATUS		0x46
+
+/* DAC digital controls */
+#define AC100_DAC_DIG_CTRL		0x48
+#define AC100_DAC_VOL_CTRL		0x49
+#define AC100_DAC_MXR_SRC		0x4c
+#define AC100_DAC_MXR_GAIN		0x4d
+
+/* Analog controls */
+#define AC100_ADC_APC_CTRL		0x50
+#define AC100_ADC_SRC			0x51
+#define AC100_ADC_SRC_BST_CTRL		0x52
+#define AC100_OUT_MXR_DAC_A_CTRL	0x53
+#define AC100_OUT_MXR_SRC		0x54
+#define AC100_OUT_MXR_SRC_BST		0x55
+#define AC100_HPOUT_CTRL		0x56
+#define AC100_ERPOUT_CTRL		0x57
+#define AC100_SPKOUT_CTRL		0x58
+#define AC100_LINEOUT_CTRL		0x59
+
+/* ADC digital audio processing (high pass filter & auto gain control */
+#define AC100_ADC_DAP_L_STA		0x80
+#define AC100_ADC_DAP_R_STA		0x81
+#define AC100_ADC_DAP_L_CTRL		0x82
+#define AC100_ADC_DAP_R_CTRL		0x83
+#define AC100_ADC_DAP_L_T_L		0x84 /* Left Target Level */
+#define AC100_ADC_DAP_R_T_L		0x85 /* Right Target Level */
+#define AC100_ADC_DAP_L_H_A_C		0x86 /* Left High Avg. Coef */
+#define AC100_ADC_DAP_L_L_A_C		0x87 /* Left Low Avg. Coef */
+#define AC100_ADC_DAP_R_H_A_C		0x88 /* Right High Avg. Coef */
+#define AC100_ADC_DAP_R_L_A_C		0x89 /* Right Low Avg. Coef */
+#define AC100_ADC_DAP_L_D_T		0x8a /* Left Decay Time */
+#define AC100_ADC_DAP_L_A_T		0x8b /* Left Attack Time */
+#define AC100_ADC_DAP_R_D_T		0x8c /* Right Decay Time */
+#define AC100_ADC_DAP_R_A_T		0x8d /* Right Attack Time */
+#define AC100_ADC_DAP_N_TH		0x8e /* Noise Threshold */
+#define AC100_ADC_DAP_L_H_N_A_C		0x8f /* Left High Noise Avg. Coef */
+#define AC100_ADC_DAP_L_L_N_A_C		0x90 /* Left Low Noise Avg. Coef */
+#define AC100_ADC_DAP_R_H_N_A_C		0x91 /* Right High Noise Avg. Coef */
+#define AC100_ADC_DAP_R_L_N_A_C		0x92 /* Right Low Noise Avg. Coef */
+#define AC100_ADC_DAP_H_HPF_C		0x93 /* High High-Pass-Filter Coef */
+#define AC100_ADC_DAP_L_HPF_C		0x94 /* Low High-Pass-Filter Coef */
+#define AC100_ADC_DAP_OPT		0x95 /* AGC Optimum */
+
+/* DAC digital audio processing (high pass filter & dynamic range control) */
+#define AC100_DAC_DAP_CTRL		0xa0
+#define AC100_DAC_DAP_H_HPF_C		0xa1 /* High High-Pass-Filter Coef */
+#define AC100_DAC_DAP_L_HPF_C		0xa2 /* Low High-Pass-Filter Coef */
+#define AC100_DAC_DAP_L_H_E_A_C		0xa3 /* Left High Energy Avg Coef */
+#define AC100_DAC_DAP_L_L_E_A_C		0xa4 /* Left Low Energy Avg Coef */
+#define AC100_DAC_DAP_R_H_E_A_C		0xa5 /* Right High Energy Avg Coef */
+#define AC100_DAC_DAP_R_L_E_A_C		0xa6 /* Right Low Energy Avg Coef */
+#define AC100_DAC_DAP_H_G_D_T_C		0xa7 /* High Gain Delay Time Coef */
+#define AC100_DAC_DAP_L_G_D_T_C		0xa8 /* Low Gain Delay Time Coef */
+#define AC100_DAC_DAP_H_G_A_T_C		0xa9 /* High Gain Attack Time Coef */
+#define AC100_DAC_DAP_L_G_A_T_C		0xaa /* Low Gain Attack Time Coef */
+#define AC100_DAC_DAP_H_E_TH		0xab /* High Energy Threshold */
+#define AC100_DAC_DAP_L_E_TH		0xac /* Low Energy Threshold */
+#define AC100_DAC_DAP_H_G_K		0xad /* High Gain K parameter */
+#define AC100_DAC_DAP_L_G_K		0xae /* Low Gain K parameter */
+#define AC100_DAC_DAP_H_G_OFF		0xaf /* High Gain offset */
+#define AC100_DAC_DAP_L_G_OFF		0xb0 /* Low Gain offset */
+#define AC100_DAC_DAP_OPT		0xb1 /* DRC optimum */
+
+/* Digital audio processing enable */
+#define AC100_ADC_DAP_ENA		0xb4
+#define AC100_DAC_DAP_ENA		0xb5
+
+/* SRC control */
+#define AC100_SRC1_CTRL1		0xb8
+#define AC100_SRC1_CTRL2		0xb9
+#define AC100_SRC1_CTRL3		0xba
+#define AC100_SRC1_CTRL4		0xbb
+#define AC100_SRC2_CTRL1		0xbc
+#define AC100_SRC2_CTRL2		0xbd
+#define AC100_SRC2_CTRL3		0xbe
+#define AC100_SRC2_CTRL4		0xbf
+
+/* RTC clk control */
+#define AC100_CLK32K_ANALOG_CTRL	0xc0
+#define AC100_CLK32K_OUT_CTRL1		0xc1
+#define AC100_CLK32K_OUT_CTRL2		0xc2
+#define AC100_CLK32K_OUT_CTRL3		0xc3
+
+/* RTC module */
+#define AC100_RTC_RST			0xc6
+#define AC100_RTC_CTRL			0xc7
+#define AC100_RTC_SEC			0xc8 /* second */
+#define AC100_RTC_MIN			0xc9 /* minute */
+#define AC100_RTC_HOU			0xca /* hour */
+#define AC100_RTC_WEE			0xcb /* weekday */
+#define AC100_RTC_DAY			0xcc /* day */
+#define AC100_RTC_MON			0xcd /* month */
+#define AC100_RTC_YEA			0xce /* year */
+#define AC100_RTC_UPD			0xcf /* update trigger */
+
+/* RTC alarm */
+#define AC100_ALM_INT_ENA		0xd0
+#define	AC100_ALM_INT_STA		0xd1
+#define AC100_ALM_SEC			0xd8
+#define AC100_ALM_MIN			0xd9
+#define AC100_ALM_HOU			0xda
+#define AC100_ALM_WEE			0xdb
+#define AC100_ALM_DAY			0xdc
+#define AC100_ALM_MON			0xdd
+#define AC100_ALM_YEA			0xde
+#define AC100_ALM_UPD			0xdf
+
+/* RTC general purpose register 0 ~ 15 */
+#define AC100_RTC_GP(x)			(0xe0 + (x))
+
+#endif /* __LINUX_MFD_AC100_H */
-- 
2.8.1

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

* [PATCH 03/10] mfd: Add driver for X-Powers AC100 audio codec / RTC combo IC
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (2 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 03/10] mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC " Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:20   ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 04/10] rtc: ac100: Add RTC driver for X-Powers AC100 Chen-Yu Tsai
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

The AC100 is a multifunction device with an audio codec subsystem and
an RTC subsystem. These two subsystems share a common register space
and host interface.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/mfd/Kconfig       |  10 ++++
 drivers/mfd/Makefile      |   2 +
 drivers/mfd/ac100.c       | 116 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/ac100.h |  62 +++++++++++++++++++++++++
 4 files changed, 190 insertions(+)
 create mode 100644 drivers/mfd/ac100.c
 create mode 100644 include/linux/mfd/ac100.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1bcf601de5bc..bd83849a0c8d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -101,6 +101,16 @@ config MFD_BCM590XX
 	help
 	  Support for the BCM590xx PMUs from Broadcom
 
+config MFD_AC100
+	tristate "X-Powers AC100"
+	select MFD_CORE
+	depends on SUNXI_RSB
+	help
+	  If you say Y here you get support for the X-Powers AC100 audio codec
+	  IC.
+	  This driver include only the core APIs. You have to select individual
+	  components like codecs or RTC under the corresponding menus.
+
 config MFD_AXP20X
 	tristate
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 42a66e19e191..60f9a6b0557c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -113,6 +113,8 @@ obj-$(CONFIG_PMIC_DA9052)	+= da9052-irq.o
 obj-$(CONFIG_PMIC_DA9052)	+= da9052-core.o
 obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o
+
+obj-$(CONFIG_MFD_AC100)		+= ac100.o
 obj-$(CONFIG_MFD_AXP20X)	+= axp20x.o
 obj-$(CONFIG_MFD_AXP20X_I2C)	+= axp20x-i2c.o
 obj-$(CONFIG_MFD_AXP20X_RSB)	+= axp20x-rsb.o
diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c
new file mode 100644
index 000000000000..3e6752e9dc07
--- /dev/null
+++ b/drivers/mfd/ac100.c
@@ -0,0 +1,116 @@
+/*
+ * ac100.c - MFD core driver for X-Powers' AC100 Audio Codec IC
+ *
+ * The AC100 is a highly integrated audio codec and RTC subsystem designed
+ * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC,
+ * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has
+ * 3 clock outputs.
+ *
+ * The audio codec and RTC parts are completely separate, sharing only the
+ * host interface for access to its registers.
+ *
+ * Author: Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ac100.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/sunxi-rsb.h>
+
+static const struct regmap_range ac100_writeable_ranges[] = {
+	regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLK32K_OUT_CTRL3),
+	regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD),
+	regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA),
+	regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)),
+};
+
+static const struct regmap_range ac100_volatile_ranges[] = {
+	regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST),
+	regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA),
+	regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD),
+};
+
+static const struct regmap_access_table ac100_writeable_table = {
+	.yes_ranges	= ac100_writeable_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(ac100_writeable_ranges),
+};
+
+static const struct regmap_access_table ac100_volatile_table = {
+	.yes_ranges	= ac100_volatile_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(ac100_volatile_ranges),
+};
+
+static const struct regmap_config ac100_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 16,
+	.wr_table	= &ac100_writeable_table,
+	.volatile_table	= &ac100_volatile_table,
+	.max_register	= AC100_RTC_GP(15),
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static struct mfd_cell ac100_cells[] = {
+	{
+		.name		= "ac100-codec",
+		.of_compatible	= "x-powers,ac100-codec",
+	}, {
+		.name		= "ac100-rtc",
+		.of_compatible	= "x-powers,ac100-rtc",
+	},
+};
+
+static int ac100_rsb_probe(struct sunxi_rsb_device *rdev)
+{
+	struct ac100_dev *ac100;
+	int ret;
+
+	ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL);
+	if (!ac100)
+		return -ENOMEM;
+
+	ac100->dev = &rdev->dev;
+	sunxi_rsb_device_set_drvdata(rdev, ac100);
+
+	ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config);
+	if (IS_ERR(ac100->regmap)) {
+		ret = PTR_ERR(ac100->regmap);
+		dev_err(ac100->dev, "regmap init failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells,
+				   ARRAY_SIZE(ac100_cells), NULL, 0, NULL);
+	if (ret) {
+		dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id ac100_of_match[] = {
+	{ .compatible = "x-powers,ac100" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ac100_of_match);
+
+static struct sunxi_rsb_driver ac100_rsb_driver = {
+	.driver = {
+		.name	= "ac100",
+		.of_match_table	= of_match_ptr(ac100_of_match),
+	},
+	.probe	= ac100_rsb_probe,
+};
+module_sunxi_rsb_driver(ac100_rsb_driver);
+
+MODULE_DESCRIPTION("Audio codec MFD core driver for AC100");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h
new file mode 100644
index 000000000000..6d3b9f5c5aa2
--- /dev/null
+++ b/include/linux/mfd/ac100.h
@@ -0,0 +1,62 @@
+/*
+ * Functions and registers to access AC100 codec / RTC combo IC.
+ *
+ * Copyright (C) 2013, Carlo Caione <carlo@caione.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_AC100_H
+#define __LINUX_MFD_AC100_H
+
+#include <linux/regmap.h>
+
+struct ac100_dev {
+	struct device			*dev;
+	struct regmap			*regmap;
+};
+
+/* Audio codec related registers */
+#define AC100_AUDIO_CHIP_RST		0x00
+#define AC100_PLL_CTRL1			0x01
+#define AC100_PLL_CTRL2			0x02
+#define AC100_SYSCLK_CTRL		0x03
+#define AC100_MOD_CLK_ENA		0x04
+#define AC100_MOD_RST_CTRL		0x05
+
+/* RTC */
+#define AC100_CLK32K_ANALOG_CTRL	0xc0
+#define AC100_CLK32K_OUT_CTRL1		0xc1
+#define AC100_CLK32K_OUT_CTRL2		0xc2
+#define AC100_CLK32K_OUT_CTRL3		0xc3
+
+/* RTC module */
+#define AC100_RTC_RST			0xc6
+#define AC100_RTC_CTRL			0xc7
+#define AC100_RTC_SEC			0xc8 /* second */
+#define AC100_RTC_MIN			0xc9 /* minute */
+#define AC100_RTC_HOU			0xca /* hour */
+#define AC100_RTC_WEE			0xcb /* weekday */
+#define AC100_RTC_DAY			0xcc /* day */
+#define AC100_RTC_MON			0xcd /* month */
+#define AC100_RTC_YEA			0xce /* year */
+#define AC100_RTC_UPD			0xcf /* update trigger */
+
+/* RTC Alarm */
+#define AC100_ALM_INT_ENA		0xd0
+#define	AC100_ALM_INT_STA		0xd1
+#define AC100_ALM_SEC			0xd8
+#define AC100_ALM_MIN			0xd9
+#define AC100_ALM_HOU			0xda
+#define AC100_ALM_WEE			0xdb
+#define AC100_ALM_DAY			0xdc
+#define AC100_ALM_MON			0xdd
+#define AC100_ALM_YEA			0xde
+#define AC100_ALM_UPD			0xdf
+
+/* RTC general purpose register 0 ~ 15 */
+#define AC100_RTC_GP(x)			(0xe0 + (x))
+
+#endif /* __LINUX_MFD_AC100_H */
-- 
2.8.1

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

* [PATCH 04/10] rtc: ac100: Add RTC driver for X-Powers AC100
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (3 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 03/10] mfd: " Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 05/10] rtc: ac100: Add clk output support Chen-Yu Tsai
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

X-Powers AC100 is a codec / RTC combo chip. This driver supports
the RTC sub-device.

The RTC block also has clock outputs and non-volatile storage.
Non-volatile storage wthin the RTC hardware is not supported.
Clock output support is added in the next patch.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/rtc/Kconfig     |  10 ++
 drivers/rtc/Makefile    |   1 +
 drivers/rtc/rtc-ac100.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 378 insertions(+)
 create mode 100644 drivers/rtc/rtc-ac100.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 18639e0cb6e2..b9d7cbb6bd76 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -183,6 +183,16 @@ config RTC_DRV_ABX80X
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-abx80x.
 
+config RTC_DRV_AC100
+	tristate "X-Powers AC100"
+	depends on MFD_AC100
+	help
+	  If you say yes here you get support for the real-time clock found
+	  in X-Powers AC100 family peripheral ICs.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-ac100.
+
 config RTC_DRV_AS3722
 	tristate "ams AS3722 RTC driver"
 	depends on MFD_AS3722
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index ea2833723fa9..b07c28779573 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_RTC_DRV_AB3100)	+= rtc-ab3100.o
 obj-$(CONFIG_RTC_DRV_AB8500)	+= rtc-ab8500.o
 obj-$(CONFIG_RTC_DRV_ABB5ZES3)	+= rtc-ab-b5ze-s3.o
 obj-$(CONFIG_RTC_DRV_ABX80X)	+= rtc-abx80x.o
+obj-$(CONFIG_RTC_DRV_AC100)	+= rtc-ac100.o
 obj-$(CONFIG_RTC_DRV_ARMADA38X)	+= rtc-armada38x.o
 obj-$(CONFIG_RTC_DRV_AS3722)	+= rtc-as3722.o
 obj-$(CONFIG_RTC_DRV_ASM9260)	+= rtc-asm9260.o
diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c
new file mode 100644
index 000000000000..360346792ca5
--- /dev/null
+++ b/drivers/rtc/rtc-ac100.c
@@ -0,0 +1,367 @@
+/*
+ * RTC Driver for X-Powers AC100
+ *
+ * Copyright (c) 2015, Chen-Yu Tsai <wens@csie.org>
+ *
+ * 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/bcd.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ac100.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+
+/* Control register */
+#define AC100_RTC_CTRL_24HOUR	BIT(0)
+
+/* RTC */
+#define AC100_RTC_SEC_MASK	GENMASK(6, 0)
+#define AC100_RTC_MIN_MASK	GENMASK(6, 0)
+#define AC100_RTC_HOU_MASK	GENMASK(5, 0)
+#define AC100_RTC_WEE_MASK	GENMASK(3, 0)
+#define AC100_RTC_DAY_MASK	GENMASK(5, 0)
+#define AC100_RTC_MON_MASK	GENMASK(4, 0)
+#define AC100_RTC_YEA_MASK	GENMASK(7, 0)
+#define AC100_RTC_YEA_LEAP	BIT(15)
+#define AC100_RTC_UPD_TRIGGER	BIT(15)
+
+/* Alarm (wall clock) */
+#define AC100_ALM_INT_ENABLE	BIT(0)
+
+#define AC100_ALM_SEC_MASK	GENMASK(5, 0)
+#define AC100_ALM_MIN_MASK	GENMASK(5, 0)
+#define AC100_ALM_HOU_MASK	GENMASK(4, 0)
+#define AC100_ALM_WEE_MASK	GENMASK(3, 0)
+#define AC100_ALM_DAY_MASK	GENMASK(5, 0)
+#define AC100_ALM_MON_MASK	GENMASK(4, 0)
+#define AC100_ALM_YEA_MASK	GENMASK(7, 0)
+#define AC100_ALM_ENABLE_FLAG	BIT(15)
+#define AC100_ALM_UPD_TRIGGER	BIT(15)
+
+/*
+ * 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 - 2069. This range is selected to match Allwinner's
+ * driver.
+ */
+#define AC100_YEAR_MIN				1970
+#define AC100_YEAR_MAX				2069
+#define AC100_YEAR_OFF				(AC100_YEAR_MIN - 1900)
+
+struct ac100_rtc_dev {
+	struct rtc_device *rtc;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex mutex;
+	int irq;
+	unsigned long alarm;
+};
+
+static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+	struct regmap *regmap = chip->regmap;
+	u16 reg[7];
+	int ret;
+
+	ret = regmap_bulk_read(regmap, AC100_RTC_SEC, reg, 7);
+	if (ret)
+		return ret;
+
+	rtc_tm->tm_sec  = bcd2bin(reg[0] & AC100_RTC_SEC_MASK);
+	rtc_tm->tm_min  = bcd2bin(reg[1] & AC100_RTC_MIN_MASK);
+	rtc_tm->tm_hour = bcd2bin(reg[2] & AC100_RTC_HOU_MASK);
+	rtc_tm->tm_wday = bcd2bin(reg[3] & AC100_RTC_WEE_MASK);
+	rtc_tm->tm_mday = bcd2bin(reg[4] & AC100_RTC_DAY_MASK);
+	rtc_tm->tm_mon  = bcd2bin(reg[5] & AC100_RTC_MON_MASK);
+	rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK);
+
+	rtc_tm->tm_mon  -= 1;
+
+	/*
+	 * switch from (data_year->min)-relative offset to
+	 * a (1900)-relative one
+	 */
+	rtc_tm->tm_year += AC100_YEAR_OFF;
+
+	return rtc_valid_tm(rtc_tm);
+}
+
+static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+	struct regmap *regmap = chip->regmap;
+	int year;
+	u16 reg[8];
+
+	/* our RTC has a limited year range... */
+	year = rtc_tm->tm_year + 1900;
+	if (year < AC100_YEAR_MIN || year > AC100_YEAR_MAX) {
+		dev_err(dev, "rtc only supports year in range %d - %d\n",
+			AC100_YEAR_MIN, AC100_YEAR_MAX);
+		return -EINVAL;
+	}
+
+	/* correct offsets */
+	rtc_tm->tm_year -= AC100_YEAR_OFF;
+	rtc_tm->tm_mon += 1;
+
+	/* convert to BCD */
+	reg[0] = bin2bcd(rtc_tm->tm_sec)  & AC100_RTC_SEC_MASK;
+	reg[1] = bin2bcd(rtc_tm->tm_min)  & AC100_RTC_MIN_MASK;
+	reg[2] = bin2bcd(rtc_tm->tm_hour) & AC100_RTC_HOU_MASK;
+	reg[3] = bin2bcd(rtc_tm->tm_wday) & AC100_RTC_WEE_MASK;
+	reg[4] = bin2bcd(rtc_tm->tm_mday) & AC100_RTC_DAY_MASK;
+	reg[5] = bin2bcd(rtc_tm->tm_mon)  & AC100_RTC_MON_MASK;
+	reg[6] = bin2bcd(rtc_tm->tm_year) & AC100_RTC_YEA_MASK;
+	/* trigger write */
+	reg[7] = AC100_RTC_UPD_TRIGGER;
+
+	/* Is it a leap year? */
+	if (is_leap_year(year))
+		reg[6] |= AC100_RTC_YEA_LEAP;
+
+	return regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8);
+}
+
+static int _ac100_rtc_alarm_irq_enable(struct ac100_rtc_dev *chip,
+				       unsigned int en)
+{
+	struct regmap *regmap = chip->regmap;
+	unsigned int val;
+
+	val = en ? AC100_ALM_INT_ENABLE : 0;
+
+	return regmap_write(regmap, AC100_ALM_INT_ENA, val);
+}
+
+static int ac100_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
+{
+	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&chip->mutex);
+	ret = _ac100_rtc_alarm_irq_enable(chip, en);
+	mutex_unlock(&chip->mutex);
+
+	return ret;
+}
+
+static int ac100_rtc_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+	struct regmap *regmap = chip->regmap;
+	struct rtc_time *alrm_tm = &alrm->time;
+	u16 reg[7];
+	unsigned int val;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	ret = regmap_read(regmap, AC100_ALM_INT_ENA, &val);
+	if (ret)
+		goto out;
+
+	alrm->enabled = !!(val & AC100_ALM_INT_ENABLE);
+
+	ret = regmap_bulk_read(regmap, AC100_ALM_SEC, reg, 7);
+	if (ret)
+		goto out;
+
+	alrm_tm->tm_sec  = bcd2bin(reg[0] & AC100_ALM_SEC_MASK);
+	alrm_tm->tm_min  = bcd2bin(reg[1] & AC100_ALM_MIN_MASK);
+	alrm_tm->tm_hour = bcd2bin(reg[2] & AC100_ALM_HOU_MASK);
+	alrm_tm->tm_wday = bcd2bin(reg[3] & AC100_ALM_WEE_MASK);
+	alrm_tm->tm_mday = bcd2bin(reg[4] & AC100_ALM_DAY_MASK);
+	alrm_tm->tm_mon  = bcd2bin(reg[5] & AC100_ALM_MON_MASK);
+	alrm_tm->tm_year = bcd2bin(reg[6] & AC100_ALM_YEA_MASK);
+
+	alrm_tm->tm_mon  -= 1;
+
+	/*
+	 * switch from (data_year->min)-relative offset to
+	 * a (1900)-relative one
+	 */
+	alrm_tm->tm_year += AC100_YEAR_OFF;
+
+out:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int ac100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
+	struct regmap *regmap = chip->regmap;
+	struct rtc_time *alrm_tm = &alrm->time;
+	u16 reg[8];
+	int year;
+	int ret;
+
+	/* our alarm has a limited year range... */
+	year = alrm_tm->tm_year + 1900;
+	if (year < AC100_YEAR_MIN || year > AC100_YEAR_MAX) {
+		dev_err(dev, "alarm only supports year in range %d - %d\n",
+			AC100_YEAR_MIN, AC100_YEAR_MAX);
+		return -EINVAL;
+	}
+
+	/* correct offsets */
+	alrm_tm->tm_year -= AC100_YEAR_OFF;
+	alrm_tm->tm_mon += 1;
+
+	/* convert to BCD */
+	reg[0] = (bin2bcd(alrm_tm->tm_sec)  & AC100_RTC_SEC_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	reg[1] = (bin2bcd(alrm_tm->tm_min)  & AC100_RTC_MIN_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	reg[2] = (bin2bcd(alrm_tm->tm_hour) & AC100_RTC_HOU_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	/* Do not enable weekday alarm */
+	reg[3] = bin2bcd(alrm_tm->tm_wday) & AC100_RTC_WEE_MASK;
+	reg[4] = (bin2bcd(alrm_tm->tm_mday) & AC100_RTC_DAY_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	reg[5] = (bin2bcd(alrm_tm->tm_mon)  & AC100_RTC_MON_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	reg[6] = (bin2bcd(alrm_tm->tm_year) & AC100_RTC_YEA_MASK) |
+			AC100_ALM_ENABLE_FLAG;
+	/* trigger write */
+	reg[7] = AC100_RTC_UPD_TRIGGER;
+
+	mutex_lock(&chip->mutex);
+
+	ret = regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8);
+	if (ret)
+		goto out;
+
+	ret = _ac100_rtc_alarm_irq_enable(chip, alrm->enabled);
+
+out:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static irqreturn_t ac100_rtc_irq(int irq, void *data)
+{
+	struct ac100_rtc_dev *chip = data;
+	struct regmap *regmap = chip->regmap;
+	unsigned int val = 0;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	/* read status */
+	ret = regmap_read(regmap, AC100_ALM_INT_STA, &val);
+	if (ret)
+		goto out;
+
+	if (val & AC100_ALM_INT_ENABLE) {
+		/* signal rtc framework */
+		rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+		/* clear status */
+		ret = regmap_write(regmap, AC100_ALM_INT_STA, val);
+		if (ret)
+			goto out;
+
+		/* disable interrupt */
+		ret = _ac100_rtc_alarm_irq_enable(chip, 0);
+		if (ret)
+			goto out;
+	}
+
+out:
+	mutex_unlock(&chip->mutex);
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops ac100_rtc_ops = {
+	.read_time	  = ac100_rtc_get_time,
+	.set_time	  = ac100_rtc_set_time,
+	.read_alarm	  = ac100_rtc_get_alarm,
+	.set_alarm	  = ac100_rtc_set_alarm,
+	.alarm_irq_enable = ac100_rtc_alarm_irq_enable,
+};
+
+static int ac100_rtc_probe(struct platform_device *pdev)
+{
+	struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent);
+	struct ac100_rtc_dev *chip;
+	int ret;
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	platform_set_drvdata(pdev, chip);
+	chip->dev = &pdev->dev;
+	chip->regmap = ac100->regmap;
+	mutex_init(&chip->mutex);
+
+	chip->irq = of_irq_get(pdev->dev.of_node, 0);
+	if (chip->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		return chip->irq;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL,
+					ac100_rtc_irq,
+					IRQF_SHARED | IRQF_ONESHOT,
+					dev_name(&pdev->dev), chip);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not request IRQ\n");
+		return ret;
+	}
+
+	/* always use 24 hour mode */
+	regmap_write_bits(chip->regmap, AC100_RTC_CTRL, AC100_RTC_CTRL_24HOUR,
+			  AC100_RTC_CTRL_24HOUR);
+
+	/* disable counter alarm interrupt */
+	regmap_write(chip->regmap, AC100_ALM_INT_ENA, 0);
+
+	/* clear counter alarm pending interrupts */
+	regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE);
+
+	chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ac100",
+					     &ac100_rtc_ops, THIS_MODULE);
+	if (IS_ERR(chip->rtc)) {
+		dev_err(&pdev->dev, "unable to register device\n");
+		return PTR_ERR(chip->rtc);
+	}
+
+	/* We do not support user interrupts */
+	chip->rtc->uie_unsupported = 1;
+
+	dev_info(&pdev->dev, "RTC enabled\n");
+
+	return 0;
+}
+
+static struct platform_driver ac100_rtc_driver = {
+	.probe		= ac100_rtc_probe,
+	.driver		= {
+		.name		= "ac100-rtc",
+	},
+};
+module_platform_driver(ac100_rtc_driver);
+
+MODULE_DESCRIPTION("X-Powers AC100 RTC driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
-- 
2.8.1

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

* [PATCH 05/10] rtc: ac100: Add clk output support
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (4 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 04/10] rtc: ac100: Add RTC driver for X-Powers AC100 Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 06/10] ARM: dts: sun9i: a80-optimus: Add device node for AC100 Chen-Yu Tsai
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

The AC100's RTC side has 3 clock outputs on external pins, which can
provide a clock signal to the SoC or other modules, such as WiFi or
GSM modules.

Support this with a custom clk driver integrated with the rtc driver.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/rtc/rtc-ac100.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 319 insertions(+)

diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c
index 360346792ca5..89b63b4bb88f 100644
--- a/drivers/rtc/rtc-ac100.c
+++ b/drivers/rtc/rtc-ac100.c
@@ -15,6 +15,7 @@
  */
 
 #include <linux/bcd.h>
+#include <linux/clk-provider.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -31,6 +32,15 @@
 /* Control register */
 #define AC100_RTC_CTRL_24HOUR	BIT(0)
 
+/* Clock output register bits */
+#define AC100_CLK32K_PRE_DIV_SHIFT	5
+#define AC100_CLK32K_PRE_DIV_WIDTH	3
+#define AC100_CLK32K_MUX_SHIFT		4
+#define AC100_CLK32K_MUX_WIDTH		1
+#define AC100_CLK32K_DIV_SHIFT		1
+#define AC100_CLK32K_DIV_WIDTH		3
+#define AC100_CLK32K_EN			BIT(0)
+
 /* RTC */
 #define AC100_RTC_SEC_MASK	GENMASK(6, 0)
 #define AC100_RTC_MIN_MASK	GENMASK(6, 0)
@@ -67,6 +77,26 @@
 #define AC100_YEAR_MAX				2069
 #define AC100_YEAR_OFF				(AC100_YEAR_MIN - 1900)
 
+struct ac100_clk32k {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 offset;
+};
+
+#define to_ac100_clk32k(_hw) container_of(_hw, struct ac100_clk32k, hw)
+
+#define AC100_RTC_32K_NAME	"ac100-rtc-32k"
+#define AC100_RTC_32K_RATE	32768
+#define AC100_ADDA_4M_NAME	"ac100-adda-4M"
+#define AC100_ADDA_4M_RATE	4000000
+#define AC100_CLK32K_NUM	3
+
+static const char * const ac100_clk32k_names[] = {
+	"ac100-clk32k-ap",
+	"ac100-clk32k-bb",
+	"ac100-clk32k-md",
+};
+
 struct ac100_rtc_dev {
 	struct rtc_device *rtc;
 	struct device *dev;
@@ -74,8 +104,283 @@ struct ac100_rtc_dev {
 	struct mutex mutex;
 	int irq;
 	unsigned long alarm;
+
+	struct clk_hw *rtc_32k_clk;
+	struct clk_hw *adda_4M_clk;
+	struct ac100_clk32k clks[AC100_CLK32K_NUM];
+	struct clk_hw_onecell_data *clk_data;
+};
+
+/**
+ * Clock controls for 3 clock output pins
+ */
+
+static const struct clk_div_table ac100_clk32k_prediv[] = {
+	{ .val = 0, .div = 1 },
+	{ .val = 1, .div = 2 },
+	{ .val = 2, .div = 4 },
+	{ .val = 3, .div = 8 },
+	{ .val = 4, .div = 16 },
+	{ .val = 5, .div = 32 },
+	{ .val = 6, .div = 64 },
+	{ .val = 7, .div = 122 },
+	{ },
+};
+
+/* Abuse the fact that one parent is 32768 Hz, and the other is 4 MHz */
+static unsigned long ac100_clk32k_recalc_rate(struct clk_hw *hw,
+					      unsigned long prate)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+	unsigned int reg, div;
+
+	regmap_read(clk->regmap, clk->offset, &reg);
+
+	/* Handle pre-divider first */
+	if (prate != AC100_RTC_32K_RATE) {
+		div = (reg >> AC100_CLK32K_PRE_DIV_SHIFT) &
+			((1 << AC100_CLK32K_PRE_DIV_WIDTH) - 1);
+		prate = divider_recalc_rate(hw, prate, div,
+					    ac100_clk32k_prediv, 0);
+	}
+
+	div = (reg >> AC100_CLK32K_DIV_SHIFT) &
+		(BIT(AC100_CLK32K_DIV_WIDTH) - 1);
+	return divider_recalc_rate(hw, prate, div, NULL,
+				   CLK_DIVIDER_POWER_OF_TWO);
+}
+
+static long ac100_clk32k_round_rate(struct clk_hw *hw, unsigned long rate,
+				    unsigned long prate)
+{
+	unsigned long best_rate = 0, tmp_rate, tmp_prate;
+	int i;
+
+	if (prate == AC100_RTC_32K_RATE)
+		return divider_round_rate(hw, rate, &prate, NULL,
+					  AC100_CLK32K_DIV_WIDTH,
+					  CLK_DIVIDER_POWER_OF_TWO);
+
+	for (i = 0; ac100_clk32k_prediv[i].div; i++) {
+		tmp_prate = DIV_ROUND_UP(prate, ac100_clk32k_prediv[i].val);
+		tmp_rate = divider_round_rate(hw, rate, &tmp_prate, NULL,
+					      AC100_CLK32K_DIV_WIDTH,
+					      CLK_DIVIDER_POWER_OF_TWO);
+
+		if (tmp_rate > rate)
+			continue;
+		if (rate - tmp_rate < best_rate - tmp_rate)
+			best_rate = tmp_rate;
+	}
+
+	return best_rate;
+}
+
+static int ac100_clk32k_determine_rate(struct clk_hw *hw,
+				       struct clk_rate_request *req)
+{
+	struct clk_hw *best_parent;
+	unsigned long best = 0;
+	int i, num_parents = clk_hw_get_num_parents(hw);
+
+	for (i = 0; i < num_parents; i++) {
+		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
+		unsigned long tmp, prate = clk_hw_get_rate(parent);
+
+		tmp = ac100_clk32k_round_rate(hw, req->rate, prate);
+
+		if (tmp > req->rate)
+			continue;
+		if (req->rate - tmp < req->rate - best) {
+			best = tmp;
+			best_parent = parent;
+		}
+	}
+
+	if (!best)
+		return -EINVAL;
+
+	req->best_parent_hw = best_parent;
+	req->best_parent_rate = best;
+	req->rate = best;
+
+	return 0;
+}
+
+static int ac100_clk32k_set_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long prate)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+	int div = 0, pre_div = 0;
+
+	do {
+		div = divider_get_val(rate * ac100_clk32k_prediv[pre_div].div,
+				      prate, NULL, AC100_CLK32K_DIV_WIDTH,
+				      CLK_DIVIDER_POWER_OF_TWO);
+		if (div >= 0)
+			break;
+	} while (prate == AC100_ADDA_4M_RATE &&
+		 ac100_clk32k_prediv[++pre_div].div);
+
+	if (div < 0)
+		return div;
+
+	pre_div = ac100_clk32k_prediv[pre_div].val;
+
+	regmap_update_bits(clk->regmap, clk->offset,
+			   ((1 << AC100_CLK32K_DIV_WIDTH) - 1) << AC100_CLK32K_DIV_SHIFT |
+			   ((1 << AC100_CLK32K_PRE_DIV_WIDTH) - 1) << AC100_CLK32K_PRE_DIV_SHIFT,
+			   (div - 1) << AC100_CLK32K_DIV_SHIFT |
+			   (pre_div - 1) << AC100_CLK32K_PRE_DIV_SHIFT);
+
+	return 0;
+}
+
+static int ac100_clk32k_prepare(struct clk_hw *hw)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+
+	return regmap_update_bits(clk->regmap, clk->offset, AC100_CLK32K_EN,
+				  AC100_CLK32K_EN);
+}
+
+static void ac100_clk32k_unprepare(struct clk_hw *hw)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+
+	regmap_update_bits(clk->regmap, clk->offset, AC100_CLK32K_EN, 0);
+}
+
+static int ac100_clk32k_is_prepared(struct clk_hw *hw)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+	unsigned int reg;
+
+	regmap_read(clk->regmap, clk->offset, &reg);
+
+	return reg & AC100_CLK32K_EN;
+}
+
+static u8 ac100_clk32k_get_parent(struct clk_hw *hw)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+	unsigned int reg;
+
+	regmap_read(clk->regmap, clk->offset, &reg);
+
+	return (reg >> AC100_CLK32K_MUX_SHIFT) & 0x1;
+}
+
+static int ac100_clk32k_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct ac100_clk32k *clk = to_ac100_clk32k(hw);
+
+	return regmap_update_bits(clk->regmap, clk->offset,
+				  BIT(AC100_CLK32K_MUX_SHIFT),
+				  index ? BIT(AC100_CLK32K_MUX_SHIFT) : 0);
+}
+
+static const struct clk_ops ac100_clk32k_ops = {
+	.prepare	= ac100_clk32k_prepare,
+	.unprepare	= ac100_clk32k_unprepare,
+	.is_prepared	= ac100_clk32k_is_prepared,
+	.recalc_rate	= ac100_clk32k_recalc_rate,
+	.determine_rate	= ac100_clk32k_determine_rate,
+	.get_parent	= ac100_clk32k_get_parent,
+	.set_parent	= ac100_clk32k_set_parent,
+	.set_rate	= ac100_clk32k_set_rate,
 };
 
+static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
+{
+	struct device_node *np = chip->dev->of_node;
+	const char *parents[2] = {AC100_RTC_32K_NAME, AC100_ADDA_4M_NAME};
+	int i, ret;
+
+	chip->clk_data = devm_kzalloc(chip->dev, sizeof(*chip->clk_data) +
+						 sizeof(*chip->clk_data->hws) *
+						 AC100_CLK32K_NUM,
+						 GFP_KERNEL);
+	if (!chip->clk_data)
+		return -ENOMEM;
+
+	chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev,
+						       AC100_RTC_32K_NAME,
+						       NULL, 0,
+						       AC100_RTC_32K_RATE);
+	if (IS_ERR(chip->rtc_32k_clk)) {
+		ret = PTR_ERR(chip->rtc_32k_clk);
+		dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n",
+			ret);
+		return ret;
+	}
+
+	/*
+	 * The ADDA 4 MHz clock is from the codec side of the AC100,
+	 * which is likely a different power domain. However, boards
+	 * always have both sides powered on, so it is impossible to
+	 * test this.
+	 */
+	chip->adda_4M_clk = clk_hw_register_fixed_rate(chip->dev,
+						       AC100_ADDA_4M_NAME,
+						       NULL, 0,
+						       AC100_ADDA_4M_RATE);
+	if (IS_ERR(chip->adda_4M_clk)) {
+		ret = PTR_ERR(chip->adda_4M_clk);
+		dev_err(chip->dev, "Failed to register ADDA-4M clock: %d\n",
+			ret);
+		goto err_unregister_rtc_32k;
+	}
+
+	for (i = 0; i < AC100_CLK32K_NUM; i++) {
+		struct ac100_clk32k *clk = &chip->clks[i];
+		struct clk_init_data init = {
+			.name = ac100_clk32k_names[i],
+			.ops = &ac100_clk32k_ops,
+			.parent_names = parents,
+			.num_parents = ARRAY_SIZE(parents),
+			.flags = 0,
+		};
+
+		clk->regmap = chip->regmap;
+		clk->offset = AC100_CLK32K_OUT_CTRL1 + i;
+		clk->hw.init = &init;
+
+		ret = devm_clk_hw_register(chip->dev, &clk->hw);
+		if (ret) {
+			dev_err(chip->dev, "Failed to register clk '%s': %d\n",
+				init.name, ret);
+			goto err_unregister_adda_4M;
+		}
+
+		chip->clk_data->hws[i] = &clk->hw;
+	}
+
+	chip->clk_data->num = i;
+	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data);
+	if (ret)
+		goto err_unregister_adda_4M;
+
+	return 0;
+
+err_unregister_adda_4M:
+	clk_unregister_fixed_rate(chip->adda_4M_clk->clk);
+err_unregister_rtc_32k:
+	clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
+
+	return ret;
+}
+
+static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip)
+{
+	of_clk_del_provider(chip->dev->of_node);
+	clk_unregister_fixed_rate(chip->adda_4M_clk->clk);
+	clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
+}
+
+/**
+ * RTC related bits
+ */
 static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm)
 {
 	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
@@ -349,13 +654,27 @@ static int ac100_rtc_probe(struct platform_device *pdev)
 	/* We do not support user interrupts */
 	chip->rtc->uie_unsupported = 1;
 
+	ret = ac100_rtc_register_clks(chip);
+	if (ret)
+		return ret;
+
 	dev_info(&pdev->dev, "RTC enabled\n");
 
 	return 0;
 }
 
+static int ac100_rtc_remove(struct platform_device *pdev)
+{
+	struct ac100_rtc_dev *chip = platform_get_drvdata(pdev);
+
+	ac100_rtc_unregister_clks(chip);
+
+	return 0;
+}
+
 static struct platform_driver ac100_rtc_driver = {
 	.probe		= ac100_rtc_probe,
+	.remove		= ac100_rtc_remove,
 	.driver		= {
 		.name		= "ac100-rtc",
 	},
-- 
2.8.1

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

* [PATCH 06/10] ARM: dts: sun9i: a80-optimus: Add device node for AC100
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (5 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 05/10] rtc: ac100: Add clk output support Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 07/10] ARM: dts: sun9i: cubieboard4: " Chen-Yu Tsai
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun9i-a80-optimus.dts | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm/boot/dts/sun9i-a80-optimus.dts b/arch/arm/boot/dts/sun9i-a80-optimus.dts
index 78fddd9152a5..24ef8e65a975 100644
--- a/arch/arm/boot/dts/sun9i-a80-optimus.dts
+++ b/arch/arm/boot/dts/sun9i-a80-optimus.dts
@@ -322,6 +322,27 @@
 			};
 		};
 	};
+
+	ac100: codec@e89 {
+		compatible = "x-powers,ac100";
+		reg = <0xe89>;
+
+		ac100_codec: codec {
+			compatible = "x-powers,ac100-codec";
+			interrupt-parent = <&r_pio>;
+			interrupts = <0 9 IRQ_TYPE_LEVEL_LOW>; /* PL9 */
+		};
+
+		ac100_rtc: rtc {
+			compatible = "x-powers,ac100-rtc";
+			interrupt-parent = <&nmi_intc>;
+			interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+			#clock-cells = <1>;
+			clock-output-names = "cko1_rtc",
+					     "cko2_rtc",
+					     "cko3_rtc";
+		};
+	};
 };
 
 #include "axp809.dtsi"
-- 
2.8.1

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

* [PATCH 07/10] ARM: dts: sun9i: cubieboard4: Add device node for AC100
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (6 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 06/10] ARM: dts: sun9i: a80-optimus: Add device node for AC100 Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 08/10] ARM: dts: sun9i: cubieboard4: Order nodes by alphabetical order Chen-Yu Tsai
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun9i-a80-cubieboard4.dts | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
index db770d06c31e..a92eeea1b222 100644
--- a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
+++ b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
@@ -250,6 +250,27 @@
 			};
 		};
 	};
+
+	ac100: codec@e89 {
+		compatible = "x-powers,ac100";
+		reg = <0xe89>;
+
+		ac100_codec: codec {
+			compatible = "x-powers,ac100-codec";
+			interrupt-parent = <&r_pio>;
+			interrupts = <0 9 IRQ_TYPE_LEVEL_LOW>; /* PL9 */
+		};
+
+		ac100_rtc: rtc {
+			compatible = "x-powers,ac100-rtc";
+			interrupt-parent = <&nmi_intc>;
+			interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+			#clock-cells = <1>;
+			clock-output-names = "cko1_rtc",
+					     "cko2_rtc",
+					     "cko3_rtc";
+		};
+	};
 };
 
 #include "axp809.dtsi"
-- 
2.8.1

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

* [PATCH 08/10] ARM: dts: sun9i: cubieboard4: Order nodes by alphabetical order
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (7 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 07/10] ARM: dts: sun9i: cubieboard4: " Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 09/10] ARM: dts: sun9i: a80-optimus: " Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 10/10] ARM: dts: sun9i: Switch to the AC100 RTC clock outputs for osc32k Chen-Yu Tsai
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Move the &pio node below the mmc nodes for proper ordering by name.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun9i-a80-cubieboard4.dts | 32 ++++++++++++++---------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
index a92eeea1b222..65f4f32f89ad 100644
--- a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
+++ b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
@@ -78,22 +78,6 @@
 	};
 };
 
-&pio {
-	led_pins_cubieboard4: led-pins@0 {
-		allwinner,pins = "PH6", "PH17";
-		allwinner,function = "gpio_out";
-		allwinner,drive = <SUN4I_PINCTRL_10_MA>;
-		allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
-	};
-
-	mmc0_cd_pin_cubieboard4: mmc0_cd_pin@0 {
-		allwinner,pins = "PH18";
-		allwinner,function = "gpio_in";
-		allwinner,drive = <SUN4I_PINCTRL_10_MA>;
-		allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
-	};
-};
-
 &mmc0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&mmc0_pins>, <&mmc0_cd_pin_cubieboard4>;
@@ -119,6 +103,22 @@
 	allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 };
 
+&pio {
+	led_pins_cubieboard4: led-pins@0 {
+		allwinner,pins = "PH6", "PH17";
+		allwinner,function = "gpio_out";
+		allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+		allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+	};
+
+	mmc0_cd_pin_cubieboard4: mmc0_cd_pin@0 {
+		allwinner,pins = "PH18";
+		allwinner,function = "gpio_in";
+		allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+		allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
+	};
+};
+
 &r_ir {
 	status = "okay";
 };
-- 
2.8.1

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

* [PATCH 09/10] ARM: dts: sun9i: a80-optimus: Order nodes by alphabetical order
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (8 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 08/10] ARM: dts: sun9i: cubieboard4: Order nodes by alphabetical order Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  2016-06-14  2:17 ` [PATCH 10/10] ARM: dts: sun9i: Switch to the AC100 RTC clock outputs for osc32k Chen-Yu Tsai
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

Move the mmc nodes above the ohci nodes for proper ordering by name.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun9i-a80-optimus.dts | 50 ++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 25 deletions(-)

diff --git a/arch/arm/boot/dts/sun9i-a80-optimus.dts b/arch/arm/boot/dts/sun9i-a80-optimus.dts
index 24ef8e65a975..3b013dc5fef1 100644
--- a/arch/arm/boot/dts/sun9i-a80-optimus.dts
+++ b/arch/arm/boot/dts/sun9i-a80-optimus.dts
@@ -119,6 +119,31 @@
 	status = "okay";
 };
 
+&mmc0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&mmc0_pins>, <&mmc0_cd_pin_optimus>;
+	vmmc-supply = <&reg_dcdc1>;
+	bus-width = <4>;
+	cd-gpios = <&pio 7 18 GPIO_ACTIVE_HIGH>; /* PH8 */
+	cd-inverted;
+	status = "okay";
+};
+
+&mmc2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&mmc2_8bit_pins>;
+	vmmc-supply = <&reg_dcdc1>;
+	bus-width = <8>;
+	non-removable;
+	cap-mmc-hw-reset;
+	status = "okay";
+};
+
+&mmc2_8bit_pins {
+	/* Increase drive strength for DDR modes */
+	allwinner,drive = <SUN4I_PINCTRL_40_MA>;
+};
+
 &ohci0 {
 	status = "okay";
 };
@@ -157,31 +182,6 @@
 	};
 };
 
-&mmc0 {
-	pinctrl-names = "default";
-	pinctrl-0 = <&mmc0_pins>, <&mmc0_cd_pin_optimus>;
-	vmmc-supply = <&reg_dcdc1>;
-	bus-width = <4>;
-	cd-gpios = <&pio 7 18 GPIO_ACTIVE_HIGH>; /* PH8 */
-	cd-inverted;
-	status = "okay";
-};
-
-&mmc2 {
-	pinctrl-names = "default";
-	pinctrl-0 = <&mmc2_8bit_pins>;
-	vmmc-supply = <&reg_dcdc1>;
-	bus-width = <8>;
-	non-removable;
-	cap-mmc-hw-reset;
-	status = "okay";
-};
-
-&mmc2_8bit_pins {
-	/* Increase drive strength for DDR modes */
-	allwinner,drive = <SUN4I_PINCTRL_40_MA>;
-};
-
 &r_ir {
 	status = "okay";
 };
-- 
2.8.1

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

* [PATCH 10/10] ARM: dts: sun9i: Switch to the AC100 RTC clock outputs for osc32k
  2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
                   ` (9 preceding siblings ...)
  2016-06-14  2:17 ` [PATCH 09/10] ARM: dts: sun9i: a80-optimus: " Chen-Yu Tsai
@ 2016-06-14  2:17 ` Chen-Yu Tsai
  10 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:17 UTC (permalink / raw)
  To: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd
  Cc: Chen-Yu Tsai, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

The 32.768 kHz clock inside the A80 SoC is fed from an external source,
typically the AC100 RTC module.

Make the osc32k placeholder a fixed-factor clock so board dts files can
specify its source.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun9i-a80-cubieboard4.dts | 5 +++++
 arch/arm/boot/dts/sun9i-a80-optimus.dts     | 5 +++++
 arch/arm/boot/dts/sun9i-a80.dtsi            | 9 +++------
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
index 65f4f32f89ad..b6299a9c14e3 100644
--- a/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
+++ b/arch/arm/boot/dts/sun9i-a80-cubieboard4.dts
@@ -103,6 +103,11 @@
 	allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 };
 
+&osc32k {
+	/* osc32k input is from AC100 */
+	clocks = <&ac100_rtc 0>;
+};
+
 &pio {
 	led_pins_cubieboard4: led-pins@0 {
 		allwinner,pins = "PH6", "PH17";
diff --git a/arch/arm/boot/dts/sun9i-a80-optimus.dts b/arch/arm/boot/dts/sun9i-a80-optimus.dts
index 3b013dc5fef1..2f079cbd2025 100644
--- a/arch/arm/boot/dts/sun9i-a80-optimus.dts
+++ b/arch/arm/boot/dts/sun9i-a80-optimus.dts
@@ -152,6 +152,11 @@
 	status = "okay";
 };
 
+&osc32k {
+	/* osc32k input is from AC100 */
+	clocks = <&ac100_rtc 0>;
+};
+
 &pio {
 	led_pins_optimus: led-pins@0 {
 		allwinner,pins = "PH0", "PH1";
diff --git a/arch/arm/boot/dts/sun9i-a80.dtsi b/arch/arm/boot/dts/sun9i-a80.dtsi
index f68b3242b33a..dd11115ec087 100644
--- a/arch/arm/boot/dts/sun9i-a80.dtsi
+++ b/arch/arm/boot/dts/sun9i-a80.dtsi
@@ -148,15 +148,12 @@
 
 		/*
 		 * The 32k clock is from an external source, normally the
-		 * AC100 codec/RTC chip. This clock is by default enabled
-		 * and clocked at 32768 Hz, from the oscillator connected
-		 * to the AC100. It is configurable, but no such driver or
-		 * bindings exist yet.
+		 * AC100 codec/RTC chip. This serves as a placeholder for
+		 * board dts files to specify the source.
 		 */
 		osc32k: osc32k_clk {
 			#clock-cells = <0>;
-			compatible = "fixed-clock";
-			clock-frequency = <32768>;
+			compatible = "fixed-factor-clock";
 			clock-output-names = "osc32k";
 		};
 
-- 
2.8.1

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

* Re: [PATCH 03/10] mfd: Add driver for X-Powers AC100 audio codec / RTC combo IC
  2016-06-14  2:17 ` [PATCH 03/10] mfd: " Chen-Yu Tsai
@ 2016-06-14  2:20   ` Chen-Yu Tsai
  0 siblings, 0 replies; 14+ messages in thread
From: Chen-Yu Tsai @ 2016-06-14  2:20 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mark Brown, Lee Jones, Alessandro Zummo, Alexandre Belloni,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Maxime Ripard, Michael Turquette, Stephen Boyd, rtc-linux,
	linux-kernel, devicetree, linux-arm-kernel, linux-clk

On Tue, Jun 14, 2016 at 10:17 AM, Chen-Yu Tsai <wens@csie.org> wrote:
> The AC100 is a multifunction device with an audio codec subsystem and
> an RTC subsystem. These two subsystems share a common register space
> and host interface.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>

Self-NACK. A last minute fix up resulted in a compile error.

I'll send v2 later. Sorry for the noise.

Regards
ChenYu

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

* Applied "regmap: Support bulk writes for devices without raw formatting" to the regmap tree
  2016-06-14  2:17 ` [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting Chen-Yu Tsai
@ 2016-06-29 18:48   ` Mark Brown
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2016-06-29 18:48 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mark Brown, Mark Brown, Lee Jones, Alessandro Zummo,
	Alexandre Belloni, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Maxime Ripard, Michael Turquette,
	Stephen Boyd, rtc-linux, linux-kernel, devicetree,
	linux-arm-kernel, linux-clk

The patch

   regmap: Support bulk writes for devices without raw formatting

has been applied to the regmap tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 5bf75b44972a7edffa9f52cddb291d66bc16a4d6 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Mon, 20 Jun 2016 10:52:11 +0800
Subject: [PATCH] regmap: Support bulk writes for devices without raw
 formatting

When doing a bulk writes from a device which lacks raw I/O support we
fall back to doing register at a time reads but we still use the raw
formatters in order to render the data into the word size used by the
device (since bulk reads still operate on the device word size rather
than unsigned ints).  This means that devices without raw formatting
such as those that provide reg_read() are not supported.  Provide
handling for them by copying the values read into native endian values
of the appropriate size.

This complements commit d5b98eb12420 ("regmap: Support bulk reads for
devices without raw formatting").

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 drivers/base/regmap/regmap.c | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index df2d2ef5d6b3..51fa7d66a393 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1777,8 +1777,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 	size_t val_bytes = map->format.val_bytes;
 	size_t total_size = val_bytes * val_count;
 
-	if (map->bus && !map->format.parse_inplace)
-		return -EINVAL;
 	if (!IS_ALIGNED(reg, map->reg_stride))
 		return -EINVAL;
 
@@ -1789,7 +1787,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 	 *
 	 * The first if block is used for memory mapped io. It does not allow
 	 * val_bytes of 3 for example.
-	 * The second one is used for busses which do not have this limitation
+	 * The second one is for busses that do not provide raw I/O.
+	 * The third one is used for busses which do not have these limitations
 	 * and can write arbitrary value lengths.
 	 */
 	if (!map->bus) {
@@ -1825,6 +1824,32 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 		}
 out:
 		map->unlock(map->lock_arg);
+	} else if (map->bus && !map->format.parse_inplace) {
+		const u8 *u8 = val;
+		const u16 *u16 = val;
+		const u32 *u32 = val;
+		unsigned int ival;
+
+		for (i = 0; i < val_count; i++) {
+			switch (map->format.val_bytes) {
+			case 4:
+				ival = u32[i];
+				break;
+			case 2:
+				ival = u16[i];
+				break;
+			case 1:
+				ival = u8[i];
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			ret = regmap_write(map, reg + (i * map->reg_stride),
+					   ival);
+			if (ret)
+				return ret;
+		}
 	} else if (map->use_single_write ||
 		   (map->max_raw_write && map->max_raw_write < total_size)) {
 		int chunk_stride = map->reg_stride;
-- 
2.8.1

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

end of thread, other threads:[~2016-06-29 18:49 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-14  2:17 [PATCH 00/10] mfd: ac100: Add support for X-Powers AC100 audio codec / RTC combo IC Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 01/10] regmap: Support bulk writes for devices without raw formatting Chen-Yu Tsai
2016-06-29 18:48   ` Applied "regmap: Support bulk writes for devices without raw formatting" to the regmap tree Mark Brown
2016-06-14  2:17 ` [PATCH 02/10] mfd: ac100: Add device tree bindings for X-Powers AC100 codec/RTC combo IC Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 03/10] mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC " Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 03/10] mfd: " Chen-Yu Tsai
2016-06-14  2:20   ` Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 04/10] rtc: ac100: Add RTC driver for X-Powers AC100 Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 05/10] rtc: ac100: Add clk output support Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 06/10] ARM: dts: sun9i: a80-optimus: Add device node for AC100 Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 07/10] ARM: dts: sun9i: cubieboard4: " Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 08/10] ARM: dts: sun9i: cubieboard4: Order nodes by alphabetical order Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 09/10] ARM: dts: sun9i: a80-optimus: " Chen-Yu Tsai
2016-06-14  2:17 ` [PATCH 10/10] ARM: dts: sun9i: Switch to the AC100 RTC clock outputs for osc32k 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).