diff --git a/arch/arm/configs/imx_defconfig b/arch/arm/configs/imx_defconfig index 69ab021..7d061cd 100644 --- a/arch/arm/configs/imx_defconfig +++ b/arch/arm/configs/imx_defconfig @@ -107,6 +107,8 @@ CONFIG_WATCHDOG=y CONFIG_WATCHDOG_IMX=y CONFIG_IMX_WEIM=y +CONFIG_GENERIC_PHY=y +CONFIG_USB_NOP_XCEIV=y CONFIG_FS_EXT4=y CONFIG_FS_TFTP=y CONFIG_FS_NFS=y diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig index 51cbf60..50106df 100644 --- a/arch/arm/configs/imx_v7_defconfig +++ b/arch/arm/configs/imx_v7_defconfig @@ -179,6 +179,8 @@ CONFIG_GPIO_STMPE=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED=y +CONFIG_GENERIC_PHY=y +CONFIG_USB_NOP_XCEIV=y CONFIG_FS_EXT4=y CONFIG_FS_TFTP=y CONFIG_FS_NFS=y diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e9461e1..b0c8b9b 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -2,9 +2,7 @@ # PHY # -menu "PHY Subsystem" - -config GENERIC_PHY +menuconfig GENERIC_PHY bool "PHY Core" help Generic PHY support. @@ -15,4 +13,13 @@ phy users can obtain reference to the PHY. All the users of this framework should select this config. -endmenu +if GENERIC_PHY + +config USB_NOP_XCEIV + bool "Generic USB nop phy" + help + This driver is to be used by all the usb transceiver which are either + built-in with usb ip or which are autonomous and doesn't require any + phy programming such as ISP1x04 etc. + +endif diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 74514ae..8fc8595 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 67af14f..1b6a9f7 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -15,6 +15,7 @@ #include #include #include +#include static LIST_HEAD(phy_provider_list); static int phy_ida; @@ -201,6 +202,17 @@ return 0; } +struct usb_phy *phy_to_usbphy(struct phy *phy) +{ + if (!phy) + return NULL; + + if (!phy->ops->to_usbphy) + return ERR_PTR(-EINVAL); + + return phy->ops->to_usbphy(phy); +} + static struct phy_provider *of_phy_provider_lookup(struct device_node *node) { struct phy_provider *phy_provider; @@ -268,6 +280,42 @@ } /** + * of_phy_get_by_phandle() - lookup and obtain a reference to a phy. + * @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, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling phy_put() to release that count. + */ +struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle, + u8 index) +{ + struct device_node *np; + struct phy_provider *phy_provider; + + if (!dev->device_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + np = of_parse_phandle(dev->device_node, phandle, index); + if (!np) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->device_node->full_name); + return ERR_PTR(-ENODEV); + } + + phy_provider = of_phy_provider_lookup(np); + if (IS_ERR(phy_provider)) { + return ERR_PTR(-ENODEV); + } + + return phy_provider->of_xlate(phy_provider->dev, NULL); +} + +/** * phy_get() - lookup and obtain a reference to a phy. * @dev: device that requests this phy * @string: the phy name as given in the dt data or the name of the controller diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c new file mode 100644 index 0000000..606e098 --- /dev/null +++ b/drivers/phy/usb-nop-xceiv.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Sascha Hauer + * + * 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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nop_usbphy { + struct usb_phy usb_phy; + struct phy *phy; + struct phy_provider *provider; +}; + +static struct phy *nop_usbphy_xlate(struct device_d *dev, + struct of_phandle_args *args) +{ + struct nop_usbphy *nopphy = dev->priv; + + return nopphy->phy; +} + +static struct usb_phy *nop_usbphy_to_usbphy(struct phy *phy) +{ + struct nop_usbphy *nopphy = phy_get_drvdata(phy); + + return &nopphy->usb_phy; +} + +static const struct phy_ops nop_phy_ops = { + .to_usbphy = nop_usbphy_to_usbphy, +}; + +static int nop_usbphy_probe(struct device_d *dev) +{ + int ret; + struct nop_usbphy *nopphy; + + nopphy = xzalloc(sizeof(*nopphy)); + + dev->priv = nopphy; + + /* FIXME: Add clk support */ + /* FIXME: Add vbus regulator support */ + /* FIXME: Add vbus-detect-gpio support */ + + nopphy->usb_phy.dev = dev; + nopphy->phy = phy_create(dev, NULL, &nop_phy_ops, NULL); + if (IS_ERR(nopphy->phy)) { + ret = PTR_ERR(nopphy->phy); + goto err_free; + } + + phy_set_drvdata(nopphy->phy, nopphy); + + nopphy->provider = of_phy_provider_register(dev, nop_usbphy_xlate); + if (IS_ERR(nopphy->provider)) { + ret = PTR_ERR(nopphy->provider); + goto err_free; + } + + return 0; +err_free: + free(nopphy); + + return ret; +}; + +static __maybe_unused struct of_device_id nop_usbphy_dt_ids[] = { + { + .compatible = "usb-nop-xceiv", + }, { + /* sentinel */ + }, +}; + +static struct driver_d nop_usbphy_driver = { + .name = "usb-nop-xceiv", + .probe = nop_usbphy_probe, + .of_compatible = DRV_OF_COMPAT(nop_usbphy_dt_ids), +}; + +static int nop_usbphy_init(void) +{ + return platform_driver_register(&nop_usbphy_driver); +} +fs_initcall(nop_usbphy_init); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index dd3c10e..f44aea5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -189,6 +190,10 @@ if (dev->children[port] && !(portstatus & USB_PORT_STAT_CONNECTION)) { dev_dbg(&dev->dev, "disconnect detected on port %d\n", port + 1); usb_remove_device(dev->children[port]); + + if (!dev->parent && dev->host->usbphy) + usb_phy_notify_disconnect(dev->host->usbphy, dev->speed); + return; } @@ -231,6 +236,9 @@ return; } + if (!dev->parent && dev->host->usbphy) + usb_phy_notify_connect(dev->host->usbphy, usb->speed); + device_detect(&usb->dev); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ce229f2..aba2da0 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -40,6 +40,9 @@ * * For each transfer (except "Interrupt") we wait for completion. */ + +#define pr_fmt(fmt) "usb: " fmt + #include #include #include @@ -56,14 +59,6 @@ #include "usb.h" #include "hub.h" -/* #define USB_DEBUG */ - -#ifdef USB_DEBUG -#define USB_PRINTF(fmt, args...) printf(fmt , ##args) -#else -#define USB_PRINTF(fmt, args...) -#endif - #define USB_BUFSIZ 512 static int dev_count; @@ -113,7 +108,9 @@ static int usb_set_configuration(struct usb_device *dev, int configuration) { int res; - USB_PRINTF("set configuration %d\n", configuration); + + pr_debug("set configuration %d\n", configuration); + /* set setup command */ res = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, @@ -147,21 +144,21 @@ /* Control => bidirectional */ dev->epmaxpacketout[b] = ep->wMaxPacketSize; dev->epmaxpacketin[b] = ep->wMaxPacketSize; - USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n", + pr_debug("##Control EP epmaxpacketout/in[%d] = %d\n", b, dev->epmaxpacketin[b]); } else { if ((ep->bEndpointAddress & 0x80) == 0) { /* OUT Endpoint */ if (ep->wMaxPacketSize > dev->epmaxpacketout[b]) { dev->epmaxpacketout[b] = ep->wMaxPacketSize; - USB_PRINTF("##EP epmaxpacketout[%d] = %d\n", + pr_debug("##EP epmaxpacketout[%d] = %d\n", b, dev->epmaxpacketout[b]); } } else { /* IN Endpoint */ if (ep->wMaxPacketSize > dev->epmaxpacketin[b]) { dev->epmaxpacketin[b] = ep->wMaxPacketSize; - USB_PRINTF("##EP epmaxpacketin[%d] = %d\n", + pr_debug("##EP epmaxpacketin[%d] = %d\n", b, dev->epmaxpacketin[b]); } } /* if out */ @@ -250,20 +247,20 @@ &buffer[index], buffer[index]); le16_to_cpus(&(dev->config.interface[ifno].ep_desc[epno].\ wMaxPacketSize)); - USB_PRINTF("if %d, ep %d\n", ifno, epno); + pr_debug("if %d, ep %d\n", ifno, epno); break; default: if (head->bLength == 0) return 1; - USB_PRINTF("unknown Description Type : %x\n", + pr_debug("unknown Description Type : %x\n", head->bDescriptorType); { ch = (unsigned char *)head; for (i = 0; i < head->bLength; i++) - USB_PRINTF("%02X ", *ch++); - USB_PRINTF("\n\n\n"); + pr_debug("%02X ", *ch++); + pr_debug("\n\n\n"); } break; } @@ -281,7 +278,8 @@ { int res; - USB_PRINTF("set address %d\n", dev->devnum); + pr_debug("set address %d\n", dev->devnum); + res = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, 0, (dev->devnum), 0, @@ -345,7 +343,7 @@ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); if (err < 0) { - USB_PRINTF("%s: usb_get_descriptor() failed with %d\n", __func__, err); + pr_debug("%s: usb_get_descriptor() failed with %d\n", __func__, err); goto err_out; } @@ -417,7 +415,7 @@ "len %d, status %lX\n", dev->act_len, dev->status); goto err_out; } - USB_PRINTF("new device: Mfr=%d, Product=%d, SerialNumber=%d\n", + pr_debug("new device: Mfr=%d, Product=%d, SerialNumber=%d\n", dev->descriptor->iManufacturer, dev->descriptor->iProduct, dev->descriptor->iSerialNumber); memset(dev->mf, 0, sizeof(dev->mf)); @@ -624,7 +622,7 @@ setup_packet->value = cpu_to_le16(value); setup_packet->index = cpu_to_le16(index); setup_packet->length = cpu_to_le16(size); - USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ + pr_debug("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \ "value 0x%X index 0x%X length 0x%X\n", request, requesttype, value, index, size); dev->status = USB_ST_NOT_PROC; /*not yet processed */ @@ -744,13 +742,13 @@ tmp = le16_to_cpu(config->wTotalLength); if (tmp > USB_BUFSIZ) { - USB_PRINTF("usb_get_configuration_no: failed to get " \ + pr_debug("usb_get_configuration_no: failed to get " \ "descriptor - too long: %u\n", tmp); return -1; } result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp); - USB_PRINTF("get_conf_no %d Result %d, wLength %u\n", + pr_debug("get_conf_no %d Result %d, wLength %u\n", cfgno, result, tmp); return result; } @@ -931,17 +929,17 @@ if (!dev->have_langid) { err = usb_string_sub(dev, 0, 0, tbuf); if (err < 0) { - USB_PRINTF("error getting string descriptor 0 " \ + pr_debug("error getting string descriptor 0 " \ "(error=%lx)\n", dev->status); return -1; } else if (tbuf[0] < 4) { - USB_PRINTF("string descriptor 0 too short\n"); + pr_debug("string descriptor 0 too short\n"); return -1; } else { dev->have_langid = -1; dev->string_langid = tbuf[2] | (tbuf[3] << 8); /* always use the first langid listed */ - USB_PRINTF("USB device number %d default " \ + pr_debug("USB device number %d default " \ "language ID 0x%x\n", dev->devnum, dev->string_langid); } @@ -1063,12 +1061,12 @@ struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); const struct usb_device_id *id; - debug("matching: 0x%04x 0x%04x\n", usbdev->descriptor->idVendor, + pr_debug("matching: 0x%04x 0x%04x\n", usbdev->descriptor->idVendor, usbdev->descriptor->idProduct); id = usb_match_id(usbdev, usbdrv->id_table); if (id) { - debug("match: 0x%04x 0x%04x\n", id->idVendor, id->idProduct); + pr_debug("match: 0x%04x 0x%04x\n", id->idVendor, id->idProduct); return 0; } return 1; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 35cf6aa..f6e9099 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1297,6 +1297,7 @@ host->hw_dev = dev; host->init = ehci_init; + host->usbphy = data->usbphy; host->submit_int_msg = submit_int_msg; host->submit_control_msg = submit_control_msg; host->submit_bulk_msg = submit_bulk_msg; diff --git a/drivers/usb/imx/Kconfig b/drivers/usb/imx/Kconfig index b1ce682..b0c6a41 100644 --- a/drivers/usb/imx/Kconfig +++ b/drivers/usb/imx/Kconfig @@ -16,5 +16,4 @@ config USB_IMX_PHY bool - depends on ARCH_IMX - default y if ARCH_IMX6 + default y if ARCH_IMX6 && GENERIC_PHY diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index a6f5926..ed00ff4 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -26,6 +26,7 @@ #include #include #include +#include #define MXC_EHCI_PORTSC_MASK ((0xf << 28) | (1 << 25)) @@ -40,6 +41,8 @@ struct param_d *param_mode; int role_registered; struct regulator *vbus; + struct phy *phy; + struct usb_phy *usbphy; }; static int imx_chipidea_port_init(void *drvdata) @@ -260,6 +263,23 @@ if (IS_ERR(ci->vbus)) ci->vbus = NULL; + if (of_property_read_bool(dev->device_node, "fsl,usbphy")) { + ci->phy = of_phy_get_by_phandle(dev, "fsl,usbphy", 0); + if (IS_ERR(ci->phy)) { + ret = PTR_ERR(ci->phy); + dev_err(dev, "Cannot get phy: %s\n", strerror(-ret)); + return ret; + } else { + ci->usbphy = phy_to_usbphy(ci->phy); + if (IS_ERR(ci->usbphy)) + return PTR_ERR(ci->usbphy); + + ret = phy_init(ci->phy); + if (ret) + return ret; + } + } + iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -270,6 +290,7 @@ ci->data.init = imx_chipidea_port_init; ci->data.post_init = imx_chipidea_port_post_init; ci->data.drvdata = ci; + ci->data.usbphy = ci->usbphy; if ((ci->flags & MXC_EHCI_PORTSC_MASK) == MXC_EHCI_MODE_HSIC) imx_chipidea_port_init(ci); diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c index 1aa12be..9f46f8d 100644 --- a/drivers/usb/imx/imx-usb-phy.c +++ b/drivers/usb/imx/imx-usb-phy.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -31,15 +33,19 @@ #define USBPHY_CTRL_CLKGATE (1 << 30) #define USBPHY_CTRL_ENUTMILEVEL3 (1 << 15) #define USBPHY_CTRL_ENUTMILEVEL2 (1 << 14) +#define USBPHY_CTRL_ENHOSTDISCONDETECT (1 << 1) struct imx_usbphy { + struct usb_phy usb_phy; + struct phy *phy; void __iomem *base; struct clk *clk; + struct phy_provider *provider; }; -static int imx_usbphy_enable(struct imx_usbphy *imxphy) +static int imx_usbphy_phy_init(struct phy *phy) { - u32 val; + struct imx_usbphy *imxphy = phy_get_drvdata(phy); clk_enable(imxphy->clk); @@ -56,13 +62,56 @@ writel(0xffffffff, imxphy->base + CLR); /* set utmilvl2/3 */ - val = readl(imxphy->base + USBPHY_CTRL); - val |= USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2; - writel(val, imxphy->base + USBPHY_CTRL + SET); + writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2, + imxphy->base + USBPHY_CTRL + SET); return 0; } +static int imx_usbphy_notify_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct imx_usbphy *imxphy = container_of(phy, struct imx_usbphy, usb_phy); + + if (speed == USB_SPEED_HIGH) { + writel(USBPHY_CTRL_ENHOSTDISCONDETECT, + imxphy->base + USBPHY_CTRL + SET); + } + + return 0; +} + +static int imx_usbphy_notify_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct imx_usbphy *imxphy = container_of(phy, struct imx_usbphy, usb_phy); + + writel(USBPHY_CTRL_ENHOSTDISCONDETECT, + imxphy->base + USBPHY_CTRL + CLR); + + return 0; +} + +static struct phy *imx_usbphy_xlate(struct device_d *dev, + struct of_phandle_args *args) +{ + struct imx_usbphy *imxphy = dev->priv; + + return imxphy->phy; +} + +static struct usb_phy *imx_usbphy_to_usbphy(struct phy *phy) +{ + struct imx_usbphy *imxphy = phy_get_drvdata(phy); + + return &imxphy->usb_phy; +} + +static const struct phy_ops imx_phy_ops = { + .init = imx_usbphy_phy_init, + .to_usbphy = imx_usbphy_to_usbphy, +}; + static int imx_usbphy_probe(struct device_d *dev) { struct resource *iores; @@ -85,7 +134,24 @@ goto err_clk; } - imx_usbphy_enable(imxphy); + dev->priv = imxphy; + + imxphy->usb_phy.dev = dev; + imxphy->usb_phy.notify_connect = imx_usbphy_notify_connect; + imxphy->usb_phy.notify_disconnect = imx_usbphy_notify_disconnect; + imxphy->phy = phy_create(dev, NULL, &imx_phy_ops, NULL); + if (IS_ERR(imxphy->phy)) { + ret = PTR_ERR(imxphy->phy); + goto err_clk; + } + + phy_set_drvdata(imxphy->phy, imxphy); + + imxphy->provider = of_phy_provider_register(dev, imx_usbphy_xlate); + if (IS_ERR(imxphy->provider)) { + ret = PTR_ERR(imxphy->provider); + goto err_clk; + } dev_dbg(dev, "phy enabled\n"); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 94f0044..5d96e02 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -33,6 +33,7 @@ int (*exit)(struct phy *phy); int (*power_on)(struct phy *phy); int (*power_off)(struct phy *phy); + struct usb_phy *(*to_usbphy)(struct phy *phy); }; /** @@ -136,6 +137,8 @@ } struct phy *phy_get(struct device_d *dev, const char *string); struct phy *phy_optional_get(struct device_d *dev, const char *string); +struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle, + u8 index); void phy_put(struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); struct phy *of_phy_simple_xlate(struct device_d *dev, @@ -148,6 +151,7 @@ struct phy * (*of_xlate)(struct device_d *dev, struct of_phandle_args *args)); void of_phy_provider_unregister(struct phy_provider *phy_provider); +struct usb_phy *phy_to_usbphy(struct phy *phy); #else static inline int phy_init(struct phy *phy) { @@ -198,6 +202,12 @@ return ERR_PTR(-ENOSYS); } +static inline struct phy *of_phy_get_by_phandle(struct device_d *dev, + const char *phandle, u8 index) +{ + return ERR_PTR(-ENOSYS); +} + static inline void phy_put(struct phy *phy) { } @@ -235,6 +245,12 @@ static inline void of_phy_provider_unregister(struct phy_provider *phy_provider) { } + +static inline struct usb_phy *phy_to_usbphy(struct phy *phy) +{ + return NULL; +} + #endif #endif /* __DRIVERS_PHY_H */ diff --git a/include/usb/ehci.h b/include/usb/ehci.h index 93f980d..1008e92 100644 --- a/include/usb/ehci.h +++ b/include/usb/ehci.h @@ -11,6 +11,7 @@ void __iomem *hccr; void __iomem *hcor; unsigned long flags; + struct usb_phy *usbphy; /* platform specific init functions */ int (*init)(void *drvdata); diff --git a/include/usb/usb.h b/include/usb/usb.h index aedc527..93308ce 100644 --- a/include/usb/usb.h +++ b/include/usb/usb.h @@ -153,6 +153,7 @@ int busnum; struct usb_device *root_dev; int sem; + struct usb_phy *usbphy; }; int usb_register_host(struct usb_host *);