aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd/da9052-i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/da9052-i2c.c')
-rw-r--r--drivers/mfd/da9052-i2c.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
new file mode 100644
index 00000000000..dba23f16188
--- /dev/null
+++ b/drivers/mfd/da9052-i2c.c
@@ -0,0 +1,379 @@
+/*
+ * 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/module.h>
+#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(KERN_DEBUG "da9052_i2c_is_connected - "\
+ "i2c read failed.............\n");
+ return -1;
+ } else {
+ printk(KERN_DEBUG "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);