All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] mfd/mc13783: near complete rewrite
@ 2009-10-23 20:38 Uwe Kleine-König
  2009-10-24  8:35   ` Uwe Kleine-König
                   ` (2 more replies)
  0 siblings, 3 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-10-23 20:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Samuel Ortiz

This fixes several things while still providing the old API:

 - simplify and fix locking
 - better error handling
 - don't ack all irqs making it impossible to detect a reset of the
   rtc
 - use a timeout variant to wait for completion of ADC conversion
 - provide platform-data to regulator subdevice (This allows making
   struct mc13783 opaque for other drivers after the regulator driver is
   updated to use its platform_data.)
 - expose all interrupts
 - use threaded irq

After all users in mainline are converted to the new API, some things
(e.g. mc13783-private.h) can go away.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
 drivers/mfd/mc13783-core.c          |  789 ++++++++++++++++++++++++-----------
 include/linux/mfd/mc13783-private.h |  208 +---------
 include/linux/mfd/mc13783.h         |  122 +++++--
 3 files changed, 651 insertions(+), 468 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index e354d29..63a5104 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -1,286 +1,579 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * This code is in parts based on wm8350-core.c and pcf50633-core.c
- *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
-
-#include <linux/mfd/mc13783-private.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/core.h>
-#include <linux/spi/spi.h>
-#include <linux/uaccess.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13783-private.h>
 
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_FRAME_MASK	0x00ffffff
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_REG_NUM_SHIFT	0x19
-#define MC13783_WRITE_BIT_SHIFT	31
+#define MC13783_IRQSTAT0	0
+#define MC13783_IRQSTAT0_ADCDONEI	(1 << 0)
+#define MC13783_IRQSTAT0_ADCBISDONEI	(1 << 1)
+#define MC13783_IRQSTAT0_TSI		(1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI		(1 << 3)
+#define MC13783_IRQSTAT0_WLOWI		(1 << 4)
+#define MC13783_IRQSTAT0_CHGDETI	(1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI		(1 << 7)
+#define MC13783_IRQSTAT0_CHGREVI	(1 << 8)
+#define MC13783_IRQSTAT0_CHGSHORTI	(1 << 9)
+#define MC13783_IRQSTAT0_CCCVI		(1 << 10)
+#define MC13783_IRQSTAT0_CHGCURRI	(1 << 11)
+#define MC13783_IRQSTAT0_BPONI		(1 << 12)
+#define MC13783_IRQSTAT0_LOBATLI	(1 << 13)
+#define MC13783_IRQSTAT0_LOBATHI	(1 << 14)
+#define MC13783_IRQSTAT0_UDPI		(1 << 15)
+#define MC13783_IRQSTAT0_USBI		(1 << 16)
+#define MC13783_IRQSTAT0_IDI		(1 << 19)
+#define MC13783_IRQSTAT0_SE1I		(1 << 21)
+#define MC13783_IRQSTAT0_CKDETI		(1 << 22)
+#define MC13783_IRQSTAT0_UDMI		(1 << 23)
+
+#define MC13783_IRQMASK0	1
+#define MC13783_IRQMASK0_ADCDONEM	MC13783_IRQSTAT0_ADCDONEI
+#define MC13783_IRQMASK0_ADCBISDONEM	MC13783_IRQSTAT0_ADCBISDONEI
+#define MC13783_IRQMASK0_TSM		MC13783_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM		MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM		MC13783_IRQSTAT0_WLOWI
+#define MC13783_IRQMASK0_CHGDETM	MC13783_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM		MC13783_IRQSTAT0_CHGOVI
+#define MC13783_IRQMASK0_CHGREVM	MC13783_IRQSTAT0_CHGREVI
+#define MC13783_IRQMASK0_CHGSHORTM	MC13783_IRQSTAT0_CHGSHORTI
+#define MC13783_IRQMASK0_CCCVM		MC13783_IRQSTAT0_CCCVI
+#define MC13783_IRQMASK0_CHGCURRM	MC13783_IRQSTAT0_CHGCURRI
+#define MC13783_IRQMASK0_BPONM		MC13783_IRQSTAT0_BPONI
+#define MC13783_IRQMASK0_LOBATLM	MC13783_IRQSTAT0_LOBATLI
+#define MC13783_IRQMASK0_LOBATHM	MC13783_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM		MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM		MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM		MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M		MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM		MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM		MC13783_IRQSTAT0_UDMI
+
+#define MC13783_IRQSTAT1	3
+#define MC13783_IRQSTAT1_1HZI		(1 << 0)
+#define MC13783_IRQSTAT1_TODAI		(1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I	(1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I	(1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I	(1 << 5)
+#define MC13783_IRQSTAT1_SYSRSTI	(1 << 6)
+#define MC13783_IRQSTAT1_RTCRSTI	(1 << 7)
+#define MC13783_IRQSTAT1_PCI		(1 << 8)
+#define MC13783_IRQSTAT1_WARMI		(1 << 9)
+#define MC13783_IRQSTAT1_MEMHLDI	(1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI	(1 << 11)
+#define MC13783_IRQSTAT1_THWARNLI	(1 << 12)
+#define MC13783_IRQSTAT1_THWARNHI	(1 << 13)
+#define MC13783_IRQSTAT1_CLKI		(1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI		(1 << 15)
+#define MC13783_IRQSTAT1_MC2BI		(1 << 17)
+#define MC13783_IRQSTAT1_HSDETI		(1 << 18)
+#define MC13783_IRQSTAT1_HSLI		(1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI	(1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI	(1 << 21)
+
+#define MC13783_IRQMASK1	4
+#define MC13783_IRQMASK1_1HZM		MC13783_IRQSTAT1_1HZI
+#define MC13783_IRQMASK1_TODAM		MC13783_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M	MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M	MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M	MC13783_IRQSTAT1_ONOFD3I
+#define MC13783_IRQMASK1_SYSRSTM	MC13783_IRQSTAT1_SYSRSTI
+#define MC13783_IRQMASK1_RTCRSTM	MC13783_IRQSTAT1_RTCRSTI
+#define MC13783_IRQMASK1_PCM		MC13783_IRQSTAT1_PCI
+#define MC13783_IRQMASK1_WARMM		MC13783_IRQSTAT1_WARMI
+#define MC13783_IRQMASK1_MEMHLDM	MC13783_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM	MC13783_IRQSTAT1_PWRRDYI
+#define MC13783_IRQMASK1_THWARNLM	MC13783_IRQSTAT1_THWARNLI
+#define MC13783_IRQMASK1_THWARNHM	MC13783_IRQSTAT1_THWARNHI
+#define MC13783_IRQMASK1_CLKM		MC13783_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM		MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM		MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM		MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM		MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM	MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM	MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13783_ADC1		44
+#define MC13783_ADC1_ADEN		(1 << 0)
+#define MC13783_ADC1_RAND		(1 << 1)
+#define MC13783_ADC1_ADSEL		(1 << 3)
+#define MC13783_ADC1_ASC		(1 << 20)
+#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
+
+#define MC13783_NUMREGS 0x3f
+
+void mc13783_lock(struct mc13783 *mc13783)
+{
+	if (!mutex_trylock(&mc13783->lock)) {
+		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
+			__func__, __builtin_return_address(0));
 
-static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+		mutex_lock(&mc13783->lock);
+	}
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13783_lock);
+
+void mc13783_unlock(struct mc13783 *mc13783)
 {
-	struct spi_transfer t = {
-		.tx_buf = (const void *)buf,
-		.rx_buf = buf,
-		.len = len,
-		.cs_change = 0,
-		.delay_usecs = 0,
-	};
-	struct spi_message m;
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+	mutex_unlock(&mc13783->lock);
+}
+EXPORT_SYMBOL(mc13783_unlock);
 
-	spi_message_init(&m);
-	spi_message_add_tail(&t, &m);
-	if (spi_sync(spi, &m) != 0 || m.status != 0)
+static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 *val)
+{
+	if (offset > MC13783_NUMREGS)
 		return -EINVAL;
-	return len - m.actual_length;
+
+	buf[0] = offset << 25;
+
+	memset(t, 0, sizeof(*t));
+
+	t->tx_buf = buf;
+	t->rx_buf = buf;
+	t->len = sizeof(u32);
+
+	return 1;
 }
 
-static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 *val)
 {
-	unsigned int frame = 0;
-	int ret = 0;
+	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
+
+	*val = buf[0] & 0xffffff;
 
-	if (reg_num > MC13783_MAX_REG_NUM)
+	return 1;
+}
+
+static int mc13783_prep_write_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 val)
+{
+	if (offset > MC13783_NUMREGS || val > 0xffffff) 
 		return -EINVAL;
 
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
+	buf[0] = 1 << 31 | offset << 25 | val;
 
-	ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	memset(t, 0, sizeof(*t));
 
-	*reg_val = frame & MC13783_FRAME_MASK;
+	t->tx_buf = buf;
+	t->rx_buf = buf;
+	t->len = sizeof(u32);
 
-	return ret;
+	return 1;
 }
 
-static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 val)
 {
-	unsigned int frame = 0;
+	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
 
-	if (reg_num > MC13783_MAX_REG_NUM)
-		return -EINVAL;
+	return 1;
+}
+
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
+{
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
+	int ret;
 
-	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
-	frame |= reg_val & MC13783_FRAME_MASK;
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	ret = mc13783_prep_read_transfer(mc13783, &t, &buf, offset, val);
+
+	if (ret < 0)
+		return ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
 
-	return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	ret = spi_sync(mc13783->spidev, &m);
+
+	/* error in message.status implies error return from spi_sync */
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	ret = mc13783_eval_read_transfer(mc13783, &t, &buf, offset, val);
+
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
+	
+	return ret < 0 ? ret : 0;
 }
+EXPORT_SYMBOL(mc13783_reg_read);
 
-int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
 {
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
 	int ret;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_read(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
 
-	return ret;
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+	ret = mc13783_prep_write_transfer(mc13783, &t, &buf, offset, val);
+
+	if (ret < 0)
+		return ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13783->spidev, &m);
+
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
+
+	return ret < 0 ? ret : 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_read);
+EXPORT_SYMBOL(mc13783_reg_write);
 
-int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val)
 {
 	int ret;
+	u32 valread;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_write(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	BUG_ON(val & ~mask);
 
-	return ret;
+	ret = mc13783_reg_read(mc13783, offset, &valread);
+	if (ret)
+		return ret;
+
+	valread = (valread & ~mask) | val;
+
+	return mc13783_reg_write(mc13783, offset, valread);
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_write);
+EXPORT_SYMBOL(mc13783_reg_rmw);
 
-/**
- * mc13783_set_bits - Bitmask write
- *
- * @mc13783: Pointer to mc13783 control structure
- * @reg:    Register to access
- * @mask:   Mask of bits to change
- * @val:    Value to set for masked bits
- */
-int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
+int mc13783_mask(struct mc13783 *mc13783, unsigned int irq)
 {
-	u32 tmp;
 	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
+
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
 
-	mutex_lock(&mc13783->io_lock);
+	if (mask & irqbit)
+		/* already masked */
+		return 0;
 
-	ret = mc13783_read(mc13783, reg, &tmp);
-	tmp = (tmp & ~mask) | val;
-	if (ret == 0)
-		ret = mc13783_write(mc13783, reg, tmp);
+	return mc13783_reg_write(mc13783, offmask, mask | irqbit);
+}
+EXPORT_SYMBOL(mc13783_mask);
 
-	mutex_unlock(&mc13783->io_lock);
+int mc13783_unmask(struct mc13783 *mc13783, unsigned int irq)
+{
+	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
 
-	return ret;
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	if (!(mask & irqbit))
+		/* already unmasked */
+		return 0;
+
+	return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
 }
-EXPORT_SYMBOL_GPL(mc13783_set_bits);
+EXPORT_SYMBOL(mc13783_unmask);
 
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data)
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
+		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
+		const char *name, void *dev)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+	BUG_ON(!handler);
+
+	if (irq >= MC13783_NUM_IRQ)
 		return -EINVAL;
 
-	if (WARN_ON(mc13783->irq_handler[irq].handler))
+	if (mc13783->irqhandler[irq])
 		return -EBUSY;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = handler;
-	mc13783->irq_handler[irq].data = data;
-	mutex_unlock(&mc13783->io_lock);
+	mc13783->irqhandler[irq] = handler;
+	mc13783->irqdata[irq] = dev;
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13783_irq_request_nounmask);
+
+int mc13783_irq_request(struct mc13783 *mc13783, unsigned int irq,
+		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
+		const char *name, void *dev)
+{
+	int ret;
+
+	ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
+	if (ret)
+		return ret;
+
+	ret = mc13783_unmask(mc13783, irq);
+	if (ret) {
+		mc13783->irqhandler[irq] = NULL;
+		mc13783->irqdata[irq] = NULL;
+		return ret;
+	}
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_register_irq);
+EXPORT_SYMBOL(mc13783_irq_request);
 
-int mc13783_free_irq(struct mc13783 *mc13783, int irq)
+int mc13783_irq_free(struct mc13783 *mc13783, unsigned int irq, void *dev)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ)
+	int ret;
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	if (irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
+			mc13783->irqdata[irq] != dev)
 		return -EINVAL;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = NULL;
-	mutex_unlock(&mc13783->io_lock);
+	ret = mc13783_mask(mc13783, irq);
+	if (ret)
+		return ret;
+
+	mc13783->irqhandler[irq] = NULL;
+	mc13783->irqdata[irq] = NULL;
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_free_irq);
+EXPORT_SYMBOL(mc13783_irq_free);
 
-static void mc13783_irq_work(struct work_struct *work)
+static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783,
+		unsigned int irq)
 {
-	struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
-	int i;
-	unsigned int adc_sts;
-
-	/* check if the adc has finished any completion */
-	mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
-			adc_sts & MC13783_INT_STAT_ADCDONEI);
-
-	if (adc_sts & MC13783_INT_STAT_ADCDONEI)
-		complete_all(&mc13783->adc_done);
-
-	for (i = 0; i < MC13783_NUM_IRQ; i++)
-		if (mc13783->irq_handler[i].handler)
-			mc13783->irq_handler[i].handler(i,
-					mc13783->irq_handler[i].data);
-	enable_irq(mc13783->irq);
+	return mc13783->irqhandler[irq](mc13783, irq, mc13783->irqdata[irq]);
 }
 
-static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
+int mc13783_ackirq(struct mc13783 *mc13783, unsigned int irq)
 {
-	struct mc13783 *mc13783 = dev_id;
+	unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
 
-	disable_irq_nosync(irq);
+	BUG_ON(irq >= MC13783_NUM_IRQ);
 
-	schedule_work(&mc13783->work);
-	return IRQ_HANDLED;
+	return mc13783_reg_write(mc13783, offstat, val);
 }
+EXPORT_SYMBOL(mc13783_ackirq);
 
-/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
-static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13783->lock
+ */
+static int mc13783_irq_handle(struct mc13783 *mc13783,
+		unsigned int offstat, unsigned int offmask,
+		unsigned int baseirq)
 {
-	unsigned int reg_adc0, reg_adc1;
+	u32 stat, mask;
+	int ret = mc13783_reg_read(mc13783, offstat, &stat);
+	int num_handled = 0;
 
-	reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
-			| MC13783_ADC0_TSMOD0;
-	reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+	if (ret)
+		return ret;
+
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	while (stat & ~mask) {
+		unsigned int irq = __ffs(stat & ~mask);
+
+		stat &= ~(1 << irq);
+
+		if (likely(mc13783->irqhandler[baseirq + irq])) {
+			irqreturn_t handled;
+
+			handled = mc13783_irqhandler(mc13783, baseirq + irq);
+			if (handled == IRQ_HANDLED)
+				num_handled++;
+		} else {
+			dev_err(&mc13783->spidev->dev,
+					"BUG: irq %u but no handler\n",
+					baseirq + irq);
+
+			mask |= 1 << irq;
+
+			ret = mc13783_reg_write(mc13783, offmask, mask);
+		}
+	}
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+	return num_handled;
 }
 
+static irqreturn_t mc13783_irq_thread(int irq, void *data)
+{
+	struct mc13783 *mc13783 = data;
+	irqreturn_t ret;
+	int handled = 0;
+
+	mc13783_lock(mc13783);
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
+			MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
+	if (ret > 0)
+		handled = 1;
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
+			MC13783_IRQMASK1, MC13783_IRQ_1HZ);
+	if (ret > 0)
+		handled = 1;
+
+	mc13783_unlock(mc13783);
+
+	return IRQ_RETVAL(handled);
+}
+
+#define MC13783_ADC1_CHAN0_SHIFT	5
+#define MC13783_ADC1_CHAN1_SHIFT	8
+
+static irqreturn_t mc13783_handler_adcdone(struct mc13783 *mc13783,
+		unsigned int irq, void *data)
+{
+	struct completion *done = data;
+
+	mc13783_ackirq(mc13783, irq);
+
+	complete_all(done);
+
+	return IRQ_HANDLED;
+}
+
+#define MC13783_ADC_WORKING (1 << 16)
+
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample)
 {
-	unsigned int reg_adc0, reg_adc1;
-	int i;
+	u32 adc0, adc1, old_adc0;
+	int i, ret;
+
+	DECLARE_COMPLETION_ONSTACK(done);
+	dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
 
-	mutex_lock(&mc13783->adc_conv_lock);
+	mc13783_lock(mc13783);
+
+	if (mc13783->flags & MC13783_ADC_WORKING) {
+		ret = -EBUSY;
+		goto out;
+	}
 
-	/* set up auto incrementing anyway to make quick read */
-	reg_adc0 =  MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
-	/* enable the adc, ignore external triggering and set ASC to trigger
-	 * conversion */
-	reg_adc1 =  MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
-		| MC13783_ADC1_ASC;
+	mc13783->flags |= MC13783_ADC_WORKING;
+
+	mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
+
+	adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+	adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
 
-	/* setup channel number */
 	if (channel > 7)
-		reg_adc1 |= MC13783_ADC1_ADSEL;
+		adc1 |= MC13783_ADC1_ADSEL;
 
-	switch (mode) {
+	switch(mode) {
 	case MC13783_ADC_MODE_TS:
-		/* enables touch screen reference mode and set touchscreen mode
-		 * to position mode */
-		reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
 			| MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	case MC13783_ADC_MODE_SINGLE_CHAN:
-		reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
-		reg_adc1 |= MC13783_ADC1_RAND;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+		adc1 |= MC13783_ADC1_RAND;
 		break;
+
 	case MC13783_ADC_MODE_MULT_CHAN:
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	default:
+		mc13783_unlock(mc13783);
 		return -EINVAL;
 	}
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
 
-	wait_for_completion_interruptible(&mc13783->adc_done);
+	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
+	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
+			mc13783_handler_adcdone, __func__, &done);
 
-	for (i = 0; i < 4; i++)
-		mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
+	mc13783_unlock(mc13783);
 
-	if (mc13783->ts_active)
-		mc13783_adc_set_ts_irq_mode(mc13783);
+	ret = wait_for_completion_interruptible_timeout(&done, HZ);
 
-	mutex_unlock(&mc13783->adc_conv_lock);
+	if (!ret)
+		ret = -ETIMEDOUT;
 
-	return 0;
+	mc13783_lock(mc13783);
+
+	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &done);
+
+	if (mode == MC13783_ADC_MODE_TS)
+		/* restore TSMOD */
+		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
+
+	if (ret > 0)
+		for (i = 0; i < 4; ++i)
+			mc13783_reg_read(mc13783,
+					MC13783_REG_ADC_2, &sample[i]);
+
+	mc13783->flags &= ~MC13783_ADC_WORKING;
+out:
+	mc13783_unlock(mc13783);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
+		const char *name, void *pdata, size_t pdata_size)
+{
+	struct mfd_cell cell = {
+		.name = name,
+		.platform_data = pdata,
+		.data_size = pdata_size,
+	};
+
+	return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
 {
-	mc13783->ts_active = status;
+	return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
 }
-EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
 
 static int mc13783_check_revision(struct mc13783 *mc13783)
 {
 	u32 rev_id, rev1, rev2, finid, icid;
 
-	mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
+	mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
 
 	rev1 = (rev_id & 0x018) >> 3;
 	rev2 = (rev_id & 0x007);
@@ -292,38 +585,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
 		rev1 = 3;
 
 	if (rev1 == 0 || icid != 2) {
-		dev_err(mc13783->dev, "No MC13783 detected.\n");
+		dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
 		return -ENODEV;
 	}
 
-	mc13783->revision = ((rev1 * 10) + rev2);
-	dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
-	       rev2, finid);
+	dev_info(&mc13783->spidev->dev,
+			"MC13783 Rev %d.%d FinVer %x detected\n",
+			rev1, rev2, finid);
 
 	return 0;
 }
 
-/*
- * Register a client device.  This is non-fatal since there is no need to
- * fail the entire device init due to a single platform device failing.
- */
-static void mc13783_client_dev_register(struct mc13783 *mc13783,
-				       const char *name)
-{
-	struct mfd_cell cell = {};
-
-	cell.name = name;
-
-	mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
-}
-
-static int __devinit mc13783_probe(struct spi_device *spi)
+static int mc13783_probe(struct spi_device *spi)
 {
 	struct mc13783 *mc13783;
-	struct mc13783_platform_data *pdata = spi->dev.platform_data;
+	struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
 	int ret;
 
-	mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+	mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
 	if (!mc13783)
 		return -ENOMEM;
 
@@ -332,96 +611,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
 	spi->bits_per_word = 32;
 	spi_setup(spi);
 
-	mc13783->spi_device = spi;
-	mc13783->dev = &spi->dev;
-	mc13783->irq = spi->irq;
+	mc13783->spidev = spi;
+
+	mutex_init(&mc13783->lock);
+	mc13783_lock(mc13783);
 
-	INIT_WORK(&mc13783->work, mc13783_irq_work);
-	mutex_init(&mc13783->io_lock);
-	mutex_init(&mc13783->adc_conv_lock);
-	init_completion(&mc13783->adc_done);
+	ret = mc13783_check_revision(mc13783);
+	if (ret)
+		goto err_revision;
+
+	/* mask all irqs */
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
+			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
+
+	if (ret) {
+err_mask:
+err_revision:
+		mutex_unlock(&mc13783->lock);
+		dev_set_drvdata(&spi->dev, NULL);
+		kfree(mc13783);
+		return ret;
+	}
 
+	/* This should go away (BEGIN) */
 	if (pdata) {
 		mc13783->flags = pdata->flags;
 		mc13783->regulators = pdata->regulators;
 		mc13783->num_regulators = pdata->num_regulators;
 	}
+	/* This should go away (END) */
+
+	if (pdata->flags & MC13783_USE_ADC)
+		mc13783_add_subdevice(mc13783, "mc13783-adc");
+
+	if (pdata->flags & MC13783_USE_CODEC)
+		mc13783_add_subdevice(mc13783, "mc13783-codec");
 
-	if (mc13783_check_revision(mc13783)) {
-		ret = -ENODEV;
-		goto err_out;
+	if (pdata->flags & MC13783_USE_REGULATOR) {
+		struct mc13783_regulator_platform_data regulator_pdata = {
+			.num_regulators = pdata->num_regulators,
+			.regulators = pdata->regulators,
+		};
+
+		mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
+				&regulator_pdata, sizeof(regulator_pdata));
 	}
 
-	/* clear and mask all interrupts */
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
+	if (pdata->flags & MC13783_USE_RTC)
+		mc13783_add_subdevice(mc13783, "mc13783-rtc");
 
-	/* unmask adcdone interrupts */
-	mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
-			MC13783_INT_MASK_ADCDONEM, 0);
+	if (pdata->flags & MC13783_USE_TOUCHSCREEN)
+		mc13783_add_subdevice(mc13783, "mc13783-ts");
 
-	ret = request_irq(mc13783->irq, mc13783_interrupt,
-			IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
-			mc13783);
-	if (ret)
-		goto err_out;
-
-	if (mc13783->flags & MC13783_USE_CODEC)
-		mc13783_client_dev_register(mc13783, "mc13783-codec");
-	if (mc13783->flags & MC13783_USE_ADC)
-		mc13783_client_dev_register(mc13783, "mc13783-adc");
-	if (mc13783->flags & MC13783_USE_RTC)
-		mc13783_client_dev_register(mc13783, "mc13783-rtc");
-	if (mc13783->flags & MC13783_USE_REGULATOR)
-		mc13783_client_dev_register(mc13783, "mc13783-regulator");
-	if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
-		mc13783_client_dev_register(mc13783, "mc13783-ts");
+	mc13783_unlock(mc13783);
 
 	return 0;
-
-err_out:
-	kfree(mc13783);
-	return ret;
 }
 
 static int __devexit mc13783_remove(struct spi_device *spi)
 {
-	struct mc13783 *mc13783;
+	struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
 
-	mc13783 = dev_get_drvdata(&spi->dev);
-
-	free_irq(mc13783->irq, mc13783);
+	free_irq(mc13783->spidev->irq, mc13783);
 
 	mfd_remove_devices(&spi->dev);
 
 	return 0;
 }
 
-static struct spi_driver pmic_driver = {
+static struct spi_driver mc13783_driver = {
 	.driver = {
-		   .name = "mc13783",
-		   .bus = &spi_bus_type,
-		   .owner = THIS_MODULE,
+		.name = "mc13783",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
 	},
 	.probe = mc13783_probe,
 	.remove = __devexit_p(mc13783_remove),
 };
 
-static int __init pmic_init(void)
+static int __init mc13783_init(void)
 {
-	return spi_register_driver(&pmic_driver);
+	return spi_register_driver(&mc13783_driver);
 }
-subsys_initcall(pmic_init);
+subsys_initcall(mc13783_init);
 
-static void __exit pmic_exit(void)
+static void __exit mc13783_exit(void)
 {
-	spi_unregister_driver(&pmic_driver);
+	spi_unregister_driver(&mc13783_driver);
 }
-module_exit(pmic_exit);
-
-MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
 
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index 47e698c..c5eca82 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -24,52 +24,24 @@
 
 #include <linux/platform_device.h>
 #include <linux/mfd/mc13783.h>
-#include <linux/workqueue.h>
 #include <linux/mutex.h>
-
-struct mc13783_irq {
-	void (*handler)(int, void *);
-	void *data;
-};
-
-#define MC13783_NUM_IRQ		2
-#define MC13783_IRQ_TS		0
-#define MC13783_IRQ_REGULATOR	1
-
-#define MC13783_ADC_MODE_TS		1
-#define MC13783_ADC_MODE_SINGLE_CHAN	2
-#define MC13783_ADC_MODE_MULT_CHAN	3
+#include <linux/interrupt.h>
 
 struct mc13783 {
-	int revision;
-	struct device *dev;
-	struct spi_device *spi_device;
-
-	int (*read_dev)(void *data, char reg, int count, u32 *dst);
-	int (*write_dev)(void *data, char reg, int count, const u32 *src);
-
-	struct mutex io_lock;
-	void *io_data;
+	struct spi_device *spidev;
+	struct mutex lock;
 	int irq;
-	unsigned int flags;
+	int flags;
 
-	struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
-	struct work_struct work;
-	struct completion adc_done;
-	unsigned int ts_active;
-	struct mutex adc_conv_lock;
+	irqreturn_t (*irqhandler[MC13783_NUM_IRQ])(struct mc13783 *,
+			unsigned int, void *);
+	void *irqdata[MC13783_NUM_IRQ];
 
+	/* XXX these should go as platformdata to the regulator subdevice */
 	struct mc13783_regulator_init_data *regulators;
 	int num_regulators;
 };
 
-int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
-int mc13783_reg_write(struct mc13783 *, int, u32);
-int mc13783_set_bits(struct mc13783 *, int, u32, u32);
-int mc13783_free_irq(struct mc13783 *mc13783, int irq);
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data);
-
 #define MC13783_REG_INTERRUPT_STATUS_0		 0
 #define MC13783_REG_INTERRUPT_MASK_0		 1
 #define MC13783_REG_INTERRUPT_SENSE_0		 2
@@ -136,55 +108,6 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_REG_TEST_3			63
 #define MC13783_REG_NB				64
 
-
-/*
- * Interrupt Status
- */
-#define MC13783_INT_STAT_ADCDONEI	(1 << 0)
-#define MC13783_INT_STAT_ADCBISDONEI	(1 << 1)
-#define MC13783_INT_STAT_TSI		(1 << 2)
-#define MC13783_INT_STAT_WHIGHI		(1 << 3)
-#define MC13783_INT_STAT_WLOWI		(1 << 4)
-#define MC13783_INT_STAT_CHGDETI	(1 << 6)
-#define MC13783_INT_STAT_CHGOVI		(1 << 7)
-#define MC13783_INT_STAT_CHGREVI	(1 << 8)
-#define MC13783_INT_STAT_CHGSHORTI	(1 << 9)
-#define MC13783_INT_STAT_CCCVI		(1 << 10)
-#define MC13783_INT_STAT_CHGCURRI	(1 << 11)
-#define MC13783_INT_STAT_BPONI		(1 << 12)
-#define MC13783_INT_STAT_LOBATLI	(1 << 13)
-#define MC13783_INT_STAT_LOBATHI	(1 << 14)
-#define MC13783_INT_STAT_UDPI		(1 << 15)
-#define MC13783_INT_STAT_USBI		(1 << 16)
-#define MC13783_INT_STAT_IDI		(1 << 19)
-#define MC13783_INT_STAT_Unused		(1 << 20)
-#define MC13783_INT_STAT_SE1I		(1 << 21)
-#define MC13783_INT_STAT_CKDETI		(1 << 22)
-#define MC13783_INT_STAT_UDMI		(1 << 23)
-
-/*
- * Interrupt Mask
- */
-#define MC13783_INT_MASK_ADCDONEM	(1 << 0)
-#define MC13783_INT_MASK_ADCBISDONEM	(1 << 1)
-#define MC13783_INT_MASK_TSM		(1 << 2)
-#define MC13783_INT_MASK_WHIGHM		(1 << 3)
-#define MC13783_INT_MASK_WLOWM		(1 << 4)
-#define MC13783_INT_MASK_CHGDETM	(1 << 6)
-#define MC13783_INT_MASK_CHGOVM		(1 << 7)
-#define MC13783_INT_MASK_CHGREVM	(1 << 8)
-#define MC13783_INT_MASK_CHGSHORTM	(1 << 9)
-#define MC13783_INT_MASK_CCCVM		(1 << 10)
-#define MC13783_INT_MASK_CHGCURRM	(1 << 11)
-#define MC13783_INT_MASK_BPONM		(1 << 12)
-#define MC13783_INT_MASK_LOBATLM	(1 << 13)
-#define MC13783_INT_MASK_LOBATHM	(1 << 14)
-#define MC13783_INT_MASK_UDPM		(1 << 15)
-#define MC13783_INT_MASK_USBM		(1 << 16)
-#define MC13783_INT_MASK_IDM		(1 << 19)
-#define MC13783_INT_MASK_SE1M		(1 << 21)
-#define MC13783_INT_MASK_CKDETM		(1 << 22)
-
 /*
  * Reg Regulator Mode 0
  */
@@ -284,113 +207,14 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_SWCTRL_SW3_STBY		(1 << 21)
 #define MC13783_SWCTRL_SW3_MODE		(1 << 22)
 
-/*
- * ADC/Touch
- */
-#define MC13783_ADC0_LICELLCON		(1 << 0)
-#define MC13783_ADC0_CHRGICON		(1 << 1)
-#define MC13783_ADC0_BATICON		(1 << 2)
-#define MC13783_ADC0_RTHEN 		(1 << 3)
-#define MC13783_ADC0_DTHEN		(1 << 4)
-#define MC13783_ADC0_UIDEN		(1 << 5)
-#define MC13783_ADC0_ADOUTEN 		(1 << 6)
-#define MC13783_ADC0_ADOUTPER		(1 << 7)
-#define MC13783_ADC0_ADREFEN		(1 << 10)
-#define MC13783_ADC0_ADREFMODE		(1 << 11)
-#define MC13783_ADC0_TSMOD0		(1 << 12)
-#define MC13783_ADC0_TSMOD1		(1 << 13)
-#define MC13783_ADC0_TSMOD2		(1 << 14)
-#define MC13783_ADC0_CHRGRAWDIV		(1 << 15)
-#define MC13783_ADC0_ADINC1		(1 << 16)
-#define MC13783_ADC0_ADINC2		(1 << 17)
-#define MC13783_ADC0_WCOMP		(1 << 18)
-#define MC13783_ADC0_ADCBIS0		(1 << 23)
-
-#define MC13783_ADC1_ADEN		(1 << 0)
-#define MC13783_ADC1_RAND		(1 << 1)
-#define MC13783_ADC1_ADSEL		(1 << 3)
-#define MC13783_ADC1_TRIGMASK		(1 << 4)
-#define MC13783_ADC1_ADA10		(1 << 5)
-#define MC13783_ADC1_ADA11		(1 << 6)
-#define MC13783_ADC1_ADA12		(1 << 7)
-#define MC13783_ADC1_ADA20		(1 << 8)
-#define MC13783_ADC1_ADA21		(1 << 9)
-#define MC13783_ADC1_ADA22		(1 << 10)
-#define MC13783_ADC1_ATO0		(1 << 11)
-#define MC13783_ADC1_ATO1		(1 << 12)
-#define MC13783_ADC1_ATO2		(1 << 13)
-#define MC13783_ADC1_ATO3		(1 << 14)
-#define MC13783_ADC1_ATO4		(1 << 15)
-#define MC13783_ADC1_ATO5		(1 << 16)
-#define MC13783_ADC1_ATO6		(1 << 17)
-#define MC13783_ADC1_ATO7		(1 << 18)
-#define MC13783_ADC1_ATOX		(1 << 19)
-#define MC13783_ADC1_ASC		(1 << 20)
-#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
-#define MC13783_ADC1_ADONESHOT		(1 << 22)
-#define MC13783_ADC1_ADCBIS1		(1 << 23)
-
-#define MC13783_ADC1_CHAN0_SHIFT	5
-#define MC13783_ADC1_CHAN1_SHIFT	8
-
-#define MC13783_ADC2_ADD10		(1 << 2)
-#define MC13783_ADC2_ADD11		(1 << 3)
-#define MC13783_ADC2_ADD12		(1 << 4)
-#define MC13783_ADC2_ADD13		(1 << 5)
-#define MC13783_ADC2_ADD14		(1 << 6)
-#define MC13783_ADC2_ADD15		(1 << 7)
-#define MC13783_ADC2_ADD16		(1 << 8)
-#define MC13783_ADC2_ADD17		(1 << 9)
-#define MC13783_ADC2_ADD18		(1 << 10)
-#define MC13783_ADC2_ADD19		(1 << 11)
-#define MC13783_ADC2_ADD20		(1 << 14)
-#define MC13783_ADC2_ADD21		(1 << 15)
-#define MC13783_ADC2_ADD22		(1 << 16)
-#define MC13783_ADC2_ADD23		(1 << 17)
-#define MC13783_ADC2_ADD24		(1 << 18)
-#define MC13783_ADC2_ADD25		(1 << 19)
-#define MC13783_ADC2_ADD26		(1 << 20)
-#define MC13783_ADC2_ADD27		(1 << 21)
-#define MC13783_ADC2_ADD28		(1 << 22)
-#define MC13783_ADC2_ADD29		(1 << 23)
+static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset, u32 mask, u32 val)
+{
+	int ret;
+	mc13783_lock(mc13783);
+	ret = mc13783_reg_rmw(mc13783, offset, mask, val);
+	mc13783_unlock(mc13783);
 
-#define MC13783_ADC3_WHIGH0		(1 << 0)
-#define MC13783_ADC3_WHIGH1		(1 << 1)
-#define MC13783_ADC3_WHIGH2		(1 << 2)
-#define MC13783_ADC3_WHIGH3		(1 << 3)
-#define MC13783_ADC3_WHIGH4		(1 << 4)
-#define MC13783_ADC3_WHIGH5		(1 << 5)
-#define MC13783_ADC3_ICID0		(1 << 6)
-#define MC13783_ADC3_ICID1		(1 << 7)
-#define MC13783_ADC3_ICID2		(1 << 8)
-#define MC13783_ADC3_WLOW0		(1 << 9)
-#define MC13783_ADC3_WLOW1		(1 << 10)
-#define MC13783_ADC3_WLOW2		(1 << 11)
-#define MC13783_ADC3_WLOW3		(1 << 12)
-#define MC13783_ADC3_WLOW4		(1 << 13)
-#define MC13783_ADC3_WLOW5		(1 << 14)
-#define MC13783_ADC3_ADCBIS2		(1 << 23)
-
-#define MC13783_ADC4_ADDBIS10		(1 << 2)
-#define MC13783_ADC4_ADDBIS11		(1 << 3)
-#define MC13783_ADC4_ADDBIS12		(1 << 4)
-#define MC13783_ADC4_ADDBIS13		(1 << 5)
-#define MC13783_ADC4_ADDBIS14		(1 << 6)
-#define MC13783_ADC4_ADDBIS15		(1 << 7)
-#define MC13783_ADC4_ADDBIS16		(1 << 8)
-#define MC13783_ADC4_ADDBIS17		(1 << 9)
-#define MC13783_ADC4_ADDBIS18		(1 << 10)
-#define MC13783_ADC4_ADDBIS19		(1 << 11)
-#define MC13783_ADC4_ADDBIS20		(1 << 14)
-#define MC13783_ADC4_ADDBIS21		(1 << 15)
-#define MC13783_ADC4_ADDBIS22		(1 << 16)
-#define MC13783_ADC4_ADDBIS23		(1 << 17)
-#define MC13783_ADC4_ADDBIS24		(1 << 18)
-#define MC13783_ADC4_ADDBIS25		(1 << 19)
-#define MC13783_ADC4_ADDBIS26		(1 << 20)
-#define MC13783_ADC4_ADDBIS27		(1 << 21)
-#define MC13783_ADC4_ADDBIS28		(1 << 22)
-#define MC13783_ADC4_ADDBIS29		(1 << 23)
+	return ret;
+}
 
 #endif /* __LINUX_MFD_MC13783_PRIV_H */
