brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 60204cc Raw
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