All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/7] PCF50633 support
@ 2008-12-18  5:56 Balaji Rao
  2008-12-18  5:56 ` [PATCH V2 1/7] mfd: PCF50633 core driver Balaji Rao
                   ` (7 more replies)
  0 siblings, 8 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:56 UTC (permalink / raw)
  To: Samuel Ortiz, linux-kernel

Changes from V1:
	- Subdevice specific header files now included in its respective patch.
	- Introduces pcf50633_register_irq and pcf50633_free_irq for irq
	  management
	- Many changes in how subdevices are created and information is passed
	  to them. Thanks to Alessandro Zummo for suggestions.

The following series implements support for NXP PCF50633. It's basically
an I2C device with 9 regulators, an ADC, a PMIC, a Battery Charger
and a RTC.

This chip is used in Openmoko Neo Freerunner mobile phone.

Driver for a similar chip PCF50606, used in Openmoko Neo 1973 device
will follow soon.

The specs are open and are available at 
http://people.openmoko.org/tony_tu/GTA02/datasheet/PMU/PCF50633DS_02.pdf

Samuel, are you ok with merging this via linux-mfd tree ?

---
Balaji Rao (7):
      mfd: PCF50633 core driver
      mfd: PCF50633 adc driver
      mfd: PCF50633 gpio support
      rtc: PCF50633 rtc driver
      power_supply: PCF50633 battery charger driver
      input: PCF50633 input driver
      regulator: PCF50633 pmic driver

 drivers/input/misc/Kconfig             |    7 
 drivers/input/misc/Makefile            |    1 
 drivers/input/misc/pcf50633-input.c    |  132 ++++++
 drivers/mfd/Kconfig                    |   23 +
 drivers/mfd/Makefile                   |    6 
 drivers/mfd/pcf50633-adc.c             |  277 +++++++++++++
 drivers/mfd/pcf50633-core.c            |  707 ++++++++++++++++++++++++++++++++
 drivers/mfd/pcf50633-gpio.c            |  118 +++++
 drivers/power/Kconfig                  |    6 
 drivers/power/Makefile                 |    2 
 drivers/power/pcf50633-charger.c       |  358 ++++++++++++++++
 drivers/regulator/Kconfig              |    7 
 drivers/regulator/Makefile             |    1 
 drivers/regulator/pcf50633-regulator.c |  330 +++++++++++++++
 drivers/rtc/Kconfig                    |    6 
 drivers/rtc/Makefile                   |    1 
 drivers/rtc/rtc-pcf50633.c             |  336 +++++++++++++++
 include/linux/mfd/pcf50633/adc.h       |   72 +++
 include/linux/mfd/pcf50633/core.h      |  218 ++++++++++
 include/linux/mfd/pcf50633/gpio.h      |   52 ++
 include/linux/mfd/pcf50633/mbc.h       |  134 ++++++
 include/linux/mfd/pcf50633/pmic.h      |   68 +++
 22 files changed, 2861 insertions(+), 1 deletions(-)
 create mode 100644 drivers/input/misc/pcf50633-input.c
 create mode 100644 drivers/mfd/pcf50633-adc.c
 create mode 100644 drivers/mfd/pcf50633-core.c
 create mode 100644 drivers/mfd/pcf50633-gpio.c
 create mode 100644 drivers/power/pcf50633-charger.c
 create mode 100644 drivers/regulator/pcf50633-regulator.c
 create mode 100644 drivers/rtc/rtc-pcf50633.c
 create mode 100644 include/linux/mfd/pcf50633/adc.h
 create mode 100644 include/linux/mfd/pcf50633/core.h
 create mode 100644 include/linux/mfd/pcf50633/gpio.h
 create mode 100644 include/linux/mfd/pcf50633/mbc.h
 create mode 100644 include/linux/mfd/pcf50633/pmic.h


-- 
Thanks,
Balaji Rao


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

* [PATCH V2 1/7] mfd: PCF50633 core driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
@ 2008-12-18  5:56 ` Balaji Rao
  2008-12-18  5:56 ` [PATCH V2 2/7] mfd: PCF50633 adc driver Balaji Rao
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Samuel Ortiz

This patch implements the core of the PCF50633 driver. This core driver has
generic register read/write functions and does interrupt management for its
sub devices.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Samuel Ortiz <sameo@openedhand.com>
---
 drivers/mfd/Kconfig               |    9 
 drivers/mfd/Makefile              |    4 
 drivers/mfd/pcf50633-core.c       |  707 +++++++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/core.h |  218 +++++++++++
 4 files changed, 937 insertions(+), 1 deletions(-)
 create mode 100644 drivers/mfd/pcf50633-core.c
 create mode 100644 include/linux/mfd/pcf50633/core.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8cd3dd9..fb1f0f7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -153,6 +153,15 @@ config MFD_WM8350_I2C
 	  I2C as the control interface.  Additional options must be
 	  selected to enable support for the functionality of the chip.
 
+config MFD_PCF50633
+	tristate "Support for NXP PCF50633"
+	depends on I2C
+	help
+	  Say yes here if you have NXP PCF50633 chip on your board.
+	  This core driver provides register access and IRQ handling
+	  facilities, and registers devices for the various functions
+	  so that function-specific drivers can bind to them.
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9a5ad8a..6a4add8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -31,4 +31,6 @@ obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
 endif
 obj-$(CONFIG_UCB1400_CORE)	+= ucb1400_core.o
 
-obj-$(CONFIG_PMIC_DA903X)	+= da903x.o
\ No newline at end of file
+obj-$(CONFIG_PMIC_DA903X)	+= da903x.o
+
+obj-$(CONFIG_MFD_PCF50633)	+= pcf50633-core.o
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
new file mode 100644
index 0000000..07f433c
--- /dev/null
+++ b/drivers/mfd/pcf50633-core.c
@@ -0,0 +1,707 @@
+/* NXP PCF50633 Power Management Unit (PMU) driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * 	   Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+/* Two MBCS registers used during cold start */
+#define PCF50633_REG_MBCS1		0x4b
+#define PCF50633_REG_MBCS2		0x4c
+#define PCF50633_MBCS1_USBPRES 		0x01
+#define PCF50633_MBCS1_ADAPTPRES	0x01
+
+static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(pcf->i2c_client, reg,
+				num, data);
+	if (ret < 0)
+		dev_err(pcf->dev, "Error reading %d regs at %d\n", num, reg);
+
+	return ret;
+}
+
+static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_i2c_block_data(pcf->i2c_client, reg,
+				num, data);
+	if (ret < 0)
+		dev_err(pcf->dev, "Error writing %d regs at %d\n", num, reg);
+
+	return ret;
+
+}
+
+/* Read a block of upto 32 regs  */
+int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
+					int nr_regs, u8 *data)
+{
+	int ret;
+
+	mutex_lock(&pcf->lock);
+	ret = __pcf50633_read(pcf, reg, nr_regs, data);
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_read_block);
+
+/* Write a block of upto 32 regs  */
+int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
+					int nr_regs, u8 *data)
+{
+	int ret;
+
+	mutex_lock(&pcf->lock);
+	ret = __pcf50633_write(pcf, reg, nr_regs, data);
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_write_block);
+
+u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
+{
+	u8 val;
+
+	mutex_lock(&pcf->lock);
+	__pcf50633_read(pcf, reg, 1, &val);
+	mutex_unlock(&pcf->lock);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_read);
+
+int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
+{
+	int ret;
+
+	mutex_lock(&pcf->lock);
+	ret = __pcf50633_write(pcf, reg, 1, &val);
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_write);
+
+int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
+{
+	int ret;
+	u8 tmp;
+
+	val &= mask;
+
+	mutex_lock(&pcf->lock);
+	ret = __pcf50633_read(pcf, reg, 1, &tmp);
+	if (ret < 0)
+		goto out;
+
+	tmp &= ~mask;
+	tmp |= val;
+	ret = __pcf50633_write(pcf, reg, 1, &tmp);
+
+out:
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
+
+int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
+{
+	int ret;
+	u8 tmp;
+
+	mutex_lock(&pcf->lock);
+	ret = __pcf50633_read(pcf, reg, 1, &tmp);
+	if (ret < 0)
+		goto out;
+
+	tmp &= ~val;
+	ret = __pcf50633_write(pcf, reg, 1, &tmp);
+
+out:
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
+
+/* sysfs attributes */
+static ssize_t show_dump_regs(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pcf50633 *pcf = dev_get_drvdata(dev);
+	u8 dump[16];
+	int n, n1, idx = 0;
+	char *buf1 = buf;
+	static u8 address_no_read[] = { /* must be ascending */
+		PCF50633_REG_INT1,
+		PCF50633_REG_INT2,
+		PCF50633_REG_INT3,
+		PCF50633_REG_INT4,
+		PCF50633_REG_INT5,
+		0 /* terminator */
+	};
+
+	for (n = 0; n < 256; n += sizeof(dump)) {
+		for (n1 = 0; n1 < sizeof(dump); n1++)
+			if (n == address_no_read[idx]) {
+				idx++;
+				dump[n1] = 0x00;
+			} else
+				dump[n1] = pcf50633_reg_read(pcf, n + n1);
+
+		hex_dump_to_buffer(dump, sizeof(dump), 16, 1, buf1, 128, 0);
+		buf1 += strlen(buf1);
+		*buf1++ = '\n';
+		*buf1 = '\0';
+	}
+
+	return buf1 - buf;
+}
+static DEVICE_ATTR(dump_regs, 0400, show_dump_regs, NULL);
+
+static ssize_t show_resume_reason(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct pcf50633 *pcf = dev_get_drvdata(dev);
+	int n;
+
+	n = sprintf(buf, "%02x%02x%02x%02x%02x\n",
+				pcf->resume_reason[0],
+				pcf->resume_reason[1],
+				pcf->resume_reason[2],
+				pcf->resume_reason[3],
+				pcf->resume_reason[4]);
+
+	return n;
+}
+static DEVICE_ATTR(resume_reason, 0400, show_resume_reason, NULL);
+
+static struct attribute *pcf_sysfs_entries[] = {
+	&dev_attr_dump_regs.attr,
+	&dev_attr_resume_reason.attr,
+	NULL,
+};
+
+static struct attribute_group pcf_attr_group = {
+	.name	= NULL,			/* put in device directory */
+	.attrs	= pcf_sysfs_entries,
+};
+
+int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
+			void (*handler) (int, void *), void *data)
+{
+	if (irq < 0 || irq > PCF50633_NUM_IRQ || !handler)
+		return -EINVAL;
+
+	if (WARN_ON(pcf->irq_handler[irq].handler))
+		return -EBUSY;
+
+	mutex_lock(&pcf->lock);
+	pcf->irq_handler[irq].handler = handler;
+	pcf->irq_handler[irq].data = data;
+	mutex_unlock(&pcf->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_register_irq);
+
+int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
+{
+	if (irq < 0 || irq > PCF50633_NUM_IRQ)
+		return -EINVAL;
+
+	mutex_lock(&pcf->lock);
+	pcf->irq_handler[irq].handler = NULL;
+	mutex_unlock(&pcf->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_free_irq);
+
+static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
+{
+	u8 reg, bits, tmp;
+	int ret = 0, idx;
+
+	idx = irq >> 3;
+	reg =  PCF50633_REG_INT1M + idx;
+	bits = 1 << (irq & 0x07);
+
+	mutex_lock(&pcf->lock);
+
+	if (mask) {
+		ret = __pcf50633_read(pcf, reg, 1, &tmp);
+		if (ret < 0)
+			goto out;
+
+		tmp |= bits;
+
+		ret = __pcf50633_write(pcf, reg, 1, &tmp);
+		if (ret < 0)
+			goto out;
+
+		pcf->mask_regs[idx] &= ~bits;
+		pcf->mask_regs[idx] |= bits;
+	} else {
+		ret = __pcf50633_read(pcf, reg, 1, &tmp);
+		if (ret < 0)
+			goto out;
+
+		tmp &= ~bits;
+
+		ret = __pcf50633_write(pcf, reg, 1, &tmp);
+		if (ret < 0)
+			goto out;
+
+		pcf->mask_regs[idx] &= ~bits;
+	}
+out:
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+
+int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
+{
+	dev_info(pcf->dev, "Masking IRQ %d\n", irq);
+
+	return __pcf50633_irq_mask_set(pcf, irq, 1);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
+
+int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
+{
+	dev_info(pcf->dev, "Unmasking IRQ %d\n", irq);
+
+	return __pcf50633_irq_mask_set(pcf, irq, 0);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
+
+int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
+{
+	u8 reg, bits;
+
+	reg =  irq >> 3;
+	bits = 1 << (irq & 0x07);
+
+	return pcf->mask_regs[reg] & bits;
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
+
+static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
+{
+	if (pcf->irq_handler[irq].handler)
+		pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
+}
+
+/* Maximum amount of time ONKEY is held before emergency action is taken */
+#define PCF50633_ONKEY1S_TIMEOUT 8
+
+static void pcf50633_irq_worker(struct work_struct *work)
+{
+	struct pcf50633 *pcf;
+	int ret, i, j;
+	u8 pcf_int[5], chgstat;
+
+	pcf = container_of(work, struct pcf50633, irq_work);
+
+	/* Read the 5 INT regs in one transaction */
+	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
+						ARRAY_SIZE(pcf_int), pcf_int);
+	if (ret != ARRAY_SIZE(pcf_int)) {
+		dev_err(pcf->dev, "Error reading INT registers\n");
+
+		/*
+		 * If this doesn't ACK the interrupt to the chip, we'll be
+		 * called once again as we're level triggered.
+		 */
+		goto out;
+	}
+
+	/* We immediately read the usb and adapter status. We thus make sure
+	 * only of USBINS/USBREM IRQ handlers are called */
+	if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
+		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+		if (chgstat & (0x3 << 4))
+			pcf_int[0] &= ~(1 << PCF50633_INT1_USBREM);
+		else
+			pcf_int[0] &= ~(1 << PCF50633_INT1_USBINS);
+	}
+
+	/* Make sure only one of ADPINS or ADPREM is set */
+	if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
+		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+		if (chgstat & (0x3 << 4))
+			pcf_int[0] &= ~(1 << PCF50633_INT1_ADPREM);
+		else
+			pcf_int[0] &= ~(1 << PCF50633_INT1_ADPINS);
+	}
+
+	dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
+			"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
+			pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
+
+	/* Some revisions of the chip don't have a 8s standby mode on
+	 * ONKEY1S press. We try to manually do it in such cases. */
+	if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
+		dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
+							pcf->onkey1s_held);
+		if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
+			if (pcf->pdata->force_shutdown)
+				pcf->pdata->force_shutdown(pcf);
+	}
+
+	if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
+		dev_info(pcf->dev, "ONKEY1S held\n");
+		pcf->onkey1s_held = 1 ;
+
+		/* Unmask IRQ_SECOND */
+		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
+						PCF50633_INT1_SECOND);
+
+		/* Unmask IRQ_ONKEYR */
+		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
+						PCF50633_INT2_ONKEYR);
+	}
+
+	if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
+		pcf->onkey1s_held = 0;
+
+		/* Mask SECOND and ONKEYR interrupts */
+		if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
+			pcf50633_reg_set_bit_mask(pcf,
+					PCF50633_REG_INT1M,
+					PCF50633_INT1_SECOND,
+					PCF50633_INT1_SECOND);
+
+		if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
+			pcf50633_reg_set_bit_mask(pcf,
+					PCF50633_REG_INT2M,
+					PCF50633_INT2_ONKEYR,
+					PCF50633_INT2_ONKEYR);
+	}
+
+	/* Have we just resumed ? */
+	if (pcf->is_suspended) {
+		pcf->is_suspended = 0;
+
+		/* Set the resume reason filtering out non resumers */
+		for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
+			pcf->resume_reason[i] = pcf_int[i] &
+						pcf->pdata->resumers[i];
+
+		/* Make sure we don't pass on any ONKEY events to
+		 * userspace now */
+		pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
+		/* Unset masked interrupts */
+		pcf_int[i] &= ~pcf->mask_regs[i];
+
+		for (j = 0; j < 8 ; j++)
+			if (pcf_int[i] & (1 << j))
+				pcf50633_irq_call_handler(pcf, (i * 8) + j);
+	}
+
+out:
+	put_device(pcf->dev);
+	enable_irq(pcf->irq);
+}
+
+static irqreturn_t pcf50633_irq(int irq, void *data)
+{
+	struct pcf50633 *pcf = data;
+
+	dev_dbg(pcf->dev, "pcf50633_irq\n");
+
+	get_device(pcf->dev);
+	disable_irq(pcf->irq);
+	schedule_work(&pcf->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static void
+pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
+						struct platform_device **pdev)
+{
+	struct pcf50633_subdev_pdata *subdev_pdata;
+	int ret;
+
+	*pdev = platform_device_alloc(name, -1);
+	if (!*pdev) {
+		dev_err(pcf->dev, "Falied to allocate %s\n", name);
+		return;
+	}
+
+	subdev_pdata = kmalloc(sizeof(*subdev_pdata), GFP_KERNEL);
+	if (!subdev_pdata) {
+		dev_err(pcf->dev, "Error allocating subdev pdata\n");
+		platform_device_put(*pdev);
+	}
+
+	subdev_pdata->pcf = pcf;
+	platform_device_add_data(*pdev, subdev_pdata, sizeof(*subdev_pdata));
+
+	(*pdev)->dev.parent = pcf->dev;
+
+	ret = platform_device_add(*pdev);
+	if (ret) {
+		dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret);
+		platform_device_put(*pdev);
+		*pdev = NULL;
+	}
+}
+
+#ifdef CONFIG_PM
+static int pcf50633_suspend(struct device *dev, pm_message_t state)
+{
+	struct pcf50633 *pcf;
+	int ret = 0, i;
+	u8 res[5];
+
+	pcf = dev_get_drvdata(dev);
+
+	/* Make sure our interrupt handlers are not called
+	 * henceforth */
+	disable_irq(pcf->irq);
+
+	/* Make sure that any running IRQ worker has quit */
+	cancel_work_sync(&pcf->irq_work);
+
+	/* Save the masks */
+	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
+				ARRAY_SIZE(pcf->suspend_irq_masks),
+					pcf->suspend_irq_masks);
+	if (ret < 0) {
+		dev_err(pcf->dev, "error saving irq masks\n");
+		goto out;
+	}
+
+	/* Write wakeup irq masks */
+	for (i = 0; i < ARRAY_SIZE(res); i++)
+		res[i] = ~pcf->pdata->resumers[i];
+
+	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+					ARRAY_SIZE(res), &res[0]);
+	if (ret < 0) {
+		dev_err(pcf->dev, "error writing wakeup irq masks\n");
+		goto out;
+	}
+
+	pcf->is_suspended = 1;
+
+out:
+	return ret;
+}
+
+static int pcf50633_resume(struct device *dev)
+{
+	struct pcf50633 *pcf;
+	int ret;
+
+	pcf = dev_get_drvdata(dev);
+
+	/* Write the saved mask registers */
+	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+				ARRAY_SIZE(pcf->suspend_irq_masks),
+					pcf->suspend_irq_masks);
+	if (ret < 0)
+		dev_err(pcf->dev, "Error restoring saved suspend masks\n");
+
+	get_device(pcf->dev);
+
+	/*
+	 * Clear any pending interrupts and set resume reason if any.
+	 * This will leave with enable_irq()
+	 */
+	pcf50633_irq_worker(&pcf->irq_work);
+
+	return 0;
+}
+#else
+#define pcf50633_suspend NULL
+#define pcf50633_resume NULL
+#endif
+
+static int __devinit pcf50633_probe(struct i2c_client *client,
+				const struct i2c_device_id *ids)
+{
+	struct pcf50633 *pcf;
+	struct pcf50633_platform_data *pdata = client->dev.platform_data;
+	int i, ret = 0;
+	int version, variant;
+
+	pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
+	if (!pcf)
+		return -ENOMEM;
+
+	pcf->pdata = pdata;
+
+	mutex_init(&pcf->lock);
+
+	i2c_set_clientdata(client, pcf);
+	pcf->dev = &client->dev;
+	pcf->i2c_client = client;
+	pcf->irq = client->irq;
+
+	INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
+
+	version = pcf50633_reg_read(pcf, 0);
+	variant = pcf50633_reg_read(pcf, 1);
+	if (version < 0 || variant < 0) {
+		dev_err(pcf->dev, "Unable to probe pcf50633\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	dev_info(pcf->dev, "Probed device version %d variant %d\n",
+							version, variant);
+
+	/* Enable all interrupts except RTC SECOND */
+	pcf->mask_regs[0] = 0x80;
+	pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
+
+	/* Create sub devices */
+	pcf50633_client_dev_register(pcf, "pcf50633-input",
+						&pcf->input_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-rtc",
+						&pcf->rtc_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-mbc",
+						&pcf->mbc_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-adc",
+						&pcf->adc_pdev);
+
+	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
+		struct platform_device *pdev;
+
+		pdev = platform_device_alloc("pcf50633-regltr", i);
+		if (!pdev) {
+			dev_err(pcf->dev, "Cannot create regulator\n");
+			continue;
+		}
+
+		pdev->dev.parent = pcf->dev;
+		pdev->dev.platform_data = &pdata->reg_init_data[i];
+		pdev->dev.driver_data = pcf;
+		pcf->regulator_pdev[i] = pdev;
+
+		platform_device_add(pdev);
+	}
+
+	if (client->irq) {
+		set_irq_handler(client->irq, handle_level_irq);
+		ret = request_irq(client->irq, pcf50633_irq,
+				IRQF_TRIGGER_LOW, "pcf50633", pcf);
+
+		if (ret) {
+			dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
+			goto err;
+		}
+	} else {
+		dev_err(pcf->dev, "No IRQ configured\n");
+		goto err;
+	}
+
+	if (enable_irq_wake(client->irq) < 0)
+		dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
+			"in this hardware revision", client->irq);
+
+	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
+	if (ret)
+		dev_err(pcf->dev, "error creating sysfs entries\n");
+
+	if (pdata->probe_done)
+		pdata->probe_done(pcf);
+
+	return 0;
+
+err:
+	kfree(pcf);
+	return ret;
+}
+
+static int __devexit pcf50633_remove(struct i2c_client *client)
+{
+	struct pcf50633 *pcf = i2c_get_clientdata(client);
+	int i;
+
+	free_irq(pcf->irq, pcf);
+
+	platform_device_unregister(pcf->input_pdev);
+	platform_device_unregister(pcf->rtc_pdev);
+	platform_device_unregister(pcf->mbc_pdev);
+	platform_device_unregister(pcf->adc_pdev);
+
+	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
+		platform_device_unregister(pcf->regulator_pdev[i]);
+
+	kfree(pcf);
+
+	return 0;
+}
+
+static struct i2c_device_id pcf50633_id_table[] = {
+	{"pcf50633", 0x73},
+};
+
+static struct i2c_driver pcf50633_driver = {
+	.driver = {
+		.name	= "pcf50633",
+		.suspend = pcf50633_suspend,
+		.resume	= pcf50633_resume,
+	},
+	.id_table = pcf50633_id_table,
+	.probe = pcf50633_probe,
+	.remove = __devexit_p(pcf50633_remove),
+};
+
+static int __init pcf50633_init(void)
+{
+	return i2c_add_driver(&pcf50633_driver);
+}
+
+static void __exit pcf50633_exit(void)
+{
+	i2c_del_driver(&pcf50633_driver);
+}
+
+MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
+MODULE_LICENSE("GPL");
+
+module_init(pcf50633_init);
+module_exit(pcf50633_exit);
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
new file mode 100644
index 0000000..4455b21
--- /dev/null
+++ b/include/linux/mfd/pcf50633/core.h
@@ -0,0 +1,218 @@
+/*
+ * core.h  -- Core driver for NXP PCF50633
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_PCF50633_CORE_H
+#define __LINUX_MFD_PCF50633_CORE_H
+
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/power_supply.h>
+
+struct pcf50633;
+
+#define PCF50633_NUM_REGULATORS	11
+
+struct pcf50633_platform_data {
+	struct regulator_init_data reg_init_data[PCF50633_NUM_REGULATORS];
+
+	char **batteries;
+	int num_batteries;
+
+	/* Callbacks */
+	void (*probe_done)(struct pcf50633 *);
+	void (*mbc_event_callback)(struct pcf50633 *, int);
+	void (*regulator_registered)(struct pcf50633 *, int);
+	void (*force_shutdown)(struct pcf50633 *);
+
+	u8 resumers[5];
+};
+
+struct pcf50633_subdev_pdata {
+	struct pcf50633 *pcf;
+};
+
+struct pcf50633_irq {
+	void (*handler) (int, void *);
+	void *data;
+};
+
+int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
+			void (*handler) (int, void *), void *data);
+int pcf50633_free_irq(struct pcf50633 *pcf, int irq);
+
+int pcf50633_irq_mask(struct pcf50633 *pcf, int irq);
+int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq);
+int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq);
+
+int pcf50633_read_block(struct pcf50633 *, u8 reg,
+					int nr_regs, u8 *data);
+int pcf50633_write_block(struct pcf50633 *pcf, u8 reg,
+					int nr_regs, u8 *data);
+u8 pcf50633_reg_read(struct pcf50633 *, u8 reg);
+int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val);
+
+int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val);
+int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 bits);
+
+/* Interrupt registers */
+
+#define PCF50633_REG_INT1	0x02
+#define PCF50633_REG_INT2	0x03
+#define PCF50633_REG_INT3	0x04
+#define PCF50633_REG_INT4	0x05
+#define PCF50633_REG_INT5	0x06
+
+#define PCF50633_REG_INT1M	0x07
+#define PCF50633_REG_INT2M	0x08
+#define PCF50633_REG_INT3M	0x09
+#define PCF50633_REG_INT4M	0x0a
+#define PCF50633_REG_INT5M	0x0b
+
+enum {
+	/* Chip IRQs */
+	PCF50633_IRQ_ADPINS,
+	PCF50633_IRQ_ADPREM,
+	PCF50633_IRQ_USBINS,
+	PCF50633_IRQ_USBREM,
+	PCF50633_IRQ_RESERVED1,
+	PCF50633_IRQ_RESERVED2,
+	PCF50633_IRQ_ALARM,
+	PCF50633_IRQ_SECOND,
+	PCF50633_IRQ_ONKEYR,
+	PCF50633_IRQ_ONKEYF,
+	PCF50633_IRQ_EXTON1R,
+	PCF50633_IRQ_EXTON1F,
+	PCF50633_IRQ_EXTON2R,
+	PCF50633_IRQ_EXTON2F,
+	PCF50633_IRQ_EXTON3R,
+	PCF50633_IRQ_EXTON3F,
+	PCF50633_IRQ_BATFULL,
+	PCF50633_IRQ_CHGHALT,
+	PCF50633_IRQ_THLIMON,
+	PCF50633_IRQ_THLIMOFF,
+	PCF50633_IRQ_USBLIMON,
+	PCF50633_IRQ_USBLIMOFF,
+	PCF50633_IRQ_ADCRDY,
+	PCF50633_IRQ_ONKEY1S,
+	PCF50633_IRQ_LOWSYS,
+	PCF50633_IRQ_LOWBAT,
+	PCF50633_IRQ_HIGHTMP,
+	PCF50633_IRQ_AUTOPWRFAIL,
+	PCF50633_IRQ_DWN1PWRFAIL,
+	PCF50633_IRQ_DWN2PWRFAIL,
+	PCF50633_IRQ_LEDPWRFAIL,
+	PCF50633_IRQ_LEDOVP,
+	PCF50633_IRQ_LDO1PWRFAIL,
+	PCF50633_IRQ_LDO2PWRFAIL,
+	PCF50633_IRQ_LDO3PWRFAIL,
+	PCF50633_IRQ_LDO4PWRFAIL,
+	PCF50633_IRQ_LDO5PWRFAIL,
+	PCF50633_IRQ_LDO6PWRFAIL,
+	PCF50633_IRQ_HCLDOPWRFAIL,
+	PCF50633_IRQ_HCLDOOVL,
+
+	/* Always last */
+	PCF50633_NUM_IRQ,
+};
+
+struct pcf50633 {
+	struct device *dev;
+	struct i2c_client *i2c_client;
+
+	struct pcf50633_platform_data *pdata;
+	int irq;
+	struct pcf50633_irq irq_handler[PCF50633_NUM_IRQ];
+	struct work_struct irq_work;
+	struct mutex lock;
+
+	u8 mask_regs[5];
+
+	u8 suspend_irq_masks[5];
+	u8 resume_reason[5];
+	int is_suspended;
+
+	int onkey1s_held;
+
+	struct platform_device *rtc_pdev;
+	struct platform_device *mbc_pdev;
+	struct platform_device *adc_pdev;
+	struct platform_device *input_pdev;
+	struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
+};
+
+enum pcf50633_reg_int1 {
+	PCF50633_INT1_ADPINS	= 0x01,	/* Adapter inserted */
+	PCF50633_INT1_ADPREM	= 0x02,	/* Adapter removed */
+	PCF50633_INT1_USBINS	= 0x04,	/* USB inserted */
+	PCF50633_INT1_USBREM	= 0x08,	/* USB removed */
+	/* reserved */
+	PCF50633_INT1_ALARM	= 0x40, /* RTC alarm time is reached */
+	PCF50633_INT1_SECOND	= 0x80,	/* RTC periodic second interrupt */
+};
+
+enum pcf50633_reg_int2 {
+	PCF50633_INT2_ONKEYR	= 0x01, /* ONKEY rising edge */
+	PCF50633_INT2_ONKEYF	= 0x02, /* ONKEY falling edge */
+	PCF50633_INT2_EXTON1R	= 0x04, /* EXTON1 rising edge */
+	PCF50633_INT2_EXTON1F	= 0x08, /* EXTON1 falling edge */
+	PCF50633_INT2_EXTON2R	= 0x10, /* EXTON2 rising edge */
+	PCF50633_INT2_EXTON2F	= 0x20, /* EXTON2 falling edge */
+	PCF50633_INT2_EXTON3R	= 0x40, /* EXTON3 rising edge */
+	PCF50633_INT2_EXTON3F	= 0x80, /* EXTON3 falling edge */
+};
+
+enum pcf50633_reg_int3 {
+	PCF50633_INT3_BATFULL	= 0x01, /* Battery full */
+	PCF50633_INT3_CHGHALT	= 0x02,	/* Charger halt */
+	PCF50633_INT3_THLIMON	= 0x04,
+	PCF50633_INT3_THLIMOFF	= 0x08,
+	PCF50633_INT3_USBLIMON	= 0x10,
+	PCF50633_INT3_USBLIMOFF	= 0x20,
+	PCF50633_INT3_ADCRDY	= 0x40, /* ADC result ready */
+	PCF50633_INT3_ONKEY1S	= 0x80,	/* ONKEY pressed 1 second */
+};
+
+enum pcf50633_reg_int4 {
+	PCF50633_INT4_LOWSYS		= 0x01,
+	PCF50633_INT4_LOWBAT		= 0x02,
+	PCF50633_INT4_HIGHTMP		= 0x04,
+	PCF50633_INT4_AUTOPWRFAIL	= 0x08,
+	PCF50633_INT4_DWN1PWRFAIL	= 0x10,
+	PCF50633_INT4_DWN2PWRFAIL	= 0x20,
+	PCF50633_INT4_LEDPWRFAIL	= 0x40,
+	PCF50633_INT4_LEDOVP		= 0x80,
+};
+
+enum pcf50633_reg_int5 {
+	PCF50633_INT5_LDO1PWRFAIL	= 0x01,
+	PCF50633_INT5_LDO2PWRFAIL	= 0x02,
+	PCF50633_INT5_LDO3PWRFAIL	= 0x04,
+	PCF50633_INT5_LDO4PWRFAIL	= 0x08,
+	PCF50633_INT5_LDO5PWRFAIL	= 0x10,
+	PCF50633_INT5_LDO6PWRFAIL	= 0x20,
+	PCF50633_INT5_HCLDOPWRFAIL	= 0x40,
+	PCF50633_INT5_HCLDOOVL		= 0x80,
+};
+
+/* misc. registers */
+#define PCF50633_REG_OOCSHDWN	0x0c
+
+/* LED registers */
+#define PCF50633_REG_LEDOUT 0x28
+#define PCF50633_REG_LEDENA 0x29
+#define PCF50633_REG_LEDCTL 0x2a
+#define PCF50633_REG_LEDDIM 0x2b
+
+#endif
+


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

