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 <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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 <[email protected]>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
*
- * 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",
+ ®ulator_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 <[email protected]>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <[email protected]>");
+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 <[email protected]>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
*
- * 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
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 <[email protected]>
Signed-off-by: Uwe Kleine-König <u.kleine-kö[email protected]>
Cc: Valentin Longchamp <[email protected]>
Cc: Paul Gortmaker <[email protected]>
Cc: Alessandro Zummo <[email protected]>
Cc: [email protected]
---
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/[email protected]
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 <[email protected]>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
1.6.5
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 <[email protected]>
> Signed-off-by: Uwe Kleine-K?nig <[email protected]>
> Cc: Valentin Longchamp <[email protected]>
> Cc: Paul Gortmaker <[email protected]>
> Cc: Alessandro Zummo <[email protected]>
> Cc: [email protected]
> ---
> 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/ |
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 <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
> 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/ |
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.
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/ |
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.
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/ |
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.
Signed-off-by: Uwe Kleine-König <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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
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 <[email protected]>
> Signed-off-by: Uwe Kleine-K?nig <[email protected]>
> Cc: Valentin Longchamp <[email protected]>
> Cc: Paul Gortmaker <[email protected]>
> Cc: Alessandro Zummo <[email protected]>
> Cc: [email protected]
> ---
> 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/ |
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 <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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 <[email protected]>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
*
- * 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",
+ ®ulator_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 <[email protected]>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <[email protected]>");
+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 <[email protected]>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
*
- * 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
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 <[email protected]>
>> Signed-off-by: Uwe Kleine-K?nig <[email protected]>
>> Cc: Valentin Longchamp <[email protected]>
>> Cc: Paul Gortmaker <[email protected]>
>> Cc: Alessandro Zummo <[email protected]>
>> Cc: [email protected]
>> ---
>> 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
[email protected], Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne
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 <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
> 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 <[email protected]>
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/
Hello Samuel,
On Wed, Nov 04, 2009 at 07:35:08PM +0100, Samuel Ortiz wrote:
> > - * Copyright 2009 Pengutronix, Sascha Hauer <[email protected]>
> 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/ |
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 <[email protected]>
>>> Signed-off-by: Uwe Kleine-K?nig <[email protected]>
>>> Cc: Valentin Longchamp <[email protected]>
>>> Cc: Paul Gortmaker <[email protected]>
>>> Cc: Alessandro Zummo <[email protected]>
>>> Cc: [email protected]
>>> ---
>>> 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 <[email protected]>
Val
--
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
[email protected], Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne
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/
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 <[email protected]>
*
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <[email protected]>
+ *
* 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/ |
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 <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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 <[email protected]>
- *
- * 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 <[email protected]>
*
- * 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 <[email protected]>
*
- * 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",
+ ®ulator_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 <[email protected]>");
-MODULE_LICENSE("GPL");
+module_exit(mc13783_exit);
+MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <[email protected]>");
+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 <[email protected]>
+ * Copyright 2009 Pengutronix
+ * Uwe Kleine-Koenig <[email protected]>
*
- * 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
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 <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
> 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 <[email protected]>
> - *
> - * 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 <[email protected]>
> *
> - * 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 <[email protected]>
> *
> - * 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",
> + ®ulator_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 <[email protected]>");
> -MODULE_LICENSE("GPL");
> +module_exit(mc13783_exit);
>
> +MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
> +MODULE_AUTHOR("Uwe Kleine-Koenig <[email protected]>");
> +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 <[email protected]>
> + * Copyright 2009 Pengutronix
> + * Uwe Kleine-Koenig <[email protected]>
> *
> - * 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/
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 <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Liam Girdwood <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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
- 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 <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Liam Girdwood <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Samuel Ortiz <[email protected]>
---
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 <[email protected]");
MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
MODULE_ALIAS("platform:mc13783-regulator");
--
1.6.5.2
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 <[email protected]>
Signed-off-by: Uwe Kleine-König <u.kleine-kö[email protected]>
Acked-by: Valentin Longchamp <[email protected]>
Cc: Paul Gortmaker <[email protected]>
Cc: Alessandro Zummo <[email protected]>
Cc: [email protected]
---
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 <[email protected]>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
1.6.5.2
On Tue, 10 Nov 2009 09:32:47 +0100
Uwe Kleine-K?nig <[email protected]> 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
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 <[email protected]> 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/ |
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 <[email protected]>
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 <[email protected]>
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.
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 <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
Applied.
Thanks
Liam
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 <[email protected]>
> Cc: Sascha Hauer <[email protected]>
> Cc: Liam Girdwood <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Samuel Ortiz <[email protected]>
> ---
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
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 <[email protected]>
> > Cc: Sascha Hauer <[email protected]>
> > Cc: Liam Girdwood <[email protected]>
> > Cc: Mark Brown <[email protected]>
> > Cc: Samuel Ortiz <[email protected]>
> > ---
>
> 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/ |
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 <[email protected]>
> > > Cc: Sascha Hauer <[email protected]>
> > > Cc: Liam Girdwood <[email protected]>
> > > Cc: Mark Brown <[email protected]>
> > > Cc: Samuel Ortiz <[email protected]>
> > > ---
> >
> > I assume the new mc13783 function and type definitions are in mfd-next ?
> Yes, it's 3a69e7d9f6b1fb6c6db7f23e22351e3db7d55ebb in todays linux-next.
>
Applied
Thanks.
Liam
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 <[email protected]> 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/ |
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 <[email protected]>
---
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/ |
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 <[email protected]>
> ---
> 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/
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 <[email protected]>
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 <[email protected]>
---
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/ |