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 regmap_irq hi655x_irqs[] = { 289e3d5c99SJohn Stultz { .reg_offset = 0, .mask = OTMP_D1R_INT_MASK }, 299e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_2P5_R_INT_MASK }, 309e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_UV_D3R_INT_MASK }, 319e3d5c99SJohn Stultz { .reg_offset = 0, .mask = VSYS_6P0_D200UR_INT_MASK }, 329e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D4SR_INT_MASK }, 339e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D20F_INT_MASK }, 349e3d5c99SJohn Stultz { .reg_offset = 0, .mask = PWRON_D20R_INT_MASK }, 359e3d5c99SJohn Stultz { .reg_offset = 0, .mask = RESERVE_INT_MASK }, 36b52207efSChen Feng }; 37b52207efSChen Feng 38b52207efSChen Feng static const struct regmap_irq_chip hi655x_irq_chip = { 39b52207efSChen Feng .name = "hi655x-pmic", 40b52207efSChen Feng .irqs = hi655x_irqs, 41b52207efSChen Feng .num_regs = 1, 42b52207efSChen Feng .num_irqs = ARRAY_SIZE(hi655x_irqs), 43b52207efSChen Feng .status_base = HI655X_IRQ_STAT_BASE, 44417e06bbSJohn Stultz .ack_base = HI655X_IRQ_STAT_BASE, 45b52207efSChen Feng .mask_base = HI655X_IRQ_MASK_BASE, 46b52207efSChen Feng }; 47b52207efSChen Feng 48b52207efSChen Feng static struct regmap_config hi655x_regmap_config = { 49b52207efSChen Feng .reg_bits = 32, 50b52207efSChen Feng .reg_stride = HI655X_STRIDE, 51b52207efSChen Feng .val_bits = 8, 52b52207efSChen Feng .max_register = HI655X_BUS_ADDR(0xFFF), 53b52207efSChen Feng }; 54b52207efSChen Feng 55eb10245fSJohn Stultz static struct resource pwrkey_resources[] = { 56eb10245fSJohn Stultz { 57eb10245fSJohn Stultz .name = "down", 58eb10245fSJohn Stultz .start = PWRON_D20R_INT, 59eb10245fSJohn Stultz .end = PWRON_D20R_INT, 60eb10245fSJohn Stultz .flags = IORESOURCE_IRQ, 61eb10245fSJohn Stultz }, { 62eb10245fSJohn Stultz .name = "up", 63eb10245fSJohn Stultz .start = PWRON_D20F_INT, 64eb10245fSJohn Stultz .end = PWRON_D20F_INT, 65eb10245fSJohn Stultz .flags = IORESOURCE_IRQ, 66eb10245fSJohn Stultz }, { 67eb10245fSJohn Stultz .name = "hold 4s", 68eb10245fSJohn Stultz .start = PWRON_D4SR_INT, 69eb10245fSJohn Stultz .end = PWRON_D4SR_INT, 70eb10245fSJohn Stultz .flags = IORESOURCE_IRQ, 71eb10245fSJohn Stultz }, 72eb10245fSJohn Stultz }; 73eb10245fSJohn Stultz 74eb10245fSJohn Stultz static const struct mfd_cell hi655x_pmic_devs[] = { 75eb10245fSJohn Stultz { 76eb10245fSJohn Stultz .name = "hi65xx-powerkey", 77eb10245fSJohn Stultz .num_resources = ARRAY_SIZE(pwrkey_resources), 78eb10245fSJohn Stultz .resources = &pwrkey_resources[0], 79eb10245fSJohn Stultz }, 80eb10245fSJohn Stultz { .name = "hi655x-regulator", }, 81ac03fec1SDaniel Lezcano { .name = "hi655x-clk", }, 82eb10245fSJohn Stultz }; 83eb10245fSJohn Stultz 84b52207efSChen Feng static void hi655x_local_irq_clear(struct regmap *map) 85b52207efSChen Feng { 86b52207efSChen Feng int i; 87b52207efSChen Feng 88b52207efSChen Feng regmap_write(map, HI655X_ANA_IRQM_BASE, HI655X_IRQ_CLR); 89b52207efSChen Feng for (i = 0; i < HI655X_IRQ_ARRAY; i++) { 90b52207efSChen Feng regmap_write(map, HI655X_IRQ_STAT_BASE + i * HI655X_STRIDE, 91b52207efSChen Feng HI655X_IRQ_CLR); 92b52207efSChen Feng } 93b52207efSChen Feng } 94b52207efSChen Feng 95b52207efSChen Feng static int hi655x_pmic_probe(struct platform_device *pdev) 96b52207efSChen Feng { 97b52207efSChen Feng int ret; 98b52207efSChen Feng struct hi655x_pmic *pmic; 99b52207efSChen Feng struct device *dev = &pdev->dev; 100b52207efSChen Feng struct device_node *np = dev->of_node; 101b52207efSChen Feng void __iomem *base; 102b52207efSChen Feng 103b52207efSChen Feng pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); 104b52207efSChen Feng if (!pmic) 105b52207efSChen Feng return -ENOMEM; 106b52207efSChen Feng pmic->dev = dev; 107b52207efSChen Feng 108b52207efSChen Feng pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 109b52207efSChen Feng base = devm_ioremap_resource(dev, pmic->res); 1107e28abdaSWei Yongjun if (IS_ERR(base)) 1117e28abdaSWei Yongjun return PTR_ERR(base); 112b52207efSChen Feng 113b52207efSChen Feng pmic->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, 114b52207efSChen Feng &hi655x_regmap_config); 115b52207efSChen Feng 116b52207efSChen Feng regmap_read(pmic->regmap, HI655X_BUS_ADDR(HI655X_VER_REG), &pmic->ver); 117b52207efSChen Feng if ((pmic->ver < PMU_VER_START) || (pmic->ver > PMU_VER_END)) { 118b52207efSChen Feng dev_warn(dev, "PMU version %d unsupported\n", pmic->ver); 119b52207efSChen Feng return -EINVAL; 120b52207efSChen Feng } 121b52207efSChen Feng 122b52207efSChen Feng hi655x_local_irq_clear(pmic->regmap); 123b52207efSChen Feng 124b52207efSChen Feng pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0); 125b52207efSChen Feng if (!gpio_is_valid(pmic->gpio)) { 126b52207efSChen Feng dev_err(dev, "Failed to get the pmic-gpios\n"); 127b52207efSChen Feng return -ENODEV; 128b52207efSChen Feng } 129b52207efSChen Feng 130b52207efSChen Feng ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, 131b52207efSChen Feng "hi655x_pmic_irq"); 132b52207efSChen Feng if (ret < 0) { 133b52207efSChen Feng dev_err(dev, "Failed to request gpio %d ret = %d\n", 134b52207efSChen Feng pmic->gpio, ret); 135b52207efSChen Feng return ret; 136b52207efSChen Feng } 137b52207efSChen Feng 138b52207efSChen Feng ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio), 139b52207efSChen Feng IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, 140b52207efSChen Feng &hi655x_irq_chip, &pmic->irq_data); 141b52207efSChen Feng if (ret) { 142b52207efSChen Feng dev_err(dev, "Failed to obtain 'hi655x_pmic_irq' %d\n", ret); 143b52207efSChen Feng return ret; 144b52207efSChen Feng } 145b52207efSChen Feng 146b52207efSChen Feng platform_set_drvdata(pdev, pmic); 147b52207efSChen Feng 148b52207efSChen Feng ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, hi655x_pmic_devs, 149eb10245fSJohn Stultz ARRAY_SIZE(hi655x_pmic_devs), NULL, 0, 150eb10245fSJohn Stultz regmap_irq_get_domain(pmic->irq_data)); 151b52207efSChen Feng if (ret) { 152b52207efSChen Feng dev_err(dev, "Failed to register device %d\n", ret); 153b52207efSChen Feng regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); 154b52207efSChen Feng return ret; 155b52207efSChen Feng } 156b52207efSChen Feng 157b52207efSChen Feng return 0; 158b52207efSChen Feng } 159b52207efSChen Feng 160b52207efSChen Feng static int hi655x_pmic_remove(struct platform_device *pdev) 161b52207efSChen Feng { 162b52207efSChen Feng struct hi655x_pmic *pmic = platform_get_drvdata(pdev); 163b52207efSChen Feng 164b52207efSChen Feng regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); 165b52207efSChen Feng mfd_remove_devices(&pdev->dev); 166b52207efSChen Feng return 0; 167b52207efSChen Feng } 168b52207efSChen Feng 169b52207efSChen Feng static const struct of_device_id hi655x_pmic_match[] = { 170b52207efSChen Feng { .compatible = "hisilicon,hi655x-pmic", }, 171b52207efSChen Feng {}, 172b52207efSChen Feng }; 173e45b6c80SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, hi655x_pmic_match); 174b52207efSChen Feng 175b52207efSChen Feng static struct platform_driver hi655x_pmic_driver = { 176b52207efSChen Feng .driver = { 177b52207efSChen Feng .name = "hi655x-pmic", 178b52207efSChen Feng .of_match_table = of_match_ptr(hi655x_pmic_match), 179b52207efSChen Feng }, 180b52207efSChen Feng .probe = hi655x_pmic_probe, 181b52207efSChen Feng .remove = hi655x_pmic_remove, 182b52207efSChen Feng }; 183b52207efSChen Feng module_platform_driver(hi655x_pmic_driver); 184b52207efSChen Feng 185b52207efSChen Feng MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); 186b52207efSChen Feng MODULE_DESCRIPTION("Hisilicon hi655x PMIC driver"); 187b52207efSChen Feng MODULE_LICENSE("GPL v2"); 188