* [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
  2008-12-18  5:56 ` [PATCH V2 1/7] mfd: PCF50633 core driver Balaji Rao
@ 2008-12-18  5:56 ` Balaji Rao
  2008-12-19 11:18   ` Jonathan Cameron
  2008-12-18  5:57 ` [PATCH V2 3/7] mfd: PCF50633 gpio support Balaji Rao
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Samuel Ortiz

This patch adds basic support for the PCF50633 ADC. The subtractive mode
is not supported yet.

Since we don't have adc subsystem, it currently lives in drivers/mfd.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Samuel Ortiz <sameo@openedhand.com>
---
 drivers/mfd/Kconfig              |    7 +
 drivers/mfd/Makefile             |    1 
 drivers/mfd/pcf50633-adc.c       |  277 ++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/adc.h |   72 ++++++++++
 4 files changed, 357 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/pcf50633-adc.c
 create mode 100644 include/linux/mfd/pcf50633/adc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fb1f0f7..02e3d73 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -162,6 +162,13 @@ config MFD_PCF50633
 	  facilities, and registers devices for the various functions
 	  so that function-specific drivers can bind to them.
 
+config PCF50633_ADC
+	tristate "Support for NXP PCF50633 ADC"
+	depends on MFD_PCF50633
+	help
+	 Say yes here if you want to include support for ADC in the
+	 NXP PCF50633 chip.
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 6a4add8..7a12b09 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_UCB1400_CORE)	+= ucb1400_core.o
 obj-$(CONFIG_PMIC_DA903X)	+= da903x.o
 
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633-core.o
+obj-$(CONFIG_PCF50633_ADC)	+= pcf50633-adc.o
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
new file mode 100644
index 0000000..f5f1fb0
--- /dev/null
+++ b/drivers/mfd/pcf50633-adc.c
@@ -0,0 +1,277 @@
+/* NXP PCF50633 ADC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ *  NOTE: This driver does not yet support subtractive ADC mode, which means
+ *  you can do only one measurement per read request.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/adc.h>
+
+struct pcf50633_adc_request {
+	int mux;
+	int avg;
+	int result;
+	void (*callback)(struct pcf50633 *, void *, int);
+	void *callback_param;
+
+	/* Used in case of sync requests */
+	struct completion completion;
+
+};
+
+#define PCF50633_MAX_ADC_FIFO_DEPTH 8
+
+struct pcf50633_adc {
+	struct pcf50633 *pcf;
+
+	/* Private stuff */
+	struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];
+	int queue_head;
+	int queue_tail;
+	struct mutex queue_mutex;
+};
+
+static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)
+{
+	return platform_get_drvdata(pcf->adc_pdev);
+}
+
+static void adc_read_setup(struct pcf50633 *pcf, int channel, int avg)
+{
+	channel &= PCF50633_ADCC1_ADCMUX_MASK;
+
+	/* kill ratiometric, but enable ACCSW biasing */
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
+
+	/* start ADC conversion on selected channel */
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
+		    PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
+}
+
+static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
+{
+	struct pcf50633_adc *adc = __to_adc(pcf);
+	int head;
+
+	mutex_lock(&adc->queue_mutex);
+
+	head = adc->queue_head;
+
+	if (!adc->queue[head]) {
+		mutex_unlock(&adc->queue_mutex);
+		return;
+	}
+	mutex_unlock(&adc->queue_mutex);
+
+	adc_read_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
+}
+
+static int
+adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
+{
+	struct pcf50633_adc *adc = __to_adc(pcf);
+	int head, tail;
+
+	mutex_lock(&adc->queue_mutex);
+
+	head = adc->queue_head;
+	tail = adc->queue_tail;
+
+	if (adc->queue[tail]) {
+		mutex_unlock(&adc->queue_mutex);
+		return -EBUSY;
+	}
+
+	adc->queue[tail] = req;
+	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
+
+	mutex_unlock(&adc->queue_mutex);
+
+	trigger_next_adc_job_if_any(pcf);
+
+	return 0;
+}
+
+static void
+pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
+{
+	struct pcf50633_adc_request *req = param;
+
+	req->result = result;
+	complete(&req->completion);
+}
+
+int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
+{
+	struct pcf50633_adc_request *req;
+
+	/* req is freed when the result is ready, in interrupt handler */
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->mux = mux;
+	req->avg = avg;
+	req->callback =  pcf50633_adc_sync_read_callback;
+	req->callback_param = req;
+
+	init_completion(&req->completion);
+	adc_enqueue_request(pcf, req);
+	wait_for_completion(&req->completion);
+
+	return req->result;
+}
+EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
+
+int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
+			     void (*callback)(struct pcf50633 *, void *, int),
+			     void *callback_param)
+{
+	struct pcf50633_adc_request *req;
+
+	/* req is freed when the result is ready, in interrupt handler */
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->mux = mux;
+	req->avg = avg;
+	req->callback = callback;
+	req->callback_param = callback_param;
+
+	adc_enqueue_request(pcf, req);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
+
+static int adc_result(struct pcf50633 *pcf)
+{
+	u8 adcs1, adcs3;
+	u16 result;
+
+	adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);
+	adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);
+	result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
+
+	dev_dbg(pcf->dev, "adc result = %d\n", result);
+
+	return result;
+}
+
+static void pcf50633_adc_irq(int irq, void *data)
+{
+	struct pcf50633_adc *adc = data;
+	struct pcf50633 *pcf = adc->pcf;
+	struct pcf50633_adc_request *req;
+	int head;
+
+	mutex_lock(&adc->queue_mutex);
+	head = adc->queue_head;
+
+	req = adc->queue[head];
+	if (WARN_ON(!req)) {
+		dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
+		mutex_unlock(&adc->queue_mutex);
+		return;
+	}
+	adc->queue[head] = NULL;
+	adc->queue_head = (head + 1) &
+				      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
+
+	mutex_unlock(&adc->queue_mutex);
+
+	req->callback(pcf, req->callback_param, adc_result(pcf));
+	kfree(req);
+
+	trigger_next_adc_job_if_any(pcf);
+}
+
+static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
+{
+	struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
+	struct pcf50633_adc *adc;
+
+	adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+	if (!adc)
+		return -ENOMEM;
+
+	adc->pcf = pdata->pcf;
+	platform_set_drvdata(pdev, adc);
+
+	pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ADCRDY,
+					pcf50633_adc_irq, adc);
+
+	mutex_init(&adc->queue_mutex);
+
+	return 0;
+}
+
+static int __devexit pcf50633_adc_remove(struct platform_device *pdev)
+{
+	struct pcf50633_adc *adc = platform_get_drvdata(pdev);
+	int i, head;
+
+	pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);
+
+	mutex_lock(&adc->queue_mutex);
+	head = adc->queue_head;
+
+	if (WARN_ON(adc->queue[head]))
+		dev_err(adc->pcf->dev,
+			"adc driver removed with request pending\n");
+
+	for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)
+		kfree(adc->queue[i]);
+
+	mutex_unlock(&adc->queue_mutex);
+	kfree(adc);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_adc_driver = {
+	.driver = {
+		.name = "pcf50633-adc",
+	},
+	.probe = pcf50633_adc_probe,
+	.remove = __devexit_p(pcf50633_adc_remove),
+};
+
+static int __init pcf50633_adc_init(void)
+{
+	return platform_driver_register(&pcf50633_adc_driver);
+}
+module_init(pcf50633_adc_init);
+
+static void __exit pcf50633_adc_exit(void)
+{
+	platform_driver_unregister(&pcf50633_adc_driver);
+}
+module_exit(pcf50633_adc_exit);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 adc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-adc");
+
diff --git a/include/linux/mfd/pcf50633/adc.h b/include/linux/mfd/pcf50633/adc.h
new file mode 100644
index 0000000..56669b4
--- /dev/null
+++ b/include/linux/mfd/pcf50633/adc.h
@@ -0,0 +1,72 @@
+/*
+ * adc.h  -- Driver for NXP PCF50633 ADC
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_PCF50633_ADC_H
+#define __LINUX_MFD_PCF50633_ADC_H
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/platform_device.h>
+
+/* ADC Registers */
+#define PCF50633_REG_ADCC3		0x52
+#define PCF50633_REG_ADCC2		0x53
+#define PCF50633_REG_ADCC1		0x54
+#define PCF50633_REG_ADCS1		0x55
+#define PCF50633_REG_ADCS2		0x56
+#define PCF50633_REG_ADCS3		0x57
+
+#define PCF50633_ADCC1_ADCSTART		0x01
+#define PCF50633_ADCC1_RES_10BIT	0x02
+#define PCF50633_ADCC1_AVERAGE_NO	0x00
+#define PCF50633_ADCC1_AVERAGE_4	0x04
+#define PCF50633_ADCC1_AVERAGE_8	0x08
+#define PCF50633_ADCC1_AVERAGE_16	0x0c
+#define PCF50633_ADCC1_MUX_BATSNS_RES	0x00
+#define PCF50633_ADCC1_MUX_BATSNS_SUBTR	0x10
+#define PCF50633_ADCC1_MUX_ADCIN2_RES	0x20
+#define PCF50633_ADCC1_MUX_ADCIN2_SUBTR	0x30
+#define PCF50633_ADCC1_MUX_BATTEMP	0x60
+#define PCF50633_ADCC1_MUX_ADCIN1	0x70
+#define PCF50633_ADCC1_AVERAGE_MASK	0x0c
+#define PCF50633_ADCC1_ADCMUX_MASK	0xf0
+
+#define PCF50633_ADCC2_RATIO_NONE	0x00
+#define PCF50633_ADCC2_RATIO_BATTEMP	0x01
+#define PCF50633_ADCC2_RATIO_ADCIN1	0x02
+#define PCF50633_ADCC2_RATIO_BOTH	0x03
+#define PCF50633_ADCC2_RATIOSETTL_100US 0x04
+
+#define PCF50633_ADCC3_ACCSW_EN		0x01
+#define PCF50633_ADCC3_NTCSW_EN		0x04
+#define PCF50633_ADCC3_RES_DIV_TWO	0x10
+#define PCF50633_ADCC3_RES_DIV_THREE	0x00
+
+#define PCF50633_ADCS3_REF_NTCSW	0x00
+#define PCF50633_ADCS3_REF_ACCSW	0x10
+#define PCF50633_ADCS3_REF_2V0		0x20
+#define PCF50633_ADCS3_REF_VISA		0x30
+#define PCF50633_ADCS3_REF_2V0_2	0x70
+#define PCF50633_ADCS3_ADCRDY		0x80
+
+#define PCF50633_ADCS3_ADCDAT1L_MASK	0x03
+#define PCF50633_ADCS3_ADCDAT2L_MASK	0x0c
+#define PCF50633_ADCS3_ADCDAT2L_SHIFT	2
+#define PCF50633_ASCS3_REF_MASK		0x70
+
+extern int
+pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
+		void (*callback)(struct pcf50633 *, void *, int),
+		void *callback_param);
+extern int
+pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg);
+
+#endif /* __LINUX_PCF50633_ADC_H */


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

* [PATCH V2 3/7] mfd: PCF50633 gpio support
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
  2008-12-18  5:56 ` [PATCH V2 1/7] mfd: PCF50633 core driver Balaji Rao
  2008-12-18  5:56 ` [PATCH V2 2/7] mfd: PCF50633 adc driver Balaji Rao
