kernel-fxtec-pro1x/drivers/mfd/tps65090.c
Venu Byravarasu 3c33be06f9 mfd: Add support for TPS65090
TPS65090 is a Texas Instrument PMIC. It contains 3 Step-Down converters, 2
always on LDO's and  7 current limited load switches.

Signed-off-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2012-03-20 16:56:33 +01:00

387 lines
9.3 KiB
C

/*
* Core driver for TI TPS65090 PMIC family
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.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/regmap.h>
#include <linux/err.h>
#define NUM_INT_REG 2
#define TOTAL_NUM_REG 0x18
/* interrupt status registers */
#define TPS65090_INT_STS 0x0
#define TPS65090_INT_STS2 0x1
/* interrupt mask registers */
#define TPS65090_INT_MSK 0x2
#define TPS65090_INT_MSK2 0x3
struct tps65090_irq_data {
u8 mask_reg;
u8 mask_pos;
};
#define TPS65090_IRQ(_reg, _mask_pos) \
{ \
.mask_reg = (_reg), \
.mask_pos = (_mask_pos), \
}
static const struct tps65090_irq_data tps65090_irqs[] = {
[0] = TPS65090_IRQ(0, 0),
[1] = TPS65090_IRQ(0, 1),
[2] = TPS65090_IRQ(0, 2),
[3] = TPS65090_IRQ(0, 3),
[4] = TPS65090_IRQ(0, 4),
[5] = TPS65090_IRQ(0, 5),
[6] = TPS65090_IRQ(0, 6),
[7] = TPS65090_IRQ(0, 7),
[8] = TPS65090_IRQ(1, 0),
[9] = TPS65090_IRQ(1, 1),
[10] = TPS65090_IRQ(1, 2),
[11] = TPS65090_IRQ(1, 3),
[12] = TPS65090_IRQ(1, 4),
[13] = TPS65090_IRQ(1, 5),
[14] = TPS65090_IRQ(1, 6),
[15] = TPS65090_IRQ(1, 7),
};
static struct mfd_cell tps65090s[] = {
{
.name = "tps65910-pmic",
},
{
.name = "tps65910-regulator",
},
};
struct tps65090 {
struct mutex lock;
struct device *dev;
struct i2c_client *client;
struct regmap *rmap;
struct irq_chip irq_chip;
struct mutex irq_lock;
int irq_base;
unsigned int id;
};
int tps65090_write(struct device *dev, int reg, uint8_t val)
{
struct tps65090 *tps = dev_get_drvdata(dev);
return regmap_write(tps->rmap, reg, val);
}
EXPORT_SYMBOL_GPL(tps65090_write);
int tps65090_read(struct device *dev, int reg, uint8_t *val)
{
struct tps65090 *tps = dev_get_drvdata(dev);
unsigned int temp_val;
int ret;
ret = regmap_read(tps->rmap, reg, &temp_val);
if (!ret)
*val = temp_val;
return ret;
}
EXPORT_SYMBOL_GPL(tps65090_read);
int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num)
{
struct tps65090 *tps = dev_get_drvdata(dev);
return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u);
}
EXPORT_SYMBOL_GPL(tps65090_set_bits);
int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num)
{
struct tps65090 *tps = dev_get_drvdata(dev);
return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u);
}
EXPORT_SYMBOL_GPL(tps65090_clr_bits);
static void tps65090_irq_lock(struct irq_data *data)
{
struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
mutex_lock(&tps65090->irq_lock);
}
static void tps65090_irq_mask(struct irq_data *irq_data)
{
struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
unsigned int __irq = irq_data->hwirq;
const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
data->mask_pos);
}
static void tps65090_irq_unmask(struct irq_data *irq_data)
{
struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
unsigned int __irq = irq_data->irq - tps65090->irq_base;
const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
data->mask_pos);
}
static void tps65090_irq_sync_unlock(struct irq_data *data)
{
struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
mutex_unlock(&tps65090->irq_lock);
}
static irqreturn_t tps65090_irq(int irq, void *data)
{
struct tps65090 *tps65090 = data;
int ret = 0;
u8 status, mask;
unsigned long int acks = 0;
int i;
for (i = 0; i < NUM_INT_REG; i++) {
ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask);
if (ret < 0) {
dev_err(tps65090->dev,
"failed to read mask reg [addr:%d]\n",
TPS65090_INT_MSK + i);
return IRQ_NONE;
}
ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i,
&status);
if (ret < 0) {
dev_err(tps65090->dev,
"failed to read status reg [addr:%d]\n",
TPS65090_INT_STS + i);
return IRQ_NONE;
}
if (status) {
/* Ack only those interrupts which are not masked */
status &= (~mask);
ret = tps65090_write(tps65090->dev,
TPS65090_INT_STS + i, status);
if (ret < 0) {
dev_err(tps65090->dev,
"failed to write interrupt status\n");
return IRQ_NONE;
}
acks |= (status << (i * 8));
}
}
for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs))
handle_nested_irq(tps65090->irq_base + i);
return acks ? IRQ_HANDLED : IRQ_NONE;
}
static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
int irq_base)
{
int i, ret;
if (!irq_base) {
dev_err(tps65090->dev, "IRQ base not set\n");
return -EINVAL;
}
mutex_init(&tps65090->irq_lock);
for (i = 0; i < NUM_INT_REG; i++)
tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF);
for (i = 0; i < NUM_INT_REG; i++)
tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff);
tps65090->irq_base = irq_base;
tps65090->irq_chip.name = "tps65090";
tps65090->irq_chip.irq_mask = tps65090_irq_mask;
tps65090->irq_chip.irq_unmask = tps65090_irq_unmask;
tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock;
tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock;
for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) {
int __irq = i + tps65090->irq_base;
irq_set_chip_data(__irq, tps65090);
irq_set_chip_and_handler(__irq, &tps65090->irq_chip,
handle_simple_irq);
irq_set_nested_thread(__irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(__irq, IRQF_VALID);
#endif
}
ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT,
"tps65090", tps65090);
if (!ret) {
device_init_wakeup(tps65090->dev, 1);
enable_irq_wake(irq);
}
return ret;
}
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS))
return true;
else
return false;
}
static const struct regmap_config tps65090_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TOTAL_NUM_REG,
.num_reg_defaults_raw = TOTAL_NUM_REG,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = is_volatile_reg,
};
static int __devinit tps65090_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tps65090_platform_data *pdata = client->dev.platform_data;
struct tps65090 *tps65090;
int ret;
if (!pdata) {
dev_err(&client->dev, "tps65090 requires platform data\n");
return -EINVAL;
}
tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090),
GFP_KERNEL);
if (tps65090 == NULL)
return -ENOMEM;
tps65090->client = client;
tps65090->dev = &client->dev;
i2c_set_clientdata(client, tps65090);
mutex_init(&tps65090->lock);
if (client->irq) {
ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base);
if (ret) {
dev_err(&client->dev, "IRQ init failed with err: %d\n",
ret);
goto err_exit;
}
}
tps65090->rmap = regmap_init_i2c(tps65090->client,
&tps65090_regmap_config);
if (IS_ERR(tps65090->rmap)) {
dev_err(&client->dev, "regmap_init failed with err: %ld\n",
PTR_ERR(tps65090->rmap));
goto err_irq_exit;
};
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
ARRAY_SIZE(tps65090s), NULL, 0);
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
goto err_regmap_exit;
}
return 0;
err_regmap_exit:
regmap_exit(tps65090->rmap);
err_irq_exit:
if (client->irq)
free_irq(client->irq, tps65090);
err_exit:
return ret;
}
static int __devexit tps65090_i2c_remove(struct i2c_client *client)
{
struct tps65090 *tps65090 = i2c_get_clientdata(client);
mfd_remove_devices(tps65090->dev);
regmap_exit(tps65090->rmap);
if (client->irq)
free_irq(client->irq, tps65090);
return 0;
}
#ifdef CONFIG_PM
static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
{
if (client->irq)
disable_irq(client->irq);
return 0;
}
static int tps65090_i2c_resume(struct i2c_client *client)
{
if (client->irq)
enable_irq(client->irq);
return 0;
}
#endif
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, tps65090_id_table);
static struct i2c_driver tps65090_driver = {
.driver = {
.name = "tps65090",
.owner = THIS_MODULE,
},
.probe = tps65090_i2c_probe,
.remove = __devexit_p(tps65090_i2c_remove),
#ifdef CONFIG_PM
.suspend = tps65090_i2c_suspend,
.resume = tps65090_i2c_resume,
#endif
.id_table = tps65090_id_table,
};
static int __init tps65090_init(void)
{
return i2c_add_driver(&tps65090_driver);
}
subsys_initcall(tps65090_init);
static void __exit tps65090_exit(void)
{
i2c_del_driver(&tps65090_driver);
}
module_exit(tps65090_exit);
MODULE_DESCRIPTION("TPS65090 core driver");
MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
MODULE_LICENSE("GPL v2");