-
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index b3a2a72..910d5cf 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -1,28 +1,52 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
+#ifndef __LINUX_MFD_MC13783_H
+#define __LINUX_MFD_MC13783_H
 
-#ifndef __INCLUDE_LINUX_MFD_MC13783_H
-#define __INCLUDE_LINUX_MFD_MC13783_H
+#include <linux/interrupt.h>
 
 struct mc13783;
+
+void mc13783_lock(struct mc13783 *mc13783);
+void mc13783_unlock(struct mc13783 *mc13783);
+
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val);
+
+int mc13783_irq_request(struct mc13783 *mc13783, unsigned int irq,
+		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
+		const char *name, void *dev);
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
+		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
+		const char *name, void *dev);
+int mc13783_irq_free(struct mc13783 *mc13783, unsigned int irq, void *dev);
+int mc13783_ackirq(struct mc13783 *mc13783, unsigned int irq);
+
+int mc13783_mask(struct mc13783 *mc13783, unsigned int irq);
+int mc13783_unmask(struct mc13783 *mc13783, unsigned int irq);
+
+#define MC13783_ADC0		43
+#define MC13783_ADC0_ADREFEN		(1 << 10)
+#define MC13783_ADC0_ADREFMODE		(1 << 11)
+#define MC13783_ADC0_TSMOD0		(1 << 12)
+#define MC13783_ADC0_TSMOD1		(1 << 13)
+#define MC13783_ADC0_TSMOD2		(1 << 14)
+#define MC13783_ADC0_ADINC1		(1 << 16)
+#define MC13783_ADC0_ADINC2		(1 << 17)
+
+#define MC13783_ADC0_TSMOD_MASK		(MC13783_ADC0_TSMOD0 | \
+					MC13783_ADC0_TSMOD1 | \
+					MC13783_ADC0_TSMOD2)
+
+/* to be cleaned up */
 struct regulator_init_data;
 
 struct mc13783_regulator_init_data {
@@ -30,23 +54,30 @@ struct mc13783_regulator_init_data {
 	struct regulator_init_data *init_data;
 };
 
-struct mc13783_platform_data {
-	struct mc13783_regulator_init_data *regulators;
+struct mc13783_regulator_platform_data {
 	int num_regulators;
-	unsigned int flags;
+	struct mc13783_regulator_init_data *regulators;
 };
 
-/* mc13783_platform_data flags */
+struct mc13783_platform_data {
+	int num_regulators;
+	struct mc13783_regulator_init_data *regulators;
+
 #define MC13783_USE_TOUCHSCREEN (1 << 0)
 #define MC13783_USE_CODEC	(1 << 1)
 #define MC13783_USE_ADC		(1 << 2)
 #define MC13783_USE_RTC		(1 << 3)
 #define MC13783_USE_REGULATOR	(1 << 4)
+	unsigned int flags;
+};
+
+#define MC13783_ADC_MODE_TS		1
+#define MC13783_ADC_MODE_SINGLE_CHAN	2
+#define MC13783_ADC_MODE_MULT_CHAN	3
 
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 
 #define	MC13783_SW_SW1A		0
 #define	MC13783_SW_SW1B		1
@@ -80,5 +111,46 @@ void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 #define	MC13783_REGU_V3		29
 #define	MC13783_REGU_V4		30
 
-#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
+#define MC13783_IRQ_ADCDONE	0
+#define MC13783_IRQ_ADCBISDONE	1
+#define MC13783_IRQ_TS		2
+#define MC13783_IRQ_WHIGH	3
+#define MC13783_IRQ_WLOW	4
+#define MC13783_IRQ_CHGDET	6
+#define MC13783_IRQ_CHGOV	7
+#define MC13783_IRQ_CHGREV	8
+#define MC13783_IRQ_CHGSHORT	9
+#define MC13783_IRQ_CCCV	10
+#define MC13783_IRQ_CHGCURR	11
+#define MC13783_IRQ_BPON	12
+#define MC13783_IRQ_LOBATL	13
+#define MC13783_IRQ_LOBATH	14
+#define MC13783_IRQ_UDP		15
+#define MC13783_IRQ_USB		16
+#define MC13783_IRQ_ID		19
+#define MC13783_IRQ_SE1		21
+#define MC13783_IRQ_CKDET	22
+#define MC13783_IRQ_UDM		23
+#define MC13783_IRQ_1HZ		24
+#define MC13783_IRQ_TODA	25
+#define MC13783_IRQ_ONOFD1	27
+#define MC13783_IRQ_ONOFD2	28
+#define MC13783_IRQ_ONOFD3	29
+#define MC13783_IRQ_SYSRST	30
+#define MC13783_IRQ_RTCRST	31
+#define MC13783_IRQ_PC		32
+#define MC13783_IRQ_WARM	33
+#define MC13783_IRQ_MEMHLD	34
+#define MC13783_IRQ_PWRRDY	35
+#define MC13783_IRQ_THWARNL	36
+#define MC13783_IRQ_THWARNH	37
+#define MC13783_IRQ_CLK		38
+#define MC13783_IRQ_SEMAF	39
+#define MC13783_IRQ_MC2B	41
+#define MC13783_IRQ_HSDET	42
+#define MC13783_IRQ_HSL		43
+#define MC13783_IRQ_ALSPTH	44
+#define MC13783_IRQ_AHSSHORT	45
+#define MC13783_NUM_IRQ		46
 
+#endif /* __LINUX_MFD_MC13783_H */
-- 
1.6.5


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
  2009-10-23 20:38 [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
@ 2009-10-24  8:35   ` Uwe Kleine-König
  2009-11-01 21:06 ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  2009-11-02 11:51 ` Mark Brown
  2 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-10-24  8:35 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-arm-kernel, Sascha Hauer, Valentin Longchamp,
	Paul Gortmaker, Alessandro Zummo, rtc-linux

This driver provides support for the RTC part integrated into the
Freescale MC13783 PMIC and bases on patch created earlier by Sascha
Hauer.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: rtc-linux@googlegroups.com
---
Hello,

this patch depends on

	mfd/mc13783: near complete rewrite

sent earlier on lkml[1].  Compared to the earlier version of rtc support
on mc13783 as sent by Sascha, this driver got reset detection and
therefore depends on the patch above.

A tree runnable on Phytec's PCM038 is available in my git tree

	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783

.  (Maybe I will rewrite these commits, so please expect it might change
in a non-fast-forward manner.)

Best regards
Uwe

[1] http://mid.gmane.org/1256330323-13300-1-git-send-email-u.kleine-koenig@pengutronix.de

 drivers/rtc/Kconfig       |    6 +
 drivers/rtc/Makefile      |    1 +
 drivers/rtc/rtc-mc13783.c |  262 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-mc13783.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3c20dae..7fa8db3 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -827,4 +827,10 @@ config RTC_DRV_PCAP
 	  If you say Y here you will get support for the RTC found on
 	  the PCAP2 ASIC used on some Motorola phones.
 
+config RTC_DRV_MC13783
+	depends on MFD_MC13783
+	tristate "Freescale MC13783 RTC"
+	help
+	  This enables support for the Freescale MC13783 PMIC RTC
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index aa3fbd5..f4d01ba 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o
 obj-$(CONFIG_RTC_MXC)		+= rtc-mxc.o
 obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MC13783)	+= rtc-mc13783.o
 obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
 obj-$(CONFIG_RTC_DRV_OMAP)	+= rtc-omap.o
 obj-$(CONFIG_RTC_DRV_PCAP)	+= rtc-pcap.o
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
new file mode 100644
index 0000000..7a1019e
--- /dev/null
+++ b/drivers/rtc/rtc-mc13783.c
@@ -0,0 +1,262 @@
+/*
+ * Real Time Clock driver for Freescale MC13783 PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13783.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13783-rtc"
+
+#define MC13783_RTCTOD	20
+#define MC13783_RTCTODA	21
+#define MC13783_RTCDAY	22
+#define MC13783_RTCDAYA	23
+
+struct mc13783_rtc {
+	struct rtc_device *rtc;
+	struct mc13783 *mc13783;
+	int valid;
+};
+
+static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days1, days2;
+	unsigned long s1970;
+	int ret;
+
+	mc13783_lock(priv->mc13783);
+
+	if (!priv->valid) {
+		ret = -ENODATA;
+		goto out;
+	}
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	if (ret)
+		return ret;
+
+	if (days2 == days1 + 1) {
+		if (seconds >= 86400 / 2)
+			days2 = days1;
+		else
+			days1 = days2;
+	}
+
+	if (days1 != days2)
+		return -EIO;
+
+	s1970 = days1 * 86400 + seconds;
+
+	rtc_time_to_tm(s1970, tm);
+
+	return rtc_valid_tm(tm);
+}
+
+static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days;
+	int ret;
+
+	seconds = secs % 86400;
+	days = secs / 86400;
+
+	mc13783_lock(priv->mc13783);
+
+	/*
+	 * first write seconds=0 to prevent a day switch between writing days
+	 * and seconds below
+	 */
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
+out:
+	priv->valid = !ret;
+
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static irqreturn_t mc13783_rtc_update_handler(struct mc13783 *mc13783,
+		unsigned int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+
+	dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+	rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+	mc13783_ackirq(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int mc13783_rtc_update_irq_enable(struct device *dev,
+		unsigned int enabled)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	int ret = -ENODATA;
+
+	mc13783_lock(priv->mc13783);
+	if (!priv->valid)
+		goto out;
+
+	ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
+			MC13783_IRQ_1HZ);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static const struct rtc_class_ops mc13783_rtc_ops = {
+	.read_time = mc13783_rtc_read_time,
+	.set_mmss = mc13783_rtc_set_mmss,
+	.update_irq_enable = mc13783_rtc_update_irq_enable,
+};
+
+static irqreturn_t mc13783_rtc_reset_handler(struct mc13783 *mc13783,
+		unsigned int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+
+	dev_dbg(&priv->rtc->dev, "RTCRST\n");
+	priv->valid = 0;
+
+	mc13783_mask(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct mc13783_rtc *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+	platform_set_drvdata(pdev, priv);
+
+	priv->valid = 1;
+
+	mc13783_lock(priv->mc13783);
+
+	ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
+			mc13783_rtc_reset_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_reset_irq_request;
+
+	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
+			mc13783_rtc_update_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_update_irq_request;
+
+	mc13783_unlock(priv->mc13783);
+
+	priv->rtc = rtc_device_register(pdev->name,
+			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(priv->rtc)) {
+		ret = PTR_ERR(priv->rtc);
+
+		mc13783_lock(priv->mc13783);
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+err_update_irq_request:
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+err_reset_irq_request:
+
+		mc13783_unlock(priv->mc13783);
+
+		platform_set_drvdata(pdev, NULL);
+		kfree(priv);
+	}
+
+	return ret;
+}
+
+static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
+{
+	struct mc13783_rtc *priv = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(priv->rtc);
+
+	mc13783_lock(priv->mc13783);
+
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+
+	mc13783_unlock(priv->mc13783);
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_rtc_driver = {
+	.remove = __devexit_p(mc13783_rtc_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mc13783_rtc_init(void)
+{
+	return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
+}
+module_init(mc13783_rtc_init);
+
+static void __exit mc13783_rtc_exit(void)
+{
+	platform_driver_unregister(&mc13783_rtc_driver);
+}
+module_exit(mc13783_rtc_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
-- 
1.6.5


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
@ 2009-10-24  8:35   ` Uwe Kleine-König
  0 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-10-24  8:35 UTC (permalink / raw)
  To: linux-arm-kernel

This driver provides support for the RTC part integrated into the
Freescale MC13783 PMIC and bases on patch created earlier by Sascha
Hauer.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Uwe Kleine-K?nig <u.kleine-k?nig@pengutronix.de>
Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: rtc-linux at googlegroups.com
---
Hello,

this patch depends on

	mfd/mc13783: near complete rewrite

sent earlier on lkml[1].  Compared to the earlier version of rtc support
on mc13783 as sent by Sascha, this driver got reset detection and
therefore depends on the patch above.

A tree runnable on Phytec's PCM038 is available in my git tree

	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783

.  (Maybe I will rewrite these commits, so please expect it might change
in a non-fast-forward manner.)

Best regards
Uwe

[1] http://mid.gmane.org/1256330323-13300-1-git-send-email-u.kleine-koenig at pengutronix.de

 drivers/rtc/Kconfig       |    6 +
 drivers/rtc/Makefile      |    1 +
 drivers/rtc/rtc-mc13783.c |  262 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-mc13783.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3c20dae..7fa8db3 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -827,4 +827,10 @@ config RTC_DRV_PCAP
 	  If you say Y here you will get support for the RTC found on
 	  the PCAP2 ASIC used on some Motorola phones.
 
+config RTC_DRV_MC13783
+	depends on MFD_MC13783
+	tristate "Freescale MC13783 RTC"
+	help
+	  This enables support for the Freescale MC13783 PMIC RTC
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index aa3fbd5..f4d01ba 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o
 obj-$(CONFIG_RTC_MXC)		+= rtc-mxc.o
 obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MC13783)	+= rtc-mc13783.o
 obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
 obj-$(CONFIG_RTC_DRV_OMAP)	+= rtc-omap.o
 obj-$(CONFIG_RTC_DRV_PCAP)	+= rtc-pcap.o
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
new file mode 100644
index 0000000..7a1019e
--- /dev/null
+++ b/drivers/rtc/rtc-mc13783.c
@@ -0,0 +1,262 @@
+/*
+ * Real Time Clock driver for Freescale MC13783 PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13783.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13783-rtc"
+
+#define MC13783_RTCTOD	20
+#define MC13783_RTCTODA	21
+#define MC13783_RTCDAY	22
+#define MC13783_RTCDAYA	23
+
+struct mc13783_rtc {
+	struct rtc_device *rtc;
+	struct mc13783 *mc13783;
+	int valid;
+};
+
+static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days1, days2;
+	unsigned long s1970;
+	int ret;
+
+	mc13783_lock(priv->mc13783);
+
+	if (!priv->valid) {
+		ret = -ENODATA;
+		goto out;
+	}
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	if (ret)
+		return ret;
+
+	if (days2 == days1 + 1) {
+		if (seconds >= 86400 / 2)
+			days2 = days1;
+		else
+			days1 = days2;
+	}
+
+	if (days1 != days2)
+		return -EIO;
+
+	s1970 = days1 * 86400 + seconds;
+
+	rtc_time_to_tm(s1970, tm);
+
+	return rtc_valid_tm(tm);
+}
+
+static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days;
+	int ret;
+
+	seconds = secs % 86400;
+	days = secs / 86400;
+
+	mc13783_lock(priv->mc13783);
+
+	/*
+	 * first write seconds=0 to prevent a day switch between writing days
+	 * and seconds below
+	 */
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
+out:
+	priv->valid = !ret;
+
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static irqreturn_t mc13783_rtc_update_handler(struct mc13783 *mc13783,
+		unsigned int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+
+	dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+	rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+	mc13783_ackirq(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int mc13783_rtc_update_irq_enable(struct device *dev,
+		unsigned int enabled)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	int ret = -ENODATA;
+
+	mc13783_lock(priv->mc13783);
+	if (!priv->valid)
+		goto out;
+
+	ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
+			MC13783_IRQ_1HZ);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static const struct rtc_class_ops mc13783_rtc_ops = {
+	.read_time = mc13783_rtc_read_time,
+	.set_mmss = mc13783_rtc_set_mmss,
+	.update_irq_enable = mc13783_rtc_update_irq_enable,
+};
+
+static irqreturn_t mc13783_rtc_reset_handler(struct mc13783 *mc13783,
+		unsigned int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+
+	dev_dbg(&priv->rtc->dev, "RTCRST\n");
+	priv->valid = 0;
+
+	mc13783_mask(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct mc13783_rtc *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+	platform_set_drvdata(pdev, priv);
+
+	priv->valid = 1;
+
+	mc13783_lock(priv->mc13783);
+
+	ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
+			mc13783_rtc_reset_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_reset_irq_request;
+
+	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
+			mc13783_rtc_update_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_update_irq_request;
+
+	mc13783_unlock(priv->mc13783);
+
+	priv->rtc = rtc_device_register(pdev->name,
+			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(priv->rtc)) {
+		ret = PTR_ERR(priv->rtc);
+
+		mc13783_lock(priv->mc13783);
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+err_update_irq_request:
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+err_reset_irq_request:
+
+		mc13783_unlock(priv->mc13783);
+
+		platform_set_drvdata(pdev, NULL);
+		kfree(priv);
+	}
+
+	return ret;
+}
+
+static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
+{
+	struct mc13783_rtc *priv = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(priv->rtc);
+
+	mc13783_lock(priv->mc13783);
+
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+
+	mc13783_unlock(priv->mc13783);
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_rtc_driver = {
+	.remove = __devexit_p(mc13783_rtc_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mc13783_rtc_init(void)
+{
+	return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
+}
+module_init(mc13783_rtc_init);
+
+static void __exit mc13783_rtc_exit(void)
+{
+	platform_driver_unregister(&mc13783_rtc_driver);
+}
+module_exit(mc13783_rtc_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH] [RTC] Add Freescale MC13783 RTC driver
  2009-10-24  8:35   ` Uwe Kleine-König
@ 2009-11-01 20:34     ` Uwe Kleine-König
  -1 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-01 20:34 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-arm-kernel, Sascha Hauer, Valentin Longchamp,
	Paul Gortmaker, Alessandro Zummo, rtc-linux

Hello,

On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-König wrote:
> This driver provides support for the RTC part integrated into the
> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
> Hauer.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: rtc-linux@googlegroups.com
> ---
> Hello,
> 
> this patch depends on
> 
> 	mfd/mc13783: near complete rewrite
> 
> sent earlier on lkml[1].  Compared to the earlier version of rtc support
> on mc13783 as sent by Sascha, this driver got reset detection and
> therefore depends on the patch above.
> 
> A tree runnable on Phytec's PCM038 is available in my git tree
> 
> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
> 
> .  (Maybe I will rewrite these commits, so please expect it might change
> in a non-fast-forward manner.)

Valentin, could you already test this?  Any comments by the others?

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
@ 2009-11-01 20:34     ` Uwe Kleine-König
  0 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-01 20:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-K?nig wrote:
> This driver provides support for the RTC part integrated into the
> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
> Hauer.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Uwe Kleine-K?nig <u.kleine-k?nig@pengutronix.de>
> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: rtc-linux at googlegroups.com
> ---
> Hello,
> 
> this patch depends on
> 
> 	mfd/mc13783: near complete rewrite
> 
> sent earlier on lkml[1].  Compared to the earlier version of rtc support
> on mc13783 as sent by Sascha, this driver got reset detection and
> therefore depends on the patch above.
> 
> A tree runnable on Phytec's PCM038 is available in my git tree
> 
> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
> 
> .  (Maybe I will rewrite these commits, so please expect it might change
> in a non-fast-forward manner.)

Valentin, could you already test this?  Any comments by the others?

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-K?nig            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-10-23 20:38 [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  2009-10-24  8:35   ` Uwe Kleine-König
@ 2009-11-01 21:06 ` Uwe Kleine-König
  2009-11-02 11:51 ` Mark Brown
  2 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-01 21:06 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Samuel Ortiz

Hello,

On Fri, Oct 23, 2009 at 10:38:43PM +0200, Uwe Kleine-König wrote:
> This fixes several things while still providing the old API:
> 
>  - simplify and fix locking
>  - better error handling
>  - don't ack all irqs making it impossible to detect a reset of the
>    rtc
>  - use a timeout variant to wait for completion of ADC conversion
>  - provide platform-data to regulator subdevice (This allows making
>    struct mc13783 opaque for other drivers after the regulator driver is
>    updated to use its platform_data.)
>  - expose all interrupts
>  - use threaded irq
> 
> After all users in mainline are converted to the new API, some things
> (e.g. mc13783-private.h) can go away.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---
>  drivers/mfd/mc13783-core.c          |  789 ++++++++++++++++++++++++-----------
>  include/linux/mfd/mc13783-private.h |  208 +---------
>  include/linux/mfd/mc13783.h         |  122 +++++--
>  3 files changed, 651 insertions(+), 468 deletions(-)
I havn't received feedback yet for this patch.

I'd like to start updating the regulator driver, but only when I now my
updates here are considered sensible.

As this is a rewrite, reading the patch might not be that easy.  So if
you only want to see the new version, you can find it at

	http://git.pengutronix.de/?p=ukl/linux-2.6.git;a=blob;f=drivers/mfd/mc13783-core.c;h=63a5104bf2ea83be817587f83a53beaabd418a6b;hb=e49c74fa5001b54632b704deb837e98bc5b307c0

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-10-23 20:38 [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  2009-10-24  8:35   ` Uwe Kleine-König
  2009-11-01 21:06 ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
@ 2009-11-02 11:51 ` Mark Brown
  2009-11-02 13:58   ` Uwe Kleine-König
  2009-11-02 20:56   ` [PATCH] mfd/mc13783: change type of irq handlers to irq_handler_t Uwe Kleine-König
  2 siblings, 2 replies; 40+ messages in thread
From: Mark Brown @ 2009-11-02 11:51 UTC (permalink / raw)
  To: Uwe Kleine-K??nig; +Cc: linux-kernel, Sascha Hauer, Samuel Ortiz

On Fri, Oct 23, 2009 at 10:38:43PM +0200, Uwe Kleine-K??nig wrote:

> -int mc13783_register_irq(struct mc13783 *mc13783, int irq,
> -		void (*handler) (int, void *), void *data)
> +int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
> +		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
> +		const char *name, void *dev)

If you're changing the signature of the IRQ handler functions it'd be
nice to change them to irq_handler_t - that way it'll be much easier to
transition the driver to using genirq in future since it should end up
being possible to just stub out the Atlas-specific calls in the header
with calls to the standard IRQ functions when the core is transitioned,
reducing cross-tree issues.

This might create issues with a request_nounmask() function, though a
request plus mask is probably enough - I guess you're using this for
your the RTC driver in which case a spurious periodic interrupt is
unlikely to be an issue.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-02 11:51 ` Mark Brown
@ 2009-11-02 13:58   ` Uwe Kleine-König
  2009-11-02 14:09     ` Mark Brown
  2009-11-02 20:56   ` [PATCH] mfd/mc13783: change type of irq handlers to irq_handler_t Uwe Kleine-König
  1 sibling, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-02 13:58 UTC (permalink / raw)
  To: Mark Brown; +Cc: linux-kernel, Sascha Hauer, Samuel Ortiz

Hello Mark,

On Mon, Nov 02, 2009 at 11:51:01AM +0000, Mark Brown wrote:
> On Fri, Oct 23, 2009 at 10:38:43PM +0200, Uwe Kleine-K??nig wrote:
> 
> > -int mc13783_register_irq(struct mc13783 *mc13783, int irq,
> > -		void (*handler) (int, void *), void *data)
> > +int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
> > +		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
> > +		const char *name, void *dev)
> 
> If you're changing the signature of the IRQ handler functions it'd be
> nice to change them to irq_handler_t - that way it'll be much easier to
> transition the driver to using genirq in future since it should end up
> being possible to just stub out the Atlas-specific calls in the header
> with calls to the standard IRQ functions when the core is transitioned,
> reducing cross-tree issues.
OK, will do.
 
> This might create issues with a request_nounmask() function, though a
> request plus mask is probably enough - I guess you're using this for
> your the RTC driver in which case a spurious periodic interrupt is
> unlikely to be an issue.
Yes, I use it to simplify things in the RTC driver a bit.  I register
the 1HZ irq unconditionally and only track if it's masked or not.  This way
I can save some case discrimination.

Best regards and thaks for your comments
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-02 13:58   ` Uwe Kleine-König
@ 2009-11-02 14:09     ` Mark Brown
  2009-11-02 14:32       ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Mark Brown @ 2009-11-02 14:09 UTC (permalink / raw)
  To: Uwe Kleine-K?nig; +Cc: linux-kernel, Sascha Hauer, Samuel Ortiz

On Mon, Nov 02, 2009 at 02:58:27PM +0100, Uwe Kleine-K?nig wrote:
> On Mon, Nov 02, 2009 at 11:51:01AM +0000, Mark Brown wrote:

> > This might create issues with a request_nounmask() function, though a
> > request plus mask is probably enough - I guess you're using this for
> > your the RTC driver in which case a spurious periodic interrupt is
> > unlikely to be an issue.

> Yes, I use it to simplify things in the RTC driver a bit.  I register
> the 1HZ irq unconditionally and only track if it's masked or not.  This way
> I can save some case discrimination.

What I've done for wm8350 is just request the IRQ then immediately mask
it and not worry about a periodic notification coming in in the gap -
reporting the extra periodic notification is vanishingly unlikely to do
any harm in the case that the race does crop up.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-02 14:09     ` Mark Brown
@ 2009-11-02 14:32       ` Uwe Kleine-König
  2009-11-02 15:12         ` Mark Brown
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-02 14:32 UTC (permalink / raw)
  To: Mark Brown; +Cc: linux-kernel, Sascha Hauer, Samuel Ortiz

Hi Mark,

On Mon, Nov 02, 2009 at 02:09:25PM +0000, Mark Brown wrote:
> On Mon, Nov 02, 2009 at 02:58:27PM +0100, Uwe Kleine-K?nig wrote:
> > On Mon, Nov 02, 2009 at 11:51:01AM +0000, Mark Brown wrote:
> 
> > > This might create issues with a request_nounmask() function, though a
> > > request plus mask is probably enough - I guess you're using this for
> > > your the RTC driver in which case a spurious periodic interrupt is
> > > unlikely to be an issue.
> 
> > Yes, I use it to simplify things in the RTC driver a bit.  I register
> > the 1HZ irq unconditionally and only track if it's masked or not.  This way
> > I can save some case discrimination.
> 
> What I've done for wm8350 is just request the IRQ then immediately mask
> it and not worry about a periodic notification coming in in the gap -
> reporting the extra periodic notification is vanishingly unlikely to do
> any harm in the case that the race does crop up.
On mc13783 the 1HZ irq triggers immediately after unmasking provided
that it has power for more than 1s.  Because of locking the irq isn't
recognized as 1HZ as the irqhandler tries to take the lock that the rtc
probe function is holding and only releasing after the irq is masked.
So for me it's just a spurious irq.

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-02 14:32       ` Uwe Kleine-König
@ 2009-11-02 15:12         ` Mark Brown
  0 siblings, 0 replies; 40+ messages in thread
From: Mark Brown @ 2009-11-02 15:12 UTC (permalink / raw)
  To: Uwe Kleine-König; +Cc: linux-kernel, Sascha Hauer, Samuel Ortiz

On Mon, Nov 02, 2009 at 03:32:29PM +0100, Uwe Kleine-König wrote:

> On mc13783 the 1HZ irq triggers immediately after unmasking provided
> that it has power for more than 1s.  Because of locking the irq isn't
> recognized as 1HZ as the irqhandler tries to take the lock that the rtc
> probe function is holding and only releasing after the irq is masked.
> So for me it's just a spurious irq.

Hrm, I can't see that in the RTC driver code?  I'd have expected that if
there is a lock it'd ensure that the handler is in place before the
interrupt is unmasked.

Looking at the IRQ handler I do notice that you require all the handlers
to explicitly ack their interrupts - are there circumstances where a
handler wouldn't want to do the ack?  If there aren't it'd seem like
it'd save work to have the core driver do the acks.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] mfd/mc13783: change type of irq handlers to irq_handler_t
  2009-11-02 11:51 ` Mark Brown
  2009-11-02 13:58   ` Uwe Kleine-König
@ 2009-11-02 20:56   ` Uwe Kleine-König
  2009-11-03 19:31     ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  1 sibling, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-02 20:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: Mark Brown, Sascha Hauer, Samuel Ortiz

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
Hello,

now I changed the signature of the mc13783 irq handlers to irq_handler_t
as suggested by Mark Brown.

For easier review you can find the incremental diff below.  If this is
considered OK, I'd squash it into the original commits (all but the
drivers/rtc/rtc-mc13783.c bits into "mfd/mc13783: near complete
rewrite", and drivers/rtc/rtc-mc13783.c into "Add Freescale MC13783 RTC
driver").

The patch is that big as I had to change the irq variable to int (from
unsigned) and so had to update some conditions to check the irq to be in
range.  The last few hunks in drivers/mfd/mc13783-core.c are necessary
to make the mc13783 struct available to the irq handler.

Best regards
Uwe

 drivers/mfd/mc13783-core.c          |   65 ++++++++++++++++++++---------------
 drivers/rtc/rtc-mc13783.c           |    8 ++--
 include/linux/mfd/mc13783-private.h |    3 +-
 include/linux/mfd/mc13783.h         |   18 ++++-----
 4 files changed, 50 insertions(+), 44 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index 63a5104..90644d9 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -268,13 +268,16 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
 }
 EXPORT_SYMBOL(mc13783_reg_rmw);
 
-int mc13783_mask(struct mc13783 *mc13783, unsigned int irq)
+int mc13783_mask(struct mc13783 *mc13783, int irq)
 {
 	int ret;
 	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
 	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
 	u32 mask;
 
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
+
 	ret = mc13783_reg_read(mc13783, offmask, &mask);
 	if (ret)
 		return ret;
@@ -287,13 +290,16 @@ int mc13783_mask(struct mc13783 *mc13783, unsigned int irq)
 }
 EXPORT_SYMBOL(mc13783_mask);
 
-int mc13783_unmask(struct mc13783 *mc13783, unsigned int irq)
+int mc13783_unmask(struct mc13783 *mc13783, int irq)
 {
 	int ret;
 	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
 	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
 	u32 mask;
 
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
+
 	ret = mc13783_reg_read(mc13783, offmask, &mask);
 	if (ret)
 		return ret;
@@ -306,14 +312,13 @@ int mc13783_unmask(struct mc13783 *mc13783, unsigned int irq)
 }
 EXPORT_SYMBOL(mc13783_unmask);
 
-int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
-		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
-		const char *name, void *dev)
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
 {
 	BUG_ON(!mutex_is_locked(&mc13783->lock));
 	BUG_ON(!handler);
 
-	if (irq >= MC13783_NUM_IRQ)
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
 		return -EINVAL;
 
 	if (mc13783->irqhandler[irq])
@@ -326,9 +331,8 @@ int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
 }
 EXPORT_SYMBOL(mc13783_irq_request_nounmask);
 
-int mc13783_irq_request(struct mc13783 *mc13783, unsigned int irq,
-		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
-		const char *name, void *dev)
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
 {
 	int ret;
 
@@ -347,12 +351,12 @@ int mc13783_irq_request(struct mc13783 *mc13783, unsigned int irq,
 }
 EXPORT_SYMBOL(mc13783_irq_request);
 
-int mc13783_irq_free(struct mc13783 *mc13783, unsigned int irq, void *dev)
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
 {
 	int ret;
 	BUG_ON(!mutex_is_locked(&mc13783->lock));
 
-	if (irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
+	if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
 			mc13783->irqdata[irq] != dev)
 		return -EINVAL;
 
@@ -367,18 +371,17 @@ int mc13783_irq_free(struct mc13783 *mc13783, unsigned int irq, void *dev)
 }
 EXPORT_SYMBOL(mc13783_irq_free);
 
-static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783,
-		unsigned int irq)
+static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
 {
-	return mc13783->irqhandler[irq](mc13783, irq, mc13783->irqdata[irq]);
+	return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
 }
 
-int mc13783_ackirq(struct mc13783 *mc13783, unsigned int irq)
+int mc13783_ackirq(struct mc13783 *mc13783, int irq)
 {
 	unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
 	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
 
-	BUG_ON(irq >= MC13783_NUM_IRQ);
+	BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
 
 	return mc13783_reg_write(mc13783, offstat, val);
 }
@@ -389,8 +392,7 @@ EXPORT_SYMBOL(mc13783_ackirq);
  * locking: holds mc13783->lock
  */
 static int mc13783_irq_handle(struct mc13783 *mc13783,
-		unsigned int offstat, unsigned int offmask,
-		unsigned int baseirq)
+		unsigned int offstat, unsigned int offmask, int baseirq)
 {
 	u32 stat, mask;
 	int ret = mc13783_reg_read(mc13783, offstat, &stat);
@@ -404,7 +406,7 @@ static int mc13783_irq_handle(struct mc13783 *mc13783,
 		return ret;
 
 	while (stat & ~mask) {
-		unsigned int irq = __ffs(stat & ~mask);
+		int irq = __ffs(stat & ~mask);
 
 		stat &= ~(1 << irq);
 
@@ -454,14 +456,18 @@ static irqreturn_t mc13783_irq_thread(int irq, void *data)
 #define MC13783_ADC1_CHAN0_SHIFT	5
 #define MC13783_ADC1_CHAN1_SHIFT	8
 
-static irqreturn_t mc13783_handler_adcdone(struct mc13783 *mc13783,
-		unsigned int irq, void *data)
+struct mc13783_adcdone_data {
+	struct mc13783 *mc13783;
+	struct completion done;
+};
+
+static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
 {
-	struct completion *done = data;
+	struct mc13783_adcdone_data *adcdone_data = data;
 
-	mc13783_ackirq(mc13783, irq);
+	mc13783_ackirq(adcdone_data->mc13783, irq);
 
-	complete_all(done);
+	complete_all(&adcdone_data->done);
 
 	return IRQ_HANDLED;
 }
@@ -473,8 +479,11 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 {
 	u32 adc0, adc1, old_adc0;
 	int i, ret;
+	struct mc13783_adcdone_data adcdone_data = {
+		.mc13783 = mc13783,
+	};
+	init_completion(&adcdone_data.done);
 
-	DECLARE_COMPLETION_ONSTACK(done);
 	dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
 
 	mc13783_lock(mc13783);
@@ -522,18 +531,18 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 
 	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
 	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
-			mc13783_handler_adcdone, __func__, &done);
+			mc13783_handler_adcdone, __func__, &adcdone_data);
 
 	mc13783_unlock(mc13783);
 
-	ret = wait_for_completion_interruptible_timeout(&done, HZ);
+	ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
 
 	if (!ret)
 		ret = -ETIMEDOUT;
 
 	mc13783_lock(mc13783);
 
-	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &done);
+	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
 
 	if (mode == MC13783_ADC_MODE_TS)
 		/* restore TSMOD */
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
index 7a1019e..1a4615b 100644
--- a/drivers/rtc/rtc-mc13783.c
+++ b/drivers/rtc/rtc-mc13783.c
@@ -114,10 +114,10 @@ out:
 	return ret;
 }
 
-static irqreturn_t mc13783_rtc_update_handler(struct mc13783 *mc13783,
-		unsigned int irq, void *dev)
+static irqreturn_t mc13783_rtc_update_handler(unsigned int irq, void *dev)
 {
 	struct mc13783_rtc *priv = dev;
+	struct mc13783 *mc13783 = priv->mc13783;
 
 	dev_dbg(&priv->rtc->dev, "1HZ\n");
 
@@ -152,10 +152,10 @@ static const struct rtc_class_ops mc13783_rtc_ops = {
 	.update_irq_enable = mc13783_rtc_update_irq_enable,
 };
 
-static irqreturn_t mc13783_rtc_reset_handler(struct mc13783 *mc13783,
-		unsigned int irq, void *dev)
+static irqreturn_t mc13783_rtc_reset_handler(unsigned int irq, void *dev)
 {
 	struct mc13783_rtc *priv = dev;
+	struct mc13783 *mc13783 = priv->mc13783;
 
 	dev_dbg(&priv->rtc->dev, "RTCRST\n");
 	priv->valid = 0;
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index c5eca82..75a1f47 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -33,8 +33,7 @@ struct mc13783 {
 	int irq;
 	int flags;
 
-	irqreturn_t (*irqhandler[MC13783_NUM_IRQ])(struct mc13783 *,
-			unsigned int, void *);
+	irq_handler_t irqhandler[MC13783_NUM_IRQ];
 	void *irqdata[MC13783_NUM_IRQ];
 
 	/* XXX these should go as platformdata to the regulator subdevice */
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index 910d5cf..3568040 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -21,17 +21,15 @@ int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
 int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
 		u32 mask, u32 val);
 
-int mc13783_irq_request(struct mc13783 *mc13783, unsigned int irq,
-		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
-		const char *name, void *dev);
-int mc13783_irq_request_nounmask(struct mc13783 *mc13783, unsigned int irq,
-		irqreturn_t (*handler)(struct mc13783 *, unsigned int, void *),
-		const char *name, void *dev);
-int mc13783_irq_free(struct mc13783 *mc13783, unsigned int irq, void *dev);
-int mc13783_ackirq(struct mc13783 *mc13783, unsigned int irq);
-
-int mc13783_mask(struct mc13783 *mc13783, unsigned int irq);
-int mc13783_unmask(struct mc13783 *mc13783, unsigned int irq);
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
+int mc13783_ackirq(struct mc13783 *mc13783, int irq);
+
+int mc13783_mask(struct mc13783 *mc13783, int irq);
+int mc13783_unmask(struct mc13783 *mc13783, int irq);
 
 #define MC13783_ADC0		43
 #define MC13783_ADC0_ADREFEN		(1 << 10)
-- 
1.6.5


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH] [RTC] Add Freescale MC13783 RTC driver
  2009-10-24  8:35   ` Uwe Kleine-König
@ 2009-11-03 19:20     ` Uwe Kleine-König
  -1 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-03 19:20 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-arm-kernel, Sascha Hauer, Valentin Longchamp,
	Paul Gortmaker, Alessandro Zummo, rtc-linux

Hello,

On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-König wrote:
> This driver provides support for the RTC part integrated into the
> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
> Hauer.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: rtc-linux@googlegroups.com
> ---
> Hello,
> 
> this patch depends on
> 
> 	mfd/mc13783: near complete rewrite
> 
> sent earlier on lkml[1].  Compared to the earlier version of rtc support
> on mc13783 as sent by Sascha, this driver got reset detection and
> therefore depends on the patch above.
> 
> A tree runnable on Phytec's PCM038 is available in my git tree
> 
> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
> 
> .  (Maybe I will rewrite these commits, so please expect it might change
> in a non-fast-forward manner.)
This happend now.  Based on feed-back for the mc13783-core driver I had
to modify the rtc-driver to.  Only the signature of the irq handler
function changed.

I don't consider that change worth to repost, so if you're interested
please check out my branch.

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
@ 2009-11-03 19:20     ` Uwe Kleine-König
  0 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-03 19:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-K?nig wrote:
> This driver provides support for the RTC part integrated into the
> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
> Hauer.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Uwe Kleine-K?nig <u.kleine-k?nig@pengutronix.de>
> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: rtc-linux at googlegroups.com
> ---
> Hello,
> 
> this patch depends on
> 
> 	mfd/mc13783: near complete rewrite
> 
> sent earlier on lkml[1].  Compared to the earlier version of rtc support
> on mc13783 as sent by Sascha, this driver got reset detection and
> therefore depends on the patch above.
> 
> A tree runnable on Phytec's PCM038 is available in my git tree
> 
> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
> 
> .  (Maybe I will rewrite these commits, so please expect it might change
> in a non-fast-forward manner.)
This happend now.  Based on feed-back for the mc13783-core driver I had
to modify the rtc-driver to.  Only the signature of the irq handler
function changed.

I don't consider that change worth to repost, so if you're interested
please check out my branch.

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-K?nig            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] mfd/mc13783: near complete rewrite
  2009-11-02 20:56   ` [PATCH] mfd/mc13783: change type of irq handlers to irq_handler_t Uwe Kleine-König
@ 2009-11-03 19:31     ` Uwe Kleine-König
  2009-11-04 18:35       ` Samuel Ortiz
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-03 19:31 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Mark Brown, Samuel Ortiz

This fixes several things while still providing the old API:

 - simplify and fix locking
 - better error handling
 - don't ack all irqs making it impossible to detect a reset of the
   rtc
 - use a timeout variant to wait for completion of ADC conversion
 - provide platform-data to regulator subdevice (This allows making
   struct mc13783 opaque for other drivers after the regulator driver is
   updated to use its platform_data.)
 - expose all interrupts
 - use threaded irq

After all users in mainline are converted to the new API, some things
(e.g. mc13783-private.h) can go away.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
Hello,

compared to the first submission I squashed in the patch

	mfd/mc13783: change type of irq handlers to irq_handler_t

sent earlier in that thread and fixed a few whitespace issues reported
by checkpatch.pl.

I'd be happy if this patch would make it in now.

Best regards
Uwe

 drivers/mfd/mc13783-core.c          |  796 ++++++++++++++++++++++++-----------
 include/linux/mfd/mc13783-private.h |  208 +---------
 include/linux/mfd/mc13783.h         |  120 +++++--
 3 files changed, 657 insertions(+), 467 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index e354d29..45713a4 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -1,286 +1,588 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * This code is in parts based on wm8350-core.c and pcf50633-core.c
- *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
-
-#include <linux/mfd/mc13783-private.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/core.h>
-#include <linux/spi/spi.h>
-#include <linux/uaccess.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13783-private.h>
 
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_FRAME_MASK	0x00ffffff
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_REG_NUM_SHIFT	0x19
-#define MC13783_WRITE_BIT_SHIFT	31
+#define MC13783_IRQSTAT0	0
+#define MC13783_IRQSTAT0_ADCDONEI	(1 << 0)
+#define MC13783_IRQSTAT0_ADCBISDONEI	(1 << 1)
+#define MC13783_IRQSTAT0_TSI		(1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI		(1 << 3)
+#define MC13783_IRQSTAT0_WLOWI		(1 << 4)
+#define MC13783_IRQSTAT0_CHGDETI	(1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI		(1 << 7)
+#define MC13783_IRQSTAT0_CHGREVI	(1 << 8)
+#define MC13783_IRQSTAT0_CHGSHORTI	(1 << 9)
+#define MC13783_IRQSTAT0_CCCVI		(1 << 10)
+#define MC13783_IRQSTAT0_CHGCURRI	(1 << 11)
+#define MC13783_IRQSTAT0_BPONI		(1 << 12)
+#define MC13783_IRQSTAT0_LOBATLI	(1 << 13)
+#define MC13783_IRQSTAT0_LOBATHI	(1 << 14)
+#define MC13783_IRQSTAT0_UDPI		(1 << 15)
+#define MC13783_IRQSTAT0_USBI		(1 << 16)
+#define MC13783_IRQSTAT0_IDI		(1 << 19)
+#define MC13783_IRQSTAT0_SE1I		(1 << 21)
+#define MC13783_IRQSTAT0_CKDETI		(1 << 22)
+#define MC13783_IRQSTAT0_UDMI		(1 << 23)
+
+#define MC13783_IRQMASK0	1
+#define MC13783_IRQMASK0_ADCDONEM	MC13783_IRQSTAT0_ADCDONEI
+#define MC13783_IRQMASK0_ADCBISDONEM	MC13783_IRQSTAT0_ADCBISDONEI
+#define MC13783_IRQMASK0_TSM		MC13783_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM		MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM		MC13783_IRQSTAT0_WLOWI
+#define MC13783_IRQMASK0_CHGDETM	MC13783_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM		MC13783_IRQSTAT0_CHGOVI
+#define MC13783_IRQMASK0_CHGREVM	MC13783_IRQSTAT0_CHGREVI
+#define MC13783_IRQMASK0_CHGSHORTM	MC13783_IRQSTAT0_CHGSHORTI
+#define MC13783_IRQMASK0_CCCVM		MC13783_IRQSTAT0_CCCVI
+#define MC13783_IRQMASK0_CHGCURRM	MC13783_IRQSTAT0_CHGCURRI
+#define MC13783_IRQMASK0_BPONM		MC13783_IRQSTAT0_BPONI
+#define MC13783_IRQMASK0_LOBATLM	MC13783_IRQSTAT0_LOBATLI
+#define MC13783_IRQMASK0_LOBATHM	MC13783_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM		MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM		MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM		MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M		MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM		MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM		MC13783_IRQSTAT0_UDMI
+
+#define MC13783_IRQSTAT1	3
+#define MC13783_IRQSTAT1_1HZI		(1 << 0)
+#define MC13783_IRQSTAT1_TODAI		(1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I	(1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I	(1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I	(1 << 5)
+#define MC13783_IRQSTAT1_SYSRSTI	(1 << 6)
+#define MC13783_IRQSTAT1_RTCRSTI	(1 << 7)
+#define MC13783_IRQSTAT1_PCI		(1 << 8)
+#define MC13783_IRQSTAT1_WARMI		(1 << 9)
+#define MC13783_IRQSTAT1_MEMHLDI	(1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI	(1 << 11)
+#define MC13783_IRQSTAT1_THWARNLI	(1 << 12)
+#define MC13783_IRQSTAT1_THWARNHI	(1 << 13)
+#define MC13783_IRQSTAT1_CLKI		(1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI		(1 << 15)
+#define MC13783_IRQSTAT1_MC2BI		(1 << 17)
+#define MC13783_IRQSTAT1_HSDETI		(1 << 18)
+#define MC13783_IRQSTAT1_HSLI		(1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI	(1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI	(1 << 21)
+
+#define MC13783_IRQMASK1	4
+#define MC13783_IRQMASK1_1HZM		MC13783_IRQSTAT1_1HZI
+#define MC13783_IRQMASK1_TODAM		MC13783_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M	MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M	MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M	MC13783_IRQSTAT1_ONOFD3I
+#define MC13783_IRQMASK1_SYSRSTM	MC13783_IRQSTAT1_SYSRSTI
+#define MC13783_IRQMASK1_RTCRSTM	MC13783_IRQSTAT1_RTCRSTI
+#define MC13783_IRQMASK1_PCM		MC13783_IRQSTAT1_PCI
+#define MC13783_IRQMASK1_WARMM		MC13783_IRQSTAT1_WARMI
+#define MC13783_IRQMASK1_MEMHLDM	MC13783_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM	MC13783_IRQSTAT1_PWRRDYI
+#define MC13783_IRQMASK1_THWARNLM	MC13783_IRQSTAT1_THWARNLI
+#define MC13783_IRQMASK1_THWARNHM	MC13783_IRQSTAT1_THWARNHI
+#define MC13783_IRQMASK1_CLKM		MC13783_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM		MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM		MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM		MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM		MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM	MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM	MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13783_ADC1		44
+#define MC13783_ADC1_ADEN		(1 << 0)
+#define MC13783_ADC1_RAND		(1 << 1)
+#define MC13783_ADC1_ADSEL		(1 << 3)
+#define MC13783_ADC1_ASC		(1 << 20)
+#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
+
+#define MC13783_NUMREGS 0x3f
+
+void mc13783_lock(struct mc13783 *mc13783)
+{
+	if (!mutex_trylock(&mc13783->lock)) {
+		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
+			__func__, __builtin_return_address(0));
+
+		mutex_lock(&mc13783->lock);
+	}
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13783_lock);
 
-static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+void mc13783_unlock(struct mc13783 *mc13783)
 {
-	struct spi_transfer t = {
-		.tx_buf = (const void *)buf,
-		.rx_buf = buf,
-		.len = len,
-		.cs_change = 0,
-		.delay_usecs = 0,
-	};
-	struct spi_message m;
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+	mutex_unlock(&mc13783->lock);
+}
+EXPORT_SYMBOL(mc13783_unlock);
 
-	spi_message_init(&m);
-	spi_message_add_tail(&t, &m);
-	if (spi_sync(spi, &m) != 0 || m.status != 0)
+static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 *val)
+{
+	if (offset > MC13783_NUMREGS)
 		return -EINVAL;
-	return len - m.actual_length;
+
+	buf[0] = offset << 25;
+
+	memset(t, 0, sizeof(*t));
+
+	t->tx_buf = buf;
+	t->rx_buf = buf;
+	t->len = sizeof(u32);
+
+	return 1;
 }
 
-static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 *val)
 {
-	unsigned int frame = 0;
-	int ret = 0;
+	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
+
+	*val = buf[0] & 0xffffff;
+
+	return 1;
+}
 
-	if (reg_num > MC13783_MAX_REG_NUM)
+static int mc13783_prep_write_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 val)
+{
+	if (offset > MC13783_NUMREGS || val > 0xffffff)
 		return -EINVAL;
 
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
+	buf[0] = 1 << 31 | offset << 25 | val;
 
-	ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	memset(t, 0, sizeof(*t));
 
-	*reg_val = frame & MC13783_FRAME_MASK;
+	t->tx_buf = buf;
+	t->rx_buf = buf;
+	t->len = sizeof(u32);
 
-	return ret;
+	return 1;
 }
 
-static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
+		struct spi_transfer *t, u32 *buf,
+		unsigned int offset, u32 val)
 {
-	unsigned int frame = 0;
+	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
 
-	if (reg_num > MC13783_MAX_REG_NUM)
-		return -EINVAL;
+	return 1;
+}
+
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
+{
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	ret = mc13783_prep_read_transfer(mc13783, &t, &buf, offset, val);
+
+	if (ret < 0)
+		return ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13783->spidev, &m);
 
-	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
-	frame |= reg_val & MC13783_FRAME_MASK;
+	/* error in message.status implies error return from spi_sync */
+	BUG_ON(!ret && m.status);
 
-	return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	if (ret)
+		return ret;
+
+	ret = mc13783_eval_read_transfer(mc13783, &t, &buf, offset, val);
+
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
+
+	return ret < 0 ? ret : 0;
 }
+EXPORT_SYMBOL(mc13783_reg_read);
 
-int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
 {
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
 	int ret;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_read(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
 
-	return ret;
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+	ret = mc13783_prep_write_transfer(mc13783, &t, &buf, offset, val);
+
+	if (ret < 0)
+		return ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13783->spidev, &m);
+
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
+
+	return ret < 0 ? ret : 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_read);
+EXPORT_SYMBOL(mc13783_reg_write);
 
-int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val)
 {
 	int ret;
+	u32 valread;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_write(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	BUG_ON(val & ~mask);
 
-	return ret;
+	ret = mc13783_reg_read(mc13783, offset, &valread);
+	if (ret)
+		return ret;
+
+	valread = (valread & ~mask) | val;
+
+	return mc13783_reg_write(mc13783, offset, valread);
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_write);
+EXPORT_SYMBOL(mc13783_reg_rmw);
 
-/**
- * mc13783_set_bits - Bitmask write
- *
- * @mc13783: Pointer to mc13783 control structure
- * @reg:    Register to access
- * @mask:   Mask of bits to change
- * @val:    Value to set for masked bits
- */
-int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
+int mc13783_mask(struct mc13783 *mc13783, int irq)
 {
-	u32 tmp;
 	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
 
-	mutex_lock(&mc13783->io_lock);
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
 
-	ret = mc13783_read(mc13783, reg, &tmp);
-	tmp = (tmp & ~mask) | val;
-	if (ret == 0)
-		ret = mc13783_write(mc13783, reg, tmp);
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
 
-	mutex_unlock(&mc13783->io_lock);
+	if (mask & irqbit)
+		/* already masked */
+		return 0;
 
-	return ret;
+	return mc13783_reg_write(mc13783, offmask, mask | irqbit);
 }
-EXPORT_SYMBOL_GPL(mc13783_set_bits);
+EXPORT_SYMBOL(mc13783_mask);
 
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data)
+int mc13783_unmask(struct mc13783 *mc13783, int irq)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
+	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
+
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
 		return -EINVAL;
 
-	if (WARN_ON(mc13783->irq_handler[irq].handler))
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	if (!(mask & irqbit))
+		/* already unmasked */
+		return 0;
+
+	return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
+}
+EXPORT_SYMBOL(mc13783_unmask);
+
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
+{
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+	BUG_ON(!handler);
+
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
+
+	if (mc13783->irqhandler[irq])
 		return -EBUSY;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = handler;
-	mc13783->irq_handler[irq].data = data;
-	mutex_unlock(&mc13783->io_lock);
+	mc13783->irqhandler[irq] = handler;
+	mc13783->irqdata[irq] = dev;
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_register_irq);
+EXPORT_SYMBOL(mc13783_irq_request_nounmask);
 
-int mc13783_free_irq(struct mc13783 *mc13783, int irq)
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ)
+	int ret;
+
+	ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
+	if (ret)
+		return ret;
+
+	ret = mc13783_unmask(mc13783, irq);
+	if (ret) {
+		mc13783->irqhandler[irq] = NULL;
+		mc13783->irqdata[irq] = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13783_irq_request);
+
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
+{
+	int ret;
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
+			mc13783->irqdata[irq] != dev)
 		return -EINVAL;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = NULL;
-	mutex_unlock(&mc13783->io_lock);
+	ret = mc13783_mask(mc13783, irq);
+	if (ret)
+		return ret;
+
+	mc13783->irqhandler[irq] = NULL;
+	mc13783->irqdata[irq] = NULL;
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_free_irq);
+EXPORT_SYMBOL(mc13783_irq_free);
 
-static void mc13783_irq_work(struct work_struct *work)
+static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
 {
-	struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
-	int i;
-	unsigned int adc_sts;
-
-	/* check if the adc has finished any completion */
-	mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
-			adc_sts & MC13783_INT_STAT_ADCDONEI);
-
-	if (adc_sts & MC13783_INT_STAT_ADCDONEI)
-		complete_all(&mc13783->adc_done);
-
-	for (i = 0; i < MC13783_NUM_IRQ; i++)
-		if (mc13783->irq_handler[i].handler)
-			mc13783->irq_handler[i].handler(i,
-					mc13783->irq_handler[i].data);
-	enable_irq(mc13783->irq);
+	return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
 }
 
-static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
+int mc13783_ackirq(struct mc13783 *mc13783, int irq)
 {
-	struct mc13783 *mc13783 = dev_id;
+	unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
 
-	disable_irq_nosync(irq);
+	BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
 
-	schedule_work(&mc13783->work);
-	return IRQ_HANDLED;
+	return mc13783_reg_write(mc13783, offstat, val);
 }
+EXPORT_SYMBOL(mc13783_ackirq);
 
-/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
-static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13783->lock
+ */
+static int mc13783_irq_handle(struct mc13783 *mc13783,
+		unsigned int offstat, unsigned int offmask, int baseirq)
 {
-	unsigned int reg_adc0, reg_adc1;
+	u32 stat, mask;
+	int ret = mc13783_reg_read(mc13783, offstat, &stat);
+	int num_handled = 0;
 
-	reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
-			| MC13783_ADC0_TSMOD0;
-	reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+	if (ret)
+		return ret;
+
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	while (stat & ~mask) {
+		int irq = __ffs(stat & ~mask);
+
+		stat &= ~(1 << irq);
+
+		if (likely(mc13783->irqhandler[baseirq + irq])) {
+			irqreturn_t handled;
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+			handled = mc13783_irqhandler(mc13783, baseirq + irq);
+			if (handled == IRQ_HANDLED)
+				num_handled++;
+		} else {
+			dev_err(&mc13783->spidev->dev,
+					"BUG: irq %u but no handler\n",
+					baseirq + irq);
+
+			mask |= 1 << irq;
+
+			ret = mc13783_reg_write(mc13783, offmask, mask);
+		}
+	}
+
+	return num_handled;
 }
 
+static irqreturn_t mc13783_irq_thread(int irq, void *data)
+{
+	struct mc13783 *mc13783 = data;
+	irqreturn_t ret;
+	int handled = 0;
+
+	mc13783_lock(mc13783);
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
+			MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
+	if (ret > 0)
+		handled = 1;
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
+			MC13783_IRQMASK1, MC13783_IRQ_1HZ);
+	if (ret > 0)
+		handled = 1;
+
+	mc13783_unlock(mc13783);
+
+	return IRQ_RETVAL(handled);
+}
+
+#define MC13783_ADC1_CHAN0_SHIFT	5
+#define MC13783_ADC1_CHAN1_SHIFT	8
+
+struct mc13783_adcdone_data {
+	struct mc13783 *mc13783;
+	struct completion done;
+};
+
+static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+{
+	struct mc13783_adcdone_data *adcdone_data = data;
+
+	mc13783_ackirq(adcdone_data->mc13783, irq);
+
+	complete_all(&adcdone_data->done);
+
+	return IRQ_HANDLED;
+}
+
+#define MC13783_ADC_WORKING (1 << 16)
+
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample)
 {
-	unsigned int reg_adc0, reg_adc1;
-	int i;
+	u32 adc0, adc1, old_adc0;
+	int i, ret;
+	struct mc13783_adcdone_data adcdone_data = {
+		.mc13783 = mc13783,
+	};
+	init_completion(&adcdone_data.done);
 
-	mutex_lock(&mc13783->adc_conv_lock);
+	dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
 
-	/* set up auto incrementing anyway to make quick read */
-	reg_adc0 =  MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
-	/* enable the adc, ignore external triggering and set ASC to trigger
-	 * conversion */
-	reg_adc1 =  MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
-		| MC13783_ADC1_ASC;
+	mc13783_lock(mc13783);
+
+	if (mc13783->flags & MC13783_ADC_WORKING) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	mc13783->flags |= MC13783_ADC_WORKING;
+
+	mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
+
+	adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+	adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
 
-	/* setup channel number */
 	if (channel > 7)
-		reg_adc1 |= MC13783_ADC1_ADSEL;
+		adc1 |= MC13783_ADC1_ADSEL;
 
 	switch (mode) {
 	case MC13783_ADC_MODE_TS:
-		/* enables touch screen reference mode and set touchscreen mode
-		 * to position mode */
-		reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
 			| MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	case MC13783_ADC_MODE_SINGLE_CHAN:
-		reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
-		reg_adc1 |= MC13783_ADC1_RAND;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+		adc1 |= MC13783_ADC1_RAND;
 		break;
+
 	case MC13783_ADC_MODE_MULT_CHAN:
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	default:
+		mc13783_unlock(mc13783);
 		return -EINVAL;
 	}
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
 
-	wait_for_completion_interruptible(&mc13783->adc_done);
+	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
+	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
+			mc13783_handler_adcdone, __func__, &adcdone_data);
 
-	for (i = 0; i < 4; i++)
-		mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
+	mc13783_unlock(mc13783);
 
-	if (mc13783->ts_active)
-		mc13783_adc_set_ts_irq_mode(mc13783);
+	ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
 
-	mutex_unlock(&mc13783->adc_conv_lock);
+	if (!ret)
+		ret = -ETIMEDOUT;
 
-	return 0;
+	mc13783_lock(mc13783);
+
+	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
+
+	if (mode == MC13783_ADC_MODE_TS)
+		/* restore TSMOD */
+		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
+
+	if (ret > 0)
+		for (i = 0; i < 4; ++i)
+			mc13783_reg_read(mc13783,
+					MC13783_REG_ADC_2, &sample[i]);
+
+	mc13783->flags &= ~MC13783_ADC_WORKING;
+out:
+	mc13783_unlock(mc13783);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
+		const char *name, void *pdata, size_t pdata_size)
+{
+	struct mfd_cell cell = {
+		.name = name,
+		.platform_data = pdata,
+		.data_size = pdata_size,
+	};
+
+	return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
 {
-	mc13783->ts_active = status;
+	return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
 }
-EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
 
 static int mc13783_check_revision(struct mc13783 *mc13783)
 {
 	u32 rev_id, rev1, rev2, finid, icid;
 
-	mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
+	mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
 
 	rev1 = (rev_id & 0x018) >> 3;
 	rev2 = (rev_id & 0x007);
@@ -292,38 +594,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
 		rev1 = 3;
 
 	if (rev1 == 0 || icid != 2) {
-		dev_err(mc13783->dev, "No MC13783 detected.\n");
+		dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
 		return -ENODEV;
 	}
 
-	mc13783->revision = ((rev1 * 10) + rev2);
-	dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
-	       rev2, finid);
+	dev_info(&mc13783->spidev->dev,
+			"MC13783 Rev %d.%d FinVer %x detected\n",
+			rev1, rev2, finid);
 
 	return 0;
 }
 
-/*
- * Register a client device.  This is non-fatal since there is no need to
- * fail the entire device init due to a single platform device failing.
- */
-static void mc13783_client_dev_register(struct mc13783 *mc13783,
-				       const char *name)
-{
-	struct mfd_cell cell = {};
-
-	cell.name = name;
-
-	mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
-}
-
-static int __devinit mc13783_probe(struct spi_device *spi)
+static int mc13783_probe(struct spi_device *spi)
 {
 	struct mc13783 *mc13783;
-	struct mc13783_platform_data *pdata = spi->dev.platform_data;
+	struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
 	int ret;
 
-	mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+	mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
 	if (!mc13783)
 		return -ENOMEM;
 
@@ -332,96 +620,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
 	spi->bits_per_word = 32;
 	spi_setup(spi);
 
-	mc13783->spi_device = spi;
-	mc13783->dev = &spi->dev;
-	mc13783->irq = spi->irq;
+	mc13783->spidev = spi;
 
-	INIT_WORK(&mc13783->work, mc13783_irq_work);
-	mutex_init(&mc13783->io_lock);
-	mutex_init(&mc13783->adc_conv_lock);
-	init_completion(&mc13783->adc_done);
+	mutex_init(&mc13783->lock);
+	mc13783_lock(mc13783);
 
+	ret = mc13783_check_revision(mc13783);
+	if (ret)
+		goto err_revision;
+
+	/* mask all irqs */
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
+			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
+
+	if (ret) {
+err_mask:
+err_revision:
+		mutex_unlock(&mc13783->lock);
+		dev_set_drvdata(&spi->dev, NULL);
+		kfree(mc13783);
+		return ret;
+	}
+
+	/* This should go away (BEGIN) */
 	if (pdata) {
 		mc13783->flags = pdata->flags;
 		mc13783->regulators = pdata->regulators;
 		mc13783->num_regulators = pdata->num_regulators;
 	}
+	/* This should go away (END) */
+
+	if (pdata->flags & MC13783_USE_ADC)
+		mc13783_add_subdevice(mc13783, "mc13783-adc");
+
+	if (pdata->flags & MC13783_USE_CODEC)
+		mc13783_add_subdevice(mc13783, "mc13783-codec");
 
-	if (mc13783_check_revision(mc13783)) {
-		ret = -ENODEV;
-		goto err_out;
+	if (pdata->flags & MC13783_USE_REGULATOR) {
+		struct mc13783_regulator_platform_data regulator_pdata = {
+			.num_regulators = pdata->num_regulators,
+			.regulators = pdata->regulators,
+		};
+
+		mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
+				&regulator_pdata, sizeof(regulator_pdata));
 	}
 
-	/* clear and mask all interrupts */
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
+	if (pdata->flags & MC13783_USE_RTC)
+		mc13783_add_subdevice(mc13783, "mc13783-rtc");
 
-	/* unmask adcdone interrupts */
-	mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
-			MC13783_INT_MASK_ADCDONEM, 0);
+	if (pdata->flags & MC13783_USE_TOUCHSCREEN)
+		mc13783_add_subdevice(mc13783, "mc13783-ts");
 
-	ret = request_irq(mc13783->irq, mc13783_interrupt,
-			IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
-			mc13783);
-	if (ret)
-		goto err_out;
-
-	if (mc13783->flags & MC13783_USE_CODEC)
-		mc13783_client_dev_register(mc13783, "mc13783-codec");
-	if (mc13783->flags & MC13783_USE_ADC)
-		mc13783_client_dev_register(mc13783, "mc13783-adc");
-	if (mc13783->flags & MC13783_USE_RTC)
-		mc13783_client_dev_register(mc13783, "mc13783-rtc");
-	if (mc13783->flags & MC13783_USE_REGULATOR)
-		mc13783_client_dev_register(mc13783, "mc13783-regulator");
-	if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
-		mc13783_client_dev_register(mc13783, "mc13783-ts");
+	mc13783_unlock(mc13783);
 
 	return 0;
-
-err_out:
-	kfree(mc13783);
-	return ret;
 }
 
 static int __devexit mc13783_remove(struct spi_device *spi)
 {
-	struct mc13783 *mc13783;
+	struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
 
-	mc13783 = dev_get_drvdata(&spi->dev);
-
-	free_irq(mc13783->irq, mc13783);
+	free_irq(mc13783->spidev->irq, mc13783);
 
 	mfd_remove_devices(&spi->dev);
 
 	return 0;
 }
 
-static struct spi_driver pmic_driver = {
+static struct spi_driver mc13783_driver = {
 	.driver = {
-		   .name = "mc13783",
-		   .bus = &spi_bus_type,
-		   .owner = THIS_MODULE,
+		.name = "mc13783",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
 	},
 	.probe = mc13783_probe,
 	.remove = __devexit_p(mc13783_remove),
 };
 
-static int __init pmic_init(void)
+static int __init mc13783_init(void)
 {
-	return spi_register_driver(&pmic_driver);
+	return spi_register_driver(&mc13783_driver);
 }
-subsys_initcall(pmic_init);
+subsys_initcall(mc13783_init);
 
-static void __exit pmic_exit(void)
+static void __exit mc13783_exit(void)
 {
-	spi_unregister_driver(&pmic_driver);
+	spi_unregister_driver(&mc13783_driver);
 }
-module_exit(pmic_exit);
-
-MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
 
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index 47e698c..95cf936 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -24,52 +24,23 @@
 
 #include <linux/platform_device.h>
 #include <linux/mfd/mc13783.h>
-#include <linux/workqueue.h>
 #include <linux/mutex.h>
-
-struct mc13783_irq {
-	void (*handler)(int, void *);
-	void *data;
-};
-
-#define MC13783_NUM_IRQ		2
-#define MC13783_IRQ_TS		0
-#define MC13783_IRQ_REGULATOR	1
-
-#define MC13783_ADC_MODE_TS		1
-#define MC13783_ADC_MODE_SINGLE_CHAN	2
-#define MC13783_ADC_MODE_MULT_CHAN	3
+#include <linux/interrupt.h>
 
 struct mc13783 {
-	int revision;
-	struct device *dev;
-	struct spi_device *spi_device;
-
-	int (*read_dev)(void *data, char reg, int count, u32 *dst);
-	int (*write_dev)(void *data, char reg, int count, const u32 *src);
-
-	struct mutex io_lock;
-	void *io_data;
+	struct spi_device *spidev;
+	struct mutex lock;
 	int irq;
-	unsigned int flags;
+	int flags;
 
-	struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
-	struct work_struct work;
-	struct completion adc_done;
-	unsigned int ts_active;
-	struct mutex adc_conv_lock;
+	irq_handler_t irqhandler[MC13783_NUM_IRQ];
+	void *irqdata[MC13783_NUM_IRQ];
 
+	/* XXX these should go as platformdata to the regulator subdevice */
 	struct mc13783_regulator_init_data *regulators;
 	int num_regulators;
 };
 
-int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
-int mc13783_reg_write(struct mc13783 *, int, u32);
-int mc13783_set_bits(struct mc13783 *, int, u32, u32);
-int mc13783_free_irq(struct mc13783 *mc13783, int irq);
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data);
-
 #define MC13783_REG_INTERRUPT_STATUS_0		 0
 #define MC13783_REG_INTERRUPT_MASK_0		 1
 #define MC13783_REG_INTERRUPT_SENSE_0		 2
@@ -136,55 +107,6 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_REG_TEST_3			63
 #define MC13783_REG_NB				64
 
-
-/*
- * Interrupt Status
- */
-#define MC13783_INT_STAT_ADCDONEI	(1 << 0)
-#define MC13783_INT_STAT_ADCBISDONEI	(1 << 1)
-#define MC13783_INT_STAT_TSI		(1 << 2)
-#define MC13783_INT_STAT_WHIGHI		(1 << 3)
-#define MC13783_INT_STAT_WLOWI		(1 << 4)
-#define MC13783_INT_STAT_CHGDETI	(1 << 6)
-#define MC13783_INT_STAT_CHGOVI		(1 << 7)
-#define MC13783_INT_STAT_CHGREVI	(1 << 8)
-#define MC13783_INT_STAT_CHGSHORTI	(1 << 9)
-#define MC13783_INT_STAT_CCCVI		(1 << 10)
-#define MC13783_INT_STAT_CHGCURRI	(1 << 11)
-#define MC13783_INT_STAT_BPONI		(1 << 12)
-#define MC13783_INT_STAT_LOBATLI	(1 << 13)
-#define MC13783_INT_STAT_LOBATHI	(1 << 14)
-#define MC13783_INT_STAT_UDPI		(1 << 15)
-#define MC13783_INT_STAT_USBI		(1 << 16)
-#define MC13783_INT_STAT_IDI		(1 << 19)
-#define MC13783_INT_STAT_Unused		(1 << 20)
-#define MC13783_INT_STAT_SE1I		(1 << 21)
-#define MC13783_INT_STAT_CKDETI		(1 << 22)
-#define MC13783_INT_STAT_UDMI		(1 << 23)
-
-/*
- * Interrupt Mask
- */
-#define MC13783_INT_MASK_ADCDONEM	(1 << 0)
-#define MC13783_INT_MASK_ADCBISDONEM	(1 << 1)
-#define MC13783_INT_MASK_TSM		(1 << 2)
-#define MC13783_INT_MASK_WHIGHM		(1 << 3)
-#define MC13783_INT_MASK_WLOWM		(1 << 4)
-#define MC13783_INT_MASK_CHGDETM	(1 << 6)
-#define MC13783_INT_MASK_CHGOVM		(1 << 7)
-#define MC13783_INT_MASK_CHGREVM	(1 << 8)
-#define MC13783_INT_MASK_CHGSHORTM	(1 << 9)
-#define MC13783_INT_MASK_CCCVM		(1 << 10)
-#define MC13783_INT_MASK_CHGCURRM	(1 << 11)
-#define MC13783_INT_MASK_BPONM		(1 << 12)
-#define MC13783_INT_MASK_LOBATLM	(1 << 13)
-#define MC13783_INT_MASK_LOBATHM	(1 << 14)
-#define MC13783_INT_MASK_UDPM		(1 << 15)
-#define MC13783_INT_MASK_USBM		(1 << 16)
-#define MC13783_INT_MASK_IDM		(1 << 19)
-#define MC13783_INT_MASK_SE1M		(1 << 21)
-#define MC13783_INT_MASK_CKDETM		(1 << 22)
-
 /*
  * Reg Regulator Mode 0
  */
@@ -284,113 +206,15 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_SWCTRL_SW3_STBY		(1 << 21)
 #define MC13783_SWCTRL_SW3_MODE		(1 << 22)
 
-/*
- * ADC/Touch
- */
-#define MC13783_ADC0_LICELLCON		(1 << 0)
-#define MC13783_ADC0_CHRGICON		(1 << 1)
-#define MC13783_ADC0_BATICON		(1 << 2)
-#define MC13783_ADC0_RTHEN 		(1 << 3)
-#define MC13783_ADC0_DTHEN		(1 << 4)
-#define MC13783_ADC0_UIDEN		(1 << 5)
-#define MC13783_ADC0_ADOUTEN 		(1 << 6)
-#define MC13783_ADC0_ADOUTPER		(1 << 7)
-#define MC13783_ADC0_ADREFEN		(1 << 10)
-#define MC13783_ADC0_ADREFMODE		(1 << 11)
-#define MC13783_ADC0_TSMOD0		(1 << 12)
-#define MC13783_ADC0_TSMOD1		(1 << 13)
-#define MC13783_ADC0_TSMOD2		(1 << 14)
-#define MC13783_ADC0_CHRGRAWDIV		(1 << 15)
-#define MC13783_ADC0_ADINC1		(1 << 16)
-#define MC13783_ADC0_ADINC2		(1 << 17)
-#define MC13783_ADC0_WCOMP		(1 << 18)
-#define MC13783_ADC0_ADCBIS0		(1 << 23)
-
-#define MC13783_ADC1_ADEN		(1 << 0)
-#define MC13783_ADC1_RAND		(1 << 1)
-#define MC13783_ADC1_ADSEL		(1 << 3)
-#define MC13783_ADC1_TRIGMASK		(1 << 4)
-#define MC13783_ADC1_ADA10		(1 << 5)
-#define MC13783_ADC1_ADA11		(1 << 6)
-#define MC13783_ADC1_ADA12		(1 << 7)
-#define MC13783_ADC1_ADA20		(1 << 8)
-#define MC13783_ADC1_ADA21		(1 << 9)
-#define MC13783_ADC1_ADA22		(1 << 10)
-#define MC13783_ADC1_ATO0		(1 << 11)
-#define MC13783_ADC1_ATO1		(1 << 12)
-#define MC13783_ADC1_ATO2		(1 << 13)
-#define MC13783_ADC1_ATO3		(1 << 14)
-#define MC13783_ADC1_ATO4		(1 << 15)
-#define MC13783_ADC1_ATO5		(1 << 16)
-#define MC13783_ADC1_ATO6		(1 << 17)
-#define MC13783_ADC1_ATO7		(1 << 18)
-#define MC13783_ADC1_ATOX		(1 << 19)
-#define MC13783_ADC1_ASC		(1 << 20)
-#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
-#define MC13783_ADC1_ADONESHOT		(1 << 22)
-#define MC13783_ADC1_ADCBIS1		(1 << 23)
-
-#define MC13783_ADC1_CHAN0_SHIFT	5
-#define MC13783_ADC1_CHAN1_SHIFT	8
-
-#define MC13783_ADC2_ADD10		(1 << 2)
-#define MC13783_ADC2_ADD11		(1 << 3)
-#define MC13783_ADC2_ADD12		(1 << 4)
-#define MC13783_ADC2_ADD13		(1 << 5)
-#define MC13783_ADC2_ADD14		(1 << 6)
-#define MC13783_ADC2_ADD15		(1 << 7)
-#define MC13783_ADC2_ADD16		(1 << 8)
-#define MC13783_ADC2_ADD17		(1 << 9)
-#define MC13783_ADC2_ADD18		(1 << 10)
-#define MC13783_ADC2_ADD19		(1 << 11)
-#define MC13783_ADC2_ADD20		(1 << 14)
-#define MC13783_ADC2_ADD21		(1 << 15)
-#define MC13783_ADC2_ADD22		(1 << 16)
-#define MC13783_ADC2_ADD23		(1 << 17)
-#define MC13783_ADC2_ADD24		(1 << 18)
-#define MC13783_ADC2_ADD25		(1 << 19)
-#define MC13783_ADC2_ADD26		(1 << 20)
-#define MC13783_ADC2_ADD27		(1 << 21)
-#define MC13783_ADC2_ADD28		(1 << 22)
-#define MC13783_ADC2_ADD29		(1 << 23)
+static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val)
+{
+	int ret;
+	mc13783_lock(mc13783);
+	ret = mc13783_reg_rmw(mc13783, offset, mask, val);
+	mc13783_unlock(mc13783);
 
-#define MC13783_ADC3_WHIGH0		(1 << 0)
-#define MC13783_ADC3_WHIGH1		(1 << 1)
-#define MC13783_ADC3_WHIGH2		(1 << 2)
-#define MC13783_ADC3_WHIGH3		(1 << 3)
-#define MC13783_ADC3_WHIGH4		(1 << 4)
-#define MC13783_ADC3_WHIGH5		(1 << 5)
-#define MC13783_ADC3_ICID0		(1 << 6)
-#define MC13783_ADC3_ICID1		(1 << 7)
-#define MC13783_ADC3_ICID2		(1 << 8)
-#define MC13783_ADC3_WLOW0		(1 << 9)
-#define MC13783_ADC3_WLOW1		(1 << 10)
-#define MC13783_ADC3_WLOW2		(1 << 11)
-#define MC13783_ADC3_WLOW3		(1 << 12)
-#define MC13783_ADC3_WLOW4		(1 << 13)
-#define MC13783_ADC3_WLOW5		(1 << 14)
-#define MC13783_ADC3_ADCBIS2		(1 << 23)
-
-#define MC13783_ADC4_ADDBIS10		(1 << 2)
-#define MC13783_ADC4_ADDBIS11		(1 << 3)
-#define MC13783_ADC4_ADDBIS12		(1 << 4)
-#define MC13783_ADC4_ADDBIS13		(1 << 5)
-#define MC13783_ADC4_ADDBIS14		(1 << 6)
-#define MC13783_ADC4_ADDBIS15		(1 << 7)
-#define MC13783_ADC4_ADDBIS16		(1 << 8)
-#define MC13783_ADC4_ADDBIS17		(1 << 9)
-#define MC13783_ADC4_ADDBIS18		(1 << 10)
-#define MC13783_ADC4_ADDBIS19		(1 << 11)
-#define MC13783_ADC4_ADDBIS20		(1 << 14)
-#define MC13783_ADC4_ADDBIS21		(1 << 15)
-#define MC13783_ADC4_ADDBIS22		(1 << 16)
-#define MC13783_ADC4_ADDBIS23		(1 << 17)
-#define MC13783_ADC4_ADDBIS24		(1 << 18)
-#define MC13783_ADC4_ADDBIS25		(1 << 19)
-#define MC13783_ADC4_ADDBIS26		(1 << 20)
-#define MC13783_ADC4_ADDBIS27		(1 << 21)
-#define MC13783_ADC4_ADDBIS28		(1 << 22)
-#define MC13783_ADC4_ADDBIS29		(1 << 23)
+	return ret;
+}
 
 #endif /* __LINUX_MFD_MC13783_PRIV_H */
-
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index b3a2a72..3568040 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -1,28 +1,50 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
+#ifndef __LINUX_MFD_MC13783_H
+#define __LINUX_MFD_MC13783_H
 
-#ifndef __INCLUDE_LINUX_MFD_MC13783_H
-#define __INCLUDE_LINUX_MFD_MC13783_H
+#include <linux/interrupt.h>
 
 struct mc13783;
+
+void mc13783_lock(struct mc13783 *mc13783);
+void mc13783_unlock(struct mc13783 *mc13783);
+
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val);
+
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
+int mc13783_ackirq(struct mc13783 *mc13783, int irq);
+
+int mc13783_mask(struct mc13783 *mc13783, int irq);
+int mc13783_unmask(struct mc13783 *mc13783, int irq);
+
+#define MC13783_ADC0		43
+#define MC13783_ADC0_ADREFEN		(1 << 10)
+#define MC13783_ADC0_ADREFMODE		(1 << 11)
+#define MC13783_ADC0_TSMOD0		(1 << 12)
+#define MC13783_ADC0_TSMOD1		(1 << 13)
+#define MC13783_ADC0_TSMOD2		(1 << 14)
+#define MC13783_ADC0_ADINC1		(1 << 16)
+#define MC13783_ADC0_ADINC2		(1 << 17)
+
+#define MC13783_ADC0_TSMOD_MASK		(MC13783_ADC0_TSMOD0 | \
+					MC13783_ADC0_TSMOD1 | \
+					MC13783_ADC0_TSMOD2)
+
+/* to be cleaned up */
 struct regulator_init_data;
 
 struct mc13783_regulator_init_data {
@@ -30,23 +52,30 @@ struct mc13783_regulator_init_data {
 	struct regulator_init_data *init_data;
 };
 
-struct mc13783_platform_data {
-	struct mc13783_regulator_init_data *regulators;
+struct mc13783_regulator_platform_data {
 	int num_regulators;
-	unsigned int flags;
+	struct mc13783_regulator_init_data *regulators;
 };
 
-/* mc13783_platform_data flags */
+struct mc13783_platform_data {
+	int num_regulators;
+	struct mc13783_regulator_init_data *regulators;
+
 #define MC13783_USE_TOUCHSCREEN (1 << 0)
 #define MC13783_USE_CODEC	(1 << 1)
 #define MC13783_USE_ADC		(1 << 2)
 #define MC13783_USE_RTC		(1 << 3)
 #define MC13783_USE_REGULATOR	(1 << 4)
+	unsigned int flags;
+};
+
+#define MC13783_ADC_MODE_TS		1
+#define MC13783_ADC_MODE_SINGLE_CHAN	2
+#define MC13783_ADC_MODE_MULT_CHAN	3
 
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 
 #define	MC13783_SW_SW1A		0
 #define	MC13783_SW_SW1B		1
@@ -80,5 +109,46 @@ void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 #define	MC13783_REGU_V3		29
 #define	MC13783_REGU_V4		30
 
-#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
+#define MC13783_IRQ_ADCDONE	0
+#define MC13783_IRQ_ADCBISDONE	1
+#define MC13783_IRQ_TS		2
+#define MC13783_IRQ_WHIGH	3
+#define MC13783_IRQ_WLOW	4
+#define MC13783_IRQ_CHGDET	6
+#define MC13783_IRQ_CHGOV	7
+#define MC13783_IRQ_CHGREV	8
+#define MC13783_IRQ_CHGSHORT	9
+#define MC13783_IRQ_CCCV	10
+#define MC13783_IRQ_CHGCURR	11
+#define MC13783_IRQ_BPON	12
+#define MC13783_IRQ_LOBATL	13
+#define MC13783_IRQ_LOBATH	14
+#define MC13783_IRQ_UDP		15
+#define MC13783_IRQ_USB		16
+#define MC13783_IRQ_ID		19
+#define MC13783_IRQ_SE1		21
+#define MC13783_IRQ_CKDET	22
+#define MC13783_IRQ_UDM		23
+#define MC13783_IRQ_1HZ		24
+#define MC13783_IRQ_TODA	25
+#define MC13783_IRQ_ONOFD1	27
+#define MC13783_IRQ_ONOFD2	28
+#define MC13783_IRQ_ONOFD3	29
+#define MC13783_IRQ_SYSRST	30
+#define MC13783_IRQ_RTCRST	31
+#define MC13783_IRQ_PC		32
+#define MC13783_IRQ_WARM	33
+#define MC13783_IRQ_MEMHLD	34
+#define MC13783_IRQ_PWRRDY	35
+#define MC13783_IRQ_THWARNL	36
+#define MC13783_IRQ_THWARNH	37
+#define MC13783_IRQ_CLK		38
+#define MC13783_IRQ_SEMAF	39
+#define MC13783_IRQ_MC2B	41
+#define MC13783_IRQ_HSDET	42
+#define MC13783_IRQ_HSL		43
+#define MC13783_IRQ_ALSPTH	44
+#define MC13783_IRQ_AHSSHORT	45
+#define MC13783_NUM_IRQ		46
 
+#endif /* __LINUX_MFD_MC13783_H */
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH] [RTC] Add Freescale MC13783 RTC driver
  2009-11-01 20:34     ` Uwe Kleine-König
@ 2009-11-04 18:12       ` Valentin Longchamp
  -1 siblings, 0 replies; 40+ messages in thread
From: Valentin Longchamp @ 2009-11-04 18:12 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, linux-arm-kernel, Sascha Hauer, Paul Gortmaker,
	Alessandro Zummo, rtc-linux

Hi Uwe,

Uwe Kleine-König wrote:
> Hello,
> 
> On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-König wrote:
>> This driver provides support for the RTC part integrated into the
>> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
>> Hauer.
>>
>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>> Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
>> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
>> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
>> Cc: Alessandro Zummo <a.zummo@towertech.it>
>> Cc: rtc-linux@googlegroups.com
>> ---
>> Hello,
>>
>> this patch depends on
>>
>> 	mfd/mc13783: near complete rewrite
>>
>> sent earlier on lkml[1].  Compared to the earlier version of rtc support
>> on mc13783 as sent by Sascha, this driver got reset detection and
>> therefore depends on the patch above.
>>
>> A tree runnable on Phytec's PCM038 is available in my git tree
>>
>> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
>>
>> .  (Maybe I will rewrite these commits, so please expect it might change
>> in a non-fast-forward manner.)
> 
> Valentin, could you already test this?  Any comments by the others?
> 

I have tested your patches (taken your mc13783 branch today, and merged 
it into my patches rebased on 2.6.32-rc6).

Your mfd/mc13783 rewrite seems to work for me (but since there is no 
real usage for now, I don't use ADC yet and regulator don't do a lot). 
But it runs fine on my hardware.

However, I get the hctosys: unable to read the hardware clock error 
message at boot (from drivers/rtc/hctosys.c:62). Is it normal ?

Furthermore, the date and time are saved during system off, but the time 
is not updated: if I shut down the system during 10 minutes, my time 
will get a 10 minute delay. Is this a normal behavior with you current 
implementation or is there something we have wrong in our design/code 
(we have battery for the mc13783) ? I will have a further look at this 
later, didn't have time now.

Val

-- 
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp@epfl.ch, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
@ 2009-11-04 18:12       ` Valentin Longchamp
  0 siblings, 0 replies; 40+ messages in thread
From: Valentin Longchamp @ 2009-11-04 18:12 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Uwe,

Uwe Kleine-K?nig wrote:
> Hello,
> 
> On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-K?nig wrote:
>> This driver provides support for the RTC part integrated into the
>> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
>> Hauer.
>>
>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>> Signed-off-by: Uwe Kleine-K?nig <u.kleine-k?nig@pengutronix.de>
>> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
>> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
>> Cc: Alessandro Zummo <a.zummo@towertech.it>
>> Cc: rtc-linux at googlegroups.com
>> ---
>> Hello,
>>
>> this patch depends on
>>
>> 	mfd/mc13783: near complete rewrite
>>
>> sent earlier on lkml[1].  Compared to the earlier version of rtc support
>> on mc13783 as sent by Sascha, this driver got reset detection and
>> therefore depends on the patch above.
>>
>> A tree runnable on Phytec's PCM038 is available in my git tree
>>
>> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
>>
>> .  (Maybe I will rewrite these commits, so please expect it might change
>> in a non-fast-forward manner.)
> 
> Valentin, could you already test this?  Any comments by the others?
> 

I have tested your patches (taken your mc13783 branch today, and merged 
it into my patches rebased on 2.6.32-rc6).

Your mfd/mc13783 rewrite seems to work for me (but since there is no 
real usage for now, I don't use ADC yet and regulator don't do a lot). 
But it runs fine on my hardware.

However, I get the hctosys: unable to read the hardware clock error 
message at boot (from drivers/rtc/hctosys.c:62). Is it normal ?

Furthermore, the date and time are saved during system off, but the time 
is not updated: if I shut down the system during 10 minutes, my time 
will get a 10 minute delay. Is this a normal behavior with you current 
implementation or is there something we have wrong in our design/code 
(we have battery for the mc13783) ? I will have a further look at this 
later, didn't have time now.

Val

-- 
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp at epfl.ch, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-03 19:31     ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
@ 2009-11-04 18:35       ` Samuel Ortiz
  2009-11-04 22:28         ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Samuel Ortiz @ 2009-11-04 18:35 UTC (permalink / raw)
  To: Uwe Kleine-König; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hi Uwe,

On Tue, Nov 03, 2009 at 08:31:13PM +0100, Uwe Kleine-König wrote:
> This fixes several things while still providing the old API:
> 
>  - simplify and fix locking
>  - better error handling
>  - don't ack all irqs making it impossible to detect a reset of the
>    rtc
>  - use a timeout variant to wait for completion of ADC conversion
>  - provide platform-data to regulator subdevice (This allows making
>    struct mc13783 opaque for other drivers after the regulator driver is
>    updated to use its platform_data.)
>  - expose all interrupts
>  - use threaded irq
> 
> After all users in mainline are converted to the new API, some things
> (e.g. mc13783-private.h) can go away.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---
> Hello,
> 
> compared to the first submission I squashed in the patch
> 
> 	mfd/mc13783: change type of irq handlers to irq_handler_t
> 
> sent earlier in that thread and fixed a few whitespace issues reported
> by checkpatch.pl.
> 
> I'd be happy if this patch would make it in now.
The patch looks mostly ok, thanks for this work.
I have a few comments though.

> - * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
Even though this looks like a major rewrite, I still think it's unfair to
remove Sascha from there.


> +void mc13783_lock(struct mc13783 *mc13783)
> +{
> +	if (!mutex_trylock(&mc13783->lock)) {
> +		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
> +			__func__, __builtin_return_address(0));
> +
> +		mutex_lock(&mc13783->lock);
That is just for debugging purposes, right ?


> +static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
> +		struct spi_transfer *t, u32 *buf,
> +		unsigned int offset, u32 *val
What is val used for in that function ?

)
> +{
> +	if (offset > MC13783_NUMREGS)
>  		return -EINVAL;
> -	return len - m.actual_length;
> +
> +	buf[0] = offset << 25;
Could we have a define for that 25 ?


> +	memset(t, 0, sizeof(*t));
> +
> +	t->tx_buf = buf;
> +	t->rx_buf = buf;
> +	t->len = sizeof(u32);
> +
> +	return 1;
>  }
>  
> -static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> +static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
> +		struct spi_transfer *t, u32 *buf,
> +		unsigned int offset, u32 *val)
>  {
> -	unsigned int frame = 0;
> -	int ret = 0;
> +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
your SPI read will be on t->rx_buf. I could understand that you want to check
for t->rx_buf not being NULL (although a BUG_ON() seems too much here), but
checking for t->rx_buf pointing to buf really looks akward to me.

why not:

BUG_ON(t->rx_buf == NULL)

*val = *((u32 *)t->rx_buf) & 0xffffff;

> -static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
> +static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
> +		struct spi_transfer *t, u32 *buf,
> +		unsigned int offset, u32 val)
>  {
> -	unsigned int frame = 0;
> +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
>  
> -	if (reg_num > MC13783_MAX_REG_NUM)
> -		return -EINVAL;
> +	return 1;
> +}
I dont get the point of mc13783_eval_write_transfer().


> +int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
> +{
> +	u32 buf;
> +	struct spi_transfer t;
> +	struct spi_message m;
> +	int ret;
> +
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
> +
> +	ret = mc13783_prep_read_transfer(mc13783, &t, &buf, offset, val);
Do you really need buf here ?
I think mc13783_prep_read_transfer(mc13783, &t, val, offset); should be
enough.


> +	if (ret < 0)
> +		return ret;
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t, &m);
> +
> +	ret = spi_sync(mc13783->spidev, &m);
>  
> -	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
> -	frame |= reg_num << MC13783_REG_NUM_SHIFT;
> -	frame |= reg_val & MC13783_FRAME_MASK;
> +	/* error in message.status implies error return from spi_sync */
> +	BUG_ON(!ret && m.status);
So, you really want to crash your board because of an SPI inconsistency ?
Seems like an overkill to me.


> -	return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
> +	if (ret)
> +		return ret;
> +
> +	ret = mc13783_eval_read_transfer(mc13783, &t, &buf, offset, val);
> +
> +	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
> +
> +	return ret < 0 ? ret : 0;
>  }
> +EXPORT_SYMBOL(mc13783_reg_read);
>  
> -int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> +int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
>  {
> +	u32 buf;
> +	struct spi_transfer t;
> +	struct spi_message m;
>  	int ret;
>  
> -	mutex_lock(&mc13783->io_lock);
> -	ret = mc13783_read(mc13783, reg_num, reg_val);
> -	mutex_unlock(&mc13783->io_lock);
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
>  
> -	return ret;
> +	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
> +
> +	ret = mc13783_prep_write_transfer(mc13783, &t, &buf, offset, val);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t, &m);
> +
> +	ret = spi_sync(mc13783->spidev, &m);
> +
> +	BUG_ON(!ret && m.status);
> +
> +	if (ret)
> +		return ret;
> +
> +	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
Again, I dont see the point of this function.

The rest of the code looks fine to me.

Cheers,
Samuel.

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-04 18:35       ` Samuel Ortiz
@ 2009-11-04 22:28         ` Uwe Kleine-König
  2009-11-05 22:31           ` Samuel Ortiz
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-04 22:28 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hello Samuel,

On Wed, Nov 04, 2009 at 07:35:08PM +0100, Samuel Ortiz wrote:
> > - * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
> Even though this looks like a major rewrite, I still think it's unfair to
> remove Sascha from there.
OK.
 
> > +void mc13783_lock(struct mc13783 *mc13783)
> > +{
> > +	if (!mutex_trylock(&mc13783->lock)) {
> > +		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
> > +			__func__, __builtin_return_address(0));
> > +
> > +		mutex_lock(&mc13783->lock);
> That is just for debugging purposes, right ?
Yes, the intention is to see lock contentions.  I thought about making
this

	#if defined(DEBUG)
		if (!mutex_trylock(&mc13783->lock)) {
			...
		}
		dev_dbg(...)
	#else
		mutex_lock(...);
	#endif

but it didn't feel right to have a different locking scheme depending on
DEBUG or not.  Does your question imply that I should change something
here?

> > +static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
> > +		struct spi_transfer *t, u32 *buf,
> > +		unsigned int offset, u32 *val
> What is val used for in that function ?
It's there for symmetry with mc13783_eval_read_transfer.

> )
> > +{
> > +	if (offset > MC13783_NUMREGS)
> >  		return -EINVAL;
> > -	return len - m.actual_length;
> > +
> > +	buf[0] = offset << 25;
> Could we have a define for that 25 ?
Yes, will do.
 
> > +	memset(t, 0, sizeof(*t));
> > +
> > +	t->tx_buf = buf;
> > +	t->rx_buf = buf;
> > +	t->len = sizeof(u32);
> > +
> > +	return 1;
> >  }
> >  
> > -static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> > +static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
> > +		struct spi_transfer *t, u32 *buf,
> > +		unsigned int offset, u32 *val)
> >  {
> > -	unsigned int frame = 0;
> > -	int ret = 0;
> > +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
> your SPI read will be on t->rx_buf. I could understand that you want to check
> for t->rx_buf not being NULL (although a BUG_ON() seems too much here), but
> checking for t->rx_buf pointing to buf really looks akward to me.
The intention here is to assert that mc13783_eval_read_transfer is
called for a transfer prepared by mc13783_prep_read_transfer.  As this
sets up t->tx_buf = t->rx_buf = buf, it seems to be the right assertion.

> why not:
> 
> BUG_ON(t->rx_buf == NULL)
> 
> *val = *((u32 *)t->rx_buf) & 0xffffff;
> 
> > -static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
> > +static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
> > +		struct spi_transfer *t, u32 *buf,
> > +		unsigned int offset, u32 val)
> >  {
> > -	unsigned int frame = 0;
> > +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
> >  
> > -	if (reg_num > MC13783_MAX_REG_NUM)
> > -		return -EINVAL;
> > +	return 1;
> > +}
> I dont get the point of mc13783_eval_write_transfer().
The idea here is that I could setup, send and receive multi-transfer
messages with a single buffer array.  Then the return value would tell me how
much to advance in the buffer for the next result.  Maybe that's just
paranoid over-engineering.

> > +int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
> > +{
> > +	u32 buf;
> > +	struct spi_transfer t;
> > +	struct spi_message m;
> > +	int ret;
> > +
> > +	BUG_ON(!mutex_is_locked(&mc13783->lock));
> > +
> > +	ret = mc13783_prep_read_transfer(mc13783, &t, &buf, offset, val);
> Do you really need buf here ?
> I think mc13783_prep_read_transfer(mc13783, &t, val, offset); should be
> enough.
Yes, should work.
 
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	spi_message_init(&m);
> > +	spi_message_add_tail(&t, &m);
> > +
> > +	ret = spi_sync(mc13783->spidev, &m);
> >  
> > -	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
> > -	frame |= reg_num << MC13783_REG_NUM_SHIFT;
> > -	frame |= reg_val & MC13783_FRAME_MASK;
> > +	/* error in message.status implies error return from spi_sync */
> > +	BUG_ON(!ret && m.status);
> So, you really want to crash your board because of an SPI inconsistency ?
> Seems like an overkill to me.
This only bugs if spi_sync succeeds even though the message wasn't
transfered correctly.  Sascha's driver had:

	if (spi_sync(spi, &m) != 0 || m.status != 0)
		return -EINVAL;

If I understand spi_sync correctly m.status != 0 implies spi_sync
returning != 0, so the above should be equivalent to:

	if (spi_sync(spi, &m) != 0)
		return -EINVAL;

So my BUG_ON is only for the case that Sascha saw something I missed.
 
> > +	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
> Again, I dont see the point of this function.
Do you insist on fixing that?  It might look a bit strange (which is
subjective) but I don't see much benefit in changing it because I expect
the compiler to produce similar code.  Currently all
mc13783_{prep,eval}_{read,write}_transfer calls are inlined by my
compiler anyhow.

Best regards and thanks for your comments,
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] [RTC] Add Freescale MC13783 RTC driver
  2009-11-04 18:12       ` Valentin Longchamp
@ 2009-11-05 15:06         ` Valentin Longchamp
  -1 siblings, 0 replies; 40+ messages in thread
From: Valentin Longchamp @ 2009-11-05 15:06 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, linux-arm-kernel, Sascha Hauer, Paul Gortmaker,
	Alessandro Zummo, rtc-linux

Valentin Longchamp wrote:
> Hi Uwe,
> 
> Uwe Kleine-König wrote:
>> Hello,
>>
>> On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-König wrote:
>>> This driver provides support for the RTC part integrated into the
>>> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
>>> Hauer.
>>>
>>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>>> Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
>>> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
>>> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
>>> Cc: Alessandro Zummo <a.zummo@towertech.it>
>>> Cc: rtc-linux@googlegroups.com
>>> ---
>>> Hello,
>>>
>>> this patch depends on
>>>
>>> 	mfd/mc13783: near complete rewrite
>>>
>>> sent earlier on lkml[1].  Compared to the earlier version of rtc support
>>> on mc13783 as sent by Sascha, this driver got reset detection and
>>> therefore depends on the patch above.
>>>
>>> A tree runnable on Phytec's PCM038 is available in my git tree
>>>
>>> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
>>>
>>> .  (Maybe I will rewrite these commits, so please expect it might change
>>> in a non-fast-forward manner.)
>> Valentin, could you already test this?  Any comments by the others?
>>
> 
> I have tested your patches (taken your mc13783 branch today, and merged 
> it into my patches rebased on 2.6.32-rc6).
> 
> Your mfd/mc13783 rewrite seems to work for me (but since there is no 
> real usage for now, I don't use ADC yet and regulator don't do a lot). 
> But it runs fine on my hardware.
> 
> However, I get the hctosys: unable to read the hardware clock error 
> message at boot (from drivers/rtc/hctosys.c:62). Is it normal ?
> 
> Furthermore, the date and time are saved during system off, but the time 
> is not updated: if I shut down the system during 10 minutes, my time 
> will get a 10 minute delay. Is this a normal behavior with you current 
> implementation or is there something we have wrong in our design/code 
> (we have battery for the mc13783) ? I will have a further look at this 
> later, didn't have time now.
> 

This was due to a small hardware problem on our platform. Your RTC 
driver now works well on mx31moboard. Please consider my ackey-by:

Ackey-by: Valentin Longchamp <valentin.longchamp@epfl.ch>

Val

-- 
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp@epfl.ch, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] [RTC] Add Freescale MC13783 RTC driver
@ 2009-11-05 15:06         ` Valentin Longchamp
  0 siblings, 0 replies; 40+ messages in thread
From: Valentin Longchamp @ 2009-11-05 15:06 UTC (permalink / raw)
  To: linux-arm-kernel

Valentin Longchamp wrote:
> Hi Uwe,
> 
> Uwe Kleine-K?nig wrote:
>> Hello,
>>
>> On Sat, Oct 24, 2009 at 10:35:42AM +0200, Uwe Kleine-K?nig wrote:
>>> This driver provides support for the RTC part integrated into the
>>> Freescale MC13783 PMIC and bases on patch created earlier by Sascha
>>> Hauer.
>>>
>>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>>> Signed-off-by: Uwe Kleine-K?nig <u.kleine-k?nig@pengutronix.de>
>>> Cc: Valentin Longchamp <valentin.longchamp@epfl.ch>
>>> Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
>>> Cc: Alessandro Zummo <a.zummo@towertech.it>
>>> Cc: rtc-linux at googlegroups.com
>>> ---
>>> Hello,
>>>
>>> this patch depends on
>>>
>>> 	mfd/mc13783: near complete rewrite
>>>
>>> sent earlier on lkml[1].  Compared to the earlier version of rtc support
>>> on mc13783 as sent by Sascha, this driver got reset detection and
>>> therefore depends on the patch above.
>>>
>>> A tree runnable on Phytec's PCM038 is available in my git tree
>>>
>>> 	git://git.pengutronix.de/git/ukl/linux-2.6.git mc13783
>>>
>>> .  (Maybe I will rewrite these commits, so please expect it might change
>>> in a non-fast-forward manner.)
>> Valentin, could you already test this?  Any comments by the others?
>>
> 
> I have tested your patches (taken your mc13783 branch today, and merged 
> it into my patches rebased on 2.6.32-rc6).
> 
> Your mfd/mc13783 rewrite seems to work for me (but since there is no 
> real usage for now, I don't use ADC yet and regulator don't do a lot). 
> But it runs fine on my hardware.
> 
> However, I get the hctosys: unable to read the hardware clock error 
> message at boot (from drivers/rtc/hctosys.c:62). Is it normal ?
> 
> Furthermore, the date and time are saved during system off, but the time 
> is not updated: if I shut down the system during 10 minutes, my time 
> will get a 10 minute delay. Is this a normal behavior with you current 
> implementation or is there something we have wrong in our design/code 
> (we have battery for the mc13783) ? I will have a further look at this 
> later, didn't have time now.
> 

This was due to a small hardware problem on our platform. Your RTC 
driver now works well on mx31moboard. Please consider my ackey-by:

Ackey-by: Valentin Longchamp <valentin.longchamp@epfl.ch>

Val

-- 
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp at epfl.ch, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-04 22:28         ` Uwe Kleine-König
@ 2009-11-05 22:31           ` Samuel Ortiz
  2009-11-05 23:53             ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Samuel Ortiz @ 2009-11-05 22:31 UTC (permalink / raw)
  To: Uwe Kleine-König; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hi Uwe,

On Wed, Nov 04, 2009 at 11:28:39PM +0100, Uwe Kleine-König wrote:
> > > +void mc13783_lock(struct mc13783 *mc13783)
> > > +{
> > > +	if (!mutex_trylock(&mc13783->lock)) {
> > > +		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
> > > +			__func__, __builtin_return_address(0));
> > > +
> > > +		mutex_lock(&mc13783->lock);
> > That is just for debugging purposes, right ?
> Yes, the intention is to see lock contentions.  I thought about making
> this
> 
> 	#if defined(DEBUG)
> 		if (!mutex_trylock(&mc13783->lock)) {
> 			...
> 		}
> 		dev_dbg(...)
> 	#else
> 		mutex_lock(...);
> 	#endif
> 
> but it didn't feel right to have a different locking scheme depending on
> DEBUG or not.  Does your question imply that I should change something
> here?
No, I was just curious. This is much nicer than the ifdef DEBUG solution,
definitely.

 
> > > +static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
> > > +		struct spi_transfer *t, u32 *buf,
> > > +		unsigned int offset, u32 *val
> > What is val used for in that function ?
> It's there for symmetry with mc13783_eval_read_transfer.
> 
> > )
> > > +{
> > > +	if (offset > MC13783_NUMREGS)
> > >  		return -EINVAL;
> > > -	return len - m.actual_length;
> > > +
> > > +	buf[0] = offset << 25;
> > Could we have a define for that 25 ?
> Yes, will do.
>  
> > > +	memset(t, 0, sizeof(*t));
> > > +
> > > +	t->tx_buf = buf;
> > > +	t->rx_buf = buf;
> > > +	t->len = sizeof(u32);
> > > +
> > > +	return 1;
> > >  }
> > >  
> > > -static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> > > +static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
> > > +		struct spi_transfer *t, u32 *buf,
> > > +		unsigned int offset, u32 *val)
> > >  {
> > > -	unsigned int frame = 0;
> > > -	int ret = 0;
> > > +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
> > your SPI read will be on t->rx_buf. I could understand that you want to check
> > for t->rx_buf not being NULL (although a BUG_ON() seems too much here), but
> > checking for t->rx_buf pointing to buf really looks akward to me.
> The intention here is to assert that mc13783_eval_read_transfer is
> called for a transfer prepared by mc13783_prep_read_transfer.  As this
> sets up t->tx_buf = t->rx_buf = buf, it seems to be the right assertion.
It is the right assertion. I'm just saying that this looks quite paranoid to
me: you're running those checks from a non exported function, that is only
called once in your code, from a routine where you specifically set tx_buf and
rx_buf properly. I think it makes the code more complicated to read and follow
than what is should.

 
> > why not:
> > 
> > BUG_ON(t->rx_buf == NULL)
> > 
> > *val = *((u32 *)t->rx_buf) & 0xffffff;
> > 
> > > -static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
> > > +static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
> > > +		struct spi_transfer *t, u32 *buf,
> > > +		unsigned int offset, u32 val)
> > >  {
> > > -	unsigned int frame = 0;
> > > +	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
> > >  
> > > -	if (reg_num > MC13783_MAX_REG_NUM)
> > > -		return -EINVAL;
> > > +	return 1;
> > > +}
> > I dont get the point of mc13783_eval_write_transfer().
> The idea here is that I could setup, send and receive multi-transfer
> messages with a single buffer array.  Then the return value would tell me how
> much to advance in the buffer for the next result.  Maybe that's just
> paranoid over-engineering.
I'm glad we agree :) This routine is just not neede, for the mere fact that it
does nothing. Unless you have bigger plans for this driver, right now you're
doing simple SPI register reads and writes, afaict.

> > > +	/* error in message.status implies error return from spi_sync */
> > > +	BUG_ON(!ret && m.status);
> > So, you really want to crash your board because of an SPI inconsistency ?
> > Seems like an overkill to me.
> This only bugs if spi_sync succeeds even though the message wasn't
> transfered correctly.  Sascha's driver had:
> 
> 	if (spi_sync(spi, &m) != 0 || m.status != 0)
> 		return -EINVAL;
> 
> If I understand spi_sync correctly m.status != 0 implies spi_sync
> returning != 0, so the above should be equivalent to:
> 
> 	if (spi_sync(spi, &m) != 0)
> 		return -EINVAL;
> 
> So my BUG_ON is only for the case that Sascha saw something I missed.
Oh, dont get me wrong: I'm not saying the check is bogus, I'm just saying that
I would just have a WARN_ON() here. I wouldnt be happy if my board would crash
because of an SPI read error.

  
> > > +	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
> > Again, I dont see the point of this function.
> Do you insist on fixing that?  It might look a bit strange (which is
> subjective) but I don't see much benefit in changing it because I expect
> the compiler to produce similar code.  Currently all
> mc13783_{prep,eval}_{read,write}_transfer calls are inlined by my
> compiler anyhow.
The only one I'd like to be removed is mc13783_eval_write_transfer(). It might
look a bit strange, but it looks even stranger to me when I see a routine
that basically does nothing but returning 1.


> Best regards and thanks for your comments,
Thanks for your patch, sorry for the late comments.

Cheers,
Samuel.


> Uwe
> 
> -- 
> Pengutronix e.K.                              | Uwe Kleine-König            |
> Industrial Linux Solutions                    | http://www.pengutronix.de/  |

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-05 22:31           ` Samuel Ortiz
@ 2009-11-05 23:53             ` Uwe Kleine-König
  2009-11-05 23:56               ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-05 23:53 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hello,

On Thu, Nov 05, 2009 at 11:31:04PM +0100, Samuel Ortiz wrote:
> > The idea here is that I could setup, send and receive multi-transfer
> > messages with a single buffer array.  Then the return value would tell me how
> > much to advance in the buffer for the next result.  Maybe that's just
> > paranoid over-engineering.
> I'm glad we agree :) This routine is just not neede, for the mere fact that it
> does nothing. Unless you have bigger plans for this driver, right now you're
> doing simple SPI register reads and writes, afaict.
OK, I moved the functionality of {prep,eval}_{read,write} to
reg_{read,write} now and removed the BUG_ONs.  Incremental patch below.
 
> > > > +	/* error in message.status implies error return from spi_sync */
> > > > +	BUG_ON(!ret && m.status);
> > > So, you really want to crash your board because of an SPI inconsistency ?
> > > Seems like an overkill to me.
> > This only bugs if spi_sync succeeds even though the message wasn't
> > transfered correctly.  Sascha's driver had:
> > 
> > 	if (spi_sync(spi, &m) != 0 || m.status != 0)
> > 		return -EINVAL;
> > 
> > If I understand spi_sync correctly m.status != 0 implies spi_sync
> > returning != 0, so the above should be equivalent to:
> > 
> > 	if (spi_sync(spi, &m) != 0)
> > 		return -EINVAL;
> > 
> > So my BUG_ON is only for the case that Sascha saw something I missed.
> Oh, dont get me wrong: I'm not saying the check is bogus, I'm just saying that
> I would just have a WARN_ON() here. I wouldnt be happy if my board would crash
> because of an SPI read error.
An SPI read error won't trigger that.  In this case ret is < 0, and so
(!ret && m.status) is false.

The incremental diff based on the earlier post can be found below.  I
will follow up with the updated patch.

Best regards
Uwe

 drivers/mfd/mc13783-core.c |   95 ++++++++++++--------------------------------
 1 files changed, 26 insertions(+), 69 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index 45713a4..99267ed 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -2,6 +2,9 @@
  * Copyright 2009 Pengutronix
  * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
  * This program is free software; you can redistribute it and/or modify it under
  * the terms of the GNU General Public License version 2 as published by the
  * Free Software Foundation.
@@ -112,7 +115,7 @@ void mc13783_lock(struct mc13783 *mc13783)
 {
 	if (!mutex_trylock(&mc13783->lock)) {
 		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
-			__func__, __builtin_return_address(0));
+				__func__, __builtin_return_address(0));
 
 		mutex_lock(&mc13783->lock);
 	}
@@ -129,75 +132,25 @@ void mc13783_unlock(struct mc13783 *mc13783)
 }
 EXPORT_SYMBOL(mc13783_unlock);
 
-static int mc13783_prep_read_transfer(struct mc13783 *mc13783,
-		struct spi_transfer *t, u32 *buf,
-		unsigned int offset, u32 *val)
-{
-	if (offset > MC13783_NUMREGS)
-		return -EINVAL;
-
-	buf[0] = offset << 25;
-
-	memset(t, 0, sizeof(*t));
-
-	t->tx_buf = buf;
-	t->rx_buf = buf;
-	t->len = sizeof(u32);
-
-	return 1;
-}
-
-static int mc13783_eval_read_transfer(struct mc13783 *mc13783,
-		struct spi_transfer *t, u32 *buf,
-		unsigned int offset, u32 *val)
-{
-	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
-
-	*val = buf[0] & 0xffffff;
-
-	return 1;
-}
-
-static int mc13783_prep_write_transfer(struct mc13783 *mc13783,
-		struct spi_transfer *t, u32 *buf,
-		unsigned int offset, u32 val)
-{
-	if (offset > MC13783_NUMREGS || val > 0xffffff)
-		return -EINVAL;
-
-	buf[0] = 1 << 31 | offset << 25 | val;
-
-	memset(t, 0, sizeof(*t));
-
-	t->tx_buf = buf;
-	t->rx_buf = buf;
-	t->len = sizeof(u32);
-
-	return 1;
-}
-
-static int mc13783_eval_write_transfer(struct mc13783 *mc13783,
-		struct spi_transfer *t, u32 *buf,
-		unsigned int offset, u32 val)
-{
-	BUG_ON(t->tx_buf != buf || t->rx_buf != buf);
-
-	return 1;
-}
-
+#define MC13783_REGOFFSET_SHIFT 25
 int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
 {
-	u32 buf;
 	struct spi_transfer t;
 	struct spi_message m;
 	int ret;
 
 	BUG_ON(!mutex_is_locked(&mc13783->lock));
 
-	ret = mc13783_prep_read_transfer(mc13783, &t, &buf, offset, val);
+	if (offset > MC13783_NUMREGS)
+		return -EINVAL;
 
-	if (ret < 0)
-		return ret;
+	*val = offset << MC13783_REGOFFSET_SHIFT;
+
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = val;
+	t.rx_buf = val;
+	t.len = sizeof(u32);
 
 	spi_message_init(&m);
 	spi_message_add_tail(&t, &m);
@@ -210,11 +163,11 @@ int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
 	if (ret)
 		return ret;
 
-	ret = mc13783_eval_read_transfer(mc13783, &t, &buf, offset, val);
+	*val &= 0xffffff;
 
 	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
 
-	return ret < 0 ? ret : 0;
+	return 0;
 }
 EXPORT_SYMBOL(mc13783_reg_read);
 
@@ -229,10 +182,16 @@ int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
 
 	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
 
-	ret = mc13783_prep_write_transfer(mc13783, &t, &buf, offset, val);
+	if (offset > MC13783_NUMREGS || val > 0xffffff)
+		return -EINVAL;
+
+	buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
 
-	if (ret < 0)
-		return ret;
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = &buf;
+	t.rx_buf = &buf;
+	t.len = sizeof(u32);
 
 	spi_message_init(&m);
 	spi_message_add_tail(&t, &m);
@@ -244,9 +203,7 @@ int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
 	if (ret)
 		return ret;
 
-	ret = mc13783_eval_write_transfer(mc13783, &t, &buf, offset, val);
-
-	return ret < 0 ? ret : 0;
+	return 0;
 }
 EXPORT_SYMBOL(mc13783_reg_write);
 
-- 
1.6.5.2


-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH] mfd/mc13783: near complete rewrite
  2009-11-05 23:53             ` Uwe Kleine-König
@ 2009-11-05 23:56               ` Uwe Kleine-König
  2009-11-06  0:28                 ` Samuel Ortiz
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-05 23:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Mark Brown, Samuel Ortiz

This fixes several things while still providing the old API:

 - simplify and fix locking
 - better error handling
 - don't ack all irqs making it impossible to detect a reset of the
   rtc
 - use a timeout variant to wait for completion of ADC conversion
 - provide platform-data to regulator subdevice (This allows making
   struct mc13783 opaque for other drivers after the regulator driver is
   updated to use its platform_data.)
 - expose all interrupts
 - use threaded irq

After all users in mainline are converted to the new API, some things
(e.g. mc13783-private.h) can go away.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
 drivers/mfd/mc13783-core.c          |  751 +++++++++++++++++++++++------------
 include/linux/mfd/mc13783-private.h |  208 +---------
 include/linux/mfd/mc13783.h         |  120 +++++--
 3 files changed, 613 insertions(+), 466 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index e354d29..99267ed 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -1,286 +1,545 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This code is in parts based on wm8350-core.c and pcf50633-core.c
- *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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.
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
-
-#include <linux/mfd/mc13783-private.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/core.h>
-#include <linux/spi/spi.h>
-#include <linux/uaccess.h>
-#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13783-private.h>
+
+#define MC13783_IRQSTAT0	0
+#define MC13783_IRQSTAT0_ADCDONEI	(1 << 0)
+#define MC13783_IRQSTAT0_ADCBISDONEI	(1 << 1)
+#define MC13783_IRQSTAT0_TSI		(1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI		(1 << 3)
+#define MC13783_IRQSTAT0_WLOWI		(1 << 4)
+#define MC13783_IRQSTAT0_CHGDETI	(1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI		(1 << 7)
+#define MC13783_IRQSTAT0_CHGREVI	(1 << 8)
+#define MC13783_IRQSTAT0_CHGSHORTI	(1 << 9)
+#define MC13783_IRQSTAT0_CCCVI		(1 << 10)
+#define MC13783_IRQSTAT0_CHGCURRI	(1 << 11)
+#define MC13783_IRQSTAT0_BPONI		(1 << 12)
+#define MC13783_IRQSTAT0_LOBATLI	(1 << 13)
+#define MC13783_IRQSTAT0_LOBATHI	(1 << 14)
+#define MC13783_IRQSTAT0_UDPI		(1 << 15)
+#define MC13783_IRQSTAT0_USBI		(1 << 16)
+#define MC13783_IRQSTAT0_IDI		(1 << 19)
+#define MC13783_IRQSTAT0_SE1I		(1 << 21)
+#define MC13783_IRQSTAT0_CKDETI		(1 << 22)
+#define MC13783_IRQSTAT0_UDMI		(1 << 23)
+
+#define MC13783_IRQMASK0	1
+#define MC13783_IRQMASK0_ADCDONEM	MC13783_IRQSTAT0_ADCDONEI
+#define MC13783_IRQMASK0_ADCBISDONEM	MC13783_IRQSTAT0_ADCBISDONEI
+#define MC13783_IRQMASK0_TSM		MC13783_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM		MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM		MC13783_IRQSTAT0_WLOWI
+#define MC13783_IRQMASK0_CHGDETM	MC13783_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM		MC13783_IRQSTAT0_CHGOVI
+#define MC13783_IRQMASK0_CHGREVM	MC13783_IRQSTAT0_CHGREVI
+#define MC13783_IRQMASK0_CHGSHORTM	MC13783_IRQSTAT0_CHGSHORTI
+#define MC13783_IRQMASK0_CCCVM		MC13783_IRQSTAT0_CCCVI
+#define MC13783_IRQMASK0_CHGCURRM	MC13783_IRQSTAT0_CHGCURRI
+#define MC13783_IRQMASK0_BPONM		MC13783_IRQSTAT0_BPONI
+#define MC13783_IRQMASK0_LOBATLM	MC13783_IRQSTAT0_LOBATLI
+#define MC13783_IRQMASK0_LOBATHM	MC13783_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM		MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM		MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM		MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M		MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM		MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM		MC13783_IRQSTAT0_UDMI
+
+#define MC13783_IRQSTAT1	3
+#define MC13783_IRQSTAT1_1HZI		(1 << 0)
+#define MC13783_IRQSTAT1_TODAI		(1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I	(1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I	(1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I	(1 << 5)
+#define MC13783_IRQSTAT1_SYSRSTI	(1 << 6)
+#define MC13783_IRQSTAT1_RTCRSTI	(1 << 7)
+#define MC13783_IRQSTAT1_PCI		(1 << 8)
+#define MC13783_IRQSTAT1_WARMI		(1 << 9)
+#define MC13783_IRQSTAT1_MEMHLDI	(1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI	(1 << 11)
+#define MC13783_IRQSTAT1_THWARNLI	(1 << 12)
+#define MC13783_IRQSTAT1_THWARNHI	(1 << 13)
+#define MC13783_IRQSTAT1_CLKI		(1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI		(1 << 15)
+#define MC13783_IRQSTAT1_MC2BI		(1 << 17)
+#define MC13783_IRQSTAT1_HSDETI		(1 << 18)
+#define MC13783_IRQSTAT1_HSLI		(1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI	(1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI	(1 << 21)
+
+#define MC13783_IRQMASK1	4
+#define MC13783_IRQMASK1_1HZM		MC13783_IRQSTAT1_1HZI
+#define MC13783_IRQMASK1_TODAM		MC13783_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M	MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M	MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M	MC13783_IRQSTAT1_ONOFD3I
+#define MC13783_IRQMASK1_SYSRSTM	MC13783_IRQSTAT1_SYSRSTI
+#define MC13783_IRQMASK1_RTCRSTM	MC13783_IRQSTAT1_RTCRSTI
+#define MC13783_IRQMASK1_PCM		MC13783_IRQSTAT1_PCI
+#define MC13783_IRQMASK1_WARMM		MC13783_IRQSTAT1_WARMI
+#define MC13783_IRQMASK1_MEMHLDM	MC13783_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM	MC13783_IRQSTAT1_PWRRDYI
+#define MC13783_IRQMASK1_THWARNLM	MC13783_IRQSTAT1_THWARNLI
+#define MC13783_IRQMASK1_THWARNHM	MC13783_IRQSTAT1_THWARNHI
+#define MC13783_IRQMASK1_CLKM		MC13783_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM		MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM		MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM		MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM		MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM	MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM	MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13783_ADC1		44
+#define MC13783_ADC1_ADEN		(1 << 0)
+#define MC13783_ADC1_RAND		(1 << 1)
+#define MC13783_ADC1_ADSEL		(1 << 3)
+#define MC13783_ADC1_ASC		(1 << 20)
+#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
+
+#define MC13783_NUMREGS 0x3f
+
+void mc13783_lock(struct mc13783 *mc13783)
+{
+	if (!mutex_trylock(&mc13783->lock)) {
+		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
+				__func__, __builtin_return_address(0));
+
+		mutex_lock(&mc13783->lock);
+	}
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13783_lock);
 
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_FRAME_MASK	0x00ffffff
-#define MC13783_MAX_REG_NUM	0x3f
-#define MC13783_REG_NUM_SHIFT	0x19
-#define MC13783_WRITE_BIT_SHIFT	31
+void mc13783_unlock(struct mc13783 *mc13783)
+{
+	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+	mutex_unlock(&mc13783->lock);
+}
+EXPORT_SYMBOL(mc13783_unlock);
 
-static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+#define MC13783_REGOFFSET_SHIFT 25
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
 {
-	struct spi_transfer t = {
-		.tx_buf = (const void *)buf,
-		.rx_buf = buf,
-		.len = len,
-		.cs_change = 0,
-		.delay_usecs = 0,
-	};
+	struct spi_transfer t;
 	struct spi_message m;
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	if (offset > MC13783_NUMREGS)
+		return -EINVAL;
+
+	*val = offset << MC13783_REGOFFSET_SHIFT;
+
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = val;
+	t.rx_buf = val;
+	t.len = sizeof(u32);
 
 	spi_message_init(&m);
 	spi_message_add_tail(&t, &m);
-	if (spi_sync(spi, &m) != 0 || m.status != 0)
-		return -EINVAL;
-	return len - m.actual_length;
-}
 
-static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
-{
-	unsigned int frame = 0;
-	int ret = 0;
+	ret = spi_sync(mc13783->spidev, &m);
 
-	if (reg_num > MC13783_MAX_REG_NUM)
-		return -EINVAL;
+	/* error in message.status implies error return from spi_sync */
+	BUG_ON(!ret && m.status);
 
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
+	if (ret)
+		return ret;
 
-	ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	*val &= 0xffffff;
 
-	*reg_val = frame & MC13783_FRAME_MASK;
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
 
-	return ret;
+	return 0;
 }
+EXPORT_SYMBOL(mc13783_reg_read);
 
-static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
 {
-	unsigned int frame = 0;
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
 
-	if (reg_num > MC13783_MAX_REG_NUM)
+	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+	if (offset > MC13783_NUMREGS || val > 0xffffff)
 		return -EINVAL;
 
-	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
-	frame |= reg_num << MC13783_REG_NUM_SHIFT;
-	frame |= reg_val & MC13783_FRAME_MASK;
+	buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
+
+	memset(&t, 0, sizeof(t));
 
-	return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
+	t.tx_buf = &buf;
+	t.rx_buf = &buf;
+	t.len = sizeof(u32);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13783->spidev, &m);
+
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	return 0;
 }
+EXPORT_SYMBOL(mc13783_reg_write);
 
-int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val)
 {
 	int ret;
+	u32 valread;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_read(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	BUG_ON(val & ~mask);
 
-	return ret;
+	ret = mc13783_reg_read(mc13783, offset, &valread);
+	if (ret)
+		return ret;
+
+	valread = (valread & ~mask) | val;
+
+	return mc13783_reg_write(mc13783, offset, valread);
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_read);
+EXPORT_SYMBOL(mc13783_reg_rmw);
 
-int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
+int mc13783_mask(struct mc13783 *mc13783, int irq)
 {
 	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
 
-	mutex_lock(&mc13783->io_lock);
-	ret = mc13783_write(mc13783, reg_num, reg_val);
-	mutex_unlock(&mc13783->io_lock);
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
 
-	return ret;
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	if (mask & irqbit)
+		/* already masked */
+		return 0;
+
+	return mc13783_reg_write(mc13783, offmask, mask | irqbit);
 }
-EXPORT_SYMBOL_GPL(mc13783_reg_write);
+EXPORT_SYMBOL(mc13783_mask);
 
-/**
- * mc13783_set_bits - Bitmask write
- *
- * @mc13783: Pointer to mc13783 control structure
- * @reg:    Register to access
- * @mask:   Mask of bits to change
- * @val:    Value to set for masked bits
- */
-int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
+int mc13783_unmask(struct mc13783 *mc13783, int irq)
 {
-	u32 tmp;
 	int ret;
+	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
 
-	mutex_lock(&mc13783->io_lock);
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
+		return -EINVAL;
 
-	ret = mc13783_read(mc13783, reg, &tmp);
-	tmp = (tmp & ~mask) | val;
-	if (ret == 0)
-		ret = mc13783_write(mc13783, reg, tmp);
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
 
-	mutex_unlock(&mc13783->io_lock);
+	if (!(mask & irqbit))
+		/* already unmasked */
+		return 0;
 
-	return ret;
+	return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
 }
-EXPORT_SYMBOL_GPL(mc13783_set_bits);
+EXPORT_SYMBOL(mc13783_unmask);
 
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data)
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+	BUG_ON(!handler);
+
+	if (irq < 0 || irq >= MC13783_NUM_IRQ)
 		return -EINVAL;
 
-	if (WARN_ON(mc13783->irq_handler[irq].handler))
+	if (mc13783->irqhandler[irq])
 		return -EBUSY;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = handler;
-	mc13783->irq_handler[irq].data = data;
-	mutex_unlock(&mc13783->io_lock);
+	mc13783->irqhandler[irq] = handler;
+	mc13783->irqdata[irq] = dev;
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_register_irq);
+EXPORT_SYMBOL(mc13783_irq_request_nounmask);
 
-int mc13783_free_irq(struct mc13783 *mc13783, int irq)
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev)
 {
-	if (irq < 0 || irq > MC13783_NUM_IRQ)
+	int ret;
+
+	ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
+	if (ret)
+		return ret;
+
+	ret = mc13783_unmask(mc13783, irq);
+	if (ret) {
+		mc13783->irqhandler[irq] = NULL;
+		mc13783->irqdata[irq] = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13783_irq_request);
+
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
+{
+	int ret;
+	BUG_ON(!mutex_is_locked(&mc13783->lock));
+
+	if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
+			mc13783->irqdata[irq] != dev)
 		return -EINVAL;
 
-	mutex_lock(&mc13783->io_lock);
-	mc13783->irq_handler[irq].handler = NULL;
-	mutex_unlock(&mc13783->io_lock);
+	ret = mc13783_mask(mc13783, irq);
+	if (ret)
+		return ret;
+
+	mc13783->irqhandler[irq] = NULL;
+	mc13783->irqdata[irq] = NULL;
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(mc13783_free_irq);
+EXPORT_SYMBOL(mc13783_irq_free);
 
-static void mc13783_irq_work(struct work_struct *work)
+static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
 {
-	struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
-	int i;
-	unsigned int adc_sts;
-
-	/* check if the adc has finished any completion */
-	mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
-			adc_sts & MC13783_INT_STAT_ADCDONEI);
-
-	if (adc_sts & MC13783_INT_STAT_ADCDONEI)
-		complete_all(&mc13783->adc_done);
-
-	for (i = 0; i < MC13783_NUM_IRQ; i++)
-		if (mc13783->irq_handler[i].handler)
-			mc13783->irq_handler[i].handler(i,
-					mc13783->irq_handler[i].data);
-	enable_irq(mc13783->irq);
+	return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
 }
 
-static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
+int mc13783_ackirq(struct mc13783 *mc13783, int irq)
 {
-	struct mc13783 *mc13783 = dev_id;
+	unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
 
-	disable_irq_nosync(irq);
+	BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
 
-	schedule_work(&mc13783->work);
-	return IRQ_HANDLED;
+	return mc13783_reg_write(mc13783, offstat, val);
 }
+EXPORT_SYMBOL(mc13783_ackirq);
 
-/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
-static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13783->lock
+ */
+static int mc13783_irq_handle(struct mc13783 *mc13783,
+		unsigned int offstat, unsigned int offmask, int baseirq)
 {
-	unsigned int reg_adc0, reg_adc1;
+	u32 stat, mask;
+	int ret = mc13783_reg_read(mc13783, offstat, &stat);
+	int num_handled = 0;
+
+	if (ret)
+		return ret;
+
+	ret = mc13783_reg_read(mc13783, offmask, &mask);
+	if (ret)
+		return ret;
+
+	while (stat & ~mask) {
+		int irq = __ffs(stat & ~mask);
+
+		stat &= ~(1 << irq);
+
+		if (likely(mc13783->irqhandler[baseirq + irq])) {
+			irqreturn_t handled;
 
-	reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
-			| MC13783_ADC0_TSMOD0;
-	reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+			handled = mc13783_irqhandler(mc13783, baseirq + irq);
+			if (handled == IRQ_HANDLED)
+				num_handled++;
+		} else {
+			dev_err(&mc13783->spidev->dev,
+					"BUG: irq %u but no handler\n",
+					baseirq + irq);
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+			mask |= 1 << irq;
+
+			ret = mc13783_reg_write(mc13783, offmask, mask);
+		}
+	}
+
+	return num_handled;
 }
 
+static irqreturn_t mc13783_irq_thread(int irq, void *data)
+{
+	struct mc13783 *mc13783 = data;
+	irqreturn_t ret;
+	int handled = 0;
+
+	mc13783_lock(mc13783);
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
+			MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
+	if (ret > 0)
+		handled = 1;
+
+	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
+			MC13783_IRQMASK1, MC13783_IRQ_1HZ);
+	if (ret > 0)
+		handled = 1;
+
+	mc13783_unlock(mc13783);
+
+	return IRQ_RETVAL(handled);
+}
+
+#define MC13783_ADC1_CHAN0_SHIFT	5
+#define MC13783_ADC1_CHAN1_SHIFT	8
+
+struct mc13783_adcdone_data {
+	struct mc13783 *mc13783;
+	struct completion done;
+};
+
+static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
+{
+	struct mc13783_adcdone_data *adcdone_data = data;
+
+	mc13783_ackirq(adcdone_data->mc13783, irq);
+
+	complete_all(&adcdone_data->done);
+
+	return IRQ_HANDLED;
+}
+
+#define MC13783_ADC_WORKING (1 << 16)
+
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample)
 {
-	unsigned int reg_adc0, reg_adc1;
-	int i;
+	u32 adc0, adc1, old_adc0;
+	int i, ret;
+	struct mc13783_adcdone_data adcdone_data = {
+		.mc13783 = mc13783,
+	};
+	init_completion(&adcdone_data.done);
+
+	dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
+
+	mc13783_lock(mc13783);
+
+	if (mc13783->flags & MC13783_ADC_WORKING) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	mc13783->flags |= MC13783_ADC_WORKING;
 
-	mutex_lock(&mc13783->adc_conv_lock);
+	mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
 
-	/* set up auto incrementing anyway to make quick read */
-	reg_adc0 =  MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
-	/* enable the adc, ignore external triggering and set ASC to trigger
-	 * conversion */
-	reg_adc1 =  MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
-		| MC13783_ADC1_ASC;
+	adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+	adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
 
-	/* setup channel number */
 	if (channel > 7)
-		reg_adc1 |= MC13783_ADC1_ADSEL;
+		adc1 |= MC13783_ADC1_ADSEL;
 
 	switch (mode) {
 	case MC13783_ADC_MODE_TS:
-		/* enables touch screen reference mode and set touchscreen mode
-		 * to position mode */
-		reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
 			| MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	case MC13783_ADC_MODE_SINGLE_CHAN:
-		reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
-		reg_adc1 |= MC13783_ADC1_RAND;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+		adc1 |= MC13783_ADC1_RAND;
 		break;
+
 	case MC13783_ADC_MODE_MULT_CHAN:
-		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
+		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
+
 	default:
+		mc13783_unlock(mc13783);
 		return -EINVAL;
 	}
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
 
-	wait_for_completion_interruptible(&mc13783->adc_done);
+	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
+	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
+			mc13783_handler_adcdone, __func__, &adcdone_data);
 
