Renesas ARM Based SoC da9063/da9210 Regulator Quirk for v4.1
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVBgAuAAoJENfPZGlqN0++Ks0P/04y8/pbeyOIz3jnPXvXKjE0 ouZc+wcoIlJzV2uTpHnfNsHeepKG7HBHF3GH6DLn6Au21W68JwJ3fbV7Lo0xCI2P gmphYThMfRLLxy0wx/tJhG99KDannzHxEIVpTjS7IYWCJMB/WX0s2pjxf5GlFlDu sfp1AdFlw0QuOa55lUk7nluyrw6OfJoAKUZVVeK8q8gZXRju2ecnv0yeS511d38k vrRhYip2Gone69GFIusdhDozng51YdbnAle4Z2ZLpzrkIFp+yONYvLnDlRXtxJqH UAL6cjX4O2WewO5bgnF6dnVnwm2wfXb3WcsfWQVmpQ7ujk0GsehwGvECFODh/ikm IViNijKOMhFJhJlR56bED/a1fDSWZs8eQf1q1lsUYCJDvsZ1bf15aaj5lVI2nZ+n v0/p4ZbzR2dNhPNEveKKMcLO+LNHmOAH9QgvrLLHrs7osfYimht+Rd7z1mRoNnl4 nyg/SqdPVuEu52uA1EXuDPdwJm9oGU/QNaazaR3GROPvVbgodYhnni7ors5nOFyI XRgcnW06yWUoAn5BNW67p0/mg2F1YELmp652CnWj80s7O53vc0c5vF0vmgy6fSb3 mlUqDDtUvyLROdjgW0elWph6nRIE5nFBZEylQf3PVedvKa5VxrJ6Epd03cgLw1GQ qPeQl8tUW2z48BSTMGNb =EXNz -----END PGP SIGNATURE----- Merge tag 'renesas-da9063-da9210-quirk-for-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas into next/soc Merge "Renesas ARM Based SoC da9063/da9210 Regulator Quirk for v4.1" from Simon Horman: 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. * tag 'renesas-da9063-da9210-quirk-for-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas: ARM: shmobile: lager: Add da9063 PMIC device node for system restart ARM: shmobile: lager dts: Add da9210 regulator interrupt ARM: shmobile: koelsch: Add da9063 PMIC device node for system restart ARM: shmobile: koelsch dts: Add da9210 regulator interrupt ARM: shmobile: R-Car Gen2: Add da9063/da9210 regulator quirk
This commit is contained in:
commit
ffe971ef40
|
@ -513,9 +513,27 @@
|
|||
pinctrl-0 = <&iic3_pins>;
|
||||
status = "okay";
|
||||
|
||||
pmic@58 {
|
||||
compatible = "dlg,da9063";
|
||||
reg = <0x58>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
|
||||
rtc {
|
||||
compatible = "dlg,da9063-rtc";
|
||||
};
|
||||
|
||||
wdt {
|
||||
compatible = "dlg,da9063-watchdog";
|
||||
};
|
||||
};
|
||||
|
||||
vdd_dvfs: regulator@68 {
|
||||
compatible = "dlg,da9210";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <1000000>;
|
||||
|
|
|
@ -517,9 +517,27 @@
|
|||
status = "okay";
|
||||
clock-frequency = <100000>;
|
||||
|
||||
pmic@58 {
|
||||
compatible = "dlg,da9063";
|
||||
reg = <0x58>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
|
||||
rtc {
|
||||
compatible = "dlg,da9063-rtc";
|
||||
};
|
||||
|
||||
wdt {
|
||||
compatible = "dlg,da9063-watchdog";
|
||||
};
|
||||
};
|
||||
|
||||
vdd_dvfs: regulator@68 {
|
||||
compatible = "dlg,da9210";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <1000000>;
|
||||
|
|
|
@ -69,10 +69,12 @@ config ARCH_R8A7779
|
|||
config ARCH_R8A7790
|
||||
bool "R-Car H2 (R8A77900)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select I2C
|
||||
|
||||
config ARCH_R8A7791
|
||||
bool "R-Car M2-W (R8A77910)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select I2C
|
||||
|
||||
config ARCH_R8A7794
|
||||
bool "R-Car E2 (R8A77940)"
|
||||
|
|
|
@ -35,6 +35,8 @@ cpu-y := platsmp.o headsmp.o
|
|||
# Shared SoC family objects
|
||||
obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y)
|
||||
CFLAGS_setup-rcar-gen2.o += -march=armv7-a
|
||||
obj-$(CONFIG_ARCH_R8A7790) += regulator-quirk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_R8A7791) += regulator-quirk-rcar-gen2.o
|
||||
|
||||
# SMP objects
|
||||
smp-y := $(cpu-y)
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* R-Car Generation 2 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.
|
||||
*
|
||||
* Copyright (C) 2015 Glider bvba
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/da9063/registers.h>
|
||||
|
||||
|
||||
#define IRQC_BASE 0xe61c0000
|
||||
#define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */
|
||||
|
||||
#define REGULATOR_IRQ_MASK BIT(2) /* IRQ2, active low */
|
||||
|
||||
static void __iomem *irqc;
|
||||
|
||||
static const u8 da9063_mask_regs[] = {
|
||||
DA9063_REG_IRQ_MASK_A,
|
||||
DA9063_REG_IRQ_MASK_B,
|
||||
DA9063_REG_IRQ_MASK_C,
|
||||
DA9063_REG_IRQ_MASK_D,
|
||||
};
|
||||
|
||||
/* DA9210 System Control and Event Registers */
|
||||
#define DA9210_REG_MASK_A 0x54
|
||||
#define DA9210_REG_MASK_B 0x55
|
||||
|
||||
static const u8 da9210_mask_regs[] = {
|
||||
DA9210_REG_MASK_A,
|
||||
DA9210_REG_MASK_B,
|
||||
};
|
||||
|
||||
static void da9xxx_mask_irqs(struct i2c_client *client, const u8 regs[],
|
||||
unsigned int nregs)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
dev_info(&client->dev, "Masking %s interrupt sources\n", client->name);
|
||||
|
||||
for (i = 0; i < nregs; i++) {
|
||||
int error = i2c_smbus_write_byte_data(client, regs[i], ~0);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "i2c error %d\n", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int regulator_quirk_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct i2c_client *client;
|
||||
u32 mon;
|
||||
|
||||
mon = ioread32(irqc + IRQC_MONITOR);
|
||||
dev_dbg(dev, "%s: %ld, IRQC_MONITOR = 0x%x\n", __func__, action, mon);
|
||||
if (mon & REGULATOR_IRQ_MASK)
|
||||
goto remove;
|
||||
|
||||
if (action != BUS_NOTIFY_ADD_DEVICE || dev->type == &i2c_adapter_type)
|
||||
return 0;
|
||||
|
||||
client = to_i2c_client(dev);
|
||||
dev_dbg(dev, "Detected %s\n", client->name);
|
||||
|
||||
if ((client->addr == 0x58 && !strcmp(client->name, "da9063")))
|
||||
da9xxx_mask_irqs(client, da9063_mask_regs,
|
||||
ARRAY_SIZE(da9063_mask_regs));
|
||||
else if (client->addr == 0x68 && !strcmp(client->name, "da9210"))
|
||||
da9xxx_mask_irqs(client, da9210_mask_regs,
|
||||
ARRAY_SIZE(da9210_mask_regs));
|
||||
|
||||
mon = ioread32(irqc + IRQC_MONITOR);
|
||||
if (mon & REGULATOR_IRQ_MASK)
|
||||
goto remove;
|
||||
|
||||
return 0;
|
||||
|
||||
remove:
|
||||
dev_info(dev, "IRQ2 is not asserted, removing quirk\n");
|
||||
|
||||
bus_unregister_notifier(&i2c_bus_type, nb);
|
||||
iounmap(irqc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block regulator_quirk_nb = {
|
||||
.notifier_call = regulator_quirk_notify
|
||||
};
|
||||
|
||||
static int __init rcar_gen2_regulator_quirk(void)
|
||||
{
|
||||
u32 mon;
|
||||
|
||||
if (!of_machine_is_compatible("renesas,koelsch") &&
|
||||
!of_machine_is_compatible("renesas,lager"))
|
||||
return -ENODEV;
|
||||
|
||||
irqc = ioremap(IRQC_BASE, PAGE_SIZE);
|
||||
if (!irqc)
|
||||
return -ENOMEM;
|
||||
|
||||
mon = ioread32(irqc + IRQC_MONITOR);
|
||||
if (mon & REGULATOR_IRQ_MASK) {
|
||||
pr_debug("%s: IRQ2 is not asserted, not installing quirk\n",
|
||||
__func__);
|
||||
iounmap(irqc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("IRQ2 is asserted, installing da9063/da9210 regulator quirk\n");
|
||||
|
||||
bus_register_notifier(&i2c_bus_type, ®ulator_quirk_nb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(rcar_gen2_regulator_quirk);
|
Loading…
Reference in New Issue