mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
![Wolfram Sang](/assets/img/avatar_default.png)
The current implementation only works if the da9xxx devices are added
before their drivers are registered. Only then it can apply the fixes to
both devices. Otherwise, the driver for the first device gets probed
before the fix for the second device can be applied. This is what
fails when using the IP core switcher or when having the i2c master
driver as a module.
So, we need to disable both da9xxx once we detected one of them. We now
use i2c_transfer with hardcoded i2c_messages and device addresses, so we
don't need the da9xxx client devices to be instantiated. Because the
fixup is used on specific boards only, the addresses are not going to
change.
Fixes: 663fbb5215
("ARM: shmobile: R-Car Gen2: Add da9063/da9210 regulator quirk")
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> (r8a7791/koelsch)
Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
138 lines
4 KiB
C
138 lines
4 KiB
C
/*
|
|
* 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 */
|
|
|
|
/* start of DA9210 System Control and Event Registers */
|
|
#define DA9210_REG_MASK_A 0x54
|
|
|
|
static void __iomem *irqc;
|
|
|
|
/* first byte sets the memory pointer, following are consecutive reg values */
|
|
static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff };
|
|
static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff };
|
|
|
|
static struct i2c_msg da9xxx_msgs[2] = {
|
|
{
|
|
.addr = 0x58,
|
|
.len = ARRAY_SIZE(da9063_irq_clr),
|
|
.buf = da9063_irq_clr,
|
|
}, {
|
|
.addr = 0x68,
|
|
.len = ARRAY_SIZE(da9210_irq_clr),
|
|
.buf = da9210_irq_clr,
|
|
},
|
|
};
|
|
|
|
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")) ||
|
|
(client->addr == 0x68 && !strcmp(client->name, "da9210"))) {
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "clearing da9063/da9210 interrupts\n");
|
|
ret = i2c_transfer(client->adapter, da9xxx_msgs, ARRAY_SIZE(da9xxx_msgs));
|
|
if (ret != ARRAY_SIZE(da9xxx_msgs))
|
|
dev_err(&client->dev, "i2c error %d\n", ret);
|
|
}
|
|
|
|
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") &&
|
|
!of_machine_is_compatible("renesas,gose"))
|
|
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);
|