-	for (i = 0; i < 4; i++)
-		mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
+	mc13783_unlock(mc13783);
 
-	if (mc13783->ts_active)
-		mc13783_adc_set_ts_irq_mode(mc13783);
+	ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
 
-	mutex_unlock(&mc13783->adc_conv_lock);
+	if (!ret)
+		ret = -ETIMEDOUT;
 
-	return 0;
+	mc13783_lock(mc13783);
+
+	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
+
+	if (mode == MC13783_ADC_MODE_TS)
+		/* restore TSMOD */
+		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
+
+	if (ret > 0)
+		for (i = 0; i < 4; ++i)
+			mc13783_reg_read(mc13783,
+					MC13783_REG_ADC_2, &sample[i]);
+
+	mc13783->flags &= ~MC13783_ADC_WORKING;
+out:
+	mc13783_unlock(mc13783);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
+		const char *name, void *pdata, size_t pdata_size)
 {
-	mc13783->ts_active = status;
+	struct mfd_cell cell = {
+		.name = name,
+		.platform_data = pdata,
+		.data_size = pdata_size,
+	};
+
+	return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
+{
+	return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
 }
-EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
 
 static int mc13783_check_revision(struct mc13783 *mc13783)
 {
 	u32 rev_id, rev1, rev2, finid, icid;
 
-	mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
+	mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
 
 	rev1 = (rev_id & 0x018) >> 3;
 	rev2 = (rev_id & 0x007);
@@ -292,38 +551,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
 		rev1 = 3;
 
 	if (rev1 == 0 || icid != 2) {
-		dev_err(mc13783->dev, "No MC13783 detected.\n");
+		dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
 		return -ENODEV;
 	}
 
-	mc13783->revision = ((rev1 * 10) + rev2);
-	dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
-	       rev2, finid);
+	dev_info(&mc13783->spidev->dev,
+			"MC13783 Rev %d.%d FinVer %x detected\n",
+			rev1, rev2, finid);
 
 	return 0;
 }
 
