Skip to content

Commit

Permalink
mfd: Add support for twl6030 irq framework
Browse files Browse the repository at this point in the history
This patch adds support for phoenix interrupt framework. New iInterrupt
status register A, B, C are introduced in Phoenix and are cleared on write.
Due to the differences in interrupt handling with respect to TWL4030,
twl6030-irq.c is created for TWL6030 PMIC

Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Balaji T K <balajitk@ti.com>
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Balaji T K authored and Samuel Ortiz committed Dec 13, 2009
1 parent c4aa6f3 commit e8deb28
Show file tree
Hide file tree
Showing 7 changed files with 490 additions and 21 deletions.
16 changes: 15 additions & 1 deletion arch/arm/plat-omap/include/plat/irqs.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,22 @@
#endif
#define TWL4030_GPIO_IRQ_END (TWL4030_GPIO_IRQ_BASE + TWL4030_GPIO_NR_IRQS)

#define TWL6030_IRQ_BASE (OMAP_FPGA_IRQ_END)
#ifdef CONFIG_TWL4030_CORE
#define TWL6030_BASE_NR_IRQS 20
#else
#define TWL6030_BASE_NR_IRQS 0
#endif
#define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS)

/* Total number of interrupts depends on the enabled blocks above */
#define NR_IRQS TWL4030_GPIO_IRQ_END
#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END)
#define TWL_IRQ_END TWL4030_GPIO_IRQ_END
#else
#define TWL_IRQ_END TWL6030_IRQ_END
#endif

#define NR_IRQS TWL_IRQ_END

#define OMAP_IRQ_BIT(irq) (1 << ((irq) % 32))

Expand Down
4 changes: 2 additions & 2 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ config MENELAUS
cell phones and PDAs.

config TWL4030_CORE
bool "Texas Instruments TWL4030/TPS659x0 Support"
bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y && GENERIC_HARDIRQS
help
Say yes here if you have TWL4030 family chip on your board.
Say yes here if you have TWL4030 / TWL6030 family chip on your board.
This core driver provides register access and IRQ handling
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
Expand Down
2 changes: 1 addition & 1 deletion drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_MENELAUS) += menelaus.o

obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o

Expand Down
120 changes: 106 additions & 14 deletions drivers/mfd/twl-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,30 @@
/* Triton Core internal information (END) */


/* subchip/slave 0 0x48 - POWER */
#define TWL6030_BASEADD_RTC 0x0000
#define TWL6030_BASEADD_MEM 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
#define TWL6030_BASEADD_PM_MISC 0x00E2
#define TWL6030_BASEADD_PM_PUPD 0x00F0

/* subchip/slave 1 0x49 - FEATURE */
#define TWL6030_BASEADD_USB 0x0000
#define TWL6030_BASEADD_GPADC_CTRL 0x002E
#define TWL6030_BASEADD_AUX 0x0090
#define TWL6030_BASEADD_PWM 0x00BA
#define TWL6030_BASEADD_GASGAUGE 0x00C0
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0

/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0

/* subchip/slave 3 0x4B - AUDIO */
#define TWL6030_BASEADD_AUDIO 0x0000
#define TWL6030_BASEADD_RSV 0x0000

/* Few power values */
#define R_CFG_BOOT 0x05
#define R_PROTECT_KEY 0x0E
Expand All @@ -202,13 +226,21 @@
#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */
#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */
#define TWL5031 BIT(2) /* twl5031 has different registers */
#define TWL6030_CLASS BIT(3) /* TWL6030 class */

/*----------------------------------------------------------------------*/

/* is driver active, bound to a chip? */
static bool inuse;

/* Structure for each TWL4030 Slave */
static unsigned int twl_id;
unsigned int twl_rev(void)
{
return twl_id;
}
EXPORT_SYMBOL(twl_rev);

/* Structure for each TWL4030/TWL6030 Slave */
struct twl_client {
struct i2c_client *client;
u8 address;
Expand All @@ -228,11 +260,12 @@ struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
struct twl_mapping *twl_map;

static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl4030.h> defines for TWL4030_MODULE_*
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/

Expand Down Expand Up @@ -265,6 +298,40 @@ static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{ 3, TWL4030_BASEADD_SECURED_REG },
};

