Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * ON Semiconductor LC824206XA Micro USB Switch driver
  4 *
  5 * Copyright (c) 2024 Hans de Goede <hansg@kernel.org>
  6 *
  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.
 10 *
 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.
 17 */
 18
 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>
 30
 31/*
 32 * Register defines as mentioned above there is no datasheet with register
 33 * info, so this may not be 100% accurate.
 34 */
 35#define REG00				0x00
 36#define REG00_INIT_VALUE		0x01
 37
 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
 46
 47/*
 48 * This controls the DP/DM muxes + other switches,
 49 * meaning of individual bits is unknown.
 50 */
 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
 56
 57/* 5 bits? ADC 0x10 GND, 0x1a-0x1f ACA, 0x1f float */
 58#define REG_ID_PIN_ADC_VALUE		0x03
 59
 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)
 69
 70/* There are 7 interrupt sources, bit 6 use is unknown (OCP?) */
 71#define INTR_ALL			GENMASK(6, 0)
 72
 73/* Unmask interrupts this driver cares about */
 74#define INTR_MASK \
 75	(INTR_ALL & ~(INTR_ID_PIN_CHANGE | INTR_VBUS_CHANGE | INTR_CHARGER_DET_DONE))
 76
 77/* Active (event happened and not cleared yet) interrupts */
 78#define REG_INTR_STATUS			0x04
 79
 80/*
 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.
 83 */
 84#define REG_INTR_CLEAR			0x05
 85
 86/* Interrupts which bit is set to 1 here will not raise the HW IRQ */
 87#define REG_INTR_MASK			0x06
 88
 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
 93
 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)
 98
 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