-/*
- * Register a client device.  This is non-fatal since there is no need to
- * fail the entire device init due to a single platform device failing.
- */
-static void mc13783_client_dev_register(struct mc13783 *mc13783,
-				       const char *name)
-{
-	struct mfd_cell cell = {};
-
-	cell.name = name;
-
-	mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
-}
-
-static int __devinit mc13783_probe(struct spi_device *spi)
+static int mc13783_probe(struct spi_device *spi)
 {
 	struct mc13783 *mc13783;
-	struct mc13783_platform_data *pdata = spi->dev.platform_data;
+	struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
 	int ret;
 
-	mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+	mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
 	if (!mc13783)
 		return -ENOMEM;
 
@@ -332,96 +577,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
 	spi->bits_per_word = 32;
 	spi_setup(spi);
 
-	mc13783->spi_device = spi;
-	mc13783->dev = &spi->dev;
-	mc13783->irq = spi->irq;
+	mc13783->spidev = spi;
+
+	mutex_init(&mc13783->lock);
+	mc13783_lock(mc13783);
+
+	ret = mc13783_check_revision(mc13783);
+	if (ret)
+		goto err_revision;
+
+	/* mask all irqs */
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
+	if (ret)
+		goto err_mask;
 
-	INIT_WORK(&mc13783->work, mc13783_irq_work);
-	mutex_init(&mc13783->io_lock);
-	mutex_init(&mc13783->adc_conv_lock);
-	init_completion(&mc13783->adc_done);
+	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
+			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
+
+	if (ret) {
+err_mask:
+err_revision:
+		mutex_unlock(&mc13783->lock);
+		dev_set_drvdata(&spi->dev, NULL);
+		kfree(mc13783);
+		return ret;
+	}
 
+	/* This should go away (BEGIN) */
 	if (pdata) {
 		mc13783->flags = pdata->flags;
 		mc13783->regulators = pdata->regulators;
 		mc13783->num_regulators = pdata->num_regulators;
 	}
+	/* This should go away (END) */
 
-	if (mc13783_check_revision(mc13783)) {
-		ret = -ENODEV;
-		goto err_out;
+	if (pdata->flags & MC13783_USE_ADC)
+		mc13783_add_subdevice(mc13783, "mc13783-adc");
+
+	if (pdata->flags & MC13783_USE_CODEC)
+		mc13783_add_subdevice(mc13783, "mc13783-codec");
+
+	if (pdata->flags & MC13783_USE_REGULATOR) {
+		struct mc13783_regulator_platform_data regulator_pdata = {
+			.num_regulators = pdata->num_regulators,
+			.regulators = pdata->regulators,
+		};
+
+		mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
+				&regulator_pdata, sizeof(regulator_pdata));
 	}
 
-	/* clear and mask all interrupts */
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
-	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
+	if (pdata->flags & MC13783_USE_RTC)
+		mc13783_add_subdevice(mc13783, "mc13783-rtc");
 
-	/* unmask adcdone interrupts */
-	mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
-			MC13783_INT_MASK_ADCDONEM, 0);
+	if (pdata->flags & MC13783_USE_TOUCHSCREEN)
+		mc13783_add_subdevice(mc13783, "mc13783-ts");
 
