aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2015-03-09 15:50:40 -0400
committerSimon Horman <horms+renesas@verge.net.au>2015-03-10 20:52:12 -0400
commit663fbb52159cca6ffb049cb15b9f9d1cd7ad7d8a (patch)
treec354ff173f5616bd24faf68b441d1cc21d5c271f
parentc517d838eb7d07bbe9507871fab3931deccff539 (diff)
ARM: shmobile: R-Car Gen2: Add da9063/da9210 regulator quirk
The r8a7790/lager and r8a7791/koelsch development boards have da9063 and da9210 regulators. Both regulators have their interrupt request lines tied to the same interrupt pin (IRQ2) on the SoC. After cold boot or da9063-induced restart, both the da9063 and da9210 seem to assert their interrupt request lines. Hence as soon as one driver requests this irq, it gets stuck in an interrupt storm, as it only manages to deassert its own interrupt request line, and the other driver hasn't installed an interrupt handler yet. To handle this, install a quirk that masks the interrupts in both the da9063 and da9210. This quirk has to run after the i2c master driver has been initialized, but before the i2c slave drivers are initialized. As it depends on i2c, select I2C if one of the affected platforms is enabled in the kernel config. On koelsch, the following happens: - Cold boot or reboot using the da9063 restart handler: IRQ2 is asserted, installing da9063/da9210 regulator quirk ... i2c i2c-6: regulator_quirk_notify: 1, IRQC_MONITOR = 0x3fb i2c 6-0058: regulator_quirk_notify: 1, IRQC_MONITOR = 0x3fb i2c 6-0058: Detected da9063 i2c 6-0058: Masking da9063 interrupt sources i2c 6-0068: regulator_quirk_notify: 1, IRQC_MONITOR = 0x3fb i2c 6-0068: Detected da9210 i2c 6-0068: Masking da9210 interrupt sources i2c 6-0068: IRQ2 is not asserted, removing quirk - Warm boot (reset button): rcar_gen2_regulator_quirk: IRQ2 is not asserted, not installing quirk Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Reviewed-by: Mark Brown <broonie@kernel.org> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
-rw-r--r--arch/arm/mach-shmobile/Kconfig2
-rw-r--r--arch/arm/mach-shmobile/Makefile2
-rw-r--r--arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c147
3 files changed, 151 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 2f36c85eec4b..347b6a58fc3e 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -69,10 +69,12 @@ config ARCH_R8A7779
69config ARCH_R8A7790 69config ARCH_R8A7790
70 bool "R-Car H2 (R8A77900)" 70 bool "R-Car H2 (R8A77900)"
71 select ARCH_RCAR_GEN2 71 select ARCH_RCAR_GEN2
72 select I2C
72 73
73config ARCH_R8A7791 74config ARCH_R8A7791
74 bool "R-Car M2-W (R8A77910)" 75 bool "R-Car M2-W (R8A77910)"
75 select ARCH_RCAR_GEN2 76 select ARCH_RCAR_GEN2
77 select I2C
76 78
77config ARCH_R8A7794 79config ARCH_R8A7794
78 bool "R-Car E2 (R8A77940)" 80 bool "R-Car E2 (R8A77940)"
diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index d53996e6da97..fb9ef904f4b5 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -35,6 +35,8 @@ cpu-y := platsmp.o headsmp.o
35# Shared SoC family objects 35# Shared SoC family objects
36obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y) 36obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y)
37CFLAGS_setup-rcar-gen2.o += -march=armv7-a 37CFLAGS_setup-rcar-gen2.o += -march=armv7-a
38obj-$(CONFIG_ARCH_R8A7790) += regulator-quirk-rcar-gen2.o
39obj-$(CONFIG_ARCH_R8A7791) += regulator-quirk-rcar-gen2.o
38 40
39# SMP objects 41# SMP objects
40smp-y := $(cpu-y) 42smp-y := $(cpu-y)
diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
new file mode 100644
index 000000000000..384e6e934b87
--- /dev/null
+++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
@@ -0,0 +1,147 @@
1/*
2 * R-Car Generation 2 da9063/da9210 regulator quirk
3 *
4 * The r8a7790/lager and r8a7791/koelsch development boards have da9063 and
5 * da9210 regulators. Both regulators have their interrupt request lines tied
6 * to the same interrupt pin (IRQ2) on the SoC.
7 *
8 * After cold boot or da9063-induced restart, both the da9063 and da9210 seem
9 * to assert their interrupt request lines. Hence as soon as one driver
10 * requests this irq, it gets stuck in an interrupt storm, as it only manages
11 * to deassert its own interrupt request line, and the other driver hasn't
12 * installed an interrupt handler yet.
13 *
14 * To handle this, install a quirk that masks the interrupts in both the
15 * da9063 and da9210. This quirk has to run after the i2c master driver has
16 * been initialized, but before the i2c slave drivers are initialized.
17 *
18 * Copyright (C) 2015 Glider bvba
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; version 2 of the License.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 */
29
30#include <linux/device.h>
31#include <linux/i2c.h>
32#include <linux/init.h>
33#include <linux/io.h>
34#include <linux/notifier.h>
35#include <linux/of.h>
36#include <linux/mfd/da9063/registers.h>
37
38
39#define IRQC_BASE 0xe61c0000
40#define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */
41
42#define REGULATOR_IRQ_MASK BIT(2) /* IRQ2, active low */
43
44static void __iomem *irqc;
45
46static const u8 da9063_mask_regs[] = {
47 DA9063_REG_IRQ_MASK_A,
48 DA9063_REG_IRQ_MASK_B,
49 DA9063_REG_IRQ_MASK_C,
50 DA9063_REG_IRQ_MASK_D,
51};
52
53/* DA9210 System Control and Event Registers */
54#define DA9210_REG_MASK_A 0x54
55#define DA9210_REG_MASK_B 0x55
56
57static const u8 da9210_mask_regs[] = {
58 DA9210_REG_MASK_A,
59 DA9210_REG_MASK_B,
60};
61
62static void da9xxx_mask_irqs(struct i2c_client *client, const u8 regs[],
63 unsigned int nregs)
64{
65 unsigned int i;
66
67 dev_info(&client->dev, "Masking %s interrupt sources\n", client->name);
68
69 for (i = 0; i < nregs; i++) {
70 int error = i2c_smbus_write_byte_data(client, regs[i], ~0);
71 if (error) {
72 dev_err(&client->dev, "i2c error %d\n", error);
73 return;
74 }
75 }
76}
77
78static int regulator_quirk_notify(struct notifier_block *nb,
79 unsigned long action, void *data)
80{
81 struct device *dev = data;
82 struct i2c_client *client;
83 u32 mon;
84
85 mon = ioread32(irqc + IRQC_MONITOR);
86 dev_dbg(dev, "%s: %ld, IRQC_MONITOR = 0x%x\n", __func__, action, mon);
87 if (mon & REGULATOR_IRQ_MASK)
88 goto remove;
89
90 if (action != BUS_NOTIFY_ADD_DEVICE || dev->type == &i2c_adapter_type)
91 return 0;
92
93 client = to_i2c_client(dev);
94 dev_dbg(dev, "Detected %s\n", client->name);
95
96 if ((client->addr == 0x58 && !strcmp(client->name, "da9063")))
97 da9xxx_mask_irqs(client, da9063_mask_regs,
98 ARRAY_SIZE(da9063_mask_regs));
99 else if (client->addr == 0x68 && !strcmp(client->name, "da9210"))
100 da9xxx_mask_irqs(client, da9210_mask_regs,
101 ARRAY_SIZE(da9210_mask_regs));
102
103 mon = ioread32(irqc + IRQC_MONITOR);
104 if (mon & REGULATOR_IRQ_MASK)
105 goto remove;
106
107 return 0;
108
109remove:
110 dev_info(dev, "IRQ2 is not asserted, removing quirk\n");
111
112 bus_unregister_notifier(&i2c_bus_type, nb);
113 iounmap(irqc);
114 return 0;
115}
116
117static struct notifier_block regulator_quirk_nb = {
118 .notifier_call = regulator_quirk_notify
119};
120
121static int __init rcar_gen2_regulator_quirk(void)
122{
123 u32 mon;
124
125 if (!of_machine_is_compatible("renesas,koelsch") &&
126 !of_machine_is_compatible("renesas,lager"))
127 return -ENODEV;
128
129 irqc = ioremap(IRQC_BASE, PAGE_SIZE);
130 if (!irqc)
131 return -ENOMEM;
132
133 mon = ioread32(irqc + IRQC_MONITOR);
134 if (mon & REGULATOR_IRQ_MASK) {
135 pr_debug("%s: IRQ2 is not asserted, not installing quirk\n",
136 __func__);
137 iounmap(irqc);
138 return 0;
139 }
140
141 pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n");
142
143 bus_register_notifier(&i2c_bus_type, &regulator_quirk_nb);
144 return 0;
145}
146
147arch_initcall(rcar_gen2_regulator_quirk);