diff options
author | Lee Jones <lee.jones@linaro.org> | 2013-01-23 09:38:15 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2013-01-23 09:39:19 -0500 |
commit | 3988043b0ee1104d4cca7c57bbc23b16ea798b6f (patch) | |
tree | 416daa30f175aa7bb2710919ea06753ae7260013 | |
parent | e41f39ea2a0e9ba32d6896c2cc38bfec880a0937 (diff) |
pm2301: LPN mode control support
The AC charger plug-in detection while booting causes I2C read
failure if AC charger is not connected. Now the LPN pin is enabled
for every PM2301 register access, which solves the issue.
Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r-- | drivers/power/pm2301_charger.c | 71 | ||||
-rw-r--r-- | drivers/power/pm2301_charger.h | 1 | ||||
-rw-r--r-- | include/linux/pm2301_charger.h | 1 |
3 files changed, 71 insertions, 2 deletions
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index 9b2e8943f944..ed48d75bb786 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/mfd/abx500/ab8500-gpadc.h> | 28 | #include <linux/mfd/abx500/ab8500-gpadc.h> |
29 | #include <linux/mfd/abx500/ux500_chargalg.h> | 29 | #include <linux/mfd/abx500/ux500_chargalg.h> |
30 | #include <linux/pm2301_charger.h> | 30 | #include <linux/pm2301_charger.h> |
31 | #include <linux/gpio.h> | ||
31 | 32 | ||
32 | #include "pm2301_charger.h" | 33 | #include "pm2301_charger.h" |
33 | 34 | ||
@@ -110,9 +111,35 @@ static const struct i2c_device_id pm2xxx_ident[] = { | |||
110 | { } | 111 | { } |
111 | }; | 112 | }; |
112 | 113 | ||
114 | static void set_lpn_pin(struct pm2xxx_charger *pm2) | ||
115 | { | ||
116 | if (pm2->ac.charger_connected) | ||
117 | return; | ||
118 | gpio_set_value(pm2->lpn_pin, 1); | ||
119 | |||
120 | return; | ||
121 | } | ||
122 | |||
123 | static void clear_lpn_pin(struct pm2xxx_charger *pm2) | ||
124 | { | ||
125 | if (pm2->ac.charger_connected) | ||
126 | return; | ||
127 | gpio_set_value(pm2->lpn_pin, 0); | ||
128 | |||
129 | return; | ||
130 | } | ||
131 | |||
113 | static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) | 132 | static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) |
114 | { | 133 | { |
115 | int ret; | 134 | int ret; |
135 | /* | ||
136 | * When AC adaptor is unplugged, the host | ||
137 | * must put LPN high to be able to | ||
138 | * communicate by I2C with PM2301 | ||
139 | * and receive I2C "acknowledge" from PM2301. | ||
140 | */ | ||
141 | mutex_lock(&pm2->lock); | ||
142 | set_lpn_pin(pm2); | ||
116 | 143 | ||
117 | ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | 144 | ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, |
118 | 1, val); | 145 | 1, val); |
@@ -120,6 +147,8 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) | |||
120 | dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); | 147 | dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); |
121 | else | 148 | else |
122 | ret = 0; | 149 | ret = 0; |
150 | clear_lpn_pin(pm2); | ||
151 | mutex_unlock(&pm2->lock); | ||
123 | 152 | ||
124 | return ret; | 153 | return ret; |
125 | } | 154 | } |
@@ -127,6 +156,14 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) | |||
127 | static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) | 156 | static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) |
128 | { | 157 | { |
129 | int ret; | 158 | int ret; |
159 | /* | ||
160 | * When AC adaptor is unplugged, the host | ||
161 | * must put LPN high to be able to | ||
162 | * communicate by I2C with PM2301 | ||
163 | * and receive I2C "acknowledge" from PM2301. | ||
164 | */ | ||
165 | mutex_lock(&pm2->lock); | ||
166 | set_lpn_pin(pm2); | ||
130 | 167 | ||
131 | ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | 168 | ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, |
132 | 1, &val); | 169 | 1, &val); |
@@ -134,6 +171,8 @@ static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) | |||
134 | dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); | 171 | dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); |
135 | else | 172 | else |
136 | ret = 0; | 173 | ret = 0; |
174 | clear_lpn_pin(pm2); | ||
175 | mutex_unlock(&pm2->lock); | ||
137 | 176 | ||
138 | return ret; | 177 | return ret; |
139 | } | 178 | } |
@@ -850,6 +889,14 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, | |||
850 | 889 | ||
851 | pm2->bat = pl_data->battery; | 890 | pm2->bat = pl_data->battery; |
852 | 891 | ||
892 | /*get lpn GPIO from platform data*/ | ||
893 | if (!pm2->pdata->lpn_gpio) { | ||
894 | dev_err(pm2->dev, "no lpn gpio data supplied\n"); | ||
895 | ret = -EINVAL; | ||
896 | goto free_device_info; | ||
897 | } | ||
898 | pm2->lpn_pin = pm2->pdata->lpn_gpio; | ||
899 | |||
853 | if (!i2c_check_functionality(i2c_client->adapter, | 900 | if (!i2c_check_functionality(i2c_client->adapter, |
854 | I2C_FUNC_SMBUS_BYTE_DATA | | 901 | I2C_FUNC_SMBUS_BYTE_DATA | |
855 | I2C_FUNC_SMBUS_READ_WORD_DATA)) { | 902 | I2C_FUNC_SMBUS_READ_WORD_DATA)) { |
@@ -929,10 +976,25 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, | |||
929 | goto unregister_pm2xxx_charger; | 976 | goto unregister_pm2xxx_charger; |
930 | } | 977 | } |
931 | 978 | ||
979 | /*Initialize lock*/ | ||
980 | mutex_init(&pm2->lock); | ||
981 | |||
932 | /* | 982 | /* |
933 | * I2C Read/Write will fail, if AC adaptor is not connected. | 983 | * Charger detection mechanism requires pulling up the LPN pin |
934 | * fix the charger detection mechanism. | 984 | * while i2c communication if Charger is not connected |
985 | * LPN pin of PM2301 is GPIO60 of AB9540 | ||
935 | */ | 986 | */ |
987 | ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio"); | ||
988 | if (ret < 0) { | ||
989 | dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n"); | ||
990 | goto unregister_pm2xxx_charger; | ||
991 | } | ||
992 | ret = gpio_direction_output(pm2->lpn_pin, 0); | ||
993 | if (ret < 0) { | ||
994 | dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n"); | ||
995 | goto free_gpio; | ||
996 | } | ||
997 | |||
936 | ret = pm2xxx_charger_detection(pm2, &val); | 998 | ret = pm2xxx_charger_detection(pm2, &val); |
937 | 999 | ||
938 | if ((ret == 0) && val) { | 1000 | if ((ret == 0) && val) { |
@@ -944,6 +1006,8 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, | |||
944 | 1006 | ||
945 | return 0; | 1007 | return 0; |
946 | 1008 | ||
1009 | free_gpio: | ||
1010 | gpio_free(pm2->lpn_pin); | ||
947 | unregister_pm2xxx_charger: | 1011 | unregister_pm2xxx_charger: |
948 | /* unregister power supply */ | 1012 | /* unregister power supply */ |
949 | power_supply_unregister(&pm2->ac_chg.psy); | 1013 | power_supply_unregister(&pm2->ac_chg.psy); |
@@ -977,6 +1041,9 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) | |||
977 | 1041 | ||
978 | power_supply_unregister(&pm2->ac_chg.psy); | 1042 | power_supply_unregister(&pm2->ac_chg.psy); |
979 | 1043 | ||
1044 | /*Free GPIO60*/ | ||
1045 | gpio_free(pm2->lpn_pin); | ||
1046 | |||
980 | kfree(pm2); | 1047 | kfree(pm2); |
981 | 1048 | ||
982 | return 0; | 1049 | return 0; |
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h index 3531cc5a9056..e6319cdbc94f 100644 --- a/drivers/power/pm2301_charger.h +++ b/drivers/power/pm2301_charger.h | |||
@@ -493,6 +493,7 @@ struct pm2xxx_charger { | |||
493 | int old_vbat; | 493 | int old_vbat; |
494 | int failure_case; | 494 | int failure_case; |
495 | int failure_input_ovv; | 495 | int failure_input_ovv; |
496 | unsigned int lpn_pin; | ||
496 | struct pm2xxx_interrupts *pm2_int; | 497 | struct pm2xxx_interrupts *pm2_int; |
497 | struct ab8500_gpadc *gpadc; | 498 | struct ab8500_gpadc *gpadc; |
498 | struct regulator *regu; | 499 | struct regulator *regu; |
diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h index 16bb1d34b9d5..fc3f026922ae 100644 --- a/include/linux/pm2301_charger.h +++ b/include/linux/pm2301_charger.h | |||
@@ -49,6 +49,7 @@ struct pm2xxx_charger_platform_data { | |||
49 | int i2c_bus; | 49 | int i2c_bus; |
50 | const char *label; | 50 | const char *label; |
51 | int irq_number; | 51 | int irq_number; |
52 | unsigned int lpn_gpio; | ||
52 | int irq_type; | 53 | int irq_type; |
53 | }; | 54 | }; |
54 | 55 | ||