-	ret = request_irq(mc13783->irq, mc13783_interrupt,
-			IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
-			mc13783);
-	if (ret)
-		goto err_out;
-
-	if (mc13783->flags & MC13783_USE_CODEC)
-		mc13783_client_dev_register(mc13783, "mc13783-codec");
-	if (mc13783->flags & MC13783_USE_ADC)
-		mc13783_client_dev_register(mc13783, "mc13783-adc");
-	if (mc13783->flags & MC13783_USE_RTC)
-		mc13783_client_dev_register(mc13783, "mc13783-rtc");
-	if (mc13783->flags & MC13783_USE_REGULATOR)
-		mc13783_client_dev_register(mc13783, "mc13783-regulator");
-	if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
-		mc13783_client_dev_register(mc13783, "mc13783-ts");
+	mc13783_unlock(mc13783);
 
 	return 0;
-
-err_out:
-	kfree(mc13783);
-	return ret;
 }
 
 static int __devexit mc13783_remove(struct spi_device *spi)
 {
-	struct mc13783 *mc13783;
+	struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
 
-	mc13783 = dev_get_drvdata(&spi->dev);
-
-	free_irq(mc13783->irq, mc13783);
+	free_irq(mc13783->spidev->irq, mc13783);
 
 	mfd_remove_devices(&spi->dev);
 
 	return 0;
 }
 
-static struct spi_driver pmic_driver = {
+static struct spi_driver mc13783_driver = {
 	.driver = {
-		   .name = "mc13783",
-		   .bus = &spi_bus_type,
-		   .owner = THIS_MODULE,
+		.name = "mc13783",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
 	},
 	.probe = mc13783_probe,
 	.remove = __devexit_p(mc13783_remove),
 };
 
-static int __init pmic_init(void)
+static int __init mc13783_init(void)
 {
-	return spi_register_driver(&pmic_driver);
+	return spi_register_driver(&mc13783_driver);
 }
-subsys_initcall(pmic_init);
+subsys_initcall(mc13783_init);
 
-static void __exit pmic_exit(void)
+static void __exit mc13783_exit(void)
 {
-	spi_unregister_driver(&pmic_driver);
+	spi_unregister_driver(&mc13783_driver);
 }
-module_exit(pmic_exit);
-
-MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
 
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index 47e698c..95cf936 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -24,52 +24,23 @@
 
 #include <linux/platform_device.h>
 #include <linux/mfd/mc13783.h>
-#include <linux/workqueue.h>
 #include <linux/mutex.h>
-
-struct mc13783_irq {
-	void (*handler)(int, void *);
-	void *data;
-};
-
-#define MC13783_NUM_IRQ		2
-#define MC13783_IRQ_TS		0
-#define MC13783_IRQ_REGULATOR	1
-
-#define MC13783_ADC_MODE_TS		1
-#define MC13783_ADC_MODE_SINGLE_CHAN	2
-#define MC13783_ADC_MODE_MULT_CHAN	3
+#include <linux/interrupt.h>
 
 struct mc13783 {
-	int revision;
-	struct device *dev;
-	struct spi_device *spi_device;
-
-	int (*read_dev)(void *data, char reg, int count, u32 *dst);
-	int (*write_dev)(void *data, char reg, int count, const u32 *src);
-
-	struct mutex io_lock;
-	void *io_data;
+	struct spi_device *spidev;
+	struct mutex lock;
 	int irq;
-	unsigned int flags;
+	int flags;
 
-	struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
-	struct work_struct work;
-	struct completion adc_done;
-	unsigned int ts_active;
-	struct mutex adc_conv_lock;
+	irq_handler_t irqhandler[MC13783_NUM_IRQ];
+	void *irqdata[MC13783_NUM_IRQ];
 
+	/* XXX these should go as platformdata to the regulator subdevice */
 	struct mc13783_regulator_init_data *regulators;
 	int num_regulators;
 };
 
-int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
-int mc13783_reg_write(struct mc13783 *, int, u32);
-int mc13783_set_bits(struct mc13783 *, int, u32, u32);
-int mc13783_free_irq(struct mc13783 *mc13783, int irq);
-int mc13783_register_irq(struct mc13783 *mc13783, int irq,
-		void (*handler) (int, void *), void *data);
-
 #define MC13783_REG_INTERRUPT_STATUS_0		 0
 #define MC13783_REG_INTERRUPT_MASK_0		 1
 #define MC13783_REG_INTERRUPT_SENSE_0		 2
@@ -136,55 +107,6 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_REG_TEST_3			63
 #define MC13783_REG_NB				64
 
-
-/*
- * Interrupt Status
- */
-#define MC13783_INT_STAT_ADCDONEI	(1 << 0)
-#define MC13783_INT_STAT_ADCBISDONEI	(1 << 1)
-#define MC13783_INT_STAT_TSI		(1 << 2)
-#define MC13783_INT_STAT_WHIGHI		(1 << 3)
-#define MC13783_INT_STAT_WLOWI		(1 << 4)
-#define MC13783_INT_STAT_CHGDETI	(1 << 6)
-#define MC13783_INT_STAT_CHGOVI		(1 << 7)
-#define MC13783_INT_STAT_CHGREVI	(1 << 8)
-#define MC13783_INT_STAT_CHGSHORTI	(1 << 9)
-#define MC13783_INT_STAT_CCCVI		(1 << 10)
-#define MC13783_INT_STAT_CHGCURRI	(1 << 11)
-#define MC13783_INT_STAT_BPONI		(1 << 12)
-#define MC13783_INT_STAT_LOBATLI	(1 << 13)
-#define MC13783_INT_STAT_LOBATHI	(1 << 14)
-#define MC13783_INT_STAT_UDPI		(1 << 15)
-#define MC13783_INT_STAT_USBI		(1 << 16)
-#define MC13783_INT_STAT_IDI		(1 << 19)
-#define MC13783_INT_STAT_Unused		(1 << 20)
-#define MC13783_INT_STAT_SE1I		(1 << 21)
-#define MC13783_INT_STAT_CKDETI		(1 << 22)
-#define MC13783_INT_STAT_UDMI		(1 << 23)
-
-/*
- * Interrupt Mask
- */
-#define MC13783_INT_MASK_ADCDONEM	(1 << 0)
-#define MC13783_INT_MASK_ADCBISDONEM	(1 << 1)
-#define MC13783_INT_MASK_TSM		(1 << 2)
-#define MC13783_INT_MASK_WHIGHM		(1 << 3)
-#define MC13783_INT_MASK_WLOWM		(1 << 4)
-#define MC13783_INT_MASK_CHGDETM	(1 << 6)
-#define MC13783_INT_MASK_CHGOVM		(1 << 7)
-#define MC13783_INT_MASK_CHGREVM	(1 << 8)
-#define MC13783_INT_MASK_CHGSHORTM	(1 << 9)
-#define MC13783_INT_MASK_CCCVM		(1 << 10)
-#define MC13783_INT_MASK_CHGCURRM	(1 << 11)
-#define MC13783_INT_MASK_BPONM		(1 << 12)
-#define MC13783_INT_MASK_LOBATLM	(1 << 13)
-#define MC13783_INT_MASK_LOBATHM	(1 << 14)
-#define MC13783_INT_MASK_UDPM		(1 << 15)
-#define MC13783_INT_MASK_USBM		(1 << 16)
-#define MC13783_INT_MASK_IDM		(1 << 19)
-#define MC13783_INT_MASK_SE1M		(1 << 21)
-#define MC13783_INT_MASK_CKDETM		(1 << 22)
-
 /*
  * Reg Regulator Mode 0
  */
@@ -284,113 +206,15 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
 #define MC13783_SWCTRL_SW3_STBY		(1 << 21)
 #define MC13783_SWCTRL_SW3_MODE		(1 << 22)
 
-/*
- * ADC/Touch
- */
-#define MC13783_ADC0_LICELLCON		(1 << 0)
-#define MC13783_ADC0_CHRGICON		(1 << 1)
-#define MC13783_ADC0_BATICON		(1 << 2)
-#define MC13783_ADC0_RTHEN 		(1 << 3)
-#define MC13783_ADC0_DTHEN		(1 << 4)
-#define MC13783_ADC0_UIDEN		(1 << 5)
-#define MC13783_ADC0_ADOUTEN 		(1 << 6)
-#define MC13783_ADC0_ADOUTPER		(1 << 7)
-#define MC13783_ADC0_ADREFEN		(1 << 10)
-#define MC13783_ADC0_ADREFMODE		(1 << 11)
-#define MC13783_ADC0_TSMOD0		(1 << 12)
-#define MC13783_ADC0_TSMOD1		(1 << 13)
-#define MC13783_ADC0_TSMOD2		(1 << 14)
-#define MC13783_ADC0_CHRGRAWDIV		(1 << 15)
-#define MC13783_ADC0_ADINC1		(1 << 16)
-#define MC13783_ADC0_ADINC2		(1 << 17)
-#define MC13783_ADC0_WCOMP		(1 << 18)
-#define MC13783_ADC0_ADCBIS0		(1 << 23)
-
-#define MC13783_ADC1_ADEN		(1 << 0)
-#define MC13783_ADC1_RAND		(1 << 1)
-#define MC13783_ADC1_ADSEL		(1 << 3)
-#define MC13783_ADC1_TRIGMASK		(1 << 4)
-#define MC13783_ADC1_ADA10		(1 << 5)
-#define MC13783_ADC1_ADA11		(1 << 6)
-#define MC13783_ADC1_ADA12		(1 << 7)
-#define MC13783_ADC1_ADA20		(1 << 8)
-#define MC13783_ADC1_ADA21		(1 << 9)
-#define MC13783_ADC1_ADA22		(1 << 10)
-#define MC13783_ADC1_ATO0		(1 << 11)
-#define MC13783_ADC1_ATO1		(1 << 12)
-#define MC13783_ADC1_ATO2		(1 << 13)
-#define MC13783_ADC1_ATO3		(1 << 14)
-#define MC13783_ADC1_ATO4		(1 << 15)
-#define MC13783_ADC1_ATO5		(1 << 16)
-#define MC13783_ADC1_ATO6		(1 << 17)
-#define MC13783_ADC1_ATO7		(1 << 18)
-#define MC13783_ADC1_ATOX		(1 << 19)
-#define MC13783_ADC1_ASC		(1 << 20)
-#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
-#define MC13783_ADC1_ADONESHOT		(1 << 22)
-#define MC13783_ADC1_ADCBIS1		(1 << 23)
-
-#define MC13783_ADC1_CHAN0_SHIFT	5
-#define MC13783_ADC1_CHAN1_SHIFT	8
-
-#define MC13783_ADC2_ADD10		(1 << 2)
-#define MC13783_ADC2_ADD11		(1 << 3)
-#define MC13783_ADC2_ADD12		(1 << 4)
-#define MC13783_ADC2_ADD13		(1 << 5)
-#define MC13783_ADC2_ADD14		(1 << 6)
-#define MC13783_ADC2_ADD15		(1 << 7)
-#define MC13783_ADC2_ADD16		(1 << 8)
-#define MC13783_ADC2_ADD17		(1 << 9)
-#define MC13783_ADC2_ADD18		(1 << 10)
-#define MC13783_ADC2_ADD19		(1 << 11)
-#define MC13783_ADC2_ADD20		(1 << 14)
-#define MC13783_ADC2_ADD21		(1 << 15)
-#define MC13783_ADC2_ADD22		(1 << 16)
-#define MC13783_ADC2_ADD23		(1 << 17)
-#define MC13783_ADC2_ADD24		(1 << 18)
-#define MC13783_ADC2_ADD25		(1 << 19)
-#define MC13783_ADC2_ADD26		(1 << 20)
-#define MC13783_ADC2_ADD27		(1 << 21)
-#define MC13783_ADC2_ADD28		(1 << 22)
-#define MC13783_ADC2_ADD29		(1 << 23)
+static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val)
+{
+	int ret;
+	mc13783_lock(mc13783);
+	ret = mc13783_reg_rmw(mc13783, offset, mask, val);
+	mc13783_unlock(mc13783);
 
-#define MC13783_ADC3_WHIGH0		(1 << 0)
-#define MC13783_ADC3_WHIGH1		(1 << 1)
-#define MC13783_ADC3_WHIGH2		(1 << 2)
-#define MC13783_ADC3_WHIGH3		(1 << 3)
-#define MC13783_ADC3_WHIGH4		(1 << 4)
-#define MC13783_ADC3_WHIGH5		(1 << 5)
-#define MC13783_ADC3_ICID0		(1 << 6)
-#define MC13783_ADC3_ICID1		(1 << 7)
-#define MC13783_ADC3_ICID2		(1 << 8)
-#define MC13783_ADC3_WLOW0		(1 << 9)
-#define MC13783_ADC3_WLOW1		(1 << 10)
-#define MC13783_ADC3_WLOW2		(1 << 11)
-#define MC13783_ADC3_WLOW3		(1 << 12)
-#define MC13783_ADC3_WLOW4		(1 << 13)
-#define MC13783_ADC3_WLOW5		(1 << 14)
-#define MC13783_ADC3_ADCBIS2		(1 << 23)
-
-#define MC13783_ADC4_ADDBIS10		(1 << 2)
-#define MC13783_ADC4_ADDBIS11		(1 << 3)
-#define MC13783_ADC4_ADDBIS12		(1 << 4)
-#define MC13783_ADC4_ADDBIS13		(1 << 5)
-#define MC13783_ADC4_ADDBIS14		(1 << 6)
-#define MC13783_ADC4_ADDBIS15		(1 << 7)
-#define MC13783_ADC4_ADDBIS16		(1 << 8)
-#define MC13783_ADC4_ADDBIS17		(1 << 9)
-#define MC13783_ADC4_ADDBIS18		(1 << 10)
-#define MC13783_ADC4_ADDBIS19		(1 << 11)
-#define MC13783_ADC4_ADDBIS20		(1 << 14)
-#define MC13783_ADC4_ADDBIS21		(1 << 15)
-#define MC13783_ADC4_ADDBIS22		(1 << 16)
-#define MC13783_ADC4_ADDBIS23		(1 << 17)
-#define MC13783_ADC4_ADDBIS24		(1 << 18)
-#define MC13783_ADC4_ADDBIS25		(1 << 19)
-#define MC13783_ADC4_ADDBIS26		(1 << 20)
-#define MC13783_ADC4_ADDBIS27		(1 << 21)
-#define MC13783_ADC4_ADDBIS28		(1 << 22)
-#define MC13783_ADC4_ADDBIS29		(1 << 23)
+	return ret;
+}
 
 #endif /* __LINUX_MFD_MC13783_PRIV_H */
