linux/drivers/mfd/hi655x-pmic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Device driver for MFD hi655x PMIC
   4 *
   5 * Copyright (c) 2016 HiSilicon Ltd.
   6 *
   7 * Authors:
   8 * Chen Feng <puck.chen@hisilicon.com>
   9 * Fei  Wang <w.f@huawei.com>
  10 */
  11
  12#include <linux/gpio.h>
  13#include <linux/io.h>
  14#include <linux/interrupt.h>
  15#include <linux/init.h>
  16#include <linux/mfd/core.h>
  17#include <linux/mfd/hi655x-pmic.h>
  18#include <linux/module.h>
  19#include <linux/of_gpio.h>
  20#include <linux/of_platform.h>
  21#include <linux/platform_device.h>
  22#include <linux/regmap.h>
  23
  24static const struct regmap_irq hi655x_irqs[] = {
  25        { .reg_offset = 0, .mask = OTMP_D1R_INT_MASK },
  26        { .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK },
  27        { .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK },
  28        { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK },
  29        { .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK },
  30        { .reg_offset = 0, .mask = PWRON_D20F_INT_MASK },
  31        { .reg_offset = 0, .mask = PWRON_D20R_INT_MASK },
  32        { .reg_offset = 0, .mask = RESERVE_INT_MASK },
  33};
  34
  35static const struct regmap_irq_chip hi655x_irq_chip = {
  36        .name = "hi655x-pmic",
  37        .irqs = hi655x_irqs,
  38        .num_regs = 1,
  39        .num_irqs = ARRAY_SIZE(hi655x_irqs),
  40        .status_base = HI655X_IRQ_STAT_BASE,
  41        .ack_base = HI655X_IRQ_STAT_BASE,
  42        .mask_base = HI655X_IRQ_MASK_BASE,
  43};
  44
  45static struct regmap_config hi655x_regmap_config = {
  46        .reg_bits = 32,
  47        .reg_stride = HI655X_STRIDE,
  48        .val_bits = 8,
  49        .max_register = HI655X_BUS_ADDR(0x400) - HI655X_STRIDE,
  50};
  51
  52static const struct resource pwrkey_resources[] = {
  53        {
  54                .name   = "down",
  55                .start  = PWRON_D20R_INT,
  56                .end    = PWRON_D20R_INT,
  57                .flags  = IORESOURCE_IRQ,
  58        }, {
  59                .name   = "up",
  60                .start  = PWRON_D20F_INT,
  61                .end    = PWRON_D20F_INT,
  62                .flags  = IORESOURCE_IRQ,
  63        }, {
  64                .name   = "hold 4s",
  65                .start  = PWRON_D4SR_INT,
  66                .end    = PWRON_D4SR_INT,
  67                .flags  = IORESOURCE_IRQ,
  68        },
  69};
  70
  71static const struct mfd_cell hi655x_pmic_devs[] = {
  72        {
  73                .name           = "hi65xx-powerkey",
  74                .num_resources  = ARRAY_SIZE(pwrkey_resources),
  75                .resources      = &pwrkey_resources[0],
  76        },
  77        {       .name           = "hi655x-regulator",   },
  78        {       .name           = "hi655x-clk",         },
  79};
  80
  81static void hi655x_local_irq_clear(struct regmap *map)
  82{
  83        int i;
  84
  85        regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR);
  86        for (i = 0; i < HI655X_IRQ_ARRAY; i++) {
  87                regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE,
  88                             HI655X_IRQ_CLR);
  89        }
  90}
  91
  92static int hi655x_pmic_probe(struct platform_device *pdev)
  93{
  94        int ret;
  95        struct hi655x_pmic *pmic;
  96        struct device *dev = &pdev->dev;
  97        struct device_node *np = dev->of_node;
  98        void __iomem *base;
  99
 100        pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
 101        if (!pmic)
 102                return -ENOMEM;
 103        pmic->dev = dev;
 104
 105        pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 106        base = devm_ioremap_resource(dev, pmic->res);
 107        if (IS_ERR(base))
 108                return PTR_ERR(base);
 109
 110        pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
 111                                                 &hi655x_regmap_config);
 112        if (IS_ERR(pmic->regmap))
 113                return PTR_ERR(pmic->regmap);
 114
 115        regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver);
 116        if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) {
 117                dev_warn(dev, "PMU version %d unsupported\n", pmic->ver);
 118                return -EINVAL;
 119        }
 120
 121        hi655x_local_irq_clear(pmic->regmap);
 122
 123        pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0);
 124        if (!gpio_is_valid(pmic->gpio)) {
 125                dev_err(dev, "Failed to get the pmic-gpios\n");
 126                return -ENODEV;
 127        }
 128
 129        ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN,
 130                                    "hi655x_pmic_irq");
 131        if (ret < 0) {
 132                dev_err(dev, "Failed to request gpio %d  ret = %d\n",
 133                        pmic->gpio, ret);
 134                return ret;
 135        }
 136
 137        ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio),
 138                                  IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0,
 139                                  &hi655x_irq_chip, &pmic->irq_data);
 140        if (ret) {
 141                dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret);
 142                return ret;
 143        }
 144
 145        platform_set_drvdata(pdev, pmic);
 146
 147        ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs,
 148                              ARRAY_SIZE(hi655x_pmic_devs), NULL, 0,
 149                              regmap_irq_get_domain(pmic->irq_data));
 150        if (ret) {
 151                dev_err(dev, "Failed to register device %d\n", ret);
 152                regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
 153                return ret;
 154        }
 155
 156        return 0;
 157}
 158
 159static int hi655x_pmic_remove(struct platform_device *pdev)
 160{
 161        struct hi655x_pmic *pmic = platform_get_drvdata(pdev);
 162
 163        regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data);
 164        mfd_remove_devices(&pdev->dev);
 165        return 0;
 166}
 167
 168static const struct of_device_id hi655x_pmic_match[] = {
 169        { .compatible = "hisilicon,hi655x-pmic", },
 170        {},
 171};
 172MODULE_DEVICE_TABLE(of, hi655x_pmic_match);
 173
 174static struct platform_driver hi655x_pmic_driver = {
 175        .driver = {
 176                .name = "hi655x-pmic",
 177                .of_match_table = of_match_ptr(hi655x_pmic_match),
 178        },
 179        .probe  = hi655x_pmic_probe,
 180        .remove = hi655x_pmic_remove,
 181};
 182module_platform_driver(hi655x_pmic_driver);
 183
 184MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>");
 185MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver");
 186MODULE_LICENSE("GPL v2");
 187