+Cc: Heikki, On Fri, Apr 21, 2017 at 4:01 PM, Hans de Goede <hdego...@redhat.com> wrote: > On some boards the Whiskey Cove PMIC is combined with an external USB > Type-C controller, in this case extcon consumers should use the Type-C > extcon state, except when the USB Type-C controller detects a current > limit of 500 mA which may indicate USB-C to USB-A cable at which point > the extcon consumer should use the Whiskey Cove's BC-1.2 detection's > state. > > Since the Type-C controller info is incomplete and needs to be > supplemented with the BC1.2 detection result in some cases, this > commit makes the intel-cht-wc extcon driver monitor the extcon device > registered by the Type-C controller and report that as our extcon state > except when BC-1.2 detection should be used. This allows extcon > consumers on these boards to keep monitoring only the intel-cht-wc extcon > and then get complete extcon info from that, which combines the Type-C > and BC-1.2 info. > > Signed-off-by: Hans de Goede <hdego...@redhat.com> > --- > drivers/extcon/extcon-intel-cht-wc.c | 96 > ++++++++++++++++++++++++++++++------ > 1 file changed, 80 insertions(+), 16 deletions(-) > > diff --git a/drivers/extcon/extcon-intel-cht-wc.c > b/drivers/extcon/extcon-intel-cht-wc.c > index 684f6b2..e510188 100644 > --- a/drivers/extcon/extcon-intel-cht-wc.c > +++ b/drivers/extcon/extcon-intel-cht-wc.c > @@ -15,6 +15,7 @@ > * more details. > */ > > +#include <linux/acpi.h> > #include <linux/extcon.h> > #include <linux/interrupt.h> > #include <linux/kernel.h> > @@ -88,6 +89,7 @@ static const unsigned int cht_wc_extcon_cables[] = { > EXTCON_CHG_USB_CDP, > EXTCON_CHG_USB_DCP, > EXTCON_CHG_USB_ACA, > + EXTCON_CHG_USB_FAST, > EXTCON_NONE, > }; > > @@ -95,6 +97,9 @@ struct cht_wc_extcon_data { > struct device *dev; > struct regmap *regmap; > struct extcon_dev *edev; > + struct extcon_dev *usbc; > + struct notifier_block usbc_nb; > + struct work_struct work; > unsigned int previous_cable; > bool usb_host; > }; > @@ -224,8 +229,32 @@ static void cht_wc_extcon_set_state(struct > cht_wc_extcon_data *ext, > extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host); > } > > -static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext) > +static void cht_wc_extcon_usb_c_event(struct work_struct *work) > { > + struct cht_wc_extcon_data *ext = > + container_of(work, struct cht_wc_extcon_data, work); > + unsigned int cable = EXTCON_NONE; > + > + if (extcon_get_state(ext->usbc, EXTCON_CHG_USB_SDP) > 0) { > + /* > + * USB-C to USB-A cable ? Try BC1.2 charger detection from > + * WC PMIC, ignore charger detection errors. > + */ > + cable = cht_wc_extcon_get_charger(ext, true); > + } else if (extcon_get_state(ext->usbc, EXTCON_CHG_USB_CDP) > 0) { > + cable = EXTCON_CHG_USB_CDP; > + } else if (extcon_get_state(ext->usbc, EXTCON_CHG_USB_FAST) > 0) { > + cable = EXTCON_CHG_USB_FAST; > + } > + > + ext->usb_host = (extcon_get_state(ext->usbc, EXTCON_USB_HOST) > 0); > + cht_wc_extcon_set_state(ext, cable); > +} > + > +static void cht_wc_extcon_pwrsrc_event(struct work_struct *work) > +{ > + struct cht_wc_extcon_data *ext = > + container_of(work, struct cht_wc_extcon_data, work); > int ret, pwrsrc_sts, id; > unsigned int cable = EXTCON_NONE; > /* Ignore errors in host mode, as the 5v boost converter is on then */ > @@ -258,7 +287,7 @@ static irqreturn_t cht_wc_extcon_isr(int irq, void *data) > return IRQ_NONE; > } > > - cht_wc_extcon_pwrsrc_event(ext); > + schedule_work(&ext->work); > > ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ, irqs); > if (ret) { > @@ -269,6 +298,17 @@ static irqreturn_t cht_wc_extcon_isr(int irq, void *data) > return IRQ_HANDLED; > } > > +static int cht_wc_extcon_usbc_evt(struct notifier_block *nb, > + unsigned long event, void *param) > +{ > + struct cht_wc_extcon_data *ext = > + container_of(nb, struct cht_wc_extcon_data, usbc_nb); > + > + schedule_work(&ext->work); > + > + return NOTIFY_OK; > +} > + > static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool > enable) > { > int ret, mask, val; > @@ -300,6 +340,15 @@ static int cht_wc_extcon_probe(struct platform_device > *pdev) > ext->regmap = pmic->regmap; > ext->previous_cable = EXTCON_NONE; > > + if (acpi_dev_present("INT33FE", NULL, -1)) { > + ext->usbc = extcon_get_extcon_dev("fusb302"); > + if (!ext->usbc) > + return -EPROBE_DEFER; > + > + dev_info(&pdev->dev, > + "Using FUSB302 extcon for USB Type-C cable info\n"); > + } > + > /* Initialize extcon device */ > ext->edev = devm_extcon_dev_allocate(ext->dev, cht_wc_extcon_cables); > if (IS_ERR(ext->edev)) > @@ -335,25 +384,40 @@ static int cht_wc_extcon_probe(struct platform_device > *pdev) > /* Route D+ and D- to PMIC for initial charger detection */ > cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC); > > - /* Get initial state */ > - cht_wc_extcon_pwrsrc_event(ext); > - > - ret = devm_request_threaded_irq(ext->dev, irq, NULL, > cht_wc_extcon_isr, > - IRQF_ONESHOT, pdev->name, ext); > - if (ret) { > - dev_err(ext->dev, "Error requesting interrupt: %d\n", ret); > - goto disable_sw_control; > - } > + if (ext->usbc) { > + INIT_WORK(&ext->work, cht_wc_extcon_usb_c_event); > + ext->usbc_nb.notifier_call = cht_wc_extcon_usbc_evt; > + ret = devm_extcon_register_notifier_all(&pdev->dev, ext->usbc, > + &ext->usbc_nb); > + if (ret) { > + dev_err(&pdev->dev, > + "Error registering usbc extcon notifier: > %d\n", > + ret); > + goto disable_sw_control; > + } > + } else { > + INIT_WORK(&ext->work, cht_wc_extcon_pwrsrc_event); > + ret = devm_request_threaded_irq(ext->dev, irq, NULL, > + cht_wc_extcon_isr, > IRQF_ONESHOT, > + pdev->name, ext); > + if (ret) { > + dev_err(ext->dev, "Error requesting irq: %d\n", ret); > + goto disable_sw_control; > + } > > - /* Unmask irqs */ > - ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, > + /* Unmask irqs */ > + ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, > (int)~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_ID_GND | > CHT_WC_PWRSRC_ID_FLOAT)); > - if (ret) { > - dev_err(ext->dev, "Error writing irq-mask: %d\n", ret); > - goto disable_sw_control; > + if (ret) { > + dev_err(ext->dev, "Error writing irq-mask: %d\n", > ret); > + goto disable_sw_control; > + } > } > > + /* Get initial state */ > + schedule_work(&ext->work); > + > platform_set_drvdata(pdev, ext); > > return 0; > -- > 2.9.3 >
-- With Best Regards, Andy Shevchenko