-
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index b3a2a72..3568040 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -1,28 +1,50 @@
 /*
- * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
  *
- * Initial development of this code was funded by
- * Phytec Messtechnik GmbH, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
  */
+#ifndef __LINUX_MFD_MC13783_H
+#define __LINUX_MFD_MC13783_H
 
-#ifndef __INCLUDE_LINUX_MFD_MC13783_H
-#define __INCLUDE_LINUX_MFD_MC13783_H
+#include <linux/interrupt.h>
 
 struct mc13783;
+
+void mc13783_lock(struct mc13783 *mc13783);
+void mc13783_unlock(struct mc13783 *mc13783);
+
+int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
+int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
+int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
+		u32 mask, u32 val);
+
+int mc13783_irq_request(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
+		irq_handler_t handler, const char *name, void *dev);
+int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
+int mc13783_ackirq(struct mc13783 *mc13783, int irq);
+
+int mc13783_mask(struct mc13783 *mc13783, int irq);
+int mc13783_unmask(struct mc13783 *mc13783, int irq);
+
+#define MC13783_ADC0		43
+#define MC13783_ADC0_ADREFEN		(1 << 10)
+#define MC13783_ADC0_ADREFMODE		(1 << 11)
+#define MC13783_ADC0_TSMOD0		(1 << 12)
+#define MC13783_ADC0_TSMOD1		(1 << 13)
+#define MC13783_ADC0_TSMOD2		(1 << 14)
+#define MC13783_ADC0_ADINC1		(1 << 16)
+#define MC13783_ADC0_ADINC2		(1 << 17)
+
+#define MC13783_ADC0_TSMOD_MASK		(MC13783_ADC0_TSMOD0 | \
+					MC13783_ADC0_TSMOD1 | \
+					MC13783_ADC0_TSMOD2)
+
+/* to be cleaned up */
 struct regulator_init_data;
 
 struct mc13783_regulator_init_data {
@@ -30,23 +52,30 @@ struct mc13783_regulator_init_data {
 	struct regulator_init_data *init_data;
 };
 
-struct mc13783_platform_data {
-	struct mc13783_regulator_init_data *regulators;
+struct mc13783_regulator_platform_data {
 	int num_regulators;
-	unsigned int flags;
+	struct mc13783_regulator_init_data *regulators;
 };
 
-/* mc13783_platform_data flags */
+struct mc13783_platform_data {
+	int num_regulators;
+	struct mc13783_regulator_init_data *regulators;
+
 #define MC13783_USE_TOUCHSCREEN (1 << 0)
 #define MC13783_USE_CODEC	(1 << 1)
 #define MC13783_USE_ADC		(1 << 2)
 #define MC13783_USE_RTC		(1 << 3)
 #define MC13783_USE_REGULATOR	(1 << 4)
+	unsigned int flags;
+};
+
+#define MC13783_ADC_MODE_TS		1
+#define MC13783_ADC_MODE_SINGLE_CHAN	2
+#define MC13783_ADC_MODE_MULT_CHAN	3
 
 int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		unsigned int channel, unsigned int *sample);
 
-void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 
 #define	MC13783_SW_SW1A		0
 #define	MC13783_SW_SW1B		1
@@ -80,5 +109,46 @@ void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
 #define	MC13783_REGU_V3		29
 #define	MC13783_REGU_V4		30
 
-#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
+#define MC13783_IRQ_ADCDONE	0
+#define MC13783_IRQ_ADCBISDONE	1
+#define MC13783_IRQ_TS		2
+#define MC13783_IRQ_WHIGH	3
+#define MC13783_IRQ_WLOW	4
+#define MC13783_IRQ_CHGDET	6
+#define MC13783_IRQ_CHGOV	7
+#define MC13783_IRQ_CHGREV	8
+#define MC13783_IRQ_CHGSHORT	9
+#define MC13783_IRQ_CCCV	10
+#define MC13783_IRQ_CHGCURR	11
+#define MC13783_IRQ_BPON	12
+#define MC13783_IRQ_LOBATL	13
+#define MC13783_IRQ_LOBATH	14
+#define MC13783_IRQ_UDP		15
+#define MC13783_IRQ_USB		16
+#define MC13783_IRQ_ID		19
+#define MC13783_IRQ_SE1		21
+#define MC13783_IRQ_CKDET	22
+#define MC13783_IRQ_UDM		23
+#define MC13783_IRQ_1HZ		24
+#define MC13783_IRQ_TODA	25
+#define MC13783_IRQ_ONOFD1	27
+#define MC13783_IRQ_ONOFD2	28
+#define MC13783_IRQ_ONOFD3	29
+#define MC13783_IRQ_SYSRST	30
+#define MC13783_IRQ_RTCRST	31
+#define MC13783_IRQ_PC		32
+#define MC13783_IRQ_WARM	33
+#define MC13783_IRQ_MEMHLD	34
+#define MC13783_IRQ_PWRRDY	35
+#define MC13783_IRQ_THWARNL	36
+#define MC13783_IRQ_THWARNH	37
+#define MC13783_IRQ_CLK		38
+#define MC13783_IRQ_SEMAF	39
+#define MC13783_IRQ_MC2B	41
+#define MC13783_IRQ_HSDET	42
+#define MC13783_IRQ_HSL		43
+#define MC13783_IRQ_ALSPTH	44
+#define MC13783_IRQ_AHSSHORT	45
+#define MC13783_NUM_IRQ		46
 
+#endif /* __LINUX_MFD_MC13783_H */
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-05 23:56               ` Uwe Kleine-König
@ 2009-11-06  0:28                 ` Samuel Ortiz
  2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
  2009-11-24 21:44                   ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  0 siblings, 2 replies; 40+ messages in thread
From: Samuel Ortiz @ 2009-11-06  0:28 UTC (permalink / raw)
  To: Uwe Kleine-König; +Cc: linux-kernel, Sascha Hauer, Mark Brown

On Fri, Nov 06, 2009 at 12:56:08AM +0100, Uwe Kleine-König wrote:
> This fixes several things while still providing the old API:
> 
>  - simplify and fix locking
>  - better error handling
>  - don't ack all irqs making it impossible to detect a reset of the
>    rtc
>  - use a timeout variant to wait for completion of ADC conversion
>  - provide platform-data to regulator subdevice (This allows making
>    struct mc13783 opaque for other drivers after the regulator driver is
>    updated to use its platform_data.)
>  - expose all interrupts
>  - use threaded irq
Thanks Uwe, patch applied to my for-next branch.

Cheers,
Samuel.

 
> After all users in mainline are converted to the new API, some things
> (e.g. mc13783-private.h) can go away.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---
>  drivers/mfd/mc13783-core.c          |  751 +++++++++++++++++++++++------------
>  include/linux/mfd/mc13783-private.h |  208 +---------
>  include/linux/mfd/mc13783.h         |  120 +++++--
>  3 files changed, 613 insertions(+), 466 deletions(-)
> 
> diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
> index e354d29..99267ed 100644
> --- a/drivers/mfd/mc13783-core.c
> +++ b/drivers/mfd/mc13783-core.c
> @@ -1,286 +1,545 @@
>  /*
> - * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
> - *
> - * This code is in parts based on wm8350-core.c and pcf50633-core.c
> - *
> - * Initial development of this code was funded by
> - * Phytec Messtechnik GmbH, http://www.phytec.de
> + * Copyright 2009 Pengutronix
> + * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
>   *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that 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.
> + * loosely based on an earlier driver that has
> + * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
>   *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
>   */
> -
> -#include <linux/mfd/mc13783-private.h>
> -#include <linux/platform_device.h>
> -#include <linux/mfd/mc13783.h>
> -#include <linux/completion.h>
> -#include <linux/interrupt.h>
> -#include <linux/mfd/core.h>
> -#include <linux/spi/spi.h>
> -#include <linux/uaccess.h>
> -#include <linux/kernel.h>
>  #include <linux/module.h>
> -#include <linux/init.h>
> -#include <linux/slab.h>
> -#include <linux/irq.h>
> +#include <linux/spi/spi.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/mc13783-private.h>
> +
> +#define MC13783_IRQSTAT0	0
> +#define MC13783_IRQSTAT0_ADCDONEI	(1 << 0)
> +#define MC13783_IRQSTAT0_ADCBISDONEI	(1 << 1)
> +#define MC13783_IRQSTAT0_TSI		(1 << 2)
> +#define MC13783_IRQSTAT0_WHIGHI		(1 << 3)
> +#define MC13783_IRQSTAT0_WLOWI		(1 << 4)
> +#define MC13783_IRQSTAT0_CHGDETI	(1 << 6)
> +#define MC13783_IRQSTAT0_CHGOVI		(1 << 7)
> +#define MC13783_IRQSTAT0_CHGREVI	(1 << 8)
> +#define MC13783_IRQSTAT0_CHGSHORTI	(1 << 9)
> +#define MC13783_IRQSTAT0_CCCVI		(1 << 10)
> +#define MC13783_IRQSTAT0_CHGCURRI	(1 << 11)
> +#define MC13783_IRQSTAT0_BPONI		(1 << 12)
> +#define MC13783_IRQSTAT0_LOBATLI	(1 << 13)
> +#define MC13783_IRQSTAT0_LOBATHI	(1 << 14)
> +#define MC13783_IRQSTAT0_UDPI		(1 << 15)
> +#define MC13783_IRQSTAT0_USBI		(1 << 16)
> +#define MC13783_IRQSTAT0_IDI		(1 << 19)
> +#define MC13783_IRQSTAT0_SE1I		(1 << 21)
> +#define MC13783_IRQSTAT0_CKDETI		(1 << 22)
> +#define MC13783_IRQSTAT0_UDMI		(1 << 23)
> +
> +#define MC13783_IRQMASK0	1
> +#define MC13783_IRQMASK0_ADCDONEM	MC13783_IRQSTAT0_ADCDONEI
> +#define MC13783_IRQMASK0_ADCBISDONEM	MC13783_IRQSTAT0_ADCBISDONEI
> +#define MC13783_IRQMASK0_TSM		MC13783_IRQSTAT0_TSI
> +#define MC13783_IRQMASK0_WHIGHM		MC13783_IRQSTAT0_WHIGHI
> +#define MC13783_IRQMASK0_WLOWM		MC13783_IRQSTAT0_WLOWI
> +#define MC13783_IRQMASK0_CHGDETM	MC13783_IRQSTAT0_CHGDETI
> +#define MC13783_IRQMASK0_CHGOVM		MC13783_IRQSTAT0_CHGOVI
> +#define MC13783_IRQMASK0_CHGREVM	MC13783_IRQSTAT0_CHGREVI
> +#define MC13783_IRQMASK0_CHGSHORTM	MC13783_IRQSTAT0_CHGSHORTI
> +#define MC13783_IRQMASK0_CCCVM		MC13783_IRQSTAT0_CCCVI
> +#define MC13783_IRQMASK0_CHGCURRM	MC13783_IRQSTAT0_CHGCURRI
> +#define MC13783_IRQMASK0_BPONM		MC13783_IRQSTAT0_BPONI
> +#define MC13783_IRQMASK0_LOBATLM	MC13783_IRQSTAT0_LOBATLI
> +#define MC13783_IRQMASK0_LOBATHM	MC13783_IRQSTAT0_LOBATHI
> +#define MC13783_IRQMASK0_UDPM		MC13783_IRQSTAT0_UDPI
> +#define MC13783_IRQMASK0_USBM		MC13783_IRQSTAT0_USBI
> +#define MC13783_IRQMASK0_IDM		MC13783_IRQSTAT0_IDI
> +#define MC13783_IRQMASK0_SE1M		MC13783_IRQSTAT0_SE1I
> +#define MC13783_IRQMASK0_CKDETM		MC13783_IRQSTAT0_CKDETI
> +#define MC13783_IRQMASK0_UDMM		MC13783_IRQSTAT0_UDMI
> +
> +#define MC13783_IRQSTAT1	3
> +#define MC13783_IRQSTAT1_1HZI		(1 << 0)
> +#define MC13783_IRQSTAT1_TODAI		(1 << 1)
> +#define MC13783_IRQSTAT1_ONOFD1I	(1 << 3)
> +#define MC13783_IRQSTAT1_ONOFD2I	(1 << 4)
> +#define MC13783_IRQSTAT1_ONOFD3I	(1 << 5)
> +#define MC13783_IRQSTAT1_SYSRSTI	(1 << 6)
> +#define MC13783_IRQSTAT1_RTCRSTI	(1 << 7)
> +#define MC13783_IRQSTAT1_PCI		(1 << 8)
> +#define MC13783_IRQSTAT1_WARMI		(1 << 9)
> +#define MC13783_IRQSTAT1_MEMHLDI	(1 << 10)
> +#define MC13783_IRQSTAT1_PWRRDYI	(1 << 11)
> +#define MC13783_IRQSTAT1_THWARNLI	(1 << 12)
> +#define MC13783_IRQSTAT1_THWARNHI	(1 << 13)
> +#define MC13783_IRQSTAT1_CLKI		(1 << 14)
> +#define MC13783_IRQSTAT1_SEMAFI		(1 << 15)
> +#define MC13783_IRQSTAT1_MC2BI		(1 << 17)
> +#define MC13783_IRQSTAT1_HSDETI		(1 << 18)
> +#define MC13783_IRQSTAT1_HSLI		(1 << 19)
> +#define MC13783_IRQSTAT1_ALSPTHI	(1 << 20)
> +#define MC13783_IRQSTAT1_AHSSHORTI	(1 << 21)
> +
> +#define MC13783_IRQMASK1	4
> +#define MC13783_IRQMASK1_1HZM		MC13783_IRQSTAT1_1HZI
> +#define MC13783_IRQMASK1_TODAM		MC13783_IRQSTAT1_TODAI
> +#define MC13783_IRQMASK1_ONOFD1M	MC13783_IRQSTAT1_ONOFD1I
> +#define MC13783_IRQMASK1_ONOFD2M	MC13783_IRQSTAT1_ONOFD2I
> +#define MC13783_IRQMASK1_ONOFD3M	MC13783_IRQSTAT1_ONOFD3I
> +#define MC13783_IRQMASK1_SYSRSTM	MC13783_IRQSTAT1_SYSRSTI
> +#define MC13783_IRQMASK1_RTCRSTM	MC13783_IRQSTAT1_RTCRSTI
> +#define MC13783_IRQMASK1_PCM		MC13783_IRQSTAT1_PCI
> +#define MC13783_IRQMASK1_WARMM		MC13783_IRQSTAT1_WARMI
> +#define MC13783_IRQMASK1_MEMHLDM	MC13783_IRQSTAT1_MEMHLDI
> +#define MC13783_IRQMASK1_PWRRDYM	MC13783_IRQSTAT1_PWRRDYI
> +#define MC13783_IRQMASK1_THWARNLM	MC13783_IRQSTAT1_THWARNLI
> +#define MC13783_IRQMASK1_THWARNHM	MC13783_IRQSTAT1_THWARNHI
> +#define MC13783_IRQMASK1_CLKM		MC13783_IRQSTAT1_CLKI
> +#define MC13783_IRQMASK1_SEMAFM		MC13783_IRQSTAT1_SEMAFI
> +#define MC13783_IRQMASK1_MC2BM		MC13783_IRQSTAT1_MC2BI
> +#define MC13783_IRQMASK1_HSDETM		MC13783_IRQSTAT1_HSDETI
> +#define MC13783_IRQMASK1_HSLM		MC13783_IRQSTAT1_HSLI
> +#define MC13783_IRQMASK1_ALSPTHM	MC13783_IRQSTAT1_ALSPTHI
> +#define MC13783_IRQMASK1_AHSSHORTM	MC13783_IRQSTAT1_AHSSHORTI
> +
> +#define MC13783_ADC1		44
> +#define MC13783_ADC1_ADEN		(1 << 0)
> +#define MC13783_ADC1_RAND		(1 << 1)
> +#define MC13783_ADC1_ADSEL		(1 << 3)
> +#define MC13783_ADC1_ASC		(1 << 20)
> +#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
> +
> +#define MC13783_NUMREGS 0x3f
> +
> +void mc13783_lock(struct mc13783 *mc13783)
> +{
> +	if (!mutex_trylock(&mc13783->lock)) {
> +		dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
> +				__func__, __builtin_return_address(0));
> +
> +		mutex_lock(&mc13783->lock);
> +	}
> +	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
> +			__func__, __builtin_return_address(0));
> +}
> +EXPORT_SYMBOL(mc13783_lock);
>  
> -#define MC13783_MAX_REG_NUM	0x3f
> -#define MC13783_FRAME_MASK	0x00ffffff
> -#define MC13783_MAX_REG_NUM	0x3f
> -#define MC13783_REG_NUM_SHIFT	0x19
> -#define MC13783_WRITE_BIT_SHIFT	31
> +void mc13783_unlock(struct mc13783 *mc13783)
> +{
> +	dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
> +			__func__, __builtin_return_address(0));
> +	mutex_unlock(&mc13783->lock);
> +}
> +EXPORT_SYMBOL(mc13783_unlock);
>  
> -static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
> +#define MC13783_REGOFFSET_SHIFT 25
> +int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
>  {
> -	struct spi_transfer t = {
> -		.tx_buf = (const void *)buf,
> -		.rx_buf = buf,
> -		.len = len,
> -		.cs_change = 0,
> -		.delay_usecs = 0,
> -	};
> +	struct spi_transfer t;
>  	struct spi_message m;
> +	int ret;
> +
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
> +
> +	if (offset > MC13783_NUMREGS)
> +		return -EINVAL;
> +
> +	*val = offset << MC13783_REGOFFSET_SHIFT;
> +
> +	memset(&t, 0, sizeof(t));
> +
> +	t.tx_buf = val;
> +	t.rx_buf = val;
> +	t.len = sizeof(u32);
>  
>  	spi_message_init(&m);
>  	spi_message_add_tail(&t, &m);
> -	if (spi_sync(spi, &m) != 0 || m.status != 0)
> -		return -EINVAL;
> -	return len - m.actual_length;
> -}
>  
> -static int mc13783_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> -{
> -	unsigned int frame = 0;
> -	int ret = 0;
> +	ret = spi_sync(mc13783->spidev, &m);
>  
> -	if (reg_num > MC13783_MAX_REG_NUM)
> -		return -EINVAL;
> +	/* error in message.status implies error return from spi_sync */
> +	BUG_ON(!ret && m.status);
>  
> -	frame |= reg_num << MC13783_REG_NUM_SHIFT;
> +	if (ret)
> +		return ret;
>  
> -	ret = spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
> +	*val &= 0xffffff;
>  
> -	*reg_val = frame & MC13783_FRAME_MASK;
> +	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
>  
> -	return ret;
> +	return 0;
>  }
> +EXPORT_SYMBOL(mc13783_reg_read);
>  
> -static int mc13783_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
> +int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
>  {
> -	unsigned int frame = 0;
> +	u32 buf;
> +	struct spi_transfer t;
> +	struct spi_message m;
> +	int ret;
> +
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
>  
> -	if (reg_num > MC13783_MAX_REG_NUM)
> +	dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
> +
> +	if (offset > MC13783_NUMREGS || val > 0xffffff)
>  		return -EINVAL;
>  
> -	frame |= (1 << MC13783_WRITE_BIT_SHIFT);
> -	frame |= reg_num << MC13783_REG_NUM_SHIFT;
> -	frame |= reg_val & MC13783_FRAME_MASK;
> +	buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
> +
> +	memset(&t, 0, sizeof(t));
>  
> -	return spi_rw(mc13783->spi_device, (u8 *)&frame, 4);
> +	t.tx_buf = &buf;
> +	t.rx_buf = &buf;
> +	t.len = sizeof(u32);
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t, &m);
> +
> +	ret = spi_sync(mc13783->spidev, &m);
> +
> +	BUG_ON(!ret && m.status);
> +
> +	if (ret)
> +		return ret;
> +
> +	return 0;
>  }
> +EXPORT_SYMBOL(mc13783_reg_write);
>  
> -int mc13783_reg_read(struct mc13783 *mc13783, int reg_num, u32 *reg_val)
> +int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
> +		u32 mask, u32 val)
>  {
>  	int ret;
> +	u32 valread;
>  
> -	mutex_lock(&mc13783->io_lock);
> -	ret = mc13783_read(mc13783, reg_num, reg_val);
> -	mutex_unlock(&mc13783->io_lock);
> +	BUG_ON(val & ~mask);
>  
> -	return ret;
> +	ret = mc13783_reg_read(mc13783, offset, &valread);
> +	if (ret)
> +		return ret;
> +
> +	valread = (valread & ~mask) | val;
> +
> +	return mc13783_reg_write(mc13783, offset, valread);
>  }
> -EXPORT_SYMBOL_GPL(mc13783_reg_read);
> +EXPORT_SYMBOL(mc13783_reg_rmw);
>  
> -int mc13783_reg_write(struct mc13783 *mc13783, int reg_num, u32 reg_val)
> +int mc13783_mask(struct mc13783 *mc13783, int irq)
>  {
>  	int ret;
> +	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
> +	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
> +	u32 mask;
>  
> -	mutex_lock(&mc13783->io_lock);
> -	ret = mc13783_write(mc13783, reg_num, reg_val);
> -	mutex_unlock(&mc13783->io_lock);
> +	if (irq < 0 || irq >= MC13783_NUM_IRQ)
> +		return -EINVAL;
>  
> -	return ret;
> +	ret = mc13783_reg_read(mc13783, offmask, &mask);
> +	if (ret)
> +		return ret;
> +
> +	if (mask & irqbit)
> +		/* already masked */
> +		return 0;
> +
> +	return mc13783_reg_write(mc13783, offmask, mask | irqbit);
>  }
> -EXPORT_SYMBOL_GPL(mc13783_reg_write);
> +EXPORT_SYMBOL(mc13783_mask);
>  
> -/**
> - * mc13783_set_bits - Bitmask write
> - *
> - * @mc13783: Pointer to mc13783 control structure
> - * @reg:    Register to access
> - * @mask:   Mask of bits to change
> - * @val:    Value to set for masked bits
> - */
> -int mc13783_set_bits(struct mc13783 *mc13783, int reg, u32 mask, u32 val)
> +int mc13783_unmask(struct mc13783 *mc13783, int irq)
>  {
> -	u32 tmp;
>  	int ret;
> +	unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
> +	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
> +	u32 mask;
>  
> -	mutex_lock(&mc13783->io_lock);
> +	if (irq < 0 || irq >= MC13783_NUM_IRQ)
> +		return -EINVAL;
>  
> -	ret = mc13783_read(mc13783, reg, &tmp);
> -	tmp = (tmp & ~mask) | val;
> -	if (ret == 0)
> -		ret = mc13783_write(mc13783, reg, tmp);
> +	ret = mc13783_reg_read(mc13783, offmask, &mask);
> +	if (ret)
> +		return ret;
>  
> -	mutex_unlock(&mc13783->io_lock);
> +	if (!(mask & irqbit))
> +		/* already unmasked */
> +		return 0;
>  
> -	return ret;
> +	return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
>  }
> -EXPORT_SYMBOL_GPL(mc13783_set_bits);
> +EXPORT_SYMBOL(mc13783_unmask);
>  
> -int mc13783_register_irq(struct mc13783 *mc13783, int irq,
> -		void (*handler) (int, void *), void *data)
> +int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
> +		irq_handler_t handler, const char *name, void *dev)
>  {
> -	if (irq < 0 || irq > MC13783_NUM_IRQ || !handler)
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
> +	BUG_ON(!handler);
> +
> +	if (irq < 0 || irq >= MC13783_NUM_IRQ)
>  		return -EINVAL;
>  
> -	if (WARN_ON(mc13783->irq_handler[irq].handler))
> +	if (mc13783->irqhandler[irq])
>  		return -EBUSY;
>  
> -	mutex_lock(&mc13783->io_lock);
> -	mc13783->irq_handler[irq].handler = handler;
> -	mc13783->irq_handler[irq].data = data;
> -	mutex_unlock(&mc13783->io_lock);
> +	mc13783->irqhandler[irq] = handler;
> +	mc13783->irqdata[irq] = dev;
>  
>  	return 0;
>  }
> -EXPORT_SYMBOL_GPL(mc13783_register_irq);
> +EXPORT_SYMBOL(mc13783_irq_request_nounmask);
>  
> -int mc13783_free_irq(struct mc13783 *mc13783, int irq)
> +int mc13783_irq_request(struct mc13783 *mc13783, int irq,
> +		irq_handler_t handler, const char *name, void *dev)
>  {
> -	if (irq < 0 || irq > MC13783_NUM_IRQ)
> +	int ret;
> +
> +	ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = mc13783_unmask(mc13783, irq);
> +	if (ret) {
> +		mc13783->irqhandler[irq] = NULL;
> +		mc13783->irqdata[irq] = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mc13783_irq_request);
> +
> +int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
> +{
> +	int ret;
> +	BUG_ON(!mutex_is_locked(&mc13783->lock));
> +
> +	if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
> +			mc13783->irqdata[irq] != dev)
>  		return -EINVAL;
>  
> -	mutex_lock(&mc13783->io_lock);
> -	mc13783->irq_handler[irq].handler = NULL;
> -	mutex_unlock(&mc13783->io_lock);
> +	ret = mc13783_mask(mc13783, irq);
> +	if (ret)
> +		return ret;
> +
> +	mc13783->irqhandler[irq] = NULL;
> +	mc13783->irqdata[irq] = NULL;
>  
>  	return 0;
>  }
> -EXPORT_SYMBOL_GPL(mc13783_free_irq);
> +EXPORT_SYMBOL(mc13783_irq_free);
>  
> -static void mc13783_irq_work(struct work_struct *work)
> +static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
>  {
> -	struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
> -	int i;
> -	unsigned int adc_sts;
> -
> -	/* check if the adc has finished any completion */
> -	mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
> -	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
> -			adc_sts & MC13783_INT_STAT_ADCDONEI);
> -
> -	if (adc_sts & MC13783_INT_STAT_ADCDONEI)
> -		complete_all(&mc13783->adc_done);
> -
> -	for (i = 0; i < MC13783_NUM_IRQ; i++)
> -		if (mc13783->irq_handler[i].handler)
> -			mc13783->irq_handler[i].handler(i,
> -					mc13783->irq_handler[i].data);
> -	enable_irq(mc13783->irq);
> +	return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
>  }
>  
> -static irqreturn_t mc13783_interrupt(int irq, void *dev_id)
> +int mc13783_ackirq(struct mc13783 *mc13783, int irq)
>  {
> -	struct mc13783 *mc13783 = dev_id;
> +	unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
> +	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
>  
> -	disable_irq_nosync(irq);
> +	BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
>  
> -	schedule_work(&mc13783->work);
> -	return IRQ_HANDLED;
> +	return mc13783_reg_write(mc13783, offstat, val);
>  }
> +EXPORT_SYMBOL(mc13783_ackirq);
>  
> -/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
> -static inline void mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
> +/*
> + * returns: number of handled irqs or negative error
> + * locking: holds mc13783->lock
> + */
> +static int mc13783_irq_handle(struct mc13783 *mc13783,
> +		unsigned int offstat, unsigned int offmask, int baseirq)
>  {
> -	unsigned int reg_adc0, reg_adc1;
> +	u32 stat, mask;
> +	int ret = mc13783_reg_read(mc13783, offstat, &stat);
> +	int num_handled = 0;
> +
> +	if (ret)
> +		return ret;
> +
> +	ret = mc13783_reg_read(mc13783, offmask, &mask);
> +	if (ret)
> +		return ret;
> +
> +	while (stat & ~mask) {
> +		int irq = __ffs(stat & ~mask);
> +
> +		stat &= ~(1 << irq);
> +
> +		if (likely(mc13783->irqhandler[baseirq + irq])) {
> +			irqreturn_t handled;
>  
> -	reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
> -			| MC13783_ADC0_TSMOD0;
> -	reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
> +			handled = mc13783_irqhandler(mc13783, baseirq + irq);
> +			if (handled == IRQ_HANDLED)
> +				num_handled++;
> +		} else {
> +			dev_err(&mc13783->spidev->dev,
> +					"BUG: irq %u but no handler\n",
> +					baseirq + irq);
>  
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
> +			mask |= 1 << irq;
> +
> +			ret = mc13783_reg_write(mc13783, offmask, mask);
> +		}
> +	}
> +
> +	return num_handled;
>  }
>  
> +static irqreturn_t mc13783_irq_thread(int irq, void *data)
> +{
> +	struct mc13783 *mc13783 = data;
> +	irqreturn_t ret;
> +	int handled = 0;
> +
> +	mc13783_lock(mc13783);
> +
> +	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
> +			MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
> +	if (ret > 0)
> +		handled = 1;
> +
> +	ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
> +			MC13783_IRQMASK1, MC13783_IRQ_1HZ);
> +	if (ret > 0)
> +		handled = 1;
> +
> +	mc13783_unlock(mc13783);
> +
> +	return IRQ_RETVAL(handled);
> +}
> +
> +#define MC13783_ADC1_CHAN0_SHIFT	5
> +#define MC13783_ADC1_CHAN1_SHIFT	8
> +
> +struct mc13783_adcdone_data {
> +	struct mc13783 *mc13783;
> +	struct completion done;
> +};
> +
> +static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
> +{
> +	struct mc13783_adcdone_data *adcdone_data = data;
> +
> +	mc13783_ackirq(adcdone_data->mc13783, irq);
> +
> +	complete_all(&adcdone_data->done);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +#define MC13783_ADC_WORKING (1 << 16)
> +
>  int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
>  		unsigned int channel, unsigned int *sample)
>  {
> -	unsigned int reg_adc0, reg_adc1;
> -	int i;
> +	u32 adc0, adc1, old_adc0;
> +	int i, ret;
> +	struct mc13783_adcdone_data adcdone_data = {
> +		.mc13783 = mc13783,
> +	};
> +	init_completion(&adcdone_data.done);
> +
> +	dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
> +
> +	mc13783_lock(mc13783);
> +
> +	if (mc13783->flags & MC13783_ADC_WORKING) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	mc13783->flags |= MC13783_ADC_WORKING;
>  
> -	mutex_lock(&mc13783->adc_conv_lock);
> +	mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
>  
> -	/* set up auto incrementing anyway to make quick read */
> -	reg_adc0 =  MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
> -	/* enable the adc, ignore external triggering and set ASC to trigger
> -	 * conversion */
> -	reg_adc1 =  MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
> -		| MC13783_ADC1_ASC;
> +	adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
> +	adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
>  
> -	/* setup channel number */
>  	if (channel > 7)
> -		reg_adc1 |= MC13783_ADC1_ADSEL;
> +		adc1 |= MC13783_ADC1_ADSEL;
>  
>  	switch (mode) {
>  	case MC13783_ADC_MODE_TS:
> -		/* enables touch screen reference mode and set touchscreen mode
> -		 * to position mode */
> -		reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
> +		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
>  			| MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
> -		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
> +		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
>  		break;
> +
>  	case MC13783_ADC_MODE_SINGLE_CHAN:
> -		reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
> -		reg_adc1 |= MC13783_ADC1_RAND;
> +		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
> +		adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
> +		adc1 |= MC13783_ADC1_RAND;
>  		break;
> +
>  	case MC13783_ADC_MODE_MULT_CHAN:
> -		reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
> +		adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
> +		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
>  		break;
> +
>  	default:
> +		mc13783_unlock(mc13783);
>  		return -EINVAL;
>  	}
>  
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
> +	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
> +	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
>  
> -	wait_for_completion_interruptible(&mc13783->adc_done);
> +	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
> +	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
> +			mc13783_handler_adcdone, __func__, &adcdone_data);
>  
> -	for (i = 0; i < 4; i++)
> -		mc13783_reg_read(mc13783, MC13783_REG_ADC_2, &sample[i]);
> +	mc13783_unlock(mc13783);
>  
> -	if (mc13783->ts_active)
> -		mc13783_adc_set_ts_irq_mode(mc13783);
> +	ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
>  
> -	mutex_unlock(&mc13783->adc_conv_lock);
> +	if (!ret)
> +		ret = -ETIMEDOUT;
>  
> -	return 0;
> +	mc13783_lock(mc13783);
> +
> +	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
> +
> +	if (mode == MC13783_ADC_MODE_TS)
> +		/* restore TSMOD */
> +		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
> +
> +	if (ret > 0)
> +		for (i = 0; i < 4; ++i)
> +			mc13783_reg_read(mc13783,
> +					MC13783_REG_ADC_2, &sample[i]);
> +
> +	mc13783->flags &= ~MC13783_ADC_WORKING;
> +out:
> +	mc13783_unlock(mc13783);
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
>  
> -void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
> +static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
> +		const char *name, void *pdata, size_t pdata_size)
>  {
> -	mc13783->ts_active = status;
> +	struct mfd_cell cell = {
> +		.name = name,
> +		.platform_data = pdata,
> +		.data_size = pdata_size,
> +	};
> +
> +	return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
> +}
> +
> +static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
> +{
> +	return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
>  }
> -EXPORT_SYMBOL_GPL(mc13783_adc_set_ts_status);
>  
>  static int mc13783_check_revision(struct mc13783 *mc13783)
>  {
>  	u32 rev_id, rev1, rev2, finid, icid;
>  
> -	mc13783_read(mc13783, MC13783_REG_REVISION, &rev_id);
> +	mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
>  
>  	rev1 = (rev_id & 0x018) >> 3;
>  	rev2 = (rev_id & 0x007);
> @@ -292,38 +551,24 @@ static int mc13783_check_revision(struct mc13783 *mc13783)
>  		rev1 = 3;
>  
>  	if (rev1 == 0 || icid != 2) {
> -		dev_err(mc13783->dev, "No MC13783 detected.\n");
> +		dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
>  		return -ENODEV;
>  	}
>  
> -	mc13783->revision = ((rev1 * 10) + rev2);
> -	dev_info(mc13783->dev, "MC13783 Rev %d.%d FinVer %x detected\n", rev1,
> -	       rev2, finid);
> +	dev_info(&mc13783->spidev->dev,
> +			"MC13783 Rev %d.%d FinVer %x detected\n",
> +			rev1, rev2, finid);
>  
>  	return 0;
>  }
>  
> -/*
> - * Register a client device.  This is non-fatal since there is no need to
> - * fail the entire device init due to a single platform device failing.
> - */
> -static void mc13783_client_dev_register(struct mc13783 *mc13783,
> -				       const char *name)
> -{
> -	struct mfd_cell cell = {};
> -
> -	cell.name = name;
> -
> -	mfd_add_devices(mc13783->dev, -1, &cell, 1, NULL, 0);
> -}
> -
> -static int __devinit mc13783_probe(struct spi_device *spi)
> +static int mc13783_probe(struct spi_device *spi)
>  {
>  	struct mc13783 *mc13783;
> -	struct mc13783_platform_data *pdata = spi->dev.platform_data;
> +	struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
>  	int ret;
>  
> -	mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
> +	mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
>  	if (!mc13783)
>  		return -ENOMEM;
>  
> @@ -332,96 +577,104 @@ static int __devinit mc13783_probe(struct spi_device *spi)
>  	spi->bits_per_word = 32;
>  	spi_setup(spi);
>  
> -	mc13783->spi_device = spi;
> -	mc13783->dev = &spi->dev;
> -	mc13783->irq = spi->irq;
> +	mc13783->spidev = spi;
> +
> +	mutex_init(&mc13783->lock);
> +	mc13783_lock(mc13783);
> +
> +	ret = mc13783_check_revision(mc13783);
> +	if (ret)
> +		goto err_revision;
> +
> +	/* mask all irqs */
> +	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
> +	if (ret)
> +		goto err_mask;
>  
> -	INIT_WORK(&mc13783->work, mc13783_irq_work);
> -	mutex_init(&mc13783->io_lock);
> -	mutex_init(&mc13783->adc_conv_lock);
> -	init_completion(&mc13783->adc_done);
> +	ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
> +	if (ret)
> +		goto err_mask;
> +
> +	ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
> +			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
> +
> +	if (ret) {
> +err_mask:
> +err_revision:
> +		mutex_unlock(&mc13783->lock);
> +		dev_set_drvdata(&spi->dev, NULL);
> +		kfree(mc13783);
> +		return ret;
> +	}
>  
> +	/* This should go away (BEGIN) */
>  	if (pdata) {
>  		mc13783->flags = pdata->flags;
>  		mc13783->regulators = pdata->regulators;
>  		mc13783->num_regulators = pdata->num_regulators;
>  	}
> +	/* This should go away (END) */
>  
> -	if (mc13783_check_revision(mc13783)) {
> -		ret = -ENODEV;
> -		goto err_out;
> +	if (pdata->flags & MC13783_USE_ADC)
> +		mc13783_add_subdevice(mc13783, "mc13783-adc");
> +
> +	if (pdata->flags & MC13783_USE_CODEC)
> +		mc13783_add_subdevice(mc13783, "mc13783-codec");
> +
> +	if (pdata->flags & MC13783_USE_REGULATOR) {
> +		struct mc13783_regulator_platform_data regulator_pdata = {
> +			.num_regulators = pdata->num_regulators,
> +			.regulators = pdata->regulators,
> +		};
> +
> +		mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
> +				&regulator_pdata, sizeof(regulator_pdata));
>  	}
>  
> -	/* clear and mask all interrupts */
> -	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0, 0x00ffffff);
> -	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_0, 0x00ffffff);
> -	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_1, 0x00ffffff);
> -	mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_MASK_1, 0x00ffffff);
> +	if (pdata->flags & MC13783_USE_RTC)
> +		mc13783_add_subdevice(mc13783, "mc13783-rtc");
>  
> -	/* unmask adcdone interrupts */
> -	mc13783_set_bits(mc13783, MC13783_REG_INTERRUPT_MASK_0,
> -			MC13783_INT_MASK_ADCDONEM, 0);
> +	if (pdata->flags & MC13783_USE_TOUCHSCREEN)
> +		mc13783_add_subdevice(mc13783, "mc13783-ts");
>  
> -	ret = request_irq(mc13783->irq, mc13783_interrupt,
> -			IRQF_DISABLED | IRQF_TRIGGER_HIGH, "mc13783",
> -			mc13783);
> -	if (ret)
> -		goto err_out;
> -
> -	if (mc13783->flags & MC13783_USE_CODEC)
> -		mc13783_client_dev_register(mc13783, "mc13783-codec");
> -	if (mc13783->flags & MC13783_USE_ADC)
> -		mc13783_client_dev_register(mc13783, "mc13783-adc");
> -	if (mc13783->flags & MC13783_USE_RTC)
> -		mc13783_client_dev_register(mc13783, "mc13783-rtc");
> -	if (mc13783->flags & MC13783_USE_REGULATOR)
> -		mc13783_client_dev_register(mc13783, "mc13783-regulator");
> -	if (mc13783->flags & MC13783_USE_TOUCHSCREEN)
> -		mc13783_client_dev_register(mc13783, "mc13783-ts");
> +	mc13783_unlock(mc13783);
>  
>  	return 0;
> -
> -err_out:
> -	kfree(mc13783);
> -	return ret;
>  }
>  
>  static int __devexit mc13783_remove(struct spi_device *spi)
>  {
> -	struct mc13783 *mc13783;
> +	struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
>  
> -	mc13783 = dev_get_drvdata(&spi->dev);
> -
> -	free_irq(mc13783->irq, mc13783);
> +	free_irq(mc13783->spidev->irq, mc13783);
>  
>  	mfd_remove_devices(&spi->dev);
>  
>  	return 0;
>  }
>  
> -static struct spi_driver pmic_driver = {
> +static struct spi_driver mc13783_driver = {
>  	.driver = {
> -		   .name = "mc13783",
> -		   .bus = &spi_bus_type,
> -		   .owner = THIS_MODULE,
> +		.name = "mc13783",
> +		.bus = &spi_bus_type,
> +		.owner = THIS_MODULE,
>  	},
>  	.probe = mc13783_probe,
>  	.remove = __devexit_p(mc13783_remove),
>  };
>  
> -static int __init pmic_init(void)
> +static int __init mc13783_init(void)
>  {
> -	return spi_register_driver(&pmic_driver);
> +	return spi_register_driver(&mc13783_driver);
>  }
> -subsys_initcall(pmic_init);
> +subsys_initcall(mc13783_init);
>  
> -static void __exit pmic_exit(void)
> +static void __exit mc13783_exit(void)
>  {
> -	spi_unregister_driver(&pmic_driver);
> +	spi_unregister_driver(&mc13783_driver);
>  }
> -module_exit(pmic_exit);
> -
> -MODULE_DESCRIPTION("Core/Protocol driver for Freescale MC13783 PMIC");
> -MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
> -MODULE_LICENSE("GPL");
> +module_exit(mc13783_exit);
>  
> +MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
> +MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
> index 47e698c..95cf936 100644
> --- a/include/linux/mfd/mc13783-private.h
> +++ b/include/linux/mfd/mc13783-private.h
> @@ -24,52 +24,23 @@
>  
>  #include <linux/platform_device.h>
>  #include <linux/mfd/mc13783.h>
> -#include <linux/workqueue.h>
>  #include <linux/mutex.h>
> -
> -struct mc13783_irq {
> -	void (*handler)(int, void *);
> -	void *data;
> -};
> -
> -#define MC13783_NUM_IRQ		2
> -#define MC13783_IRQ_TS		0
> -#define MC13783_IRQ_REGULATOR	1
> -
> -#define MC13783_ADC_MODE_TS		1
> -#define MC13783_ADC_MODE_SINGLE_CHAN	2
> -#define MC13783_ADC_MODE_MULT_CHAN	3
> +#include <linux/interrupt.h>
>  
>  struct mc13783 {
> -	int revision;
> -	struct device *dev;
> -	struct spi_device *spi_device;
> -
> -	int (*read_dev)(void *data, char reg, int count, u32 *dst);
> -	int (*write_dev)(void *data, char reg, int count, const u32 *src);
> -
> -	struct mutex io_lock;
> -	void *io_data;
> +	struct spi_device *spidev;
> +	struct mutex lock;
>  	int irq;
> -	unsigned int flags;
> +	int flags;
>  
> -	struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
> -	struct work_struct work;
> -	struct completion adc_done;
> -	unsigned int ts_active;
> -	struct mutex adc_conv_lock;
> +	irq_handler_t irqhandler[MC13783_NUM_IRQ];
> +	void *irqdata[MC13783_NUM_IRQ];
>  
> +	/* XXX these should go as platformdata to the regulator subdevice */
>  	struct mc13783_regulator_init_data *regulators;
>  	int num_regulators;
>  };
>  
> -int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
> -int mc13783_reg_write(struct mc13783 *, int, u32);
> -int mc13783_set_bits(struct mc13783 *, int, u32, u32);
> -int mc13783_free_irq(struct mc13783 *mc13783, int irq);
> -int mc13783_register_irq(struct mc13783 *mc13783, int irq,
> -		void (*handler) (int, void *), void *data);
> -
>  #define MC13783_REG_INTERRUPT_STATUS_0		 0
>  #define MC13783_REG_INTERRUPT_MASK_0		 1
>  #define MC13783_REG_INTERRUPT_SENSE_0		 2
> @@ -136,55 +107,6 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
>  #define MC13783_REG_TEST_3			63
>  #define MC13783_REG_NB				64
>  
> -
> -/*
> - * Interrupt Status
> - */
> -#define MC13783_INT_STAT_ADCDONEI	(1 << 0)
> -#define MC13783_INT_STAT_ADCBISDONEI	(1 << 1)
> -#define MC13783_INT_STAT_TSI		(1 << 2)
> -#define MC13783_INT_STAT_WHIGHI		(1 << 3)
> -#define MC13783_INT_STAT_WLOWI		(1 << 4)
> -#define MC13783_INT_STAT_CHGDETI	(1 << 6)
> -#define MC13783_INT_STAT_CHGOVI		(1 << 7)
> -#define MC13783_INT_STAT_CHGREVI	(1 << 8)
> -#define MC13783_INT_STAT_CHGSHORTI	(1 << 9)
> -#define MC13783_INT_STAT_CCCVI		(1 << 10)
> -#define MC13783_INT_STAT_CHGCURRI	(1 << 11)
> -#define MC13783_INT_STAT_BPONI		(1 << 12)
> -#define MC13783_INT_STAT_LOBATLI	(1 << 13)
> -#define MC13783_INT_STAT_LOBATHI	(1 << 14)
> -#define MC13783_INT_STAT_UDPI		(1 << 15)
> -#define MC13783_INT_STAT_USBI		(1 << 16)
> -#define MC13783_INT_STAT_IDI		(1 << 19)
> -#define MC13783_INT_STAT_Unused		(1 << 20)
> -#define MC13783_INT_STAT_SE1I		(1 << 21)
> -#define MC13783_INT_STAT_CKDETI		(1 << 22)
> -#define MC13783_INT_STAT_UDMI		(1 << 23)
> -
> -/*
> - * Interrupt Mask
> - */
> -#define MC13783_INT_MASK_ADCDONEM	(1 << 0)
> -#define MC13783_INT_MASK_ADCBISDONEM	(1 << 1)
> -#define MC13783_INT_MASK_TSM		(1 << 2)
> -#define MC13783_INT_MASK_WHIGHM		(1 << 3)
> -#define MC13783_INT_MASK_WLOWM		(1 << 4)
> -#define MC13783_INT_MASK_CHGDETM	(1 << 6)
> -#define MC13783_INT_MASK_CHGOVM		(1 << 7)
> -#define MC13783_INT_MASK_CHGREVM	(1 << 8)
> -#define MC13783_INT_MASK_CHGSHORTM	(1 << 9)
> -#define MC13783_INT_MASK_CCCVM		(1 << 10)
> -#define MC13783_INT_MASK_CHGCURRM	(1 << 11)
> -#define MC13783_INT_MASK_BPONM		(1 << 12)
> -#define MC13783_INT_MASK_LOBATLM	(1 << 13)
> -#define MC13783_INT_MASK_LOBATHM	(1 << 14)
> -#define MC13783_INT_MASK_UDPM		(1 << 15)
> -#define MC13783_INT_MASK_USBM		(1 << 16)
> -#define MC13783_INT_MASK_IDM		(1 << 19)
> -#define MC13783_INT_MASK_SE1M		(1 << 21)
> -#define MC13783_INT_MASK_CKDETM		(1 << 22)
> -
>  /*
>   * Reg Regulator Mode 0
>   */
> @@ -284,113 +206,15 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
>  #define MC13783_SWCTRL_SW3_STBY		(1 << 21)
>  #define MC13783_SWCTRL_SW3_MODE		(1 << 22)
>  
> -/*
> - * ADC/Touch
> - */
> -#define MC13783_ADC0_LICELLCON		(1 << 0)
> -#define MC13783_ADC0_CHRGICON		(1 << 1)
> -#define MC13783_ADC0_BATICON		(1 << 2)
> -#define MC13783_ADC0_RTHEN 		(1 << 3)
> -#define MC13783_ADC0_DTHEN		(1 << 4)
> -#define MC13783_ADC0_UIDEN		(1 << 5)
> -#define MC13783_ADC0_ADOUTEN 		(1 << 6)
> -#define MC13783_ADC0_ADOUTPER		(1 << 7)
> -#define MC13783_ADC0_ADREFEN		(1 << 10)
> -#define MC13783_ADC0_ADREFMODE		(1 << 11)
> -#define MC13783_ADC0_TSMOD0		(1 << 12)
> -#define MC13783_ADC0_TSMOD1		(1 << 13)
> -#define MC13783_ADC0_TSMOD2		(1 << 14)
> -#define MC13783_ADC0_CHRGRAWDIV		(1 << 15)
> -#define MC13783_ADC0_ADINC1		(1 << 16)
> -#define MC13783_ADC0_ADINC2		(1 << 17)
> -#define MC13783_ADC0_WCOMP		(1 << 18)
> -#define MC13783_ADC0_ADCBIS0		(1 << 23)
> -
> -#define MC13783_ADC1_ADEN		(1 << 0)
> -#define MC13783_ADC1_RAND		(1 << 1)
> -#define MC13783_ADC1_ADSEL		(1 << 3)
> -#define MC13783_ADC1_TRIGMASK		(1 << 4)
> -#define MC13783_ADC1_ADA10		(1 << 5)
> -#define MC13783_ADC1_ADA11		(1 << 6)
> -#define MC13783_ADC1_ADA12		(1 << 7)
> -#define MC13783_ADC1_ADA20		(1 << 8)
> -#define MC13783_ADC1_ADA21		(1 << 9)
> -#define MC13783_ADC1_ADA22		(1 << 10)
> -#define MC13783_ADC1_ATO0		(1 << 11)
> -#define MC13783_ADC1_ATO1		(1 << 12)
> -#define MC13783_ADC1_ATO2		(1 << 13)
> -#define MC13783_ADC1_ATO3		(1 << 14)
> -#define MC13783_ADC1_ATO4		(1 << 15)
> -#define MC13783_ADC1_ATO5		(1 << 16)
> -#define MC13783_ADC1_ATO6		(1 << 17)
> -#define MC13783_ADC1_ATO7		(1 << 18)
> -#define MC13783_ADC1_ATOX		(1 << 19)
> -#define MC13783_ADC1_ASC		(1 << 20)
> -#define MC13783_ADC1_ADTRIGIGN		(1 << 21)
> -#define MC13783_ADC1_ADONESHOT		(1 << 22)
> -#define MC13783_ADC1_ADCBIS1		(1 << 23)
> -
> -#define MC13783_ADC1_CHAN0_SHIFT	5
> -#define MC13783_ADC1_CHAN1_SHIFT	8
> -
> -#define MC13783_ADC2_ADD10		(1 << 2)
> -#define MC13783_ADC2_ADD11		(1 << 3)
> -#define MC13783_ADC2_ADD12		(1 << 4)
> -#define MC13783_ADC2_ADD13		(1 << 5)
> -#define MC13783_ADC2_ADD14		(1 << 6)
> -#define MC13783_ADC2_ADD15		(1 << 7)
> -#define MC13783_ADC2_ADD16		(1 << 8)
> -#define MC13783_ADC2_ADD17		(1 << 9)
> -#define MC13783_ADC2_ADD18		(1 << 10)
> -#define MC13783_ADC2_ADD19		(1 << 11)
> -#define MC13783_ADC2_ADD20		(1 << 14)
> -#define MC13783_ADC2_ADD21		(1 << 15)
> -#define MC13783_ADC2_ADD22		(1 << 16)
> -#define MC13783_ADC2_ADD23		(1 << 17)
> -#define MC13783_ADC2_ADD24		(1 << 18)
> -#define MC13783_ADC2_ADD25		(1 << 19)
> -#define MC13783_ADC2_ADD26		(1 << 20)
> -#define MC13783_ADC2_ADD27		(1 << 21)
> -#define MC13783_ADC2_ADD28		(1 << 22)
> -#define MC13783_ADC2_ADD29		(1 << 23)
> +static inline int mc13783_set_bits(struct mc13783 *mc13783, unsigned int offset,
> +		u32 mask, u32 val)
> +{
> +	int ret;
> +	mc13783_lock(mc13783);
> +	ret = mc13783_reg_rmw(mc13783, offset, mask, val);
> +	mc13783_unlock(mc13783);
>  
> -#define MC13783_ADC3_WHIGH0		(1 << 0)
> -#define MC13783_ADC3_WHIGH1		(1 << 1)
> -#define MC13783_ADC3_WHIGH2		(1 << 2)
> -#define MC13783_ADC3_WHIGH3		(1 << 3)
> -#define MC13783_ADC3_WHIGH4		(1 << 4)
> -#define MC13783_ADC3_WHIGH5		(1 << 5)
> -#define MC13783_ADC3_ICID0		(1 << 6)
> -#define MC13783_ADC3_ICID1		(1 << 7)
> -#define MC13783_ADC3_ICID2		(1 << 8)
> -#define MC13783_ADC3_WLOW0		(1 << 9)
> -#define MC13783_ADC3_WLOW1		(1 << 10)
> -#define MC13783_ADC3_WLOW2		(1 << 11)
> -#define MC13783_ADC3_WLOW3		(1 << 12)
> -#define MC13783_ADC3_WLOW4		(1 << 13)
> -#define MC13783_ADC3_WLOW5		(1 << 14)
> -#define MC13783_ADC3_ADCBIS2		(1 << 23)
> -
> -#define MC13783_ADC4_ADDBIS10		(1 << 2)
> -#define MC13783_ADC4_ADDBIS11		(1 << 3)
> -#define MC13783_ADC4_ADDBIS12		(1 << 4)
> -#define MC13783_ADC4_ADDBIS13		(1 << 5)
> -#define MC13783_ADC4_ADDBIS14		(1 << 6)
> -#define MC13783_ADC4_ADDBIS15		(1 << 7)
> -#define MC13783_ADC4_ADDBIS16		(1 << 8)
> -#define MC13783_ADC4_ADDBIS17		(1 << 9)
> -#define MC13783_ADC4_ADDBIS18		(1 << 10)
> -#define MC13783_ADC4_ADDBIS19		(1 << 11)
> -#define MC13783_ADC4_ADDBIS20		(1 << 14)
> -#define MC13783_ADC4_ADDBIS21		(1 << 15)
> -#define MC13783_ADC4_ADDBIS22		(1 << 16)
> -#define MC13783_ADC4_ADDBIS23		(1 << 17)
> -#define MC13783_ADC4_ADDBIS24		(1 << 18)
> -#define MC13783_ADC4_ADDBIS25		(1 << 19)
> -#define MC13783_ADC4_ADDBIS26		(1 << 20)
> -#define MC13783_ADC4_ADDBIS27		(1 << 21)
> -#define MC13783_ADC4_ADDBIS28		(1 << 22)
> -#define MC13783_ADC4_ADDBIS29		(1 << 23)
> +	return ret;
> +}
>  
>  #endif /* __LINUX_MFD_MC13783_PRIV_H */
> -
> diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
> index b3a2a72..3568040 100644
> --- a/include/linux/mfd/mc13783.h
> +++ b/include/linux/mfd/mc13783.h
> @@ -1,28 +1,50 @@
>  /*
> - * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
> + * Copyright 2009 Pengutronix
> + * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
>   *
> - * Initial development of this code was funded by
> - * Phytec Messtechnik GmbH, http://www.phytec.de
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that 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, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
>   */
> +#ifndef __LINUX_MFD_MC13783_H
> +#define __LINUX_MFD_MC13783_H
>  
> -#ifndef __INCLUDE_LINUX_MFD_MC13783_H
> -#define __INCLUDE_LINUX_MFD_MC13783_H
> +#include <linux/interrupt.h>
>  
>  struct mc13783;
> +
> +void mc13783_lock(struct mc13783 *mc13783);
> +void mc13783_unlock(struct mc13783 *mc13783);
> +
> +int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
> +int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
> +int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
> +		u32 mask, u32 val);
> +
> +int mc13783_irq_request(struct mc13783 *mc13783, int irq,
> +		irq_handler_t handler, const char *name, void *dev);
> +int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
> +		irq_handler_t handler, const char *name, void *dev);
> +int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
> +int mc13783_ackirq(struct mc13783 *mc13783, int irq);
> +
> +int mc13783_mask(struct mc13783 *mc13783, int irq);
> +int mc13783_unmask(struct mc13783 *mc13783, int irq);
> +
> +#define MC13783_ADC0		43
> +#define MC13783_ADC0_ADREFEN		(1 << 10)
> +#define MC13783_ADC0_ADREFMODE		(1 << 11)
> +#define MC13783_ADC0_TSMOD0		(1 << 12)
> +#define MC13783_ADC0_TSMOD1		(1 << 13)
> +#define MC13783_ADC0_TSMOD2		(1 << 14)
> +#define MC13783_ADC0_ADINC1		(1 << 16)
> +#define MC13783_ADC0_ADINC2		(1 << 17)
> +
> +#define MC13783_ADC0_TSMOD_MASK		(MC13783_ADC0_TSMOD0 | \
> +					MC13783_ADC0_TSMOD1 | \
> +					MC13783_ADC0_TSMOD2)
> +
> +/* to be cleaned up */
>  struct regulator_init_data;
>  
>  struct mc13783_regulator_init_data {
> @@ -30,23 +52,30 @@ struct mc13783_regulator_init_data {
>  	struct regulator_init_data *init_data;
>  };
>  
> -struct mc13783_platform_data {
> -	struct mc13783_regulator_init_data *regulators;
> +struct mc13783_regulator_platform_data {
>  	int num_regulators;
> -	unsigned int flags;
> +	struct mc13783_regulator_init_data *regulators;
>  };
>  
> -/* mc13783_platform_data flags */
> +struct mc13783_platform_data {
> +	int num_regulators;
> +	struct mc13783_regulator_init_data *regulators;
> +
>  #define MC13783_USE_TOUCHSCREEN (1 << 0)
>  #define MC13783_USE_CODEC	(1 << 1)
>  #define MC13783_USE_ADC		(1 << 2)
>  #define MC13783_USE_RTC		(1 << 3)
>  #define MC13783_USE_REGULATOR	(1 << 4)
> +	unsigned int flags;
> +};
> +
> +#define MC13783_ADC_MODE_TS		1
> +#define MC13783_ADC_MODE_SINGLE_CHAN	2
> +#define MC13783_ADC_MODE_MULT_CHAN	3
>  
>  int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
>  		unsigned int channel, unsigned int *sample);
>  
> -void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
>  
>  #define	MC13783_SW_SW1A		0
>  #define	MC13783_SW_SW1B		1
> @@ -80,5 +109,46 @@ void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
>  #define	MC13783_REGU_V3		29
>  #define	MC13783_REGU_V4		30
>  
> -#endif /* __INCLUDE_LINUX_MFD_MC13783_H */
> +#define MC13783_IRQ_ADCDONE	0
> +#define MC13783_IRQ_ADCBISDONE	1
> +#define MC13783_IRQ_TS		2
> +#define MC13783_IRQ_WHIGH	3
> +#define MC13783_IRQ_WLOW	4
> +#define MC13783_IRQ_CHGDET	6
> +#define MC13783_IRQ_CHGOV	7
> +#define MC13783_IRQ_CHGREV	8
> +#define MC13783_IRQ_CHGSHORT	9
> +#define MC13783_IRQ_CCCV	10
> +#define MC13783_IRQ_CHGCURR	11
> +#define MC13783_IRQ_BPON	12
> +#define MC13783_IRQ_LOBATL	13
> +#define MC13783_IRQ_LOBATH	14
> +#define MC13783_IRQ_UDP		15
> +#define MC13783_IRQ_USB		16
> +#define MC13783_IRQ_ID		19
> +#define MC13783_IRQ_SE1		21
> +#define MC13783_IRQ_CKDET	22
> +#define MC13783_IRQ_UDM		23
> +#define MC13783_IRQ_1HZ		24
> +#define MC13783_IRQ_TODA	25
> +#define MC13783_IRQ_ONOFD1	27
> +#define MC13783_IRQ_ONOFD2	28
> +#define MC13783_IRQ_ONOFD3	29
> +#define MC13783_IRQ_SYSRST	30
> +#define MC13783_IRQ_RTCRST	31
> +#define MC13783_IRQ_PC		32
> +#define MC13783_IRQ_WARM	33
> +#define MC13783_IRQ_MEMHLD	34
> +#define MC13783_IRQ_PWRRDY	35
> +#define MC13783_IRQ_THWARNL	36
> +#define MC13783_IRQ_THWARNH	37
> +#define MC13783_IRQ_CLK		38
> +#define MC13783_IRQ_SEMAF	39
> +#define MC13783_IRQ_MC2B	41
> +#define MC13783_IRQ_HSDET	42
> +#define MC13783_IRQ_HSL		43
> +#define MC13783_IRQ_ALSPTH	44
> +#define MC13783_IRQ_AHSSHORT	45
> +#define MC13783_NUM_IRQ		46
>  
> +#endif /* __LINUX_MFD_MC13783_H */
> -- 
> 1.6.5.2
> 

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 1/2] regulator/mc13783: rename source file to match other drivers
  2009-11-06  0:28                 ` Samuel Ortiz
@ 2009-11-10  8:18                   ` Uwe Kleine-König
  2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
                                       ` (2 more replies)
  2009-11-24 21:44                   ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
  1 sibling, 3 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-10  8:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Liam Girdwood, Mark Brown, Samuel Ortiz

One annoying thing about the old name was that the module was just
called mc13783 which caused wrong expectations (at least for me).

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Liam Girdwood <lrg@slimlogic.co.uk>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
Hello,

I split that from the following patch mainly for review because if I
squash them together git's rename detection doesn't trigger any more.

Best regards
Uwe

 drivers/regulator/Makefile                         |    2 +-
 .../regulator/{mc13783.c => mc13783-regulator.c}   |    0
 2 files changed, 1 insertions(+), 1 deletions(-)
 rename drivers/regulator/{mc13783.c => mc13783-regulator.c} (100%)

diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4257a86..8891767 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -20,7 +20,7 @@ obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
 obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
-obj-$(CONFIG_REGULATOR_MC13783) += mc13783.o
+obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
 
 obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
diff --git a/drivers/regulator/mc13783.c b/drivers/regulator/mc13783-regulator.c
similarity index 100%
rename from drivers/regulator/mc13783.c
rename to drivers/regulator/mc13783-regulator.c
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 2/2] regulator/mc13783: various cleanups
  2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
@ 2009-11-10  8:18                     ` Uwe Kleine-König
  2009-11-10 13:08                       ` Mark Brown
  2009-11-11 14:11                       ` Liam Girdwood
  2009-11-10 11:44                     ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Mark Brown
  2009-11-11 14:09                     ` Liam Girdwood
  2 siblings, 2 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-10  8:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Liam Girdwood, Mark Brown, Samuel Ortiz

- define needed registers and bits in the driver
- properly namespace functions and structs
- fix locking as required by patch
  "mfd/mc13783: near complete rewrite"
- use platform_data as provided by "mfd/mc13783: near complete rewrite"
  instead of accessing struct mc13783
- struct mc13783_regulator_priv.desc is (and was) unused and so can go
  away
- use cpp magic to initialize mc13783_regulators
- bring MODULE_LICENSE in sync with actual copyright
- minor style fixes

This allows not including mc13783-private.h which I intend to remove
soon.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Liam Girdwood <lrg@slimlogic.co.uk>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
Hello,

should I invest the effort to split that patch up?  E.g. one patch per
list item above?  (Probably defining the registers in
mc13783-regulator.c would clash wich removing #include
<linux/mfd/mc13783-private.h>, but the other changes should be
ortogonal.)

Best regards
Uwe

 drivers/regulator/mc13783-regulator.c |  389 ++++++++++-----------------------
 1 files changed, 112 insertions(+), 277 deletions(-)

diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index 710211f..9f99862 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -8,15 +8,47 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/driver.h>
 #include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/err.h>
 
+#define MC13783_REG_SWITCHERS4			28
+#define MC13783_REG_SWITCHERS4_PLLEN			(1 << 18)
+
+#define MC13783_REG_SWITCHERS5			29
+#define MC13783_REG_SWITCHERS5_SW3EN			(1 << 20)
+
+#define MC13783_REG_REGULATORMODE0		32
+#define MC13783_REG_REGULATORMODE0_VAUDIOEN		(1 << 0)
+#define MC13783_REG_REGULATORMODE0_VIOHIEN		(1 << 3)
+#define MC13783_REG_REGULATORMODE0_VIOLOEN		(1 << 6)
+#define MC13783_REG_REGULATORMODE0_VDIGEN		(1 << 9)
+#define MC13783_REG_REGULATORMODE0_VGENEN		(1 << 12)
+#define MC13783_REG_REGULATORMODE0_VRFDIGEN		(1 << 15)
+#define MC13783_REG_REGULATORMODE0_VRFREFEN		(1 << 18)
+#define MC13783_REG_REGULATORMODE0_VRFCPEN		(1 << 21)
+
+#define MC13783_REG_REGULATORMODE1		33
+#define MC13783_REG_REGULATORMODE1_VSIMEN		(1 << 0)
+#define MC13783_REG_REGULATORMODE1_VESIMEN		(1 << 3)
+#define MC13783_REG_REGULATORMODE1_VCAMEN		(1 << 6)
+#define MC13783_REG_REGULATORMODE1_VRFBGEN		(1 << 9)
+#define MC13783_REG_REGULATORMODE1_VVIBEN		(1 << 11)
+#define MC13783_REG_REGULATORMODE1_VRF1EN		(1 << 12)
+#define MC13783_REG_REGULATORMODE1_VRF2EN		(1 << 15)
+#define MC13783_REG_REGULATORMODE1_VMMC1EN		(1 << 18)
+#define MC13783_REG_REGULATORMODE1_VMMC2EN		(1 << 21)
+
+#define MC13783_REG_POWERMISC			34
+#define MC13783_REG_POWERMISC_GPO1EN			(1 << 6)
+#define MC13783_REG_POWERMISC_GPO2EN			(1 << 8)
+#define MC13783_REG_POWERMISC_GPO3EN			(1 << 10)
+#define MC13783_REG_POWERMISC_GPO4EN			(1 << 12)
+
 struct mc13783_regulator {
 	struct regulator_desc desc;
 	int reg;
@@ -25,298 +57,97 @@ struct mc13783_regulator {
 
 static struct regulator_ops mc13783_regulator_ops;
 
+#define MC13783_DEFINE(prefix, _name, _reg)				\
+	[MC13783_ ## prefix ## _ ## _name] = {				\
+		.desc = {						\
+			.name = #prefix "_" #_name,			\
+			.ops = &mc13783_regulator_ops,			\
+			.type = REGULATOR_VOLTAGE,			\
+			.id = MC13783_ ## prefix ## _ ## _name,		\
+			.owner = THIS_MODULE,				\
+		},							\
+		.reg = MC13783_REG_ ## _reg,				\
+		.enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN,	\
+	}
+
+#define MC13783_DEFINE_SW(_name, _reg) MC13783_DEFINE(SW, _name, _reg)
+#define MC13783_DEFINE_REGU(_name, _reg) MC13783_DEFINE(REGU, _name, _reg)
+
 static struct mc13783_regulator mc13783_regulators[] = {
-	[MC13783_SW_SW3] = {
-		.desc = {
-			.name	= "SW_SW3",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_SW_SW3,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_SWITCHERS_5,
-		.enable_bit = MC13783_SWCTRL_SW3_EN,
-	},
-	[MC13783_SW_PLL] = {
-		.desc = {
-			.name	= "SW_PLL",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_SW_PLL,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_SWITCHERS_4,
-		.enable_bit = MC13783_SWCTRL_PLL_EN,
-	},
-	[MC13783_REGU_VAUDIO] = {
-		.desc = {
-			.name	= "REGU_VAUDIO",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VAUDIO,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VAUDIO_EN,
-	},
-	[MC13783_REGU_VIOHI] = {
-		.desc = {
-			.name	= "REGU_VIOHI",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VIOHI,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VIOHI_EN,
-	},
-	[MC13783_REGU_VIOLO] = {
-		.desc = {
-			.name	= "REGU_VIOLO",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VIOLO,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VIOLO_EN,
-	},
-	[MC13783_REGU_VDIG] = {
-		.desc = {
-			.name	= "REGU_VDIG",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VDIG,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VDIG_EN,
-	},
-	[MC13783_REGU_VGEN] = {
-		.desc = {
-			.name	= "REGU_VGEN",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VGEN,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VGEN_EN,
-	},
-	[MC13783_REGU_VRFDIG] = {
-		.desc = {
-			.name	= "REGU_VRFDIG",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRFDIG,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VRFDIG_EN,
-	},
-	[MC13783_REGU_VRFREF] = {
-		.desc = {
-			.name	= "REGU_VRFREF",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRFREF,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VRFREF_EN,
-	},
-	[MC13783_REGU_VRFCP] = {
-		.desc = {
-			.name	= "REGU_VRFCP",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRFCP,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_0,
-		.enable_bit = MC13783_REGCTRL_VRFCP_EN,
-	},
-	[MC13783_REGU_VSIM] = {
-		.desc = {
-			.name	= "REGU_VSIM",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VSIM,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VSIM_EN,
-	},
-	[MC13783_REGU_VESIM] = {
-		.desc = {
-			.name	= "REGU_VESIM",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VESIM,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VESIM_EN,
-	},
-	[MC13783_REGU_VCAM] = {
-		.desc = {
-			.name	= "REGU_VCAM",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VCAM,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VCAM_EN,
-	},
-	[MC13783_REGU_VRFBG] = {
-		.desc = {
-			.name	= "REGU_VRFBG",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRFBG,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VRFBG_EN,
-	},
-	[MC13783_REGU_VVIB] = {
-		.desc = {
-			.name	= "REGU_VVIB",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VVIB,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VVIB_EN,
-	},
-	[MC13783_REGU_VRF1] = {
-		.desc = {
-			.name	= "REGU_VRF1",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRF1,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VRF1_EN,
-	},
-	[MC13783_REGU_VRF2] = {
-		.desc = {
-			.name	= "REGU_VRF2",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VRF2,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VRF2_EN,
-	},
-	[MC13783_REGU_VMMC1] = {
-		.desc = {
-			.name	= "REGU_VMMC1",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VMMC1,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VMMC1_EN,
-	},
-	[MC13783_REGU_VMMC2] = {
-		.desc = {
-			.name	= "REGU_VMMC2",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_VMMC2,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_REGULATOR_MODE_1,
-		.enable_bit = MC13783_REGCTRL_VMMC2_EN,
-	},
-	[MC13783_REGU_GPO1] = {
-		.desc = {
-			.name	= "REGU_GPO1",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_GPO1,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_POWER_MISCELLANEOUS,
-		.enable_bit = MC13783_REGCTRL_GPO1_EN,
-	},
-	[MC13783_REGU_GPO2] = {
-		.desc = {
-			.name	= "REGU_GPO2",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_GPO2,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_POWER_MISCELLANEOUS,
-		.enable_bit = MC13783_REGCTRL_GPO2_EN,
-	},
-	[MC13783_REGU_GPO3] = {
-		.desc = {
-			.name	= "REGU_GPO3",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_GPO3,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_POWER_MISCELLANEOUS,
-		.enable_bit = MC13783_REGCTRL_GPO3_EN,
-	},
-	[MC13783_REGU_GPO4] = {
-		.desc = {
-			.name	= "REGU_GPO4",
-			.ops	= &mc13783_regulator_ops,
-			.type	= REGULATOR_VOLTAGE,
-			.id	= MC13783_REGU_GPO4,
-			.owner	= THIS_MODULE,
-		},
-		.reg = MC13783_REG_POWER_MISCELLANEOUS,
-		.enable_bit = MC13783_REGCTRL_GPO4_EN,
-	},
+	MC13783_DEFINE_SW(SW3, SWITCHERS5),
+	MC13783_DEFINE_SW(PLL, SWITCHERS4),
+
+	MC13783_DEFINE_REGU(VAUDIO, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VIOHI, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VDIG, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VGEN, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0),
+	MC13783_DEFINE_REGU(VSIM, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VESIM, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VCAM, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VRFBG, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VVIB, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VRF1, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VRF2, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1),
+	MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1),
+	MC13783_DEFINE_REGU(GPO1, POWERMISC),
+	MC13783_DEFINE_REGU(GPO2, POWERMISC),
+	MC13783_DEFINE_REGU(GPO3, POWERMISC),
+	MC13783_DEFINE_REGU(GPO4, POWERMISC),
 };
 
-struct mc13783_priv {
-	struct regulator_desc desc[ARRAY_SIZE(mc13783_regulators)];
+struct mc13783_regulator_priv {
 	struct mc13783 *mc13783;
-	struct regulator_dev *regulators[0];
+	struct regulator_dev *regulators[];
 };
 
-static int mc13783_enable(struct regulator_dev *rdev)
+static int mc13783_regulator_enable(struct regulator_dev *rdev)
 {
-	struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+	struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
 	int id = rdev_get_id(rdev);
+	int ret;
 
 	dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
 
-	return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
+	mc13783_lock(priv->mc13783);
+	ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
 			mc13783_regulators[id].enable_bit,
 			mc13783_regulators[id].enable_bit);
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
 }
 
-static int mc13783_disable(struct regulator_dev *rdev)
+static int mc13783_regulator_disable(struct regulator_dev *rdev)
 {
-	struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+	struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
 	int id = rdev_get_id(rdev);
+	int ret;
 
 	dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
 
-	return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
+	mc13783_lock(priv->mc13783);
+	ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
 			mc13783_regulators[id].enable_bit, 0);
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
 }
 
-static int mc13783_is_enabled(struct regulator_dev *rdev)
+static int mc13783_regulator_is_enabled(struct regulator_dev *rdev)
 {
-	struct mc13783_priv *priv = rdev_get_drvdata(rdev);
+	struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
 	int ret, id = rdev_get_id(rdev);
 	unsigned int val;
 
+	mc13783_lock(priv->mc13783);
 	ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
+	mc13783_unlock(priv->mc13783);
+
 	if (ret)
 		return ret;
 
@@ -324,29 +155,32 @@ static int mc13783_is_enabled(struct regulator_dev *rdev)
 }
 
 static struct regulator_ops mc13783_regulator_ops = {
-	.enable		= mc13783_enable,
-	.disable	= mc13783_disable,
-	.is_enabled	= mc13783_is_enabled,
+	.enable = mc13783_regulator_enable,
+	.disable = mc13783_regulator_disable,
+	.is_enabled = mc13783_regulator_is_enabled,
 };
 
 static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
 {
-	struct mc13783_priv *priv;
+	struct mc13783_regulator_priv *priv;
 	struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
+	struct mc13783_regulator_platform_data *pdata =
+		dev_get_platdata(&pdev->dev);
 	struct mc13783_regulator_init_data *init_data;
 	int i, ret;
 
 	dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
 
-	priv = kzalloc(sizeof(*priv) + mc13783->num_regulators * sizeof(void *),
+	priv = kzalloc(sizeof(*priv) +
+			pdata->num_regulators * sizeof(priv->regulators[0]),
 			GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
 	priv->mc13783 = mc13783;
 
-	for (i = 0; i < mc13783->num_regulators; i++) {
-		init_data = &mc13783->regulators[i];
+	for (i = 0; i < pdata->num_regulators; i++) {
+		init_data = &pdata->regulators[i];
 		priv->regulators[i] = regulator_register(
 				&mc13783_regulators[init_data->id].desc,
 				&pdev->dev, init_data->init_data, priv);
@@ -373,11 +207,12 @@ err:
 
 static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
 {
-	struct mc13783_priv *priv = platform_get_drvdata(pdev);
-	struct mc13783 *mc13783 = priv->mc13783;
+	struct mc13783_regulator_priv *priv = platform_get_drvdata(pdev);
+	struct mc13783_regulator_platform_data *pdata =
+		dev_get_platdata(&pdev->dev);
 	int i;
 
-	for (i = 0; i < mc13783->num_regulators; i++)
+	for (i = 0; i < pdata->num_regulators; i++)
 		regulator_unregister(priv->regulators[i]);
 
 	return 0;
@@ -404,7 +239,7 @@ static void __exit mc13783_regulator_exit(void)
 }
 module_exit(mc13783_regulator_exit);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
 MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
 MODULE_ALIAS("platform:mc13783-regulator");
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH RESENT] [RTC] Add Freescale MC13783 RTC driver
  2009-11-05 15:06         ` Valentin Longchamp
  (?)
