1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ON Semiconductor LC824206XA Micro USB Switch driver
5 * Copyright (c) 2024 Hans de Goede <hansg@kernel.org>
7 * ON Semiconductor has an "Advance Information" datasheet available
8 * (ENA2222-D.PDF), but no full datasheet. So there is no documentation
9 * available for the registers.
11 * This driver is based on the register info from the extcon-fsa9285.c driver,
12 * from the Lollipop Android sources for the Lenovo Yoga Tablet 2 (Pro)
13 * 830 / 1050 / 1380 models. Note despite the name this is actually a driver
14 * for the LC824206XA not the FSA9285. The Android sources can be downloaded
15 * from Lenovo's support page for these tablets, filename:
16 * yoga_tab_2_osc_android_to_lollipop_201505.rar.
19 #include <linux/bits.h>
20 #include <linux/delay.h>
21 #include <linux/device.h>
22 #include <linux/extcon-provider.h>
23 #include <linux/i2c.h>
24 #include <linux/interrupt.h>
25 #include <linux/module.h>
26 #include <linux/power_supply.h>
27 #include <linux/property.h>
28 #include <linux/regulator/consumer.h>
29 #include <linux/workqueue.h>
32 * Register defines as mentioned above there is no datasheet with register
33 * info, so this may not be 100% accurate.
36 #define REG00_INIT_VALUE 0x01
38 #define REG_STATUS 0x01
39 #define STATUS_OVP BIT(0)
40 #define STATUS_DATA_SHORT BIT(1)
41 #define STATUS_VBUS_PRESENT BIT(2)
42 #define STATUS_USB_ID GENMASK(7, 3)
43 #define STATUS_USB_ID_GND 0x80
44 #define STATUS_USB_ID_ACA 0xf0
45 #define STATUS_USB_ID_FLOAT 0xf8
48 * This controls the DP/DM muxes + other switches,
49 * meaning of individual bits is unknown.
51 #define REG_SWITCH_CONTROL 0x02
52 #define SWITCH_STEREO_MIC 0xc8
53 #define SWITCH_USB_HOST 0xec
54 #define SWITCH_DISCONNECTED 0xf8
55 #define SWITCH_USB_DEVICE 0xfc
57 /* 5 bits? ADC 0x10 GND, 0x1a-0x1f ACA, 0x1f float */
58 #define REG_ID_PIN_ADC_VALUE 0x03
60 /* Masks for all 3 interrupt registers */
61 #define INTR_ID_PIN_CHANGE BIT(0)
62 #define INTR_VBUS_CHANGE BIT(1)
63 /* Both of these get set after a continuous mode ADC conversion */
64 #define INTR_ID_PIN_ADC_INT1 BIT(2)
65 #define INTR_ID_PIN_ADC_INT2 BIT(3)
66 /* Charger type available in reg 0x09 */
67 #define INTR_CHARGER_DET_DONE BIT(4)
68 #define INTR_OVP BIT(5)
70 /* There are 7 interrupt sources, bit 6 use is unknown (OCP?) */
71 #define INTR_ALL GENMASK(6, 0)
73 /* Unmask interrupts this driver cares about */
75 (INTR_ALL & ~(INTR_ID_PIN_CHANGE | INTR_VBUS_CHANGE | INTR_CHARGER_DET_DONE))
77 /* Active (event happened and not cleared yet) interrupts */
78 #define REG_INTR_STATUS 0x04
81 * Writing a 1 to a bit here clears it in INTR_STATUS. These bits do NOT
82 * auto-reset to 0, so these must be set to 0 manually after clearing.
84 #define REG_INTR_CLEAR 0x05
86 /* Interrupts which bit is set to 1 here will not raise the HW IRQ */
87 #define REG_INTR_MASK 0x06
89 /* ID pin ADC control, meaning of individual bits is unknown */
90 #define REG_ID_PIN_ADC_CTRL 0x07
91 #define ID_PIN_ADC_AUTO 0x40
92 #define ID_PIN_ADC_CONTINUOUS 0x44
94 #define REG_CHARGER_DET 0x08
95 #define CHARGER_DET_ON BIT(0)
96 #define CHARGER_DET_CDP_ON BIT(1)
97 #define CHARGER_DET_CDP_VAL BIT(2)
99 #define REG_CHARGER_TYPE 0x09
100 #define CHARGER_TYPE_UNKNOWN 0x00
101 #define CHARGER_TYPE_DCP 0x01
102 #define CHARGER_TYPE_SDP_OR_CDP 0x04
103 #define CHARGER_TYPE_QC 0x06
106 #define REG10_INIT_VALUE 0x00
108 struct lc824206xa_data
{
109 struct work_struct work
;
110 struct i2c_client
*client
;
111 struct extcon_dev
*edev
;
112 struct power_supply
*psy
;
113 struct regulator
*vbus_boost
;
114 unsigned int usb_type
;
116 unsigned int previous_cable
;
118 u8 previous_switch_control
;
120 bool vbus_boost_enabled
;
121 bool fastcharge_over_miclr
;
124 static const unsigned int lc824206xa_cables
[] = {
134 /* read/write reg helpers to add error logging to smbus byte functions */
135 static int lc824206xa_read_reg(struct lc824206xa_data
*data
, u8 reg
)
139 ret
= i2c_smbus_read_byte_data(data
->client
, reg
);
141 dev_err(&data
->client
->dev
, "Error %d reading reg 0x%02x\n", ret
, reg
);
146 static int lc824206xa_write_reg(struct lc824206xa_data
*data
, u8 reg
, u8 val
)
150 ret
= i2c_smbus_write_byte_data(data
->client
, reg
, val
);
152 dev_err(&data
->client
->dev
, "Error %d writing reg 0x%02x\n", ret
, reg
);
157 static int lc824206xa_get_id(struct lc824206xa_data
*data
)
161 ret
= lc824206xa_write_reg(data
, REG_ID_PIN_ADC_CTRL
, ID_PIN_ADC_CONTINUOUS
);
165 ret
= lc824206xa_read_reg(data
, REG_ID_PIN_ADC_VALUE
);
167 lc824206xa_write_reg(data
, REG_ID_PIN_ADC_CTRL
, ID_PIN_ADC_AUTO
);
172 static void lc824206xa_set_vbus_boost(struct lc824206xa_data
*data
, bool enable
)
176 if (data
->vbus_boost_enabled
== enable
)
180 ret
= regulator_enable(data
->vbus_boost
);
182 ret
= regulator_disable(data
->vbus_boost
);
185 data
->vbus_boost_enabled
= enable
;
187 dev_err(&data
->client
->dev
, "Error updating Vbus boost regulator: %d\n", ret
);
190 static void lc824206xa_charger_detect(struct lc824206xa_data
*data
)
192 int charger_type
, ret
;
194 charger_type
= lc824206xa_read_reg(data
, REG_CHARGER_TYPE
);
195 if (charger_type
< 0)
198 dev_dbg(&data
->client
->dev
, "charger type 0x%02x\n", charger_type
);
200 switch (charger_type
) {
201 case CHARGER_TYPE_UNKNOWN
:
202 data
->usb_type
= POWER_SUPPLY_USB_TYPE_UNKNOWN
;
204 data
->cable
= EXTCON_CHG_USB_SDP
;
205 data
->switch_control
= SWITCH_USB_DEVICE
;
207 case CHARGER_TYPE_SDP_OR_CDP
:
208 data
->usb_type
= POWER_SUPPLY_USB_TYPE_SDP
;
209 data
->cable
= EXTCON_CHG_USB_SDP
;
210 data
->switch_control
= SWITCH_USB_DEVICE
;
212 ret
= lc824206xa_write_reg(data
, REG_CHARGER_DET
,
213 CHARGER_DET_CDP_ON
| CHARGER_DET_ON
);
218 ret
= lc824206xa_read_reg(data
, REG_CHARGER_DET
);
219 if (ret
>= 0 && (ret
& CHARGER_DET_CDP_VAL
)) {
220 data
->usb_type
= POWER_SUPPLY_USB_TYPE_CDP
;
221 data
->cable
= EXTCON_CHG_USB_CDP
;
224 lc824206xa_write_reg(data
, REG_CHARGER_DET
, CHARGER_DET_ON
);
226 case CHARGER_TYPE_DCP
:
227 data
->usb_type
= POWER_SUPPLY_USB_TYPE_DCP
;
228 data
->cable
= EXTCON_CHG_USB_DCP
;
229 if (data
->fastcharge_over_miclr
)
230 data
->switch_control
= SWITCH_STEREO_MIC
;
232 data
->switch_control
= SWITCH_DISCONNECTED
;
234 case CHARGER_TYPE_QC
:
235 data
->usb_type
= POWER_SUPPLY_USB_TYPE_DCP
;
236 data
->cable
= EXTCON_CHG_USB_DCP
;
237 data
->switch_control
= SWITCH_DISCONNECTED
;
240 dev_warn(&data
->client
->dev
, "Unknown charger type: 0x%02x\n", charger_type
);
245 static void lc824206xa_work(struct work_struct
*work
)
247 struct lc824206xa_data
*data
= container_of(work
, struct lc824206xa_data
, work
);
248 bool vbus_boost_enable
= false;
251 status
= lc824206xa_read_reg(data
, REG_STATUS
);
255 dev_dbg(&data
->client
->dev
, "status 0x%02x\n", status
);
257 data
->vbus_ok
= (status
& (STATUS_VBUS_PRESENT
| STATUS_OVP
)) == STATUS_VBUS_PRESENT
;
259 /* Read id pin ADC if necessary */
260 switch (status
& STATUS_USB_ID
) {
261 case STATUS_USB_ID_GND
:
262 case STATUS_USB_ID_FLOAT
:
265 /* Happens when the connector is inserted slowly, log at dbg level */
266 dev_dbg(&data
->client
->dev
, "Unknown status 0x%02x\n", status
);
268 case STATUS_USB_ID_ACA
:
269 id
= lc824206xa_get_id(data
);
270 dev_dbg(&data
->client
->dev
, "RID 0x%02x\n", id
);
273 status
= STATUS_USB_ID_GND
;
276 status
= STATUS_USB_ID_ACA
;
279 status
= STATUS_USB_ID_FLOAT
;
282 dev_warn(&data
->client
->dev
, "Unknown RID 0x%02x\n", id
);
287 /* Check for out of spec OTG charging hubs, treat as ACA */
288 if ((status
& STATUS_USB_ID
) == STATUS_USB_ID_GND
&&
289 data
->vbus_ok
&& !data
->vbus_boost_enabled
) {
290 dev_info(&data
->client
->dev
, "Out of spec USB host adapter with Vbus present, not enabling 5V output\n");
291 status
= STATUS_USB_ID_ACA
;
294 switch (status
& STATUS_USB_ID
) {
295 case STATUS_USB_ID_ACA
:
296 data
->usb_type
= POWER_SUPPLY_USB_TYPE_ACA
;
297 data
->cable
= EXTCON_CHG_USB_ACA
;
298 data
->switch_control
= SWITCH_USB_HOST
;
300 case STATUS_USB_ID_GND
:
301 data
->usb_type
= POWER_SUPPLY_USB_TYPE_UNKNOWN
;
302 data
->cable
= EXTCON_USB_HOST
;
303 data
->switch_control
= SWITCH_USB_HOST
;
304 vbus_boost_enable
= true;
306 case STATUS_USB_ID_FLOAT
:
307 /* When fast charging with Vbus > 5V, OVP will be set */
308 if (data
->fastcharge_over_miclr
&&
309 data
->switch_control
== SWITCH_STEREO_MIC
&&
310 (status
& STATUS_OVP
)) {
311 data
->cable
= EXTCON_CHG_USB_FAST
;
316 lc824206xa_charger_detect(data
);
318 data
->usb_type
= POWER_SUPPLY_USB_TYPE_UNKNOWN
;
319 data
->cable
= EXTCON_NONE
;
320 data
->switch_control
= SWITCH_DISCONNECTED
;
325 lc824206xa_set_vbus_boost(data
, vbus_boost_enable
);
327 if (data
->switch_control
!= data
->previous_switch_control
) {
328 lc824206xa_write_reg(data
, REG_SWITCH_CONTROL
, data
->switch_control
);
329 data
->previous_switch_control
= data
->switch_control
;
332 if (data
->cable
!= data
->previous_cable
) {
333 extcon_set_state_sync(data
->edev
, data
->previous_cable
, false);
334 extcon_set_state_sync(data
->edev
, data
->cable
, true);
335 data
->previous_cable
= data
->cable
;
338 power_supply_changed(data
->psy
);
341 static irqreturn_t
lc824206xa_irq(int irq
, void *_data
)
343 struct lc824206xa_data
*data
= _data
;
346 intr_status
= lc824206xa_read_reg(data
, REG_INTR_STATUS
);
348 intr_status
= INTR_ALL
; /* Should never happen, clear all */
350 dev_dbg(&data
->client
->dev
, "interrupt 0x%02x\n", intr_status
);
352 lc824206xa_write_reg(data
, REG_INTR_CLEAR
, intr_status
);
353 lc824206xa_write_reg(data
, REG_INTR_CLEAR
, 0);
355 schedule_work(&data
->work
);
360 * Newer charger (power_supply) drivers expect the max input current to be
361 * provided by a parent power_supply device for the charger chip.
363 static int lc824206xa_psy_get_prop(struct power_supply
*psy
,
364 enum power_supply_property psp
,
365 union power_supply_propval
*val
)
367 struct lc824206xa_data
*data
= power_supply_get_drvdata(psy
);
370 case POWER_SUPPLY_PROP_ONLINE
:
371 val
->intval
= data
->vbus_ok
&& !data
->vbus_boost_enabled
;
373 case POWER_SUPPLY_PROP_USB_TYPE
:
374 val
->intval
= data
->usb_type
;
376 case POWER_SUPPLY_PROP_CURRENT_MAX
:
377 switch (data
->usb_type
) {
378 case POWER_SUPPLY_USB_TYPE_DCP
:
379 case POWER_SUPPLY_USB_TYPE_ACA
:
380 val
->intval
= 2000000;
382 case POWER_SUPPLY_USB_TYPE_CDP
:
383 val
->intval
= 1500000;
386 val
->intval
= 500000;
396 static const enum power_supply_property lc824206xa_psy_props
[] = {
397 POWER_SUPPLY_PROP_ONLINE
,
398 POWER_SUPPLY_PROP_USB_TYPE
,
399 POWER_SUPPLY_PROP_CURRENT_MAX
,
402 static const struct power_supply_desc lc824206xa_psy_desc
= {
403 .name
= "lc824206xa-charger-detect",
404 .type
= POWER_SUPPLY_TYPE_USB
,
405 .usb_types
= BIT(POWER_SUPPLY_USB_TYPE_SDP
) |
406 BIT(POWER_SUPPLY_USB_TYPE_CDP
) |
407 BIT(POWER_SUPPLY_USB_TYPE_DCP
) |
408 BIT(POWER_SUPPLY_USB_TYPE_ACA
) |
409 BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN
),
410 .properties
= lc824206xa_psy_props
,
411 .num_properties
= ARRAY_SIZE(lc824206xa_psy_props
),
412 .get_property
= lc824206xa_psy_get_prop
,
415 static int lc824206xa_probe(struct i2c_client
*client
)
417 struct power_supply_config psy_cfg
= { };
418 struct device
*dev
= &client
->dev
;
419 struct lc824206xa_data
*data
;
422 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
426 data
->client
= client
;
427 INIT_WORK(&data
->work
, lc824206xa_work
);
428 data
->cable
= EXTCON_NONE
;
429 data
->previous_cable
= EXTCON_NONE
;
430 data
->usb_type
= POWER_SUPPLY_USB_TYPE_UNKNOWN
;
431 /* Some designs use a custom fast-charge protocol over the mic L/R inputs */
432 data
->fastcharge_over_miclr
=
433 device_property_read_bool(dev
, "onnn,enable-miclr-for-dcp");
435 data
->vbus_boost
= devm_regulator_get(dev
, "vbus");
436 if (IS_ERR(data
->vbus_boost
))
437 return dev_err_probe(dev
, PTR_ERR(data
->vbus_boost
),
438 "getting regulator\n");
441 ret
= lc824206xa_write_reg(data
, REG00
, REG00_INIT_VALUE
);
442 ret
|= lc824206xa_write_reg(data
, REG10
, REG10_INIT_VALUE
);
444 ret
|= lc824206xa_write_reg(data
, REG_INTR_CLEAR
, INTR_ALL
);
445 ret
|= lc824206xa_write_reg(data
, REG_INTR_CLEAR
, 0);
446 ret
|= lc824206xa_write_reg(data
, REG_INTR_MASK
, INTR_MASK
);
447 ret
|= lc824206xa_write_reg(data
, REG_ID_PIN_ADC_CTRL
, ID_PIN_ADC_AUTO
);
448 ret
|= lc824206xa_write_reg(data
, REG_CHARGER_DET
, CHARGER_DET_ON
);
452 /* Initialize extcon device */
453 data
->edev
= devm_extcon_dev_allocate(dev
, lc824206xa_cables
);
454 if (IS_ERR(data
->edev
))
455 return PTR_ERR(data
->edev
);
457 ret
= devm_extcon_dev_register(dev
, data
->edev
);
459 return dev_err_probe(dev
, ret
, "registering extcon device\n");
461 psy_cfg
.drv_data
= data
;
462 data
->psy
= devm_power_supply_register(dev
, &lc824206xa_psy_desc
, &psy_cfg
);
463 if (IS_ERR(data
->psy
))
464 return dev_err_probe(dev
, PTR_ERR(data
->psy
), "registering power supply\n");
466 ret
= devm_request_threaded_irq(dev
, client
->irq
, NULL
, lc824206xa_irq
,
467 IRQF_TRIGGER_LOW
| IRQF_ONESHOT
,
468 KBUILD_MODNAME
, data
);
470 return dev_err_probe(dev
, ret
, "requesting IRQ\n");
472 /* Sync initial state */
473 schedule_work(&data
->work
);
477 static const struct i2c_device_id lc824206xa_i2c_ids
[] = {
481 MODULE_DEVICE_TABLE(i2c
, lc824206xa_i2c_ids
);
483 static struct i2c_driver lc824206xa_driver
= {
485 .name
= KBUILD_MODNAME
,
487 .probe
= lc824206xa_probe
,
488 .id_table
= lc824206xa_i2c_ids
,
491 module_i2c_driver(lc824206xa_driver
);
493 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
494 MODULE_DESCRIPTION("LC824206XA Micro USB Switch driver");
495 MODULE_LICENSE("GPL");