diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-02 04:47:03 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-02 04:47:03 +0300 |
commit | e152813ff5fea74094ae296367f477411df1ba24 (patch) | |
tree | 6e8c38a3b83f0f07f75df50793332aa47a81c709 /drivers/usb/phy | |
parent | 74db22cb3a16dcd782a31236eb139f5865804ae6 (diff) | |
parent | e18b7975c885bc3a938b9a76daf32957ea0235fa (diff) | |
download | linux-e152813ff5fea74094ae296367f477411df1ba24.tar.xz |
Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
usb: patches for v4.2 merge window
- dwc2 adds hibernation support
- preparation for sunxi glue to musb driver
- new ULPI bus
- new ULPI PHY driver for TUSB1210
- musb patches to support multiple DMA engines on same binary
- support for R-Car E2 on renesas_usbhs
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r-- | drivers/usb/phy/Kconfig | 3 | ||||
-rw-r--r-- | drivers/usb/phy/phy-msm-usb.c | 110 | ||||
-rw-r--r-- | drivers/usb/phy/phy.c | 97 |
3 files changed, 182 insertions, 28 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3cd3bee54ca6..869c0cfcad98 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -91,7 +91,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an @@ -141,6 +141,7 @@ config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER + depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c9156beeadef..00c49bb1bd29 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg) static int msm_phy_notify_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int val; + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; + usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); + } + /* * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. @@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; + ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); + + val = readl(USB_GENCONFIG_2); + val |= GENCONFIG_2_SESS_VLD_CTRL_EN; + writel(val, USB_GENCONFIG_2); + + val = readl(USB_USBCMD); + val |= USBCMD_SESS_VLD_CTRL; + writel(val, USB_USBCMD); + + val = ulpi_read(phy, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, val, ULPI_FUNC_CTRL); + } + if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); @@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_otg_dt_match); +static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); + + if (event) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + +static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(id, struct msm_otg, id); + + if (event) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; + struct extcon_dev *ext_id, *ext_vbus; const struct of_device_id *id; struct device_node *node = pdev->dev.of_node; struct property *prop; @@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); + + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(node, "extcon")) { + + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; + ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, + "USB", &motg->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } + + if (!IS_ERR(ext_id)) { + motg->id.nb.notifier_call = msm_otg_id_notifier; + ret = extcon_register_interest(&motg->id.conn, ext_id->name, + "USB-HOST", &motg->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; @@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev) if (phy->otg->host || phy->otg->gadget) return -EBUSY; + if (motg->id.conn.edev) + extcon_unregister_interest(&motg->id.conn); + if (motg->vbus.conn.edev) + extcon_unregister_interest(&motg->vbus.conn); + msm_otg_debugfs_cleanup(); cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index d1cd6b50f520..98f75d2842b7 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -22,6 +22,11 @@ static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); +struct phy_devm { + struct usb_phy *phy; + struct notifier_block *nb; +}; + static struct usb_phy *__usb_find_phy(struct list_head *list, enum usb_phy_type type) { @@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) usb_put_phy(phy); } +static void devm_usb_phy_release2(struct device *dev, void *_res) +{ + struct phy_devm *res = _res; + + if (res->nb) + usb_unregister_notifier(res->phy, res->nb); + usb_put_phy(res->phy); +} + static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { struct usb_phy **phy = res; @@ -153,40 +167,30 @@ err0: EXPORT_SYMBOL_GPL(usb_get_phy); /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * devm_usb_get_phy_by_node - find the USB PHY by device_node * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy + * @node - the device_node for the phy device. + * @nb - a notifier_block to register with the phy. * - * Returns the phy driver associated with the given phandle value, + * Returns the phy driver associated with the given device_node, * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with + * -EPROBE_DEFER if the device is not yet loaded. While at that, it + * also associates the device with * the phy using devres. On driver detach, release function is invoked * on the devres data, then, devres data is freed. * - * For use by USB host and peripheral drivers. + * For use by peripheral drivers for devices related to a phy, + * such as a charger. */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) +struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, + struct notifier_block *nb) { - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + struct usb_phy *phy = ERR_PTR(-ENOMEM); + struct phy_devm *ptr; unsigned long flags; - struct device_node *node; - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); if (!ptr) { dev_dbg(dev, "failed to allocate memory for devres\n"); goto err0; @@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, devres_free(ptr); goto err1; } - - *ptr = phy; + if (nb) + usb_register_notifier(phy, nb); + ptr->phy = phy; + ptr->nb = nb; devres_add(dev, ptr); get_device(phy->dev); @@ -215,10 +221,47 @@ err1: spin_unlock_irqrestore(&phy_lock, flags); err0: - of_node_put(node); return phy; } +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); + +/** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct device_node *node; + struct usb_phy *phy; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + phy = devm_usb_get_phy_by_node(dev, node, NULL); + of_node_put(node); + return phy; +} EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** |