@ 2009-11-10  8:32         ` Uwe Kleine-König
  2009-11-10  9:30           ` Alessandro Zummo
  -1 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-10  8:32 UTC (permalink / raw)
  To: linux-kernel; +Cc: Sascha Hauer, Paul Gortmaker, Alessandro Zummo, rtc-linux

This driver provides support for the RTC part integrated into the
Freescale MC13783 PMIC and bases on patch created earlier by Sascha
Hauer.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Uwe Kleine-König <u.kleine-könig@pengutronix.de>
Acked-by: Valentin Longchamp <valentin.longchamp@epfl.ch>
Cc: Paul Gortmaker <p_gortmaker@yahoo.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: rtc-linux@googlegroups.com
---
 drivers/rtc/Kconfig       |    6 +
 drivers/rtc/Makefile      |    1 +
 drivers/rtc/rtc-mc13783.c |  262 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-mc13783.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3c20dae..7fa8db3 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -827,4 +827,10 @@ config RTC_DRV_PCAP
 	  If you say Y here you will get support for the RTC found on
 	  the PCAP2 ASIC used on some Motorola phones.
 
+config RTC_DRV_MC13783
+	depends on MFD_MC13783
+	tristate "Freescale MC13783 RTC"
+	help
+	  This enables support for the Freescale MC13783 PMIC RTC
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index aa3fbd5..f4d01ba 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o
 obj-$(CONFIG_RTC_MXC)		+= rtc-mxc.o
 obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MC13783)	+= rtc-mc13783.o
 obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
 obj-$(CONFIG_RTC_DRV_OMAP)	+= rtc-omap.o
 obj-$(CONFIG_RTC_DRV_PCAP)	+= rtc-pcap.o
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
new file mode 100644
index 0000000..850f983
--- /dev/null
+++ b/drivers/rtc/rtc-mc13783.c
@@ -0,0 +1,262 @@
+/*
+ * Real Time Clock driver for Freescale MC13783 PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13783.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13783-rtc"
+
+#define MC13783_RTCTOD	20
+#define MC13783_RTCTODA	21
+#define MC13783_RTCDAY	22
+#define MC13783_RTCDAYA	23
+
+struct mc13783_rtc {
+	struct rtc_device *rtc;
+	struct mc13783 *mc13783;
+	int valid;
+};
+
+static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days1, days2;
+	unsigned long s1970;
+	int ret;
+
+	mc13783_lock(priv->mc13783);
+
+	if (!priv->valid) {
+		ret = -ENODATA;
+		goto out;
+	}
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	if (ret)
+		return ret;
+
+	if (days2 == days1 + 1) {
+		if (seconds >= 86400 / 2)
+			days2 = days1;
+		else
+			days1 = days2;
+	}
+
+	if (days1 != days2)
+		return -EIO;
+
+	s1970 = days1 * 86400 + seconds;
+
+	rtc_time_to_tm(s1970, tm);
+
+	return rtc_valid_tm(tm);
+}
+
+static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	unsigned int seconds, days;
+	int ret;
+
+	seconds = secs % 86400;
+	days = secs / 86400;
+
+	mc13783_lock(priv->mc13783);
+
+	/*
+	 * first write seconds=0 to prevent a day switch between writing days
+	 * and seconds below
+	 */
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
+	if (unlikely(ret))
+		goto out;
+
+	ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
+out:
+	priv->valid = !ret;
+
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+	struct mc13783 *mc13783 = priv->mc13783;
+
+	dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+	rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+	mc13783_ackirq(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int mc13783_rtc_update_irq_enable(struct device *dev,
+		unsigned int enabled)
+{
+	struct mc13783_rtc *priv = dev_get_drvdata(dev);
+	int ret = -ENODATA;
+
+	mc13783_lock(priv->mc13783);
+	if (!priv->valid)
+		goto out;
+
+	ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
+			MC13783_IRQ_1HZ);
+out:
+	mc13783_unlock(priv->mc13783);
+
+	return ret;
+}
+
+static const struct rtc_class_ops mc13783_rtc_ops = {
+	.read_time = mc13783_rtc_read_time,
+	.set_mmss = mc13783_rtc_set_mmss,
+	.update_irq_enable = mc13783_rtc_update_irq_enable,
+};
+
+static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
+{
+	struct mc13783_rtc *priv = dev;
+	struct mc13783 *mc13783 = priv->mc13783;
+
+	dev_dbg(&priv->rtc->dev, "RTCRST\n");
+	priv->valid = 0;
+
+	mc13783_mask(mc13783, irq);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct mc13783_rtc *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+	platform_set_drvdata(pdev, priv);
+
+	priv->valid = 1;
+
+	mc13783_lock(priv->mc13783);
+
+	ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
+			mc13783_rtc_reset_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_reset_irq_request;
+
+	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
+			mc13783_rtc_update_handler, DRIVER_NAME, priv);
+	if (ret)
+		goto err_update_irq_request;
+
+	mc13783_unlock(priv->mc13783);
+
+	priv->rtc = rtc_device_register(pdev->name,
+			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(priv->rtc)) {
+		ret = PTR_ERR(priv->rtc);
+
+		mc13783_lock(priv->mc13783);
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+err_update_irq_request:
+
+		mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+err_reset_irq_request:
+
+		mc13783_unlock(priv->mc13783);
+
+		platform_set_drvdata(pdev, NULL);
+		kfree(priv);
+	}
+
+	return ret;
+}
+
+static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
+{
+	struct mc13783_rtc *priv = platform_get_drvdata(pdev);
+
+	rtc_device_unregister(priv->rtc);
+
+	mc13783_lock(priv->mc13783);
+
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
+	mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
+
+	mc13783_unlock(priv->mc13783);
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_rtc_driver = {
+	.remove = __devexit_p(mc13783_rtc_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mc13783_rtc_init(void)
+{
+	return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
+}
+module_init(mc13783_rtc_init);
+
+static void __exit mc13783_rtc_exit(void)
+{
+	platform_driver_unregister(&mc13783_rtc_driver);
+}
+module_exit(mc13783_rtc_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH RESENT] [RTC] Add Freescale MC13783 RTC driver
  2009-11-10  8:32         ` [PATCH RESENT] " Uwe Kleine-König
@ 2009-11-10  9:30           ` Alessandro Zummo
  2009-11-10 11:10             ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Alessandro Zummo @ 2009-11-10  9:30 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Paul Gortmaker, rtc-linux

On Tue, 10 Nov 2009 09:32:47 +0100
Uwe Kleine-König <u.kleine-koenig@pengutronix.de> wrote:

> +	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
> +			mc13783_rtc_update_handler, DRIVER_NAME, priv);
> +	if (ret)
> +		goto err_update_irq_request;
> +
> +	mc13783_unlock(priv->mc13783);
> +
> +	priv->rtc = rtc_device_register(pdev->name,
> +			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
> +

 isn't better to enable irqs after registration?

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH RESENT] [RTC] Add Freescale MC13783 RTC driver
  2009-11-10  9:30           ` Alessandro Zummo
@ 2009-11-10 11:10             ` Uwe Kleine-König
  2009-11-22 21:49               ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-10 11:10 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel, Sascha Hauer, Paul Gortmaker, rtc-linux

On Tue, Nov 10, 2009 at 10:30:54AM +0100, Alessandro Zummo wrote:
> On Tue, 10 Nov 2009 09:32:47 +0100
> Uwe Kleine-König <u.kleine-koenig@pengutronix.de> wrote:
> 
> > +	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
> > +			mc13783_rtc_update_handler, DRIVER_NAME, priv);
> > +	if (ret)
> > +		goto err_update_irq_request;
> > +
> > +	mc13783_unlock(priv->mc13783);
> > +
> > +	priv->rtc = rtc_device_register(pdev->name,
> > +			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
> > +
> 
>  isn't better to enable irqs after registration.
IMHO it doesn't make a difference for the 1HZ irq, still more as it
remains masked.  The reset irq should be requested before registration.

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 1/2] regulator/mc13783: rename source file to match other drivers
  2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
  2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
@ 2009-11-10 11:44                     ` Mark Brown
  2009-11-11 14:09                     ` Liam Girdwood
  2 siblings, 0 replies; 40+ messages in thread
From: Mark Brown @ 2009-11-10 11:44 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Liam Girdwood, Samuel Ortiz

On Tue, Nov 10, 2009 at 09:18:06AM +0100, Uwe Kleine-König wrote:
> One annoying thing about the old name was that the module was just
> called mc13783 which caused wrong expectations (at least for me).

Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 2/2] regulator/mc13783: various cleanups
  2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
