forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.2 KiB
249 lines
6.2 KiB
// 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 <[email protected]> |
|
*/ |
|
|
|
#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);
|
|
|