* [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, ®);
+
+ /* 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, ®);
+
+ 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, ®);
+
+ 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 = <®_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 = <®_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 = <®_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 = <®_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).