|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * Core driver for TI TPS65090 PMIC family | 
|  | * | 
|  | * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved. | 
|  | * | 
|  | * Author: Venu Byravarasu <vbyravarasu@nvidia.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/irq.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/mutex.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/i2c.h> | 
|  | #include <linux/mfd/core.h> | 
|  | #include <linux/mfd/tps65090.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/err.h> | 
|  |  | 
|  | #define NUM_INT_REG 2 | 
|  |  | 
|  | #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE		1 | 
|  | #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE		2 | 
|  | #define TPS65090_INT1_MASK_BAT_STATUS_CHANGE		3 | 
|  | #define TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE	4 | 
|  | #define TPS65090_INT1_MASK_CHARGING_COMPLETE		5 | 
|  | #define TPS65090_INT1_MASK_OVERLOAD_DCDC1		6 | 
|  | #define TPS65090_INT1_MASK_OVERLOAD_DCDC2		7 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_DCDC3		0 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET1		1 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET2		2 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET3		3 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET4		4 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET5		5 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET6		6 | 
|  | #define TPS65090_INT2_MASK_OVERLOAD_FET7		7 | 
|  |  | 
|  | static const struct resource charger_resources[] = { | 
|  | { | 
|  | .start  = TPS65090_IRQ_VAC_STATUS_CHANGE, | 
|  | .end    = TPS65090_IRQ_VAC_STATUS_CHANGE, | 
|  | .flags  = IORESOURCE_IRQ, | 
|  | } | 
|  | }; | 
|  |  | 
|  | enum tps65090_cells { | 
|  | PMIC = 0, | 
|  | CHARGER = 1, | 
|  | }; | 
|  |  | 
|  | static struct mfd_cell tps65090s[] = { | 
|  | [PMIC] = { | 
|  | .name = "tps65090-pmic", | 
|  | }, | 
|  | [CHARGER] = { | 
|  | .name = "tps65090-charger", | 
|  | .num_resources = ARRAY_SIZE(charger_resources), | 
|  | .resources = &charger_resources[0], | 
|  | .of_compatible = "ti,tps65090-charger", | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const struct regmap_irq tps65090_irqs[] = { | 
|  | /* INT1 IRQs*/ | 
|  | [TPS65090_IRQ_VAC_STATUS_CHANGE] = { | 
|  | .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, | 
|  | }, | 
|  | [TPS65090_IRQ_VSYS_STATUS_CHANGE] = { | 
|  | .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, | 
|  | }, | 
|  | [TPS65090_IRQ_BAT_STATUS_CHANGE] = { | 
|  | .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, | 
|  | }, | 
|  | [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = { | 
|  | .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, | 
|  | }, | 
|  | [TPS65090_IRQ_CHARGING_COMPLETE] = { | 
|  | .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_DCDC1] = { | 
|  | .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_DCDC2] = { | 
|  | .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, | 
|  | }, | 
|  | /* INT2 IRQs*/ | 
|  | [TPS65090_IRQ_OVERLOAD_DCDC3] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET1] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET2] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET3] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET4] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET5] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET6] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, | 
|  | }, | 
|  | [TPS65090_IRQ_OVERLOAD_FET7] = { | 
|  | .reg_offset = 1, | 
|  | .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct regmap_irq_chip tps65090_irq_chip = { | 
|  | .name = "tps65090", | 
|  | .irqs = tps65090_irqs, | 
|  | .num_irqs = ARRAY_SIZE(tps65090_irqs), | 
|  | .num_regs = NUM_INT_REG, | 
|  | .status_base = TPS65090_REG_INTR_STS, | 
|  | .mask_base = TPS65090_REG_INTR_MASK, | 
|  | .mask_invert = true, | 
|  | }; | 
|  |  | 
|  | static bool is_volatile_reg(struct device *dev, unsigned int reg) | 
|  | { | 
|  | /* Nearly all registers have status bits mixed in, except a few */ | 
|  | switch (reg) { | 
|  | case TPS65090_REG_INTR_MASK: | 
|  | case TPS65090_REG_INTR_MASK2: | 
|  | case TPS65090_REG_CG_CTRL0: | 
|  | case TPS65090_REG_CG_CTRL1: | 
|  | case TPS65090_REG_CG_CTRL2: | 
|  | case TPS65090_REG_CG_CTRL3: | 
|  | case TPS65090_REG_CG_CTRL4: | 
|  | case TPS65090_REG_CG_CTRL5: | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static const struct regmap_config tps65090_regmap_config = { | 
|  | .reg_bits = 8, | 
|  | .val_bits = 8, | 
|  | .max_register = TPS65090_MAX_REG, | 
|  | .num_reg_defaults_raw = TPS65090_NUM_REGS, | 
|  | .cache_type = REGCACHE_RBTREE, | 
|  | .volatile_reg = is_volatile_reg, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_OF | 
|  | static const struct of_device_id tps65090_of_match[] = { | 
|  | { .compatible = "ti,tps65090",}, | 
|  | {}, | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | static int tps65090_i2c_probe(struct i2c_client *client, | 
|  | const struct i2c_device_id *id) | 
|  | { | 
|  | struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev); | 
|  | int irq_base = 0; | 
|  | struct tps65090 *tps65090; | 
|  | int ret; | 
|  |  | 
|  | if (!pdata && !client->dev.of_node) { | 
|  | dev_err(&client->dev, | 
|  | "tps65090 requires platform data or of_node\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (pdata) | 
|  | irq_base = pdata->irq_base; | 
|  |  | 
|  | tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); | 
|  | if (!tps65090) | 
|  | return -ENOMEM; | 
|  |  | 
|  | tps65090->dev = &client->dev; | 
|  | i2c_set_clientdata(client, tps65090); | 
|  |  | 
|  | tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config); | 
|  | if (IS_ERR(tps65090->rmap)) { | 
|  | ret = PTR_ERR(tps65090->rmap); | 
|  | dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (client->irq) { | 
|  | ret = regmap_add_irq_chip(tps65090->rmap, client->irq, | 
|  | IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, | 
|  | &tps65090_irq_chip, &tps65090->irq_data); | 
|  | if (ret) { | 
|  | dev_err(&client->dev, | 
|  | "IRQ init failed with err: %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  | } else { | 
|  | /* Don't tell children they have an IRQ that'll never fire */ | 
|  | tps65090s[CHARGER].num_resources = 0; | 
|  | } | 
|  |  | 
|  | ret = mfd_add_devices(tps65090->dev, -1, tps65090s, | 
|  | ARRAY_SIZE(tps65090s), NULL, | 
|  | 0, regmap_irq_get_domain(tps65090->irq_data)); | 
|  | if (ret) { | 
|  | dev_err(&client->dev, "add mfd devices failed with err: %d\n", | 
|  | ret); | 
|  | goto err_irq_exit; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_irq_exit: | 
|  | if (client->irq) | 
|  | regmap_del_irq_chip(client->irq, tps65090->irq_data); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | static const struct i2c_device_id tps65090_id_table[] = { | 
|  | { "tps65090", 0 }, | 
|  | { }, | 
|  | }; | 
|  |  | 
|  | static struct i2c_driver tps65090_driver = { | 
|  | .driver	= { | 
|  | .name	= "tps65090", | 
|  | .suppress_bind_attrs = true, | 
|  | .of_match_table = of_match_ptr(tps65090_of_match), | 
|  | }, | 
|  | .probe		= tps65090_i2c_probe, | 
|  | .id_table	= tps65090_id_table, | 
|  | }; | 
|  |  | 
|  | static int __init tps65090_init(void) | 
|  | { | 
|  | return i2c_add_driver(&tps65090_driver); | 
|  | } | 
|  | subsys_initcall(tps65090_init); |