104
105#define REG10				0x10
106#define REG10_INIT_VALUE		0x00
107
108struct 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;
115	unsigned int cable;
116	unsigned int previous_cable;
117	u8 switch_control;
118	u8 previous_switch_control;
119	bool vbus_ok;
120	bool vbus_boost_enabled;
121	bool fastcharge_over_miclr;
122};
123
124static const unsigned int lc824206xa_cables[] = {
125	EXTCON_USB_HOST,
126	EXTCON_CHG_USB_SDP,
127	EXTCON_CHG_USB_CDP,
128	EXTCON_CHG_USB_DCP,
129	EXTCON_CHG_USB_ACA,
130	EXTCON_CHG_USB_FAST,
131	EXTCON_NONE,
132};
133
134/* read/write reg helpers to add error logging to smbus byte functions */
135static int lc824206xa_read_reg(struct lc824206xa_data *data, u8 reg)
136{
137	int ret;
138
139	ret = i2c_smbus_read_byte_data(data->client, reg);
140	if (ret < 0)
141		dev_err(&data->client->dev, "Error %d reading reg 0x%02x\n", ret, reg);
142
143	return ret;
144}
145
146static int lc824206xa_write_reg(struct lc824206xa_data *data, u8 reg, u8 val)
147{
148	int ret;
149
150	ret = i2c_smbus_write_byte_data(data->client, reg, val);
151	if (ret < 0)
152		dev_err(&data->client->dev, "Error %d writing reg 0x%02x\n", ret, reg);
153
154	return ret;
155}
156
157static int lc824206xa_get_id(struct lc824206xa_data *data)
158{
159	int ret;
160
161	ret = lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_CONTINUOUS);
162	if (ret)
163		return ret;
164
165	ret = lc824206xa_read_reg(data, REG_ID_PIN_ADC_VALUE);
166
167	lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_AUTO);
168
169	return ret;
170}
171
172static void lc824206xa_set_vbus_boost(struct lc824206xa_data *data, bool enable)
173{
174	int ret;
175
176	if (data->vbus_boost_enabled == enable)
177		return;
178
179	if (enable)
180		ret = regulator_enable(data->vbus_boost);
181	else
182		ret = regulator_disable(data->vbus_boost);
183
184	if (ret == 0)
185		data->vbus_boost_enabled = enable;
186	else
187		dev_err(&data->client->dev, "Error updating Vbus boost regulator: %d\n", ret);
188}
189
190static void lc824206xa_charger_detect(struct lc824206xa_data *data)
191{
192	int charger_type, ret;
193
194	charger_type = lc824206xa_read_reg(data, REG_CHARGER_TYPE);
195	if (charger_type < 0)
196		return;
197
198	dev_dbg(&data->client->dev, "charger type 0x%02x\n", charger_type);
199
200	switch (charger_type) {
201	case CHARGER_TYPE_UNKNOWN:
202		data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
203		/* Treat as SDP */
204		data->cable = EXTCON_CHG_USB_SDP;
205		data->switch_control = SWITCH_USB_DEVICE;
206		break;
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;
211
212		ret = lc824206xa_write_reg(data, REG_CHARGER_DET,
213					   CHARGER_DET_CDP_ON | CHARGER_DET_ON);
214		if (ret < 0)
215			break;
216
217		msleep(100);
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;
222		}
223
224		lc824206xa_write_reg(data, REG_CHARGER_DET, CHARGER_DET_ON);
225		break;
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;
231		else
232			data->switch_control = SWITCH_DISCONNECTED;
233		break;
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;
238		break;
239	default:
240		dev_warn(&data->client->dev, "Unknown charger type: 0x%02x\n", charger_type);
241		break;
242	}
243}
244
245static void lc824206xa_work(struct work_struct *work)
246{
247	struct lc824206xa_data *data = container_of(work, struct lc824206xa_data, work);
248	bool vbus_boost_enable = false;
249	int status, id;
250
251	status = lc824206xa_read_reg(data, REG_STATUS);
252	if (status < 0)
253		return;
254
255	dev_dbg(&data->client->dev, "status 0x%02x\n", status);
256
257	data->vbus_ok = (status & (STATUS_VBUS_PRESENT | STATUS_OVP)) == STATUS_VBUS_PRESENT;
258
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:
263		break;
264	default:
265		/* Happens when the connector is inserted slowly, log at dbg level */
266		dev_dbg(&data->client->dev, "Unknown status 0x%02x\n", status);
267		fallthrough;
268	case STATUS_USB_ID_ACA:
269		id = lc824206xa_get_id(data);
270		dev_dbg(&data->client->dev, "RID 0x%02x\n", id);
271		switch (id) {
272		case 0x10:
273			status = STATUS_USB_ID_GND;
274			break;
275		case 0x18 ... 0x1e:
276			status = STATUS_USB_ID_ACA;
277			break;
278		case 0x1f:
279			status = STATUS_USB_ID_FLOAT;
280			break;
281		default:
282			dev_warn(&data->client->dev, "Unknown RID 0x%02x\n", id);
283			return;
284		}
285	}
286
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;
292	}
293
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;
299		break;
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;
305		break;
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;
312			break;
313		}
314
315		if (data->vbus_ok) {
316			lc824206xa_charger_detect(data);
317		} else {
318			data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
319			data->cable = EXTCON_NONE;
320			data->switch_control = SWITCH_DISCONNECTED;
321		}
322		break;
323	}
324
325	lc824206xa_set_vbus_boost(data, vbus_boost_enable);
326
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;
330	}
331
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;
336	}
337
338	power_supply_changed(data->psy);
339}
340
341static irqreturn_t lc824206xa_irq(int irq, void *_data)
342{
343	struct lc824206xa_data *data = _data;
344	int intr_status;
345
346	intr_status = lc824206xa_read_reg(data, REG_INTR_STATUS);
347	if (intr_status < 0)
348		intr_status = INTR_ALL; /* Should never happen, clear all */
349
350	dev_dbg(&data->client->dev, "interrupt 0x%02x\n", intr_status);
351
352	lc824206xa_write_reg(data, REG_INTR_CLEAR, intr_status);
353	lc824206xa_write_reg(data, REG_INTR_CLEAR, 0);
354
355	schedule_work(&data->work);
356	return IRQ_HANDLED;
357}
358
359/*
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.
362 */
363static int lc824206xa_psy_get_prop(struct power_supply *psy,
364				   enum power_supply_property psp,
365				   union power_supply_propval *val)
366{
367	struct lc824206xa_data *data = power_supply_get_drvdata(psy);
368
369	switch (psp) {
370	case POWER_SUPPLY_PROP_ONLINE:
371		val->intval = data->vbus_ok && !data->vbus_boost_enabled;
372		break;
373	case POWER_SUPPLY_PROP_USB_TYPE:
374		val->intval = data->usb_type;
375		break;
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;
381			break;
382		case POWER_SUPPLY_USB_TYPE_CDP:
383			val->intval = 1500000;
384			break;
385		default:
386			val->intval = 500000;
387		}
388		break;
389	default:
390		return -EINVAL;
391	}
392
393	return 0;
394}
395
396static 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,
400};
401
402static 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,
413};
414
415static int lc824206xa_probe(struct i2c_client *client)
416{
417	struct power_supply_config psy_cfg = { };
418	struct device *dev = &client->dev;
419	struct lc824206xa_data *data;
420	int ret;
421
422	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
423	if (!data)
424		return -ENOMEM;
425
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");
434
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");
439
440	/* Init */
441	ret = lc824206xa_write_reg(data, REG00, REG00_INIT_VALUE);
442	ret |= lc824206xa_write_reg(data, REG10, REG10_INIT_VALUE);
443	msleep(100);
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);
449	if (ret)
450		return -EIO;
451
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);
456
457	ret = devm_extcon_dev_register(dev, data->edev);
458	if (ret)
459		return dev_err_probe(dev, ret, "registering extcon device\n");
460
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");
465
466	ret = devm_request_threaded_irq(dev, client->irq, NULL, lc824206xa_irq,
467					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
468					KBUILD_MODNAME, data);
469	if (ret)
470		return dev_err_probe(dev, ret, "requesting IRQ\n");
471
472	/* Sync initial state */
473	schedule_work(&data->work);
474	return 0;
475}
476
477static const struct i2c_device_id lc824206xa_i2c_ids[] = {
478	{ "lc824206xa" },
479	{ }
480};
481MODULE_DEVICE_TABLE(i2c, lc824206xa_i2c_ids);
482
483static struct i2c_driver lc824206xa_driver = {
484	.driver = {
485		.name = KBUILD_MODNAME,
486	},
487	.probe = lc824206xa_probe,
488	.id_table = lc824206xa_i2c_ids,
489};
490
491module_i2c_driver(lc824206xa_driver);
492
493MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
494MODULE_DESCRIPTION("LC824206XA Micro USB Switch driver");
495MODULE_LICENSE("GPL");