Skip to contentSkip to conversation

add drivers/mfd/twl4030-core.c

PATCH Applied David Brownell 2 participants 6 messages Oct 6, 2008 – Oct 19, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 add drivers/mfd/twl4030-core.c

From: David Brownell <[email protected]>

This patch adds the core of the TWL4030 driver, which supports chips including the TPS65950. These chips are multi-function; see

http://focus.ti.com/docs/prod/folders/print/tps65950.html

Public specs are in the works. For now, the block diagram on the second page of the datasheet is fairly informative.

There are some known issues with this core code. Most notably, the IRQ dispatching needs simplification (to use more of genirq), generalization (integrating support for secondary IRQ dispatch as well as primary, and removing the build dependency on OMAP), and then probably updating to leverage threaded IRQ support (expected to arrive in mainline "soon").

Once the core is in mainline, drivers for other parts of this chip can follow its lead and start swimming upstream too.

---
 drivers/mfd/Kconfig         |   14 
 drivers/mfd/Makefile        |    2 
 drivers/mfd/twl4030-core.c  | 1257 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/twl4030.h |  339 +++++++++++
 4 files changed, 1612 insertions(+)
drivers/mfd/Kconfig +14 -0
@@ -50,6 +50,20 @@ config HTC_PASIC3
 	  HTC Magician devices, respectively. Actual functionality is
 	  handled by the leds-pasic3 and ds1wm drivers.
 
+config TWL4030_CORE
+	bool "Texas Instruments TWL4030/TPS659x0 Support"
+	depends on I2C=y && GENERIC_HARDIRQS && (ARCH_OMAP2 || ARCH_OMAP3)
+	help
+	  Say yes here if you have TWL4030 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.
+
+	  These multi-function chips are found on many OMAP2 and OMAP3
+	  boards, providing power management, RTC, GPIO, keypad, a
+	  high speed USB OTG transceiver, an audio codec (on most
+	  versions) and many other features.
+
 config MFD_TMIO
 	bool
 	default n
drivers/mfd/Makefile +2 -0
@@ -12,6 +12,8 @@ obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o
 obj-$(CONFIG_MFD_TC6387XB)	+= tc6387xb.o
 obj-$(CONFIG_MFD_TC6393XB)	+= tc6393xb.o
 
+obj-$(CONFIG_TWL4030_CORE)	+= twl4030-core.o
+
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
 obj-$(CONFIG_MCP)		+= mcp-core.o
drivers/mfd/twl4030-core.c new file +1257 -0

1,257 lines not on this page · raw message

include/linux/i2c/twl4030.h new file +339 -0

339 lines not on this page · raw message

Applied
#1 lore raw

Hi Dave,

Quoted 17 lines · David Brownell

From: David Brownell <[email protected]>

This patch adds the core of the TWL4030 driver, which supports chips including the TPS65950. These chips are multi-function; see

http://focus.ti.com/docs/prod/folders/print/tps65950.html

Public specs are in the works. For now, the block diagram on the second page of the datasheet is fairly informative.

There are some known issues with this core code. Most notably, the IRQ dispatching needs simplification (to use more of genirq), generalization (integrating support for secondary IRQ dispatch as well as primary, and removing the build dependency on OMAP), and then probably updating to leverage threaded IRQ support (expected to arrive in mainline "soon").

Quoted 4 lines

Once the core is in mainline, drivers for other parts of this chip can follow its lead and start swimming upstream too.

Thanks, applied to my mfd tree.

drivers/mfd/twl4030-core.c line 60

Quoted 2 lines
drivers/mfd/twl4030-core.c new file +2 -0
+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+	defined(CONFIG_TWL4030_BCI_BATTERY_MODUEL)

minor typo, you probably meant: CONFIG_TWL4030_BCI_BATTERY_MODULE

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#2 lore raw
Quoted 1 line · Samuel Ortiz

Thanks, applied to my mfd tree.

Great!

Quoted 4 lines
Quoted 2 lines

+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+     defined(CONFIG_TWL4030_BCI_BATTERY_MODUEL)

minor typo, you probably meant: CONFIG_TWL4030_BCI_BATTERY_MODULE

Yes, good catch. You'll tweak?

Appended is that simple IRQ cleanup I mentioned.
(Get a load of the diffstat!)

There's more substantial IRQ stuff on the way, but it won't be ready before the 2.6.28 merges start.

- Dave

================== CUT HERE
From: David Brownell <[email protected]>

twl4030-core irq simplification

Simplify twl4030 IRQ handling by removing a needless custom flow handler. The top level IRQs, from the PIH, are well suited for handle_simple_irq() ... they can't be acked or masked.

Switching resolves some issues with how IRQs were dispatched. Notably, abuse of desc->status, IRQ accounting, and handling of various faults.

In short, use standard genirq code.

Drivers that request_irq() to the PIH will need to pay more attention to things like setting IRQF_DISABLED (since it's no longer ignored), and making I2C calls from handlers (you'll need a lockdep workaround).

