Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  4 */
  5
  6#include <linux/bitops.h>
  7#include <linux/i2c.h>
  8#include <linux/interrupt.h>
  9#include <linux/irq.h>
 10#include <linux/irqdomain.h>
 11#include <linux/module.h>
 12#include <linux/of_device.h>
 13#include <linux/of_platform.h>
 14#include <linux/pinctrl/consumer.h>
 15#include <linux/regmap.h>
 16#include <linux/slab.h>
 17
 18#include <dt-bindings/mfd/qcom-pm8008.h>
 19
 20#define I2C_INTR_STATUS_BASE		0x0550
 21#define INT_RT_STS_OFFSET		0x10
 22#define INT_SET_TYPE_OFFSET		0x11
 23#define INT_POL_HIGH_OFFSET		0x12
 24#define INT_POL_LOW_OFFSET		0x13
 25#define INT_LATCHED_CLR_OFFSET		0x14
 26#define INT_EN_SET_OFFSET		0x15
 27#define INT_EN_CLR_OFFSET		0x16
 28#define INT_LATCHED_STS_OFFSET		0x18
 29
 30enum {
 31	PM8008_MISC,
 32	PM8008_TEMP_ALARM,
 33	PM8008_GPIO1,
 34	PM8008_GPIO2,
 35	PM8008_NUM_PERIPHS,
 36};
 37
 38#define PM8008_PERIPH_0_BASE	0x900
 39#define PM8008_PERIPH_1_BASE	0x2400
 40#define PM8008_PERIPH_2_BASE	0xC000
 41#define PM8008_PERIPH_3_BASE	0xC100
 42
 43#define PM8008_TEMP_ALARM_ADDR	PM8008_PERIPH_1_BASE
 44#define PM8008_GPIO1_ADDR	PM8008_PERIPH_2_BASE
 45#define PM8008_GPIO2_ADDR	PM8008_PERIPH_3_BASE
 46
 47#define PM8008_STATUS_BASE	(PM8008_PERIPH_0_BASE | INT_LATCHED_STS_OFFSET)
 48#define PM8008_MASK_BASE	(PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET)
 49#define PM8008_UNMASK_BASE	(PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET)
 50#define PM8008_TYPE_BASE	(PM8008_PERIPH_0_BASE | INT_SET_TYPE_OFFSET)
 51#define PM8008_ACK_BASE		(PM8008_PERIPH_0_BASE | INT_LATCHED_CLR_OFFSET)
 52#define PM8008_POLARITY_HI_BASE	(PM8008_PERIPH_0_BASE | INT_POL_HIGH_OFFSET)
 53#define PM8008_POLARITY_LO_BASE	(PM8008_PERIPH_0_BASE | INT_POL_LOW_OFFSET)
 54
 55#define PM8008_PERIPH_OFFSET(paddr)	(paddr - PM8008_PERIPH_0_BASE)
 56
 57struct pm8008_data {
 58	struct device *dev;
 59	struct regmap *regmap;
 60	int irq;
 61	struct regmap_irq_chip_data *irq_data;
 62};
 63
 64static unsigned int p0_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_0_BASE)};
 65static unsigned int p1_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_1_BASE)};
 66static unsigned int p2_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_2_BASE)};
 67static unsigned int p3_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_3_BASE)};
 68
 69static struct regmap_irq_sub_irq_map pm8008_sub_reg_offsets[] = {
 70	REGMAP_IRQ_MAIN_REG_OFFSET(p0_offs),
 71	REGMAP_IRQ_MAIN_REG_OFFSET(p1_offs),
 72	REGMAP_IRQ_MAIN_REG_OFFSET(p2_offs),
 73	REGMAP_IRQ_MAIN_REG_OFFSET(p3_offs),
 74};
 75
 76static unsigned int pm8008_virt_regs[] = {
 77	PM8008_POLARITY_HI_BASE,
 78	PM8008_POLARITY_LO_BASE,
 79};
 80
 81enum {
 82	POLARITY_HI_INDEX,
 83	POLARITY_LO_INDEX,
 84	PM8008_NUM_VIRT_REGS,
 85};
 86
 87static struct regmap_irq pm8008_irqs[] = {
 88	REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO,	PM8008_MISC,	BIT(0)),
 89	REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO,	PM8008_MISC,	BIT(1)),
 90	REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2,	PM8008_MISC,	BIT(2)),
 91	REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3,	PM8008_MISC,	BIT(3)),
 92	REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP,	PM8008_MISC,	BIT(4)),
 93	REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM,	PM8008_TEMP_ALARM, BIT(0)),
 94	REGMAP_IRQ_REG(PM8008_IRQ_GPIO1,	PM8008_GPIO1,	BIT(0)),
 95	REGMAP_IRQ_REG(PM8008_IRQ_GPIO2,	PM8008_GPIO2,	BIT(0)),
 96};
 97
 98static int pm8008_set_type_virt(unsigned int **virt_buf,
 99				      unsigned int type, unsigned long hwirq,
100				      int reg)
101{
102	switch (type) {
103	case IRQ_TYPE_EDGE_FALLING:
104	case IRQ_TYPE_LEVEL_LOW:
105		virt_buf[POLARITY_HI_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
106		virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
107		break;
108
109	case IRQ_TYPE_EDGE_RISING:
110	case IRQ_TYPE_LEVEL_HIGH:
111		virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
112		virt_buf[POLARITY_LO_INDEX][reg] &= ~pm8008_irqs[hwirq].mask;
113		break;
114
115	case IRQ_TYPE_EDGE_BOTH:
116		virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask;
117		virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask;
118		break;
119
120	default:
121		return -EINVAL;
122	}
123
124	return 0;
125}
126
127static struct regmap_irq_chip pm8008_irq_chip = {
128	.name			= "pm8008_irq",
129	.main_status		= I2C_INTR_STATUS_BASE,
130	.num_main_regs		= 1,
131	.num_virt_regs		= PM8008_NUM_VIRT_REGS,
132	.irqs			= pm8008_irqs,
133	.num_irqs		= ARRAY_SIZE(pm8008_irqs),
134	.num_regs		= PM8008_NUM_PERIPHS,
135	.not_fixed_stride	= true,
136	.sub_reg_offsets	= pm8008_sub_reg_offsets,
137	.set_type_virt		= pm8008_set_type_virt,
138	.status_base		= PM8008_STATUS_BASE,
139	.mask_base		= PM8008_MASK_BASE,
140	.unmask_base		= PM8008_UNMASK_BASE,
141	.type_base		= PM8008_TYPE_BASE,
142	.ack_base		= PM8008_ACK_BASE,
143	.virt_reg_base		= pm8008_virt_regs,
144	.num_type_reg		= PM8008_NUM_PERIPHS,
145};
146
147static struct regmap_config qcom_mfd_regmap_cfg = {
148	.reg_bits	= 16,
149	.val_bits	= 8,
150	.max_register	= 0xFFFF,
151};
152
153static int pm8008_init(struct pm8008_data *chip)
154{
155	int rc;
156
157	/*
158	 * Set TEMP_ALARM peripheral's TYPE so that the regmap-irq framework
159	 * reads this as the default value instead of zero, the HW default.
160	 * This is required to enable the writing of TYPE registers in
161	 * regmap_irq_sync_unlock().
162	 */
163	rc = regmap_write(chip->regmap,
164			 (PM8008_TEMP_ALARM_ADDR | INT_SET_TYPE_OFFSET),
165			 BIT(0));
166	if (rc)
167		return rc;
168
169	/* Do the same for GPIO1 and GPIO2 peripherals */
170	rc = regmap_write(chip->regmap,
171			 (PM8008_GPIO1_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
172	if (rc)
173		return rc;
174
175	rc = regmap_write(chip->regmap,
176			 (PM8008_GPIO2_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
177
178	return rc;
179}
180
181static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
182					int client_irq)
183{
184	int rc, i;
185	struct regmap_irq_type *type;
186	struct regmap_irq_chip_data *irq_data;
187
188	rc = pm8008_init(chip);
189	if (rc) {
190		dev_err(chip->dev, "Init failed: %d\n", rc);
191		return rc;
192	}
193
194	for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
195		type = &pm8008_irqs[i].type;
196
197		type->type_reg_offset	  = pm8008_irqs[i].reg_offset;
198		type->type_rising_val	  = pm8008_irqs[i].mask;
199		type->type_falling_val	  = pm8008_irqs[i].mask;
200		type->type_level_high_val = 0;
201		type->type_level_low_val  = 0;
202
203		if (type->type_reg_offset == PM8008_MISC)
204			type->types_supported = IRQ_TYPE_EDGE_RISING;
205		else
206			type->types_supported = (IRQ_TYPE_EDGE_BOTH |
207				IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
208	}
209
210	rc = devm_regmap_add_irq_chip(chip->dev, chip->regmap, client_irq,
211			IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
212	if (rc) {
213		dev_err(chip->dev, "Failed to add IRQ chip: %d\n", rc);
214		return rc;
215	}
216
217	return 0;
218}
219
220static int pm8008_probe(struct i2c_client *client)
221{
222	int rc;
223	struct pm8008_data *chip;
224
225	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
226	if (!chip)
227		return -ENOMEM;
228
229	chip->dev = &client->dev;
230	chip->regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
231	if (!chip->regmap)
232		return -ENODEV;
233
234	i2c_set_clientdata(client, chip);
235
236	if (of_property_read_bool(chip->dev->of_node, "interrupt-controller")) {
237		rc = pm8008_probe_irq_peripherals(chip, client->irq);
238		if (rc)
239			dev_err(chip->dev, "Failed to probe irq periphs: %d\n", rc);
240	}
241
242	return devm_of_platform_populate(chip->dev);
243}
244
245static const struct of_device_id pm8008_match[] = {
246	{ .compatible = "qcom,pm8008", },
247	{ },
248};
249
250static struct i2c_driver pm8008_mfd_driver = {
251	.driver = {
252		.name = "pm8008",
253		.of_match_table = pm8008_match,
254	},
255	.probe_new = pm8008_probe,
256};
257module_i2c_driver(pm8008_mfd_driver);
258
259MODULE_LICENSE("GPL v2");
260MODULE_ALIAS("i2c:qcom-pm8008");