summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJihoon Bang <jbang@nvidia.com>2011-10-11 17:15:30 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-10-13 12:58:49 -0700
commite8d056675c755d399b893b9dc126168178116ffd (patch)
tree3ef8222ac4f1fa902e38f7de5c5e0a5a000d19d1
parent6609c0ec7d52f7656e3c520e36ae836cbc63aa6a (diff)
i2c: add regulators to PCA954x
Add regulators to PCA954x in case PCA954x is controlled by programmable PMIC. Add two Vcc. One is for PCA954x itself and the other is for I2C bus. Bug 871860 Reviewed-on: http://git-master/r/54745 (cherry picked from commit 7beddf4e72024e6f140ed306e80a7e1d19d7f36b) Reviewed-on: http://git-master/r/55800 (cherry picked from commit 57138283d20301658ffe904577a72e9ea95b053e) Change-Id: Ic98f33d2b8b324b7539d9b5b000a5591c5f7f742 Reviewed-on: http://git-master/r/57415 Reviewed-by: Jihoon Bang <jbang@nvidia.com> Tested-by: Jihoon Bang <jbang@nvidia.com> Reviewed-by: Dan Willemsen <dwillemsen@nvidia.com>
-rw-r--r--drivers/i2c/muxes/pca954x.c101
1 files changed, 100 insertions, 1 deletions
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 6f8953664636..90fd47b54df5 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -41,6 +41,9 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/i2c/pca954x.h>
@@ -62,6 +65,8 @@ struct pca954x {
struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
u8 last_chan; /* last register value */
+ struct regulator *vcc_reg;
+ struct regulator *i2c_reg;
};
struct chip_desc {
@@ -123,6 +128,26 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
int ret = -ENODEV;
+ struct pca954x *data = i2c_get_clientdata(client);
+
+ /* Increase ref count for pca954x vcc */
+ if (data->vcc_reg) {
+ ret = regulator_enable(data->vcc_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc\n",
+ __func__);
+ goto vcc_regulator_failed;
+ }
+ }
+ /* Increase ref count for pca954x vcc_i2c */
+ if (data->i2c_reg) {
+ ret = regulator_enable(data->i2c_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc_i2c\n",
+ __func__);
+ goto i2c_regulator_failed;
+ }
+ }
if (adap->algo->master_xfer) {
struct i2c_msg msg;
@@ -142,6 +167,15 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
val, I2C_SMBUS_BYTE, &data);
}
+ /* Decrease ref count for pca954x vcc_i2c */
+ if (data->i2c_reg)
+ regulator_disable(data->i2c_reg);
+
+i2c_regulator_failed:
+ /* Decrease ref count for pca954x vcc */
+ if (data->vcc_reg)
+ regulator_disable(data->vcc_reg);
+vcc_regulator_failed:
return ret;
}
@@ -201,15 +235,74 @@ static int pca954x_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
+ /* Get regulator pointer for pca954x vcc */
+ data->vcc_reg = regulator_get(&client->dev, "vcc");
+ if (PTR_ERR(data->vcc_reg) == -ENODEV)
+ data->vcc_reg = NULL;
+ else if (IS_ERR(data->vcc_reg)) {
+ dev_err(&client->dev, "%s: failed to get vcc\n",
+ __func__);
+ ret = PTR_ERR(data->vcc_reg);
+ goto exit_free;
+ }
+ /* Get regulator pointer for pca954x vcc_i2c */
+ data->i2c_reg = regulator_get(&client->dev, "vcc_i2c");
+ if (PTR_ERR(data->i2c_reg) == -ENODEV)
+ data->i2c_reg = NULL;
+ else if (IS_ERR(data->i2c_reg)) {
+ dev_err(&client->dev, "%s: failed to get vcc_i2c\n",
+ __func__);
+ ret = PTR_ERR(data->i2c_reg);
+ regulator_put(data->vcc_reg);
+ goto exit_free;
+ }
+
+ /* Increase ref count for pca954x vcc */
+ if (data->vcc_reg) {
+ pr_info("%s: enable vcc\n", __func__);
+ ret = regulator_enable(data->vcc_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc\n",
+ __func__);
+ goto exit_regulator_put;
+ }
+ }
+ /* Increase ref count for pca954x vcc_i2c */
+ if (data->i2c_reg) {
+ pr_info("%s: enable vcc_i2c\n", __func__);
+ ret = regulator_enable(data->i2c_reg);
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to enable vcc_i2c\n",
+ __func__);
+ regulator_disable(data->vcc_reg);
+ goto exit_regulator_put;
+ }
+ }
+
+ /*
+ * Power-On Reset takes time.
+ * I2C is ready after Power-On Reset.
+ */
+ msleep(1);
+
/* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to disconnected state.
*/
if (i2c_smbus_write_byte(client, 0) < 0) {
dev_warn(&client->dev, "probe failed\n");
- goto exit_free;
+ regulator_disable(data->vcc_reg);
+ regulator_disable(data->i2c_reg);
+ goto exit_regulator_put;
}
+ /* Decrease ref count for pca954x vcc */
+ if (data->vcc_reg)
+ regulator_disable(data->vcc_reg);
+ /* Decrease ref count for pca954x vcc_i2c */
+ if (data->i2c_reg)
+ regulator_disable(data->i2c_reg);
+
data->type = id->driver_data;
data->last_chan = 0; /* force the first selection */
@@ -250,6 +343,9 @@ static int pca954x_probe(struct i2c_client *client,
virt_reg_failed:
for (num--; num >= 0; num--)
i2c_del_mux_adapter(data->virt_adaps[num]);
+exit_regulator_put:
+ regulator_put(data->vcc_reg);
+ regulator_put(data->i2c_reg);
exit_free:
kfree(data);
err:
@@ -270,6 +366,9 @@ static int pca954x_remove(struct i2c_client *client)
data->virt_adaps[i] = NULL;
}
+ regulator_put(data->i2c_reg);
+ regulator_put(data->vcc_reg);
+
kfree(data);
return 0;
}