static struct twl_mapping twl6030_map[] = {
/*
* NOTE: don't change this table without updating the
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
{ SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },

{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },

{ SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },

{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },

{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
};

/*----------------------------------------------------------------------*/

/* Exported Functions */
Expand Down Expand Up @@ -292,7 +359,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
sid = twl_map[mod_no].sid;
twl = &twl_modules[sid];

if (unlikely(!inuse)) {
Expand All @@ -310,7 +377,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->flags = 0;
msg->buf = value;
/* over write the first byte of buffer with the register address */
*value = twl4030_map[mod_no].base + reg;
*value = twl_map[mod_no].base + reg;
ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
mutex_unlock(&twl->xfer_lock);

Expand Down Expand Up @@ -349,7 +416,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
sid = twl_map[mod_no].sid;
twl = &twl_modules[sid];

if (unlikely(!inuse)) {
Expand All @@ -362,7 +429,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
msg->addr = twl->address;
msg->len = 1;
msg->flags = 0; /* Read the register value */
val = twl4030_map[mod_no].base + reg;
val = twl_map[mod_no].base + reg;
msg->buf = &val;
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
Expand Down Expand Up @@ -486,6 +553,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers)
{
unsigned sub_chip_id;
/* regulator framework demands init_data ... */
if (!pdata)
return NULL;
Expand All @@ -496,7 +564,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}

/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
return add_numbered_child(3, "twl_reg", num,
sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
return add_numbered_child(sub_chip_id, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}

Expand All @@ -516,6 +585,7 @@ static int
add_children(struct twl4030_platform_data *pdata, unsigned long features)
{
struct device *child;
unsigned sub_chip_id;

if (twl_has_bci() && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
Expand Down Expand Up @@ -561,7 +631,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
child = add_child(3, "twl_rtc",
sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
child = add_child(sub_chip_id, "twl_rtc",
NULL, 0,
true, pdata->irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
Expand Down Expand Up @@ -812,16 +883,22 @@ static void clocks_init(struct device *dev,

/*----------------------------------------------------------------------*/

int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl_exit_irq(void);
int twl_init_chip_irq(const char *chip);
int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl4030_exit_irq(void);
int twl4030_init_chip_irq(const char *chip);
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl6030_exit_irq(void);

static int twl_remove(struct i2c_client *client)
{
unsigned i;
int status;

status = twl_exit_irq();
if (twl_class_is_4030())
status = twl4030_exit_irq();
else
status = twl6030_exit_irq();

if (status < 0)
return status;

Expand Down Expand Up @@ -878,6 +955,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
mutex_init(&twl->xfer_lock);
}
inuse = true;
if ((id->driver_data) & TWL6030_CLASS) {
twl_id = TWL6030_CLASS_ID;
twl_map = &twl6030_map[0];
} else {
twl_id = TWL4030_CLASS_ID;
twl_map = &twl4030_map[0];
}

/* setup clock framework */
clocks_init(&client->dev, pdata->clock);
Expand All @@ -890,8 +974,15 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->irq
&& pdata->irq_base
&& pdata->irq_end > pdata->irq_base) {
twl_init_chip_irq(id->name);
status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end);
if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
status = twl4030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
} else {
status = twl6030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
}

if (status < 0)
goto fail;
}
Expand All @@ -910,6 +1001,7 @@ static const struct i2c_device_id twl_ids[] = {
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl_ids);
Expand Down
6 changes: 3 additions & 3 deletions drivers/mfd/twl4030-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ int twl4030_sih_setup(int module)
/* FIXME pass in which interrupt line we'll use ... */
#define twl_irq_line 0

int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
static struct irq_chip twl4030_irq_chip;

Expand Down Expand Up @@ -858,7 +858,7 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
return status;
}

int twl_exit_irq(void)
int twl4030_exit_irq(void)
{
/* FIXME undo twl_init_irq() */
if (twl4030_irq_base) {
Expand All @@ -868,7 +868,7 @@ int twl_exit_irq(void)
return 0;
}

int twl_init_chip_irq(const char *chip)
int twl4030_init_chip_irq(const char *chip)
{
if (!strcmp(chip, "twl5031")) {
sih_modules = sih_modules_twl5031;
Expand Down
Loading

0 comments on commit e8deb28

Please sign in to comment.