1b52207efSChen Feng /* 2b52207efSChen Feng * Device driver for MFD hi655x PMIC 3b52207efSChen Feng * 4b52207efSChen Feng * Copyright (c) 2016 Hisilicon. 5b52207efSChen Feng * 6b52207efSChen Feng * Authors: 7b52207efSChen Feng * Chen Feng <puck.chen@hisilicon.com> 8b52207efSChen Feng * Fei Wang <w.f@huawei.com> 9b52207efSChen Feng * 10b52207efSChen Feng * This program is free software; you can redistribute it and/or modify 11b52207efSChen Feng * it under the terms of the GNU General Public License version 2 as 12b52207efSChen Feng * published by the Free Software Foundation. 13b52207efSChen Feng */ 14b52207efSChen Feng 15b52207efSChen Feng #include <linux/gpio.h> 16b52207efSChen Feng #include <linux/io.h> 17b52207efSChen Feng #include <linux/interrupt.h> 18b52207efSChen Feng #include <linux/init.h> 19b52207efSChen Feng #include <linux/mfd/core.h> 20b52207efSChen Feng #include <linux/mfd/hi655x-pmic.h> 21b52207efSChen Feng #include <linux/module.h> 22b52207efSChen Feng #include <linux/of_gpio.h> 23b52207efSChen Feng #include <linux/of_platform.h> 24b52207efSChen Feng #include <linux/platform_device.h> 25b52207efSChen Feng #include <linux/regmap.h> 26b52207efSChen Feng 27b52207efSChen Feng static const struct mfd_cell hi655x_pmic_devs[] = { 28b52207efSChen Feng { .name = "hi655x-regulator", }, 29b52207efSChen Feng }; 30b52207efSChen Feng 31b52207efSChen Feng static const struct regmap_irq hi655x_irqs[] = { 329e3d5c99SJohn Stultz { .reg_offset = 0, .mask = OTMP_D1R_INT_MASK }, 339e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK }, 349e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK }, 359e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK }, 369e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK }, 379e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D20F_INT_MASK }, 389e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D20R_INT_MASK }, 399e3d5c99SJohn Stultz { .reg_offset = 0, .mask = RESERVE_INT_MASK }, 40b52207efSChen Feng }; 41b52207efSChen Feng 42b52207efSChen Feng static const struct regmap_irq_chip hi655x_irq_chip = { 43b52207efSChen Feng .name = "hi655x-pmic", 44b52207efSChen Feng .irqs = hi655x_irqs, 45b52207efSChen Feng .num_regs = 1, 46b52207efSChen Feng .num_irqs = ARRAY_SIZE(hi655x_irqs), 47b52207efSChen Feng .status_base = HI655X_IRQ_STAT_BASE, 48417e06bbSJohn Stultz .ack_base = HI655X_IRQ_STAT_BASE, 49b52207efSChen Feng .mask_base = HI655X_IRQ_MASK_BASE, 50b52207efSChen Feng }; 51b52207efSChen Feng 52b52207efSChen Feng static struct regmap_config hi655x_regmap_config = { 53b52207efSChen Feng .reg_bits = 32, 54b52207efSChen Feng .reg_stride = HI655X_STRIDE, 55b52207efSChen Feng .val_bits = 8, 56b52207efSChen Feng .max_register = HI655X_BUS_ADDR(0xFFF), 57b52207efSChen Feng }; 58b52207efSChen Feng 59b52207efSChen Feng static void hi655x_local_irq_clear(struct regmap *map) 60b52207efSChen Feng { 61b52207efSChen Feng int i; 62b52207efSChen Feng 63b52207efSChen Feng regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR); 64b52207efSChen Feng for (i = 0; i < HI655X_IRQ_ARRAY; i++) { 65b52207efSChen Feng regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE, 66b52207efSChen Feng HI655X_IRQ_CLR); 67b52207efSChen Feng } 68b52207efSChen Feng } 69b52207efSChen Feng 70b52207efSChen Feng static int hi655x_pmic_probe(struct platform_device *pdev) 71b52207efSChen Feng { 72b52207efSChen Feng int ret; 73b52207efSChen Feng struct hi655x_pmic *pmic; 74b52207efSChen Feng struct device *dev = &pdev->dev; 75b52207efSChen Feng struct device_node *np = dev->of_node; 76b52207efSChen Feng void __iomem *base; 77b52207efSChen Feng 78b52207efSChen Feng pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 79b52207efSChen Feng if (!pmic) 80b52207efSChen Feng return -ENOMEM; 81b52207efSChen Feng pmic->dev = dev; 82b52207efSChen Feng 83b52207efSChen Feng pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 84b52207efSChen Feng if (!pmic->res) 85b52207efSChen Feng return -ENOENT; 86b52207efSChen Feng 87b52207efSChen Feng base = devm_ioremap_resource(dev, pmic->res); 88b52207efSChen Feng if (!base) 89b52207efSChen Feng return -ENOMEM; 90b52207efSChen Feng 91b52207efSChen Feng pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, 92b52207efSChen Feng &hi655x_regmap_config); 93b52207efSChen Feng 94b52207efSChen Feng regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver); 95b52207efSChen Feng if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) { 96b52207efSChen Feng dev_warn(dev, "PMU version %d unsupported\n", pmic->ver); 97b52207efSChen Feng return -EINVAL; 98b52207efSChen Feng } 99b52207efSChen Feng 100b52207efSChen Feng hi655x_local_irq_clear(pmic->regmap); 101b52207efSChen Feng 102b52207efSChen Feng pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0); 103b52207efSChen Feng if (!gpio_is_valid(pmic->gpio)) { 104b52207efSChen Feng dev_err(dev, "Failed to get the pmic-gpios\n"); 105b52207efSChen Feng return -ENODEV; 106b52207efSChen Feng } 107b52207efSChen Feng 108b52207efSChen Feng ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, 109b52207efSChen Feng "hi655x_pmic_irq"); 110b52207efSChen Feng if (ret < 0) { 111b52207efSChen Feng dev_err(dev, "Failed to request gpio %d ret = %d\n", 112b52207efSChen Feng pmic->gpio, ret); 113b52207efSChen Feng return ret; 114b52207efSChen Feng } 115b52207efSChen Feng 116b52207efSChen Feng ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio), 117b52207efSChen Feng IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, 118b52207efSChen Feng &hi655x_irq_chip, &pmic->irq_data); 119b52207efSChen Feng if (ret) { 120b52207efSChen Feng dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret); 121b52207efSChen Feng return ret; 122b52207efSChen Feng } 123b52207efSChen Feng 124b52207efSChen Feng platform_set_drvdata(pdev, pmic); 125b52207efSChen Feng 126b52207efSChen Feng ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs, 127b52207efSChen Feng ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, NULL); 128b52207efSChen Feng if (ret) { 129b52207efSChen Feng dev_err(dev, "Failed to register device %d\n", ret); 130b52207efSChen Feng regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); 131b52207efSChen Feng return ret; 132b52207efSChen Feng } 133b52207efSChen Feng 134b52207efSChen Feng return 0; 135b52207efSChen Feng } 136b52207efSChen Feng 137b52207efSChen Feng static int hi655x_pmic_remove(struct platform_device *pdev) 138b52207efSChen Feng { 139b52207efSChen Feng struct hi655x_pmic *pmic = platform_get_drvdata(pdev); 140b52207efSChen Feng 141b52207efSChen Feng regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); 142b52207efSChen Feng mfd_remove_devices(&pdev->dev); 143b52207efSChen Feng return 0; 144b52207efSChen Feng } 145b52207efSChen Feng 146b52207efSChen Feng static const struct of_device_id hi655x_pmic_match[] = { 147b52207efSChen Feng { .compatible = "hisilicon,hi655x-pmic", }, 148b52207efSChen Feng {}, 149b52207efSChen Feng }; 150b52207efSChen Feng 151b52207efSChen Feng static struct platform_driver hi655x_pmic_driver = { 152b52207efSChen Feng .driver = { 153b52207efSChen Feng .name = "hi655x-pmic", 154b52207efSChen Feng .of_match_table = of_match_ptr(hi655x_pmic_match), 155b52207efSChen Feng }, 156b52207efSChen Feng .probe = hi655x_pmic_probe, 157b52207efSChen Feng .remove = hi655x_pmic_remove, 158b52207efSChen Feng }; 159b52207efSChen Feng module_platform_driver(hi655x_pmic_driver); 160b52207efSChen Feng 161b52207efSChen Feng MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); 162b52207efSChen Feng MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver"); 163b52207efSChen Feng MODULE_LICENSE("GPL v2"); 164