aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon/extcon-axp288.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/extcon/extcon-axp288.c')
-rw-r--r--drivers/extcon/extcon-axp288.c110
1 files changed, 44 insertions, 66 deletions
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index 42f41e808292..f4fd03e58e37 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -21,7 +21,6 @@
21#include <linux/interrupt.h> 21#include <linux/interrupt.h>
22#include <linux/platform_device.h> 22#include <linux/platform_device.h>
23#include <linux/property.h> 23#include <linux/property.h>
24#include <linux/usb/phy.h>
25#include <linux/notifier.h> 24#include <linux/notifier.h>
26#include <linux/extcon.h> 25#include <linux/extcon.h>
27#include <linux/regmap.h> 26#include <linux/regmap.h>
@@ -71,12 +70,6 @@
71#define DET_STAT_CDP 2 70#define DET_STAT_CDP 2
72#define DET_STAT_DCP 3 71#define DET_STAT_DCP 3
73 72
74/* IRQ enable-1 register */
75#define PWRSRC_IRQ_CFG_MASK (BIT(4)|BIT(3)|BIT(2))
76
77/* IRQ enable-6 register */
78#define BC12_IRQ_CFG_MASK BIT(1)
79
80enum axp288_extcon_reg { 73enum axp288_extcon_reg {
81 AXP288_PS_STAT_REG = 0x00, 74 AXP288_PS_STAT_REG = 0x00,
82 AXP288_PS_BOOT_REASON_REG = 0x02, 75 AXP288_PS_BOOT_REASON_REG = 0x02,
@@ -84,8 +77,6 @@ enum axp288_extcon_reg {
84 AXP288_BC_VBUS_CNTL_REG = 0x2d, 77 AXP288_BC_VBUS_CNTL_REG = 0x2d,
85 AXP288_BC_USB_STAT_REG = 0x2e, 78 AXP288_BC_USB_STAT_REG = 0x2e,
86 AXP288_BC_DET_STAT_REG = 0x2f, 79 AXP288_BC_DET_STAT_REG = 0x2f,
87 AXP288_PWRSRC_IRQ_CFG_REG = 0x40,
88 AXP288_BC12_IRQ_CFG_REG = 0x45,
89}; 80};
90 81
91enum axp288_mux_select { 82enum axp288_mux_select {
@@ -105,6 +96,7 @@ static const unsigned int axp288_extcon_cables[] = {
105 EXTCON_CHG_USB_SDP, 96 EXTCON_CHG_USB_SDP,
106 EXTCON_CHG_USB_CDP, 97 EXTCON_CHG_USB_CDP,
107 EXTCON_CHG_USB_DCP, 98 EXTCON_CHG_USB_DCP,
99 EXTCON_USB,
108 EXTCON_NONE, 100 EXTCON_NONE,
109}; 101};
110 102
@@ -112,11 +104,11 @@ struct axp288_extcon_info {
112 struct device *dev; 104 struct device *dev;
113 struct regmap *regmap; 105 struct regmap *regmap;
114 struct regmap_irq_chip_data *regmap_irqc; 106 struct regmap_irq_chip_data *regmap_irqc;
115 struct axp288_extcon_pdata *pdata; 107 struct gpio_desc *gpio_mux_cntl;
116 int irq[EXTCON_IRQ_END]; 108 int irq[EXTCON_IRQ_END];
117 struct extcon_dev *edev; 109 struct extcon_dev *edev;
118 struct notifier_block extcon_nb; 110 struct notifier_block extcon_nb;
119 struct usb_phy *otg; 111 unsigned int previous_cable;
120}; 112};
121 113
122/* Power up/down reason string array */ 114/* Power up/down reason string array */
@@ -156,10 +148,9 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
156 148
157static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) 149static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
158{ 150{
159 static bool notify_otg, notify_charger;
160 static unsigned int cable;
161 int ret, stat, cfg, pwr_stat; 151 int ret, stat, cfg, pwr_stat;
162 u8 chrg_type; 152 u8 chrg_type;
153 unsigned int cable = info->previous_cable;
163 bool vbus_attach = false; 154 bool vbus_attach = false;
164 155
165 ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat); 156 ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
@@ -168,9 +159,9 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
168 return ret; 159 return ret;
169 } 160 }
170 161
171 vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT); 162 vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID);
172 if (!vbus_attach) 163 if (!vbus_attach)
173 goto notify_otg; 164 goto no_vbus;
174 165
175 /* Check charger detection completion status */ 166 /* Check charger detection completion status */
176 ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg); 167 ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
@@ -190,19 +181,14 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
190 switch (chrg_type) { 181 switch (chrg_type) {
191 case DET_STAT_SDP: 182 case DET_STAT_SDP:
192 dev_dbg(info->dev, "sdp cable is connected\n"); 183 dev_dbg(info->dev, "sdp cable is connected\n");
193 notify_otg = true;
194 notify_charger = true;
195 cable = EXTCON_CHG_USB_SDP; 184 cable = EXTCON_CHG_USB_SDP;
196 break; 185 break;
197 case DET_STAT_CDP: 186 case DET_STAT_CDP:
198 dev_dbg(info->dev, "cdp cable is connected\n"); 187 dev_dbg(info->dev, "cdp cable is connected\n");
199 notify_otg = true;
200 notify_charger = true;
201 cable = EXTCON_CHG_USB_CDP; 188 cable = EXTCON_CHG_USB_CDP;
202 break; 189 break;
203 case DET_STAT_DCP: 190 case DET_STAT_DCP:
204 dev_dbg(info->dev, "dcp cable is connected\n"); 191 dev_dbg(info->dev, "dcp cable is connected\n");
205 notify_charger = true;
206 cable = EXTCON_CHG_USB_DCP; 192 cable = EXTCON_CHG_USB_DCP;
207 break; 193 break;
208 default: 194 default:
@@ -210,27 +196,28 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
210 "disconnect or unknown or ID event\n"); 196 "disconnect or unknown or ID event\n");
211 } 197 }
212 198
213notify_otg: 199no_vbus:
214 if (notify_otg) { 200 /*
215 /* 201 * If VBUS is absent Connect D+/D- lines to PMIC for BC
216 * If VBUS is absent Connect D+/D- lines to PMIC for BC 202 * detection. Else connect them to SOC for USB communication.
217 * detection. Else connect them to SOC for USB communication. 203 */
218 */ 204 if (info->gpio_mux_cntl)
219 if (info->pdata->gpio_mux_cntl) 205 gpiod_set_value(info->gpio_mux_cntl,
220 gpiod_set_value(info->pdata->gpio_mux_cntl, 206 vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC
221 vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC 207 : EXTCON_GPIO_MUX_SEL_PMIC);
222 : EXTCON_GPIO_MUX_SEL_PMIC); 208
223 209 extcon_set_state_sync(info->edev, info->previous_cable, false);
224 atomic_notifier_call_chain(&info->otg->notifier, 210 if (info->previous_cable == EXTCON_CHG_USB_SDP)
225 vbus_attach ? USB_EVENT_VBUS : USB_EVENT_NONE, NULL); 211 extcon_set_state_sync(info->edev, EXTCON_USB, false);
226 } 212
227 213 if (vbus_attach) {
228 if (notify_charger)
229 extcon_set_state_sync(info->edev, cable, vbus_attach); 214 extcon_set_state_sync(info->edev, cable, vbus_attach);
215 if (cable == EXTCON_CHG_USB_SDP)
216 extcon_set_state_sync(info->edev, EXTCON_USB,
217 vbus_attach);
230 218
231 /* Clear the flags on disconnect event */ 219 info->previous_cable = cable;
232 if (!vbus_attach) 220 }
233 notify_otg = notify_charger = false;
234 221
235 return 0; 222 return 0;
236 223
@@ -253,15 +240,10 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data)
253 return IRQ_HANDLED; 240 return IRQ_HANDLED;
254} 241}
255 242
256static void axp288_extcon_enable_irq(struct axp288_extcon_info *info) 243static void axp288_extcon_enable(struct axp288_extcon_info *info)
257{ 244{
258 /* Unmask VBUS interrupt */
259 regmap_write(info->regmap, AXP288_PWRSRC_IRQ_CFG_REG,
260 PWRSRC_IRQ_CFG_MASK);
261 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, 245 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
262 BC_GLOBAL_RUN, 0); 246 BC_GLOBAL_RUN, 0);
263 /* Unmask the BC1.2 complete interrupts */
264 regmap_write(info->regmap, AXP288_BC12_IRQ_CFG_REG, BC12_IRQ_CFG_MASK);
265 /* Enable the charger detection logic */ 247 /* Enable the charger detection logic */
266 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, 248 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
267 BC_GLOBAL_RUN, BC_GLOBAL_RUN); 249 BC_GLOBAL_RUN, BC_GLOBAL_RUN);
@@ -271,6 +253,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
271{ 253{
272 struct axp288_extcon_info *info; 254 struct axp288_extcon_info *info;
273 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 255 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
256 struct axp288_extcon_pdata *pdata = pdev->dev.platform_data;
274 int ret, i, pirq, gpio; 257 int ret, i, pirq, gpio;
275 258
276 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 259 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@@ -280,15 +263,10 @@ static int axp288_extcon_probe(struct platform_device *pdev)
280 info->dev = &pdev->dev; 263 info->dev = &pdev->dev;
281 info->regmap = axp20x->regmap; 264 info->regmap = axp20x->regmap;
282 info->regmap_irqc = axp20x->regmap_irqc; 265 info->regmap_irqc = axp20x->regmap_irqc;
283 info->pdata = pdev->dev.platform_data; 266 info->previous_cable = EXTCON_NONE;
284 267 if (pdata)
285 if (!info->pdata) { 268 info->gpio_mux_cntl = pdata->gpio_mux_cntl;
286 /* Try ACPI provided pdata via device properties */ 269
287 if (!device_property_present(&pdev->dev,
288 "axp288_extcon_data\n"))
289 dev_err(&pdev->dev, "failed to get platform data\n");
290 return -ENODEV;
291 }
292 platform_set_drvdata(pdev, info); 270 platform_set_drvdata(pdev, info);
293 271
294 axp288_extcon_log_rsi(info); 272 axp288_extcon_log_rsi(info);
@@ -308,23 +286,16 @@ static int axp288_extcon_probe(struct platform_device *pdev)
308 return ret; 286 return ret;
309 } 287 }
310 288
311 /* Get otg transceiver phy */
312 info->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
313 if (IS_ERR(info->otg)) {
314 dev_err(&pdev->dev, "failed to get otg transceiver\n");
315 return PTR_ERR(info->otg);
316 }
317
318 /* Set up gpio control for USB Mux */ 289 /* Set up gpio control for USB Mux */
319 if (info->pdata->gpio_mux_cntl) { 290 if (info->gpio_mux_cntl) {
320 gpio = desc_to_gpio(info->pdata->gpio_mux_cntl); 291 gpio = desc_to_gpio(info->gpio_mux_cntl);
321 ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX"); 292 ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX");
322 if (ret < 0) { 293 if (ret < 0) {
323 dev_err(&pdev->dev, 294 dev_err(&pdev->dev,
324 "failed to request the gpio=%d\n", gpio); 295 "failed to request the gpio=%d\n", gpio);
325 return ret; 296 return ret;
326 } 297 }
327 gpiod_direction_output(info->pdata->gpio_mux_cntl, 298 gpiod_direction_output(info->gpio_mux_cntl,
328 EXTCON_GPIO_MUX_SEL_PMIC); 299 EXTCON_GPIO_MUX_SEL_PMIC);
329 } 300 }
330 301
@@ -349,14 +320,21 @@ static int axp288_extcon_probe(struct platform_device *pdev)
349 } 320 }
350 } 321 }
351 322
352 /* Enable interrupts */ 323 /* Start charger cable type detection */
353 axp288_extcon_enable_irq(info); 324 axp288_extcon_enable(info);
354 325
355 return 0; 326 return 0;
356} 327}
357 328
329static const struct platform_device_id axp288_extcon_table[] = {
330 { .name = "axp288_extcon" },
331 {},
332};
333MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
334
358static struct platform_driver axp288_extcon_driver = { 335static struct platform_driver axp288_extcon_driver = {
359 .probe = axp288_extcon_probe, 336 .probe = axp288_extcon_probe,
337 .id_table = axp288_extcon_table,
360 .driver = { 338 .driver = {
361 .name = "axp288_extcon", 339 .name = "axp288_extcon",
362 }, 340 },