280 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2021, The Linux Foundation. All rights reserved.4 */5 6#include <linux/bitops.h>7#include <linux/gpio/consumer.h>8#include <linux/i2c.h>9#include <linux/interrupt.h>10#include <linux/ioport.h>11#include <linux/irq.h>12#include <linux/irqdomain.h>13#include <linux/mfd/core.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/of_platform.h>17#include <linux/pinctrl/consumer.h>18#include <linux/regmap.h>19#include <linux/slab.h>20 21#define I2C_INTR_STATUS_BASE 0x055022#define INT_RT_STS_OFFSET 0x1023#define INT_SET_TYPE_OFFSET 0x1124#define INT_POL_HIGH_OFFSET 0x1225#define INT_POL_LOW_OFFSET 0x1326#define INT_LATCHED_CLR_OFFSET 0x1427#define INT_EN_SET_OFFSET 0x1528#define INT_EN_CLR_OFFSET 0x1629#define INT_LATCHED_STS_OFFSET 0x1830 31enum {32 PM8008_MISC,33 PM8008_TEMP_ALARM,34 PM8008_GPIO1,35 PM8008_GPIO2,36 PM8008_NUM_PERIPHS,37};38 39#define PM8008_PERIPH_0_BASE 0x90040#define PM8008_PERIPH_1_BASE 0x240041#define PM8008_PERIPH_2_BASE 0xc00042#define PM8008_PERIPH_3_BASE 0xc10043 44#define PM8008_TEMP_ALARM_ADDR PM8008_PERIPH_1_BASE45#define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE46#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE47 48/* PM8008 IRQ numbers */49#define PM8008_IRQ_MISC_UVLO 050#define PM8008_IRQ_MISC_OVLO 151#define PM8008_IRQ_MISC_OTST2 252#define PM8008_IRQ_MISC_OTST3 353#define PM8008_IRQ_MISC_LDO_OCP 454#define PM8008_IRQ_TEMP_ALARM 555#define PM8008_IRQ_GPIO1 656#define PM8008_IRQ_GPIO2 757 58enum {59 SET_TYPE_INDEX,60 POLARITY_HI_INDEX,61 POLARITY_LO_INDEX,62};63 64static const unsigned int pm8008_config_regs[] = {65 INT_SET_TYPE_OFFSET,66 INT_POL_HIGH_OFFSET,67 INT_POL_LOW_OFFSET,68};69 70#define _IRQ(_irq, _off, _mask, _types) \71 [_irq] = { \72 .reg_offset = (_off), \73 .mask = (_mask), \74 .type = { \75 .type_reg_offset = (_off), \76 .types_supported = (_types), \77 }, \78 }79 80static const struct regmap_irq pm8008_irqs[] = {81 _IRQ(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0), IRQ_TYPE_EDGE_RISING),82 _IRQ(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1), IRQ_TYPE_EDGE_RISING),83 _IRQ(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2), IRQ_TYPE_EDGE_RISING),84 _IRQ(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3), IRQ_TYPE_EDGE_RISING),85 _IRQ(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4), IRQ_TYPE_EDGE_RISING),86 _IRQ(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM,BIT(0), IRQ_TYPE_SENSE_MASK),87 _IRQ(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0), IRQ_TYPE_SENSE_MASK),88 _IRQ(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0), IRQ_TYPE_SENSE_MASK),89};90 91static const unsigned int pm8008_periph_base[] = {92 PM8008_PERIPH_0_BASE,93 PM8008_PERIPH_1_BASE,94 PM8008_PERIPH_2_BASE,95 PM8008_PERIPH_3_BASE,96};97 98static unsigned int pm8008_get_irq_reg(struct regmap_irq_chip_data *data,99 unsigned int base, int index)100{101 /* Simple linear addressing for the main status register */102 if (base == I2C_INTR_STATUS_BASE)103 return base + index;104 105 return pm8008_periph_base[index] + base;106}107 108static int pm8008_set_type_config(unsigned int **buf, unsigned int type,109 const struct regmap_irq *irq_data, int idx,110 void *irq_drv_data)111{112 switch (type) {113 case IRQ_TYPE_EDGE_FALLING:114 case IRQ_TYPE_LEVEL_LOW:115 buf[POLARITY_HI_INDEX][idx] &= ~irq_data->mask;116 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;117 break;118 119 case IRQ_TYPE_EDGE_RISING:120 case IRQ_TYPE_LEVEL_HIGH:121 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;122 buf[POLARITY_LO_INDEX][idx] &= ~irq_data->mask;123 break;124 125 case IRQ_TYPE_EDGE_BOTH:126 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;127 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;128 break;129 130 default:131 return -EINVAL;132 }133 134 if (type & IRQ_TYPE_EDGE_BOTH)135 buf[SET_TYPE_INDEX][idx] |= irq_data->mask;136 else137 buf[SET_TYPE_INDEX][idx] &= ~irq_data->mask;138 139 return 0;140}141 142static const struct regmap_irq_chip pm8008_irq_chip = {143 .name = "pm8008",144 .main_status = I2C_INTR_STATUS_BASE,145 .num_main_regs = 1,146 .irqs = pm8008_irqs,147 .num_irqs = ARRAY_SIZE(pm8008_irqs),148 .num_regs = PM8008_NUM_PERIPHS,149 .status_base = INT_LATCHED_STS_OFFSET,150 .mask_base = INT_EN_CLR_OFFSET,151 .unmask_base = INT_EN_SET_OFFSET,152 .mask_unmask_non_inverted = true,153 .ack_base = INT_LATCHED_CLR_OFFSET,154 .config_base = pm8008_config_regs,155 .num_config_bases = ARRAY_SIZE(pm8008_config_regs),156 .num_config_regs = PM8008_NUM_PERIPHS,157 .set_type_config = pm8008_set_type_config,158 .get_irq_reg = pm8008_get_irq_reg,159};160 161static const struct regmap_config qcom_mfd_regmap_cfg = {162 .name = "primary",163 .reg_bits = 16,164 .val_bits = 8,165 .max_register = 0xffff,166};167 168static const struct regmap_config pm8008_regmap_cfg_2 = {169 .name = "secondary",170 .reg_bits = 16,171 .val_bits = 8,172 .max_register = 0xffff,173};174 175static const struct resource pm8008_temp_res[] = {176 DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100),177 DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM),178};179 180static const struct mfd_cell pm8008_cells[] = {181 MFD_CELL_NAME("pm8008-regulator"),182 MFD_CELL_RES("qpnp-temp-alarm", pm8008_temp_res),183 MFD_CELL_NAME("pm8008-gpio"),184};185 186static void devm_irq_domain_fwnode_release(void *data)187{188 struct fwnode_handle *fwnode = data;189 190 irq_domain_free_fwnode(fwnode);191}192 193static int pm8008_probe(struct i2c_client *client)194{195 struct regmap_irq_chip_data *irq_data;196 struct device *dev = &client->dev;197 struct regmap *regmap, *regmap2;198 struct fwnode_handle *fwnode;199 struct i2c_client *dummy;200 struct gpio_desc *reset;201 char *name;202 int ret;203 204 dummy = devm_i2c_new_dummy_device(dev, client->adapter, client->addr + 1);205 if (IS_ERR(dummy)) {206 ret = PTR_ERR(dummy);207 dev_err(dev, "failed to claim second address: %d\n", ret);208 return ret;209 }210 211 regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg);212 if (IS_ERR(regmap2))213 return PTR_ERR(regmap2);214 215 ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2);216 if (ret)217 return ret;218 219 /* Default regmap must be attached last. */220 regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);221 if (IS_ERR(regmap))222 return PTR_ERR(regmap);223 224 reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);225 if (IS_ERR(reset))226 return PTR_ERR(reset);227 228 /*229 * The PMIC does not appear to require a post-reset delay, but wait230 * for a millisecond for now anyway.231 */232 usleep_range(1000, 2000);233 234 name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node);235 if (!name)236 return -ENOMEM;237 238 name = strreplace(name, '/', ':');239 240 fwnode = irq_domain_alloc_named_fwnode(name);241 if (!fwnode)242 return -ENOMEM;243 244 ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode);245 if (ret)246 return ret;247 248 ret = devm_regmap_add_irq_chip_fwnode(dev, fwnode, regmap, client->irq,249 IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);250 if (ret) {251 dev_err(dev, "failed to add IRQ chip: %d\n", ret);252 return ret;253 }254 255 /* Needed by GPIO driver. */256 dev_set_drvdata(dev, regmap_irq_get_domain(irq_data));257 258 return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, pm8008_cells,259 ARRAY_SIZE(pm8008_cells), NULL, 0,260 regmap_irq_get_domain(irq_data));261}262 263static const struct of_device_id pm8008_match[] = {264 { .compatible = "qcom,pm8008", },265 { },266};267MODULE_DEVICE_TABLE(of, pm8008_match);268 269static struct i2c_driver pm8008_mfd_driver = {270 .driver = {271 .name = "pm8008",272 .of_match_table = pm8008_match,273 },274 .probe = pm8008_probe,275};276module_i2c_driver(pm8008_mfd_driver);277 278MODULE_DESCRIPTION("QCOM PM8008 Power Management IC driver");279MODULE_LICENSE("GPL v2");280