diff options
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/Kconfig | 14 | ||||
-rw-r--r-- | drivers/extcon/Makefile | 1 | ||||
-rw-r--r-- | drivers/extcon/extcon-adc-jack.c | 6 | ||||
-rw-r--r-- | drivers/extcon/extcon-axp288.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-fsa9480.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-intel-cht-wc.c | 19 | ||||
-rw-r--r-- | drivers/extcon/extcon-intel-mrfld.c | 24 | ||||
-rw-r--r-- | drivers/extcon/extcon-lc824206xa.c | 495 | ||||
-rw-r--r-- | drivers/extcon/extcon-max3355.c | 4 | ||||
-rw-r--r-- | drivers/extcon/extcon-max77843.c | 4 | ||||
-rw-r--r-- | drivers/extcon/extcon-ptn5150.c | 2 | ||||
-rw-r--r-- | drivers/extcon/extcon-rtk-type-c.c | 5 | ||||
-rw-r--r-- | drivers/extcon/extcon-usb-gpio.c | 4 | ||||
-rw-r--r-- | drivers/extcon/extcon-usbc-cros-ec.c | 4 |
14 files changed, 536 insertions, 50 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 5f869eacd19a..a6f6d467aacf 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -75,6 +75,17 @@ config EXTCON_INTEL_MRFLD Say Y here to enable extcon support for charger detection / control on the Intel Merrifield Basin Cove PMIC. +config EXTCON_LC824206XA + tristate "LC824206XA extcon Support" + depends on I2C + depends on POWER_SUPPLY + help + Say Y here to enable support for the ON Semiconductor LC824206XA + microUSB switch and accessory detector chip. The LC824206XA is a USB + port accessory detector and switch. The LC824206XA is fully controlled + using I2C and enables USB data, stereo and mono audio, video, + microphone and UART data to use a common connector port. + config EXTCON_MAX14577 tristate "Maxim MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 @@ -116,7 +127,8 @@ config EXTCON_MAX77843 config EXTCON_MAX8997 tristate "Maxim MAX8997 EXTCON Support" - depends on MFD_MAX8997 && IRQ_DOMAIN + depends on MFD_MAX8997 + select IRQ_DOMAIN help If you say yes here you get support for the MUIC device of Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index f779adb5e4c7..0d6d23faf748 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o obj-$(CONFIG_EXTCON_INTEL_CHT_WC) += extcon-intel-cht-wc.o obj-$(CONFIG_EXTCON_INTEL_MRFLD) += extcon-intel-mrfld.o +obj-$(CONFIG_EXTCON_LC824206XA) += extcon-lc824206xa.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 0317b614b680..46c40d85c2ac 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -26,6 +26,7 @@ /** * struct adc_jack_data - internal data for adc_jack device driver + * @dev: The device structure associated with the adc_jack. * @edev: extcon device. * @cable_names: list of supported cables. * @adc_conditions: list of adc value conditions. @@ -35,6 +36,7 @@ * handling at handling_delay jiffies. * @handler: extcon event handler called by interrupt handler. * @chan: iio channel being queried. + * @wakeup_source: Indicates if the device can wake up the system. */ struct adc_jack_data { struct device *dev; @@ -158,14 +160,12 @@ static int adc_jack_probe(struct platform_device *pdev) return 0; } -static int adc_jack_remove(struct platform_device *pdev) +static void adc_jack_remove(struct platform_device *pdev) { struct adc_jack_data *data = platform_get_drvdata(pdev); free_irq(data->irq, data); cancel_work_sync(&data->handler.work); - - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index a703a8315634..d3bcbe839c09 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -108,7 +108,7 @@ struct axp288_extcon_info { }; static const struct x86_cpu_id cherry_trail_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL), + X86_MATCH_VFM(INTEL_ATOM_AIRMONT, NULL), {} }; diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c index e458ce0c45ab..b11b43171063 100644 --- a/drivers/extcon/extcon-fsa9480.c +++ b/drivers/extcon/extcon-fsa9480.c @@ -350,7 +350,7 @@ static const struct dev_pm_ops fsa9480_pm_ops = { }; static const struct i2c_device_id fsa9480_id[] = { - { "fsa9480", 0 }, + { "fsa9480" }, {} }; MODULE_DEVICE_TABLE(i2c, fsa9480_id); diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 2c55f06ba699..8131a3d7d562 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -461,14 +461,6 @@ static int cht_wc_extcon_psy_get_prop(struct power_supply *psy, return 0; } -static const enum power_supply_usb_type cht_wc_extcon_psy_usb_types[] = { - POWER_SUPPLY_USB_TYPE_SDP, - POWER_SUPPLY_USB_TYPE_CDP, - POWER_SUPPLY_USB_TYPE_DCP, - POWER_SUPPLY_USB_TYPE_ACA, - POWER_SUPPLY_USB_TYPE_UNKNOWN, -}; - static const enum power_supply_property cht_wc_extcon_psy_props[] = { POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_ONLINE, @@ -477,8 +469,11 @@ static const enum power_supply_property cht_wc_extcon_psy_props[] = { static const struct power_supply_desc cht_wc_extcon_psy_desc = { .name = "cht_wcove_pwrsrc", .type = POWER_SUPPLY_TYPE_USB, - .usb_types = cht_wc_extcon_psy_usb_types, - .num_usb_types = ARRAY_SIZE(cht_wc_extcon_psy_usb_types), + .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | + BIT(POWER_SUPPLY_USB_TYPE_CDP) | + BIT(POWER_SUPPLY_USB_TYPE_DCP) | + BIT(POWER_SUPPLY_USB_TYPE_ACA) | + BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), .properties = cht_wc_extcon_psy_props, .num_properties = ARRAY_SIZE(cht_wc_extcon_psy_props), .get_property = cht_wc_extcon_psy_get_prop, @@ -617,13 +612,11 @@ disable_sw_control: return ret; } -static int cht_wc_extcon_remove(struct platform_device *pdev) +static void cht_wc_extcon_remove(struct platform_device *pdev) { struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev); cht_wc_extcon_sw_control(ext, false); - - return 0; } static const struct platform_device_id cht_wc_extcon_table[] = { diff --git a/drivers/extcon/extcon-intel-mrfld.c b/drivers/extcon/extcon-intel-mrfld.c index cd1a5f230077..9219f4328d70 100644 --- a/drivers/extcon/extcon-intel-mrfld.c +++ b/drivers/extcon/extcon-intel-mrfld.c @@ -214,27 +214,21 @@ static int mrfld_extcon_probe(struct platform_device *pdev) data->edev = devm_extcon_dev_allocate(dev, mrfld_extcon_cable); if (IS_ERR(data->edev)) - return -ENOMEM; + return PTR_ERR(data->edev); ret = devm_extcon_dev_register(dev, data->edev); - if (ret < 0) { - dev_err(dev, "can't register extcon device: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "can't register extcon device\n"); ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_extcon_interrupt, IRQF_ONESHOT | IRQF_SHARED, pdev->name, data); - if (ret) { - dev_err(dev, "can't register IRQ handler: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't register IRQ handler\n"); ret = regmap_read(regmap, BCOVE_ID, &id); - if (ret) { - dev_err(dev, "can't read PMIC ID: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "can't read PMIC ID\n"); data->id = id; @@ -263,13 +257,11 @@ static int mrfld_extcon_probe(struct platform_device *pdev) return 0; } -static int mrfld_extcon_remove(struct platform_device *pdev) +static void mrfld_extcon_remove(struct platform_device *pdev) { struct mrfld_extcon_data *data = platform_get_drvdata(pdev); mrfld_extcon_sw_control(data, false); - - return 0; } static const struct platform_device_id mrfld_extcon_id_table[] = { diff --git a/drivers/extcon/extcon-lc824206xa.c b/drivers/extcon/extcon-lc824206xa.c new file mode 100644 index 000000000000..56938748aea8 --- /dev/null +++ b/drivers/extcon/extcon-lc824206xa.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ON Semiconductor LC824206XA Micro USB Switch driver + * + * Copyright (c) 2024 Hans de Goede <hansg@kernel.org> + * + * ON Semiconductor has an "Advance Information" datasheet available + * (ENA2222-D.PDF), but no full datasheet. So there is no documentation + * available for the registers. + * + * This driver is based on the register info from the extcon-fsa9285.c driver, + * from the Lollipop Android sources for the Lenovo Yoga Tablet 2 (Pro) + * 830 / 1050 / 1380 models. Note despite the name this is actually a driver + * for the LC824206XA not the FSA9285. The Android sources can be downloaded + * from Lenovo's support page for these tablets, filename: + * yoga_tab_2_osc_android_to_lollipop_201505.rar. + */ + +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/extcon-provider.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> + +/* + * Register defines as mentioned above there is no datasheet with register + * info, so this may not be 100% accurate. + */ +#define REG00 0x00 +#define REG00_INIT_VALUE 0x01 + +#define REG_STATUS 0x01 +#define STATUS_OVP BIT(0) +#define STATUS_DATA_SHORT BIT(1) +#define STATUS_VBUS_PRESENT BIT(2) +#define STATUS_USB_ID GENMASK(7, 3) +#define STATUS_USB_ID_GND 0x80 +#define STATUS_USB_ID_ACA 0xf0 +#define STATUS_USB_ID_FLOAT 0xf8 + +/* + * This controls the DP/DM muxes + other switches, + * meaning of individual bits is unknown. + */ +#define REG_SWITCH_CONTROL 0x02 +#define SWITCH_STEREO_MIC 0xc8 +#define SWITCH_USB_HOST 0xec +#define SWITCH_DISCONNECTED 0xf8 +#define SWITCH_USB_DEVICE 0xfc + +/* 5 bits? ADC 0x10 GND, 0x1a-0x1f ACA, 0x1f float */ +#define REG_ID_PIN_ADC_VALUE 0x03 + +/* Masks for all 3 interrupt registers */ +#define INTR_ID_PIN_CHANGE BIT(0) +#define INTR_VBUS_CHANGE BIT(1) +/* Both of these get set after a continuous mode ADC conversion */ +#define INTR_ID_PIN_ADC_INT1 BIT(2) +#define INTR_ID_PIN_ADC_INT2 BIT(3) +/* Charger type available in reg 0x09 */ +#define INTR_CHARGER_DET_DONE BIT(4) +#define INTR_OVP BIT(5) + +/* There are 7 interrupt sources, bit 6 use is unknown (OCP?) */ +#define INTR_ALL GENMASK(6, 0) + +/* Unmask interrupts this driver cares about */ +#define INTR_MASK \ + (INTR_ALL & ~(INTR_ID_PIN_CHANGE | INTR_VBUS_CHANGE | INTR_CHARGER_DET_DONE)) + +/* Active (event happened and not cleared yet) interrupts */ +#define REG_INTR_STATUS 0x04 + +/* + * Writing a 1 to a bit here clears it in INTR_STATUS. These bits do NOT + * auto-reset to 0, so these must be set to 0 manually after clearing. + */ +#define REG_INTR_CLEAR 0x05 + +/* Interrupts which bit is set to 1 here will not raise the HW IRQ */ +#define REG_INTR_MASK 0x06 + +/* ID pin ADC control, meaning of individual bits is unknown */ +#define REG_ID_PIN_ADC_CTRL 0x07 +#define ID_PIN_ADC_AUTO 0x40 +#define ID_PIN_ADC_CONTINUOUS 0x44 + +#define REG_CHARGER_DET 0x08 +#define CHARGER_DET_ON BIT(0) +#define CHARGER_DET_CDP_ON BIT(1) +#define CHARGER_DET_CDP_VAL BIT(2) + +#define REG_CHARGER_TYPE 0x09 +#define CHARGER_TYPE_UNKNOWN 0x00 +#define CHARGER_TYPE_DCP 0x01 +#define CHARGER_TYPE_SDP_OR_CDP 0x04 +#define CHARGER_TYPE_QC 0x06 + +#define REG10 0x10 +#define REG10_INIT_VALUE 0x00 + +struct lc824206xa_data { + struct work_struct work; + struct i2c_client *client; + struct extcon_dev *edev; + struct power_supply *psy; + struct regulator *vbus_boost; + unsigned int usb_type; + unsigned int cable; + unsigned int previous_cable; + u8 switch_control; + u8 previous_switch_control; + bool vbus_ok; + bool vbus_boost_enabled; + bool fastcharge_over_miclr; +}; + +static const unsigned int lc824206xa_cables[] = { + EXTCON_USB_HOST, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_ACA, + EXTCON_CHG_USB_FAST, + EXTCON_NONE, +}; + +/* read/write reg helpers to add error logging to smbus byte functions */ +static int lc824206xa_read_reg(struct lc824206xa_data *data, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, reg); + if (ret < 0) + dev_err(&data->client->dev, "Error %d reading reg 0x%02x\n", ret, reg); + + return ret; +} + +static int lc824206xa_write_reg(struct lc824206xa_data *data, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, reg, val); + if (ret < 0) + dev_err(&data->client->dev, "Error %d writing reg 0x%02x\n", ret, reg); + + return ret; +} + +static int lc824206xa_get_id(struct lc824206xa_data *data) +{ + int ret; + + ret = lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_CONTINUOUS); + if (ret) + return ret; + + ret = lc824206xa_read_reg(data, REG_ID_PIN_ADC_VALUE); + + lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_AUTO); + + return ret; +} + +static void lc824206xa_set_vbus_boost(struct lc824206xa_data *data, bool enable) +{ + int ret; + + if (data->vbus_boost_enabled == enable) + return; + + if (enable) + ret = regulator_enable(data->vbus_boost); + else + ret = regulator_disable(data->vbus_boost); + + if (ret == 0) + data->vbus_boost_enabled = enable; + else + dev_err(&data->client->dev, "Error updating Vbus boost regulator: %d\n", ret); +} + +static void lc824206xa_charger_detect(struct lc824206xa_data *data) +{ + int charger_type, ret; + + charger_type = lc824206xa_read_reg(data, REG_CHARGER_TYPE); + if (charger_type < 0) + return; + + dev_dbg(&data->client->dev, "charger type 0x%02x\n", charger_type); + + switch (charger_type) { + case CHARGER_TYPE_UNKNOWN: + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + /* Treat as SDP */ + data->cable = EXTCON_CHG_USB_SDP; + data->switch_control = SWITCH_USB_DEVICE; + break; + case CHARGER_TYPE_SDP_OR_CDP: + data->usb_type = POWER_SUPPLY_USB_TYPE_SDP; + data->cable = EXTCON_CHG_USB_SDP; + data->switch_control = SWITCH_USB_DEVICE; + + ret = lc824206xa_write_reg(data, REG_CHARGER_DET, + CHARGER_DET_CDP_ON | CHARGER_DET_ON); + if (ret < 0) + break; + + msleep(100); + ret = lc824206xa_read_reg(data, REG_CHARGER_DET); + if (ret >= 0 && (ret & CHARGER_DET_CDP_VAL)) { + data->usb_type = POWER_SUPPLY_USB_TYPE_CDP; + data->cable = EXTCON_CHG_USB_CDP; + } + + lc824206xa_write_reg(data, REG_CHARGER_DET, CHARGER_DET_ON); + break; + case CHARGER_TYPE_DCP: + data->usb_type = POWER_SUPPLY_USB_TYPE_DCP; + data->cable = EXTCON_CHG_USB_DCP; + if (data->fastcharge_over_miclr) + data->switch_control = SWITCH_STEREO_MIC; + else + data->switch_control = SWITCH_DISCONNECTED; + break; + case CHARGER_TYPE_QC: + data->usb_type = POWER_SUPPLY_USB_TYPE_DCP; + data->cable = EXTCON_CHG_USB_DCP; + data->switch_control = SWITCH_DISCONNECTED; + break; + default: + dev_warn(&data->client->dev, "Unknown charger type: 0x%02x\n", charger_type); + break; + } +} + +static void lc824206xa_work(struct work_struct *work) +{ + struct lc824206xa_data *data = container_of(work, struct lc824206xa_data, work); + bool vbus_boost_enable = false; + int status, id; + + status = lc824206xa_read_reg(data, REG_STATUS); + if (status < 0) + return; + + dev_dbg(&data->client->dev, "status 0x%02x\n", status); + + data->vbus_ok = (status & (STATUS_VBUS_PRESENT | STATUS_OVP)) == STATUS_VBUS_PRESENT; + + /* Read id pin ADC if necessary */ + switch (status & STATUS_USB_ID) { + case STATUS_USB_ID_GND: + case STATUS_USB_ID_FLOAT: + break; + default: + /* Happens when the connector is inserted slowly, log at dbg level */ + dev_dbg(&data->client->dev, "Unknown status 0x%02x\n", status); + fallthrough; + case STATUS_USB_ID_ACA: + id = lc824206xa_get_id(data); + dev_dbg(&data->client->dev, "RID 0x%02x\n", id); + switch (id) { + case 0x10: + status = STATUS_USB_ID_GND; + break; + case 0x18 ... 0x1e: + status = STATUS_USB_ID_ACA; + break; + case 0x1f: + status = STATUS_USB_ID_FLOAT; + break; + default: + dev_warn(&data->client->dev, "Unknown RID 0x%02x\n", id); + return; + } + } + + /* Check for out of spec OTG charging hubs, treat as ACA */ + if ((status & STATUS_USB_ID) == STATUS_USB_ID_GND && + data->vbus_ok && !data->vbus_boost_enabled) { + dev_info(&data->client->dev, "Out of spec USB host adapter with Vbus present, not enabling 5V output\n"); + status = STATUS_USB_ID_ACA; + } + + switch (status & STATUS_USB_ID) { + case STATUS_USB_ID_ACA: + data->usb_type = POWER_SUPPLY_USB_TYPE_ACA; + data->cable = EXTCON_CHG_USB_ACA; + data->switch_control = SWITCH_USB_HOST; + break; + case STATUS_USB_ID_GND: + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + data->cable = EXTCON_USB_HOST; + data->switch_control = SWITCH_USB_HOST; + vbus_boost_enable = true; + break; + case STATUS_USB_ID_FLOAT: + /* When fast charging with Vbus > 5V, OVP will be set */ + if (data->fastcharge_over_miclr && + data->switch_control == SWITCH_STEREO_MIC && + (status & STATUS_OVP)) { + data->cable = EXTCON_CHG_USB_FAST; + break; + } + + if (data->vbus_ok) { + lc824206xa_charger_detect(data); + } else { + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + data->cable = EXTCON_NONE; + data->switch_control = SWITCH_DISCONNECTED; + } + break; + } + + lc824206xa_set_vbus_boost(data, vbus_boost_enable); + + if (data->switch_control != data->previous_switch_control) { + lc824206xa_write_reg(data, REG_SWITCH_CONTROL, data->switch_control); + data->previous_switch_control = data->switch_control; + } + + if (data->cable != data->previous_cable) { + extcon_set_state_sync(data->edev, data->previous_cable, false); + extcon_set_state_sync(data->edev, data->cable, true); + data->previous_cable = data->cable; + } + + power_supply_changed(data->psy); +} + +static irqreturn_t lc824206xa_irq(int irq, void *_data) +{ + struct lc824206xa_data *data = _data; + int intr_status; + + intr_status = lc824206xa_read_reg(data, REG_INTR_STATUS); + if (intr_status < 0) + intr_status = INTR_ALL; /* Should never happen, clear all */ + + dev_dbg(&data->client->dev, "interrupt 0x%02x\n", intr_status); + + lc824206xa_write_reg(data, REG_INTR_CLEAR, intr_status); + lc824206xa_write_reg(data, REG_INTR_CLEAR, 0); + + schedule_work(&data->work); + return IRQ_HANDLED; +} + +/* + * Newer charger (power_supply) drivers expect the max input current to be + * provided by a parent power_supply device for the charger chip. + */ +static int lc824206xa_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct lc824206xa_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = data->vbus_ok && !data->vbus_boost_enabled; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = data->usb_type; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + switch (data->usb_type) { + case POWER_SUPPLY_USB_TYPE_DCP: + case POWER_SUPPLY_USB_TYPE_ACA: + val->intval = 2000000; + break; + case POWER_SUPPLY_USB_TYPE_CDP: + val->intval = 1500000; + break; + default: + val->intval = 500000; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static const enum power_supply_property lc824206xa_psy_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static const struct power_supply_desc lc824206xa_psy_desc = { + .name = "lc824206xa-charger-detect", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = BIT(POWER_SUPPLY_USB_TYPE_SDP) | + BIT(POWER_SUPPLY_USB_TYPE_CDP) | + BIT(POWER_SUPPLY_USB_TYPE_DCP) | + BIT(POWER_SUPPLY_USB_TYPE_ACA) | + BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), + .properties = lc824206xa_psy_props, + .num_properties = ARRAY_SIZE(lc824206xa_psy_props), + .get_property = lc824206xa_psy_get_prop, +}; + +static int lc824206xa_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = { }; + struct device *dev = &client->dev; + struct lc824206xa_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + INIT_WORK(&data->work, lc824206xa_work); + data->cable = EXTCON_NONE; + data->previous_cable = EXTCON_NONE; + data->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + /* Some designs use a custom fast-charge protocol over the mic L/R inputs */ + data->fastcharge_over_miclr = + device_property_read_bool(dev, "onnn,enable-miclr-for-dcp"); + + data->vbus_boost = devm_regulator_get(dev, "vbus"); + if (IS_ERR(data->vbus_boost)) + return dev_err_probe(dev, PTR_ERR(data->vbus_boost), + "getting regulator\n"); + + /* Init */ + ret = lc824206xa_write_reg(data, REG00, REG00_INIT_VALUE); + ret |= lc824206xa_write_reg(data, REG10, REG10_INIT_VALUE); + msleep(100); + ret |= lc824206xa_write_reg(data, REG_INTR_CLEAR, INTR_ALL); + ret |= lc824206xa_write_reg(data, REG_INTR_CLEAR, 0); + ret |= lc824206xa_write_reg(data, REG_INTR_MASK, INTR_MASK); + ret |= lc824206xa_write_reg(data, REG_ID_PIN_ADC_CTRL, ID_PIN_ADC_AUTO); + ret |= lc824206xa_write_reg(data, REG_CHARGER_DET, CHARGER_DET_ON); + if (ret) + return -EIO; + + /* Initialize extcon device */ + data->edev = devm_extcon_dev_allocate(dev, lc824206xa_cables); + if (IS_ERR(data->edev)) + return PTR_ERR(data->edev); + + ret = devm_extcon_dev_register(dev, data->edev); + if (ret) + return dev_err_probe(dev, ret, "registering extcon device\n"); + + psy_cfg.drv_data = data; + data->psy = devm_power_supply_register(dev, &lc824206xa_psy_desc, &psy_cfg); + if (IS_ERR(data->psy)) + return dev_err_probe(dev, PTR_ERR(data->psy), "registering power supply\n"); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, lc824206xa_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + KBUILD_MODNAME, data); + if (ret) + return dev_err_probe(dev, ret, "requesting IRQ\n"); + + /* Sync initial state */ + schedule_work(&data->work); + return 0; +} + +static const struct i2c_device_id lc824206xa_i2c_ids[] = { + { "lc824206xa" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lc824206xa_i2c_ids); + +static struct i2c_driver lc824206xa_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = lc824206xa_probe, + .id_table = lc824206xa_i2c_ids, +}; + +module_i2c_driver(lc824206xa_driver); + +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_DESCRIPTION("LC824206XA Micro USB Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c index d7795607f693..b2ee4ff8b04d 100644 --- a/drivers/extcon/extcon-max3355.c +++ b/drivers/extcon/extcon-max3355.c @@ -112,13 +112,11 @@ static int max3355_probe(struct platform_device *pdev) return 0; } -static int max3355_remove(struct platform_device *pdev) +static void max3355_remove(struct platform_device *pdev) { struct max3355_data *data = platform_get_drvdata(pdev); gpiod_set_value_cansleep(data->shdn_gpiod, 0); - - return 0; } static const struct of_device_id max3355_match_table[] = { diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index acb11a54f875..2ae9f7f1a67f 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -928,7 +928,7 @@ err_muic_irq: return ret; } -static int max77843_muic_remove(struct platform_device *pdev) +static void max77843_muic_remove(struct platform_device *pdev) { struct max77843_muic_info *info = platform_get_drvdata(pdev); struct max77693_dev *max77843 = info->max77843; @@ -936,8 +936,6 @@ static int max77843_muic_remove(struct platform_device *pdev) cancel_work_sync(&info->irq_work); regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic); i2c_unregister_device(max77843->i2c_muic); - - return 0; } static const struct platform_device_id max77843_muic_id[] = { diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 4616da7e5430..78ad86c4a3be 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -338,7 +338,7 @@ static const struct of_device_id ptn5150_dt_match[] = { MODULE_DEVICE_TABLE(of, ptn5150_dt_match); static const struct i2c_device_id ptn5150_i2c_id[] = { - { "ptn5150", 0 }, + { "ptn5150" }, { } }; MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c index a592bab77538..82b60b927e41 100644 --- a/drivers/extcon/extcon-rtk-type-c.c +++ b/drivers/extcon/extcon-rtk-type-c.c @@ -13,7 +13,6 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/of_gpio.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/syscalls.h> @@ -1370,6 +1369,8 @@ static int extcon_rtk_type_c_probe(struct platform_device *pdev) } type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), GFP_KERNEL); + if (!type_c->type_c_cfg) + return -ENOMEM; memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg)); @@ -1779,7 +1780,7 @@ static const struct dev_pm_ops extcon_rtk_type_c_pm_ops = { static struct platform_driver extcon_rtk_type_c_driver = { .probe = extcon_rtk_type_c_probe, - .remove_new = extcon_rtk_type_c_remove, + .remove = extcon_rtk_type_c_remove, .driver = { .name = "extcon-rtk-type_c", .of_match_table = extcon_rtk_type_c_match, diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 40d967a11e87..5e8ad21ad206 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -193,14 +193,12 @@ static int usb_extcon_probe(struct platform_device *pdev) return 0; } -static int usb_extcon_remove(struct platform_device *pdev) +static void usb_extcon_remove(struct platform_device *pdev) { struct usb_extcon_info *info = platform_get_drvdata(pdev); cancel_delayed_work_sync(&info->wq_detcable); device_init_wakeup(&pdev->dev, false); - - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c index fde1db62be0d..1fb627ea8b50 100644 --- a/drivers/extcon/extcon-usbc-cros-ec.c +++ b/drivers/extcon/extcon-usbc-cros-ec.c @@ -480,14 +480,12 @@ unregister_notifier: return ret; } -static int extcon_cros_ec_remove(struct platform_device *pdev) +static void extcon_cros_ec_remove(struct platform_device *pdev) { struct cros_ec_extcon_info *info = platform_get_drvdata(pdev); blocking_notifier_chain_unregister(&info->ec->event_notifier, &info->notifier); - - return 0; } #ifdef CONFIG_PM_SLEEP |