---
 drivers/mfd/twl4030-core.c |   68 +--------------------------------------------
 1 file changed, 2 insertions(+), 66 deletions(-)
drivers/mfd/twl4030-core.c +2 -66
@@ -535,68 +535,6 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8);
 
 /*----------------------------------------------------------------------*/
 
-/*
- * do_twl4030_module_irq() is the desc->handle method for each of the twl4030
- * module interrupts that doesn't chain to another irq_chip (GPIO, power, etc).
- * It executes in kernel thread context.  On entry, cpu interrupts are disabled.
- */
-static void do_twl4030_module_irq(unsigned int irq, irq_desc_t *desc)
-{
-	struct irqaction *action;
-	const unsigned int cpu = smp_processor_id();
-
-	/*
-	 * Earlier this was desc->triggered = 1;
-	 */
-	desc->status |= IRQ_LEVEL;
-
-	/*
-	 * The desc->handle method would normally call the desc->chip->ack
-	 * method here, but we won't bother since our ack method is NULL.
-	 */
-
-	if (!desc->depth) {
-		kstat_cpu(cpu).irqs[irq]++;
-
-		action = desc->action;
-		if (action) {
-			int ret;
-			int status = 0;
-			int retval = 0;
-
-			local_irq_enable();
-
-			do {
-				/* Call the ISR with cpu interrupts enabled */
-				ret = action->handler(irq, action->dev_id);
-				if (ret == IRQ_HANDLED)
-					status |= action->flags;
-				retval |= ret;
-				action = action->next;
-			} while (action);
-
-			if (status & IRQF_SAMPLE_RANDOM)
-				add_interrupt_randomness(irq);
-
-			local_irq_disable();
-
-			if (retval != IRQ_HANDLED)
-				printk(KERN_ERR "ISR for TWL4030 module"
-					" irq %d can't handle interrupt\n",
-					irq);
-
-			/*
-			 * Here is where we should call the unmask method, but
-			 * again we won't bother since it is NULL.
-			 */
-		} else
-			printk(KERN_CRIT "TWL4030 module irq %d has no ISR"
-					" but can't be masked!\n", irq);
-	} else
-		printk(KERN_CRIT "TWL4030 module irq %d is disabled but can't"
-				" be masked!\n", irq);
-}
-
 static unsigned twl4030_irq_base;
 
 static struct completion irq_event;
@@ -611,7 +549,6 @@ static int twl4030_irq_thread(void *data)
 	static unsigned i2c_errors;
 	const static unsigned max_i2c_errors = 100;
 
-	daemonize("twl4030-irq");
 	current->flags |= PF_NOFREEZE;
 
 	while (!kthread_should_stop()) {
@@ -691,8 +628,7 @@ static struct task_struct * __init start_twl4030_irq_thread(long irq)
 	struct task_struct *thread;
 
 	init_completion(&irq_event);
-	thread = kthread_run(twl4030_irq_thread, (void *)irq,
-			     "twl4030 irq %ld", irq);
+	thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq");
 	if (!thread)
 		pr_err("%s: could not create twl4030 irq %ld thread!\n",
 		       DRIVER_NAME, irq);
@@ -1126,7 +1062,7 @@ static void twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 	/* install an irq handler for each of the PIH modules */
 	for (i = irq_base; i < irq_end; i++) {
 		set_irq_chip_and_handler(i, &twl4030_irq_chip,
-				do_twl4030_module_irq);
+				handle_simple_irq);
 		activate_irq(i);
 	}
 
#3 lore raw
Quoted 11 lines · David Brownell
Quoted 1 line · Samuel Ortiz

Thanks, applied to my mfd tree.

Great!

Quoted 4 lines
Quoted 2 lines

+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+     defined(CONFIG_TWL4030_BCI_BATTERY_MODUEL)

minor typo, you probably meant: CONFIG_TWL4030_BCI_BATTERY_MODULE

Yes, good catch. You'll tweak?

Sure.

Quoted 2 lines

Appended is that simple IRQ cleanup I mentioned.
(Get a load of the diffstat!)

Sweet. I should look at it for the asic3 driver.
Applied to the mfd tree, thanks.

Quoted 2 lines

There's more substantial IRQ stuff on the way, but it won't be ready before the 2.6.28 merges start.

I can still take patches during the merge window, if you think they're really ready and necessary.

Cheers,
Samuel.

Quoted 127 lines

- Dave

================== CUT HERE
From: David Brownell <[email protected]>

twl4030-core irq simplification

Simplify twl4030 IRQ handling by removing a needless custom flow handler. The top level IRQs, from the PIH, are well suited for handle_simple_irq() ... they can't be acked or masked.

Switching resolves some issues with how IRQs were dispatched. Notably, abuse of desc->status, IRQ accounting, and handling of various faults.

In short, use standard genirq code.

