diff options
Diffstat (limited to 'drivers/extcon/extcon-axp288.c')
-rw-r--r-- | drivers/extcon/extcon-axp288.c | 110 |
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 | |||
80 | enum axp288_extcon_reg { | 73 | enum 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 | ||
91 | enum axp288_mux_select { | 82 | enum 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 | ||
157 | static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) | 149 | static 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 | ||
213 | notify_otg: | 199 | no_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 | ||
256 | static void axp288_extcon_enable_irq(struct axp288_extcon_info *info) | 243 | static 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 | ||
329 | static const struct platform_device_id axp288_extcon_table[] = { | ||
330 | { .name = "axp288_extcon" }, | ||
331 | {}, | ||
332 | }; | ||
333 | MODULE_DEVICE_TABLE(platform, axp288_extcon_table); | ||
334 | |||
358 | static struct platform_driver axp288_extcon_driver = { | 335 | static 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 | }, |