diff options
Diffstat (limited to 'drivers')
72 files changed, 2221 insertions, 980 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 3d89e60a3e71..04788d92ea52 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -96,6 +96,12 @@ config EXTCON_PALMAS Say Y here to enable support for USB peripheral and USB host detection by palmas usb. +config EXTCON_QCOM_SPMI_MISC + tristate "Qualcomm USB extcon support" + help + Say Y here to enable SPMI PMIC based USB cable detection + support on Qualcomm PMICs such as PM8941. + config EXTCON_RT8973A tristate "Richtek RT8973A EXTCON support" depends on I2C diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 972c813c375b..31a0a999c4fb 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o +obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 44e48aa78a84..bc538708c753 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -3,6 +3,9 @@ * * Analog Jack extcon driver with ADC-based detection capability. * + * Copyright (C) 2016 Samsung Electronics + * Chanwoo Choi <cw00.choi@samsung.com> + * * Copyright (C) 2012 Samsung Electronics * MyungJoo Ham <myungjoo.ham@samsung.com> * @@ -58,7 +61,7 @@ static void adc_jack_handler(struct work_struct *work) struct adc_jack_data *data = container_of(to_delayed_work(work), struct adc_jack_data, handler); - u32 state = 0; + struct adc_jack_cond *def; int ret, adc_val; int i; @@ -70,17 +73,18 @@ static void adc_jack_handler(struct work_struct *work) /* Get state from adc value with adc_conditions */ for (i = 0; i < data->num_conditions; i++) { - struct adc_jack_cond *def = &data->adc_conditions[i]; - if (!def->state) - break; + def = &data->adc_conditions[i]; if (def->min_adc <= adc_val && def->max_adc >= adc_val) { - state = def->state; - break; + extcon_set_state_sync(data->edev, def->id, true); + return; } } - /* if no def has met, it means state = 0 (no cables attached) */ - extcon_set_state(data->edev, state); + /* Set the detached state if adc value is not included in the range */ + for (i = 0; i < data->num_conditions; i++) { + def = &data->adc_conditions[i]; + extcon_set_state_sync(data->edev, def->id, false); + } } static irqreturn_t adc_jack_irq_thread(int irq, void *_data) @@ -114,16 +118,14 @@ static int adc_jack_probe(struct platform_device *pdev) return -ENOMEM; } - if (!pdata->adc_conditions || - !pdata->adc_conditions[0].state) { + if (!pdata->adc_conditions) { dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); return -EINVAL; } data->adc_conditions = pdata->adc_conditions; /* Check the length of array and set num_conditions */ - for (i = 0; data->adc_conditions[i].state; i++) - ; + for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++); data->num_conditions = i; data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); @@ -158,6 +160,7 @@ static int adc_jack_probe(struct platform_device *pdev) if (data->wakeup_source) device_init_wakeup(&pdev->dev, 1); + adc_jack_handler(&data->handler.work); return 0; } diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 1d8e0a57bd51..56e6c4c7c60d 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -183,7 +183,7 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info, if (clamp) val = ARIZONA_RMV_SHRT_HP1L; break; - }; + } snd_soc_dapm_mutex_lock(arizona->dapm); @@ -614,7 +614,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) } /* If the cable was removed while measuring ignore the result */ - ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL); + ret = extcon_get_state(info->edev, EXTCON_MECHANICAL); if (ret < 0) { dev_err(arizona->dev, "Failed to check cable state: %d\n", ret); @@ -649,7 +649,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) else report = EXTCON_JACK_HEADPHONE; - ret = extcon_set_cable_state_(info->edev, report, true); + ret = extcon_set_state_sync(info->edev, report, true); if (ret != 0) dev_err(arizona->dev, "Failed to report HP/line: %d\n", ret); @@ -732,7 +732,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -789,7 +789,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -829,7 +829,7 @@ static void arizona_micd_detect(struct work_struct *work) mutex_lock(&info->lock); /* If the cable was removed while measuring ignore the result */ - ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL); + ret = extcon_get_state(info->edev, EXTCON_MECHANICAL); if (ret < 0) { dev_err(arizona->dev, "Failed to check cable state: %d\n", ret); @@ -914,7 +914,7 @@ static void arizona_micd_detect(struct work_struct *work) arizona_identify_headphone(info); - ret = extcon_set_cable_state_(info->edev, + ret = extcon_set_state_sync(info->edev, EXTCON_JACK_MICROPHONE, true); if (ret != 0) dev_err(arizona->dev, "Headset report failed: %d\n", @@ -1108,7 +1108,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) if (info->last_jackdet == present) { dev_dbg(arizona->dev, "Detected jack\n"); - ret = extcon_set_cable_state_(info->edev, + ret = extcon_set_state_sync(info->edev, EXTCON_MECHANICAL, true); if (ret != 0) @@ -1149,10 +1149,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data) info->micd_ranges[i].key, 0); input_sync(info->input); - ret = extcon_update_state(info->edev, 0xffffffff, 0); - if (ret != 0) - dev_err(arizona->dev, "Removal report failed: %d\n", - ret); + for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) { + ret = extcon_set_state_sync(info->edev, + arizona_cable[i], false); + if (ret != 0) + dev_err(arizona->dev, + "Removal report failed: %d\n", ret); + } regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index fd55c2f2080a..42f41e808292 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -189,19 +189,19 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) switch (chrg_type) { case DET_STAT_SDP: - dev_dbg(info->dev, "sdp cable is connecetd\n"); + dev_dbg(info->dev, "sdp cable is connected\n"); notify_otg = true; notify_charger = true; cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP: - dev_dbg(info->dev, "cdp cable is connecetd\n"); + dev_dbg(info->dev, "cdp cable is connected\n"); notify_otg = true; notify_charger = true; cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP: - dev_dbg(info->dev, "dcp cable is connecetd\n"); + dev_dbg(info->dev, "dcp cable is connected\n"); notify_charger = true; cable = EXTCON_CHG_USB_DCP; break; @@ -226,7 +226,7 @@ notify_otg: } if (notify_charger) - extcon_set_cable_state_(info->edev, cable, vbus_attach); + extcon_set_state_sync(info->edev, cable, vbus_attach); /* Clear the flags on disconnect event */ if (!vbus_attach) diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index d023789f0fda..e7aebbc945f6 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -49,7 +49,7 @@ static void gpio_extcon_work(struct work_struct *work) state = gpiod_get_value_cansleep(data->id_gpiod); if (data->pdata->gpio_active_low) state = !state; - extcon_set_state(data->edev, state); + extcon_set_state_sync(data->edev, data->pdata->extcon_id, state); } static irqreturn_t gpio_irq_handler(int irq, void *dev_id) diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index 852a7112f451..12e26c4e7763 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -3,7 +3,7 @@ * * Copyright (C) 2013,2014 Samsung Electronics * Chanwoo Choi <cw00.choi@samsung.com> - * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * Krzysztof Kozlowski <krzk@kernel.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -357,7 +357,7 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -454,24 +454,24 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX14577_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_1A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX14577_CHARGER_TYPE_NONE: @@ -791,6 +791,6 @@ static struct platform_driver max14577_muic_driver = { module_platform_driver(max14577_muic_driver); MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver"); -MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:extcon-max14577"); diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c index c24abec5d06c..533e16a952b8 100644 --- a/drivers/extcon/extcon-max3355.c +++ b/drivers/extcon/extcon-max3355.c @@ -39,16 +39,16 @@ static irqreturn_t max3355_id_irq(int irq, void *dev_id) * As we don't have event for USB peripheral cable attached, * we simulate USB peripheral attach here. */ - extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, false); - extcon_set_cable_state_(data->edev, EXTCON_USB, true); + extcon_set_state_sync(data->edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(data->edev, EXTCON_USB, true); } else { /* * ID = 0 means USB HOST cable attached. * As we don't have event for USB peripheral cable detached, * we simulate USB peripheral detach here. */ - extcon_set_cable_state_(data->edev, EXTCON_USB, false); - extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(data->edev, EXTCON_USB, false); + extcon_set_state_sync(data->edev, EXTCON_USB_HOST, true); } return IRQ_HANDLED; diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index f17cb76b567c..68dbcb814b2f 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -505,8 +505,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); goto out; case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */ dock_id = EXTCON_DOCK; @@ -514,8 +514,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD: /* Dock-Audio */ dock_id = EXTCON_DOCK; if (!attached) { - extcon_set_cable_state_(info->edev, EXTCON_USB, false); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, false); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, false); } break; @@ -530,7 +530,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, dock_id, attached); + extcon_set_state_sync(info->edev, dock_id, attached); out: return 0; @@ -596,7 +596,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX77693_MUIC_GND_AV_CABLE_LOAD: /* Audio Video Cable with load, PATH:AUDIO */ @@ -604,14 +604,14 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) attached); if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77693_MUIC_GND_MHL: case MAX77693_MUIC_GND_MHL_VB: /* MHL or MHL with USB/TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s cable of gnd type\n", @@ -653,7 +653,7 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -807,10 +807,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging through micro-usb port without * data connection */ - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); if (!cable_attached) - extcon_set_cable_state_(info->edev, + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, cable_attached); break; } @@ -834,13 +834,13 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging through micro-usb port without * data connection. */ - extcon_set_cable_state_(info->edev, EXTCON_USB, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); if (!cable_attached) - extcon_set_cable_state_(info->edev, EXTCON_DOCK, + extcon_set_state_sync(info->edev, EXTCON_DOCK, cable_attached); break; case MAX77693_MUIC_ADC_RESERVED_ACC_3: /* Dock-Smart */ @@ -869,9 +869,9 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DOCK, + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; } @@ -905,28 +905,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77693_CHARGER_TYPE_DEDICATED_CHG: /* Only TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; } break; case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77693_CHARGER_TYPE_APPLE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77693_CHARGER_TYPE_APPLE_1A_2A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77693_CHARGER_TYPE_DEAD_BATTERY: diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index b188bd650efa..5d11fdf36e94 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -346,7 +346,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX77843_MUIC_GND_MHL_VB: case MAX77843_MUIC_GND_MHL: @@ -356,7 +356,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n", @@ -392,7 +392,7 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info, if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -486,8 +486,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; case MAX77843_MUIC_CHG_DOWNSTREAM: @@ -497,7 +497,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77843_MUIC_CHG_DEDICATED: @@ -507,7 +507,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX77843_MUIC_CHG_SPECIAL_500MA: @@ -517,7 +517,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77843_MUIC_CHG_SPECIAL_1A: @@ -527,7 +527,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77843_MUIC_CHG_GND: @@ -536,10 +536,10 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) /* Charger cable on MHL accessory is attach or detach */ if (gnd_type == MAX77843_MUIC_GND_MHL_VB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, true); else if (gnd_type == MAX77843_MUIC_GND_MHL) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, false); break; case MAX77843_MUIC_CHG_NONE: diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 9a89320d09a8..4a0612fb9c07 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -331,11 +331,11 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info, switch (usb_type) { case MAX8997_USB_HOST: - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); break; case MAX8997_USB_DEVICE: - extcon_set_cable_state_(info->edev, EXTCON_USB, attached); - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_USB, attached); + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); break; default: @@ -361,7 +361,7 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info, switch (cable_type) { case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD: case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON: - extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); break; default: dev_err(info->dev, "failed to detect %s dock device\n", @@ -384,7 +384,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info, return ret; } - extcon_set_cable_state_(info->edev, EXTCON_JIG, attached); + extcon_set_state_sync(info->edev, EXTCON_JIG, attached); return 0; } @@ -406,7 +406,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info) return ret; break; case MAX8997_MUIC_ADC_MHL: - extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); break; case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF: case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: @@ -489,19 +489,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info) } break; case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX8997_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); break; case MAX8997_CHARGER_TYPE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX8997_CHARGER_TYPE_1A: - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, attached); break; default: diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index caff46c0e214..634ba70782de 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -61,7 +61,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; - extcon_set_cable_state_(edev, EXTCON_USB, true); + extcon_set_state_sync(edev, EXTCON_USB, true); dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, @@ -70,7 +70,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state_(edev, EXTCON_USB, false); + extcon_set_state_sync(edev, EXTCON_USB, false); dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, @@ -98,7 +98,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; - extcon_set_cable_state_(edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { @@ -106,17 +106,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(edev, EXTCON_USB_HOST, false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(edev, EXTCON_USB_HOST, false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_usb->linkstat = PALMAS_USB_STATE_ID; - extcon_set_cable_state_(edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); } @@ -137,10 +137,10 @@ static void palmas_gpio_id_detect(struct work_struct *work) id = gpiod_get_value_cansleep(palmas_usb->id_gpiod); if (id) { - extcon_set_cable_state_(edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(edev, EXTCON_USB_HOST, false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else { - extcon_set_cable_state_(edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(edev, EXTCON_USB_HOST, true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } } diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c new file mode 100644 index 000000000000..ca957a5f4291 --- /dev/null +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -0,0 +1,170 @@ +/** + * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID + * detection based on extcon-usb-gpio.c. + * + * Copyright (C) 2016 Linaro, Ltd. + * Stephen Boyd <stephen.boyd@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/extcon.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#define USB_ID_DEBOUNCE_MS 5 /* ms */ + +struct qcom_usb_extcon_info { + struct extcon_dev *edev; + int irq; + struct delayed_work wq_detcable; + unsigned long debounce_jiffies; +}; + +static const unsigned int qcom_usb_extcon_cable[] = { + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static void qcom_usb_extcon_detect_cable(struct work_struct *work) +{ + bool id; + int ret; + struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work), + struct qcom_usb_extcon_info, + wq_detcable); + + /* check ID and update cable state */ + ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id); + if (ret) + return; + + extcon_set_state(info->edev, EXTCON_USB_HOST, !id); +} + +static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id) +{ + struct qcom_usb_extcon_info *info = dev_id; + + queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, + info->debounce_jiffies); + + return IRQ_HANDLED; +} + +static int qcom_usb_extcon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_usb_extcon_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + + ret = devm_extcon_dev_register(dev, info->edev); + if (ret < 0) { + dev_err(dev, "failed to register extcon device\n"); + return ret; + } + + info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS); + INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable); + + info->irq = platform_get_irq_byname(pdev, "usb_id"); + if (info->irq < 0) + return info->irq; + + ret = devm_request_threaded_irq(dev, info->irq, NULL, + qcom_usb_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdev->name, info); + if (ret < 0) { + dev_err(dev, "failed to request handler for ID IRQ\n"); + return ret; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(dev, 1); + + /* Perform initial detection */ + qcom_usb_extcon_detect_cable(&info->wq_detcable.work); + + return 0; +} + +static int qcom_usb_extcon_remove(struct platform_device *pdev) +{ + struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&info->wq_detcable); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int qcom_usb_extcon_suspend(struct device *dev) +{ + struct qcom_usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) + ret = enable_irq_wake(info->irq); + + return ret; +} + +static int qcom_usb_extcon_resume(struct device *dev) +{ + struct qcom_usb_extcon_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) + ret = disable_irq_wake(info->irq); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops, + qcom_usb_extcon_suspend, qcom_usb_extcon_resume); + +static const struct of_device_id qcom_usb_extcon_dt_match[] = { + { .compatible = "qcom,pm8941-misc", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match); + +static struct platform_driver qcom_usb_extcon_driver = { + .probe = qcom_usb_extcon_probe, + .remove = qcom_usb_extcon_remove, + .driver = { + .name = "extcon-pm8941-misc", + .pm = &qcom_usb_extcon_pm_ops, + .of_match_table = qcom_usb_extcon_dt_match, + }, +}; +module_platform_driver(qcom_usb_extcon_driver); + +MODULE_DESCRIPTION("QCOM USB ID extcon driver"); +MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 97e074d70eca..174c388739ea 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -398,9 +398,9 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info, return ret; /* Change the state of external accessory */ - extcon_set_cable_state_(info->edev, id, attached); + extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); return 0; diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index df769a17e736..b22325688503 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -411,9 +411,9 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, return ret; /* Change the state of external accessory */ - extcon_set_cable_state_(info->edev, id, attached); + extcon_set_state_sync(info->edev, id, attached); if (id == EXTCON_USB) - extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP, + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, attached); return 0; diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 2512660dc4b9..a27d350f69e3 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -63,16 +63,16 @@ static void usb_extcon_detect_cable(struct work_struct *work) * As we don't have event for USB peripheral cable attached, * we simulate USB peripheral attach here. */ - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, false); - extcon_set_cable_state_(info->edev, EXTCON_USB, true); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); + extcon_set_state_sync(info->edev, EXTCON_USB, true); } else { /* * ID = 0 means USB HOST cable attached. * As we don't have event for USB peripheral cable detached, * we simulate USB peripheral detach here. */ - extcon_set_cable_state_(info->edev, EXTCON_USB, false); - extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, true); + extcon_set_state_sync(info->edev, EXTCON_USB, false); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); } } diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8682efc0f57b..78298460d168 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -38,43 +38,159 @@ #define SUPPORTED_CABLE_MAX 32 #define CABLE_NAME_MAX 30 -static const char *extcon_name[] = { - [EXTCON_NONE] = "NONE", +struct __extcon_info { + unsigned int type; + unsigned int id; + const char *name; + +} extcon_info[] = { + [EXTCON_NONE] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_NONE, + .name = "NONE", + }, /* USB external connector */ - [EXTCON_USB] = "USB", - [EXTCON_USB_HOST] = "USB-HOST", + [EXTCON_USB] = { + .type = EXTCON_TYPE_USB, + .id = EXTCON_USB, + .name = "USB", + }, + [EXTCON_USB_HOST] = { + .type = EXTCON_TYPE_USB, + .id = EXTCON_USB_HOST, + .name = "USB_HOST", + }, /* Charging external connector */ - [EXTCON_CHG_USB_SDP] = "SDP", - [EXTCON_CHG_USB_DCP] = "DCP", - [EXTCON_CHG_USB_CDP] = "CDP", - [EXTCON_CHG_USB_ACA] = "ACA", - [EXTCON_CHG_USB_FAST] = "FAST-CHARGER", - [EXTCON_CHG_USB_SLOW] = "SLOW-CHARGER", + [EXTCON_CHG_USB_SDP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_SDP, + .name = "SDP", + }, + [EXTCON_CHG_USB_DCP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_DCP, + .name = "DCP", + }, + [EXTCON_CHG_USB_CDP] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_CDP, + .name = "CDP", + }, + [EXTCON_CHG_USB_ACA] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_ACA, + .name = "ACA", + }, + [EXTCON_CHG_USB_FAST] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_FAST, + .name = "FAST-CHARGER", + }, + [EXTCON_CHG_USB_SLOW] = { + .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, + .id = EXTCON_CHG_USB_SLOW, + .name = "SLOW-CHARGER", + }, + [EXTCON_CHG_WPT] = { + .type = EXTCON_TYPE_CHG, + .id = EXTCON_CHG_WPT, + .name = "WPT", + }, /* Jack external connector */ - [EXTCON_JACK_MICROPHONE] = "MICROPHONE", - [EXTCON_JACK_HEADPHONE] = "HEADPHONE", - [EXTCON_JACK_LINE_IN] = "LINE-IN", - [EXTCON_JACK_LINE_OUT] = "LINE-OUT", - [EXTCON_JACK_VIDEO_IN] = "VIDEO-IN", - [EXTCON_JACK_VIDEO_OUT] = "VIDEO-OUT", - [EXTCON_JACK_SPDIF_IN] = "SPDIF-IN", - [EXTCON_JACK_SPDIF_OUT] = "SPDIF-OUT", + [EXTCON_JACK_MICROPHONE] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_MICROPHONE, + .name = "MICROPHONE", + }, + [EXTCON_JACK_HEADPHONE] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_HEADPHONE, + .name = "HEADPHONE", + }, + [EXTCON_JACK_LINE_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_LINE_IN, + .name = "LINE-IN", + }, + [EXTCON_JACK_LINE_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_LINE_OUT, + .name = "LINE-OUT", + }, + [EXTCON_JACK_VIDEO_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_VIDEO_IN, + .name = "VIDEO-IN", + }, + [EXTCON_JACK_VIDEO_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_VIDEO_OUT, + .name = "VIDEO-OUT", + }, + [EXTCON_JACK_SPDIF_IN] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_SPDIF_IN, + .name = "SPDIF-IN", + }, + [EXTCON_JACK_SPDIF_OUT] = { + .type = EXTCON_TYPE_JACK, + .id = EXTCON_JACK_SPDIF_OUT, + .name = "SPDIF-OUT", + }, /* Display external connector */ - [EXTCON_DISP_HDMI] = "HDMI", - [EXTCON_DISP_MHL] = "MHL", - [EXTCON_DISP_DVI] = "DVI", - [EXTCON_DISP_VGA] = "VGA", + [EXTCON_DISP_HDMI] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_HDMI, + .name = "HDMI", + }, + [EXTCON_DISP_MHL] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_MHL, + .name = "MHL", + }, + [EXTCON_DISP_DVI] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_DVI, + .name = "DVI", + }, + [EXTCON_DISP_VGA] = { + .type = EXTCON_TYPE_DISP, + .id = EXTCON_DISP_VGA, + .name = "VGA", + }, + [EXTCON_DISP_DP] = { + .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, + .id = EXTCON_DISP_DP, + .name = "DP", + }, + [EXTCON_DISP_HMD] = { + .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, + .id = EXTCON_DISP_HMD, + .name = "HMD", + }, /* Miscellaneous external connector */ - [EXTCON_DOCK] = "DOCK", - [EXTCON_JIG] = "JIG", - [EXTCON_MECHANICAL] = "MECHANICAL", - - NULL, + [EXTCON_DOCK] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_DOCK, + .name = "DOCK", + }, + [EXTCON_JIG] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_JIG, + .name = "JIG", + }, + [EXTCON_MECHANICAL] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_MECHANICAL, + .name = "MECHANICAL", + }, + + { /* sentinel */ } }; /** @@ -95,6 +211,16 @@ struct extcon_cable { struct device_attribute attr_state; struct attribute *attrs[3]; /* to be fed to attr_g.attrs */ + + union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; + union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; + union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; + union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; + + unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)]; + unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)]; + unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)]; + unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)]; }; static struct class *extcon_class; @@ -147,14 +273,93 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id return -EINVAL; } -static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) +static int get_extcon_type(unsigned int prop) { - if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) { - *attached = ((new >> idx) & 0x1) ? true : false; - return true; + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + return EXTCON_TYPE_USB; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + return EXTCON_TYPE_CHG; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + return EXTCON_TYPE_JACK; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + return EXTCON_TYPE_DISP; + default: + return -EINVAL; } +} + +static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index) +{ + return !!(edev->state & BIT(index)); +} + +static bool is_extcon_changed(struct extcon_dev *edev, int index, + bool new_state) +{ + int state = !!(edev->state & BIT(index)); + return (state != new_state); +} + +static bool is_extcon_property_supported(unsigned int id, unsigned int prop) +{ + int type; + + /* Check whether the property is supported or not. */ + type = get_extcon_type(prop); + if (type < 0) + return false; - return false; + /* Check whether a specific extcon id supports the property or not. */ + return !!(extcon_info[id].type & type); +} + +static int is_extcon_property_capability(struct extcon_dev *edev, + unsigned int id, int index,unsigned int prop) +{ + struct extcon_cable *cable; + int type, ret; + + /* Check whether the property is supported or not. */ + type = get_extcon_type(prop); + if (type < 0) + return type; + + cable = &edev->cables[index]; + + switch (type) { + case EXTCON_TYPE_USB: + ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); + break; + case EXTCON_TYPE_CHG: + ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); + break; + case EXTCON_TYPE_JACK: + ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); + break; + case EXTCON_TYPE_DISP: + ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static void init_property(struct extcon_dev *edev, unsigned int id, int index) +{ + unsigned int type = extcon_info[id].type; + struct extcon_cable *cable = &edev->cables[index]; + + if (EXTCON_TYPE_USB & type) + memset(cable->usb_propval, 0, sizeof(cable->usb_propval)); + if (EXTCON_TYPE_CHG & type) + memset(cable->chg_propval, 0, sizeof(cable->chg_propval)); + if (EXTCON_TYPE_JACK & type) + memset(cable->jack_propval, 0, sizeof(cable->jack_propval)); + if (EXTCON_TYPE_DISP & type) + memset(cable->disp_propval, 0, sizeof(cable->disp_propval)); } static ssize_t state_show(struct device *dev, struct device_attribute *attr, @@ -168,32 +373,13 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, for (i = 0; i < edev->max_supported; i++) { count += sprintf(buf + count, "%s=%d\n", - extcon_name[edev->supported_cable[i]], + extcon_info[edev->supported_cable[i]].name, !!(edev->state & (1 << i))); } return count; } - -static ssize_t state_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 state; - ssize_t ret = 0; - struct extcon_dev *edev = dev_get_drvdata(dev); - - ret = sscanf(buf, "0x%x", &state); - if (ret == 0) - ret = -EINVAL; - else - ret = extcon_set_state(edev, state); - - if (ret < 0) - return ret; - - return count; -} -static DEVICE_ATTR_RW(state); +static DEVICE_ATTR_RO(state); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -212,7 +398,7 @@ static ssize_t cable_name_show(struct device *dev, int i = cable->cable_index; return sprintf(buf, "%s\n", - extcon_name[cable->edev->supported_cable[i]]); + extcon_info[cable->edev->supported_cable[i]].name); } static ssize_t cable_state_show(struct device *dev, @@ -224,26 +410,17 @@ static ssize_t cable_state_show(struct device *dev, int i = cable->cable_index; return sprintf(buf, "%d\n", - extcon_get_cable_state_(cable->edev, - cable->edev->supported_cable[i])); + extcon_get_state(cable->edev, cable->edev->supported_cable[i])); } /** - * extcon_update_state() - Update the cable attach states of the extcon device - * only for the masked bits. - * @edev: the extcon device - * @mask: the bit mask to designate updated bits. - * @state: new cable attach status for @edev - * - * Changing the state sends uevent with environment variable containing - * the name of extcon device (envp[0]) and the state output (envp[1]). - * Tizen uses this format for extcon device to get events from ports. - * Android uses this format as well. + * extcon_sync() - Synchronize the states for both the attached/detached + * @edev: the extcon device that has the cable. * - * Note that the notifier provides which bits are changed in the state - * variable with the val parameter (second) to the callback. + * This function send a notification to synchronize the all states of a + * specific external connector */ -int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) +int extcon_sync(struct extcon_dev *edev, unsigned int id) { char name_buf[120]; char state_buf[120]; @@ -252,100 +429,102 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) int env_offset = 0; int length; int index; + int state; unsigned long flags; - bool attached; if (!edev) return -EINVAL; - spin_lock_irqsave(&edev->lock, flags); + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; - if (edev->state != ((edev->state & ~mask) | (state & mask))) { - u32 old_state; + spin_lock_irqsave(&edev->lock, flags); - if (check_mutually_exclusive(edev, (edev->state & ~mask) | - (state & mask))) { - spin_unlock_irqrestore(&edev->lock, flags); - return -EPERM; - } + state = !!(edev->state & BIT(index)); + raw_notifier_call_chain(&edev->nh[index], state, edev); - old_state = edev->state; - edev->state &= ~mask; - edev->state |= state & mask; + /* This could be in interrupt handler */ + prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); + if (!prop_buf) { + /* Unlock early before uevent */ + spin_unlock_irqrestore(&edev->lock, flags); - for (index = 0; index < edev->max_supported; index++) { - if (is_extcon_changed(old_state, edev->state, index, - &attached)) - raw_notifier_call_chain(&edev->nh[index], - attached, edev); - } + dev_err(&edev->dev, "out of memory in extcon_set_state\n"); + kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); - /* This could be in interrupt handler */ - prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); - if (prop_buf) { - length = name_show(&edev->dev, NULL, prop_buf); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(name_buf, sizeof(name_buf), - "NAME=%s", prop_buf); - envp[env_offset++] = name_buf; - } - length = state_show(&edev->dev, NULL, prop_buf); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(state_buf, sizeof(state_buf), - "STATE=%s", prop_buf); - envp[env_offset++] = state_buf; - } - envp[env_offset] = NULL; - /* Unlock early before uevent */ - spin_unlock_irqrestore(&edev->lock, flags); + return 0; + } - kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); - free_page((unsigned long)prop_buf); - } else { - /* Unlock early before uevent */ - spin_unlock_irqrestore(&edev->lock, flags); + length = name_show(&edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } - dev_err(&edev->dev, "out of memory in extcon_set_state\n"); - kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); - } - } else { - /* No changes */ - spin_unlock_irqrestore(&edev->lock, flags); + length = state_show(&edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf); + envp[env_offset++] = state_buf; } + envp[env_offset] = NULL; + + /* Unlock early before uevent */ + spin_unlock_irqrestore(&edev->lock, flags); + kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); return 0; } -EXPORT_SYMBOL_GPL(extcon_update_state); +EXPORT_SYMBOL_GPL(extcon_sync); /** - * extcon_set_state() - Set the cable attach states of the extcon device. - * @edev: the extcon device - * @state: new cable attach status for @edev - * - * Note that notifier provides which bits are changed in the state - * variable with the val parameter (second) to the callback. + * extcon_get_state() - Get the state of a external connector. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector in extcon enumeration. */ -int extcon_set_state(struct extcon_dev *edev, u32 state) +int extcon_get_state(struct extcon_dev *edev, const unsigned int id) { + int index, state; + unsigned long flags; + if (!edev) return -EINVAL; - return extcon_update_state(edev, 0xffffffff, state); + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + spin_lock_irqsave(&edev->lock, flags); + state = is_extcon_attached(edev, index); + spin_unlock_irqrestore(&edev->lock, flags); + + return state; } -EXPORT_SYMBOL_GPL(extcon_set_state); +EXPORT_SYMBOL_GPL(extcon_get_state); /** - * extcon_get_cable_state_() - Get the status of a specific cable. - * @edev: the extcon device that has the cable. - * @id: the unique id of each external connector in extcon enumeration. + * extcon_set_state() - Set the state of a external connector. + * without a notification. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @state: the new cable status. The default semantics is + * true: attached / false: detached. + * + * This function only set the state of a external connector without + * a notification. To synchronize the data of a external connector, + * use extcon_set_state_sync() and extcon_sync(). */ -int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) +int extcon_set_state(struct extcon_dev *edev, unsigned int id, + bool cable_state) { - int index; + unsigned long flags; + int index, ret = 0; if (!edev) return -EINVAL; @@ -354,41 +533,338 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) if (index < 0) return index; - if (edev->max_supported && edev->max_supported <= index) - return -EINVAL; + spin_lock_irqsave(&edev->lock, flags); + + /* Check whether the external connector's state is changed. */ + if (!is_extcon_changed(edev, index, cable_state)) + goto out; + + if (check_mutually_exclusive(edev, + (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) { + ret = -EPERM; + goto out; + } + + /* + * Initialize the value of extcon property before setting + * the detached state for an external connector. + */ + if (!cable_state) + init_property(edev, id, index); + + /* Update the state for a external connector. */ + if (cable_state) + edev->state |= BIT(index); + else + edev->state &= ~(BIT(index)); +out: + spin_unlock_irqrestore(&edev->lock, flags); - return !!(edev->state & (1 << index)); + return ret; } -EXPORT_SYMBOL_GPL(extcon_get_cable_state_); +EXPORT_SYMBOL_GPL(extcon_set_state); /** - * extcon_set_cable_state_() - Set the status of a specific cable. + * extcon_set_state_sync() - Set the state of a external connector + * with a notification. * @edev: the extcon device that has the cable. * @id: the unique id of each external connector * in extcon enumeration. * @state: the new cable status. The default semantics is * true: attached / false: detached. + * + * This function set the state of external connector and synchronize the data + * by usning a notification. */ -int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, +int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool cable_state) { - u32 state; + int ret, index; + unsigned long flags; + + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + /* Check whether the external connector's state is changed. */ + spin_lock_irqsave(&edev->lock, flags); + ret = is_extcon_changed(edev, index, cable_state); + spin_unlock_irqrestore(&edev->lock, flags); + if (!ret) + return 0; + + ret = extcon_set_state(edev, id, cable_state); + if (ret < 0) + return ret; + + return extcon_sync(edev, id); +} +EXPORT_SYMBOL_GPL(extcon_set_state_sync); + +/** + * extcon_get_property() - Get the property value of a specific cable. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * @prop_val: the pointer which store the value of property. + * + * When getting the property value of external connector, the external connector + * should be attached. If detached state, function just return 0 without + * property value. Also, the each property should be included in the list of + * supported properties according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_get_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value *prop_val) +{ + struct extcon_cable *cable; + unsigned long flags; + int index, ret = 0; + + *prop_val = (union extcon_property_value)(0); + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + spin_lock_irqsave(&edev->lock, flags); + + /* Check whether the property is available or not. */ + if (!is_extcon_property_capability(edev, id, index, prop)) { + spin_unlock_irqrestore(&edev->lock, flags); + return -EPERM; + } + + /* + * Check whether the external connector is attached. + * If external connector is detached, the user can not + * get the property value. + */ + if (!is_extcon_attached(edev, index)) { + spin_unlock_irqrestore(&edev->lock, flags); + return 0; + } + + cable = &edev->cables[index]; + + /* Get the property value according to extcon type */ + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN]; + break; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN]; + break; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN]; + break; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN]; + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_get_property); + +/** + * extcon_set_property() - Set the property value of a specific cable. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * @prop_val: the pointer including the new value of property. + * + * The each property should be included in the list of supported properties + * according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + struct extcon_cable *cable; + unsigned long flags; + int index, ret = 0; + + if (!edev) + return -EINVAL; + + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + spin_lock_irqsave(&edev->lock, flags); + + /* Check whether the property is available or not. */ + if (!is_extcon_property_capability(edev, id, index, prop)) { + spin_unlock_irqrestore(&edev->lock, flags); + return -EPERM; + } + + cable = &edev->cables[index]; + + /* Set the property value according to extcon type */ + switch (prop) { + case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: + cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val; + break; + case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: + cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val; + break; + case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: + cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val; + break; + case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: + cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val; + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock_irqrestore(&edev->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(extcon_set_property); + +/** + * extcon_set_property_sync() - Set the property value of a specific cable + with a notification. + * @prop_val: the pointer including the new value of property. + * + * When setting the property value of external connector, the external connector + * should be attached. The each property should be included in the list of + * supported properties according to the type of external connectors. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + int ret; + + ret = extcon_set_property(edev, id, prop, prop_val); + if (ret < 0) + return ret; + + return extcon_sync(edev, id); +} +EXPORT_SYMBOL_GPL(extcon_set_property_sync); + +/** + * extcon_get_property_capability() - Get the capability of property + * of an external connector. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * + * Returns 1 if the property is available or 0 if not available. + */ +int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, + unsigned int prop) +{ int index; if (!edev) return -EINVAL; + /* Check whether the property is supported or not */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id */ index = find_cable_index_by_id(edev, id); if (index < 0) return index; - if (edev->max_supported && edev->max_supported <= index) + return is_extcon_property_capability(edev, id, index, prop); +} +EXPORT_SYMBOL_GPL(extcon_get_property_capability); + +/** + * extcon_set_property_capability() - Set the capability of a property + * of an external connector. + * @edev: the extcon device that has the cable. + * @id: the unique id of each external connector + * in extcon enumeration. + * @prop: the property id among enum extcon_property. + * + * This function set the capability of a property for an external connector + * to mark the bit in capability bitmap which mean the available state of + * a property. + * + * Returns 0 if success or error number if fail + */ +int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, + unsigned int prop) +{ + struct extcon_cable *cable; + int index, type, ret = 0; + + if (!edev) return -EINVAL; - state = cable_state ? (1 << index) : 0; - return extcon_update_state(edev, 1 << index, state); + /* Check whether the property is supported or not. */ + if (!is_extcon_property_supported(id, prop)) + return -EINVAL; + + /* Find the cable index of external connector by using id. */ + index = find_cable_index_by_id(edev, id); + if (index < 0) + return index; + + type = get_extcon_type(prop); + if (type < 0) + return type; + + cable = &edev->cables[index]; + + switch (type) { + case EXTCON_TYPE_USB: + __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); + break; + case EXTCON_TYPE_CHG: + __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); + break; + case EXTCON_TYPE_JACK: + __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); + break; + case EXTCON_TYPE_DISP: + __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); + break; + default: + ret = -EINVAL; + } + + return ret; } -EXPORT_SYMBOL_GPL(extcon_set_cable_state_); +EXPORT_SYMBOL_GPL(extcon_set_property_capability); /** * extcon_get_extcon_dev() - Get the extcon device instance from the name @@ -428,7 +904,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb) { unsigned long flags; - int ret, idx; + int ret, idx = -EINVAL; if (!nb) return -EINVAL; @@ -846,13 +1322,13 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) return ERR_PTR(-EINVAL); if (!dev->of_node) { - dev_err(dev, "device does not have a device node entry\n"); + dev_dbg(dev, "device does not have a device node entry\n"); return ERR_PTR(-EINVAL); } node = of_parse_phandle(dev->of_node, "extcon", index); if (!node) { - dev_err(dev, "failed to get phandle in %s node\n", + dev_dbg(dev, "failed to get phandle in %s node\n", dev->of_node->full_name); return ERR_PTR(-ENODEV); } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fef0d8fcd916..15ffe389b86f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -368,17 +368,17 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) if (!test_and_clear_bit(index, &acm->read_urbs_free)) return 0; - dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); - res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { if (res != -EPERM) { dev_err(&acm->data->dev, - "%s - usb_submit_urb failed: %d\n", - __func__, res); + "urb %d failed submission with %d\n", + index, res); } set_bit(index, &acm->read_urbs_free); return res; + } else { + dev_vdbg(&acm->data->dev, "submitted urb %d\n", index); } return 0; @@ -415,8 +415,9 @@ static void acm_read_bulk_callback(struct urb *urb) unsigned long flags; int status = urb->status; - dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, - rb->index, urb->actual_length); + dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", + rb->index, urb->actual_length, + status); if (!acm->dev) { set_bit(rb->index, &acm->read_urbs_free); @@ -426,8 +427,6 @@ static void acm_read_bulk_callback(struct urb *urb) if (status) { set_bit(rb->index, &acm->read_urbs_free); - dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", - __func__, status); if ((status != -ENOENT) || (urb->actual_length == 0)) return; } @@ -462,8 +461,7 @@ static void acm_write_bulk(struct urb *urb) int status = urb->status; if (status || (urb->actual_length != urb->transfer_buffer_length)) - dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", - __func__, + dev_vdbg(&acm->data->dev, "wrote len %d/%d, status %d\n", urb->actual_length, urb->transfer_buffer_length, status); @@ -478,8 +476,6 @@ static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); - dev_vdbg(&acm->data->dev, "%s\n", __func__); - tty_port_tty_wakeup(&acm->port); } @@ -674,7 +670,7 @@ static int acm_tty_write(struct tty_struct *tty, if (!count) return 0; - dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%d bytes from tty layer\n", count); spin_lock_irqsave(&acm->write_lock, flags); wbn = acm_wb_alloc(acm); @@ -691,7 +687,7 @@ static int acm_tty_write(struct tty_struct *tty, } count = (count > acm->writesize) ? acm->writesize : count; - dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "writing %d bytes\n", count); memcpy(wb->buf, buf, count); wb->len = count; diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index e04a34e7a341..8b317702d761 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -21,13 +21,13 @@ int ulpi_read(struct ulpi *ulpi, u8 addr) { - return ulpi->ops->read(ulpi->ops, addr); + return ulpi->ops->read(ulpi->dev.parent, addr); } EXPORT_SYMBOL_GPL(ulpi_read); int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) { - return ulpi->ops->write(ulpi->ops, addr, val); + return ulpi->ops->write(ulpi->dev.parent, addr, val); } EXPORT_SYMBOL_GPL(ulpi_write); @@ -157,6 +157,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) { int ret; + ulpi->dev.parent = dev; /* needed early for ops */ + /* Test the interface */ ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); if (ret < 0) @@ -175,7 +177,6 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; - ulpi->dev.parent = dev; ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); @@ -202,7 +203,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) * Allocates and registers a ULPI device and an interface for it. Called from * the USB controller that provides the ULPI interface. */ -struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) +struct ulpi *ulpi_register_interface(struct device *dev, + const struct ulpi_ops *ops) { struct ulpi *ulpi; int ret; @@ -212,7 +214,6 @@ struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) return ERR_PTR(-ENOMEM); ulpi->ops = ops; - ops->dev = dev; ret = ulpi_register(dev, ulpi); if (ret) { diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5ab5c1a81462..3a4707746157 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1859,7 +1859,12 @@ free_interfaces: intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; + /* + * Please refer to usb_alloc_dev() to see why we set + * dma_mask and dma_pfn_offset. + */ intf->dev.dma_mask = dev->dev.dma_mask; + intf->dev.dma_pfn_offset = dev->dev.dma_pfn_offset; INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); intf->minor = -1; device_initialize(&intf->dev); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 5e80697ef952..592151461017 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -440,7 +440,18 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->dev.bus = &usb_bus_type; dev->dev.type = &usb_device_type; dev->dev.groups = usb_device_groups; + /* + * Fake a dma_mask/offset for the USB device: + * We cannot really use the dma-mapping API (dma_alloc_* and + * dma_map_*) for USB devices but instead need to use + * usb_alloc_coherent and pass data in 'urb's, but some subsystems + * manually look into the mask/offset pair to determine whether + * they need bounce buffers. + * Note: calling dma_set_mask() on a USB device would set the + * mask for the entire HCD, so don't do that. + */ dev->dev.dma_mask = bus->controller->dma_mask; + dev->dev.dma_pfn_offset = bus->controller->dma_pfn_offset; set_dev_node(&dev->dev, dev_to_node(bus->controller)); dev->state = USB_STATE_ATTACHED; dev->lpm_disable_count = 1; diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 4135a5ff67ca..fa9b26b91507 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) return ret; } +/** + * dwc2_wait_for_mode() - Waits for the controller mode. + * @hsotg: Programming view of the DWC_otg controller. + * @host_mode: If true, waits for host mode, otherwise device mode. + */ +static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg, + bool host_mode) +{ + ktime_t start; + ktime_t end; + unsigned int timeout = 110; + + dev_vdbg(hsotg->dev, "Waiting for %s mode\n", + host_mode ? "host" : "device"); + + start = ktime_get(); + + while (1) { + s64 ms; + + if (dwc2_is_host_mode(hsotg) == host_mode) { + dev_vdbg(hsotg->dev, "%s mode set\n", + host_mode ? "Host" : "Device"); + break; + } + + end = ktime_get(); + ms = ktime_to_ms(ktime_sub(end, start)); + + if (ms >= (s64)timeout) { + dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n", + __func__, host_mode ? "host" : "device"); + break; + } + + usleep_range(1000, 2000); + } +} + +/** + * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce + * filter is enabled. + */ +static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) +{ + u32 gsnpsid; + u32 ghwcfg4; + + if (!dwc2_hw_is_otg(hsotg)) + return false; + + /* Check if core configuration includes the IDDIG filter. */ + ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); + if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) + return false; + + /* + * Check if the IDDIG debounce filter is bypassed. Available + * in core version >= 3.10a. + */ + gsnpsid = dwc2_readl(hsotg->regs + GSNPSID); + if (gsnpsid >= DWC2_CORE_REV_3_10a) { + u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + + if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) + return false; + } + + return true; +} + /* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core. @@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) { u32 greset; int count = 0; + bool wait_for_host_mode = false; dev_vdbg(hsotg->dev, "%s()\n", __func__); + /* + * If the current mode is host, either due to the force mode + * bit being set (which persists after core reset) or the + * connector id pin, a core soft reset will temporarily reset + * the mode to device. A delay from the IDDIG debounce filter + * will occur before going back to host mode. + * + * Determine whether we will go back into host mode after a + * reset and account for this delay after the reset. + */ + if (dwc2_iddig_filter_enabled(hsotg)) { + u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + wait_for_host_mode = true; + } + } + /* Core Soft Reset */ greset = dwc2_readl(hsotg->regs + GRSTCTL); greset |= GRSTCTL_CSFTRST; @@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) } } while (!(greset & GRSTCTL_AHBIDLE)); + if (wait_for_host_mode) + dwc2_wait_for_mode(hsotg, true); + return 0; } @@ -300,9 +395,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) * Checks are done in this function to determine whether doing a force * would be valid or not. * - * If a force is done, it requires a 25ms delay to take effect. - * - * Returns true if the mode was forced. + * If a force is done, it requires a IDDIG debounce filter delay if + * the filter is configured and enabled. We poll the current mode of + * the controller to account for this delay. */ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) { @@ -337,12 +432,18 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) gusbcfg |= set; dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); - msleep(25); + dwc2_wait_for_mode(hsotg, host); return true; } -/* - * Clears the force mode bits. +/** + * dwc2_clear_force_mode() - Clears the force mode bits. + * + * After clearing the bits, wait up to 100 ms to account for any + * potential IDDIG filter delay. We can't know if we expect this delay + * or not because the value of the connector ID status is affected by + * the force mode. We only need to call this once during probe if + * dr_mode == OTG. */ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) { @@ -353,11 +454,8 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) gusbcfg &= ~GUSBCFG_FORCEDEVMODE; dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); - /* - * NOTE: This long sleep is _very_ important, otherwise the core will - * not stay in host mode after a connector ID change! - */ - msleep(25); + if (dwc2_iddig_filter_enabled(hsotg)) + usleep_range(100000, 110000); } /* @@ -380,12 +478,6 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) __func__, hsotg->dr_mode); break; } - - /* - * NOTE: This is required for some rockchip soc based - * platforms. - */ - msleep(50); } /* diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d64551243789..aad4107ef927 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -259,13 +259,6 @@ enum dwc2_lx_state { DWC2_L3, /* Off state */ }; -/* - * Gadget periodic tx fifo sizes as used by legacy driver - * EP0 is not included - */ -#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \ - 768, 0, 0, 0, 0, 0, 0, 0} - /* Gadget ep0 states */ enum dwc2_ep0_state { DWC2_EP0_SETUP, @@ -890,6 +883,7 @@ struct dwc2_hsotg { #define DWC2_CORE_REV_2_92a 0x4f54292a #define DWC2_CORE_REV_2_94a 0x4f54294a #define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2ff03ae08e14..4cd6403a7566 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -186,9 +186,10 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, */ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) { - unsigned int ep; + unsigned int fifo; unsigned int addr; int timeout; + u32 dptxfsizn; u32 val; /* Reset fifo map if not correctly cleared during previous session */ @@ -216,16 +217,16 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) * them to endpoints dynamically according to maxpacket size value of * given endpoint. */ - for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) { - if (!hsotg->g_tx_fifo_sz[ep]) - continue; - val = addr; - val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT; - WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem, - "insufficient fifo memory"); - addr += hsotg->g_tx_fifo_sz[ep]; + for (fifo = 1; fifo < MAX_EPS_CHANNELS; fifo++) { + dptxfsizn = dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)); + + val = (dptxfsizn & FIFOSIZE_DEPTH_MASK) | addr; + addr += dptxfsizn >> FIFOSIZE_DEPTH_SHIFT; + + if (addr > hsotg->fifo_mem) + break; - dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep)); + dwc2_writel(val, hsotg->regs + DPTXFSIZN(fifo)); } /* @@ -388,7 +389,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, return -ENOSPC; } } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { - can_write = dwc2_readl(hsotg->regs + DTXFSTS(hs_ep->index)); + can_write = dwc2_readl(hsotg->regs + + DTXFSTS(hs_ep->fifo_index)); can_write &= 0xffff; can_write *= 4; @@ -2432,7 +2434,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg, if (!hsotg->dedicated_fifos) return; - size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4; if (size < ep->fifo_size) dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); } @@ -3041,22 +3043,11 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, break; } - /* If fifo is already allocated for this ep */ - if (hs_ep->fifo_index) { - size = hs_ep->ep.maxpacket * hs_ep->mc; - /* If bigger fifo is required deallocate current one */ - if (size > hs_ep->fifo_size) { - hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); - hs_ep->fifo_index = 0; - hs_ep->fifo_size = 0; - } - } - /* * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) { + if (dir_in && hsotg->dedicated_fifos) { u32 fifo_index = 0; u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket*hs_ep->mc; @@ -3129,10 +3120,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&hsotg->lock, flags); - hsotg->fifo_map &= ~(1<<hs_ep->fifo_index); - hs_ep->fifo_index = 0; - hs_ep->fifo_size = 0; - ctrl = dwc2_readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; ctrl &= ~DXEPCTL_USBACTEP; @@ -3147,6 +3134,10 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); + hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } @@ -3475,8 +3466,11 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); spin_lock_irqsave(&hsotg->lock, flags); - dwc2_hsotg_init(hsotg); - dwc2_hsotg_core_init_disconnected(hsotg, false); + if (dwc2_hw_is_device(hsotg)) { + dwc2_hsotg_init(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + } + hsotg->enabled = 0; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3813,36 +3807,10 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) { struct device_node *np = hsotg->dev->of_node; - u32 len = 0; - u32 i = 0; /* Enable dma if requested in device tree */ hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma"); - /* - * Register TX periodic fifo size per endpoint. - * EP0 is excluded since it has no fifo configuration. - */ - if (!of_find_property(np, "g-tx-fifo-size", &len)) - goto rx_fifo; - - len /= sizeof(u32); - - /* Read tx fifo sizes other than ep0 */ - if (of_property_read_u32_array(np, "g-tx-fifo-size", - &hsotg->g_tx_fifo_sz[1], len)) - goto rx_fifo; - - /* Add ep0 */ - len++; - - /* Make remaining TX fifos unavailable */ - if (len < MAX_EPS_CHANNELS) { - for (i = len; i < MAX_EPS_CHANNELS; i++) - hsotg->g_tx_fifo_sz[i] = 0; - } - -rx_fifo: /* Register RX fifo size */ of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz); @@ -3864,13 +3832,10 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) struct device *dev = hsotg->dev; int epnum; int ret; - int i; - u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE; /* Initialize to legacy fifo configuration values */ hsotg->g_rx_fifo_sz = 2048; hsotg->g_np_g_tx_fifo_sz = 1024; - memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo)); /* Device tree specific probe */ dwc2_hsotg_of_probe(hsotg); @@ -3888,9 +3853,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", hsotg->g_np_g_tx_fifo_sz); dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz); - for (i = 0; i < MAX_EPS_CHANNELS; i++) - dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i, - hsotg->g_tx_fifo_sz[i]); hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index efc3bcde2822..91058441e62a 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -48,6 +48,7 @@ #define GOTGCTL_ASESVLD (1 << 18) #define GOTGCTL_DBNC_SHORT (1 << 17) #define GOTGCTL_CONID_B (1 << 16) +#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15) #define GOTGCTL_DEVHNPEN (1 << 11) #define GOTGCTL_HSTSETHNPEN (1 << 10) #define GOTGCTL_HNPREQ (1 << 9) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 35d092456bec..7287a763cd0c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -49,6 +49,57 @@ #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ +/** + * dwc3_get_dr_mode - Validates and sets dr_mode + * @dwc: pointer to our context structure + */ +static int dwc3_get_dr_mode(struct dwc3 *dwc) +{ + enum usb_dr_mode mode; + struct device *dev = dwc->dev; + unsigned int hw_mode; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + + mode = dwc->dr_mode; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + + switch (hw_mode) { + case DWC3_GHWPARAMS0_MODE_GADGET: + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) { + dev_err(dev, + "Controller does not support host mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_PERIPHERAL; + break; + case DWC3_GHWPARAMS0_MODE_HOST: + if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { + dev_err(dev, + "Controller does not support device mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_HOST; + break; + default: + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc->dr_mode) { + dev_warn(dev, + "Configuration mismatch. dr_mode forced to %s\n", + mode == USB_DR_MODE_HOST ? "host" : "gadget"); + + dwc->dr_mode = mode; + } + + return 0; +} + void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -448,6 +499,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_u3_susphy_quirk) reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + if (dwc->dis_del_phy_power_chg_quirk) + reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); @@ -485,6 +539,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc) break; } + switch (dwc->hsphy_mode) { + case USBPHY_INTERFACE_MODE_UTMI: + reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | + DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); + reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) | + DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT); + break; + case USBPHY_INTERFACE_MODE_UTMIW: + reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK | + DWC3_GUSB2PHYCFG_USBTRDTIM_MASK); + reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) | + DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT); + break; + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -500,6 +571,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_enblslpm_quirk) reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + if (dwc->dis_u2_freeclk_exists_quirk) + reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); return 0; @@ -666,6 +740,32 @@ static int dwc3_core_init(struct dwc3 *dwc) goto err4; } + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + break; + case USB_DR_MODE_HOST: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); + break; + case USB_DR_MODE_OTG: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + break; + default: + dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode); + break; + } + + /* + * ENDXFER polling is available on version 3.10a and later of + * the DWC_usb3 controller. It is NOT available in the + * DWC_usb31 controller. + */ + if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg |= DWC3_GUCTL2_RST_ACTBITLATER; + dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + } + return 0; err4: @@ -763,7 +863,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) @@ -772,7 +871,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_HOST: - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) @@ -781,7 +879,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_OTG: - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) @@ -888,6 +985,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->dr_mode = usb_get_dr_mode(dev); + dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node); dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); @@ -924,6 +1022,10 @@ static int dwc3_probe(struct platform_device *pdev) "snps,dis_enblslpm_quirk"); dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev, "snps,dis_rxdet_inp3_quirk"); + dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev, + "snps,dis-u2-freeclk-exists-quirk"); + dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev, + "snps,dis-del-phy-power-chg-quirk"); dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, "snps,tx_de_emphasis_quirk"); @@ -972,17 +1074,9 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && - (dwc->dr_mode == USB_DR_MODE_OTG || - dwc->dr_mode == USB_DR_MODE_UNKNOWN)) - dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) && - (dwc->dr_mode == USB_DR_MODE_OTG || - dwc->dr_mode == USB_DR_MODE_UNKNOWN)) - dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - - if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) - dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_get_dr_mode(dwc); + if (ret) + goto err3; ret = dwc3_alloc_scratch_buffers(dwc); if (ret) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 45d6de5107c7..6b60e42626a2 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -109,6 +109,7 @@ #define DWC3_GPRTBIMAP_HS1 0xc184 #define DWC3_GPRTBIMAP_FS0 0xc188 #define DWC3_GPRTBIMAP_FS1 0xc18c +#define DWC3_GUCTL2 0xc19c #define DWC3_VER_NUMBER 0xc1a0 #define DWC3_VER_TYPE 0xc1a4 @@ -199,9 +200,18 @@ /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) #define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8) +#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) +#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) +#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) +#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf) +#define USBTRDTIM_UTMI_8_BIT 9 +#define USBTRDTIM_UTMI_16_BIT 5 +#define UTMI_PHYIF_16_BIT 1 +#define UTMI_PHYIF_8_BIT 0 /* Global USB2 PHY Vendor Control Register */ #define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) @@ -235,7 +245,10 @@ #define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) /* Global HWPARAMS0 Register */ -#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3) +#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3) +#define DWC3_GHWPARAMS0_MODE_GADGET 0 +#define DWC3_GHWPARAMS0_MODE_HOST 1 +#define DWC3_GHWPARAMS0_MODE_DRD 2 #define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7) #define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3) #define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff) @@ -279,6 +292,9 @@ #define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f +/* Global User Control Register 2 */ +#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14) + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -685,6 +701,8 @@ struct dwc3_hwparams { * @request: struct usb_request to be transferred * @list: a list_head used for request queueing * @dep: struct dwc3_ep owning this request + * @sg: pointer to first incomplete sg + * @num_pending_sgs: counter to pending sgs * @first_trb_index: index to first trb used by this request * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb @@ -697,7 +715,9 @@ struct dwc3_request { struct usb_request request; struct list_head list; struct dwc3_ep *dep; + struct scatterlist *sg; + unsigned num_pending_sgs; u8 first_trb_index; u8 epnum; struct dwc3_trb *trb; @@ -743,6 +763,9 @@ struct dwc3_scratchpad_array { * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @dr_mode: requested mode of operation + * @hsphy_mode: UTMI phy mode, one of following: + * - USBPHY_INTERFACE_MODE_UTMI + * - USBPHY_INTERFACE_MODE_UTMIW * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -799,6 +822,11 @@ struct dwc3_scratchpad_array { * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, * disabling the suspend signal to the PHY. + * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists + * in GUSB2PHYCFG, specify that USB2 PHY doesn't + * provide a free-running PHY clock. + * @dis_del_phy_power_chg_quirk: set if we disable delay phy power + * change quirk. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value * 0 - -6dB de-emphasis @@ -845,6 +873,7 @@ struct dwc3 { size_t regs_size; enum usb_dr_mode dr_mode; + enum usb_phy_interface hsphy_mode; u32 fladj; u32 irq_gadget; @@ -880,6 +909,8 @@ struct dwc3 { #define DWC3_REVISION_260A 0x5533260a #define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_280A 0x5533280a +#define DWC3_REVISION_300A 0x5533300a +#define DWC3_REVISION_310A 0x5533310a /* * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really @@ -942,6 +973,8 @@ struct dwc3 { unsigned dis_u2_susphy_quirk:1; unsigned dis_enblslpm_quirk:1; unsigned dis_rxdet_inp3_quirk:1; + unsigned dis_u2_freeclk_exists_quirk:1; + unsigned dis_del_phy_power_chg_quirk:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e56d59b19a0e..fe414e7a9c78 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -36,36 +36,25 @@ struct dwc3_of_simple { int num_clocks; }; -static int dwc3_of_simple_probe(struct platform_device *pdev) +static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) { - struct dwc3_of_simple *simple; - struct device *dev = &pdev->dev; + struct device *dev = simple->dev; struct device_node *np = dev->of_node; - - unsigned int count; - int ret; int i; - simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); - if (!simple) - return -ENOMEM; + simple->num_clocks = count; - count = of_clk_get_parent_count(np); if (!count) - return -ENOENT; - - simple->num_clocks = count; + return 0; simple->clks = devm_kcalloc(dev, simple->num_clocks, sizeof(struct clk *), GFP_KERNEL); if (!simple->clks) return -ENOMEM; - platform_set_drvdata(pdev, simple); - simple->dev = dev; - for (i = 0; i < simple->num_clocks; i++) { struct clk *clk; + int ret; clk = of_clk_get(np, i); if (IS_ERR(clk)) { @@ -88,6 +77,29 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) simple->clks[i] = clk; } + return 0; +} + +static int dwc3_of_simple_probe(struct platform_device *pdev) +{ + struct dwc3_of_simple *simple; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + int ret; + int i; + + simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); + if (!simple) + return -ENOMEM; + + platform_set_drvdata(pdev, simple); + simple->dev = dev; + + ret = dwc3_of_simple_clk_init(simple, of_clk_get_parent_count(np)); + if (ret) + return ret; + ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { for (i = 0; i < simple->num_clocks; i++) { @@ -112,7 +124,7 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) int i; for (i = 0; i < simple->num_clocks; i++) { - clk_unprepare(simple->clks[i]); + clk_disable_unprepare(simple->clks[i]); clk_put(simple->clks[i]); } @@ -162,7 +174,9 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "qcom,dwc3" }, + { .compatible = "rockchip,rk3399-dwc3" }, { .compatible = "xlnx,zynqmp-dwc3" }, + { .compatible = "cavium,octeon-7130-usb-uctl" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 122e64df2f4d..07cc8929f271 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -174,15 +174,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; - int i; - if (req->started) { - i = 0; - do { - dwc3_ep_inc_deq(dep); - } while(++i < req->request.num_mapped_sgs); - req->started = false; - } + req->started = false; list_del(&req->list); req->trb = NULL; @@ -348,7 +341,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) * IN transfers due to a mishandled error condition. Synopsys * STAR 9000614252. */ - if (dep->direction && (dwc->revision >= DWC3_REVISION_260A)) + if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) && + (dwc->gadget.speed >= USB_SPEED_SUPER)) cmd |= DWC3_DEPCMD_CLEARPENDIN; memset(¶ms, 0, sizeof(params)); @@ -490,7 +484,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param0 |= DWC3_DEPCFG_ACTION_INIT; } - params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; + if (usb_endpoint_xfer_control(desc)) + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc)) params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN; @@ -764,6 +759,8 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, kfree(req); } +static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep); + /** * dwc3_prepare_one_trb - setup one TRB from one request * @dep: endpoint for which this request is prepared @@ -771,15 +768,13 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, dma_addr_t dma, - unsigned length, unsigned last, unsigned chain, unsigned node) + unsigned length, unsigned chain, unsigned node) { struct dwc3_trb *trb; - dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s", + dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s", dep->name, req, (unsigned long long) dma, - length, last ? " last" : "", - chain ? " chain" : ""); - + length, chain ? " chain" : ""); trb = &dep->trb_pool[dep->trb_enqueue]; @@ -826,12 +821,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, /* always enable Continue on Short Packet */ trb->ctrl |= DWC3_TRB_CTRL_CSP; - if (!req->request.no_interrupt && !chain) + if ((!req->request.no_interrupt && !chain) || + (dwc3_calc_trbs_left(dep) == 0)) trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI; - if (last && !usb_endpoint_xfer_isoc(dep->endpoint.desc)) - trb->ctrl |= DWC3_TRB_CTRL_LST; - if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; @@ -856,12 +849,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, */ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) { - if (!index) - index = DWC3_TRB_NUM - 2; - else - index = dep->trb_enqueue - 1; + u8 tmp = index; - return &dep->trb_pool[index]; + if (!tmp) + tmp = DWC3_TRB_NUM - 1; + + return &dep->trb_pool[tmp - 1]; } static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) @@ -894,65 +887,42 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) } static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned int trbs_left, - unsigned int more_coming) + struct dwc3_request *req) { - struct usb_request *request = &req->request; - struct scatterlist *sg = request->sg; + struct scatterlist *sg = req->sg; struct scatterlist *s; - unsigned int last = false; unsigned int length; dma_addr_t dma; int i; - for_each_sg(sg, s, request->num_mapped_sgs, i) { + for_each_sg(sg, s, req->num_pending_sgs, i) { unsigned chain = true; length = sg_dma_len(s); dma = sg_dma_address(s); - if (sg_is_last(s)) { - if (usb_endpoint_xfer_int(dep->endpoint.desc) || - !more_coming) - last = true; - - chain = false; - } - - if (!trbs_left--) - last = true; - - if (last) + if (sg_is_last(s)) chain = false; dwc3_prepare_one_trb(dep, req, dma, length, - last, chain, i); + chain, i); - if (last) + if (!dwc3_calc_trbs_left(dep)) break; } } static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned int trbs_left, - unsigned int more_coming) + struct dwc3_request *req) { - unsigned int last = false; unsigned int length; dma_addr_t dma; dma = req->request.dma; length = req->request.length; - if (!trbs_left) - last = true; - - /* Is this the last request? */ - if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming) - last = true; - dwc3_prepare_one_trb(dep, req, dma, length, - last, false, 0); + false, 0); } /* @@ -966,26 +936,19 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, static void dwc3_prepare_trbs(struct dwc3_ep *dep) { struct dwc3_request *req, *n; - unsigned int more_coming; - u32 trbs_left; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); - trbs_left = dwc3_calc_trbs_left(dep); - if (!trbs_left) + if (!dwc3_calc_trbs_left(dep)) return; - more_coming = dep->allocated_requests - dep->queued_requests; - list_for_each_entry_safe(req, n, &dep->pending_list, list) { - if (req->request.num_mapped_sgs > 0) - dwc3_prepare_one_trb_sg(dep, req, trbs_left--, - more_coming); + if (req->num_pending_sgs > 0) + dwc3_prepare_one_trb_sg(dep, req); else - dwc3_prepare_one_trb_linear(dep, req, trbs_left--, - more_coming); + dwc3_prepare_one_trb_linear(dep, req); - if (!trbs_left) + if (!dwc3_calc_trbs_left(dep)) return; } } @@ -1101,93 +1064,29 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) trace_dwc3_ep_queue(req); - /* - * We only add to our list of requests now and - * start consuming the list once we get XferNotReady - * IRQ. - * - * That way, we avoid doing anything that we don't need - * to do now and defer it until the point we receive a - * particular token from the Host side. - * - * This will also avoid Host cancelling URBs due to too - * many NAKs. - */ ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->direction); if (ret) return ret; - list_add_tail(&req->list, &dep->pending_list); - - /* - * If there are no pending requests and the endpoint isn't already - * busy, we will just start the request straight away. - * - * This will save one IRQ (XFER_NOT_READY) and possibly make it a - * little bit faster. - */ - if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && - !usb_endpoint_xfer_int(dep->endpoint.desc)) { - ret = __dwc3_gadget_kick_transfer(dep, 0); - goto out; - } - - /* - * There are a few special cases: - * - * 1. XferNotReady with empty list of requests. We need to kick the - * transfer here in that situation, otherwise we will be NAKing - * forever. If we get XferNotReady before gadget driver has a - * chance to queue a request, we will ACK the IRQ but won't be - * able to receive the data until the next request is queued. - * The following code is handling exactly that. - * - */ - if (dep->flags & DWC3_EP_PENDING_REQUEST) { - /* - * If xfernotready is already elapsed and it is a case - * of isoc transfer, then issue END TRANSFER, so that - * you can receive xfernotready again and can have - * notion of current microframe. - */ - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - if (list_empty(&dep->started_list)) { - dwc3_stop_active_transfer(dwc, dep->number, true); - dep->flags = DWC3_EP_ENABLED; - } - return 0; - } - - ret = __dwc3_gadget_kick_transfer(dep, 0); - if (!ret) - dep->flags &= ~DWC3_EP_PENDING_REQUEST; + req->sg = req->request.sg; + req->num_pending_sgs = req->request.num_mapped_sgs; - goto out; - } + list_add_tail(&req->list, &dep->pending_list); - /* - * 2. XferInProgress on Isoc EP with an active transfer. We need to - * kick the transfer here after queuing a request, otherwise the - * core may not see the modified TRB(s). - */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (dep->flags & DWC3_EP_BUSY) && - !(dep->flags & DWC3_EP_MISSED_ISOC)) { - WARN_ON_ONCE(!dep->resource_index); - ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index); - goto out; + dep->flags & DWC3_EP_PENDING_REQUEST) { + if (list_empty(&dep->started_list)) { + dwc3_stop_active_transfer(dwc, dep->number, true); + dep->flags = DWC3_EP_ENABLED; + } + return 0; } - /* - * 4. Stream Capable Bulk Endpoints. We need to start the transfer - * right away, otherwise host will not know we have streams to be - * handled. - */ - if (dep->stream_capable) - ret = __dwc3_gadget_kick_transfer(dep, 0); + if (!dwc3_calc_trbs_left(dep)) + return 0; -out: + ret = __dwc3_gadget_kick_transfer(dep, 0); if (ret && ret != -EBUSY) dwc3_trace(trace_dwc3_gadget, "%s: failed to kick transfers", @@ -1963,6 +1862,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int trb_status; dep->queued_requests--; + dwc3_ep_inc_deq(dep); trace_dwc3_complete_trb(dep, trb); /* @@ -1982,6 +1882,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, return 1; count = trb->size & DWC3_TRB_SIZE_MASK; + req->request.actual += count; if (dep->direction) { if (count) { @@ -2021,48 +1922,51 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, if (s_pkt && !chain) return 1; - if ((event->status & DEPEVT_STATUS_LST) && - (trb->ctrl & (DWC3_TRB_CTRL_LST | - DWC3_TRB_CTRL_HWO))) - return 1; + if ((event->status & DEPEVT_STATUS_IOC) && (trb->ctrl & DWC3_TRB_CTRL_IOC)) return 1; + return 0; } static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, const struct dwc3_event_depevt *event, int status) { - struct dwc3_request *req; + struct dwc3_request *req, *n; struct dwc3_trb *trb; - unsigned int slot; - unsigned int i; - int count = 0; + bool ioc = false; int ret; - do { + list_for_each_entry_safe(req, n, &dep->started_list, list) { + unsigned length; + unsigned actual; int chain; - req = next_request(&dep->started_list); - if (WARN_ON_ONCE(!req)) - return 1; - - chain = req->request.num_mapped_sgs > 0; - i = 0; - do { - slot = req->first_trb_index + i; - if (slot == DWC3_TRB_NUM - 1) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; - count += trb->size & DWC3_TRB_SIZE_MASK; + length = req->request.length; + chain = req->num_pending_sgs > 0; + if (chain) { + struct scatterlist *sg = req->sg; + struct scatterlist *s; + unsigned int pending = req->num_pending_sgs; + unsigned int i; + for_each_sg(sg, s, pending, i) { + trb = &dep->trb_pool[dep->trb_dequeue]; + + req->sg = sg_next(s); + req->num_pending_sgs--; + + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status, chain); + if (ret) + break; + } + } else { + trb = &dep->trb_pool[dep->trb_dequeue]; ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status, chain); - if (ret) - break; - } while (++i < req->request.num_mapped_sgs); + } /* * We assume here we will always receive the entire data block @@ -2071,12 +1975,21 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, * should receive and we simply bounce the request back to the * gadget driver for further processing. */ - req->request.actual += req->request.length - count; + actual = length - req->request.actual; + req->request.actual = actual; + + if (ret && chain && (actual < length) && req->num_pending_sgs) + return __dwc3_gadget_kick_transfer(dep, 0); + dwc3_gadget_giveback(dep, req, status); - if (ret) + if (ret) { + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + ioc = true; break; - } while (1); + } + } /* * Our endpoint might get disabled by another thread during @@ -2103,10 +2016,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, return 1; } - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) - if ((event->status & DEPEVT_STATUS_IOC) && - (trb->ctrl & DWC3_TRB_CTRL_IOC)) - return 0; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc) + return 0; + return 1; } @@ -2322,6 +2234,18 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) * * - Issue EndTransfer WITH CMDIOC bit set * - Wait 100us + * + * As of IP version 3.10a of the DWC_usb3 IP, the controller + * supports a mode to work around the above limitation. The + * software can poll the CMDACT bit in the DEPCMD register + * after issuing a EndTransfer command. This mode is enabled + * by writing GUCTL2[14]. This polling is already done in the + * dwc3_send_gadget_ep_cmd() function so if the mode is + * enabled, the EndTransfer command will have completed upon + * returning from this function and we don't need to delay for + * 100us. + * + * This mode is NOT available on the DWC_usb31 IP. */ cmd = DWC3_DEPCMD_ENDTRANSFER; @@ -2333,7 +2257,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) WARN_ON_ONCE(ret); dep->resource_index = 0; dep->flags &= ~DWC3_EP_BUSY; - udelay(100); + + if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) + udelay(100); } static void dwc3_stop_active_transfers(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index ec004c6d76f2..bd86f84f3790 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -35,9 +35,9 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc) return -ETIMEDOUT; } -static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) +static int dwc3_ulpi_read(struct device *dev, u8 addr) { - struct dwc3 *dwc = dev_get_drvdata(ops->dev); + struct dwc3 *dwc = dev_get_drvdata(dev); u32 reg; int ret; @@ -53,9 +53,9 @@ static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) return DWC3_GUSB2PHYACC_DATA(reg); } -static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) +static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) { - struct dwc3 *dwc = dev_get_drvdata(ops->dev); + struct dwc3 *dwc = dev_get_drvdata(dev); u32 reg; reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); @@ -65,7 +65,7 @@ static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) return dwc3_ulpi_busyloop(dwc); } -static struct ulpi_ops dwc3_ulpi_ops = { +static const struct ulpi_ops dwc3_ulpi_ops = { .read = dwc3_ulpi_read, .write = dwc3_ulpi_write, }; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3c3f31ceece7..8ad203296079 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -209,25 +209,6 @@ config USB_F_PRINTER config USB_F_TCM tristate -choice - tristate "USB Gadget Drivers" - default USB_ETH - help - A Linux "Gadget Driver" talks to the USB Peripheral Controller - driver through the abstract "gadget" API. Some other operating - systems call these "client" drivers, of which "class drivers" - are a subset (implementing a USB device class specification). - A gadget driver implements one or more USB functions using - the peripheral hardware. - - Gadget drivers are hardware-neutral, or "platform independent", - except that they sometimes must understand quirks or limitations - of the particular controllers they work with. For example, when - a controller doesn't support alternate configurations or provide - enough of the right types of endpoints, the gadget driver might - not be able work with that controller, or might need to implement - a less common variant of a device class protocol. - # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -439,6 +420,7 @@ config USB_CONFIGFS_F_HID config USB_CONFIGFS_F_UVC bool "USB Webcam function" depends on USB_CONFIGFS + depends on VIDEO_V4L2 depends on VIDEO_DEV select VIDEOBUF2_VMALLOC select USB_F_UVC @@ -475,6 +457,25 @@ config USB_CONFIGFS_F_TCM Both protocols can work on USB2.0 and USB3.0. UAS utilizes the USB 3.0 feature called streams support. +choice + tristate "USB Gadget Drivers" + default USB_ETH + help + A Linux "Gadget Driver" talks to the USB Peripheral Controller + driver through the abstract "gadget" API. Some other operating + systems call these "client" drivers, of which "class drivers" + are a subset (implementing a USB device class specification). + A gadget driver implements one or more USB functions using + the peripheral hardware. + + Gadget drivers are hardware-neutral, or "platform independent", + except that they sometimes must understand quirks or limitations + of the particular controllers they work with. For example, when + a controller doesn't support alternate configurations or provide + enough of the right types of endpoints, the gadget driver might + not be able work with that controller, or might need to implement + a less common variant of a device class protocol. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 5ebe6af7976e..32176f779861 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1893,17 +1893,21 @@ unknown: /* functions always handle their interfaces and endpoints... * punt other recipients (other, WUSB, ...) to the current * configuration code. - * - * REVISIT it could make sense to let the composite device - * take such requests too, if that's ever needed: to work - * in config 0, etc. */ if (cdev->config) { list_for_each_entry(f, &cdev->config->functions, list) - if (f->req_match && f->req_match(f, ctrl)) + if (f->req_match && + f->req_match(f, ctrl, false)) goto try_fun_setup; - f = NULL; + } else { + struct usb_configuration *c; + list_for_each_entry(c, &cdev->configs, list) + list_for_each_entry(f, &c->functions, list) + if (f->req_match && + f->req_match(f, ctrl, true)) + goto try_fun_setup; } + f = NULL; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index f9237fe2be05..3984787f8e97 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1211,8 +1211,9 @@ static void purge_configs_funcs(struct gadget_info *gi) list_move_tail(&f->list, &cfg->func_list); if (f->unbind) { - dev_err(&gi->cdev.gadget->dev, "unbind function" - " '%s'/%p\n", f->name, f); + dev_dbg(&gi->cdev.gadget->dev, + "unbind function '%s'/%p\n", + f->name, f); f->unbind(c, f); } } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 5c8429f23a89..0aeed85bb5cb 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -98,6 +98,9 @@ static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, const struct usb_ctrlrequest *); +static bool ffs_func_req_match(struct usb_function *, + const struct usb_ctrlrequest *, + bool config0); static void ffs_func_suspend(struct usb_function *); static void ffs_func_resume(struct usb_function *); @@ -2243,7 +2246,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC | FUNCTIONFS_VIRTUAL_ADDR | - FUNCTIONFS_EVENTFD)) { + FUNCTIONFS_EVENTFD | + FUNCTIONFS_ALL_CTRL_RECIP | + FUNCTIONFS_CONFIG0_SETUP)) { ret = -ENOSYS; goto error; } @@ -3094,8 +3099,9 @@ static int ffs_func_setup(struct usb_function *f, * handle them. All other either handled by composite or * passed to usb_configuration->setup() (if one is set). No * matter, we will handle requests directed to endpoint here - * as well (as it's straightforward) but what to do with any - * other request? + * as well (as it's straightforward). Other request recipient + * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP + * is being used. */ if (ffs->state != FFS_ACTIVE) return -ENODEV; @@ -3116,7 +3122,10 @@ static int ffs_func_setup(struct usb_function *f, break; default: - return -EOPNOTSUPP; + if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP) + ret = le16_to_cpu(creq->wIndex); + else + return -EOPNOTSUPP; } spin_lock_irqsave(&ffs->ev.waitq.lock, flags); @@ -3128,6 +3137,28 @@ static int ffs_func_setup(struct usb_function *f, return 0; } +static bool ffs_func_req_match(struct usb_function *f, + const struct usb_ctrlrequest *creq, + bool config0) +{ + struct ffs_function *func = ffs_func_from_usb(f); + + if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP)) + return false; + + switch (creq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + return ffs_func_revmap_intf(func, + le16_to_cpu(creq->wIndex) >= 0); + case USB_RECIP_ENDPOINT: + return ffs_func_revmap_ep(func, + le16_to_cpu(creq->wIndex) >= 0); + default: + return (bool) (func->ffs->user_flags & + FUNCTIONFS_ALL_CTRL_RECIP); + } +} + static void ffs_func_suspend(struct usb_function *f) { ENTER(); @@ -3378,6 +3409,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) func->function.set_alt = ffs_func_set_alt; func->function.disable = ffs_func_disable; func->function.setup = ffs_func_setup; + func->function.req_match = ffs_func_req_match; func->function.suspend = ffs_func_suspend; func->function.resume = ffs_func_resume; func->function.free_func = ffs_free; @@ -3470,6 +3502,11 @@ static void _ffs_free_dev(struct ffs_dev *dev) list_del(&dev->entry); if (dev->name_allocated) kfree(dev->name); + + /* Clear the private_data pointer to stop incorrect dev access */ + if (dev->ffs_data) + dev->ffs_data->private_data = NULL; + kfree(dev); if (list_empty(&ffs_devices)) functionfs_cleanup(); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 51980c50546d..e2966f87c860 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -365,7 +365,7 @@ static int f_hidg_open(struct inode *inode, struct file *fd) static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length) { - return alloc_ep_req(ep, length, length); + return alloc_ep_req(ep, length); } static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) @@ -617,14 +617,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) /* preallocate request and buffer */ status = -ENOMEM; - hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); + hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length); if (!hidg->req) goto fail; - hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); - if (!hidg->req->buf) - goto fail; - /* set descriptor dynamic values */ hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; @@ -677,11 +673,8 @@ fail_free_descs: usb_free_all_descriptors(f); fail: ERROR(f->config->cdev, "hidg_bind FAILED\n"); - if (hidg->req != NULL) { - kfree(hidg->req->buf); - if (hidg->in_ep != NULL) - usb_ep_free_request(hidg->in_ep, hidg->req); - } + if (hidg->req != NULL) + free_ep_req(hidg->in_ep, hidg->req); return status; } @@ -809,11 +802,21 @@ end: CONFIGFS_ATTR(f_hid_opts_, report_desc); +static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page) +{ + struct f_hid_opts *opts = to_f_hid_opts(item); + + return sprintf(page, "%d:%d\n", major, opts->minor); +} + +CONFIGFS_ATTR_RO(f_hid_opts_, dev); + static struct configfs_attribute *hid_attrs[] = { &f_hid_opts_attr_subclass, &f_hid_opts_attr_protocol, &f_hid_opts_attr_report_length, &f_hid_opts_attr_report_desc, + &f_hid_opts_attr_dev, NULL, }; @@ -910,8 +913,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) /* disable/free request and end point */ usb_ep_disable(hidg->in_ep); - kfree(hidg->req->buf); - usb_ep_free_request(hidg->in_ep, hidg->req); + free_ep_req(hidg->in_ep, hidg->req); usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 3a9f8f9c77bd..e70093835e14 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -308,9 +308,7 @@ static void disable_loopback(struct f_loopback *loop) static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) { - struct f_loopback *loop = ep->driver_data; - - return alloc_ep_req(ep, len, loop->buflen); + return alloc_ep_req(ep, len); } static int alloc_requests(struct usb_composite_dev *cdev, @@ -333,7 +331,7 @@ static int alloc_requests(struct usb_composite_dev *cdev, if (!in_req) goto fail; - out_req = lb_alloc_ep_req(loop->out_ep, 0); + out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen); if (!out_req) goto fail_in; @@ -593,13 +591,9 @@ DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); int __init lb_modinit(void) { - int ret; - - ret = usb_function_register(&Loopbackusb_func); - if (ret) - return ret; - return ret; + return usb_function_register(&Loopbackusb_func); } + void __exit lb_modexit(void) { usb_function_unregister(&Loopbackusb_func); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 2505117e88e8..8f3659b65f53 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -311,11 +311,7 @@ struct fsg_common { /* Gadget's private data. */ void *private_data; - /* - * Vendor (8 chars), product (16 chars), release (4 - * hexadecimal digits) and NUL byte - */ - char inquiry_string[8 + 16 + 4 + 1]; + char inquiry_string[INQUIRY_STRING_LEN]; struct kref ref; }; @@ -1107,7 +1103,12 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) buf[5] = 0; /* No special options */ buf[6] = 0; buf[7] = 0; - memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); + if (curlun->inquiry_string[0]) + memcpy(buf + 8, curlun->inquiry_string, + sizeof(curlun->inquiry_string)); + else + memcpy(buf + 8, common->inquiry_string, + sizeof(common->inquiry_string)); return 36; } @@ -3209,12 +3210,27 @@ static ssize_t fsg_lun_opts_nofua_store(struct config_item *item, CONFIGFS_ATTR(fsg_lun_opts_, nofua); +static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item, + char *page) +{ + return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item, + const char *page, size_t len) +{ + return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string); + static struct configfs_attribute *fsg_lun_attrs[] = { &fsg_lun_opts_attr_file, &fsg_lun_opts_attr_ro, &fsg_lun_opts_attr_removable, &fsg_lun_opts_attr_cdrom, &fsg_lun_opts_attr_nofua, + &fsg_lun_opts_attr_inquiry_string, NULL, }; diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index b6a9918eaefb..d3902313b8ac 100644 --- a/drivers/usb/gadget/function/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h @@ -100,6 +100,7 @@ struct fsg_lun_config { char removable; char cdrom; char nofua; + char inquiry_string[INQUIRY_STRING_LEN]; }; struct fsg_config { diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 58fc199a18ec..a5719f271bf0 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -51,6 +51,19 @@ static const char f_midi_longname[] = "MIDI Gadget"; */ #define MAX_PORTS 16 +/* MIDI message states */ +enum { + STATE_INITIAL = 0, /* pseudo state */ + STATE_1PARAM, + STATE_2PARAM_1, + STATE_2PARAM_2, + STATE_SYSEX_0, + STATE_SYSEX_1, + STATE_SYSEX_2, + STATE_REAL_TIME, + STATE_FINISHED, /* pseudo state */ +}; + /* * This is a gadget, and the IN/OUT naming is from the host's perspective. * USB -> OUT endpoint -> rawmidi @@ -61,13 +74,6 @@ struct gmidi_in_port { int active; uint8_t cable; uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 uint8_t data[2]; }; @@ -205,7 +211,7 @@ static struct usb_gadget_strings *midi_strings[] = { static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, unsigned length) { - return alloc_ep_req(ep, length, length); + return alloc_ep_req(ep, length); } static const uint8_t f_midi_cin_length[] = { @@ -299,6 +305,19 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req) } } +static void f_midi_drop_out_substreams(struct f_midi *midi) +{ + unsigned int i; + + for (i = 0; i < midi->in_ports; i++) { + struct gmidi_in_port *port = midi->in_ports_array + i; + struct snd_rawmidi_substream *substream = port->substream; + + if (port->active && substream) + snd_rawmidi_drop_output(substream); + } +} + static int f_midi_start_ep(struct f_midi *midi, struct usb_function *f, struct usb_ep *ep) @@ -360,9 +379,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = - midi_alloc_ep_req(midi->out_ep, - max_t(unsigned, midi->buflen, - bulk_out_desc.wMaxPacketSize)); + midi_alloc_ep_req(midi->out_ep, midi->buflen); + if (req == NULL) return -ENOMEM; @@ -397,6 +415,8 @@ static void f_midi_disable(struct usb_function *f) /* release IN requests */ while (kfifo_get(&midi->in_req_fifo, &req)) free_ep_req(midi->in_ep, req); + + f_midi_drop_out_substreams(midi); } static int f_midi_snd_free(struct snd_device *device) @@ -404,130 +424,166 @@ static int f_midi_snd_free(struct snd_device *device) return 0; } -static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - /* * Converts MIDI commands to USB MIDI packets. */ static void f_midi_transmit_byte(struct usb_request *req, struct gmidi_in_port *port, uint8_t b) { - uint8_t p0 = port->cable << 4; + uint8_t p[4] = { port->cable << 4, 0, 0, 0 }; + uint8_t next_state = STATE_INITIAL; + + switch (b) { + case 0xf8 ... 0xff: + /* System Real-Time Messages */ + p[0] |= 0x0f; + p[1] = b; + next_state = port->state; + port->state = STATE_REAL_TIME; + break; - if (b >= 0xf8) { - f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { + case 0xf7: + /* End of SysEx */ + switch (port->state) { + case STATE_SYSEX_0: + p[0] |= 0x05; + p[1] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_1: + p[0] |= 0x06; + p[1] = port->data[0]; + p[2] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_2: + p[0] |= 0x07; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = 0xf7; + next_state = STATE_FINISHED; + break; + default: + /* Ignore byte */ + next_state = port->state; + port->state = STATE_INITIAL; + } + break; + + case 0xf0 ... 0xf6: + /* System Common Messages */ + port->data[0] = port->data[1] = 0; + port->state = STATE_INITIAL; switch (b) { case 0xf0: port->data[0] = b; - port->state = STATE_SYSEX_1; + port->data[1] = 0; + next_state = STATE_SYSEX_1; break; case 0xf1: case 0xf3: port->data[0] = b; - port->state = STATE_1PARAM; + next_state = STATE_1PARAM; break; case 0xf2: port->data[0] = b; - port->state = STATE_2PARAM_1; + next_state = STATE_2PARAM_1; break; case 0xf4: case 0xf5: - port->state = STATE_UNKNOWN; + next_state = STATE_INITIAL; break; case 0xf6: - f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - f_midi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - f_midi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - f_midi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; + p[0] |= 0x05; + p[1] = 0xf6; + next_state = STATE_FINISHED; break; } - } else if (b >= 0x80) { + break; + + case 0x80 ... 0xef: + /* + * Channel Voice Messages, Channel Mode Messages + * and Control Change Messages. + */ port->data[0] = b; + port->data[1] = 0; + port->state = STATE_INITIAL; if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; + next_state = STATE_1PARAM; else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ + next_state = STATE_2PARAM_1; + break; + + case 0x00 ... 0x7f: + /* Message parameters */ switch (port->state) { case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - f_midi_transmit_packet(req, p0, port->data[0], b, 0); + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x02; + + p[1] = port->data[0]; + p[2] = b; + /* This is to allow Running State Messages */ + next_state = STATE_1PARAM; break; case STATE_2PARAM_1: port->data[1] = b; - port->state = STATE_2PARAM_2; + next_state = STATE_2PARAM_2; break; case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - f_midi_transmit_packet(req, - p0, port->data[0], port->data[1], b); + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x03; + + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + /* This is to allow Running State Messages */ + next_state = STATE_2PARAM_1; break; case STATE_SYSEX_0: port->data[0] = b; - port->state = STATE_SYSEX_1; + next_state = STATE_SYSEX_1; break; case STATE_SYSEX_1: port->data[1] = b; - port->state = STATE_SYSEX_2; + next_state = STATE_SYSEX_2; break; case STATE_SYSEX_2: - f_midi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; + p[0] |= 0x04; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + next_state = STATE_SYSEX_0; break; } + break; } -} -static void f_midi_drop_out_substreams(struct f_midi *midi) -{ - unsigned int i; + /* States where we have to write into the USB request */ + if (next_state == STATE_FINISHED || + port->state == STATE_SYSEX_2 || + port->state == STATE_1PARAM || + port->state == STATE_2PARAM_2 || + port->state == STATE_REAL_TIME) { - for (i = 0; i < midi->in_ports; i++) { - struct gmidi_in_port *port = midi->in_ports_array + i; - struct snd_rawmidi_substream *substream = port->substream; - if (port->active && substream) - snd_rawmidi_drop_output(substream); + unsigned int length = req->length; + u8 *buf = (u8 *)req->buf + length; + + memcpy(buf, p, sizeof(p)); + req->length = length + sizeof(p); + + if (next_state == STATE_FINISHED) { + next_state = STATE_INITIAL; + port->data[0] = port->data[1] = 0; + } } + + port->state = next_state; } static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep) @@ -642,7 +698,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream) VDBG(midi, "%s()\n", __func__); port = midi->in_ports_array + substream->number; port->substream = substream; - port->state = STATE_UNKNOWN; + port->state = STATE_INITIAL; return 0; } @@ -1123,7 +1179,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void) opts->func_inst.free_func_inst = f_midi_free_inst; opts->index = SNDRV_DEFAULT_IDX1; opts->id = SNDRV_DEFAULT_STR1; - opts->buflen = 256; + opts->buflen = 512; opts->qlen = 32; opts->in_ports = 1; opts->out_ports = 1; diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 97f0a9bc84df..639603722709 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -90,7 +90,9 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static inline unsigned ncm_bitrate(struct usb_gadget *g) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 13 * 1024 * 8 * 1000 * 8; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else return 19 * 64 * 1 * 1000 * 8; @@ -333,6 +335,76 @@ static struct usb_descriptor_header *ncm_hs_function[] = { NULL, }; + +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_ncm_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT), + .bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS) +}; + +static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = { + .bLength = sizeof(ss_ncm_notify_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(NCM_STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ss_ncm_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ncm_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = { + .bLength = sizeof(ss_ncm_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ncm_ss_function[] = { + (struct usb_descriptor_header *) &ncm_iad_desc, + /* CDC NCM control descriptors */ + (struct usb_descriptor_header *) &ncm_control_intf, + (struct usb_descriptor_header *) &ncm_header_desc, + (struct usb_descriptor_header *) &ncm_union_desc, + (struct usb_descriptor_header *) &ecm_desc, + (struct usb_descriptor_header *) &ncm_desc, + (struct usb_descriptor_header *) &ss_ncm_notify_desc, + (struct usb_descriptor_header *) &ss_ncm_notify_comp_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ncm_data_nop_intf, + (struct usb_descriptor_header *) &ncm_data_intf, + (struct usb_descriptor_header *) &ss_ncm_in_desc, + (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_ncm_out_desc, + (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ #define STRING_CTRL_IDX 0 @@ -852,6 +924,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) */ ncm->port.is_zlp_ok = gadget_is_zlp_supported(cdev->gadget); + ncm->port.no_skb_reserve = + gadget_avoids_skb_reserve(cdev->gadget); ncm->port.cdc_filter = DEFAULT_FILTER; DBG(cdev, "activate ncm\n"); net = gether_connect(&ncm->port); @@ -1431,8 +1505,13 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) hs_ncm_notify_desc.bEndpointAddress = fs_ncm_notify_desc.bEndpointAddress; + ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress; + ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress; + ss_ncm_notify_desc.bEndpointAddress = + fs_ncm_notify_desc.bEndpointAddress; + status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, - NULL, NULL); + ncm_ss_function, NULL); if (status) goto fail; @@ -1450,6 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->task_timer.function = ncm_tx_timeout; DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ncm->port.in_ep->name, ncm->port.out_ep->name, ncm->notify->name); diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 64706a789580..0de36cda6e41 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -889,13 +889,17 @@ static void printer_soft_reset(struct printer_dev *dev) /*-------------------------------------------------------------------------*/ static bool gprinter_req_match(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) + const struct usb_ctrlrequest *ctrl, + bool config0) { struct printer_dev *dev = func_to_printer(f); u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + if (config0) + return false; + if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) return false; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index df0189ddfdd5..8784fa12ea2c 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -293,9 +293,7 @@ static struct usb_gadget_strings *sourcesink_strings[] = { static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { - struct f_sourcesink *ss = ep->driver_data; - - return alloc_ep_req(ep, len, ss->buflen); + return alloc_ep_req(ep, len); } static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) @@ -606,7 +604,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, } else { ep = is_in ? ss->in_ep : ss->out_ep; qlen = ss->bulk_qlen; - size = 0; + size = ss->buflen; } for (i = 0; i < qlen; i++) { diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 29b41b5dee04..27ed51b5082f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -258,6 +258,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); v4l2_event_queue(&uvc->vdev, &v4l2_event); + /* Pass additional setup data to userspace */ + if (uvc->event_setup_out && uvc->event_length) { + uvc->control_req->length = uvc->event_length; + return usb_ep_queue(uvc->func.config->cdev->gadget->ep0, + uvc->control_req, GFP_ATOMIC); + } + return 0; } diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 990df221c629..8fbf6861690d 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -369,6 +369,12 @@ ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) } EXPORT_SYMBOL_GPL(fsg_show_removable); +ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%s\n", curlun->inquiry_string); +} +EXPORT_SYMBOL_GPL(fsg_show_inquiry_string); + /* * The caller must hold fsg->filesem for reading when calling this function. */ @@ -499,4 +505,22 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, } EXPORT_SYMBOL_GPL(fsg_store_removable); +ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, + size_t count) +{ + const size_t len = min(count, sizeof(curlun->inquiry_string)); + + if (len == 0 || buf[0] == '\n') { + curlun->inquiry_string[0] = 0; + } else { + snprintf(curlun->inquiry_string, + sizeof(curlun->inquiry_string), "%-28s", buf); + if (curlun->inquiry_string[len-1] == '\n') + curlun->inquiry_string[len-1] = ' '; + } + + return count; +} +EXPORT_SYMBOL_GPL(fsg_store_inquiry_string); + MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index c3544e61da66..e69848994cb4 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -88,6 +88,12 @@ do { \ #define ASC(x) ((u8) ((x) >> 8)) #define ASCQ(x) ((u8) (x)) +/* + * Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL + * byte + */ +#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1)) + struct fsg_lun { struct file *filp; loff_t file_length; @@ -112,6 +118,7 @@ struct fsg_lun { struct device dev; const char *name; /* "lun.name" */ const char **name_pfx; /* "function.name" */ + char inquiry_string[INQUIRY_STRING_LEN]; }; static inline bool fsg_lun_is_open(struct fsg_lun *curlun) @@ -210,6 +217,7 @@ ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, char *buf); +ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, @@ -221,5 +229,7 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count); ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, + size_t count); #endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 5f562c1ec795..8cb08033b7c1 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -82,6 +82,7 @@ struct eth_dev { #define WORK_RX_MEMORY 0 bool zlp; + bool no_skb_reserve; u8 host_mac[ETH_ALEN]; u8 dev_mac[ETH_ALEN]; }; @@ -233,7 +234,8 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) * but on at least one, checksumming fails otherwise. Note: * RNDIS headers involve variable numbers of LE32 values. */ - skb_reserve(skb, NET_IP_ALIGN); + if (likely(!dev->no_skb_reserve)) + skb_reserve(skb, NET_IP_ALIGN); req->buf = skb->data; req->length = size; @@ -551,14 +553,16 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb) skb = dev->wrap(dev->port_usb, skb); - spin_unlock_irqrestore(&dev->lock, flags); if (!skb) { /* Multi frame CDC protocols may store the frame for * later which is not a dropped frame. */ if (dev->port_usb && - dev->port_usb->supports_multi_frame) + dev->port_usb->supports_multi_frame) { + spin_unlock_irqrestore(&dev->lock, flags); goto multiframe; + } + spin_unlock_irqrestore(&dev->lock, flags); goto drop; } } @@ -569,12 +573,14 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->complete = tx_complete; /* NCM requires no zlp if transfer is dwNtbInMaxSize */ - if (dev->port_usb->is_fixed && + if (dev->port_usb && + dev->port_usb->is_fixed && length == dev->port_usb->fixed_in_len && (length % in->maxpacket) == 0) req->zero = 0; else req->zero = 1; + spin_unlock_irqrestore(&dev->lock, flags); /* use zlp framing on tx for strict CDC-Ether conformance, * though any robust network rx path ignores extra padding. @@ -1063,6 +1069,7 @@ struct net_device *gether_connect(struct gether *link) if (result == 0) { dev->zlp = link->is_zlp_ok; + dev->no_skb_reserve = link->no_skb_reserve; DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult)); dev->header_len = link->header_len; diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index c77145bd6b5b..81d94a7ae4b4 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -64,6 +64,7 @@ struct gether { struct usb_ep *out_ep; bool is_zlp_ok; + bool no_skb_reserve; u16 cdc_filter; diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index fc2ac150f5ff..0bf39c3ccdb1 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -47,7 +47,7 @@ static char *id = SNDRV_DEFAULT_STR1; module_param(id, charp, S_IRUGO); MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); -static unsigned int buflen = 256; +static unsigned int buflen = 512; module_param(buflen, uint, S_IRUGO); MODULE_PARM_DESC(buflen, "MIDI buffer length"); diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c index 4bc7eea8bfc8..18839732c840 100644 --- a/drivers/usb/gadget/u_f.c +++ b/drivers/usb/gadget/u_f.c @@ -12,14 +12,16 @@ */ #include "u_f.h" +#include <linux/usb/ch9.h> -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) +struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len) { struct usb_request *req; req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req) { - req->length = len ?: default_len; + req->length = usb_endpoint_dir_out(ep->desc) ? + usb_ep_align(ep, len) : len; req->buf = kmalloc(req->length, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request(ep, req); diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h index 4247cc098a89..7d53a4773d1a 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -47,8 +47,21 @@ struct usb_ep; struct usb_request; -/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */ -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len); +/** + * alloc_ep_req - returns a usb_request allocated by the gadget driver and + * allocates the request's buffer. + * + * @ep: the endpoint to allocate a usb_request + * @len: usb_requests's buffer suggested size + * + * In case @ep direction is OUT, the @len will be aligned to ep's + * wMaxPacketSize. In order to avoid memory leaks or drops, *always* use + * usb_requests's length (req->length) to refer to the allocated buffer size. + * Requests allocated via alloc_ep_req() *must* be freed by free_ep_req(). + */ +struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len); + +/* Frees a usb_request previously allocated by alloc_ep_req() */ static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) { kfree(req->buf); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 40c04bb25f2f..9483489080f6 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -107,10 +107,8 @@ int usb_ep_enable(struct usb_ep *ep) goto out; ret = ep->ops->enable(ep, ep->desc); - if (ret) { - ret = ret; + if (ret) goto out; - } ep->enabled = true; diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 1400415fe67a..5107987bd353 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1838,6 +1838,8 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) err: if (dev) goku_remove (pdev); + /* gadget_release is not registered yet, kfree explicitly */ + kfree(dev); return retval; } diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 614ab951a4ae..61c938c36d88 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -589,7 +589,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !_req) { - dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n", + dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n", __func__, _ep, _req); return; } @@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, done(ep, req, status); } -static void scan_dma_completions(struct net2280_ep *ep) +static int scan_dma_completions(struct net2280_ep *ep) { + int num_completed = 0; + /* only look at descriptors that were "naturally" retired, * so fifo and list head state won't matter */ @@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep) break; /* single transfer mode */ dma_done(ep, req, tmp, 0); + num_completed++; break; } else if (!ep->is_in && (req->req.length % ep->ep.maxpacket) && @@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep) } } dma_done(ep, req, tmp, 0); + num_completed++; } + + return num_completed; } static void restart_dma(struct net2280_ep *ep) @@ -1567,6 +1573,44 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget, return ep; } + /* USB3380: Only first four endpoints have DMA channels. Allocate + * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc + * endpoints use DMA hw endpoints. + */ + if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep2in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep4in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + !usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep1out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep3out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && + usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep1in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep3in"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && + !usb_endpoint_dir_in(desc)) { + ep = gadget_find_ep_by_name(_gadget, "ep2out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + ep = gadget_find_ep_by_name(_gadget, "ep4out"); + if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) + return ep; + } + /* USB3380: use same address for usb and hardware endpoints */ snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), usb_endpoint_dir_in(desc) ? "in" : "out"); @@ -2547,8 +2591,11 @@ static void handle_ep_small(struct net2280_ep *ep) /* manual DMA queue advance after short OUT */ if (likely(ep->dma)) { if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - u32 count; + struct net2280_request *stuck_req = NULL; int stopped = ep->stopped; + int num_completed; + int stuck = 0; + u32 count; /* TRANSFERRED works around OUT_DONE erratum 0112. * we expect (N <= maxpacket) bytes; host wrote M. @@ -2560,7 +2607,7 @@ static void handle_ep_small(struct net2280_ep *ep) /* any preceding dma transfers must finish. * dma handles (M >= N), may empty the queue */ - scan_dma_completions(ep); + num_completed = scan_dma_completions(ep); if (unlikely(list_empty(&ep->queue) || ep->out_overflow)) { req = NULL; @@ -2580,6 +2627,31 @@ static void handle_ep_small(struct net2280_ep *ep) req = NULL; break; } + + /* Escape loop if no dma transfers completed + * after few retries. + */ + if (num_completed == 0) { + if (stuck_req == req && + readl(&ep->dma->dmadesc) != + req->td_dma && stuck++ > 5) { + count = readl( + &ep->dma->dmacount); + count &= DMA_BYTE_COUNT_MASK; + req = NULL; + ep_dbg(ep->dev, "%s escape stuck %d, count %u\n", + ep->ep.name, stuck, + count); + break; + } else if (stuck_req != req) { + stuck_req = req; + stuck = 0; + } + } else { + stuck_req = NULL; + stuck = 0; + } + udelay(1); } diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index ad140aa00132..7fa60f5b7ae4 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -33,6 +33,7 @@ #include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/phy.h> #include "pxa27x_udc.h" @@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } +/** + * pxa_udc_phy_event - Called by phy upon VBus event + * @nb: notifier block + * @action: phy action, is vbus connect or disconnect + * @data: the usb_gadget structure in pxa_udc + * + * Called by the USB Phy when a cable connect or disconnect is sensed. + * + * Returns 0 + */ +static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct usb_gadget *gadget = data; + + switch (action) { + case USB_EVENT_VBUS: + usb_gadget_vbus_connect(gadget); + return NOTIFY_OK; + case USB_EVENT_NONE: + usb_gadget_vbus_disconnect(gadget); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block pxa27x_udc_phy = { + .notifier_call = pxa_udc_phy_event, +}; + static int pxa27x_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver); static int pxa27x_udc_stop(struct usb_gadget *g); @@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev) return udc->irq; udc->dev = &pdev->dev; - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (of_have_populated_dt()) { + udc->transceiver = + devm_usb_get_phy_by_phandle(udc->dev, "phys", 0); + if (IS_ERR(udc->transceiver)) + return PTR_ERR(udc->transceiver); + } else { + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + } if (IS_ERR(udc->gpiod)) { dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", @@ -2465,14 +2504,20 @@ static int pxa_udc_probe(struct platform_device *pdev) goto err; } + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_register_notifier(udc->transceiver, &pxa27x_udc_phy); retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (retval) - goto err; + goto err_add_gadget; pxa_init_debugfs(udc); if (should_enable_udc(udc)) udc_enable(udc); return 0; + +err_add_gadget: + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); err: clk_unprepare(udc->clk); return retval; @@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev) usb_del_gadget_udc(&udc->gadget); pxa_cleanup_debugfs(udc); + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); usb_put_phy(udc->transceiver); udc->transceiver = NULL; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2e710a4cca52..0b80cee30da4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -472,7 +472,7 @@ config USB_OHCI_HCD_AT91 config USB_OHCI_HCD_OMAP3 tristate "OHCI support for OMAP3 and later chips" - depends on (ARCH_OMAP3 || ARCH_OMAP4) + depends on (ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5) default y ---help--- Enables support for the on-chip OHCI controller on diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 31102170c7a0..5b5880c0ae19 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -138,7 +138,7 @@ static void at91_stop_hc(struct platform_device *pdev) static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); -struct regmap *at91_dt_syscon_sfr(void) +static struct regmap *at91_dt_syscon_sfr(void) { struct regmap *regmap; diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d39b37be71f0..a59fafb4b329 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1116,6 +1116,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) tegra->hcd); if (!xhci->shared_hcd) { dev_err(&pdev->dev, "failed to create shared HCD\n"); + err = -ENOMEM; goto remove_usb2; } diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c index 947811bc8126..837208f14f86 100644 --- a/drivers/usb/misc/ezusb.c +++ b/drivers/usb/misc/ezusb.c @@ -22,7 +22,7 @@ struct ezusb_fx_type { unsigned short max_internal_adress; }; -static struct ezusb_fx_type ezusb_fx1 = { +static const struct ezusb_fx_type ezusb_fx1 = { .cpucs_reg = 0x7F92, .max_internal_adress = 0x1B3F, }; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 886526b5fcdd..c73221a819c8 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -82,7 +82,7 @@ config USB_MUSB_DA8XX tristate "DA8xx/OMAP-L1x" depends on ARCH_DAVINCI_DA8XX depends on NOP_USB_XCEIV - depends on BROKEN + select PHY_DA8XX_USB config USB_MUSB_TUSB6010 tristate "TUSB6010" diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index c14577dbedf7..50ca8052bc8e 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -510,8 +510,10 @@ static int am35x_probe(struct platform_device *pdev) pdata->platform_ops = &am35x_ops; glue->phy = usb_phy_generic_register(); - if (IS_ERR(glue->phy)) + if (IS_ERR(glue->phy)) { + ret = PTR_ERR(glue->phy); goto err7; + } platform_set_drvdata(pdev, glue); pinfo = am35x_dev_info; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 3c4dd1658f28..2358f636e48c 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -30,13 +30,11 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/usb/usb_phy_generic.h> -#include <mach/da8xx.h> -#include <linux/platform_data/usb-davinci.h> - #include "musb_core.h" /* @@ -80,61 +78,15 @@ #define DA8XX_MENTOR_CORE_OFFSET 0x400 -#define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG) - struct da8xx_glue { struct device *dev; struct platform_device *musb; - struct platform_device *phy; + struct platform_device *usb_phy; struct clk *clk; + struct phy *phy; }; /* - * REVISIT (PM): we should be able to keep the PHY in low power mode most - * of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0 - * and, when in host mode, autosuspending idle root ports... PHY_PLLON - * (overriding SUSPENDM?) then likely needs to stay off. - */ - -static inline void phy_on(void) -{ - u32 cfgchip2 = __raw_readl(CFGCHIP2); - - /* - * Start the on-chip PHY and its PLL. - */ - cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN); - cfgchip2 |= CFGCHIP2_PHY_PLLON; - __raw_writel(cfgchip2, CFGCHIP2); - - pr_info("Waiting for USB PHY clock good...\n"); - while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD)) - cpu_relax(); -} - -static inline void phy_off(void) -{ - u32 cfgchip2 = __raw_readl(CFGCHIP2); - - /* - * Ensure that USB 1.1 reference clock is not being sourced from - * USB 2.0 PHY. Otherwise do not power down the PHY. - */ - if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX) && - (cfgchip2 & CFGCHIP2_USB1SUSPENDM)) { - pr_warning("USB 1.1 clocked from USB 2.0 PHY -- " - "can't power it down\n"); - return; - } - - /* - * Power down the on-chip PHY. - */ - cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN; - __raw_writel(cfgchip2, CFGCHIP2); -} - -/* * Because we don't set CTRL.UINT, it's "important" to: * - not read/write INTRUSB/INTRUSBE (except during * initial setup, as a workaround); @@ -385,29 +337,29 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode) { - u32 cfgchip2 = __raw_readl(CFGCHIP2); + struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent); + enum phy_mode phy_mode; - cfgchip2 &= ~CFGCHIP2_OTGMODE; switch (musb_mode) { case MUSB_HOST: /* Force VBUS valid, ID = 0 */ - cfgchip2 |= CFGCHIP2_FORCE_HOST; + phy_mode = PHY_MODE_USB_HOST; break; case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ - cfgchip2 |= CFGCHIP2_FORCE_DEVICE; + phy_mode = PHY_MODE_USB_DEVICE; break; case MUSB_OTG: /* Don't override the VBUS/ID comparators */ - cfgchip2 |= CFGCHIP2_NO_OVERRIDE; + phy_mode = PHY_MODE_USB_OTG; break; default: - dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode); + return -EINVAL; } - __raw_writel(cfgchip2, CFGCHIP2); - return 0; + return phy_set_mode(glue->phy, phy_mode); } static int da8xx_musb_init(struct musb *musb) { + struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent); void __iomem *reg_base = musb->ctrl_base; u32 rev; int ret = -ENODEV; @@ -425,32 +377,56 @@ static int da8xx_musb_init(struct musb *musb) goto fail; } + ret = clk_prepare_enable(glue->clk); + if (ret) { + dev_err(glue->dev, "failed to enable clock\n"); + goto fail; + } + setup_timer(&otg_workaround, otg_timer, (unsigned long)musb); /* Reset the controller */ musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK); /* Start the on-chip PHY and its PLL. */ - phy_on(); + ret = phy_init(glue->phy); + if (ret) { + dev_err(glue->dev, "Failed to init phy.\n"); + goto err_phy_init; + } + + ret = phy_power_on(glue->phy); + if (ret) { + dev_err(glue->dev, "Failed to power on phy.\n"); + goto err_phy_power_on; + } msleep(5); /* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */ - pr_debug("DA8xx OTG revision %08x, PHY %03x, control %02x\n", - rev, __raw_readl(CFGCHIP2), + pr_debug("DA8xx OTG revision %08x, control %02x\n", rev, musb_readb(reg_base, DA8XX_USB_CTRL_REG)); musb->isr = da8xx_musb_interrupt; return 0; + +err_phy_power_on: + phy_exit(glue->phy); +err_phy_init: + clk_disable_unprepare(glue->clk); fail: return ret; } static int da8xx_musb_exit(struct musb *musb) { + struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent); + del_timer_sync(&otg_workaround); - phy_off(); + phy_power_off(glue->phy); + phy_exit(glue->phy); + clk_disable_unprepare(glue->clk); usb_put_phy(musb->xceiv); @@ -490,24 +466,22 @@ static int da8xx_probe(struct platform_device *pdev) struct da8xx_glue *glue; struct platform_device_info pinfo; struct clk *clk; + int ret; - int ret = -ENOMEM; - - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) - goto err0; + return -ENOMEM; - clk = clk_get(&pdev->dev, "usb20"); + clk = devm_clk_get(&pdev->dev, "usb20"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(clk); - goto err3; + return PTR_ERR(clk); } - ret = clk_enable(clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + glue->phy = devm_phy_get(&pdev->dev, "usb-phy"); + if (IS_ERR(glue->phy)) { + dev_err(&pdev->dev, "failed to get phy\n"); + return PTR_ERR(glue->phy); } glue->dev = &pdev->dev; @@ -515,10 +489,10 @@ static int da8xx_probe(struct platform_device *pdev) pdata->platform_ops = &da8xx_ops; - glue->phy = usb_phy_generic_register(); - if (IS_ERR(glue->phy)) { - ret = PTR_ERR(glue->phy); - goto err5; + glue->usb_phy = usb_phy_generic_register(); + if (IS_ERR(glue->usb_phy)) { + dev_err(&pdev->dev, "failed to register usb_phy\n"); + return PTR_ERR(glue->usb_phy); } platform_set_drvdata(pdev, glue); @@ -544,27 +518,12 @@ static int da8xx_probe(struct platform_device *pdev) glue->musb = musb = platform_device_register_full(&pinfo); if (IS_ERR(musb)) { - ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err6; + usb_phy_generic_unregister(glue->usb_phy); + return PTR_ERR(musb); } return 0; - -err6: - usb_phy_generic_unregister(glue->phy); - -err5: - clk_disable(clk); - -err4: - clk_put(clk); - -err3: - kfree(glue); - -err0: - return ret; } static int da8xx_remove(struct platform_device *pdev) @@ -572,10 +531,7 @@ static int da8xx_remove(struct platform_device *pdev) struct da8xx_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(glue->phy); - clk_disable(glue->clk); - clk_put(glue->clk); - kfree(glue); + usb_phy_generic_unregister(glue->usb_phy); return 0; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 74fc3069cb42..0319ea67e5a1 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1448,7 +1448,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb) { u8 reg; char *type; - char aInfo[90], aRevision[32], aDate[12]; + char aInfo[90]; void __iomem *mbase = musb->mregs; int status = 0; int i; @@ -1482,7 +1482,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb) pr_debug("%s: ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo); - aDate[0] = 0; if (MUSB_CONTROLLER_MHDRC == musb_type) { musb->is_multipoint = 1; type = "M"; @@ -1497,11 +1496,10 @@ static int musb_core_init(u16 musb_type, struct musb *musb) /* log release info */ musb->hwvers = musb_read_hwvers(mbase); - snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers), - MUSB_HWVERS_MINOR(musb->hwvers), - (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); - pr_debug("%s: %sHDRC RTL version %s %s\n", - musb_driver_name, type, aRevision, aDate); + pr_debug("%s: %sHDRC RTL version %d.%d%s\n", + musb_driver_name, type, MUSB_HWVERS_MAJOR(musb->hwvers), + MUSB_HWVERS_MINOR(musb->hwvers), + (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); /* configure ep0 */ musb_configure_ep0(musb); @@ -1831,11 +1829,77 @@ static const struct attribute_group musb_attr_group = { .attrs = musb_attributes, }; +#define MUSB_QUIRK_B_INVALID_VBUS_91 (MUSB_DEVCTL_BDEVICE | \ + (2 << MUSB_DEVCTL_VBUS_SHIFT) | \ + MUSB_DEVCTL_SESSION) +#define MUSB_QUIRK_A_DISCONNECT_19 ((3 << MUSB_DEVCTL_VBUS_SHIFT) | \ + MUSB_DEVCTL_SESSION) + +/* + * Check the musb devctl session bit to determine if we want to + * allow PM runtime for the device. In general, we want to keep things + * active when the session bit is set except after host disconnect. + * + * Only called from musb_irq_work. If this ever needs to get called + * elsewhere, proper locking must be implemented for musb->session. + */ +static void musb_pm_runtime_check_session(struct musb *musb) +{ + u8 devctl, s; + int error; + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + /* Handle session status quirks first */ + s = MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV | + MUSB_DEVCTL_HR; + switch (devctl & ~s) { + case MUSB_QUIRK_B_INVALID_VBUS_91: + if (musb->session) + break; + musb_dbg(musb, "Allow PM as device with invalid vbus: %02x", + devctl); + return; + case MUSB_QUIRK_A_DISCONNECT_19: + if (!musb->session) + break; + musb_dbg(musb, "Allow PM on possible host mode disconnect"); + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); + musb->session = false; + return; + default: + break; + } + + /* No need to do anything if session has not changed */ + s = devctl & MUSB_DEVCTL_SESSION; + if (s == musb->session) + return; + + /* Block PM or allow PM? */ + if (s) { + musb_dbg(musb, "Block PM on active session: %02x", devctl); + error = pm_runtime_get_sync(musb->controller); + if (error < 0) + dev_err(musb->controller, "Could not enable: %i\n", + error); + } else { + musb_dbg(musb, "Allow PM with no session: %02x", devctl); + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); + } + + musb->session = s; +} + /* Only used to provide driver mode change events */ static void musb_irq_work(struct work_struct *data) { struct musb *musb = container_of(data, struct musb, irq_work); + musb_pm_runtime_check_session(musb); + if (musb->xceiv->otg->state != musb->xceiv_old_state) { musb->xceiv_old_state = musb->xceiv->otg->state; sysfs_notify(&musb->controller->kobj, NULL, "mode"); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index b55a776b03eb..65288a53c19b 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -378,6 +378,7 @@ struct musb { u8 min_power; /* vbus for periph, in mA/2 */ int port_mode; /* MUSB_PORT_MODE_* */ + bool session; bool is_host; int a_wait_bcon; /* VBUS timeout in msecs */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 2537179636db..0f17d2140db6 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -145,43 +145,6 @@ static const struct debugfs_reg32 dsps_musb_regs[] = { { "mode", 0xe8 }, }; -static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) -{ - struct device *dev = musb->controller; - struct dsps_glue *glue = dev_get_drvdata(dev->parent); - - if (timeout == 0) - timeout = jiffies + msecs_to_jiffies(3); - - /* Never idle if active, or when VBUS timeout is not set as host */ - if (musb->is_active || (musb->a_wait_bcon == 0 && - musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { - dev_dbg(musb->controller, "%s active, deleting timer\n", - usb_otg_state_string(musb->xceiv->otg->state)); - del_timer(&glue->timer); - glue->last_timer = jiffies; - return; - } - if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) - return; - - if (!musb->g.dev.driver) - return; - - if (time_after(glue->last_timer, timeout) && - timer_pending(&glue->timer)) { - dev_dbg(musb->controller, - "Longer idle timer already pending, ignoring...\n"); - return; - } - glue->last_timer = timeout; - - dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - usb_otg_state_string(musb->xceiv->otg->state), - jiffies_to_msecs(timeout - jiffies)); - mod_timer(&glue->timer, timeout); -} - /** * dsps_musb_enable - enable interrupts */ @@ -206,7 +169,6 @@ static void dsps_musb_enable(struct musb *musb) musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); - dsps_musb_try_idle(musb, 0); } /** @@ -236,6 +198,11 @@ static void otg_timer(unsigned long _musb) u8 devctl; unsigned long flags; int skip_session = 0; + int err; + + err = pm_runtime_get_sync(dev); + if (err < 0) + dev_err(dev, "Poll could not pm_runtime_get: %i\n", err); /* * We poll because DSPS IP's won't expose several OTG-critical @@ -247,6 +214,10 @@ static void otg_timer(unsigned long _musb) spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_VRISE: + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); + break; case OTG_STATE_A_WAIT_BCON: musb_writeb(musb->mregs, MUSB_DEVCTL, 0); skip_session = 1; @@ -275,6 +246,9 @@ static void otg_timer(unsigned long _musb) break; } spin_unlock_irqrestore(&musb->lock, flags); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static irqreturn_t dsps_interrupt(int irq, void *hci) @@ -338,7 +312,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; - del_timer(&glue->timer); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); @@ -358,11 +333,17 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); - /* Poll for ID change in OTG port mode */ - if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && - musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) + /* Poll for ID change and connect */ + switch (musb->xceiv->otg->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_A_WAIT_BCON: mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); + break; + default: + break; + } + out: spin_unlock_irqrestore(&musb->lock, flags); @@ -461,6 +442,9 @@ static int dsps_musb_init(struct musb *musb) musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val); } + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(glue->wrp->poll_timeout)); + return dsps_musb_dbg_init(musb, glue); } @@ -620,7 +604,6 @@ static struct musb_platform_ops dsps_ops = { .enable = dsps_musb_enable, .disable = dsps_musb_disable, - .try_idle = dsps_musb_try_idle, .set_mode = dsps_musb_set_mode, .recover = dsps_musb_recover, }; @@ -784,6 +767,8 @@ static int dsps_probe(struct platform_device *pdev) platform_set_drvdata(pdev, glue); pm_runtime_enable(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 200); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { @@ -795,11 +780,15 @@ static int dsps_probe(struct platform_device *pdev) if (ret) goto err3; + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; err3: - pm_runtime_put(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); err2: + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); return ret; } @@ -811,7 +800,8 @@ static int dsps_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); /* disable usbss clocks */ - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 0b4cec940386..1ab6973d4f61 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -49,9 +49,6 @@ struct omap2430_glue { enum musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; struct device *control_otghs; - bool cable_connected; - bool enabled; - bool powered; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) @@ -141,45 +138,6 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -/* - * We can get multiple cable events so we need to keep track - * of the power state. Only keep power enabled if USB cable is - * connected and a gadget is started. - */ -static void omap2430_set_power(struct musb *musb, bool enabled, bool cable) -{ - struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - bool power_up; - int res; - - if (glue->enabled != enabled) - glue->enabled = enabled; - - if (glue->cable_connected != cable) - glue->cable_connected = cable; - - power_up = glue->enabled && glue->cable_connected; - if (power_up == glue->powered) { - dev_warn(musb->controller, "power state already %i\n", - power_up); - return; - } - - glue->powered = power_up; - - if (power_up) { - res = pm_runtime_get_sync(musb->controller); - if (res < 0) { - dev_err(musb->controller, "could not enable: %i", res); - glue->powered = false; - } - } else { - pm_runtime_mark_last_busy(musb->controller); - pm_runtime_put_autosuspend(musb->controller); - } -} - static int omap2430_musb_mailbox(enum musb_vbus_id_status status) { struct omap2430_glue *glue = _glue; @@ -203,21 +161,15 @@ static int omap2430_musb_mailbox(enum musb_vbus_id_status status) static void omap_musb_set_mailbox(struct omap2430_glue *glue) { struct musb *musb = glue_to_musb(glue); - struct device *dev = musb->controller; - struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); + struct musb_hdrc_platform_data *pdata = + dev_get_platdata(musb->controller); struct omap_musb_board_data *data = pdata->board_data; struct usb_otg *otg = musb->xceiv->otg; - bool cable_connected; - - cable_connected = ((glue->status == MUSB_ID_GROUND) || - (glue->status == MUSB_VBUS_VALID)); - - if (cable_connected) - omap2430_set_power(musb, glue->enabled, cable_connected); + pm_runtime_get_sync(musb->controller); switch (glue->status) { case MUSB_ID_GROUND: - dev_dbg(dev, "ID GND\n"); + dev_dbg(musb->controller, "ID GND\n"); otg->default_a = true; musb->xceiv->otg->state = OTG_STATE_A_IDLE; @@ -230,7 +182,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) break; case MUSB_VBUS_VALID: - dev_dbg(dev, "VBUS Connect\n"); + dev_dbg(musb->controller, "VBUS Connect\n"); otg->default_a = false; musb->xceiv->otg->state = OTG_STATE_B_IDLE; @@ -240,7 +192,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) case MUSB_ID_FLOAT: case MUSB_VBUS_OFF: - dev_dbg(dev, "VBUS Disconnect\n"); + dev_dbg(musb->controller, "VBUS Disconnect\n"); musb->xceiv->last_event = USB_EVENT_NONE; if (musb->gadget_driver) @@ -253,12 +205,10 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) USB_MODE_DISCONNECT); break; default: - dev_dbg(dev, "ID float\n"); + dev_dbg(musb->controller, "ID float\n"); } - - if (!cable_connected) - omap2430_set_power(musb, glue->enabled, cable_connected); - + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); atomic_notifier_call_chain(&musb->xceiv->notifier, musb->xceiv->last_event, NULL); } @@ -376,8 +326,6 @@ static void omap2430_musb_enable(struct musb *musb) if (!WARN_ON(!musb->phy)) phy_power_on(musb->phy); - omap2430_set_power(musb, true, glue->cable_connected); - switch (glue->status) { case MUSB_ID_GROUND: @@ -419,8 +367,6 @@ static void omap2430_musb_disable(struct musb *musb) if (glue->status != MUSB_UNKNOWN) omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DISCONNECT); - - omap2430_set_power(musb, false, glue->cable_connected); } static int omap2430_musb_exit(struct musb *musb) @@ -571,7 +517,7 @@ static int omap2430_probe(struct platform_device *pdev) pm_runtime_enable(glue->dev); pm_runtime_use_autosuspend(glue->dev); - pm_runtime_set_autosuspend_delay(glue->dev, 500); + pm_runtime_set_autosuspend_delay(glue->dev, 100); ret = platform_device_add(musb); if (ret) { @@ -591,11 +537,9 @@ err0: static int omap2430_remove(struct platform_device *pdev) { struct omap2430_glue *glue = platform_get_drvdata(pdev); - struct musb *musb = glue_to_musb(glue); pm_runtime_get_sync(glue->dev); platform_device_unregister(glue->musb); - omap2430_set_power(musb, false, false); pm_runtime_put_sync(glue->dev); pm_runtime_dont_use_autosuspend(glue->dev); pm_runtime_disable(glue->dev); diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index c6ee16660572..1408245be18e 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -74,6 +74,7 @@ #define SUNXI_MUSB_FL_HAS_SRAM 5 #define SUNXI_MUSB_FL_HAS_RESET 6 #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 /* Our read/write methods need access and do not get passed in a musb ref :| */ static struct musb *sunxi_musb; @@ -87,6 +88,7 @@ struct sunxi_glue { struct phy *phy; struct platform_device *usb_phy; struct usb_phy *xceiv; + enum phy_mode phy_mode; unsigned long flags; struct work_struct work; struct extcon_dev *extcon; @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); } } + + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) + phy_set_mode(glue->phy, glue->phy_mode); } static void sunxi_musb_set_vbus(struct musb *musb, int is_on) @@ -341,6 +346,50 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) { } +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) +{ + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); + enum phy_mode new_mode; + + switch (mode) { + case MUSB_HOST: + new_mode = PHY_MODE_USB_HOST; + break; + case MUSB_PERIPHERAL: + new_mode = PHY_MODE_USB_DEVICE; + break; + case MUSB_OTG: + new_mode = PHY_MODE_USB_OTG; + break; + default: + dev_err(musb->controller->parent, + "Error requested mode not supported by this kernel\n"); + return -EINVAL; + } + + if (glue->phy_mode == new_mode) + return 0; + + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { + dev_err(musb->controller->parent, + "Error changing modes is only supported in dual role mode\n"); + return -EINVAL; + } + + if (musb->port1_status & USB_PORT_STAT_ENABLE) + musb_root_disconnect(musb); + + /* + * phy_set_mode may sleep, and we're called with a spinlock held, + * so let sunxi_musb_work deal with it. + */ + glue->phy_mode = new_mode; + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); + schedule_work(&glue->work); + + return 0; +} + /* * sunxi musb register layout * 0x00 - 0x17 fifo regs, 1 long per fifo @@ -568,6 +617,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { .writew = sunxi_musb_writew, .dma_init = sunxi_musb_dma_controller_create, .dma_exit = sunxi_musb_dma_controller_destroy, + .set_mode = sunxi_musb_set_mode, .set_vbus = sunxi_musb_set_vbus, .pre_root_reset_end = sunxi_musb_pre_root_reset_end, .post_root_reset_end = sunxi_musb_post_root_reset_end, @@ -614,21 +664,28 @@ static int sunxi_musb_probe(struct platform_device *pdev) return -EINVAL; } + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + memset(&pdata, 0, sizeof(pdata)); switch (usb_get_dr_mode(&pdev->dev)) { #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST case USB_DR_MODE_HOST: pdata.mode = MUSB_PORT_MODE_HOST; + glue->phy_mode = PHY_MODE_USB_HOST; break; #endif #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET case USB_DR_MODE_PERIPHERAL: pdata.mode = MUSB_PORT_MODE_GADGET; + glue->phy_mode = PHY_MODE_USB_DEVICE; break; #endif #ifdef CONFIG_USB_MUSB_DUAL_ROLE case USB_DR_MODE_OTG: pdata.mode = MUSB_PORT_MODE_DUAL_ROLE; + glue->phy_mode = PHY_MODE_USB_OTG; break; #endif default: @@ -638,10 +695,6 @@ static int sunxi_musb_probe(struct platform_device *pdev) pdata.platform_ops = &sunxi_musb_ops; pdata.config = &sunxi_musb_hdrc_config; - glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); - if (!glue) - return -ENOMEM; - glue->dev = &pdev->dev; INIT_WORK(&glue->work, sunxi_musb_work); glue->host_nb.notifier_call = sunxi_musb_host_notifier; diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0c912d3950a5..a03caf4b1327 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1248,7 +1248,7 @@ static void ab8500_usb_set_ab8500_tuning_values(struct ab8500_usb *ab) err = abx500_set_register_interruptible(ab->dev, AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78); if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n", err); /* Switch to normal mode/disable Bank 0x12 access */ @@ -1290,7 +1290,7 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab) 0xFC, 0x80); if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n", err); /* Switch to normal mode/disable Bank 0x12 access */ @@ -1321,7 +1321,7 @@ static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab) err = abx500_set_register_interruptible(ab->dev, AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90); if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester ret=%d\n", + dev_err(ab->dev, "Failed to set PHY_TUNE3 register ret=%d\n", err); } @@ -1351,7 +1351,7 @@ static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab) err = abx500_set_register_interruptible(ab->dev, AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80); if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n", err); /* Switch to normal mode/disable Bank 0x12 access */ diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 427efb5eebae..8311ba2968cd 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -118,8 +118,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) status = USB_EVENT_VBUS; otg->state = OTG_STATE_B_PERIPHERAL; nop->phy.last_event = status; - if (otg->gadget) - usb_gadget_vbus_connect(otg->gadget); /* drawing a "unit load" is *always* OK, except for OTG */ nop_set_vbus_draw(nop, 100); @@ -129,8 +127,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) } else { nop_set_vbus_draw(nop, 0); - if (otg->gadget) - usb_gadget_vbus_disconnect(otg->gadget); status = USB_EVENT_NONE; otg->state = OTG_STATE_B_IDLE; nop->phy.last_event = status; @@ -191,7 +187,8 @@ static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) otg->gadget = gadget; if (otg->state == OTG_STATE_B_PERIPHERAL) - usb_gadget_vbus_connect(gadget); + atomic_notifier_call_chain(&otg->usb_phy->notifier, + USB_EVENT_VBUS, otg->gadget); else otg->state = OTG_STATE_B_IDLE; return 0; @@ -326,6 +323,8 @@ static int usb_phy_generic_probe(struct platform_device *pdev) gpiod_to_irq(nop->gpiod_vbus), err); return err; } + nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ? + OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE; } nop->phy.init = usb_gen_phy_init; diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 00bfea01be65..0e2f1a36d315 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -27,6 +27,7 @@ #define DRIVER_NAME "mxs_phy" #define HW_USBPHY_PWD 0x00 +#define HW_USBPHY_TX 0x10 #define HW_USBPHY_CTRL 0x30 #define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_CLR 0x38 @@ -38,6 +39,10 @@ #define HW_USBPHY_IP_SET 0x94 #define HW_USBPHY_IP_CLR 0x98 +#define GM_USBPHY_TX_TXCAL45DP(x) (((x) & 0xf) << 16) +#define GM_USBPHY_TX_TXCAL45DN(x) (((x) & 0xf) << 8) +#define GM_USBPHY_TX_D_CAL(x) (((x) & 0xf) << 0) + #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) #define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) @@ -115,6 +120,12 @@ */ #define MXS_PHY_NEED_IP_FIX BIT(3) +/* Minimum and maximum values for device tree entries */ +#define MXS_PHY_TX_CAL45_MIN 30 +#define MXS_PHY_TX_CAL45_MAX 55 +#define MXS_PHY_TX_D_CAL_MIN 79 +#define MXS_PHY_TX_D_CAL_MAX 119 + struct mxs_phy_data { unsigned int flags; }; @@ -164,6 +175,8 @@ struct mxs_phy { const struct mxs_phy_data *data; struct regmap *regmap_anatop; int port_id; + u32 tx_reg_set; + u32 tx_reg_mask; }; static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) @@ -185,6 +198,20 @@ static void mxs_phy_clock_switch_delay(void) usleep_range(300, 400); } +static void mxs_phy_tx_init(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 phytx; + + /* Update TX register if there is anything to write */ + if (mxs_phy->tx_reg_mask) { + phytx = readl(base + HW_USBPHY_TX); + phytx &= ~mxs_phy->tx_reg_mask; + phytx |= mxs_phy->tx_reg_set; + writel(phytx, base + HW_USBPHY_TX); + } +} + static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) { int ret; @@ -214,6 +241,8 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); + mxs_phy_tx_init(mxs_phy); + return 0; } @@ -459,6 +488,7 @@ static int mxs_phy_probe(struct platform_device *pdev) int ret; const struct of_device_id *of_id; struct device_node *np = pdev->dev.of_node; + u32 val; of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev); if (!of_id) @@ -491,6 +521,37 @@ static int mxs_phy_probe(struct platform_device *pdev) } } + /* Precompute which bits of the TX register are to be updated, if any */ + if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) && + val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) { + /* Scale to a 4-bit value */ + val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF + / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN); + mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DN(~0); + mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DN(val); + } + + if (!of_property_read_u32(np, "fsl,tx-cal-45-dp-ohms", &val) && + val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) { + /* Scale to a 4-bit value. */ + val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF + / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN); + mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DP(~0); + mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DP(val); + } + + if (!of_property_read_u32(np, "fsl,tx-d-cal", &val) && + val >= MXS_PHY_TX_D_CAL_MIN && val <= MXS_PHY_TX_D_CAL_MAX) { + /* Scale to a 4-bit value. Round up the values and heavily + * weight the rounding by adding 2/3 of the denominator. + */ + val = ((MXS_PHY_TX_D_CAL_MAX - val) * 0xF + + (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN) * 2/3) + / (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN); + mxs_phy->tx_reg_mask |= GM_USBPHY_TX_D_CAL(~0); + mxs_phy->tx_reg_set |= GM_USBPHY_TX_D_CAL(val); + } + ret = of_alias_get_id(np, "usbphy"); if (ret < 0) dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index ac67bab9124c..012a37aa3e0d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -482,6 +482,10 @@ static const struct of_device_id usbhs_of_match[] = { .data = (void *)USBHS_TYPE_RCAR_GEN3, }, { + .compatible = "renesas,usbhs-r8a7796", + .data = (void *)USBHS_TYPE_RCAR_GEN3, + }, + { .compatible = "renesas,rcar-gen2-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN2, }, diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 86b37a8eedd4..5bc7a6138855 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -1102,6 +1102,8 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; + gpriv->gadget.quirk_avoids_skb_reserve = usbhs_get_dparam(priv, + has_usb_dmac); INIT_LIST_HEAD(&gpriv->gadget.ep_list); diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c index da1b872918b5..fb70cbef0671 100644 --- a/drivers/usb/wusbcore/cbaf.c +++ b/drivers/usb/wusbcore/cbaf.c @@ -610,8 +610,7 @@ static int cbaf_probe(struct usb_interface *iface, cbaf->usb_iface = usb_get_intf(iface); result = cbaf_check(cbaf); if (result < 0) { - dev_err(dev, "This device is not WUSB-CBAF compliant" - "and is not supported yet.\n"); + dev_err(dev, "This device is not WUSB-CBAF compliant and is not supported yet.\n"); goto error_check; } |