brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 17323ae Raw
188 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2021 ROHM Semiconductors4 *5 * ROHM BD9576MUF and BD9573MUF PMIC driver6 */7 8#include <linux/i2c.h>9#include <linux/interrupt.h>10#include <linux/ioport.h>11#include <linux/irq.h>12#include <linux/mfd/core.h>13#include <linux/mfd/rohm-bd957x.h>14#include <linux/mfd/rohm-generic.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/regmap.h>18#include <linux/types.h>19 20enum {21	BD957X_REGULATOR_CELL,22	BD957X_WDT_CELL,23};24 25/*26 * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs.27 * These will be added to regulator resources only if IRQ information for the28 * PMIC is populated in device-tree.29 */30static const struct resource bd9576_regulator_irqs[] = {31	DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"),32	DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"),33	DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"),34};35 36static struct mfd_cell bd9573_mfd_cells[] = {37	[BD957X_REGULATOR_CELL]	= { .name = "bd9573-regulator", },38	[BD957X_WDT_CELL]	= { .name = "bd9576-wdt", },39};40 41static struct mfd_cell bd9576_mfd_cells[] = {42	[BD957X_REGULATOR_CELL]	= { .name = "bd9576-regulator", },43	[BD957X_WDT_CELL]	= { .name = "bd9576-wdt", },44};45 46static const struct regmap_range volatile_ranges[] = {47	regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT),48	regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT,49			 BD957X_REG_PMIC_INTERNAL_STAT),50	regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT),51	regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT),52	regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT),53};54 55static const struct regmap_access_table volatile_regs = {56	.yes_ranges = &volatile_ranges[0],57	.n_yes_ranges = ARRAY_SIZE(volatile_ranges),58};59 60static const struct regmap_config bd957x_regmap = {61	.reg_bits = 8,62	.val_bits = 8,63	.volatile_table = &volatile_regs,64	.max_register = BD957X_MAX_REGISTER,65	.cache_type = REGCACHE_MAPLE,66};67 68static const struct regmap_irq bd9576_irqs[] = {69	REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),70	REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),71	REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),72	REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP),73	REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD),74	REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD),75	REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP),76	REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),77};78 79static const struct regmap_irq_chip bd9576_irq_chip = {80	.name = "bd9576_irq",81	.irqs = &bd9576_irqs[0],82	.num_irqs = ARRAY_SIZE(bd9576_irqs),83	.status_base = BD957X_REG_INT_MAIN_STAT,84	.mask_base = BD957X_REG_INT_MAIN_MASK,85	.ack_base = BD957X_REG_INT_MAIN_STAT,86	.init_ack_masked = true,87	.num_regs = 1,88	.irq_reg_stride = 1,89};90 91static int bd957x_i2c_probe(struct i2c_client *i2c)92{93	int ret;94	struct regmap *regmap;95	struct mfd_cell *cells;96	int num_cells;97	unsigned long chip_type;98	struct irq_domain *domain;99	bool usable_irqs;100 101	chip_type = (unsigned long)of_device_get_match_data(&i2c->dev);102 103	switch (chip_type) {104	case ROHM_CHIP_TYPE_BD9576:105		cells = bd9576_mfd_cells;106		num_cells = ARRAY_SIZE(bd9576_mfd_cells);107		usable_irqs = !!i2c->irq;108		break;109	case ROHM_CHIP_TYPE_BD9573:110		cells = bd9573_mfd_cells;111		num_cells = ARRAY_SIZE(bd9573_mfd_cells);112		/*113		 * BD9573 only supports fatal IRQs which we can not handle114		 * because SoC is going to lose the power.115		 */116		usable_irqs = false;117		break;118	default:119		dev_err(&i2c->dev, "Unknown device type");120		return -EINVAL;121	}122 123	regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap);124	if (IS_ERR(regmap))125		return dev_err_probe(&i2c->dev, PTR_ERR(regmap),126				     "Failed to initialize Regmap\n");127 128	/*129	 * BD9576 behaves badly. It kepts IRQ line asserted for the whole130	 * duration of detected HW condition (like over temperature). So we131	 * don't require IRQ to be populated.132	 * If IRQ information is not given, then we mask all IRQs and do not133	 * provide IRQ resources to regulator driver - which then just omits134	 * the notifiers.135	 */136	if (usable_irqs) {137		struct regmap_irq_chip_data *irq_data;138		struct mfd_cell *regulators;139 140		regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL];141		regulators->resources = bd9576_regulator_irqs;142		regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs);143 144		ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,145					       IRQF_ONESHOT, 0,146					       &bd9576_irq_chip, &irq_data);147		if (ret)148			return dev_err_probe(&i2c->dev, ret,149					     "Failed to add IRQ chip\n");150 151		domain = regmap_irq_get_domain(irq_data);152	} else {153		ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK,154					 BD957X_MASK_INT_ALL,155					 BD957X_MASK_INT_ALL);156		if (ret)157			return ret;158		domain = NULL;159	}160 161	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells,162				   num_cells, NULL, 0, domain);163	if (ret)164		dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");165 166	return ret;167}168 169static const struct of_device_id bd957x_of_match[] = {170	{ .compatible = "rohm,bd9576", .data = (void *)ROHM_CHIP_TYPE_BD9576, },171	{ .compatible = "rohm,bd9573", .data = (void *)ROHM_CHIP_TYPE_BD9573, },172	{ },173};174MODULE_DEVICE_TABLE(of, bd957x_of_match);175 176static struct i2c_driver bd957x_drv = {177	.driver = {178		.name = "rohm-bd957x",179		.of_match_table = bd957x_of_match,180	},181	.probe = bd957x_i2c_probe,182};183module_i2c_driver(bd957x_drv);184 185MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");186MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver");187MODULE_LICENSE("GPL");188