summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig27
-rw-r--r--drivers/mfd/Makefile9
-rw-r--r--drivers/mfd/da9052-core.c521
-rw-r--r--drivers/mfd/da9052-i2c.c377
-rw-r--r--drivers/mfd/da9052-spi.c399
-rw-r--r--drivers/mfd/ltc3589-i2c.c121
-rw-r--r--drivers/mfd/max17135-core.c232
-rw-r--r--drivers/mfd/wm8350-core.c3
8 files changed, 1688 insertions, 1 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e504bbe9..0da643fe59f6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -347,6 +347,15 @@ config MFD_WM8994
core support for the WM8994, in order to use the actual
functionaltiy of the device other drivers must be enabled.
+config MFD_LTC3589
+ bool
+ depends on GENERIC_HARDIRQS
+
+config MFD_LTC3589_I2C
+ bool "Support for Linear LTC3589 with I2C"
+ select MFD_LTC3589
+ depends on I2C=y && GENERIC_HARDIRQS
+
config MFD_PCF50633
tristate "Support for NXP PCF50633"
depends on I2C
@@ -366,6 +375,17 @@ config MFD_MC13783
additional drivers must be enabled in order to use the
functionality of the device.
+config PMIC_DA9052
+ tristate "Dialog DA9052 with SPI/I2C"
+ depends on SPI_MASTER=y
+ depends on I2C=y
+ select MFD_CORE
+ help
+ SPI/I2C Support for the Dialog semiconductor DA9052 PMIC.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config PCF50633_ADC
tristate "Support for NXP PCF50633 ADC"
depends on MFD_PCF50633
@@ -482,6 +502,13 @@ config MFD_JANZ_CMODIO
host many different types of MODULbus daughterboards, including
CAN and GPIO controllers.
+config MFD_MAX17135
+ tristate "MAX17135 PMIC core"
+ depends on I2C
+ help
+ This is the MAX17135 PMIC support. It includes
+ core support for communication with the MAX17135 chip.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e77dc60..36919bd16e85 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
+obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589-i2c.o
+
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MENELAUS) += menelaus.o
@@ -71,3 +73,10 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
+obj-$(CONFIG_MFD_MAX17135) += max17135-core.o
+
+#ifeq ($(CONFIG_PMIC_DA9052),y)
+da9052-objs := da9052-spi.o da9052-i2c.o da9052-core.o
+obj-$(CONFIG_PMIC_DA9052) += da9052.o
+#endif
+
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
new file mode 100644
index 000000000000..68e8393a455f
--- /dev/null
+++ b/drivers/mfd/da9052-core.c
@@ -0,0 +1,521 @@
+/*
+ * da9052-core.c -- Device access for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ * 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/device.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define SUCCESS 0
+#define FAILURE 1
+
+struct da9052_eh_nb eve_nb_array[EVE_CNT];
+static struct da9052_ssc_ops ssc_ops;
+struct mutex manconv_lock;
+static struct semaphore eve_nb_array_lock;
+
+void da9052_lock(struct da9052 *da9052)
+{
+ mutex_lock(&da9052->ssc_lock);
+}
+EXPORT_SYMBOL(da9052_lock);
+
+void da9052_unlock(struct da9052 *da9052)
+{
+ mutex_unlock(&da9052->ssc_lock);
+}
+EXPORT_SYMBOL(da9052_unlock);
+
+int da9052_ssc_write(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg)
+{
+ int ret = 0;
+
+ /* Reg address should be a valid address on PAGE0 or PAGE1 */
+ if ((sscmsg->addr < DA9052_PAGE0_REG_START) ||
+ (sscmsg->addr > DA9052_PAGE1_REG_END) ||
+ ((sscmsg->addr > DA9052_PAGE0_REG_END) &&
+ (sscmsg->addr < DA9052_PAGE1_REG_START)))
+ return INVALID_REGISTER;
+
+ ret = ssc_ops.write(da9052, sscmsg);
+
+ /* Update local cache if required */
+ if (!ret) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg->addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg->addr].val = sscmsg->data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg->addr].status = VALID;
+ }
+ }
+
+ return ret;
+}
+
+int da9052_ssc_read(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg)
+{
+ int ret = 0;
+
+ /* Reg addr should be a valid address on PAGE0 or PAGE1 */
+ if ((sscmsg->addr < DA9052_PAGE0_REG_START) ||
+ (sscmsg->addr > DA9052_PAGE1_REG_END) ||
+ ((sscmsg->addr > DA9052_PAGE0_REG_END) &&
+ (sscmsg->addr < DA9052_PAGE1_REG_START)))
+ return INVALID_REGISTER;
+
+ /*
+ * Check if this is a Non-volatile register, if yes then return value -
+ * from cache instead of actual reading from hardware. Before reading -
+ * cache entry, make sure that the entry is valid
+ */
+ /* The read request is for Non-volatile register */
+ /* Check if we have valid cached value for this */
+ if (da9052->ssc_cache[sscmsg->addr].status == VALID) {
+ /* We have valid cached value, copy this value */
+ sscmsg->data = da9052->ssc_cache[sscmsg->addr].val;
+
+ return 0;
+ }
+
+ ret = ssc_ops.read(da9052, sscmsg);
+
+ /* Update local cache if required */
+ if (!ret) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg->addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg->addr].val = sscmsg->data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg->addr].status = VALID;
+ }
+ }
+
+ return ret;
+}
+
+int da9052_ssc_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int ret = 0;
+ int cnt = 0;
+
+ /* Check request size */
+ if (msg_no > MAX_READ_WRITE_CNT)
+ return -EIO;
+
+ ret = ssc_ops.write_many(da9052, sscmsg, msg_no);
+ /* Update local cache, if required */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg[cnt].addr].type != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg[cnt].addr].val =
+ sscmsg[cnt].data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg[cnt].addr].status = VALID;
+ }
+ }
+ return ret;
+}
+
+int da9052_ssc_read_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int ret = 0;
+ int cnt = 0;
+
+ /* Check request size */
+ if (msg_no > MAX_READ_WRITE_CNT)
+ return -EIO;
+
+ ret = ssc_ops.read_many(da9052, sscmsg, msg_no);
+ /* Update local cache, if required */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ /* Check if this register is Non-volatile*/
+ if (da9052->ssc_cache[sscmsg[cnt].addr].type
+ != VOLATILE) {
+ /* Update value */
+ da9052->ssc_cache[sscmsg[cnt].addr].val =
+ sscmsg[cnt].data;
+ /* Make this cache entry valid */
+ da9052->ssc_cache[sscmsg[cnt].addr].status = VALID;
+ }
+ }
+ return ret;
+}
+
+static irqreturn_t da9052_eh_isr(int irq, void *dev_id)
+{
+ struct da9052 *da9052 = dev_id;
+ /* Schedule work to be done */
+ schedule_work(&da9052->eh_isr_work);
+ /* Disable IRQ */
+ disable_irq_nosync(da9052->irq);
+ return IRQ_HANDLED;
+}
+
+int eh_register_nb(struct da9052 *da9052, struct da9052_eh_nb *nb)
+{
+
+ if (nb == NULL) {
+ printk(KERN_INFO "EH REGISTER FUNCTION FAILED\n");
+ return -EINVAL;
+ }
+
+ if (nb->eve_type >= EVE_CNT) {
+ printk(KERN_INFO "Invalid DA9052 Event Type\n");
+ return -EINVAL;
+ }
+
+ /* Initialize list head inside notifier block */
+ INIT_LIST_HEAD(&nb->nb_list);
+
+ /* Acquire NB array lock */
+ if (down_interruptible(&eve_nb_array_lock))
+ return -EAGAIN;
+
+ /* Add passed NB to corresponding EVENT list */
+ list_add_tail(&nb->nb_list, &(eve_nb_array[nb->eve_type].nb_list));
+
+ /* Release NB array lock */
+ up(&eve_nb_array_lock);
+
+ return 0;
+}
+
+int eh_unregister_nb(struct da9052 *da9052, struct da9052_eh_nb *nb)
+{
+
+ if (nb == NULL)
+ return -EINVAL;
+
+ /* Acquire nb array lock */
+ if (down_interruptible(&eve_nb_array_lock))
+ return -EAGAIN;
+
+ /* Remove passed NB from list */
+ list_del_init(&(nb->nb_list));
+
+ /* Release NB array lock */
+ up(&eve_nb_array_lock);
+
+ return 0;
+}
+
+static int process_events(struct da9052 *da9052, int events_sts)
+{
+
+ int cnt = 0;
+ int tmp_events_sts = 0;
+ unsigned char event = 0;
+
+ struct list_head *ptr;
+ struct da9052_eh_nb *nb_ptr;
+
+ /* Now we have retrieved all events, process them one by one */
+ //for (cnt = 0; cnt < PRIO_CNT; cnt++) {
+ for (cnt = 0; cnt < EVE_CNT; cnt++) {
+ /*
+ * Starting with highest priority event,
+ * traverse through all event
+ */
+ tmp_events_sts = events_sts;
+
+ /* Find the event associated with higher priority */
+
+ /* KPIT NOTE: This is commented as we are not using prioritization */
+ // event = eve_prio_map[cnt];
+ event = cnt;
+
+ /* Check if interrupt is received for this event */
+ /* KPIT : event is commneted to not use prioritization */
+ //if (!((tmp_events_sts >> event) & 0x1)) {
+ if (!((tmp_events_sts >> cnt) & 0x1))
+ /* Event bit is not set for this event */
+ /* Move to next priority event */
+ continue;
+
+ if (event == PEN_DOWN_EVE) {
+ if (list_empty(&(eve_nb_array[event].nb_list)))
+ continue;
+ }
+ /* Event bit is set, execute all registered call backs */
+ if (down_interruptible(&eve_nb_array_lock)){
+ printk(KERN_CRIT "Can't acquire eve_nb_array_lock \n");
+ return -EIO;
+ }
+
+ list_for_each(ptr, &(eve_nb_array[event].nb_list)) {
+ /*
+ * nb_ptr will point to the structure in which
+ * nb_list is embedded
+ */
+ nb_ptr = list_entry(ptr, struct da9052_eh_nb, nb_list);
+ nb_ptr->call_back(nb_ptr, events_sts);
+ }
+ up(&eve_nb_array_lock);
+ }
+ return 0;
+}
+
+void eh_workqueue_isr(struct work_struct *work)
+{
+ struct da9052 *da9052 =
+ container_of(work, struct da9052, eh_isr_work);
+
+ struct da9052_ssc_msg eve_data[4];
+ int events_sts, ret;
+ unsigned char cnt = 0;
+
+ /* nIRQ is asserted, read event registeres to know what happened */
+ events_sts = 0;
+
+ /* Prepare ssc message to read all four event registers */
+ for (cnt = 0; cnt < 4; cnt++) {
+ eve_data[cnt].addr = (DA9052_EVENTA_REG + cnt);
+ eve_data[cnt].data = 0;
+ }
+
+ /* Now read all event registers */
+ da9052_lock(da9052);
+
+ ret = da9052_ssc_read_many(da9052, eve_data, 4);
+ if (ret) {
+ enable_irq(da9052->irq);
+ da9052_unlock(da9052);
+ return;
+ }
+
+ /* Collect all events */
+ for (cnt = 0; cnt < 4; cnt++)
+ events_sts |= (eve_data[cnt].data << (8 * cnt));
+
+ /* Check if we really got any event */
+ if (events_sts == 0) {
+ enable_irq(da9052->irq);
+ da9052_unlock(da9052);
+ return;
+ }
+ da9052_unlock(da9052);
+
+ /* Process all events occurred */
+ process_events(da9052, events_sts);
+
+ da9052_lock(da9052);
+ /* Now clear EVENT registers */
+ for (cnt = 0; cnt < 4; cnt++) {
+ if (eve_data[cnt].data) {
+ ret = da9052_ssc_write(da9052, &eve_data[cnt]);
+ if (ret) {
+ enable_irq(da9052->irq);
+ da9052_unlock(da9052);
+ return;
+ }
+ }
+ }
+ da9052_unlock(da9052);
+
+ /*
+ * This delay is necessary to avoid hardware fake interrupts
+ * from DA9052.
+ */
+ udelay(50);
+ /* Enable HOST interrupt */
+ enable_irq(da9052->irq);
+}
+
+static void da9052_eh_restore_irq(struct da9052 *da9052)
+{
+ /* Put your platform and board specific code here */
+ free_irq(da9052->irq, NULL);
+}
+
+static int da9052_add_subdevice_pdata(struct da9052 *da9052,
+ const char *name, void *pdata, size_t pdata_size)
+{
+ struct mfd_cell cell = {
+ .name = name,
+ .platform_data = pdata,
+ .data_size = pdata_size,
+ };
+ return mfd_add_devices(da9052->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int da9052_add_subdevice(struct da9052 *da9052, const char *name)
+{
+ return da9052_add_subdevice_pdata(da9052, name, NULL, 0);
+}
+
+static int add_da9052_devices(struct da9052 *da9052)
+{
+ s32 ret = 0;
+ struct da9052_platform_data *pdata = da9052->dev->platform_data;
+ struct da9052_leds_platform_data leds_data = {
+ .num_leds = pdata->led_data->num_leds,
+ .led = pdata->led_data->led,
+ };
+ struct da9052_regulator_platform_data regulator_pdata = {
+ .regulators = pdata->regulators,
+ };
+
+ struct da9052_tsi_platform_data tsi_data = *(pdata->tsi_data);
+ struct da9052_bat_platform_data bat_data = *(pdata->bat_data);
+
+ if (pdata && pdata->init) {
+ ret = pdata->init(da9052);
+ if (ret != 0)
+ return ret;
+ } else
+ pr_err("No platform initialisation supplied\n");
+ ret = da9052_add_subdevice(da9052, "da9052-rtc");
+ if (ret)
+ return ret;
+ ret = da9052_add_subdevice(da9052, "da9052-onkey");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-1");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-2");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "WLED-3");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "da9052-adc");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice(da9052, "da9052-wdt");
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-leds",
+ &leds_data, sizeof(leds_data));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-regulator",
+ &regulator_pdata, sizeof(regulator_pdata));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-tsi",
+ &tsi_data, sizeof(tsi_data));
+ if (ret)
+ return ret;
+
+ ret = da9052_add_subdevice_pdata(da9052, "da9052-bat",
+ &bat_data, sizeof(bat_data));
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int da9052_ssc_init(struct da9052 *da9052)
+{
+ int cnt;
+ struct da9052_platform_data *pdata;
+ struct da9052_ssc_msg ssc_msg;
+
+ /* Initialize eve_nb_array */
+ for (cnt = 0; cnt < EVE_CNT; cnt++)
+ INIT_LIST_HEAD(&(eve_nb_array[cnt].nb_list));
+
+ /* Initialize mutex required for ADC Manual read */
+ mutex_init(&manconv_lock);
+
+ /* Initialize NB array lock */
+ init_MUTEX(&eve_nb_array_lock);
+
+ /* Assign the read-write function pointers */
+ da9052->read = da9052_ssc_read;
+ da9052->write = da9052_ssc_write;
+ da9052->read_many = da9052_ssc_read_many;
+ da9052->write_many = da9052_ssc_write_many;
+
+ if (SPI == da9052->connecting_device && ssc_ops.write == NULL) {
+ /* Assign the read/write pointers to SPI/read/write */
+ ssc_ops.write = da9052_spi_write;
+ ssc_ops.read = da9052_spi_read;
+ ssc_ops.write_many = da9052_spi_write_many;
+ ssc_ops.read_many = da9052_spi_read_many;
+ }
+ else if (I2C == da9052->connecting_device && ssc_ops.write == NULL) {
+ /* Assign the read/write pointers to SPI/read/write */
+ ssc_ops.write = da9052_i2c_write;
+ ssc_ops.read = da9052_i2c_read;
+ ssc_ops.write_many = da9052_i2c_write_many;
+ ssc_ops.read_many = da9052_i2c_read_many;
+ } else
+ return -1;
+
+ /* Assign the EH notifier block register/de-register functions */
+ da9052->register_event_notifier = eh_register_nb;
+ da9052->unregister_event_notifier = eh_unregister_nb;
+
+ /* Initialize ssc lock */
+ mutex_init(&da9052->ssc_lock);
+
+ pdata = da9052->dev->platform_data;
+ add_da9052_devices(da9052);
+
+ INIT_WORK(&da9052->eh_isr_work, eh_workqueue_isr);
+
+ ssc_msg.addr = DA9052_IRQMASKA_REG;
+ ssc_msg.data = 0xff;
+ da9052->write(da9052, &ssc_msg);
+ ssc_msg.addr = DA9052_IRQMASKC_REG;
+ ssc_msg.data = 0xff;
+ da9052->write(da9052, &ssc_msg);
+ if (request_irq(da9052->irq, da9052_eh_isr, IRQ_TYPE_LEVEL_LOW,
+ DA9052_EH_DEVICE_NAME, da9052))
+ return -EIO;
+ enable_irq_wake(da9052->irq);
+
+ return 0;
+}
+
+void da9052_ssc_exit(struct da9052 *da9052)
+{
+ printk(KERN_INFO "DA9052: Unregistering SSC device.\n");
+ mutex_destroy(&manconv_lock);
+ /* Restore IRQ line */
+ da9052_eh_restore_irq(da9052);
+ free_irq(da9052->irq, NULL);
+ mutex_destroy(&da9052->ssc_lock);
+ mutex_destroy(&da9052->eve_nb_lock);
+ return;
+}
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 MFD Core");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_DEVICE_NAME);
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
new file mode 100644
index 000000000000..4f050896d962
--- /dev/null
+++ b/drivers/mfd/da9052-i2c.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * da9052-i2c.c: I2C SSC (Synchronous Serial Communication) driver for DA9052
+ */
+
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/i2c.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+static struct da9052 *da9052_i2c;
+
+#define I2C_CONNECTED 0
+
+static int da9052_i2c_is_connected(void)
+{
+
+ struct da9052_ssc_msg msg;
+
+ //printk("Entered da9052_i2c_is_connected.............\n");
+
+ msg.addr = DA9052_INTERFACE_REG;
+
+ /* Test spi connectivity by performing read of the GPIO_0-1 register */
+ if ( 0 != da9052_i2c_read(da9052_i2c, &msg)) {
+ printk("da9052_i2c_is_connected - i2c read failed.............\n");
+ return -1;
+ }
+ else {
+ printk("da9052_i2c_is_connected - i2c read success..............\n");
+ return 0;
+ }
+
+}
+
+static int __devinit da9052_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter;
+ // printk("\n\tEntered da9052_i2c_is_probe.............\n");
+
+ da9052_i2c = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+
+ if (!da9052_i2c)
+ return -ENOMEM;
+
+ /* Get the bus driver handler */
+ adapter = to_i2c_adapter(client->dev.parent);
+
+ /* Check i2c bus driver supports byte data transfer */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_info(&client->dev,\
+ "Error in %s:i2c_check_functionality\n", __func__);
+ return -ENODEV;;
+ }
+
+ /* Store handle to i2c client */
+ da9052_i2c->i2c_client = client;
+ da9052_i2c->irq = client->irq;
+
+ da9052_i2c->dev = &client->dev;
+
+ /* Initialize i2c data structure here*/
+ da9052_i2c->adapter = adapter;
+
+ /* host i2c driver looks only first 7 bits for the slave address */
+ da9052_i2c->slave_addr = DA9052_I2C_ADDR >> 1;
+
+ /* Store the i2c client data */
+ i2c_set_clientdata(client, da9052_i2c);
+
+ /* Validate I2C connectivity */
+ if ( I2C_CONNECTED == da9052_i2c_is_connected()) {
+ /* I2C is connected */
+ da9052_i2c->connecting_device = I2C;
+ if( 0!= da9052_ssc_init(da9052_i2c) )
+ return -ENODEV;
+ }
+ else {
+ return -ENODEV;
+ }
+
+ //printk("Exiting da9052_i2c_probe.....\n");
+
+ return 0;
+}
+
+static int da9052_i2c_remove(struct i2c_client *client)
+{
+
+ struct da9052 *da9052 = i2c_get_clientdata(client);
+
+ mfd_remove_devices(da9052->dev);
+ kfree(da9052);
+ return 0;
+}
+
+int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+ struct i2c_msg i2cmsg;
+ unsigned char buf[2] = {0};
+ int ret = 0;
+
+ /* Copy the ssc msg to local character buffer */
+ buf[0] = msg->addr;
+ buf[1] = msg->data;
+
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg.addr = da9052->slave_addr;
+ i2cmsg.len = 2;
+ i2cmsg.buf = buf;
+
+ /* To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "_%s:master_xfer Failed!!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ /*Get the da9052_i2c client details*/
+ unsigned char buf[2] = {0, 0};
+ struct i2c_msg i2cmsg[2];
+ int ret = 0;
+
+ /* Copy SSC Msg to local character buffer */
+ buf[0] = msg->addr;
+
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg[0].addr = da9052->slave_addr ;
+ i2cmsg[0].len = 1;
+ i2cmsg[0].buf = &buf[0];
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg[0].flags = 0;
+
+ /* Read the data from da9052*/
+ /*Construct a i2c msg for a da9052 driver ssc message request */
+ i2cmsg[1].addr = da9052->slave_addr ;
+ i2cmsg[1].len = 1;
+ i2cmsg[1].buf = &buf[1];
+
+ /*To read the data on I2C set flag to I2C_M_RD */
+ i2cmsg[1].flags = I2C_M_RD;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, i2cmsg, 2);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - %s:master_xfer Failed!!\n", __func__);
+ return ret;
+ }
+
+ msg->data = *i2cmsg[1].buf;
+
+ return 0;
+}
+
+int da9052_i2c_write_many(struct da9052 *da9052,
+ struct da9052_ssc_msg *sscmsg, int msg_no)
+{
+
+ struct i2c_msg i2cmsg;
+ unsigned char data_buf[MAX_READ_WRITE_CNT+1];
+ struct da9052_ssc_msg ctrlb_msg;
+ struct da9052_ssc_msg *msg_queue = sscmsg;
+ int ret = 0;
+ /* Flag to check if requested registers are contiguous */
+ unsigned char cont_data = 1;
+ unsigned char cnt = 0;
+
+ /* Check if requested registers are contiguous */
+ for (cnt = 1; cnt < msg_no; cnt++) {
+ if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
+ /* Difference is not 1, i.e. non-contiguous registers */
+ cont_data = 0;
+ break;
+ }
+ }
+
+ if (cont_data == 0) {
+ /* Requested registers are non-contiguous */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ ret = da9052->write(da9052, &msg_queue[cnt]);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+ }
+ /*
+ * Requested registers are contiguous
+ * or PAGE WRITE sequence of I2C transactions is as below
+ * (slave_addr + reg_addr + data_1 + data_2 + ...)
+ * First read current WRITE MODE via CONTROL_B register of DA9052
+ */
+ ctrlb_msg.addr = DA9052_CONTROLB_REG;
+ ctrlb_msg.data = 0x0;
+ ret = da9052->read(da9052, &ctrlb_msg);
+
+ if (ret != 0)
+ return ret;
+
+ /* Check if PAGE WRITE mode is set */
+ if (ctrlb_msg.data & DA9052_CONTROLB_WRITEMODE) {
+ /* REPEAT WRITE mode is configured */
+ /* Now set DA9052 into PAGE WRITE mode */
+ ctrlb_msg.data &= ~DA9052_CONTROLB_WRITEMODE;
+ ret = da9052->write(da9052, &ctrlb_msg);
+
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Put first register address */
+ data_buf[0] = msg_queue[0].addr;
+
+ for (cnt = 0; cnt < msg_no; cnt++)
+ data_buf[cnt+1] = msg_queue[cnt].data;
+
+ /* Construct a i2c msg for PAGE WRITE */
+ i2cmsg.addr = da9052->slave_addr ;
+ /* First register address + all data*/
+ i2cmsg.len = (msg_no + 1);
+ i2cmsg.buf = data_buf;
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_i2c_read_many(struct da9052 *da9052,
+ struct da9052_ssc_msg *sscmsg, int msg_no)
+{
+
+ struct i2c_msg i2cmsg;
+ unsigned char data_buf[MAX_READ_WRITE_CNT];
+ struct da9052_ssc_msg *msg_queue = sscmsg;
+ int ret = 0;
+ /* Flag to check if requested registers are contiguous */
+ unsigned char cont_data = 1;
+ unsigned char cnt = 0;
+
+ /* Check if requested registers are contiguous */
+ for (cnt = 1; cnt < msg_no; cnt++) {
+ if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
+ /* Difference is not 1, i.e. non-contiguous registers */
+ cont_data = 0;
+ break;
+ }
+ }
+
+ if (cont_data == 0) {
+ /* Requested registers are non-contiguous */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ ret = da9052->read(da9052, &msg_queue[cnt]);
+ if (ret != 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "Error in %s", __func__);
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ /*
+ * We want to perform PAGE READ via I2C
+ * For PAGE READ sequence of I2C transactions is as below
+ * (slave_addr + reg_addr) + (slave_addr + data_1 + data_2 + ...)
+ */
+ /* Copy address of first register */
+ data_buf[0] = msg_queue[0].addr;
+
+ /* Construct a i2c msg for first transaction of PAGE READ i.e. write */
+ i2cmsg.addr = da9052->slave_addr ;
+ i2cmsg.len = 1;
+ i2cmsg.buf = data_buf;
+
+ /*To write the data on I2C set flag to zero */
+ i2cmsg.flags = 0;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ /* Now Read the data from da9052 */
+ /* Construct a i2c msg for second transaction of PAGE READ i.e. read */
+ i2cmsg.addr = da9052->slave_addr ;
+ i2cmsg.len = msg_no;
+ i2cmsg.buf = data_buf;
+
+ /*To read the data on I2C set flag to I2C_M_RD */
+ i2cmsg.flags = I2C_M_RD;
+
+ /* Start the i2c transfer by calling host i2c driver function */
+ ret = i2c_transfer(da9052->adapter,
+ &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ return ret;
+ }
+
+ /* Gather READ data */
+ for (cnt = 0; cnt < msg_no; cnt++)
+ sscmsg[cnt].data = data_buf[cnt];
+
+ return 0;
+}
+
+static struct i2c_device_id da9052_ssc_id[] = {
+ { DA9052_SSC_I2C_DEVICE_NAME, 0},
+ {}
+};
+
+static struct i2c_driver da9052_i2c_driver = {
+ .driver = {
+ .name = DA9052_SSC_I2C_DEVICE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_i2c_probe,
+ .remove = da9052_i2c_remove,
+ .id_table = da9052_ssc_id,
+};
+
+static int __init da9052_i2c_init(void)
+{
+ int ret = 0;
+ // printk("\n\nEntered da9052_i2c_init................\n\n");
+ ret = i2c_add_driver(&da9052_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register %s\n", DA9052_SSC_I2C_DEVICE_NAME);
+ return ret;
+ }
+ return 0;
+}
+subsys_initcall(da9052_i2c_init);
+
+static void __exit da9052_i2c_exit(void)
+{
+ i2c_del_driver(&da9052_i2c_driver);
+}
+module_exit(da9052_i2c_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_I2C_DEVICE_NAME);
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
new file mode 100644
index 000000000000..9dd90c2d19f9
--- /dev/null
+++ b/drivers/mfd/da9052-spi.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * da9052-spi.c: SPI SSC (Synchronous Serial Communication) driver for DA9052
+ */
+
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+
+struct da9052 *da9052_spi;
+
+#define SPI_CONNECTED 0
+
+static int da9052_spi_is_connected(void)
+{
+
+ struct da9052_ssc_msg msg;
+
+ //printk("Entered da9052_spi_is_connected.............\n");
+
+ msg.addr = DA9052_INTERFACE_REG;
+
+ /* Test spi connectivity by performing read of the GPIO_0-1 register and then verify the read value*/
+ if ( 0 != da9052_spi_read(da9052_spi, &msg)) {
+ printk("da9052_spi_is_connected - spi read failed.............\n");
+ return -1;
+ }
+ else if( 0x88 != msg.data ){
+ printk("da9052_spi_is_connected - spi read failed. Msg data =%x ..............\n",msg.data);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+static int da9052_spi_probe(struct spi_device *spi)
+{
+ //printk("\n\tEntered da9052_spi_probe.....\n");
+
+ da9052_spi = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+
+ if (!da9052_spi)
+ return -ENOMEM;
+
+
+ spi->mode = SPI_MODE_0 | SPI_CPOL;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ da9052_spi->dev = &spi->dev;
+
+ da9052_spi->spi_dev = spi;
+
+ /*
+ * Allocate memory for RX/TX bufferes used in single register read/write
+ */
+ da9052_spi->spi_rx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
+ if (!da9052_spi->spi_rx_buf)
+ return -ENOMEM;
+
+ da9052_spi->spi_tx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
+ if (!da9052_spi->spi_tx_buf)
+ return -ENOMEM;
+
+ da9052_spi->spi_active_page = PAGECON_0;
+ da9052_spi->rw_pol = 1;
+
+
+ dev_set_drvdata(&spi->dev, da9052_spi);
+
+
+ /* Validate SPI connectivity */
+ if ( SPI_CONNECTED == da9052_spi_is_connected()) {
+ /* SPI is connected */
+ da9052_spi->connecting_device = SPI;
+ if( 0 != da9052_ssc_init(da9052_spi) )
+ return -ENODEV;
+ }
+ else {
+ return -ENODEV;
+ }
+
+ //printk("Exiting da9052_spi_probe.....\n");
+
+ return 0;
+}
+
+static int da9052_spi_remove(struct spi_device *spi)
+{
+ struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
+
+ printk("Entered da9052_spi_remove()\n");
+ if(SPI == da9052->connecting_device ) {
+ da9052_ssc_exit(da9052);
+ }
+ mfd_remove_devices(&spi->dev);
+ kfree(da9052->spi_rx_buf);
+ kfree(da9052->spi_tx_buf);
+ kfree(da9052);
+ return 0;
+}
+
+static struct spi_driver da9052_spi_driver = {
+ .driver = {
+ .name = DA9052_SSC_SPI_DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9052_spi_probe,
+ .remove = __devexit_p(da9052_spi_remove),
+};
+
+
+static int da9052_spi_set_page(struct da9052 *da9052, unsigned char page)
+{
+
+ struct da9052_ssc_msg sscmsg;
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret = 0;
+
+ printk("Entered da9052_spi_set_page.....\n");
+ if ((page != PAGECON_0) && ((page != PAGECON_128)))
+ return INVALID_PAGE;
+
+ /* Current configuration is PAGE-0 and write request for PAGE-1 */
+ /* set register address */
+ sscmsg.addr = DA9052_PAGECON0_REG;
+ /* set value */
+ sscmsg.data = page;
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (!da9052->rw_pol) {
+ /* We need to set 0th bit for write operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ printk("da9052_spi_set_page - Calling spi_message_init.....\n");
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = sscmsg.data;
+
+ printk("da9052_spi_set_page - Calling spi_message_add_tail.....\n");
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ printk("da9052_spi_set_page - Calling spi_sync.....\n");
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ if (ret == 0) {
+ /* Active Page set successfully */
+ da9052->spi_active_page = page;
+ return 0;
+ } else {
+ /* Error in setting Active Page */
+ return ret;
+ }
+
+ return 0;
+}
+
+int da9052_spi_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret;
+
+ /*
+ * We need a seperate copy of da9052_ssc_msg so that caller's
+ * copy remains intact
+ */
+ struct da9052_ssc_msg sscmsg;
+
+ /* Copy callers data in to our local copy */
+ sscmsg.addr = msg->addr;
+ sscmsg.data = msg->data;
+
+ if ((sscmsg.addr > PAGE_0_END) &&
+ (da9052->spi_active_page == PAGECON_0)) {
+ /*
+ * Current configuration is PAGE-0 and write request
+ * for PAGE-1
+ */
+ da9052_spi_set_page(da9052, PAGECON_128);
+ /* Set register address accordindly */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ } else if ((sscmsg.addr < PAGE_1_START) &&
+ (da9052->spi_active_page == PAGECON_128)) {
+ /*
+ * Current configuration is PAGE-1 and write request
+ * for PAGE-0
+ */
+ da9052_spi_set_page(da9052, PAGECON_0);
+ } else if (sscmsg.addr > PAGE_0_END) {
+ /*
+ * Current configuration is PAGE-1 and write request
+ * for PAGE-1. Just need to adjust register address
+ */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ }
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (!da9052->rw_pol) {
+ /* We need to set 0th bit for write operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = sscmsg.data;
+
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ return ret;
+}
+
+int da9052_spi_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int cnt,ret=0;
+
+ for(cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
+ ret = da9052_ssc_write(da9052,sscmsg);
+ if(ret != 0)
+ {
+ printk("Error in %s", __FUNCTION__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int da9052_spi_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
+{
+
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int ret;
+
+ /*
+ * We need a seperate copy of da9052_ssc_msg so that
+ * caller's copy remains intact
+ */
+ struct da9052_ssc_msg sscmsg;
+
+
+ /* Copy callers data in to our local copy */
+ sscmsg.addr = msg->addr;
+ sscmsg.data = msg->data;
+
+ if ((sscmsg.addr > PAGE_0_END) &&
+ (da9052->spi_active_page == PAGECON_0)) {
+ /*
+ * Current configuration is PAGE-0 and
+ * read request for PAGE-1
+ */
+ printk("da9052_spi_read - if PAGECON_128.....\n");
+ da9052_spi_set_page(da9052, PAGECON_128);
+ /* Set register address accordindly */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ } else if ((sscmsg.addr < PAGE_1_START) &&
+ (da9052->spi_active_page == PAGECON_128)) {
+ /*
+ * Current configuration is PAGE-1 and
+ * write request for PAGE-0
+ */
+ printk("da9052_spi_read - if PAGECON_0.....\n");
+ da9052_spi_set_page(da9052, PAGECON_0);
+ } else if (sscmsg.addr > PAGE_0_END) {
+ /*
+ * Current configuration is PAGE-1 and write
+ * request for PAGE-1
+ * Just need to adjust register address
+ */
+ sscmsg.addr = (sscmsg.addr - PAGE_1_START);
+ }
+
+ /* Check value of R/W_POL bit of INTERFACE register */
+ if (da9052->rw_pol) {
+ /* We need to set 0th bit for read operation */
+ sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
+ } else {
+ /* We need to reset 0th bit for write operation */
+ sscmsg.addr = (sscmsg.addr << 1);
+ }
+
+ /* SMDK-6410 host SPI driver specific stuff */
+
+ /* Build our spi message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+
+ xfer.len = 2;
+ xfer.tx_buf = da9052->spi_tx_buf;
+ xfer.rx_buf = da9052->spi_rx_buf;
+
+ da9052->spi_tx_buf[0] = sscmsg.addr;
+ da9052->spi_tx_buf[1] = 0xff;
+
+ da9052->spi_rx_buf[0] = 0;
+ da9052->spi_rx_buf[1] = 0;
+
+ spi_message_add_tail(&xfer, &message);
+
+ /* Now, do the i/o */
+ ret = spi_sync(da9052->spi_dev, &message);
+
+ if (ret == 0) {
+ /* Update read value in callers copy */
+ msg->data = da9052->spi_rx_buf[1];
+ return 0;
+ } else {
+ return ret;
+ }
+
+
+ return 0;
+}
+
+int da9052_spi_read_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
+ int msg_no)
+{
+ int cnt,ret=0;
+
+ for(cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
+ ret = da9052_ssc_read(da9052,sscmsg);
+ if(ret != 0)
+ {
+ printk("Error in %s", __FUNCTION__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int __init da9052_spi_init(void)
+{
+ int ret = 0;
+ //printk("Entered da9052_spi_init.....\n");
+ ret = spi_register_driver(&da9052_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register %s\n", DA9052_SSC_SPI_DEVICE_NAME);
+ return ret;
+ }
+ return 0;
+}
+module_init(da9052_spi_init);
+
+static void __exit da9052_spi_exit(void)
+{
+ spi_unregister_driver(&da9052_spi_driver);
+}
+
+module_exit(da9052_spi_exit);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9052_SSC_SPI_DEVICE_NAME);
diff --git a/drivers/mfd/ltc3589-i2c.c b/drivers/mfd/ltc3589-i2c.c
new file mode 100644
index 000000000000..db0017e30827
--- /dev/null
+++ b/drivers/mfd/ltc3589-i2c.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @file ltc3589-i2c.c
+ * @brief This is the generic I2C driver for Linear 3589 PMIC.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ltc3589/core.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+static inline int ltc3589_i2c_read(struct ltc3589 *ltc, u8 reg)
+{
+ return i2c_smbus_read_byte_data(ltc->i2c_client, reg);
+}
+
+static inline int ltc3589_i2c_write(struct ltc3589 *ltc, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(ltc->i2c_client, reg, val);
+}
+
+static int ltc3589_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ltc3589 *ltc3589;
+ struct ltc3589_platform_data *plat_data = i2c->dev.platform_data;
+ int ret = 0;
+
+ ltc3589 = kzalloc(sizeof(struct ltc3589), GFP_KERNEL);
+ if (ltc3589 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ltc3589);
+ ltc3589->dev = &i2c->dev;
+ ltc3589->i2c_client = i2c;
+ ltc3589->read_dev = ltc3589_i2c_read;
+ ltc3589->write_dev = ltc3589_i2c_write;
+
+ if (ltc3589_i2c_read(ltc3589, 0x33) < 0) {
+ kfree(ltc3589);
+ return -ENODEV;
+ }
+
+ /* so far, we got matched chip on board */
+
+ ltc3589_client = i2c;
+ if (plat_data && plat_data->init) {
+ ret = plat_data->init(ltc3589);
+ if (ret != 0)
+ return -EINVAL;
+ }
+
+ dev_info(&i2c->dev, "Loaded\n");
+
+ return 0;
+}
+
+static int ltc3589_i2c_remove(struct i2c_client *i2c)
+{
+ struct ltc3589 *ltc3589 = i2c_get_clientdata(i2c);
+
+ kfree(ltc3589);
+ return 0;
+}
+
+static const struct i2c_device_id ltc3589_id[] = {
+ { "ltc3589", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3589_id);
+
+
+static struct i2c_driver ltc3589_i2c_driver = {
+ .driver = {
+ .name = "ltc3589",
+ .owner = THIS_MODULE,
+ },
+ .probe = ltc3589_i2c_probe,
+ .remove = ltc3589_i2c_remove,
+ .id_table = ltc3589_id,
+};
+
+static int __init ltc3589_i2c_init(void)
+{
+ return i2c_add_driver(&ltc3589_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(ltc3589_i2c_init);
+
+static void __exit ltc3589_i2c_exit(void)
+{
+ i2c_del_driver(&ltc3589_i2c_driver);
+}
+module_exit(ltc3589_i2c_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("I2C support for the Linear PMIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max17135-core.c b/drivers/mfd/max17135-core.c
new file mode 100644
index 000000000000..0be007fbcb40
--- /dev/null
+++ b/drivers/mfd/max17135-core.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*!
+ * @file pmic/core/max17135.c
+ * @brief This file contains MAX17135 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/max17135.h>
+#include <asm/mach-types.h>
+
+struct i2c_client *max17135_client;
+
+static const unsigned short normal_i2c[] = {0x48, I2C_CLIENT_END};
+
+int max17135_reg_read(int reg_num, unsigned int *reg_val)
+{
+ int result;
+
+ if (max17135_client == NULL)
+ return PMIC_ERROR;
+
+ if ((reg_num == REG_MAX17135_EXT_TEMP) ||
+ (reg_num == REG_MAX17135_INT_TEMP)) {
+ result = i2c_smbus_read_word_data(max17135_client, reg_num);
+ if (result < 0) {
+ dev_err(&max17135_client->dev,
+ "Unable to read MAX17135 register via I2C\n");
+ return PMIC_ERROR;
+ }
+ /* Swap bytes for dword read */
+ result = (result >> 8) | ((result & 0xFF) << 8);
+ } else {
+ result = i2c_smbus_read_byte_data(max17135_client, reg_num);
+ if (result < 0) {
+ dev_err(&max17135_client->dev,
+ "Unable to read MAX17135 register via I2C\n");
+ return PMIC_ERROR;
+ }
+ }
+
+ *reg_val = result;
+ return PMIC_SUCCESS;
+}
+
+int max17135_reg_write(int reg_num, const unsigned int reg_val)
+{
+ int result;
+
+ if (max17135_client == NULL)
+ return PMIC_ERROR;
+
+ result = i2c_smbus_write_byte_data(max17135_client, reg_num, reg_val);
+ if (result < 0) {
+ dev_err(&max17135_client->dev,
+ "Unable to write MAX17135 register via I2C\n");
+ return PMIC_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+static int max17135_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max17135 *max17135;
+ struct max17135_platform_data *pdata = client->dev.platform_data;
+ int ret = 0;
+
+ if (!pdata || !pdata->init)
+ return -ENODEV;
+
+ /* Create the PMIC data structure */
+ max17135 = kzalloc(sizeof(struct max17135), GFP_KERNEL);
+ if (max17135 == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+
+ /* Initialize the PMIC data structure */
+ i2c_set_clientdata(client, max17135);
+ max17135->dev = &client->dev;
+ max17135->i2c_client = client;
+
+ max17135_client = client;
+
+ if (pdata && pdata->init) {
+ ret = pdata->init(max17135);
+ if (ret != 0)
+ goto err;
+ }
+
+ dev_info(&client->dev, "PMIC MAX17135 for eInk display\n");
+
+ return ret;
+err:
+ kfree(max17135);
+
+ return ret;
+}
+
+
+static int max17135_remove(struct i2c_client *i2c)
+{
+ struct max17135 *max17135 = i2c_get_clientdata(i2c);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max17135->pdev); i++)
+ platform_device_unregister(max17135->pdev[i]);
+
+ kfree(max17135);
+
+ return 0;
+}
+
+static int max17135_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return 0;
+}
+
+static int max17135_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max17135_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 chip_rev, chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* detection */
+ if (i2c_smbus_read_byte_data(client,
+ REG_MAX17135_PRODUCT_REV) != 0) {
+ dev_err(&adapter->dev,
+ "Max17135 PMIC not found!\n");
+ return -ENODEV;
+ }
+
+ /* identification */
+ chip_rev = i2c_smbus_read_byte_data(client,
+ REG_MAX17135_PRODUCT_REV);
+ chip_id = i2c_smbus_read_byte_data(client,
+ REG_MAX17135_PRODUCT_ID);
+
+ if (chip_rev != 0x00 || chip_id != 0x4D) { /* identification failed */
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, "
+ "chip_id=0x%02X).\n", chip_rev, chip_id);
+ return -ENODEV;
+ }
+
+ strlcpy(info->type, "max17135_sensor", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static const struct i2c_device_id max17135_id[] = {
+ { "max17135", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max17135_id);
+
+
+static struct i2c_driver max17135_driver = {
+ .driver = {
+ .name = "max17135",
+ .owner = THIS_MODULE,
+ },
+ .probe = max17135_probe,
+ .remove = max17135_remove,
+ .suspend = max17135_suspend,
+ .resume = max17135_resume,
+ .id_table = max17135_id,
+ .detect = max17135_detect,
+ .address_list = &normal_i2c,
+};
+
+static int __init max17135_init(void)
+{
+ return i2c_add_driver(&max17135_driver);
+}
+
+static void __exit max17135_exit(void)
+{
+ i2c_del_driver(&max17135_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(max17135_init);
+module_exit(max17135_exit);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index b5807484b4c9..1ff6ddfd6aaf 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -721,7 +721,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
}
}
- wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+ /*mask gpio and rtc interrupt*/
+ wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x50);
wm8350_client_dev_register(wm8350, "wm8350-codec",
&(wm8350->codec.pdev));