@ 2008-12-18  5:57 ` Balaji Rao
  2008-12-18  5:57 ` [PATCH V2 4/7] rtc: PCF50633 rtc driver Balaji Rao
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Samuel Ortiz

What the PCF05633 calls as a 'GPIO' is much more than the GPIO in the linux
sense and there are only 4 of them - which means, the gpiolib is not used
here.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Samuel Ortiz <sameo@openedhand.com>
---
 drivers/mfd/Kconfig               |    7 ++
 drivers/mfd/Makefile              |    1 
 drivers/mfd/pcf50633-gpio.c       |  118 +++++++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/gpio.h |   52 ++++++++++++++++
 4 files changed, 178 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/pcf50633-gpio.c
 create mode 100644 include/linux/mfd/pcf50633/gpio.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 02e3d73..cdb178b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -169,6 +169,13 @@ config PCF50633_ADC
 	 Say yes here if you want to include support for ADC in the
 	 NXP PCF50633 chip.
 
+config PCF50633_GPIO
+	tristate "Support for NXP PCF50633 GPIO"
+	depends on MFD_PCF50633
+	help
+	 Say yes here if you want to include support GPIO for pins on
+	 the PCF50633 chip.
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 7a12b09..5bc7c9f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -35,3 +35,4 @@ obj-$(CONFIG_PMIC_DA903X)	+= da903x.o
 
 obj-$(CONFIG_MFD_PCF50633)	+= pcf50633-core.o
 obj-$(CONFIG_PCF50633_ADC)	+= pcf50633-adc.o
+obj-$(CONFIG_PCF50633_GPIO)	+= pcf50633-gpio.o
diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c
new file mode 100644
index 0000000..2fa2eca
--- /dev/null
+++ b/drivers/mfd/pcf50633-gpio.c
@@ -0,0 +1,118 @@
+/* NXP PCF50633 GPIO Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/gpio.h>
+
+enum pcf50633_regulator_id {
+	PCF50633_REGULATOR_AUTO,
+	PCF50633_REGULATOR_DOWN1,
+	PCF50633_REGULATOR_DOWN2,
+	PCF50633_REGULATOR_LDO1,
+	PCF50633_REGULATOR_LDO2,
+	PCF50633_REGULATOR_LDO3,
+	PCF50633_REGULATOR_LDO4,
+	PCF50633_REGULATOR_LDO5,
+	PCF50633_REGULATOR_LDO6,
+	PCF50633_REGULATOR_HCLDO,
+	PCF50633_REGULATOR_MEMLDO,
+};
+
+#define PCF50633_REG_AUTOOUT	0x1a
+#define PCF50633_REG_DOWN1OUT	0x1e
+#define PCF50633_REG_DOWN2OUT	0x22
+#define PCF50633_REG_MEMLDOOUT	0x26
+#define PCF50633_REG_LDO1OUT	0x2d
+#define PCF50633_REG_LDO2OUT	0x2f
+#define PCF50633_REG_LDO3OUT	0x31
+#define PCF50633_REG_LDO4OUT	0x33
+#define PCF50633_REG_LDO5OUT	0x35
+#define PCF50633_REG_LDO6OUT	0x37
+#define PCF50633_REG_HCLDOOUT	0x39
+
+static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
+	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT,
+	[PCF50633_REGULATOR_DOWN1]	= PCF50633_REG_DOWN1OUT,
+	[PCF50633_REGULATOR_DOWN2]	= PCF50633_REG_DOWN2OUT,
+	[PCF50633_REGULATOR_MEMLDO]	= PCF50633_REG_MEMLDOOUT,
+	[PCF50633_REGULATOR_LDO1]	= PCF50633_REG_LDO1OUT,
+	[PCF50633_REGULATOR_LDO2]	= PCF50633_REG_LDO2OUT,
+	[PCF50633_REGULATOR_LDO3]	= PCF50633_REG_LDO3OUT,
+	[PCF50633_REGULATOR_LDO4]	= PCF50633_REG_LDO4OUT,
+	[PCF50633_REGULATOR_LDO5]	= PCF50633_REG_LDO5OUT,
+	[PCF50633_REGULATOR_LDO6]	= PCF50633_REG_LDO6OUT,
+	[PCF50633_REGULATOR_HCLDO]	= PCF50633_REG_HCLDOOUT,
+};
+
+int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val)
+{
+	u8 reg;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_set);
+
+u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio)
+{
+	u8 reg, val;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = pcf50633_reg_read(pcf, reg) & 0x07;
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_get);
+
+int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert)
+{
+	u8 val, reg;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = !!invert << 3;
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set);
+
+int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio)
+{
+	u8 reg, val;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = pcf50633_reg_read(pcf, reg);
+
+	return val & (1 << 3);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get);
+
+int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf,
+					int gpio, int regulator, int on)
+{
+	u8 reg, val, mask;
+
+	/* the *ENA register is always one after the *OUT register */
+	reg = pcf50633_regulator_registers[regulator] + 1;
+
+	val = !!on << (gpio - PCF50633_GPIO1);
+	mask = 1 << (gpio - PCF50633_GPIO1);
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set);
diff --git a/include/linux/mfd/pcf50633/gpio.h b/include/linux/mfd/pcf50633/gpio.h
new file mode 100644
index 0000000..a42b845
--- /dev/null
+++ b/include/linux/mfd/pcf50633/gpio.h
@@ -0,0 +1,52 @@
+/*
+ * gpio.h -- GPIO driver for NXP PCF50633
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_PCF50633_GPIO_H
+#define __LINUX_MFD_PCF50633_GPIO_H
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_GPIO1		1
+#define PCF50633_GPIO2		2
+#define PCF50633_GPIO3		3
+#define PCF50633_GPO		4
+
+#define PCF50633_REG_GPIO1CFG	0x14
+#define PCF50633_REG_GPIO2CFG	0x15
+#define PCF50633_REG_GPIO3CFG	0x16
+#define PCF50633_REG_GPOCFG 	0x17
+
+#define PCF50633_GPOCFG_GPOSEL_MASK	0x07
+
+enum pcf50633_reg_gpocfg {
+	PCF50633_GPOCFG_GPOSEL_0	= 0x00,
+	PCF50633_GPOCFG_GPOSEL_LED_NFET	= 0x01,
+	PCF50633_GPOCFG_GPOSEL_SYSxOK	= 0x02,
+	PCF50633_GPOCFG_GPOSEL_CLK32K	= 0x03,
+	PCF50633_GPOCFG_GPOSEL_ADAPUSB	= 0x04,
+	PCF50633_GPOCFG_GPOSEL_USBxOK	= 0x05,
+	PCF50633_GPOCFG_GPOSEL_ACTPH4	= 0x06,
+	PCF50633_GPOCFG_GPOSEL_1	= 0x07,
+	PCF50633_GPOCFG_GPOSEL_INVERSE	= 0x08,
+};
+
+int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val);
+u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio);
+
+int pcf50633_gpio_invert_set(struct pcf50633 *, int gpio, int invert);
+int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio);
+
+int pcf50633_gpio_power_supply_set(struct pcf50633 *,
+					int gpio, int regulator, int on);
+#endif /* __LINUX_MFD_PCF50633_GPIO_H */
+
+


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

* [PATCH V2 4/7] rtc: PCF50633 rtc driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
                   ` (2 preceding siblings ...)
  2008-12-18  5:57 ` [PATCH V2 3/7] mfd: PCF50633 gpio support Balaji Rao
@ 2008-12-18  5:57 ` Balaji Rao
  2008-12-18  9:03   ` Alessandro Zummo
  2008-12-18  5:57 ` [PATCH V2 5/7] power_supply: PCF50633 battery charger driver Balaji Rao
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:57 UTC (permalink / raw)
  To: linux-kernel
  Cc: Balaji Rao, Andy Green, Alessandro Zummo, Paul Gortmaker, rtc-linux

Changes from V1:
	- Removed include/linux/mfd/pcf50633/rtc.h and moved defenitions into
	  the source file.
	- Remove PIE and introduce UIE support. UIE being the one actually
	  supported in the chip.

Alessandro, I'll change to the new API for AIE once it appears upstream.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Paul Gortmaker <a.zummo@towertech.it>
Cc: rtc-linux@googlegroups.com
---
 drivers/rtc/Kconfig        |    6 +
 drivers/rtc/Makefile       |    1 
 drivers/rtc/rtc-pcf50633.c |  336 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 343 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-pcf50633.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 123092d..68e68d2 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -497,6 +497,12 @@ config RTC_DRV_WM8350
 	  This driver can also be built as a module. If so, the module
 	  will be called "rtc-wm8350".
 
+config RTC_DRV_PCF50633
+	depends on MFD_PCF50633
+	tristate "NXP PCF50633 RTC"
+	help
+	  If you say yes here you get support for the NXP PCF50633 RTC.
+
 comment "on-CPU RTC drivers"
 
 config RTC_DRV_OMAP
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e79c91..a717fec 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -70,3 +70,4 @@ obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
 obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
 obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
new file mode 100644
index 0000000..2f33509
--- /dev/null
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -0,0 +1,336 @@
+/* NXP PCF50633 RTC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/err.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_REG_RTCSC	0x59 /* Second */
+#define PCF50633_REG_RTCMN	0x5a /* Minute */
+#define PCF50633_REG_RTCHR	0x5b /* Hour */
+#define PCF50633_REG_RTCWD	0x5c /* Weekday */
+#define PCF50633_REG_RTCDT	0x5d /* Day */
+#define PCF50633_REG_RTCMT	0x5e /* Month */
+#define PCF50633_REG_RTCYR	0x5f /* Year */
+#define PCF50633_REG_RTCSCA	0x60 /* Alarm Second */
+#define PCF50633_REG_RTCMNA	0x61 /* Alarm Minute */
+#define PCF50633_REG_RTCHRA	0x62 /* Alarm Hour */
+#define PCF50633_REG_RTCWDA	0x63 /* Alarm Weekday */
+#define PCF50633_REG_RTCDTA	0x64 /* Alarm Day */
+#define PCF50633_REG_RTCMTA	0x65 /* Alarm Month */
+#define PCF50633_REG_RTCYRA	0x66 /* Alarm Year */
+
+enum pcf50633_time_indexes {
+	PCF50633_TI_SEC,
+	PCF50633_TI_MIN,
+	PCF50633_TI_HOUR,
+	PCF50633_TI_WKDAY,
+	PCF50633_TI_DAY,
+	PCF50633_TI_MONTH,
+	PCF50633_TI_YEAR,
+	PCF50633_TI_EXTENT /* always last */
+};
+
+struct pcf50633_time {
+	u_int8_t time[PCF50633_TI_EXTENT];
+};
+
+struct pcf50633_rtc {
+	int alarm_enabled;
+	int second_enabled;
+
+	struct pcf50633 *pcf;
+	struct rtc_device *rtc_dev;
+};
+
+static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
+{
+	rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);
+	rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);
+	rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
+	rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
+	rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
+	rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]);
+	rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
+}
+
+static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
+{
+	pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);
+	pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);
+	pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
+	pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
+	pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
+	pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon);
+	pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
+}
+
+static int
+pcf50633_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+	struct pcf50633_rtc *rtc = dev_get_drvdata(dev);
+
+	switch (cmd) {
+	case RTC_AIE_OFF:
+		rtc->alarm_enabled = 0;
+		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+		return 0;
+	case RTC_AIE_ON:
+		rtc->alarm_enabled = 1;
+		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+		return 0;
+	case RTC_UIE_OFF:
+		rtc->second_enabled = 0;
+		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND);
+		return 0;
+	case RTC_UIE_ON:
+		rtc->second_enabled = 1;
+		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND);
+		return 0;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct pcf50633_rtc *rtc;
+	struct pcf50633_time pcf_tm;
+	int ret;
+
+	rtc = dev_get_drvdata(dev);
+
+	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,
+					    PCF50633_TI_EXTENT,
+					    &pcf_tm.time[0]);
+	if (ret != PCF50633_TI_EXTENT) {
+		dev_err(dev, "Failed to read time\n");
+		return -EIO;
+	}
+
+	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
+		pcf_tm.time[PCF50633_TI_DAY],
+		pcf_tm.time[PCF50633_TI_MONTH],
+		pcf_tm.time[PCF50633_TI_YEAR],
+		pcf_tm.time[PCF50633_TI_HOUR],
+		pcf_tm.time[PCF50633_TI_MIN],
+		pcf_tm.time[PCF50633_TI_SEC]);
+
+	pcf2rtc_time(tm, &pcf_tm);
+
+	dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+		tm->tm_mday, tm->tm_mon, tm->tm_year,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return rtc_valid_tm(tm);
+}
+
+static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct pcf50633_rtc *rtc;
+	struct pcf50633_time pcf_tm;
+	int second_masked, alarm_masked, ret = 0;
+
+	rtc = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+		tm->tm_mday, tm->tm_mon, tm->tm_year,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	rtc2pcf_time(&pcf_tm, tm);
+
+	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
+		pcf_tm.time[PCF50633_TI_DAY],
+		pcf_tm.time[PCF50633_TI_MONTH],
+		pcf_tm.time[PCF50633_TI_YEAR],
+		pcf_tm.time[PCF50633_TI_HOUR],
+		pcf_tm.time[PCF50633_TI_MIN],
+		pcf_tm.time[PCF50633_TI_SEC]);
+
+
+	second_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_SECOND);
+	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	if (!second_masked)
+		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND);
+	if (!alarm_masked)
+		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	/* Returns 0 on success */
+	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,
+					     PCF50633_TI_EXTENT,
+					     &pcf_tm.time[0]);
+
+	if (!second_masked)
+		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND);
+	if (!alarm_masked)
+		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	return ret;
+}
+
+static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct pcf50633_rtc *rtc;
+	struct pcf50633_time pcf_tm;
+	int ret = 0;
+
+	rtc = dev_get_drvdata(dev);
+
+	alrm->enabled = rtc->alarm_enabled;
+
+	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
+				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+	if (ret != PCF50633_TI_EXTENT) {
+		dev_err(dev, "Failed to read time\n");
+		return -EIO;
+	}
+
+	pcf2rtc_time(&alrm->time, &pcf_tm);
+
+	return rtc_valid_tm(&alrm->time);
+}
+
+static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct pcf50633_rtc *rtc;
+	struct pcf50633_time pcf_tm;
+	int alarm_masked, ret = 0;
+
+	rtc = dev_get_drvdata(dev);
+
+	rtc2pcf_time(&pcf_tm, &alrm->time);
+
+	/* do like mktime does and ignore tm_wday */
+	pcf_tm.time[PCF50633_TI_WKDAY] = 7;
+
+	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	/* disable alarm interrupt */
+	if (!alarm_masked)
+		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	/* Returns 0 on success */
+	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
+				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+
+	if (!alarm_masked)
+		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+	return ret;
+}
+
+static struct rtc_class_ops pcf50633_rtc_ops = {
+	.ioctl		= pcf50633_rtc_ioctl,
+	.read_time	= pcf50633_rtc_read_time,
+	.set_time	= pcf50633_rtc_set_time,
+	.read_alarm	= pcf50633_rtc_read_alarm,
+	.set_alarm	= pcf50633_rtc_set_alarm,
+};
+
+static void pcf50633_rtc_irq(int irq, void *data)
+{
+	struct pcf50633_rtc *rtc = data;
+
+	switch (irq) {
+	case PCF50633_IRQ_ALARM:
+		rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+		break;
+	case PCF50633_IRQ_SECOND:
+		rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+		break;
+	}
+}
+
+static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
+{
+	struct pcf50633_subdev_pdata *pdata;
+	struct pcf50633_rtc *rtc;
+	struct rtc_device *rtc_dev;
+
+	rtc_dev = rtc_device_register("pcf50633-rtc", &pdev->dev,
+				&pcf50633_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(rtc_dev))
+		return PTR_ERR(rtc_dev);
+
+	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+	if (!rtc) {
+		dev_err(&pdev->dev, "allocation of pcf50633_rtc failed\n");
+		rtc_device_unregister(rtc_dev);
+		return -ENOMEM;
+	}
+
+	pdata = pdev->dev.platform_data;
+	rtc->pcf = pdata->pcf;
+	rtc->rtc_dev = rtc_dev;
+	platform_set_drvdata(pdev, rtc);
+
+	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
+					pcf50633_rtc_irq, rtc);
+	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_SECOND,
+					pcf50633_rtc_irq, rtc);
+
+	return 0;
+}
+
+static int __devexit pcf50633_rtc_remove(struct platform_device *pdev)
+{
+	struct pcf50633_rtc *rtc;
+
+	rtc = platform_get_drvdata(pdev);
+	rtc_device_unregister(rtc->rtc_dev);
+
+	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
+	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND);
+
+	kfree(rtc);
+
+	return 0;
+}
+
+
+static struct platform_driver pcf50633_rtc_driver = {
+	.driver = {
+		.name = "pcf50633-rtc",
+	},
+	.probe = pcf50633_rtc_probe,
+	.remove = __devexit_p(pcf50633_rtc_remove),
+};
+
+static int __init pcf50633_rtc_init(void)
+{
+	return platform_driver_register(&pcf50633_rtc_driver);
+}
+module_init(pcf50633_rtc_init);
+
+static void __exit pcf50633_rtc_exit(void)
+{
+	platform_driver_unregister(&pcf50633_rtc_driver);
+}
+module_exit(pcf50633_rtc_exit);
+
+MODULE_DESCRIPTION("PCF50633 RTC driver");
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_LICENSE("GPL");
+


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

* [PATCH V2 5/7] power_supply: PCF50633 battery charger driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
                   ` (3 preceding siblings ...)
  2008-12-18  5:57 ` [PATCH V2 4/7] rtc: PCF50633 rtc driver Balaji Rao
@ 2008-12-18  5:57 ` Balaji Rao
  2008-12-18 20:26   ` Balaji Rao
  2008-12-25 15:45   ` Anton Vorontsov
  2008-12-18  5:58 ` [PATCH V2 6/7] input: PCF50633 input driver Balaji Rao
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Anton Vorontsov, David Woodhouse

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Anton Vorontsov <cbou@mail.ru>
Cc: David Woodhouse <dwmw2@infradead.org>
---
 drivers/power/Kconfig            |    6 +
 drivers/power/Makefile           |    2 
 drivers/power/pcf50633-charger.c |  358 ++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/mbc.h |  134 ++++++++++++++
 4 files changed, 500 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/pcf50633-charger.c
 create mode 100644 include/linux/mfd/pcf50633/mbc.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 52f8676..04f6316 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -75,4 +75,10 @@ config BATTERY_BQ27x00
 	help
 	  Say Y here to enable support for batteries with BQ27200(I2C) chip.
 
+config CHARGER_PCF50633
+	tristate "NXP PCF50633 MBC"
+	depends on MFD_PCF50633
+	help
+	 Say Y to include support for NXP PCF50633 Main Battery Charger.
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e6f6865..62bcb76 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o
+
+obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
new file mode 100644
index 0000000..83fc70c
--- /dev/null
+++ b/drivers/power/pcf50633-charger.c
@@ -0,0 +1,358 @@
+/* NXP PCF50633 Main Battery Charger Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/mbc.h>
+
+struct pcf50633_mbc {
+	struct pcf50633 *pcf;
+
+	int adapter_active;
+	int adapter_online;
+	int usb_active;
+	int usb_online;
+
+	struct power_supply usb;
+	struct power_supply adapter;
+};
+
+int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+	int ret = 0;
+	u8 bits;
+
+	if (ma >= 1000)
+		bits = PCF50633_MBCC7_USB_1000mA;
+	else if (ma >= 500)
+		bits = PCF50633_MBCC7_USB_500mA;
+	else if (ma >= 100)
+		bits = PCF50633_MBCC7_USB_100mA;
+	else
+		bits = PCF50633_MBCC7_USB_SUSPEND;
+
+	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
+					PCF50633_MBCC7_USB_MASK, bits);
+	if (ret)
+		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
+	else
+		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
+
+	power_supply_changed(&mbc->usb);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
+
+int pcf50633_mbc_get_status(struct pcf50633 *pcf)
+{
+	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
+	int status = 0;
+
+	if (mbc->usb_online)
+		status |= PCF50633_MBC_USB_ONLINE;
+	if (mbc->usb_online)
+		status |= PCF50633_MBC_USB_ACTIVE;
+	if (mbc->usb_online)
+		status |= PCF50633_MBC_ADAPTER_ONLINE;
+	if (mbc->usb_online)
+		status |= PCF50633_MBC_ADAPTER_ACTIVE;
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
+
+void pcf50633_mbc_set_status(struct pcf50633 *pcf, int what, int status)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+
+	if (what & PCF50633_MBC_USB_ONLINE)
+		mbc->usb_online = !!status;
+	if (what & PCF50633_MBC_USB_ACTIVE)
+		mbc->usb_active = !!status;
+	if (what & PCF50633_MBC_ADAPTER_ONLINE)
+		mbc->adapter_online = !!status;
+	if (what & PCF50633_MBC_ADAPTER_ACTIVE)
+		mbc->adapter_active = !!status;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_set_status);
+
+static ssize_t
+show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+
+	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
+	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
+
+	return sprintf(buf, "%d\n", chgmod);
+}
+static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
+
+static ssize_t
+show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+						PCF50633_MBCC7_USB_MASK;
+	unsigned int ma;
+
+	if (usblim == PCF50633_MBCC7_USB_1000mA)
+		ma = 1000;
+	else if (usblim == PCF50633_MBCC7_USB_500mA)
+		ma = 500;
+	else if (usblim == PCF50633_MBCC7_USB_100mA)
+		ma = 100;
+	else
+		ma = 0;
+
+	return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_usblim(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	unsigned long ma;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &ma);
+	if (ret)
+		return -EINVAL;
+
+	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
+
+	return count;
+}
+
+static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
+
+static struct attribute *pcf50633_mbc_sysfs_entries[] = {
+	&dev_attr_chgmode.attr,
+	&dev_attr_usb_curlim.attr,
+	NULL,
+};
+
+static struct attribute_group mbc_attr_group = {
+	.name	= NULL,			/* put in device directory */
+	.attrs	= pcf50633_mbc_sysfs_entries,
+};
+
+static void
+pcf50633_mbc_irq_handler(int irq, void *data)
+{
+	struct pcf50633_mbc *mbc = data;
+
+	/* USB */
+	if (irq == PCF50633_IRQ_USBINS) {
+		mbc->usb_online = 1;
+	} else if (irq == PCF50633_IRQ_USBREM) {
+		mbc->usb_online = 0;
+		mbc->usb_active = 0;
+		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
+	}
+
+	/* Adapter */
+	if (irq == PCF50633_IRQ_ADPINS) {
+		mbc->adapter_online = 1;
+		mbc->adapter_active = 1;
+	} else if (irq == PCF50633_IRQ_ADPREM) {
+		mbc->adapter_online = 0;
+		mbc->adapter_active = 0;
+	}
+
+	if (irq == PCF50633_IRQ_BATFULL) {
+		mbc->usb_active = 0;
+		mbc->adapter_active = 0;
+	}
+
+	power_supply_changed(&mbc->usb);
+	power_supply_changed(&mbc->adapter);
+
+	if (mbc->pcf->pdata->mbc_event_callback)
+		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
+}
+
+static int adapter_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval =  mbc->adapter_online;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int usb_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = mbc->usb_online;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static enum power_supply_property power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const u8 mbc_irq_handlers[] = {
+	PCF50633_IRQ_ADPINS,
+	PCF50633_IRQ_ADPREM,
+	PCF50633_IRQ_USBINS,
+	PCF50633_IRQ_USBREM,
+	PCF50633_IRQ_BATFULL,
+	PCF50633_IRQ_CHGHALT,
+	PCF50633_IRQ_THLIMON,
+	PCF50633_IRQ_THLIMOFF,
+	PCF50633_IRQ_USBLIMON,
+	PCF50633_IRQ_USBLIMOFF,
+	PCF50633_IRQ_LOWSYS,
+	PCF50633_IRQ_LOWBAT,
+};
+
+static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
+{
+	struct pcf50633_mbc *mbc;
+	struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
+	int ret;
+	int i;
+	u8 mbcs1;
+
+	mbc = kzalloc(sizeof(*mbc), GFP_KERNEL);
+	if (!mbc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, mbc);
+	mbc->pcf = pdata->pcf;
+
+	/* Set up IRQ handlers */
+	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
+					pcf50633_mbc_irq_handler, mbc);
+
+	/* Create power supplies */
+	mbc->adapter.name		= "adapter";
+	mbc->adapter.type		= POWER_SUPPLY_TYPE_MAINS;
+	mbc->adapter.properties		= power_props;
+	mbc->adapter.num_properties	= ARRAY_SIZE(power_props);
+	mbc->adapter.get_property	= &adapter_get_property;
+	mbc->adapter.supplied_to	= mbc->pcf->pdata->batteries;
+	mbc->adapter.num_supplicants	= mbc->pcf->pdata->num_batteries;
+
+	mbc->usb.name			= "usb";
+	mbc->usb.type			= POWER_SUPPLY_TYPE_USB;
+	mbc->usb.properties		= power_props;
+	mbc->usb.num_properties		= ARRAY_SIZE(power_props);
+	mbc->usb.get_property		= usb_get_property;
+	mbc->usb.supplied_to		= mbc->pcf->pdata->batteries;
+	mbc->usb.num_supplicants	= mbc->pcf->pdata->num_batteries;
+
+	ret = power_supply_register(&pdev->dev, &mbc->adapter);
+	if (ret) {
+		dev_err(mbc->pcf->dev, "failed to register adapter\n");
+		kfree(mbc);
+		return ret;
+	}
+
+	ret = power_supply_register(&pdev->dev, &mbc->usb);
+	if (ret) {
+		dev_err(mbc->pcf->dev, "failed to register usb\n");
+		power_supply_unregister(&mbc->adapter);
+		kfree(mbc);
+		return ret;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
+	if (ret)
+		dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
+
+	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
+	if (mbcs1 & PCF50633_MBCS1_USBPRES)
+		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
+	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
+		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
+
+	return 0;
+}
+
+static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
+	int i;
+
+	/* Remove IRQ handlers */
+	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
+
+	power_supply_unregister(&mbc->usb);
+	power_supply_unregister(&mbc->adapter);
+
+	kfree(mbc);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_mbc_driver = {
+	.driver = {
+		.name = "pcf50633-mbc",
+	},
+	.probe = pcf50633_mbc_probe,
+	.remove = __devexit_p(pcf50633_mbc_remove),
+};
+
+static int __init pcf50633_mbc_init(void)
+{
+	return platform_driver_register(&pcf50633_mbc_driver);
+}
+module_init(pcf50633_mbc_init);
+
+static void __exit pcf50633_mbc_exit(void)
+{
+	platform_driver_unregister(&pcf50633_mbc_driver);
+}
+module_exit(pcf50633_mbc_exit);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 mbc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-mbc");
diff --git a/include/linux/mfd/pcf50633/mbc.h b/include/linux/mfd/pcf50633/mbc.h
new file mode 100644
index 0000000..6e17619
--- /dev/null
+++ b/include/linux/mfd/pcf50633/mbc.h
@@ -0,0 +1,134 @@
+/*
+ * mbc.h  -- Driver for NXP PCF50633 Main Battery Charger
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_PCF50633_MBC_H
+#define __LINUX_MFD_PCF50633_MBC_H
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/platform_device.h>
+
+#define PCF50633_REG_MBCC1	0x43
+#define PCF50633_REG_MBCC2	0x44
+#define PCF50633_REG_MBCC3	0x45
+#define PCF50633_REG_MBCC4	0x46
+#define PCF50633_REG_MBCC5	0x47
+#define PCF50633_REG_MBCC6	0x48
+#define PCF50633_REG_MBCC7	0x49
+#define PCF50633_REG_MBCC8	0x4a
+#define PCF50633_REG_MBCS1	0x4b
+#define PCF50633_REG_MBCS2	0x4c
+#define PCF50633_REG_MBCS3	0x4d
+
+enum pcf50633_reg_mbcc1 {
+	PCF50633_MBCC1_CHGENA		= 0x01,	/* Charger enable */
+	PCF50633_MBCC1_AUTOSTOP		= 0x02,
+	PCF50633_MBCC1_AUTORES		= 0x04, /* automatic resume */
+	PCF50633_MBCC1_RESUME		= 0x08, /* explicit resume cmd */
+	PCF50633_MBCC1_RESTART		= 0x10, /* restart charging */
+	PCF50633_MBCC1_PREWDTIME_60M	= 0x20,	/* max. precharging time */
+	PCF50633_MBCC1_WDTIME_1H	= 0x00,
+	PCF50633_MBCC1_WDTIME_2H	= 0x40,
+	PCF50633_MBCC1_WDTIME_4H	= 0x80,
+	PCF50633_MBCC1_WDTIME_6H	= 0xc0,
+};
+#define PCF50633_MBCC1_WDTIME_MASK	  0xc0
+
+enum pcf50633_reg_mbcc2 {
+	PCF50633_MBCC2_VBATCOND_2V7	= 0x00,
+	PCF50633_MBCC2_VBATCOND_2V85	= 0x01,
+	PCF50633_MBCC2_VBATCOND_3V0	= 0x02,
+	PCF50633_MBCC2_VBATCOND_3V15	= 0x03,
+	PCF50633_MBCC2_VMAX_4V		= 0x00,
+	PCF50633_MBCC2_VMAX_4V20	= 0x28,
+	PCF50633_MBCC2_VRESDEBTIME_64S	= 0x80,	/* debounce time (32/64sec) */
+};
+
+enum pcf50633_reg_mbcc7 {
+	PCF50633_MBCC7_USB_100mA	= 0x00,
+	PCF50633_MBCC7_USB_500mA	= 0x01,
+	PCF50633_MBCC7_USB_1000mA	= 0x02,
+	PCF50633_MBCC7_USB_SUSPEND	= 0x03,
+	PCF50633_MBCC7_BATTEMP_EN	= 0x04,
+	PCF50633_MBCC7_BATSYSIMAX_1A6	= 0x00,
+	PCF50633_MBCC7_BATSYSIMAX_1A8	= 0x40,
+	PCF50633_MBCC7_BATSYSIMAX_2A0	= 0x80,
+	PCF50633_MBCC7_BATSYSIMAX_2A2	= 0xc0,
+};
+#define PCF50633_MBCC7_USB_MASK 0x03
+
+enum pcf50633_reg_mbcc8 {
+	PCF50633_MBCC8_USBENASUS	= 0x10,
+};
+
+enum pcf50633_reg_mbcs1 {
+	PCF50633_MBCS1_USBPRES		= 0x01,
+	PCF50633_MBCS1_USBOK		= 0x02,
+	PCF50633_MBCS1_ADAPTPRES	= 0x04,
+	PCF50633_MBCS1_ADAPTOK		= 0x08,
+	PCF50633_MBCS1_TBAT_OK		= 0x00,
+	PCF50633_MBCS1_TBAT_ABOVE	= 0x10,
+	PCF50633_MBCS1_TBAT_BELOW	= 0x20,
+	PCF50633_MBCS1_TBAT_UNDEF	= 0x30,
+	PCF50633_MBCS1_PREWDTEXP	= 0x40,
+	PCF50633_MBCS1_WDTEXP		= 0x80,
+};
+
+enum pcf50633_reg_mbcs2_mbcmod {
+	PCF50633_MBCS2_MBC_PLAY		= 0x00,
+	PCF50633_MBCS2_MBC_USB_PRE	= 0x01,
+	PCF50633_MBCS2_MBC_USB_PRE_WAIT	= 0x02,
+	PCF50633_MBCS2_MBC_USB_FAST	= 0x03,
+	PCF50633_MBCS2_MBC_USB_FAST_WAIT = 0x04,
+	PCF50633_MBCS2_MBC_USB_SUSPEND	= 0x05,
+	PCF50633_MBCS2_MBC_ADP_PRE	= 0x06,
+	PCF50633_MBCS2_MBC_ADP_PRE_WAIT	= 0x07,
+	PCF50633_MBCS2_MBC_ADP_FAST	= 0x08,
+	PCF50633_MBCS2_MBC_ADP_FAST_WAIT = 0x09,
+	PCF50633_MBCS2_MBC_BAT_FULL	= 0x0a,
+	PCF50633_MBCS2_MBC_HALT		= 0x0b,
+};
+#define PCF50633_MBCS2_MBC_MASK		0x0f
+enum pcf50633_reg_mbcs2_chgstat {
+	PCF50633_MBCS2_CHGS_NONE	= 0x00,
+	PCF50633_MBCS2_CHGS_ADAPTER	= 0x10,
+	PCF50633_MBCS2_CHGS_USB		= 0x20,
+	PCF50633_MBCS2_CHGS_BOTH	= 0x30,
+};
+#define PCF50633_MBCS2_RESSTAT_AUTO	0x40
+
+enum pcf50633_reg_mbcs3 {
+	PCF50633_MBCS3_USBLIM_PLAY	= 0x01,
+	PCF50633_MBCS3_USBLIM_CGH	= 0x02,
+	PCF50633_MBCS3_TLIM_PLAY	= 0x04,
+	PCF50633_MBCS3_TLIM_CHG		= 0x08,
+	PCF50633_MBCS3_ILIM		= 0x10,	/* 1: Ibat > Icutoff */
+	PCF50633_MBCS3_VLIM		= 0x20,	/* 1: Vbat == Vmax */
+	PCF50633_MBCS3_VBATSTAT		= 0x40,	/* 1: Vbat > Vbatcond */
+	PCF50633_MBCS3_VRES		= 0x80, /* 1: Vbat > Vth(RES) */
+};
+
+#define PCF50633_MBCC2_VBATCOND_MASK	  0x03
+#define PCF50633_MBCC2_VMAX_MASK	  0x3c
+
+/* Charger status */
+#define PCF50633_MBC_USB_ONLINE		0x01
+#define PCF50633_MBC_USB_ACTIVE		0x02
+#define PCF50633_MBC_ADAPTER_ONLINE	0x04
+#define PCF50633_MBC_ADAPTER_ACTIVE	0x08
+
+int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma);
+
+int pcf50633_mbc_get_status(struct pcf50633 *);
+void pcf50633_mbc_set_status(struct pcf50633 *, int what, int status);
+
+#endif
+


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

* [PATCH V2 6/7] input: PCF50633 input driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
                   ` (4 preceding siblings ...)
  2008-12-18  5:57 ` [PATCH V2 5/7] power_supply: PCF50633 battery charger driver Balaji Rao
@ 2008-12-18  5:58 ` Balaji Rao
  2008-12-18  5:58 ` [PATCH V2 7/7] regulator: PCF50633 pmic driver Balaji Rao
  2008-12-22 10:50 ` [PATCH V2 0/7] PCF50633 support Samuel Ortiz
  7 siblings, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:58 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Dmitry Torokhov

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/misc/Kconfig          |    7 ++
 drivers/input/misc/Makefile         |    1 
 drivers/input/misc/pcf50633-input.c |  132 +++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/misc/pcf50633-input.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 199055d..67e5553 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -220,4 +220,11 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config INPUT_PCF50633_PMU