Drivers that request_irq() to the PIH will need to pay more attention to things like setting IRQF_DISABLED (since it's no longer ignored), and making I2C calls from handlers (you'll need a lockdep workaround).

---
 drivers/mfd/twl4030-core.c |   68 +--------------------------------------------
 1 file changed, 2 insertions(+), 66 deletions(-)
drivers/mfd/twl4030-core.c +2 -66
@@ -535,68 +535,6 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8);
 
 /*----------------------------------------------------------------------*/
 
-/*
- * do_twl4030_module_irq() is the desc->handle method for each of the twl4030
- * module interrupts that doesn't chain to another irq_chip (GPIO, power, etc).
- * It executes in kernel thread context.  On entry, cpu interrupts are disabled.
- */
-static void do_twl4030_module_irq(unsigned int irq, irq_desc_t *desc)
-{
-	struct irqaction *action;
-	const unsigned int cpu = smp_processor_id();
-
-	/*
-	 * Earlier this was desc->triggered = 1;
-	 */
-	desc->status |= IRQ_LEVEL;
-
-	/*
-	 * The desc->handle method would normally call the desc->chip->ack
-	 * method here, but we won't bother since our ack method is NULL.
-	 */
-
-	if (!desc->depth) {
-		kstat_cpu(cpu).irqs[irq]++;
-
-		action = desc->action;
-		if (action) {
-			int ret;
-			int status = 0;
-			int retval = 0;
-
-			local_irq_enable();
-
-			do {
-				/* Call the ISR with cpu interrupts enabled */
-				ret = action->handler(irq, action->dev_id);
-				if (ret == IRQ_HANDLED)
-					status |= action->flags;
-				retval |= ret;
-				action = action->next;
-			} while (action);
-
-			if (status & IRQF_SAMPLE_RANDOM)
-				add_interrupt_randomness(irq);
-
-			local_irq_disable();
-
-			if (retval != IRQ_HANDLED)
-				printk(KERN_ERR "ISR for TWL4030 module"
-					" irq %d can't handle interrupt\n",
-					irq);
-
-			/*
-			 * Here is where we should call the unmask method, but
-			 * again we won't bother since it is NULL.
-			 */
-		} else
-			printk(KERN_CRIT "TWL4030 module irq %d has no ISR"
-					" but can't be masked!\n", irq);
-	} else
-		printk(KERN_CRIT "TWL4030 module irq %d is disabled but can't"
-				" be masked!\n", irq);
-}
-
 static unsigned twl4030_irq_base;
 
 static struct completion irq_event;
@@ -611,7 +549,6 @@ static int twl4030_irq_thread(void *data)
 	static unsigned i2c_errors;
 	const static unsigned max_i2c_errors = 100;
 
-	daemonize("twl4030-irq");
 	current->flags |= PF_NOFREEZE;
 
 	while (!kthread_should_stop()) {
@@ -691,8 +628,7 @@ static struct task_struct * __init start_twl4030_irq_thread(long irq)
 	struct task_struct *thread;
 
 	init_completion(&irq_event);
-	thread = kthread_run(twl4030_irq_thread, (void *)irq,
-			     "twl4030 irq %ld", irq);
+	thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq");
 	if (!thread)
 		pr_err("%s: could not create twl4030 irq %ld thread!\n",
 		       DRIVER_NAME, irq);
@@ -1126,7 +1062,7 @@ static void twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
 	/* install an irq handler for each of the PIH modules */
 	for (i = irq_base; i < irq_end; i++) {
 		set_irq_chip_and_handler(i, &twl4030_irq_chip,
-				do_twl4030_module_irq);
+				handle_simple_irq);
 		activate_irq(i);
 	}
 
Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#4 lore raw
Quoted 5 lines · Samuel Ortiz
Quoted 2 lines

There's more substantial IRQ stuff on the way, but it won't be ready before the 2.6.28 merges start.

I can still take patches during the merge window, if you think they're really ready and necessary.

I think they're necessary, but they'll be more ready a bit later in the merge window.

Other issues are still stabilizing; right now we're chasing down some unrelated issues.

Like ... it turns out some power management support in the I2C driver is causing trouble with this driver (intermittent failures). Workaround is to turn off I2C power management; the real fix seems to involve recognizing that OMAP2 != OMAP3.

The problem first surfaced when the $SUBJECT core moved from drivers/i2c/chips to drivers/mfd, and some timings changed. Intermittent failure then morphed intoGood fun! ;)

- Dave

#5 lore raw
In reply to David Brownell
Quoted 6 lines · David Brownell

This patch adds the core of the TWL4030 driver, which supports chips including the TPS65950.  These chips are multi-function; see

  http://focus.ti.com/docs/prod/folders/print/tps65950.html

Public specs are in the works.

... and are now available at that URL: all 925 pages (!!) of technical reference manual, for your reading pleasure.

I understand that the tps65950 is more or less equivalent to the twl5030, which is the latest incarnation of the twl4030.

- Dave