--- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -202,6 +202,7 @@ config USB_RCAR_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 + depends on USB_PHY select USB_ULPI_VIEWPORT help Enable this to support ULPI connected USB OTG transceivers which --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -26,9 +26,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include +#include struct ulpi_info { @@ -52,6 +59,13 @@ static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), }; +struct ulpi_phy { + struct usb_phy *usb_phy; + void __iomem *regs; + unsigned int vp_offset; + unsigned int flags; +}; + static int ulpi_set_otg_flags(struct usb_phy *phy) { unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | @@ -253,6 +267,23 @@ static int ulpi_set_vbus(struct usb_otg return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); } +static int usbphy_set_vbus(struct usb_phy *phy, int on) +{ + unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); + + flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); + + if (on) { + if (phy->flags & ULPI_OTG_DRVVBUS) + flags |= ULPI_OTG_CTRL_DRVVBUS; + + if (phy->flags & ULPI_OTG_DRVVBUS_EXT) + flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; + } + + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); +} + struct usb_phy * otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags) @@ -275,6 +306,7 @@ otg_ulpi_create(struct usb_phy_io_ops *o phy->io_ops = ops; phy->otg = otg; phy->init = ulpi_init; + phy->set_vbus = usbphy_set_vbus; otg->usb_phy = phy; otg->set_host = ulpi_set_host; @@ -284,3 +316,70 @@ otg_ulpi_create(struct usb_phy_io_ops *o } EXPORT_SYMBOL_GPL(otg_ulpi_create); +static int ulpi_phy_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct ulpi_phy *uphy; + bool flag; + int ret; + + uphy = devm_kzalloc(&pdev->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + uphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(uphy->regs)) + return PTR_ERR(uphy->regs); + + ret = of_property_read_u32(np, "view-port", &uphy->vp_offset); + if (IS_ERR(uphy->regs)) { + dev_err(&pdev->dev, "view-port register not specified\n"); + return PTR_ERR(uphy->regs); + } + + flag = of_property_read_bool(np, "drv-vbus"); + if (flag) + uphy->flags |= ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT; + + uphy->usb_phy = otg_ulpi_create(&ulpi_viewport_access_ops, uphy->flags); + + uphy->usb_phy->dev = &pdev->dev; + + uphy->usb_phy->io_priv = uphy->regs + uphy->vp_offset; + + ret = usb_add_phy_dev(uphy->usb_phy); + if (ret < 0) + return ret; + + return 0; +} + +static int ulpi_phy_remove(struct platform_device *pdev) +{ + struct ulpi_phy *uphy = platform_get_drvdata(pdev); + + usb_remove_phy(uphy->usb_phy); + + return 0; +} + +static const struct of_device_id ulpi_phy_table[] = { + { .compatible = "ulpi-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ulpi_phy_table); + +static struct platform_driver ulpi_phy_driver = { + .probe = ulpi_phy_probe, + .remove = ulpi_phy_remove, + .driver = { + .name = "ulpi-phy", + .of_match_table = ulpi_phy_table, + }, +}; +module_platform_driver(ulpi_phy_driver); + +MODULE_DESCRIPTION("ULPI PHY driver"); +MODULE_LICENSE("GPL v2");