mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
![Rafael David Tinoco](/assets/img/avatar_default.png)
Fixes https://bugs.linaro.org/show_bug.cgi?id=3903 LTP Functional tests have caused a bad paging request when triggering the regmap_read_debugfs() logic of the device PMIC Hi6553 (reading regmap/f8000000.pmic/registers file during read_all test): Unable to handle kernel paging request at virtual address ffff0 [ffff00000984e000] pgd=0000000077ffe803, pud=0000000077ffd803,0 Internal error: Oops: 96000007 [#1] SMP ... Hardware name: HiKey Development Board (DT) ... Call trace: regmap_mmio_read8+0x24/0x40 regmap_mmio_read+0x48/0x70 _regmap_bus_reg_read+0x38/0x48 _regmap_read+0x68/0x170 regmap_read+0x50/0x78 regmap_read_debugfs+0x1a0/0x308 regmap_map_read_file+0x48/0x58 full_proxy_read+0x68/0x98 __vfs_read+0x48/0x80 vfs_read+0x94/0x150 SyS_read+0x6c/0xd8 el0_svc_naked+0x30/0x34 Code: aa1e03e0 d503201f f9400280 8b334000 (39400000) Investigations have showed that, when triggered by debugfs read() handler, the mmio regmap logic was reading a bigger (16k) register area than the one mapped by devm_ioremap_resource() during hi655x-pmic probe time (4k). This commit changes hi655x's max register, according to HW specs, to be the same as the one declared in the pmic device in hi6220's dts, fixing the issue. Cc: <stable@vger.kernel.org> #v4.9 #v4.14 #v4.16 #v4.17 Signed-off-by: Rafael David Tinoco <rafael.tinoco@linaro.org> Signed-off-by: Lee Jones <lee.jones@linaro.org>
187 lines
4.9 KiB
C
187 lines
4.9 KiB
C
/*
|
|
* Device driver for MFD hi655x PMIC
|
|
*
|
|
* Copyright (c) 2016 Hisilicon.
|
|
*
|
|
* Authors:
|
|
* Chen Feng <puck.chen@hisilicon.com>
|
|
* Fei Wang <w.f@huawei.com>
|
|
*
|
|
* 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/gpio.h>
|
|
#include <linux/io.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/init.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/hi655x-pmic.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
|
|
static const struct regmap_irq hi655x_irqs[] = {
|
|
{ .reg_offset = 0, .mask = OTMP_D1R_INT_MASK },
|
|
{ .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK },
|
|
{ .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK },
|
|
{ .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK },
|
|
{ .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK },
|
|
{ .reg_offset = 0, .mask = PWRON_D20F_INT_MASK },
|
|
{ .reg_offset = 0, .mask = PWRON_D20R_INT_MASK },
|
|
{ .reg_offset = 0, .mask = RESERVE_INT_MASK },
|
|
};
|
|
|
|
static const struct regmap_irq_chip hi655x_irq_chip = {
|
|
.name = "hi655x-pmic",
|
|
.irqs = hi655x_irqs,
|
|
.num_regs = 1,
|
|
.num_irqs = ARRAY_SIZE(hi655x_irqs),
|
|
.status_base = HI655X_IRQ_STAT_BASE,
|
|
.ack_base = HI655X_IRQ_STAT_BASE,
|
|
.mask_base = HI655X_IRQ_MASK_BASE,
|
|
};
|
|
|
|
static struct regmap_config hi655x_regmap_config = {
|
|
.reg_bits = 32,
|
|
.reg_stride = HI655X_STRIDE,
|
|
.val_bits = 8,
|
|
.max_register = HI655X_BUS_ADDR(0x400) - HI655X_STRIDE,
|
|
};
|
|
|
|
static struct resource pwrkey_resources[] = {
|
|
{
|
|
.name = "down",
|
|
.start = PWRON_D20R_INT,
|
|
.end = PWRON_D20R_INT,
|
|
.flags = IORESOURCE_IRQ,
|
|
}, {
|
|
.name = "up",
|
|
.start = PWRON_D20F_INT,
|
|
.end = PWRON_D20F_INT,
|
|
.flags = IORESOURCE_IRQ,
|
|
}, {
|
|
.name = "hold 4s",
|
|
.start = PWRON_D4SR_INT,
|
|
.end = PWRON_D4SR_INT,
|
|
.flags = IORESOURCE_IRQ,
|
|
},
|
|
};
|
|
|
|
static const struct mfd_cell hi655x_pmic_devs[] = {
|
|
{
|
|
.name = "hi65xx-powerkey",
|
|
.num_resources = ARRAY_SIZE(pwrkey_resources),
|
|
.resources = &pwrkey_resources[0],
|
|
},
|
|
{ .name = "hi655x-regulator", },
|
|
{ .name = "hi655x-clk", },
|
|
};
|
|
|
|
static void hi655x_local_irq_clear(struct regmap *map)
|
|
{
|
|
int i;
|
|
|
|
regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR);
|
|
for (i = 0; i < HI655X_IRQ_ARRAY; i++) {
|
|
regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE,
|
|
HI655X_IRQ_CLR);
|
|
}
|
|
}
|
|
|
|
static int hi655x_pmic_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct hi655x_pmic *pmic;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
void __iomem *base;
|
|
|
|
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
|
|
if (!pmic)
|
|
return -ENOMEM;
|
|
pmic->dev = dev;
|
|
|
|
pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
base = devm_ioremap_resource(dev, pmic->res);
|
|
if (IS_ERR(base))
|
|
return PTR_ERR(base);
|
|
|
|
pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
|
|
&hi655x_regmap_config);
|
|
|
|
regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver);
|
|
if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) {
|
|
dev_warn(dev, "PMU version %d unsupported\n", pmic->ver);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hi655x_local_irq_clear(pmic->regmap);
|
|
|
|
pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0);
|
|
if (!gpio_is_valid(pmic->gpio)) {
|
|
dev_err(dev, "Failed to get the pmic-gpios\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN,
|
|
"hi655x_pmic_irq");
|
|
if (ret < 0) {
|
|
dev_err(dev, "Failed to request gpio %d ret = %d\n",
|
|
pmic->gpio, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio),
|
|
IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0,
|
|
&hi655x_irq_chip, &pmic->irq_data);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, pmic);
|
|
|
|
ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs,
|
|
ARRAY_SIZE(hi655x_pmic_devs), NULL, 0,
|
|
regmap_irq_get_domain(pmic->irq_data));
|
|
if (ret) {
|
|
dev_err(dev, "Failed to register device %d\n", ret);
|
|
regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hi655x_pmic_remove(struct platform_device *pdev)
|
|
{
|
|
struct hi655x_pmic *pmic = platform_get_drvdata(pdev);
|
|
|
|
regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
|
|
mfd_remove_devices(&pdev->dev);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id hi655x_pmic_match[] = {
|
|
{ .compatible = "hisilicon,hi655x-pmic", },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, hi655x_pmic_match);
|
|
|
|
static struct platform_driver hi655x_pmic_driver = {
|
|
.driver = {
|
|
.name = "hi655x-pmic",
|
|
.of_match_table = of_match_ptr(hi655x_pmic_match),
|
|
},
|
|
.probe = hi655x_pmic_probe,
|
|
.remove = hi655x_pmic_remove,
|
|
};
|
|
module_platform_driver(hi655x_pmic_driver);
|
|
|
|
MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>");
|
|
MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver");
|
|
MODULE_LICENSE("GPL v2");
|