+	tristate "PCF50633 PMU events"
+	depends on MFD_PCF50633
+	help
+	 Say Y to include support for delivering  PMU events via  input
+	 layer on NXP PCF50633.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index d7db2ae..bb62e6e 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
 obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
+obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
new file mode 100644
index 0000000..70e36fd
--- /dev/null
+++ b/drivers/input/misc/pcf50633-input.c
@@ -0,0 +1,132 @@
+/* NXP PCF50633 Input Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_OOCSTAT_ONKEY	0x01
+#define PCF50633_REG_OOCSTAT	0x12
+#define PCF50633_REG_OOCMODE	0x10
+
+struct pcf50633_input {
+	struct pcf50633 *pcf;
+	struct input_dev *input_dev;
+};
+
+static void
+pcf50633_input_irq(int irq, void *data)
+{
+	struct pcf50633_input *input;
+	int onkey_released;
+
+	input = data;
+
+	/* We report only one event depending on the key press status */
+	onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
+						& PCF50633_OOCSTAT_ONKEY;
+
+	if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
+		input_report_key(input->input_dev, KEY_POWER, 1);
+	else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
+		input_report_key(input->input_dev, KEY_POWER, 0);
+
+	input_sync(input->input_dev);
+}
+
+static int __devinit pcf50633_input_probe(struct platform_device *pdev)
+{
+	struct pcf50633_input *input;
+	struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
+	struct input_dev *input_dev;
+	int ret;
+
+
+	input = kzalloc(sizeof(*input), GFP_KERNEL);
+	if (!input)
+		return -ENOMEM;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		kfree(input);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, input);
+	input->pcf = pdata->pcf;
+	input->input_dev = input_dev;
+
+	input_dev->name = "PCF50633 PMU events";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
+	set_bit(KEY_POWER, input_dev->keybit);
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		input_free_device(input_dev);
+		kfree(input);
+		return ret;
+	}
+	pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYR,
+				pcf50633_input_irq, input);
+	pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYF,
+				pcf50633_input_irq, input);
+
+	return 0;
+}
+
+static int __devexit pcf50633_input_remove(struct platform_device *pdev)
+{
+	struct pcf50633_input *input  = platform_get_drvdata(pdev);
+
+	input_unregister_device(input->input_dev);
+	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
+	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
+
+	kfree(input);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_input_driver = {
+	.driver = {
+		.name = "pcf50633-input",
+	},
+	.probe = pcf50633_input_probe,
+	.remove = __devexit_p(pcf50633_input_remove),
+};
+
+static int __init pcf50633_input_init(void)
+{
+	return platform_driver_register(&pcf50633_input_driver);
+}
+module_init(pcf50633_input_init);
+
+static void __exit pcf50633_input_exit(void)
+{
+	platform_driver_unregister(&pcf50633_input_driver);
+}
+module_exit(pcf50633_input_exit);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 input driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-input");


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

* [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
                   ` (5 preceding siblings ...)
  2008-12-18  5:58 ` [PATCH V2 6/7] input: PCF50633 input driver Balaji Rao
@ 2008-12-18  5:58 ` Balaji Rao
  2008-12-18 10:08   ` Mark Brown
  2008-12-22 10:50 ` [PATCH V2 0/7] PCF50633 support Samuel Ortiz
  7 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18  5:58 UTC (permalink / raw)
  To: linux-kernel; +Cc: Balaji Rao, Andy Green, Liam Girdwood, Mark Brown

Changes from V1:
	- Removed support for suspend_enable & suspend_disable functions.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Liam Girdwood <lrg@simlogic.co.uk>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 drivers/regulator/Kconfig              |    7 +
 drivers/regulator/Makefile             |    1 
 drivers/regulator/pcf50633-regulator.c |  330 ++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/pmic.h      |   68 +++++++
 4 files changed, 406 insertions(+), 0 deletions(-)
 create mode 100644 drivers/regulator/pcf50633-regulator.c
 create mode 100644 include/linux/mfd/pcf50633/pmic.h

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 39360e2..e7e0cf1 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -73,4 +73,11 @@ config REGULATOR_DA903X
 	  Say y here to support the BUCKs and LDOs regulators found on
 	  Dialog Semiconductor DA9030/DA9034 PMIC.
 
+config REGULATOR_PCF50633
+	tristate "PCF50633 regulator driver"
+        depends on MFD_PCF50633
+	help
+	 Say Y here to support the voltage regulators and convertors
+	 on PCF50633
+
 endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 254d40c..61b30c6 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -11,5 +11,6 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
 obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
 obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
+obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
new file mode 100644
index 0000000..7a5d732
--- /dev/null
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -0,0 +1,330 @@
+/* NXP PCF50633 PMIC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte and Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/pmic.h>
+
+#define PCF50633_REGULATOR(_name, _id) 		\
+	{					\
+		.name = _name, 			\
+		.id = _id,			\
+		.ops = &pcf50633_regulator_ops,	\
+		.type = REGULATOR_VOLTAGE, 	\
+		.owner = THIS_MODULE, 		\
+	}
+
+static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
+	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT,
+	[PCF50633_REGULATOR_DOWN1]	= PCF50633_REG_DOWN1OUT,
+	[PCF50633_REGULATOR_DOWN2]	= PCF50633_REG_DOWN2OUT,
+	[PCF50633_REGULATOR_MEMLDO]	= PCF50633_REG_MEMLDOOUT,
+	[PCF50633_REGULATOR_LDO1]	= PCF50633_REG_LDO1OUT,
+	[PCF50633_REGULATOR_LDO2]	= PCF50633_REG_LDO2OUT,
+	[PCF50633_REGULATOR_LDO3]	= PCF50633_REG_LDO3OUT,
+	[PCF50633_REGULATOR_LDO4]	= PCF50633_REG_LDO4OUT,
+	[PCF50633_REGULATOR_LDO5]	= PCF50633_REG_LDO5OUT,
+	[PCF50633_REGULATOR_LDO6]	= PCF50633_REG_LDO6OUT,
+	[PCF50633_REGULATOR_HCLDO]	= PCF50633_REG_HCLDOOUT,
+};
+
+/* Bits from voltage value */
+static u8 auto_voltage_bits(unsigned int millivolts)
+{
+	if (millivolts < 1800)
+		return 0;
+	if (millivolts > 3800)
+		return 0xff;
+
+	millivolts -= 625;
+
+	return millivolts / 25;
+}
+
+static u8 down_voltage_bits(unsigned int millivolts)
+{
+	if (millivolts < 625)
+		return 0;
+	else if (millivolts > 3000)
+		return 0xff;
+
+	millivolts -= 625;
+
+	return millivolts / 25;
+}
+
+static u8 ldo_voltage_bits(unsigned int millivolts)
+{
+	if (millivolts < 900)
+		return 0;
+	else if (millivolts > 3600)
+		return 0x1f;
+
+	millivolts -= 900;
+	return millivolts / 100;
+}
+
+/* Obtain voltage value from bits */
+static unsigned int auto_voltage_value(u8 bits)
+{
+	if (bits < 0x2f)
+		return 0;
+
+	return 625 + (bits * 25);
+}
+
+
+static unsigned int down_voltage_value(u8 bits)
+{
+	return 625 + (bits * 25);
+}
+
+
+static unsigned int ldo_voltage_value(u8 bits)
+{
+	bits &= 0x1f;
+
+	return 900 + (bits * 100);
+}
+
+static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev,
+						int min_uV, int max_uV)
+{
+	struct pcf50633 *pcf;
+	int regulator_id, millivolts;
+	u8 volt_bits, regnr;
+
+	pcf = rdev_get_drvdata(rdev);
+
+	regulator_id = rdev_get_id(rdev);
+	if (regulator_id >= PCF50633_NUM_REGULATORS)
+		return -EINVAL;
+
+	millivolts = min_uV / 1000;
+
+	regnr = pcf50633_regulator_registers[regulator_id];
+
+	switch (regulator_id) {
+	case PCF50633_REGULATOR_AUTO:
+		volt_bits = auto_voltage_bits(millivolts);
+		break;
+	case PCF50633_REGULATOR_DOWN1:
+		volt_bits = down_voltage_bits(millivolts);
+		break;
+	case PCF50633_REGULATOR_DOWN2:
+		volt_bits = down_voltage_bits(millivolts);
+		break;
+	case PCF50633_REGULATOR_LDO1:
+	case PCF50633_REGULATOR_LDO2:
+	case PCF50633_REGULATOR_LDO3:
+	case PCF50633_REGULATOR_LDO4:
+	case PCF50633_REGULATOR_LDO5:
+	case PCF50633_REGULATOR_LDO6:
+	case PCF50633_REGULATOR_HCLDO:
+		volt_bits = ldo_voltage_bits(millivolts);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return pcf50633_reg_write(pcf, regnr, volt_bits);
+}
+
+static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct pcf50633 *pcf;
+	int regulator_id, millivolts, volt_bits;
+	u8 regnr;
+
+	pcf = rdev_get_drvdata(rdev);;
+
+	regulator_id = rdev_get_id(rdev);
+	if (regulator_id >= PCF50633_NUM_REGULATORS)
+		return -EINVAL;
+
+	regnr = pcf50633_regulator_registers[regulator_id];
+
+	volt_bits = pcf50633_reg_read(pcf, regnr);
+	if (volt_bits < 0)
+		return -1;
+
+	switch (regulator_id) {
+	case PCF50633_REGULATOR_AUTO:
+		millivolts = auto_voltage_value(volt_bits);
+		break;
+	case PCF50633_REGULATOR_DOWN1:
+		millivolts = down_voltage_value(volt_bits);
+		break;
+	case PCF50633_REGULATOR_DOWN2:
+		millivolts = down_voltage_value(volt_bits);
+		break;
+	case PCF50633_REGULATOR_LDO1:
+	case PCF50633_REGULATOR_LDO2:
+	case PCF50633_REGULATOR_LDO3:
+	case PCF50633_REGULATOR_LDO4:
+	case PCF50633_REGULATOR_LDO5:
+	case PCF50633_REGULATOR_LDO6:
+	case PCF50633_REGULATOR_HCLDO:
+		millivolts = ldo_voltage_value(volt_bits);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return millivolts * 1000;
+}
+
+static int pcf50633_regulator_enable(struct regulator_dev *rdev)
+{
+	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
+	int regulator_id;
+	u8 regnr;
+
+	regulator_id = rdev_get_id(rdev);
+	if (regulator_id >= PCF50633_NUM_REGULATORS)
+		return -EINVAL;
+
+	/* The *ENA register is always one after the *OUT register */
+	regnr = pcf50633_regulator_registers[regulator_id] + 1;
+
+	return pcf50633_reg_set_bit_mask(pcf, regnr, PCF50633_REGULATOR_ON,
+						       PCF50633_REGULATOR_ON);
+
+}
+
+static int pcf50633_regulator_disable(struct regulator_dev *rdev)
+{
+	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
+	int regulator_id;
+	u8 regnr;
+
+	regulator_id = rdev_get_id(rdev);
+	if (regulator_id >= PCF50633_NUM_REGULATORS)
+		return -EINVAL;
+
+	/* the *ENA register is always one after the *OUT register */
+	regnr = pcf50633_regulator_registers[regulator_id] + 1;
+
+	return pcf50633_reg_set_bit_mask(pcf, regnr,
+					PCF50633_REGULATOR_ON, 0);
+}
+
+static int pcf50633_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
+	int regulator_id = rdev_get_id(rdev);
+	u8 regnr;
+
+	regulator_id = rdev_get_id(rdev);
+	if (regulator_id >= PCF50633_NUM_REGULATORS)
+		return -EINVAL;
+
+	/* the *ENA register is always one after the *OUT register */
+	regnr = pcf50633_regulator_registers[regulator_id] + 1;
+
+	return pcf50633_reg_read(pcf, regnr) & PCF50633_REGULATOR_ON;
+}
+
+static struct regulator_ops pcf50633_regulator_ops = {
+	.set_voltage = pcf50633_regulator_set_voltage,
+	.get_voltage = pcf50633_regulator_get_voltage,
+	.enable = pcf50633_regulator_enable,
+	.disable = pcf50633_regulator_disable,
+	.is_enabled = pcf50633_regulator_is_enabled,
+};
+
+static struct regulator_desc regulators[] = {
+	[PCF50633_REGULATOR_AUTO] =
+		PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO),
+	[PCF50633_REGULATOR_DOWN1] =
+		PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1),
+	[PCF50633_REGULATOR_DOWN2] =
+		PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2),
+	[PCF50633_REGULATOR_LDO1] =
+		PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1),
+	[PCF50633_REGULATOR_LDO2] =
+		PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2),
+	[PCF50633_REGULATOR_LDO3] =
+		PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3),
+	[PCF50633_REGULATOR_LDO4] =
+		PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4),
+	[PCF50633_REGULATOR_LDO5] =
+		PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5),
+	[PCF50633_REGULATOR_LDO6] =
+		PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6),
+	[PCF50633_REGULATOR_HCLDO] =
+		PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO),
+	[PCF50633_REGULATOR_MEMLDO] =
+		PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO),
+};
+
+static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev;
+	struct pcf50633 *pcf;
+
+	/* Already set by core driver */
+	pcf = platform_get_drvdata(pdev);
+
+	rdev = regulator_register(&regulators[pdev->id], &pdev->dev, pcf);
+	if (IS_ERR(rdev))
+		return PTR_ERR(rdev);
+
+	if (pcf->pdata->regulator_registered)
+		pcf->pdata->regulator_registered(pcf, pdev->id);
+
+	return 0;
+}
+
+static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+	regulator_unregister(rdev);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_regulator_driver = {
+	.driver = {
+		.name = "pcf50633-regltr",
+	},
+	.probe = pcf50633_regulator_probe,
+	.remove = __devexit_p(pcf50633_regulator_remove),
+};
+
+static int __init pcf50633_regulator_init(void)
+{
+	return platform_driver_register(&pcf50633_regulator_driver);
+}
+module_init(pcf50633_regulator_init);
+
+static void __exit pcf50633_regulator_exit(void)
+{
+	platform_driver_unregister(&pcf50633_regulator_driver);
+}
+module_exit(pcf50633_regulator_exit);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-regulator");
diff --git a/include/linux/mfd/pcf50633/pmic.h b/include/linux/mfd/pcf50633/pmic.h
new file mode 100644
index 0000000..8c567a1
--- /dev/null
+++ b/include/linux/mfd/pcf50633/pmic.h
@@ -0,0 +1,68 @@
+#ifndef __LINUX_MFD_PCF50633_PMIC_H
+#define __LINUX_MFD_PCF50633_PMIC_H
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/platform_device.h>
+
+#define PCF50633_REG_AUTOOUT	0x1a
+#define PCF50633_REG_AUTOENA	0x1b
+#define PCF50633_REG_AUTOCTL	0x1c
+#define PCF50633_REG_AUTOMXC	0x1d
+#define PCF50633_REG_DOWN1OUT	0x1e
+#define PCF50633_REG_DOWN1ENA	0x1f
+#define PCF50633_REG_DOWN1CTL	0x20
+#define PCF50633_REG_DOWN1MXC	0x21
+#define PCF50633_REG_DOWN2OUT	0x22
+#define PCF50633_REG_DOWN2ENA	0x23
+#define PCF50633_REG_DOWN2CTL	0x24
+#define PCF50633_REG_DOWN2MXC	0x25
+#define PCF50633_REG_MEMLDOOUT	0x26
+#define PCF50633_REG_MEMLDOENA	0x27
+#define PCF50633_REG_LDO1OUT	0x2d
+#define PCF50633_REG_LDO1ENA	0x2e
+#define PCF50633_REG_LDO2OUT	0x2f
+#define PCF50633_REG_LDO2ENA	0x30
+#define PCF50633_REG_LDO3OUT	0x31
+#define PCF50633_REG_LDO3ENA	0x32
+#define PCF50633_REG_LDO4OUT	0x33
+#define PCF50633_REG_LDO4ENA	0x34
+#define PCF50633_REG_LDO5OUT	0x35
+#define PCF50633_REG_LDO5ENA	0x36
+#define PCF50633_REG_LDO6OUT	0x37
+#define PCF50633_REG_LDO6ENA	0x38
+#define PCF50633_REG_HCLDOOUT	0x39
+#define PCF50633_REG_HCLDOENA	0x3a
+#define PCF50633_REG_HCLDOOVL	0x40
+
+enum pcf50633_regulator_enable {
+	PCF50633_REGULATOR_ON		= 0x01,
+	PCF50633_REGULATOR_ON_GPIO1	= 0x02,
+	PCF50633_REGULATOR_ON_GPIO2	= 0x04,
+	PCF50633_REGULATOR_ON_GPIO3	= 0x08,
+};
+#define PCF50633_REGULATOR_ON_MASK	0x0f
+
+enum pcf50633_regulator_phase {
+	PCF50633_REGULATOR_ACTPH1	= 0x00,
+	PCF50633_REGULATOR_ACTPH2	= 0x10,
+	PCF50633_REGULATOR_ACTPH3	= 0x20,
+	PCF50633_REGULATOR_ACTPH4	= 0x30,
+};
+#define PCF50633_REGULATOR_ACTPH_MASK	0x30
+
+enum pcf50633_regulator_id {
+	PCF50633_REGULATOR_AUTO,
+	PCF50633_REGULATOR_DOWN1,
+	PCF50633_REGULATOR_DOWN2,
+	PCF50633_REGULATOR_LDO1,
+	PCF50633_REGULATOR_LDO2,
+	PCF50633_REGULATOR_LDO3,
+	PCF50633_REGULATOR_LDO4,
+	PCF50633_REGULATOR_LDO5,
+	PCF50633_REGULATOR_LDO6,
+	PCF50633_REGULATOR_HCLDO,
+	PCF50633_REGULATOR_MEMLDO,
+};
+
+#endif
+


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

* Re: [PATCH V2 4/7] rtc: PCF50633 rtc driver
  2008-12-18  5:57 ` [PATCH V2 4/7] rtc: PCF50633 rtc driver Balaji Rao
@ 2008-12-18  9:03   ` Alessandro Zummo
  2008-12-18 15:11     ` Balaji Rao
  0 siblings, 1 reply; 26+ messages in thread
From: Alessandro Zummo @ 2008-12-18  9:03 UTC (permalink / raw)
  To: Balaji Rao
  Cc: linux-kernel, Balaji Rao, Andy Green, Alessandro Zummo, rtc-linux

On Thu, 18 Dec 2008 11:27:26 +0530
Balaji Rao <balajirrao@openmoko.org> wrote:

> Changes from V1:
> 	- Removed include/linux/mfd/pcf50633/rtc.h and moved defenitions into
> 	  the source file.
> 	- Remove PIE and introduce UIE support. UIE being the one actually
> 	  supported in the chip.
> 
> Alessandro, I'll change to the new API for AIE once it appears upstream.

 Ok, I guess the driver will not go in tomorrow, so you can wait.
 I'd add it now, while you're at it :)

 some comments below:


> Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> Cc: Andy Green <andy@openmoko.com>
> Cc: Alessandro Zummo <a.zummo@towertech.it>
> Cc: Paul Gortmaker <a.zummo@towertech.it>
> Cc: rtc-linux@googlegroups.com
> ---
>  drivers/rtc/Kconfig        |    6 +
>  drivers/rtc/Makefile       |    1 
>  drivers/rtc/rtc-pcf50633.c |  336 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 343 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/rtc/rtc-pcf50633.c
> 
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 123092d..68e68d2 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -497,6 +497,12 @@ config RTC_DRV_WM8350
>  	  This driver can also be built as a module. If so, the module
>  	  will be called "rtc-wm8350".
>  
> +config RTC_DRV_PCF50633
> +	depends on MFD_PCF50633
> +	tristate "NXP PCF50633 RTC"
> +	help
> +	  If you say yes here you get support for the NXP PCF50633 RTC.

 a more detailed description please stating which platforms commonly
 have this rtc

>  comment "on-CPU RTC drivers"
>  
>  config RTC_DRV_OMAP
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 6e79c91..a717fec 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -70,3 +70,4 @@ obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o
>  obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o
>  obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
> +obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
> diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
> new file mode 100644
> index 0000000..2f33509
> --- /dev/null
> +++ b/drivers/rtc/rtc-pcf50633.c
> @@ -0,0 +1,336 @@
> +/* NXP PCF50633 RTC Driver
> + *
> + * (C) 2006-2008 by Openmoko, Inc.
> + * Author: Balaji Rao <balajirrao@openmoko.org>
> + * All rights reserved.
> + *
> + * Broken down from monstrous PCF50633 driver mainly by
> + * Harald Welte, Andy Green and Werner Almesberger
> + *
> + *  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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/bcd.h>
> +#include <linux/err.h>
> +
> +#include <linux/mfd/pcf50633/core.h>
> +
> +#define PCF50633_REG_RTCSC	0x59 /* Second */
> +#define PCF50633_REG_RTCMN	0x5a /* Minute */
> +#define PCF50633_REG_RTCHR	0x5b /* Hour */
> +#define PCF50633_REG_RTCWD	0x5c /* Weekday */
> +#define PCF50633_REG_RTCDT	0x5d /* Day */
> +#define PCF50633_REG_RTCMT	0x5e /* Month */
> +#define PCF50633_REG_RTCYR	0x5f /* Year */
> +#define PCF50633_REG_RTCSCA	0x60 /* Alarm Second */
> +#define PCF50633_REG_RTCMNA	0x61 /* Alarm Minute */
> +#define PCF50633_REG_RTCHRA	0x62 /* Alarm Hour */
> +#define PCF50633_REG_RTCWDA	0x63 /* Alarm Weekday */
> +#define PCF50633_REG_RTCDTA	0x64 /* Alarm Day */
> +#define PCF50633_REG_RTCMTA	0x65 /* Alarm Month */
> +#define PCF50633_REG_RTCYRA	0x66 /* Alarm Year */
> +
> +enum pcf50633_time_indexes {
> +	PCF50633_TI_SEC,
> +	PCF50633_TI_MIN,
> +	PCF50633_TI_HOUR,
> +	PCF50633_TI_WKDAY,
> +	PCF50633_TI_DAY,
> +	PCF50633_TI_MONTH,
> +	PCF50633_TI_YEAR,
> +	PCF50633_TI_EXTENT /* always last */
> +};
> +
> +struct pcf50633_time {
> +	u_int8_t time[PCF50633_TI_EXTENT];
> +};
> +
> +struct pcf50633_rtc {
> +	int alarm_enabled;
> +	int second_enabled;
> +
> +	struct pcf50633 *pcf;
> +	struct rtc_device *rtc_dev;
> +};
> +
> +static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
> +{
> +	rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);
> +	rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);
> +	rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
> +	rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
> +	rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
> +	rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]);
> +	rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
> +}
> +
> +static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
> +{
> +	pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);
> +	pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);
> +	pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
> +	pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
> +	pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
> +	pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon);
> +	pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
> +}
> +
> +static int
> +pcf50633_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
> +{
> +	struct pcf50633_rtc *rtc = dev_get_drvdata(dev);
> +
> +	switch (cmd) {
> +	case RTC_AIE_OFF:
> +		rtc->alarm_enabled = 0;
> +		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
> +		return 0;
> +	case RTC_AIE_ON:
> +		rtc->alarm_enabled = 1;
> +		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
> +		return 0;
> +	case RTC_UIE_OFF:
> +		rtc->second_enabled = 0;
> +		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND);
> +		return 0;
> +	case RTC_UIE_ON:
> +		rtc->second_enabled = 1;
> +		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND);
> +		return 0;
> +	}
> +
> +	return -ENOIOCTLCMD;
> +}
> +
> +static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct pcf50633_rtc *rtc;
> +	struct pcf50633_time pcf_tm;
> +	int ret;
> +
> +	rtc = dev_get_drvdata(dev);
> +
> +	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,
> +					    PCF50633_TI_EXTENT,
> +					    &pcf_tm.time[0]);
> +	if (ret != PCF50633_TI_EXTENT) {
> +		dev_err(dev, "Failed to read time\n");
> +		return -EIO;
> +	}
> +
> +	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
> +		pcf_tm.time[PCF50633_TI_DAY],
> +		pcf_tm.time[PCF50633_TI_MONTH],
> +		pcf_tm.time[PCF50633_TI_YEAR],
> +		pcf_tm.time[PCF50633_TI_HOUR],
> +		pcf_tm.time[PCF50633_TI_MIN],
> +		pcf_tm.time[PCF50633_TI_SEC]);
> +
> +	pcf2rtc_time(tm, &pcf_tm);
> +
> +	dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
> +		tm->tm_mday, tm->tm_mon, tm->tm_year,
> +		tm->tm_hour, tm->tm_min, tm->tm_sec);
> +
> +	return rtc_valid_tm(tm);
> +}
> +
> +static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct pcf50633_rtc *rtc;
> +	struct pcf50633_time pcf_tm;
> +	int second_masked, alarm_masked, ret = 0;
> +
> +	rtc = dev_get_drvdata(dev);
> +
> +	dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
> +		tm->tm_mday, tm->tm_mon, tm->tm_year,
> +		tm->tm_hour, tm->tm_min, tm->tm_sec);
> +
> +	rtc2pcf_time(&pcf_tm, tm);
> +
> +	dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
> +		pcf_tm.time[PCF50633_TI_DAY],
> +		pcf_tm.time[PCF50633_TI_MONTH],
> +		pcf_tm.time[PCF50633_TI_YEAR],
> +		pcf_tm.time[PCF50633_TI_HOUR],
> +		pcf_tm.time[PCF50633_TI_MIN],
> +		pcf_tm.time[PCF50633_TI_SEC]);
> +
> +
> +	second_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_SECOND);
> +	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	if (!second_masked)
> +		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND);
> +	if (!alarm_masked)
> +		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	/* Returns 0 on success */
> +	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,
> +					     PCF50633_TI_EXTENT,
> +					     &pcf_tm.time[0]);
> +
> +	if (!second_masked)
> +		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND);
> +	if (!alarm_masked)
> +		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	return ret;
> +}
> +
> +static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct pcf50633_rtc *rtc;
> +	struct pcf50633_time pcf_tm;
> +	int ret = 0;
> +
> +	rtc = dev_get_drvdata(dev);
> +
> +	alrm->enabled = rtc->alarm_enabled;
> +
> +	ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
> +				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
> +	if (ret != PCF50633_TI_EXTENT) {
> +		dev_err(dev, "Failed to read time\n");
> +		return -EIO;
> +	}
> +
> +	pcf2rtc_time(&alrm->time, &pcf_tm);
> +
> +	return rtc_valid_tm(&alrm->time);
> +}
> +
> +static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> +	struct pcf50633_rtc *rtc;
> +	struct pcf50633_time pcf_tm;
> +	int alarm_masked, ret = 0;
> +
> +	rtc = dev_get_drvdata(dev);
> +
> +	rtc2pcf_time(&pcf_tm, &alrm->time);
> +
> +	/* do like mktime does and ignore tm_wday */
> +	pcf_tm.time[PCF50633_TI_WKDAY] = 7;
> +
> +	alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	/* disable alarm interrupt */
> +	if (!alarm_masked)
> +		pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	/* Returns 0 on success */
> +	ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
> +				PCF50633_TI_EXTENT, &pcf_tm.time[0]);
> +
> +	if (!alarm_masked)
> +		pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
> +
> +	return ret;
> +}
> +
> +static struct rtc_class_ops pcf50633_rtc_ops = {
> +	.ioctl		= pcf50633_rtc_ioctl,
> +	.read_time	= pcf50633_rtc_read_time,
> +	.set_time	= pcf50633_rtc_set_time,
> +	.read_alarm	= pcf50633_rtc_read_alarm,
> +	.set_alarm	= pcf50633_rtc_set_alarm,
> +};
> +
> +static void pcf50633_rtc_irq(int irq, void *data)
> +{
> +	struct pcf50633_rtc *rtc = data;
> +
> +	switch (irq) {
> +	case PCF50633_IRQ_ALARM:
> +		rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
> +		break;
> +	case PCF50633_IRQ_SECOND:
> +		rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
> +		break;
> +	}
> +}
> +
> +static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
> +{
> +	struct pcf50633_subdev_pdata *pdata;
> +	struct pcf50633_rtc *rtc;
> +	struct rtc_device *rtc_dev;
> +
> +	rtc_dev = rtc_device_register("pcf50633-rtc", &pdev->dev,
> +				&pcf50633_rtc_ops, THIS_MODULE);
> +
> +	if (IS_ERR(rtc_dev))
> +		return PTR_ERR(rtc_dev);
> +
> +	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
> +	if (!rtc) {
> +		dev_err(&pdev->dev, "allocation of pcf50633_rtc failed\n");
> +		rtc_device_unregister(rtc_dev);
> +		return -ENOMEM;
> +	}

 once registered, the rtc could be immediately in use. you should first allocate
 and setup your data structures and only then register the device:

	rtc = kzalloc(....
	if (rtc == NULL)
		return -ENOMEM;

	..setup rtc structs here...

	rtc->dev = rtc_register(...

	..irqs...

> +	pdata = pdev->dev.platform_data;
> +	rtc->pcf = pdata->pcf;
> +	rtc->rtc_dev = rtc_dev;
> +	platform_set_drvdata(pdev, rtc);
> +
> +	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
> +					pcf50633_rtc_irq, rtc);
> +	pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_SECOND,
> +					pcf50633_rtc_irq, rtc);
> +
> +	return 0;
> +}
> +
> +static int __devexit pcf50633_rtc_remove(struct platform_device *pdev)
> +{
> +	struct pcf50633_rtc *rtc;
> +
> +	rtc = platform_get_drvdata(pdev);
> +	rtc_device_unregister(rtc->rtc_dev);
> +
> +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
> +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND);

 please remove rtc before unregistering