@ 2009-11-10 13:08                       ` Mark Brown
  2009-11-11 14:11                       ` Liam Girdwood
  1 sibling, 0 replies; 40+ messages in thread
From: Mark Brown @ 2009-11-10 13:08 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Liam Girdwood, Samuel Ortiz

On Tue, Nov 10, 2009 at 09:18:07AM +0100, Uwe Kleine-König wrote:
> - define needed registers and bits in the driver
> - properly namespace functions and structs
> - fix locking as required by patch
>   "mfd/mc13783: near complete rewrite"
> - use platform_data as provided by "mfd/mc13783: near complete rewrite"
>   instead of accessing struct mc13783
> - struct mc13783_regulator_priv.desc is (and was) unused and so can go
>   away
> - use cpp magic to initialize mc13783_regulators
> - bring MODULE_LICENSE in sync with actual copyright
> - minor style fixes
> 
> This allows not including mc13783-private.h which I intend to remove
> soon.

Acked-by: Mark Brown <broonie@opensoruce.wolfsonmicro.com>

It'd have been a bit easier to review if done as more patches - eg, the
change to use macros to define the regulators could've been done as a
separate patch.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 1/2] regulator/mc13783: rename source file to match other drivers
  2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
  2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
  2009-11-10 11:44                     ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Mark Brown
@ 2009-11-11 14:09                     ` Liam Girdwood
  2 siblings, 0 replies; 40+ messages in thread
From: Liam Girdwood @ 2009-11-11 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Mark Brown, Samuel Ortiz

On Tue, 2009-11-10 at 09:18 +0100, Uwe Kleine-König wrote:
> One annoying thing about the old name was that the module was just
> called mc13783 which caused wrong expectations (at least for me).
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---

Applied.

Thanks

Liam


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 2/2] regulator/mc13783: various cleanups
  2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
  2009-11-10 13:08                       ` Mark Brown
@ 2009-11-11 14:11                       ` Liam Girdwood
  2009-11-11 14:16                         ` Uwe Kleine-König
  1 sibling, 1 reply; 40+ messages in thread
From: Liam Girdwood @ 2009-11-11 14:11 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Mark Brown, Samuel Ortiz

On Tue, 2009-11-10 at 09:18 +0100, Uwe Kleine-König wrote:
> - define needed registers and bits in the driver
> - properly namespace functions and structs
> - fix locking as required by patch
>   "mfd/mc13783: near complete rewrite"
> - use platform_data as provided by "mfd/mc13783: near complete rewrite"
>   instead of accessing struct mc13783
> - struct mc13783_regulator_priv.desc is (and was) unused and so can go
>   away
> - use cpp magic to initialize mc13783_regulators
> - bring MODULE_LICENSE in sync with actual copyright
> - minor style fixes
> 
> This allows not including mc13783-private.h which I intend to remove
> soon.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---

I assume the new mc13783 function and type definitions are in mfd-next ?

  CC [M]  drivers/regulator/mc13783-regulator.o
drivers/regulator/mc13783-regulator.c: In function ‘mc13783_regulator_enable’:
drivers/regulator/mc13783-regulator.c:116: error: implicit declaration of function ‘mc13783_lock’
drivers/regulator/mc13783-regulator.c:117: error: implicit declaration of function ‘mc13783_reg_rmw’
drivers/regulator/mc13783-regulator.c:120: error: implicit declaration of function ‘mc13783_unlock’
drivers/regulator/mc13783-regulator.c: In function ‘mc13783_regulator_is_enabled’:
drivers/regulator/mc13783-regulator.c:148: error: implicit declaration of function ‘mc13783_reg_read’
drivers/regulator/mc13783-regulator.c: In function ‘mc13783_regulator_probe’:
drivers/regulator/mc13783-regulator.c:175: error: dereferencing pointer to incomplete type
drivers/regulator/mc13783-regulator.c:182: error: dereferencing pointer to incomplete type
drivers/regulator/mc13783-regulator.c:183: error: dereferencing pointer to incomplete type
drivers/regulator/mc13783-regulator.c: In function ‘mc13783_regulator_remove’:
drivers/regulator/mc13783-regulator.c:215: error: dereferencing pointer to incomplete type
make[1]: *** [drivers/regulator/mc13783-regulator.o] Error 1
make: *** [drivers/regulator/mc13783-regulator.o] Error 2


Liam


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 2/2] regulator/mc13783: various cleanups
  2009-11-11 14:11                       ` Liam Girdwood
@ 2009-11-11 14:16                         ` Uwe Kleine-König
  2009-11-11 14:30                           ` Liam Girdwood
  0 siblings, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-11 14:16 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: linux-kernel, Sascha Hauer, Mark Brown, Samuel Ortiz

Hello,

On Wed, Nov 11, 2009 at 02:11:48PM +0000, Liam Girdwood wrote:
> On Tue, 2009-11-10 at 09:18 +0100, Uwe Kleine-König wrote:
> > - define needed registers and bits in the driver
> > - properly namespace functions and structs
> > - fix locking as required by patch
> >   "mfd/mc13783: near complete rewrite"
> > - use platform_data as provided by "mfd/mc13783: near complete rewrite"
> >   instead of accessing struct mc13783
> > - struct mc13783_regulator_priv.desc is (and was) unused and so can go
> >   away
> > - use cpp magic to initialize mc13783_regulators
> > - bring MODULE_LICENSE in sync with actual copyright
> > - minor style fixes
> > 
> > This allows not including mc13783-private.h which I intend to remove
> > soon.
> > 
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > Cc: Sascha Hauer <s.hauer@pengutronix.de>
> > Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> > Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> > Cc: Samuel Ortiz <sameo@linux.intel.com>
> > ---
> 
> I assume the new mc13783 function and type definitions are in mfd-next ?
Yes, it's 3a69e7d9f6b1fb6c6db7f23e22351e3db7d55ebb in todays linux-next.
 
Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 2/2] regulator/mc13783: various cleanups
  2009-11-11 14:16                         ` Uwe Kleine-König
@ 2009-11-11 14:30                           ` Liam Girdwood
  0 siblings, 0 replies; 40+ messages in thread
From: Liam Girdwood @ 2009-11-11 14:30 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Sascha Hauer, Mark Brown, Samuel Ortiz

On Wed, 2009-11-11 at 15:16 +0100, Uwe Kleine-König wrote:
> Hello,
> 
> On Wed, Nov 11, 2009 at 02:11:48PM +0000, Liam Girdwood wrote:
> > On Tue, 2009-11-10 at 09:18 +0100, Uwe Kleine-König wrote:
> > > - define needed registers and bits in the driver
> > > - properly namespace functions and structs
> > > - fix locking as required by patch
> > >   "mfd/mc13783: near complete rewrite"
> > > - use platform_data as provided by "mfd/mc13783: near complete rewrite"
> > >   instead of accessing struct mc13783
> > > - struct mc13783_regulator_priv.desc is (and was) unused and so can go
> > >   away
> > > - use cpp magic to initialize mc13783_regulators
> > > - bring MODULE_LICENSE in sync with actual copyright
> > > - minor style fixes
> > > 
> > > This allows not including mc13783-private.h which I intend to remove
> > > soon.
> > > 
> > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > Cc: Sascha Hauer <s.hauer@pengutronix.de>
> > > Cc: Liam Girdwood <lrg@slimlogic.co.uk>
> > > Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> > > Cc: Samuel Ortiz <sameo@linux.intel.com>
> > > ---
> > 
> > I assume the new mc13783 function and type definitions are in mfd-next ?
> Yes, it's 3a69e7d9f6b1fb6c6db7f23e22351e3db7d55ebb in todays linux-next.
>  

Applied

Thanks.

Liam


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH RESENT] [RTC] Add Freescale MC13783 RTC driver
  2009-11-10 11:10             ` Uwe Kleine-König
@ 2009-11-22 21:49               ` Uwe Kleine-König
  0 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-22 21:49 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel, Sascha Hauer, Paul Gortmaker, rtc-linux

Hi Alessandro,

On Tue, Nov 10, 2009 at 12:10:01PM +0100, Uwe Kleine-König wrote:
> On Tue, Nov 10, 2009 at 10:30:54AM +0100, Alessandro Zummo wrote:
> > On Tue, 10 Nov 2009 09:32:47 +0100
> > Uwe Kleine-König <u.kleine-koenig@pengutronix.de> wrote:
> > 
> > > +	ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
> > > +			mc13783_rtc_update_handler, DRIVER_NAME, priv);
> > > +	if (ret)
> > > +		goto err_update_irq_request;
> > > +
> > > +	mc13783_unlock(priv->mc13783);
> > > +
> > > +	priv->rtc = rtc_device_register(pdev->name,
> > > +			&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
> > > +
> > 
> >  isn't better to enable irqs after registration.
> IMHO it doesn't make a difference for the 1HZ irq, still more as it
> remains masked.  The reset irq should be requested before registration.
I thought again about the 1HZ irq.  There are two different things to
consider:

 - After rtc_device_register the .update_irq_enable callback could be
   called at once.  At that time the update irq should already be
   registered.  So register irq before rtc_device.
 - The update irq handler calls rtc_update_irq(struct rtc_device *,
   ...) so the rtc_device should already be complete.  That is, register
   before the irq can trigger.

To satisfy both you need to make sure the irq is registered before
registration but masked (either at controller level as in my patch or at
device level (which should be the usual thing but there is no knob in
the mc13783's rtc for that)).

Will you take my patch?

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-06  0:28                 ` Samuel Ortiz
  2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
@ 2009-11-24 21:44                   ` Uwe Kleine-König
  2009-11-24 23:26                     ` Samuel Ortiz
  1 sibling, 1 reply; 40+ messages in thread
From: Uwe Kleine-König @ 2009-11-24 21:44 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hi Samuel,

On Fri, Nov 06, 2009 at 01:28:50AM +0100, Samuel Ortiz wrote:
> On Fri, Nov 06, 2009 at 12:56:08AM +0100, Uwe Kleine-König wrote:
> > This fixes several things while still providing the old API:
> > 
> >  - simplify and fix locking
> >  - better error handling
> >  - don't ack all irqs making it impossible to detect a reset of the
> >    rtc
> >  - use a timeout variant to wait for completion of ADC conversion
> >  - provide platform-data to regulator subdevice (This allows making
> >    struct mc13783 opaque for other drivers after the regulator driver is
> >    updated to use its platform_data.)
> >  - expose all interrupts
> >  - use threaded irq
> Thanks Uwe, patch applied to my for-next branch.

I now worked on the touch interface for the mc13783 and found some
non-critical but non-nice things in the mc13783_adc_do_conversion
function.

If it looks OK for you, can you please squash the patch below into the
original commit?  I think the changes are not worth to be mentioned in
the commit log.

Intensions are:
 - Request and ack irq before provoking the next to behave if there was
   already an ADCDONE irq pending.

 - restore TSMOD only after conversions are read out

 - assert return value is 0 or -E...

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/mfd/mc13783-core.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index 99267ed..dc1add0 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -483,12 +483,13 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 		return -EINVAL;
 	}
 
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
-	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
-
 	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
 	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
 			mc13783_handler_adcdone, __func__, &adcdone_data);
+	mc13783_ackirq(mc13783, MC13783_IRQ_ADCDONE);
+
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
+	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
 
 	mc13783_unlock(mc13783);
 
@@ -501,15 +502,18 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 
 	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
 
+	if (ret > 0)
+		for (i = 0; i < 4; ++i) {
+			ret = mc13783_reg_read(mc13783,
+					MC13783_REG_ADC_2, &sample[i]);
+			if (ret)
+				break;
+		}
+
 	if (mode == MC13783_ADC_MODE_TS)
 		/* restore TSMOD */
 		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
 
-	if (ret > 0)
-		for (i = 0; i < 4; ++i)
-			mc13783_reg_read(mc13783,
-					MC13783_REG_ADC_2, &sample[i]);
-
 	mc13783->flags &= ~MC13783_ADC_WORKING;
 out:
 	mc13783_unlock(mc13783);
-- 
1.6.5.2


-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-24 21:44                   ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
@ 2009-11-24 23:26                     ` Samuel Ortiz
  2009-12-02 18:54                       ` Uwe Kleine-König
  0 siblings, 1 reply; 40+ messages in thread
From: Samuel Ortiz @ 2009-11-24 23:26 UTC (permalink / raw)
  To: Uwe Kleine-König; +Cc: linux-kernel, Sascha Hauer, Mark Brown

Hi Uwe,

On Tue, Nov 24, 2009 at 10:44:52PM +0100, Uwe Kleine-König wrote:
> Hi Samuel,
> 
> On Fri, Nov 06, 2009 at 01:28:50AM +0100, Samuel Ortiz wrote:
> > On Fri, Nov 06, 2009 at 12:56:08AM +0100, Uwe Kleine-König wrote:
> > > This fixes several things while still providing the old API:
> > > 
> > >  - simplify and fix locking
> > >  - better error handling
> > >  - don't ack all irqs making it impossible to detect a reset of the
> > >    rtc
> > >  - use a timeout variant to wait for completion of ADC conversion
> > >  - provide platform-data to regulator subdevice (This allows making
> > >    struct mc13783 opaque for other drivers after the regulator driver is
> > >    updated to use its platform_data.)
> > >  - expose all interrupts
> > >  - use threaded irq
> > Thanks Uwe, patch applied to my for-next branch.
> 
> I now worked on the touch interface for the mc13783 and found some
> non-critical but non-nice things in the mc13783_adc_do_conversion
> function.
> 
> If it looks OK for you, can you please squash the patch below into the
> original commit?  
That's fine with me, I'll do it.

Cheers,
Samuel.

> I think the changes are not worth to be mentioned in
> the commit log.
> 
> Intensions are:
>  - Request and ack irq before provoking the next to behave if there was
>    already an ADCDONE irq pending.
> 
>  - restore TSMOD only after conversions are read out
> 
>  - assert return value is 0 or -E...
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
>  drivers/mfd/mc13783-core.c |   20 ++++++++++++--------
>  1 files changed, 12 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
> index 99267ed..dc1add0 100644
> --- a/drivers/mfd/mc13783-core.c
> +++ b/drivers/mfd/mc13783-core.c
> @@ -483,12 +483,13 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
>  		return -EINVAL;
>  	}
>  
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
> -	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
> -
>  	dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
>  	mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
>  			mc13783_handler_adcdone, __func__, &adcdone_data);
> +	mc13783_ackirq(mc13783, MC13783_IRQ_ADCDONE);
> +
> +	mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
> +	mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
>  
>  	mc13783_unlock(mc13783);
>  
> @@ -501,15 +502,18 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
>  
>  	mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
>  
> +	if (ret > 0)
> +		for (i = 0; i < 4; ++i) {
> +			ret = mc13783_reg_read(mc13783,
> +					MC13783_REG_ADC_2, &sample[i]);
> +			if (ret)
> +				break;
> +		}
> +
>  	if (mode == MC13783_ADC_MODE_TS)
>  		/* restore TSMOD */
>  		mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
>  
> -	if (ret > 0)
> -		for (i = 0; i < 4; ++i)
> -			mc13783_reg_read(mc13783,
> -					MC13783_REG_ADC_2, &sample[i]);
> -
>  	mc13783->flags &= ~MC13783_ADC_WORKING;
>  out:
>  	mc13783_unlock(mc13783);
> -- 
> 1.6.5.2
> 
> 
> -- 
> Pengutronix e.K.                              | Uwe Kleine-König            |
> Industrial Linux Solutions                    | http://www.pengutronix.de/  |

-- 
Intel Open Source Technology Centre
http://oss.intel.com/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] mfd/mc13783: near complete rewrite
  2009-11-24 23:26                     ` Samuel Ortiz
@ 2009-12-02 18:54                       ` Uwe Kleine-König
  0 siblings, 0 replies; 40+ messages in thread
From: Uwe Kleine-König @ 2009-12-02 18:54 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: linux-kernel, Sascha Hauer, Mark Brown, Luotao Fu

Hi Samuel,

> > If it looks OK for you, can you please squash the patch below into the
> > original commit?  
> That's fine with me, I'll do it.
In the hope not to annoy you, below is another modification that greatly
improves the touch function.  Feel free to to squash into the original
commit again.

Best regards and thanks
Uwe

----------------->8--------------
From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

mfd/mc13783: don't set ADREFMODE for touch conversions

Setting ADREFMODE is utter nonsense, but that's hard to read out of the
spec.  Strange enough it's possible to read x and y values even when
it's set.  When unset you can get values not only for the axes, but also
for contact resistance which allows the touch driver to report pressure
values.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/mfd/mc13783-core.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index dc1add0..a1ade23 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -462,8 +462,8 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 
 	switch (mode) {
 	case MC13783_ADC_MODE_TS:
-		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
-			| MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
+		adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
+			MC13783_ADC0_TSMOD1;
 		adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
 		break;
 
-- 
1.6.5.2

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |

^ permalink raw reply related	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2009-12-02 18:54 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-23 20:38 [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
2009-10-24  8:35 ` [PATCH] [RTC] Add Freescale MC13783 RTC driver Uwe Kleine-König
2009-10-24  8:35   ` Uwe Kleine-König
2009-11-01 20:34   ` Uwe Kleine-König
2009-11-01 20:34     ` Uwe Kleine-König
2009-11-04 18:12     ` Valentin Longchamp
2009-11-04 18:12       ` Valentin Longchamp
2009-11-05 15:06       ` Valentin Longchamp
2009-11-05 15:06         ` Valentin Longchamp
2009-11-10  8:32         ` [PATCH RESENT] " Uwe Kleine-König
2009-11-10  9:30           ` Alessandro Zummo
2009-11-10 11:10             ` Uwe Kleine-König
2009-11-22 21:49               ` Uwe Kleine-König
2009-11-03 19:20   ` [PATCH] " Uwe Kleine-König
2009-11-03 19:20     ` Uwe Kleine-König
2009-11-01 21:06 ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
2009-11-02 11:51 ` Mark Brown
2009-11-02 13:58   ` Uwe Kleine-König
2009-11-02 14:09     ` Mark Brown
2009-11-02 14:32       ` Uwe Kleine-König
2009-11-02 15:12         ` Mark Brown
2009-11-02 20:56   ` [PATCH] mfd/mc13783: change type of irq handlers to irq_handler_t Uwe Kleine-König
2009-11-03 19:31     ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
2009-11-04 18:35       ` Samuel Ortiz
2009-11-04 22:28         ` Uwe Kleine-König
2009-11-05 22:31           ` Samuel Ortiz
2009-11-05 23:53             ` Uwe Kleine-König
2009-11-05 23:56               ` Uwe Kleine-König
2009-11-06  0:28                 ` Samuel Ortiz
2009-11-10  8:18                   ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Uwe Kleine-König
2009-11-10  8:18                     ` [PATCH 2/2] regulator/mc13783: various cleanups Uwe Kleine-König
2009-11-10 13:08                       ` Mark Brown
2009-11-11 14:11                       ` Liam Girdwood
2009-11-11 14:16                         ` Uwe Kleine-König
2009-11-11 14:30                           ` Liam Girdwood
2009-11-10 11:44                     ` [PATCH 1/2] regulator/mc13783: rename source file to match other drivers Mark Brown
2009-11-11 14:09                     ` Liam Girdwood
2009-11-24 21:44                   ` [PATCH] mfd/mc13783: near complete rewrite Uwe Kleine-König
2009-11-24 23:26                     ` Samuel Ortiz
2009-12-02 18:54                       ` Uwe Kleine-König

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.