> +	kfree(rtc);
> +
> +	return 0;
> +}
> +
> +
> +static struct platform_driver pcf50633_rtc_driver = {
> +	.driver = {
> +		.name = "pcf50633-rtc",
> +	},
> +	.probe = pcf50633_rtc_probe,
> +	.remove = __devexit_p(pcf50633_rtc_remove),
> +};
> +
> +static int __init pcf50633_rtc_init(void)
> +{
> +	return platform_driver_register(&pcf50633_rtc_driver);
> +}
> +module_init(pcf50633_rtc_init);
> +
> +static void __exit pcf50633_rtc_exit(void)
> +{
> +	platform_driver_unregister(&pcf50633_rtc_driver);
> +}
> +module_exit(pcf50633_rtc_exit);
> +
> +MODULE_DESCRIPTION("PCF50633 RTC driver");
> +MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
> +MODULE_LICENSE("GPL");
> +
> 


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it


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

* Re: [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18  5:58 ` [PATCH V2 7/7] regulator: PCF50633 pmic driver Balaji Rao
@ 2008-12-18 10:08   ` Mark Brown
  2008-12-18 15:14     ` Balaji Rao
  0 siblings, 1 reply; 26+ messages in thread
From: Mark Brown @ 2008-12-18 10:08 UTC (permalink / raw)
  To: Balaji Rao; +Cc: linux-kernel, Andy Green, Liam Girdwood

On Thu, Dec 18, 2008 at 11:28:17AM +0530, Balaji Rao wrote:
> Changes from V1:
> 	- Removed support for suspend_enable & suspend_disable functions.
> 
> Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> Cc: Andy Green <andy@openmoko.com>
> Cc: Liam Girdwood <lrg@simlogic.co.uk>

That should be slimlogic with two ls - fixed in the CCs and not cutting
any text for his benefit.

> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>

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

> ---
>  drivers/regulator/Kconfig              |    7 +
>  drivers/regulator/Makefile             |    1 
>  drivers/regulator/pcf50633-regulator.c |  330 ++++++++++++++++++++++++++++++++
>  include/linux/mfd/pcf50633/pmic.h      |   68 +++++++
>  4 files changed, 406 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/regulator/pcf50633-regulator.c
>  create mode 100644 include/linux/mfd/pcf50633/pmic.h
> 
> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
> index 39360e2..e7e0cf1 100644
> --- a/drivers/regulator/Kconfig
> +++ b/drivers/regulator/Kconfig
> @@ -73,4 +73,11 @@ config REGULATOR_DA903X
>  	  Say y here to support the BUCKs and LDOs regulators found on
>  	  Dialog Semiconductor DA9030/DA9034 PMIC.
>  
> +config REGULATOR_PCF50633
> +	tristate "PCF50633 regulator driver"
> +        depends on MFD_PCF50633
> +	help
> +	 Say Y here to support the voltage regulators and convertors
> +	 on PCF50633
> +
>  endif
> diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
> index 254d40c..61b30c6 100644
> --- a/drivers/regulator/Makefile
> +++ b/drivers/regulator/Makefile
> @@ -11,5 +11,6 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
>  obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
>  obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
>  obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
> +obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
>  
>  ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
> diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
> new file mode 100644
> index 0000000..7a5d732
> --- /dev/null
> +++ b/drivers/regulator/pcf50633-regulator.c
> @@ -0,0 +1,330 @@
> +/* NXP PCF50633 PMIC Driver
> + *
> + * (C) 2006-2008 by Openmoko, Inc.
> + * Author: Balaji Rao <balajirrao@openmoko.org>
> + * All rights reserved.
> + *
> + * Broken down from monstrous PCF50633 driver mainly by
> + * Harald Welte and Andy Green and Werner Almesberger
> + *
> + *  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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/mfd/pcf50633/core.h>
> +#include <linux/mfd/pcf50633/pmic.h>
> +
> +#define PCF50633_REGULATOR(_name, _id) 		\
> +	{					\
> +		.name = _name, 			\
> +		.id = _id,			\
> +		.ops = &pcf50633_regulator_ops,	\
> +		.type = REGULATOR_VOLTAGE, 	\
> +		.owner = THIS_MODULE, 		\
> +	}
> +
> +static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
> +	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT,
> +	[PCF50633_REGULATOR_DOWN1]	= PCF50633_REG_DOWN1OUT,
> +	[PCF50633_REGULATOR_DOWN2]	= PCF50633_REG_DOWN2OUT,
> +	[PCF50633_REGULATOR_MEMLDO]	= PCF50633_REG_MEMLDOOUT,
> +	[PCF50633_REGULATOR_LDO1]	= PCF50633_REG_LDO1OUT,
> +	[PCF50633_REGULATOR_LDO2]	= PCF50633_REG_LDO2OUT,
> +	[PCF50633_REGULATOR_LDO3]	= PCF50633_REG_LDO3OUT,
> +	[PCF50633_REGULATOR_LDO4]	= PCF50633_REG_LDO4OUT,
> +	[PCF50633_REGULATOR_LDO5]	= PCF50633_REG_LDO5OUT,
> +	[PCF50633_REGULATOR_LDO6]	= PCF50633_REG_LDO6OUT,
> +	[PCF50633_REGULATOR_HCLDO]	= PCF50633_REG_HCLDOOUT,
> +};
> +
> +/* Bits from voltage value */
> +static u8 auto_voltage_bits(unsigned int millivolts)
> +{
> +	if (millivolts < 1800)
> +		return 0;
> +	if (millivolts > 3800)
> +		return 0xff;
> +
> +	millivolts -= 625;
> +
> +	return millivolts / 25;
> +}
> +
> +static u8 down_voltage_bits(unsigned int millivolts)
> +{
> +	if (millivolts < 625)
> +		return 0;
> +	else if (millivolts > 3000)
> +		return 0xff;
> +
> +	millivolts -= 625;
> +
> +	return millivolts / 25;
> +}
> +
> +static u8 ldo_voltage_bits(unsigned int millivolts)
> +{
> +	if (millivolts < 900)
> +		return 0;
> +	else if (millivolts > 3600)
> +		return 0x1f;
> +
> +	millivolts -= 900;
> +	return millivolts / 100;
> +}
> +
> +/* Obtain voltage value from bits */
> +static unsigned int auto_voltage_value(u8 bits)
> +{
> +	if (bits < 0x2f)
> +		return 0;
> +
> +	return 625 + (bits * 25);
> +}
> +
> +
> +static unsigned int down_voltage_value(u8 bits)
> +{
> +	return 625 + (bits * 25);
> +}
> +
> +
> +static unsigned int ldo_voltage_value(u8 bits)
> +{
> +	bits &= 0x1f;
> +
> +	return 900 + (bits * 100);
> +}
> +
> +static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev,
> +						int min_uV, int max_uV)
> +{
> +	struct pcf50633 *pcf;
> +	int regulator_id, millivolts;
> +	u8 volt_bits, regnr;
> +
> +	pcf = rdev_get_drvdata(rdev);
> +
> +	regulator_id = rdev_get_id(rdev);
> +	if (regulator_id >= PCF50633_NUM_REGULATORS)
> +		return -EINVAL;
> +
> +	millivolts = min_uV / 1000;
> +
> +	regnr = pcf50633_regulator_registers[regulator_id];
> +
> +	switch (regulator_id) {
> +	case PCF50633_REGULATOR_AUTO:
> +		volt_bits = auto_voltage_bits(millivolts);
> +		break;
> +	case PCF50633_REGULATOR_DOWN1:
> +		volt_bits = down_voltage_bits(millivolts);
> +		break;
> +	case PCF50633_REGULATOR_DOWN2:
> +		volt_bits = down_voltage_bits(millivolts);
> +		break;
> +	case PCF50633_REGULATOR_LDO1:
> +	case PCF50633_REGULATOR_LDO2:
> +	case PCF50633_REGULATOR_LDO3:
> +	case PCF50633_REGULATOR_LDO4:
> +	case PCF50633_REGULATOR_LDO5:
> +	case PCF50633_REGULATOR_LDO6:
> +	case PCF50633_REGULATOR_HCLDO:
> +		volt_bits = ldo_voltage_bits(millivolts);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return pcf50633_reg_write(pcf, regnr, volt_bits);
> +}
> +
> +static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev)
> +{
> +	struct pcf50633 *pcf;
> +	int regulator_id, millivolts, volt_bits;
> +	u8 regnr;
> +
> +	pcf = rdev_get_drvdata(rdev);;
> +
> +	regulator_id = rdev_get_id(rdev);
> +	if (regulator_id >= PCF50633_NUM_REGULATORS)
> +		return -EINVAL;
> +
> +	regnr = pcf50633_regulator_registers[regulator_id];
> +
> +	volt_bits = pcf50633_reg_read(pcf, regnr);
> +	if (volt_bits < 0)
> +		return -1;
> +
> +	switch (regulator_id) {
> +	case PCF50633_REGULATOR_AUTO:
> +		millivolts = auto_voltage_value(volt_bits);
> +		break;
> +	case PCF50633_REGULATOR_DOWN1:
> +		millivolts = down_voltage_value(volt_bits);
> +		break;
> +	case PCF50633_REGULATOR_DOWN2:
> +		millivolts = down_voltage_value(volt_bits);
> +		break;
> +	case PCF50633_REGULATOR_LDO1:
> +	case PCF50633_REGULATOR_LDO2:
> +	case PCF50633_REGULATOR_LDO3:
> +	case PCF50633_REGULATOR_LDO4:
> +	case PCF50633_REGULATOR_LDO5:
> +	case PCF50633_REGULATOR_LDO6:
> +	case PCF50633_REGULATOR_HCLDO:
> +		millivolts = ldo_voltage_value(volt_bits);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return millivolts * 1000;
> +}
> +
> +static int pcf50633_regulator_enable(struct regulator_dev *rdev)
> +{
> +	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
> +	int regulator_id;
> +	u8 regnr;
> +
> +	regulator_id = rdev_get_id(rdev);
> +	if (regulator_id >= PCF50633_NUM_REGULATORS)
> +		return -EINVAL;
> +
> +	/* The *ENA register is always one after the *OUT register */
> +	regnr = pcf50633_regulator_registers[regulator_id] + 1;
> +
> +	return pcf50633_reg_set_bit_mask(pcf, regnr, PCF50633_REGULATOR_ON,
> +						       PCF50633_REGULATOR_ON);
> +
> +}
> +
> +static int pcf50633_regulator_disable(struct regulator_dev *rdev)
> +{
> +	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
> +	int regulator_id;
> +	u8 regnr;
> +
> +	regulator_id = rdev_get_id(rdev);
> +	if (regulator_id >= PCF50633_NUM_REGULATORS)
> +		return -EINVAL;
> +
> +	/* the *ENA register is always one after the *OUT register */
> +	regnr = pcf50633_regulator_registers[regulator_id] + 1;
> +
> +	return pcf50633_reg_set_bit_mask(pcf, regnr,
> +					PCF50633_REGULATOR_ON, 0);
> +}
> +
> +static int pcf50633_regulator_is_enabled(struct regulator_dev *rdev)
> +{
> +	struct pcf50633 *pcf = rdev_get_drvdata(rdev);
> +	int regulator_id = rdev_get_id(rdev);
> +	u8 regnr;
> +
> +	regulator_id = rdev_get_id(rdev);
> +	if (regulator_id >= PCF50633_NUM_REGULATORS)
> +		return -EINVAL;
> +
> +	/* the *ENA register is always one after the *OUT register */
> +	regnr = pcf50633_regulator_registers[regulator_id] + 1;
> +
> +	return pcf50633_reg_read(pcf, regnr) & PCF50633_REGULATOR_ON;
> +}
> +
> +static struct regulator_ops pcf50633_regulator_ops = {
> +	.set_voltage = pcf50633_regulator_set_voltage,
> +	.get_voltage = pcf50633_regulator_get_voltage,
> +	.enable = pcf50633_regulator_enable,
> +	.disable = pcf50633_regulator_disable,
> +	.is_enabled = pcf50633_regulator_is_enabled,
> +};
> +
> +static struct regulator_desc regulators[] = {
> +	[PCF50633_REGULATOR_AUTO] =
> +		PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO),
> +	[PCF50633_REGULATOR_DOWN1] =
> +		PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1),
> +	[PCF50633_REGULATOR_DOWN2] =
> +		PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2),
> +	[PCF50633_REGULATOR_LDO1] =
> +		PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1),
> +	[PCF50633_REGULATOR_LDO2] =
> +		PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2),
> +	[PCF50633_REGULATOR_LDO3] =
> +		PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3),
> +	[PCF50633_REGULATOR_LDO4] =
> +		PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4),
> +	[PCF50633_REGULATOR_LDO5] =
> +		PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5),
> +	[PCF50633_REGULATOR_LDO6] =
> +		PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6),
> +	[PCF50633_REGULATOR_HCLDO] =
> +		PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO),
> +	[PCF50633_REGULATOR_MEMLDO] =
> +		PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO),
> +};
> +
> +static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
> +{
> +	struct regulator_dev *rdev;
> +	struct pcf50633 *pcf;
> +
> +	/* Already set by core driver */
> +	pcf = platform_get_drvdata(pdev);
> +
> +	rdev = regulator_register(&regulators[pdev->id], &pdev->dev, pcf);
> +	if (IS_ERR(rdev))
> +		return PTR_ERR(rdev);
> +
> +	if (pcf->pdata->regulator_registered)
> +		pcf->pdata->regulator_registered(pcf, pdev->id);
> +
> +	return 0;
> +}
> +
> +static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
> +{
> +	struct regulator_dev *rdev = platform_get_drvdata(pdev);
> +
> +	regulator_unregister(rdev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver pcf50633_regulator_driver = {
> +	.driver = {
> +		.name = "pcf50633-regltr",
> +	},
> +	.probe = pcf50633_regulator_probe,
> +	.remove = __devexit_p(pcf50633_regulator_remove),
> +};
> +
> +static int __init pcf50633_regulator_init(void)
> +{
> +	return platform_driver_register(&pcf50633_regulator_driver);
> +}
> +module_init(pcf50633_regulator_init);
> +
> +static void __exit pcf50633_regulator_exit(void)
> +{
> +	platform_driver_unregister(&pcf50633_regulator_driver);
> +}
> +module_exit(pcf50633_regulator_exit);
> +
> +MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
> +MODULE_DESCRIPTION("PCF50633 regulator driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:pcf50633-regulator");
> diff --git a/include/linux/mfd/pcf50633/pmic.h b/include/linux/mfd/pcf50633/pmic.h
> new file mode 100644
> index 0000000..8c567a1
> --- /dev/null
> +++ b/include/linux/mfd/pcf50633/pmic.h
> @@ -0,0 +1,68 @@
> +#ifndef __LINUX_MFD_PCF50633_PMIC_H
> +#define __LINUX_MFD_PCF50633_PMIC_H
> +
> +#include <linux/mfd/pcf50633/core.h>
> +#include <linux/platform_device.h>
> +
> +#define PCF50633_REG_AUTOOUT	0x1a
> +#define PCF50633_REG_AUTOENA	0x1b
> +#define PCF50633_REG_AUTOCTL	0x1c
> +#define PCF50633_REG_AUTOMXC	0x1d
> +#define PCF50633_REG_DOWN1OUT	0x1e
> +#define PCF50633_REG_DOWN1ENA	0x1f
> +#define PCF50633_REG_DOWN1CTL	0x20
> +#define PCF50633_REG_DOWN1MXC	0x21
> +#define PCF50633_REG_DOWN2OUT	0x22
> +#define PCF50633_REG_DOWN2ENA	0x23
> +#define PCF50633_REG_DOWN2CTL	0x24
> +#define PCF50633_REG_DOWN2MXC	0x25
> +#define PCF50633_REG_MEMLDOOUT	0x26
> +#define PCF50633_REG_MEMLDOENA	0x27
> +#define PCF50633_REG_LDO1OUT	0x2d
> +#define PCF50633_REG_LDO1ENA	0x2e
> +#define PCF50633_REG_LDO2OUT	0x2f
> +#define PCF50633_REG_LDO2ENA	0x30
> +#define PCF50633_REG_LDO3OUT	0x31
> +#define PCF50633_REG_LDO3ENA	0x32
> +#define PCF50633_REG_LDO4OUT	0x33
> +#define PCF50633_REG_LDO4ENA	0x34
> +#define PCF50633_REG_LDO5OUT	0x35
> +#define PCF50633_REG_LDO5ENA	0x36
> +#define PCF50633_REG_LDO6OUT	0x37
> +#define PCF50633_REG_LDO6ENA	0x38
> +#define PCF50633_REG_HCLDOOUT	0x39
> +#define PCF50633_REG_HCLDOENA	0x3a
> +#define PCF50633_REG_HCLDOOVL	0x40
> +
> +enum pcf50633_regulator_enable {
> +	PCF50633_REGULATOR_ON		= 0x01,
> +	PCF50633_REGULATOR_ON_GPIO1	= 0x02,
> +	PCF50633_REGULATOR_ON_GPIO2	= 0x04,
> +	PCF50633_REGULATOR_ON_GPIO3	= 0x08,
> +};
> +#define PCF50633_REGULATOR_ON_MASK	0x0f
> +
> +enum pcf50633_regulator_phase {
> +	PCF50633_REGULATOR_ACTPH1	= 0x00,
> +	PCF50633_REGULATOR_ACTPH2	= 0x10,
> +	PCF50633_REGULATOR_ACTPH3	= 0x20,
> +	PCF50633_REGULATOR_ACTPH4	= 0x30,
> +};
> +#define PCF50633_REGULATOR_ACTPH_MASK	0x30
> +
> +enum pcf50633_regulator_id {
> +	PCF50633_REGULATOR_AUTO,
> +	PCF50633_REGULATOR_DOWN1,
> +	PCF50633_REGULATOR_DOWN2,
> +	PCF50633_REGULATOR_LDO1,
> +	PCF50633_REGULATOR_LDO2,
> +	PCF50633_REGULATOR_LDO3,
> +	PCF50633_REGULATOR_LDO4,
> +	PCF50633_REGULATOR_LDO5,
> +	PCF50633_REGULATOR_LDO6,
> +	PCF50633_REGULATOR_HCLDO,
> +	PCF50633_REGULATOR_MEMLDO,
> +};
> +
> +#endif
> +
> 

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

* Re: [PATCH V2 4/7] rtc: PCF50633 rtc driver
  2008-12-18  9:03   ` Alessandro Zummo
@ 2008-12-18 15:11     ` Balaji Rao
  2008-12-18 16:52       ` Alessandro Zummo
  0 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18 15:11 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel, Andy Green, Alessandro Zummo, rtc-linux

On Thu, Dec 18, 2008 at 10:03:10AM +0100, Alessandro Zummo wrote:
> On Thu, 18 Dec 2008 11:27:26 +0530
> Balaji Rao <balajirrao@openmoko.org> wrote:
> 
> > Changes from V1:
> > 	- Removed include/linux/mfd/pcf50633/rtc.h and moved defenitions into
> > 	  the source file.
> > 	- Remove PIE and introduce UIE support. UIE being the one actually
> > 	  supported in the chip.
> > 
> > Alessandro, I'll change to the new API for AIE once it appears upstream.
> 
>  Ok, I guess the driver will not go in tomorrow, so you can wait.
>  I'd add it now, while you're at it :)
> 

OK! :) So that I can change when it's in -next ?

> > +config RTC_DRV_PCF50633
> > +	depends on MFD_PCF50633
> > +	tristate "NXP PCF50633 RTC"
> > +	help
> > +	  If you say yes here you get support for the NXP PCF50633 RTC.
> 
>  a more detailed description please stating which platforms commonly
>  have this rtc
> 

OK.

> > +static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
> > +{
> > +	struct pcf50633_subdev_pdata *pdata;
> > +	struct pcf50633_rtc *rtc;
> > +	struct rtc_device *rtc_dev;
> > +
> > +	rtc_dev = rtc_device_register("pcf50633-rtc", &pdev->dev,
> > +				&pcf50633_rtc_ops, THIS_MODULE);
> > +
> > +	if (IS_ERR(rtc_dev))
> > +		return PTR_ERR(rtc_dev);
> > +
> > +	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
> > +	if (!rtc) {
> > +		dev_err(&pdev->dev, "allocation of pcf50633_rtc failed\n");
> > +		rtc_device_unregister(rtc_dev);
> > +		return -ENOMEM;
> > +	}
> 
>  once registered, the rtc could be immediately in use. you should first allocate
>  and setup your data structures and only then register the device:
> 

Oh, yea right. Will fix.

> > +static int __devexit pcf50633_rtc_remove(struct platform_device *pdev)
> > +{
> > +	struct pcf50633_rtc *rtc;
> > +
> > +	rtc = platform_get_drvdata(pdev);
> > +	rtc_device_unregister(rtc->rtc_dev);
> > +
> > +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
> > +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND);
> 
>  please remove rtc before unregistering
> 

Sorry, didn't get you meant.

Thank you for the review.

	-Balaji

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

* Re: [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18 10:08   ` Mark Brown
@ 2008-12-18 15:14     ` Balaji Rao
  2008-12-18 20:30       ` Liam Girdwood
  0 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18 15:14 UTC (permalink / raw)
  To: Mark Brown; +Cc: linux-kernel, Andy Green, Liam Girdwood

On Thu, Dec 18, 2008 at 10:08:35AM +0000, Mark Brown wrote:
> On Thu, Dec 18, 2008 at 11:28:17AM +0530, Balaji Rao wrote:
> > Changes from V1:
> > 	- Removed support for suspend_enable & suspend_disable functions.
> > 
> > Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> > Cc: Andy Green <andy@openmoko.com>
> > Cc: Liam Girdwood <lrg@simlogic.co.uk>
> 
> That should be slimlogic with two ls - fixed in the CCs and not cutting
> any text for his benefit.
> 

Ah! Sorry about that, I've read it wrongly right from the first time :(
Will fix.

Thank you.

	- Balaji
	

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

* Re: [PATCH V2 4/7] rtc: PCF50633 rtc driver
  2008-12-18 15:11     ` Balaji Rao
@ 2008-12-18 16:52       ` Alessandro Zummo
  0 siblings, 0 replies; 26+ messages in thread
From: Alessandro Zummo @ 2008-12-18 16:52 UTC (permalink / raw)
  To: Balaji Rao; +Cc: linux-kernel, Andy Green, Alessandro Zummo, rtc-linux

On Thu, 18 Dec 2008 20:41:43 +0530
Balaji Rao <balajirrao@openmoko.org> wrote:

>  > +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
> > > +	pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND);  
> > 
> >  please remove rtc before unregistering
> >   
> 
> Sorry, didn't get you meant.
> 
> Thank you for the review.

 my fault. s/rtc/irq/


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Torino, Italy

  http://www.towertech.it


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

* Re: [PATCH V2 5/7] power_supply: PCF50633 battery charger driver
  2008-12-18  5:57 ` [PATCH V2 5/7] power_supply: PCF50633 battery charger driver Balaji Rao
@ 2008-12-18 20:26   ` Balaji Rao
  2008-12-25 15:45   ` Anton Vorontsov
  1 sibling, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-18 20:26 UTC (permalink / raw)
  To: linux-kernel; +Cc: Andy Green, Anton Vorontsov, David Woodhouse

On Thu, Dec 18, 2008 at 11:27:45AM +0530, Balaji Rao wrote:
> +int pcf50633_mbc_get_status(struct pcf50633 *pcf)
> +{
> +	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
> +	int status = 0;
> +
> +	if (mbc->usb_online)
> +		status |= PCF50633_MBC_USB_ONLINE;
> +	if (mbc->usb_online)
> +		status |= PCF50633_MBC_USB_ACTIVE;
> +	if (mbc->usb_online)
> +		status |= PCF50633_MBC_ADAPTER_ONLINE;
> +	if (mbc->usb_online)
> +		status |= PCF50633_MBC_ADAPTER_ACTIVE;
> +

Oops! copy-paste error.. Fixed. Resending..

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: Anton Vorontsov <cbou@mail.ru>
Cc: David Woodhouse <dwmw2@infradead.org>
---
 drivers/power/Kconfig            |    6 +
 drivers/power/Makefile           |    2 
 drivers/power/pcf50633-charger.c |  358 ++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/pcf50633/mbc.h |  134 ++++++++++++++
 4 files changed, 500 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/pcf50633-charger.c
 create mode 100644 include/linux/mfd/pcf50633/mbc.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 52f8676..04f6316 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -75,4 +75,10 @@ config BATTERY_BQ27x00
 	help
 	  Say Y here to enable support for batteries with BQ27200(I2C) chip.
 
+config CHARGER_PCF50633
+	tristate "NXP PCF50633 MBC"
+	depends on MFD_PCF50633
+	help
+	 Say Y to include support for NXP PCF50633 Main Battery Charger.
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e6f6865..62bcb76 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o
+
+obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
new file mode 100644
index 0000000..e988ec1
--- /dev/null
+++ b/drivers/power/pcf50633-charger.c
@@ -0,0 +1,358 @@
+/* NXP PCF50633 Main Battery Charger Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/mbc.h>
+
+struct pcf50633_mbc {
+	struct pcf50633 *pcf;
+
+	int adapter_active;
+	int adapter_online;
+	int usb_active;
+	int usb_online;
+
+	struct power_supply usb;
+	struct power_supply adapter;
+};
+
+int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+	int ret = 0;
+	u8 bits;
+
+	if (ma >= 1000)
+		bits = PCF50633_MBCC7_USB_1000mA;
+	else if (ma >= 500)
+		bits = PCF50633_MBCC7_USB_500mA;
+	else if (ma >= 100)
+		bits = PCF50633_MBCC7_USB_100mA;
+	else
+		bits = PCF50633_MBCC7_USB_SUSPEND;
+
+	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
+					PCF50633_MBCC7_USB_MASK, bits);
+	if (ret)
+		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
+	else
+		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
+
+	power_supply_changed(&mbc->usb);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
+
+int pcf50633_mbc_get_status(struct pcf50633 *pcf)
+{
+	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
+	int status = 0;
+
+	if (mbc->usb_online)
+		status |= PCF50633_MBC_USB_ONLINE;
+	if (mbc->usb_active)
+		status |= PCF50633_MBC_USB_ACTIVE;
+	if (mbc->adapter_online)
+		status |= PCF50633_MBC_ADAPTER_ONLINE;
+	if (mbc->adapter_active)
+		status |= PCF50633_MBC_ADAPTER_ACTIVE;
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
+
+void pcf50633_mbc_set_status(struct pcf50633 *pcf, int what, int status)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+
+	if (what & PCF50633_MBC_USB_ONLINE)
+		mbc->usb_online = !!status;
+	if (what & PCF50633_MBC_USB_ACTIVE)
+		mbc->usb_active = !!status;
+	if (what & PCF50633_MBC_ADAPTER_ONLINE)
+		mbc->adapter_online = !!status;
+	if (what & PCF50633_MBC_ADAPTER_ACTIVE)
+		mbc->adapter_active = !!status;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_set_status);
+
+static ssize_t
+show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+
+	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
+	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
+
+	return sprintf(buf, "%d\n", chgmod);
+}
+static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
+
+static ssize_t
+show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+						PCF50633_MBCC7_USB_MASK;
+	unsigned int ma;
+
+	if (usblim == PCF50633_MBCC7_USB_1000mA)
+		ma = 1000;
+	else if (usblim == PCF50633_MBCC7_USB_500mA)
+		ma = 500;
+	else if (usblim == PCF50633_MBCC7_USB_100mA)
+		ma = 100;
+	else
+		ma = 0;
+
+	return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_usblim(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	unsigned long ma;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &ma);
+	if (ret)
+		return -EINVAL;
+
+	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
+
+	return count;
+}
+
+static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
+
+static struct attribute *pcf50633_mbc_sysfs_entries[] = {
+	&dev_attr_chgmode.attr,
+	&dev_attr_usb_curlim.attr,
+	NULL,
+};
+
+static struct attribute_group mbc_attr_group = {
+	.name	= NULL,			/* put in device directory */
+	.attrs	= pcf50633_mbc_sysfs_entries,
+};
+
+static void
+pcf50633_mbc_irq_handler(int irq, void *data)
+{
+	struct pcf50633_mbc *mbc = data;
+
+	/* USB */
+	if (irq == PCF50633_IRQ_USBINS) {
+		mbc->usb_online = 1;
+	} else if (irq == PCF50633_IRQ_USBREM) {
+		mbc->usb_online = 0;
+		mbc->usb_active = 0;
+		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
+	}
+
+	/* Adapter */
+	if (irq == PCF50633_IRQ_ADPINS) {
+		mbc->adapter_online = 1;
+		mbc->adapter_active = 1;
+	} else if (irq == PCF50633_IRQ_ADPREM) {
+		mbc->adapter_online = 0;
+		mbc->adapter_active = 0;
+	}
+
+	if (irq == PCF50633_IRQ_BATFULL) {
+		mbc->usb_active = 0;
+		mbc->adapter_active = 0;
+	}
+
+	power_supply_changed(&mbc->usb);
+	power_supply_changed(&mbc->adapter);
+
+	if (mbc->pcf->pdata->mbc_event_callback)
+		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
+}
+
+static int adapter_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval =  mbc->adapter_online;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int usb_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = mbc->usb_online;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static enum power_supply_property power_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const u8 mbc_irq_handlers[] = {
+	PCF50633_IRQ_ADPINS,
+	PCF50633_IRQ_ADPREM,
+	PCF50633_IRQ_USBINS,
+	PCF50633_IRQ_USBREM,
+	PCF50633_IRQ_BATFULL,
+	PCF50633_IRQ_CHGHALT,
+	PCF50633_IRQ_THLIMON,
+	PCF50633_IRQ_THLIMOFF,
+	PCF50633_IRQ_USBLIMON,
+	PCF50633_IRQ_USBLIMOFF,
+	PCF50633_IRQ_LOWSYS,
+	PCF50633_IRQ_LOWBAT,
+};
+
+static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
+{
+	struct pcf50633_mbc *mbc;
+	struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
+	int ret;
+	int i;
+	u8 mbcs1;
+
+	mbc = kzalloc(sizeof(*mbc), GFP_KERNEL);
+	if (!mbc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, mbc);
+	mbc->pcf = pdata->pcf;
+
+	/* Set up IRQ handlers */
+	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
+					pcf50633_mbc_irq_handler, mbc);
+
+	/* Create power supplies */
+	mbc->adapter.name		= "adapter";
+	mbc->adapter.type		= POWER_SUPPLY_TYPE_MAINS;
+	mbc->adapter.properties		= power_props;
+	mbc->adapter.num_properties	= ARRAY_SIZE(power_props);
+	mbc->adapter.get_property	= &adapter_get_property;
+	mbc->adapter.supplied_to	= mbc->pcf->pdata->batteries;
+	mbc->adapter.num_supplicants	= mbc->pcf->pdata->num_batteries;
+
+	mbc->usb.name			= "usb";
+	mbc->usb.type			= POWER_SUPPLY_TYPE_USB;
+	mbc->usb.properties		= power_props;
+	mbc->usb.num_properties		= ARRAY_SIZE(power_props);
+	mbc->usb.get_property		= usb_get_property;
+	mbc->usb.supplied_to		= mbc->pcf->pdata->batteries;
+	mbc->usb.num_supplicants	= mbc->pcf->pdata->num_batteries;
+
+	ret = power_supply_register(&pdev->dev, &mbc->adapter);
+	if (ret) {
+		dev_err(mbc->pcf->dev, "failed to register adapter\n");
+		kfree(mbc);
+		return ret;
+	}
+
+	ret = power_supply_register(&pdev->dev, &mbc->usb);
+	if (ret) {
+		dev_err(mbc->pcf->dev, "failed to register usb\n");
+		power_supply_unregister(&mbc->adapter);
+		kfree(mbc);
+		return ret;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
+	if (ret)
+		dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
+
+	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
+	if (mbcs1 & PCF50633_MBCS1_USBPRES)
+		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
+	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
+		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
+
+	return 0;
+}
+
+static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
+{
+	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
+	int i;
+
+	/* Remove IRQ handlers */
+	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
+
+	power_supply_unregister(&mbc->usb);
+	power_supply_unregister(&mbc->adapter);
+
+	kfree(mbc);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_mbc_driver = {
+	.driver = {
+		.name = "pcf50633-mbc",
+	},
+	.probe = pcf50633_mbc_probe,
+	.remove = __devexit_p(pcf50633_mbc_remove),
+};
+
+static int __init pcf50633_mbc_init(void)
+{
+	return platform_driver_register(&pcf50633_mbc_driver);
+}
+module_init(pcf50633_mbc_init);
+
+static void __exit pcf50633_mbc_exit(void)
+{
+	platform_driver_unregister(&pcf50633_mbc_driver);
+}
+module_exit(pcf50633_mbc_exit);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 mbc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-mbc");
diff --git a/include/linux/mfd/pcf50633/mbc.h b/include/linux/mfd/pcf50633/mbc.h
new file mode 100644
index 0000000..6e17619
--- /dev/null
+++ b/include/linux/mfd/pcf50633/mbc.h
@@ -0,0 +1,134 @@
+/*
+ * mbc.h  -- Driver for NXP PCF50633 Main Battery Charger
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_PCF50633_MBC_H
+#define __LINUX_MFD_PCF50633_MBC_H
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/platform_device.h>
+
+#define PCF50633_REG_MBCC1	0x43
+#define PCF50633_REG_MBCC2	0x44
+#define PCF50633_REG_MBCC3	0x45
+#define PCF50633_REG_MBCC4	0x46
+#define PCF50633_REG_MBCC5	0x47
+#define PCF50633_REG_MBCC6	0x48
+#define PCF50633_REG_MBCC7	0x49
+#define PCF50633_REG_MBCC8	0x4a
+#define PCF50633_REG_MBCS1	0x4b
+#define PCF50633_REG_MBCS2	0x4c
+#define PCF50633_REG_MBCS3	0x4d
+
+enum pcf50633_reg_mbcc1 {
+	PCF50633_MBCC1_CHGENA		= 0x01,	/* Charger enable */
+	PCF50633_MBCC1_AUTOSTOP		= 0x02,
+	PCF50633_MBCC1_AUTORES		= 0x04, /* automatic resume */
+	PCF50633_MBCC1_RESUME		= 0x08, /* explicit resume cmd */
+	PCF50633_MBCC1_RESTART		= 0x10, /* restart charging */
+	PCF50633_MBCC1_PREWDTIME_60M	= 0x20,	/* max. precharging time */
+	PCF50633_MBCC1_WDTIME_1H	= 0x00,
+	PCF50633_MBCC1_WDTIME_2H	= 0x40,
+	PCF50633_MBCC1_WDTIME_4H	= 0x80,
+	PCF50633_MBCC1_WDTIME_6H	= 0xc0,
+};
+#define PCF50633_MBCC1_WDTIME_MASK	  0xc0
+
+enum pcf50633_reg_mbcc2 {
+	PCF50633_MBCC2_VBATCOND_2V7	= 0x00,
+	PCF50633_MBCC2_VBATCOND_2V85	= 0x01,
+	PCF50633_MBCC2_VBATCOND_3V0	= 0x02,
+	PCF50633_MBCC2_VBATCOND_3V15	= 0x03,
+	PCF50633_MBCC2_VMAX_4V		= 0x00,
+	PCF50633_MBCC2_VMAX_4V20	= 0x28,
+	PCF50633_MBCC2_VRESDEBTIME_64S	= 0x80,	/* debounce time (32/64sec) */
+};
+
+enum pcf50633_reg_mbcc7 {
+	PCF50633_MBCC7_USB_100mA	= 0x00,
+	PCF50633_MBCC7_USB_500mA	= 0x01,
+	PCF50633_MBCC7_USB_1000mA	= 0x02,
+	PCF50633_MBCC7_USB_SUSPEND	= 0x03,
+	PCF50633_MBCC7_BATTEMP_EN	= 0x04,
+	PCF50633_MBCC7_BATSYSIMAX_1A6	= 0x00,
+	PCF50633_MBCC7_BATSYSIMAX_1A8	= 0x40,
+	PCF50633_MBCC7_BATSYSIMAX_2A0	= 0x80,
+	PCF50633_MBCC7_BATSYSIMAX_2A2	= 0xc0,
+};
+#define PCF50633_MBCC7_USB_MASK 0x03
+
+enum pcf50633_reg_mbcc8 {
+	PCF50633_MBCC8_USBENASUS	= 0x10,
+};
+
+enum pcf50633_reg_mbcs1 {
+	PCF50633_MBCS1_USBPRES		= 0x01,
+	PCF50633_MBCS1_USBOK		= 0x02,
+	PCF50633_MBCS1_ADAPTPRES	= 0x04,
+	PCF50633_MBCS1_ADAPTOK		= 0x08,
+	PCF50633_MBCS1_TBAT_OK		= 0x00,
+	PCF50633_MBCS1_TBAT_ABOVE	= 0x10,
+	PCF50633_MBCS1_TBAT_BELOW	= 0x20,
+	PCF50633_MBCS1_TBAT_UNDEF	= 0x30,
+	PCF50633_MBCS1_PREWDTEXP	= 0x40,
+	PCF50633_MBCS1_WDTEXP		= 0x80,
+};
+
+enum pcf50633_reg_mbcs2_mbcmod {
+	PCF50633_MBCS2_MBC_PLAY		= 0x00,
+	PCF50633_MBCS2_MBC_USB_PRE	= 0x01,
+	PCF50633_MBCS2_MBC_USB_PRE_WAIT	= 0x02,
+	PCF50633_MBCS2_MBC_USB_FAST	= 0x03,
+	PCF50633_MBCS2_MBC_USB_FAST_WAIT = 0x04,
+	PCF50633_MBCS2_MBC_USB_SUSPEND	= 0x05,
+	PCF50633_MBCS2_MBC_ADP_PRE	= 0x06,
+	PCF50633_MBCS2_MBC_ADP_PRE_WAIT	= 0x07,
+	PCF50633_MBCS2_MBC_ADP_FAST	= 0x08,
+	PCF50633_MBCS2_MBC_ADP_FAST_WAIT = 0x09,
+	PCF50633_MBCS2_MBC_BAT_FULL	= 0x0a,
+	PCF50633_MBCS2_MBC_HALT		= 0x0b,
+};
+#define PCF50633_MBCS2_MBC_MASK		0x0f
+enum pcf50633_reg_mbcs2_chgstat {
+	PCF50633_MBCS2_CHGS_NONE	= 0x00,
+	PCF50633_MBCS2_CHGS_ADAPTER	= 0x10,
+	PCF50633_MBCS2_CHGS_USB		= 0x20,
+	PCF50633_MBCS2_CHGS_BOTH	= 0x30,
+};
+#define PCF50633_MBCS2_RESSTAT_AUTO	0x40
+
+enum pcf50633_reg_mbcs3 {
+	PCF50633_MBCS3_USBLIM_PLAY	= 0x01,
+	PCF50633_MBCS3_USBLIM_CGH	= 0x02,
+	PCF50633_MBCS3_TLIM_PLAY	= 0x04,
+	PCF50633_MBCS3_TLIM_CHG		= 0x08,
+	PCF50633_MBCS3_ILIM		= 0x10,	/* 1: Ibat > Icutoff */
+	PCF50633_MBCS3_VLIM		= 0x20,	/* 1: Vbat == Vmax */
+	PCF50633_MBCS3_VBATSTAT		= 0x40,	/* 1: Vbat > Vbatcond */
+	PCF50633_MBCS3_VRES		= 0x80, /* 1: Vbat > Vth(RES) */
+};
+
+#define PCF50633_MBCC2_VBATCOND_MASK	  0x03
+#define PCF50633_MBCC2_VMAX_MASK	  0x3c
+
+/* Charger status */
+#define PCF50633_MBC_USB_ONLINE		0x01
+#define PCF50633_MBC_USB_ACTIVE		0x02
+#define PCF50633_MBC_ADAPTER_ONLINE	0x04
+#define PCF50633_MBC_ADAPTER_ACTIVE	0x08
+
+int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma);
+
+int pcf50633_mbc_get_status(struct pcf50633 *);
+void pcf50633_mbc_set_status(struct pcf50633 *, int what, int status);
+
+#endif
+




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

* Re: [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18 15:14     ` Balaji Rao
@ 2008-12-18 20:30       ` Liam Girdwood
  2008-12-18 20:47         ` Balaji Rao
  0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2008-12-18 20:30 UTC (permalink / raw)
  To: Balaji Rao; +Cc: Mark Brown, linux-kernel, Andy Green

On Thu, 2008-12-18 at 20:44 +0530, Balaji Rao wrote:
> On Thu, Dec 18, 2008 at 10:08:35AM +0000, Mark Brown wrote:
> > On Thu, Dec 18, 2008 at 11:28:17AM +0530, Balaji Rao wrote:
> > > Changes from V1:
> > > 	- Removed support for suspend_enable & suspend_disable functions.
> > > 
> > > Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> > > Cc: Andy Green <andy@openmoko.com>
> > > Cc: Liam Girdwood <lrg@simlogic.co.uk>

Applied.

Thanks

Liam


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

* Re: [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18 20:30       ` Liam Girdwood
@ 2008-12-18 20:47         ` Balaji Rao
  2008-12-18 20:54           ` Liam Girdwood
  0 siblings, 1 reply; 26+ messages in thread
From: Balaji Rao @ 2008-12-18 20:47 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: Mark Brown, linux-kernel, Andy Green

On Thu, Dec 18, 2008 at 08:30:54PM +0000, Liam Girdwood wrote:
> On Thu, 2008-12-18 at 20:44 +0530, Balaji Rao wrote:
> > On Thu, Dec 18, 2008 at 10:08:35AM +0000, Mark Brown wrote:
> > > On Thu, Dec 18, 2008 at 11:28:17AM +0530, Balaji Rao wrote:
> > > > Changes from V1:
> > > > 	- Removed support for suspend_enable & suspend_disable functions.
> > > > 
> > > > Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> > > > Cc: Andy Green <andy@openmoko.com>
> > > > Cc: Liam Girdwood <lrg@simlogic.co.uk>
> 
> Applied.
> 
> Thanks
> 

Liam, the pcf50633-regulator.c has depends on
include/linux/mfd/pcf50633/core.patch which is not here, but in
[PATCH 1/7] of the series. So, pcf50633-regulator.c it wouldn't
build.

I was expecting it to make it to -next via linux-mfd - I've asked
Samuel Ortiz about this in [0/7].

So, I was hoping you to ack this so that Samuel can take it into
linux-mfd.

I apologize for the confusion.

	- Balaji

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

* Re: [PATCH V2 7/7] regulator: PCF50633 pmic driver
  2008-12-18 20:47         ` Balaji Rao
@ 2008-12-18 20:54           ` Liam Girdwood
  0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2008-12-18 20:54 UTC (permalink / raw)
  To: Balaji Rao; +Cc: Mark Brown, linux-kernel, Andy Green

On Fri, 2008-12-19 at 02:17 +0530, Balaji Rao wrote:
> On Thu, Dec 18, 2008 at 08:30:54PM +0000, Liam Girdwood wrote:
> > On Thu, 2008-12-18 at 20:44 +0530, Balaji Rao wrote:
> > > On Thu, Dec 18, 2008 at 10:08:35AM +0000, Mark Brown wrote:
> > > > On Thu, Dec 18, 2008 at 11:28:17AM +0530, Balaji Rao wrote:
> > > > > Changes from V1:
> > > > > 	- Removed support for suspend_enable & suspend_disable functions.
> > > > > 
> > > > > Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> > > > > Cc: Andy Green <andy@openmoko.com>
> > > > > Cc: Liam Girdwood <lrg@simlogic.co.uk>
> > 
> > Applied.
> > 
> > Thanks
> > 
> 
> Liam, the pcf50633-regulator.c has depends on
> include/linux/mfd/pcf50633/core.patch which is not here, but in
> [PATCH 1/7] of the series. So, pcf50633-regulator.c it wouldn't
> build.
> 
> I was expecting it to make it to -next via linux-mfd - I've asked
> Samuel Ortiz about this in [0/7].
> 
> So, I was hoping you to ack this so that Samuel can take it into
> linux-mfd.
> 
> I apologize for the confusion.

Ok. Np.

Thanks

Liam 


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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-18  5:56 ` [PATCH V2 2/7] mfd: PCF50633 adc driver Balaji Rao
@ 2008-12-19 11:18   ` Jonathan Cameron
  2008-12-19 12:05     ` Mark Brown
                       ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: Jonathan Cameron @ 2008-12-19 11:18 UTC (permalink / raw)
  To: Balaji Rao; +Cc: linux-kernel, Andy Green, Samuel Ortiz

Hi Balaji,

Nice driver.  I particularly like the queueing structure. In the IIO
design I hadn't come across any devices that operate in this request
then wait for interrupt fashion.  May need to rethink a few bits of
that.

Couple of minor suggestions below.

> This patch adds basic support for the PCF50633 ADC. The subtractive mode
> is not supported yet.
>
> Since we don't have adc subsystem, it currently lives in drivers/mfd.
>
> Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> Cc: Andy Green <andy@openmoko.com>
...
> --- /dev/null
> +++ b/drivers/mfd/pcf50633-adc.c
> @@ -0,0 +1,277 @@
> +/* NXP PCF50633 ADC Driver
> + *
> + * (C) 2006-2008 by Openmoko, Inc.
> + * Author: Balaji Rao <balajirrao@openmoko.org>
> + * All rights reserved.
> + *
> + * Broken down from monstrous PCF50633 driver mainly by
> + * Harald Welte, Andy Green and Werner Almesberger
> + *
> + *  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.
> + *
> + *  NOTE: This driver does not yet support subtractive ADC mode, which
means
> + *  you can do only one measurement per read request.
> + */
...

This is confusingly named. To my mind it is writing the setup
to the device not reading it.

> +static void adc_read_setup(struct pcf50633 *pcf, int channel, int avg)
> +{
> +    channel &= PCF50633_ADCC1_ADCMUX_MASK;
This needs a bit more explanation. Particularly as the data
sheet describes that accsw as 'for rationmetric measurement'.
Also, seeing as I assume this is the only driver that can touch
these registers and you don't change them else where, why can't
they be in initial setup code rather than here? (probably a good
reason, but be nice to have it document here!)
> +    /* kill ratiometric, but enable ACCSW biasing */
> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
> +
> +    /* start ADC conversion on selected channel */
> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
...
> +
> +static void pcf50633_adc_irq(int irq, void *data)
> +{
> +    struct pcf50633_adc *adc = data;
> +    struct pcf50633 *pcf = adc->pcf;
> +    struct pcf50633_adc_request *req;
> +    int head;
> +    mutex_lock(&adc->queue_mutex);
> +    head = adc->queue_head;
> +
> +    req = adc->queue[head];
> +    if (WARN_ON(!req)) {
> +        dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
> +        mutex_unlock(&adc->queue_mutex);
> +        return;
> +    }
> +    adc->queue[head] = NULL;

Weird formatting?

> +    adc->queue_head = (head + 1) &
> +                      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
> +
> +    mutex_unlock(&adc->queue_mutex);
> +    req->callback(pcf, req->callback_param, adc_result(pcf));
> +    kfree(req);
> +
> +    trigger_next_adc_job_if_any(pcf);
> +}
> +
Rest looks good to me.

Acked-by: Jonathan Cameron


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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-19 11:18   ` Jonathan Cameron
@ 2008-12-19 12:05     ` Mark Brown
  2008-12-19 12:47       ` Jonathan Cameron
  2008-12-19 12:15     ` Andy Green
  2008-12-22 16:23     ` Balaji Rao
  2 siblings, 1 reply; 26+ messages in thread
From: Mark Brown @ 2008-12-19 12:05 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: Balaji Rao, linux-kernel, Andy Green, Samuel Ortiz

On Fri, Dec 19, 2008 at 11:18:52AM +0000, Jonathan Cameron wrote:

> Nice driver.  I particularly like the queueing structure. In the IIO
> design I hadn't come across any devices that operate in this request
> then wait for interrupt fashion.  May need to rethink a few bits of
> that.

There's auxiliary ADCs like that in several Wolfson parts too - at least
WM97xx and WM8350 have them.  They're typically used for applications
like battery and supply monitoring.

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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-19 11:18   ` Jonathan Cameron
  2008-12-19 12:05     ` Mark Brown
@ 2008-12-19 12:15     ` Andy Green
  2008-12-19 12:51       ` Jonathan Cameron
  2008-12-22 16:23     ` Balaji Rao
  2 siblings, 1 reply; 26+ messages in thread
From: Andy Green @ 2008-12-19 12:15 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: Balaji Rao, linux-kernel, Samuel Ortiz

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Somebody in the thread at some point said:

Hi Johnathan -

| This needs a bit more explanation. Particularly as the data
| sheet describes that accsw as 'for rationmetric measurement'.

What's going on here is that ACCSW can be driven from the PMU to bias a
resistor divider to scale what's being measured... we use it to detect
resistor to 0V on ID pin of USB on our charger.

You can power accsw / that divider "by hand" which is what we're doing
here, or you can have the ratiometric state machine change the mux /
bias automatically between the two measurements it takes, which we're
not doing.

| Also, seeing as I assume this is the only driver that can touch
| these registers and you don't change them else where, why can't
| they be in initial setup code rather than here? (probably a good
| reason, but be nice to have it document here!)

ADCC2 setting concerned with ratiometric disable could indeed be moved
to init, it was done like this before I did the queuing stuff and we
took ADC measurement in two different places in the code.  But in fact
for the best we should only enable accsw until the measurement completes
and save a tiny bit of power.

|> +    /* kill ratiometric, but enable ACCSW biasing */
|> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
|> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
|> +
|> +    /* start ADC conversion on selected channel */
|> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
| ...
|> +
|> +static void pcf50633_adc_irq(int irq, void *data)
|> +{
|> +    struct pcf50633_adc *adc = data;
|> +    struct pcf50633 *pcf = adc->pcf;
|> +    struct pcf50633_adc_request *req;
|> +    int head;
|> +    mutex_lock(&adc->queue_mutex);
|> +    head = adc->queue_head;
|> +
|> +    req = adc->queue[head];
|> +    if (WARN_ON(!req)) {
|> +        dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
|> +        mutex_unlock(&adc->queue_mutex);
|> +        return;
|> +    }
|> +    adc->queue[head] = NULL;
|
| Weird formatting?
|
|> +    adc->queue_head = (head + 1) &
|> +                      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
|> +
|> +    mutex_unlock(&adc->queue_mutex);

This would save the power

~    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x0);

|> +    req->callback(pcf, req->callback_param, adc_result(pcf));
|> +    kfree(req);
|> +
|> +    trigger_next_adc_job_if_any(pcf);
|> +}
|> +
| Rest looks good to me.
|
| Acked-by: Jonathan Cameron
|

- -Andy
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iEYEARECAAYFAklLkHIACgkQOjLpvpq7dMoAtACeNxmM0m69xgbvT4YIPik2aHNP
hSIAoIUUbXcibwPYqgasik4ALFEc0img
=//X+
-----END PGP SIGNATURE-----

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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-19 12:05     ` Mark Brown
@ 2008-12-19 12:47       ` Jonathan Cameron
  0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2008-12-19 12:47 UTC (permalink / raw)
  To: Mark Brown; +Cc: Balaji Rao, linux-kernel, Andy Green, Samuel Ortiz

Mark Brown wrote:
> On Fri, Dec 19, 2008 at 11:18:52AM +0000, Jonathan Cameron wrote:
>
>   
>> Nice driver.  I particularly like the queueing structure. In the IIO
>> design I hadn't come across any devices that operate in this request
>> then wait for interrupt fashion.  May need to rethink a few bits of
>> that.
>>     
>
> There's auxiliary ADCs like that in several Wolfson parts too - at least
> WM97xx and WM8350 have them.  They're typically used for applications
> like battery and supply monitoring.
>   
True. The da903x chips have one as well.

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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-19 12:15     ` Andy Green
@ 2008-12-19 12:51       ` Jonathan Cameron
  0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2008-12-19 12:51 UTC (permalink / raw)
  To: Andy Green; +Cc: Balaji Rao, linux-kernel, Samuel Ortiz

Andy Green wrote:
> Somebody in the thread at some point said:
>
> Hi Johnathan -
>
> | This needs a bit more explanation. Particularly as the data
> | sheet describes that accsw as 'for rationmetric measurement'.
>
> What's going on here is that ACCSW can be driven from the PMU to bias a
> resistor divider to scale what's being measured... we use it to detect
> resistor to 0V on ID pin of USB on our charger.
>
> You can power accsw / that divider "by hand" which is what we're doing
> here, or you can have the ratiometric state machine change the mux /
> bias automatically between the two measurements it takes, which we're
> not doing.
That makes sense. Thanks for the explanation.
>
> | Also, seeing as I assume this is the only driver that can touch
> | these registers and you don't change them else where, why can't
> | they be in initial setup code rather than here? (probably a good
> | reason, but be nice to have it document here!)
>
> ADCC2 setting concerned with ratiometric disable could indeed be moved
> to init, it was done like this before I did the queuing stuff and we
> took ADC measurement in two different places in the code.  But in fact
> for the best we should only enable accsw until the measurement completes
> and save a tiny bit of power.
Either way sounds fine to me as I'd be surprised if it is a significant
power
drain and anyway it would come at the cost of a couple more register writes
which, if the bus is slow, would be more of an issue.

>
> |> +    /* kill ratiometric, but enable ACCSW biasing */
> |> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
> |> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
> |> +
> |> +    /* start ADC conversion on selected channel */
> |> +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
> | ...
> |> +
> |> +static void pcf50633_adc_irq(int irq, void *data)
> |> +{
> |> +    struct pcf50633_adc *adc = data;
> |> +    struct pcf50633 *pcf = adc->pcf;
> |> +    struct pcf50633_adc_request *req;
> |> +    int head;
> |> +    mutex_lock(&adc->queue_mutex);
> |> +    head = adc->queue_head;
> |> +
> |> +    req = adc->queue[head];
> |> +    if (WARN_ON(!req)) {
> |> +        dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
> |> +        mutex_unlock(&adc->queue_mutex);
> |> +        return;
> |> +    }
> |> +    adc->queue[head] = NULL;
> |
> | Weird formatting?
> |
> |> +    adc->queue_head = (head + 1) &
> |> +                      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
> |> +
> |> +    mutex_unlock(&adc->queue_mutex);
>
> This would save the power
>
> ~    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x0);
>
> |> +    req->callback(pcf, req->callback_param, adc_result(pcf));
> |> +    kfree(req);
> |> +
> |> +    trigger_next_adc_job_if_any(pcf);
> |> +}
> |> +
> | Rest looks good to me.
> |
> | Acked-by: Jonathan Cameron
> |
>
> -Andy


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

* Re: [PATCH V2 0/7] PCF50633 support
  2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
                   ` (6 preceding siblings ...)
  2008-12-18  5:58 ` [PATCH V2 7/7] regulator: PCF50633 pmic driver Balaji Rao
@ 2008-12-22 10:50 ` Samuel Ortiz
  7 siblings, 0 replies; 26+ messages in thread
From: Samuel Ortiz @ 2008-12-22 10:50 UTC (permalink / raw)
  To: Balaji Rao; +Cc: linux-kernel

Hi Balaji,

On Thu, Dec 18, 2008 at 11:26:18AM +0530, Balaji Rao wrote:
> Changes from V1:
> 	- Subdevice specific header files now included in its respective patch.
> 	- Introduces pcf50633_register_irq and pcf50633_free_irq for irq
> 	  management
> 	- Many changes in how subdevices are created and information is passed
> 	  to them. Thanks to Alessandro Zummo for suggestions.
> 
> The following series implements support for NXP PCF50633. It's basically
> an I2C device with 9 regulators, an ADC, a PMIC, a Battery Charger
> and a RTC.
> 
> This chip is used in Openmoko Neo Freerunner mobile phone.
> 
> Driver for a similar chip PCF50606, used in Openmoko Neo 1973 device
> will follow soon.
> 
> The specs are open and are available at 
> http://people.openmoko.org/tony_tu/GTA02/datasheet/PMU/PCF50633DS_02.pdf
> 
> Samuel, are you ok with merging this via linux-mfd tree ?
Yes, I'm fine with it. Two things though:
- Could you please resend the non-mfd parts (patch 4,5,6 and 7) to me ?
- I see from the lkml archives that patches 5 and 6 didnt get Acked yet. I'll
queue them, but I'd really like to get Anton and Dmitry approval before
sending them upstream.

Cheers,
Samuel.


> 
> ---
> Balaji Rao (7):
>       mfd: PCF50633 core driver
>       mfd: PCF50633 adc driver
>       mfd: PCF50633 gpio support
>       rtc: PCF50633 rtc driver
>       power_supply: PCF50633 battery charger driver
>       input: PCF50633 input driver
>       regulator: PCF50633 pmic driver
> 
>  drivers/input/misc/Kconfig             |    7 
>  drivers/input/misc/Makefile            |    1 
>  drivers/input/misc/pcf50633-input.c    |  132 ++++++
>  drivers/mfd/Kconfig                    |   23 +
>  drivers/mfd/Makefile                   |    6 
>  drivers/mfd/pcf50633-adc.c             |  277 +++++++++++++
>  drivers/mfd/pcf50633-core.c            |  707 ++++++++++++++++++++++++++++++++
>  drivers/mfd/pcf50633-gpio.c            |  118 +++++
>  drivers/power/Kconfig                  |    6 
>  drivers/power/Makefile                 |    2 
>  drivers/power/pcf50633-charger.c       |  358 ++++++++++++++++
>  drivers/regulator/Kconfig              |    7 
>  drivers/regulator/Makefile             |    1 
>  drivers/regulator/pcf50633-regulator.c |  330 +++++++++++++++
>  drivers/rtc/Kconfig                    |    6 
>  drivers/rtc/Makefile                   |    1 
>  drivers/rtc/rtc-pcf50633.c             |  336 +++++++++++++++
>  include/linux/mfd/pcf50633/adc.h       |   72 +++
>  include/linux/mfd/pcf50633/core.h      |  218 ++++++++++
>  include/linux/mfd/pcf50633/gpio.h      |   52 ++
>  include/linux/mfd/pcf50633/mbc.h       |  134 ++++++
>  include/linux/mfd/pcf50633/pmic.h      |   68 +++
>  22 files changed, 2861 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/input/misc/pcf50633-input.c
>  create mode 100644 drivers/mfd/pcf50633-adc.c
>  create mode 100644 drivers/mfd/pcf50633-core.c
>  create mode 100644 drivers/mfd/pcf50633-gpio.c
>  create mode 100644 drivers/power/pcf50633-charger.c
>  create mode 100644 drivers/regulator/pcf50633-regulator.c
>  create mode 100644 drivers/rtc/rtc-pcf50633.c
>  create mode 100644 include/linux/mfd/pcf50633/adc.h
>  create mode 100644 include/linux/mfd/pcf50633/core.h
>  create mode 100644 include/linux/mfd/pcf50633/gpio.h
>  create mode 100644 include/linux/mfd/pcf50633/mbc.h
>  create mode 100644 include/linux/mfd/pcf50633/pmic.h
> 
> 
> -- 
> Thanks,
> Balaji Rao
> 

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

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

* Re: [PATCH V2 2/7] mfd: PCF50633 adc driver
  2008-12-19 11:18   ` Jonathan Cameron
  2008-12-19 12:05     ` Mark Brown
  2008-12-19 12:15     ` Andy Green
@ 2008-12-22 16:23     ` Balaji Rao
  2 siblings, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-22 16:23 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-kernel, Andy Green, Samuel Ortiz

On Fri, Dec 19, 2008 at 11:18:52AM +0000, Jonathan Cameron wrote:
Hi Jonathan,

> 
> This is confusingly named. To my mind it is writing the setup
> to the device not reading it.
> 
> > +static void adc_read_setup(struct pcf50633 *pcf, int channel, int avg)
> > +{
> > +    channel &= PCF50633_ADCC1_ADCMUX_MASK;

Yes, right. Will change.

> This needs a bit more explanation. Particularly as the data
> sheet describes that accsw as 'for rationmetric measurement'.
> Also, seeing as I assume this is the only driver that can touch
> these registers and you don't change them else where, why can't
> they be in initial setup code rather than here? (probably a good
> reason, but be nice to have it document here!)

Yes, the for killing ratiometric measurement can be in _probe. But we
need to enable accsw everytime because it's turned off automatically 
once a conversion is complete - to save power.

> > +    /* kill ratiometric, but enable ACCSW biasing */
> > +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
> > +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
> > +
> > +    /* start ADC conversion on selected channel */
> > +    pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
> ...
> > +
> > +static void pcf50633_adc_irq(int irq, void *data)
> > +{
> > +    struct pcf50633_adc *adc = data;
> > +    struct pcf50633 *pcf = adc->pcf;
> > +    struct pcf50633_adc_request *req;
> > +    int head;
> > +    mutex_lock(&adc->queue_mutex);
> > +    head = adc->queue_head;
> > +
> > +    req = adc->queue[head];
> > +    if (WARN_ON(!req)) {
> > +        dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
> > +        mutex_unlock(&adc->queue_mutex);
> > +        return;
> > +    }
> > +    adc->queue[head] = NULL;
> 
> Weird formatting?
>

Oops! Will fix.

Thank you for the review.

Balaji Rao

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

* Re: [PATCH V2 5/7] power_supply: PCF50633 battery charger driver
  2008-12-18  5:57 ` [PATCH V2 5/7] power_supply: PCF50633 battery charger driver Balaji Rao
  2008-12-18 20:26   ` Balaji Rao
@ 2008-12-25 15:45   ` Anton Vorontsov
  2008-12-25 18:50     ` Balaji Rao
  1 sibling, 1 reply; 26+ messages in thread
From: Anton Vorontsov @ 2008-12-25 15:45 UTC (permalink / raw)
  To: Balaji Rao
  Cc: linux-kernel, Andy Green, Anton Vorontsov, David Woodhouse, Samuel Ortiz

On Thu, Dec 18, 2008 at 11:27:45AM +0530, Balaji Rao wrote:
> Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> Cc: Andy Green <andy@openmoko.com>
> Cc: Anton Vorontsov <cbou@mail.ru>
> Cc: David Woodhouse <dwmw2@infradead.org>
> ---

Acked-by: Anton Vorontsov <cbouatmailru@gmail.com>

(As the patch depends on MFD parts, I assume Samuel will merge it
into linux-mfd.)

>  drivers/power/Kconfig            |    6 +
>  drivers/power/Makefile           |    2 
>  drivers/power/pcf50633-charger.c |  358 ++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/pcf50633/mbc.h |  134 ++++++++++++++
>  4 files changed, 500 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/power/pcf50633-charger.c
>  create mode 100644 include/linux/mfd/pcf50633/mbc.h
[...]

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

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

* Re: [PATCH V2 5/7] power_supply: PCF50633 battery charger driver
  2008-12-25 15:45   ` Anton Vorontsov
@ 2008-12-25 18:50     ` Balaji Rao
  0 siblings, 0 replies; 26+ messages in thread
From: Balaji Rao @ 2008-12-25 18:50 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: linux-kernel, Andy Green, Anton Vorontsov, David Woodhouse, Samuel Ortiz

On Thu, Dec 25, 2008 at 06:45:21PM +0300, Anton Vorontsov wrote:
> On Thu, Dec 18, 2008 at 11:27:45AM +0530, Balaji Rao wrote:
> > Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
> > Cc: Andy Green <andy@openmoko.com>
> > Cc: Anton Vorontsov <cbou@mail.ru>
> > Cc: David Woodhouse <dwmw2@infradead.org>
> > ---
> 
> Acked-by: Anton Vorontsov <cbouatmailru@gmail.com>
> 
> (As the patch depends on MFD parts, I assume Samuel will merge it
> into linux-mfd.)
> 

Yes. thanks a lot!

	- Balaji

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

end of thread, other threads:[~2008-12-25 18:51 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-12-18  5:56 [PATCH V2 0/7] PCF50633 support Balaji Rao
2008-12-18  5:56 ` [PATCH V2 1/7] mfd: PCF50633 core driver Balaji Rao
2008-12-18  5:56 ` [PATCH V2 2/7] mfd: PCF50633 adc driver Balaji Rao
2008-12-19 11:18   ` Jonathan Cameron
2008-12-19 12:05     ` Mark Brown
2008-12-19 12:47       ` Jonathan Cameron
2008-12-19 12:15     ` Andy Green
2008-12-19 12:51       ` Jonathan Cameron
2008-12-22 16:23     ` Balaji Rao
2008-12-18  5:57 ` [PATCH V2 3/7] mfd: PCF50633 gpio support Balaji Rao
2008-12-18  5:57 ` [PATCH V2 4/7] rtc: PCF50633 rtc driver Balaji Rao
2008-12-18  9:03   ` Alessandro Zummo
2008-12-18 15:11     ` Balaji Rao
2008-12-18 16:52       ` Alessandro Zummo
2008-12-18  5:57 ` [PATCH V2 5/7] power_supply: PCF50633 battery charger driver Balaji Rao
2008-12-18 20:26   ` Balaji Rao
2008-12-25 15:45   ` Anton Vorontsov
2008-12-25 18:50     ` Balaji Rao
2008-12-18  5:58 ` [PATCH V2 6/7] input: PCF50633 input driver Balaji Rao
2008-12-18  5:58 ` [PATCH V2 7/7] regulator: PCF50633 pmic driver Balaji Rao
2008-12-18 10:08   ` Mark Brown
2008-12-18 15:14     ` Balaji Rao
2008-12-18 20:30       ` Liam Girdwood
2008-12-18 20:47         ` Balaji Rao
2008-12-18 20:54           ` Liam Girdwood
2008-12-22 10:50 ` [PATCH V2 0/7] PCF50633 support Samuel Ortiz

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