13bb869c8SSebastian Andrzej Siewior #include <linux/module.h> 23bb869c8SSebastian Andrzej Siewior #include <linux/platform_device.h> 38b841cb2SSebastian Andrzej Siewior #include <linux/err.h> 43bb869c8SSebastian Andrzej Siewior #include <linux/of.h> 53bb869c8SSebastian Andrzej Siewior #include <linux/io.h> 6a31a942aSDaniel Mack #include <linux/delay.h> 759f042f6SBin Liu #include <linux/usb/otg.h> 85306661eSBin Liu #include "phy-am335x-control.h" 93bb869c8SSebastian Andrzej Siewior 103bb869c8SSebastian Andrzej Siewior struct am335x_control_usb { 113bb869c8SSebastian Andrzej Siewior struct device *dev; 123bb869c8SSebastian Andrzej Siewior void __iomem *phy_reg; 133bb869c8SSebastian Andrzej Siewior void __iomem *wkup; 143bb869c8SSebastian Andrzej Siewior spinlock_t lock; 153bb869c8SSebastian Andrzej Siewior struct phy_control phy_ctrl; 163bb869c8SSebastian Andrzej Siewior }; 173bb869c8SSebastian Andrzej Siewior 183bb869c8SSebastian Andrzej Siewior #define AM335X_USB0_CTRL 0x0 193bb869c8SSebastian Andrzej Siewior #define AM335X_USB1_CTRL 0x8 203bb869c8SSebastian Andrzej Siewior #define AM335x_USB_WKUP 0x0 213bb869c8SSebastian Andrzej Siewior 223bb869c8SSebastian Andrzej Siewior #define USBPHY_CM_PWRDN (1 << 0) 233bb869c8SSebastian Andrzej Siewior #define USBPHY_OTG_PWRDN (1 << 1) 243bb869c8SSebastian Andrzej Siewior #define USBPHY_OTGVDET_EN (1 << 19) 253bb869c8SSebastian Andrzej Siewior #define USBPHY_OTGSESSEND_EN (1 << 20) 263bb869c8SSebastian Andrzej Siewior 27a1222639SSebastian Andrzej Siewior #define AM335X_PHY0_WK_EN (1 << 0) 28a1222639SSebastian Andrzej Siewior #define AM335X_PHY1_WK_EN (1 << 8) 29a1222639SSebastian Andrzej Siewior 30a1222639SSebastian Andrzej Siewior static void am335x_phy_wkup(struct phy_control *phy_ctrl, u32 id, bool on) 31a1222639SSebastian Andrzej Siewior { 32a1222639SSebastian Andrzej Siewior struct am335x_control_usb *usb_ctrl; 33a1222639SSebastian Andrzej Siewior u32 val; 34a1222639SSebastian Andrzej Siewior u32 reg; 35a1222639SSebastian Andrzej Siewior 36a1222639SSebastian Andrzej Siewior usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); 37a1222639SSebastian Andrzej Siewior 38a1222639SSebastian Andrzej Siewior switch (id) { 39a1222639SSebastian Andrzej Siewior case 0: 40a1222639SSebastian Andrzej Siewior reg = AM335X_PHY0_WK_EN; 41a1222639SSebastian Andrzej Siewior break; 42a1222639SSebastian Andrzej Siewior case 1: 43a1222639SSebastian Andrzej Siewior reg = AM335X_PHY1_WK_EN; 44a1222639SSebastian Andrzej Siewior break; 45a1222639SSebastian Andrzej Siewior default: 46a1222639SSebastian Andrzej Siewior WARN_ON(1); 47a1222639SSebastian Andrzej Siewior return; 48a1222639SSebastian Andrzej Siewior } 49a1222639SSebastian Andrzej Siewior 50a1222639SSebastian Andrzej Siewior spin_lock(&usb_ctrl->lock); 51a1222639SSebastian Andrzej Siewior val = readl(usb_ctrl->wkup); 52a1222639SSebastian Andrzej Siewior 53a1222639SSebastian Andrzej Siewior if (on) 54a1222639SSebastian Andrzej Siewior val |= reg; 55a1222639SSebastian Andrzej Siewior else 56a1222639SSebastian Andrzej Siewior val &= ~reg; 57a1222639SSebastian Andrzej Siewior 58a1222639SSebastian Andrzej Siewior writel(val, usb_ctrl->wkup); 59a1222639SSebastian Andrzej Siewior spin_unlock(&usb_ctrl->lock); 60a1222639SSebastian Andrzej Siewior } 61a1222639SSebastian Andrzej Siewior 6259f042f6SBin Liu static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, 6359f042f6SBin Liu enum usb_dr_mode dr_mode, bool on) 643bb869c8SSebastian Andrzej Siewior { 653bb869c8SSebastian Andrzej Siewior struct am335x_control_usb *usb_ctrl; 663bb869c8SSebastian Andrzej Siewior u32 val; 673bb869c8SSebastian Andrzej Siewior u32 reg; 683bb869c8SSebastian Andrzej Siewior 693bb869c8SSebastian Andrzej Siewior usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); 703bb869c8SSebastian Andrzej Siewior 713bb869c8SSebastian Andrzej Siewior switch (id) { 723bb869c8SSebastian Andrzej Siewior case 0: 733bb869c8SSebastian Andrzej Siewior reg = AM335X_USB0_CTRL; 743bb869c8SSebastian Andrzej Siewior break; 753bb869c8SSebastian Andrzej Siewior case 1: 763bb869c8SSebastian Andrzej Siewior reg = AM335X_USB1_CTRL; 773bb869c8SSebastian Andrzej Siewior break; 783bb869c8SSebastian Andrzej Siewior default: 794ff74571SSebastian Andrzej Siewior WARN_ON(1); 803bb869c8SSebastian Andrzej Siewior return; 813bb869c8SSebastian Andrzej Siewior } 823bb869c8SSebastian Andrzej Siewior 833bb869c8SSebastian Andrzej Siewior val = readl(usb_ctrl->phy_reg + reg); 843bb869c8SSebastian Andrzej Siewior if (on) { 8559f042f6SBin Liu if (dr_mode == USB_DR_MODE_HOST) { 8659f042f6SBin Liu val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN | 8759f042f6SBin Liu USBPHY_OTGVDET_EN); 8859f042f6SBin Liu val |= USBPHY_OTGSESSEND_EN; 8959f042f6SBin Liu } else { 903bb869c8SSebastian Andrzej Siewior val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN); 913bb869c8SSebastian Andrzej Siewior val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN; 9259f042f6SBin Liu } 933bb869c8SSebastian Andrzej Siewior } else { 943bb869c8SSebastian Andrzej Siewior val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN; 953bb869c8SSebastian Andrzej Siewior } 963bb869c8SSebastian Andrzej Siewior 973bb869c8SSebastian Andrzej Siewior writel(val, usb_ctrl->phy_reg + reg); 98a31a942aSDaniel Mack 99a31a942aSDaniel Mack /* 100a31a942aSDaniel Mack * Give the PHY ~1ms to complete the power up operation. 101a31a942aSDaniel Mack * Tests have shown unstable behaviour if other USB PHY related 102a31a942aSDaniel Mack * registers are written too shortly after such a transition. 103a31a942aSDaniel Mack */ 104a31a942aSDaniel Mack if (on) 105a31a942aSDaniel Mack mdelay(1); 1063bb869c8SSebastian Andrzej Siewior } 1073bb869c8SSebastian Andrzej Siewior 1083bb869c8SSebastian Andrzej Siewior static const struct phy_control ctrl_am335x = { 1093bb869c8SSebastian Andrzej Siewior .phy_power = am335x_phy_power, 110a1222639SSebastian Andrzej Siewior .phy_wkup = am335x_phy_wkup, 1113bb869c8SSebastian Andrzej Siewior }; 1123bb869c8SSebastian Andrzej Siewior 1133bb869c8SSebastian Andrzej Siewior static const struct of_device_id omap_control_usb_id_table[] = { 1143bb869c8SSebastian Andrzej Siewior { .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x }, 1153bb869c8SSebastian Andrzej Siewior {} 1163bb869c8SSebastian Andrzej Siewior }; 1173bb869c8SSebastian Andrzej Siewior MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); 1183bb869c8SSebastian Andrzej Siewior 1193bb869c8SSebastian Andrzej Siewior static struct platform_driver am335x_control_driver; 1203bb869c8SSebastian Andrzej Siewior static int match(struct device *dev, void *data) 1213bb869c8SSebastian Andrzej Siewior { 1223bb869c8SSebastian Andrzej Siewior struct device_node *node = (struct device_node *)data; 1233bb869c8SSebastian Andrzej Siewior return dev->of_node == node && 1243bb869c8SSebastian Andrzej Siewior dev->driver == &am335x_control_driver.driver; 1253bb869c8SSebastian Andrzej Siewior } 1263bb869c8SSebastian Andrzej Siewior 1273bb869c8SSebastian Andrzej Siewior struct phy_control *am335x_get_phy_control(struct device *dev) 1283bb869c8SSebastian Andrzej Siewior { 1293bb869c8SSebastian Andrzej Siewior struct device_node *node; 1303bb869c8SSebastian Andrzej Siewior struct am335x_control_usb *ctrl_usb; 1313bb869c8SSebastian Andrzej Siewior 1323bb869c8SSebastian Andrzej Siewior node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0); 1333bb869c8SSebastian Andrzej Siewior if (!node) 1343bb869c8SSebastian Andrzej Siewior return NULL; 1353bb869c8SSebastian Andrzej Siewior 1363bb869c8SSebastian Andrzej Siewior dev = bus_find_device(&platform_bus_type, NULL, node, match); 137*015105b1SJohan Hovold of_node_put(node); 138d0f347d6SDavid Dueck if (!dev) 139d0f347d6SDavid Dueck return NULL; 140d0f347d6SDavid Dueck 1413bb869c8SSebastian Andrzej Siewior ctrl_usb = dev_get_drvdata(dev); 142*015105b1SJohan Hovold put_device(dev); 1433bb869c8SSebastian Andrzej Siewior if (!ctrl_usb) 1443bb869c8SSebastian Andrzej Siewior return NULL; 1453bb869c8SSebastian Andrzej Siewior return &ctrl_usb->phy_ctrl; 1463bb869c8SSebastian Andrzej Siewior } 1473bb869c8SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(am335x_get_phy_control); 1483bb869c8SSebastian Andrzej Siewior 1493bb869c8SSebastian Andrzej Siewior static int am335x_control_usb_probe(struct platform_device *pdev) 1503bb869c8SSebastian Andrzej Siewior { 1513bb869c8SSebastian Andrzej Siewior struct resource *res; 1523bb869c8SSebastian Andrzej Siewior struct am335x_control_usb *ctrl_usb; 1533bb869c8SSebastian Andrzej Siewior const struct of_device_id *of_id; 1543bb869c8SSebastian Andrzej Siewior const struct phy_control *phy_ctrl; 1553bb869c8SSebastian Andrzej Siewior 1563bb869c8SSebastian Andrzej Siewior of_id = of_match_node(omap_control_usb_id_table, pdev->dev.of_node); 1573bb869c8SSebastian Andrzej Siewior if (!of_id) 1583bb869c8SSebastian Andrzej Siewior return -EINVAL; 1593bb869c8SSebastian Andrzej Siewior 1603bb869c8SSebastian Andrzej Siewior phy_ctrl = of_id->data; 1613bb869c8SSebastian Andrzej Siewior 1623bb869c8SSebastian Andrzej Siewior ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL); 1639aabd032SPeter Chen if (!ctrl_usb) 1643bb869c8SSebastian Andrzej Siewior return -ENOMEM; 1653bb869c8SSebastian Andrzej Siewior 1663bb869c8SSebastian Andrzej Siewior ctrl_usb->dev = &pdev->dev; 1673bb869c8SSebastian Andrzej Siewior 1683bb869c8SSebastian Andrzej Siewior res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); 1693bb869c8SSebastian Andrzej Siewior ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); 1703bb869c8SSebastian Andrzej Siewior if (IS_ERR(ctrl_usb->phy_reg)) 1713bb869c8SSebastian Andrzej Siewior return PTR_ERR(ctrl_usb->phy_reg); 172a1222639SSebastian Andrzej Siewior 173a1222639SSebastian Andrzej Siewior res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup"); 174a1222639SSebastian Andrzej Siewior ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res); 175a1222639SSebastian Andrzej Siewior if (IS_ERR(ctrl_usb->wkup)) 176a1222639SSebastian Andrzej Siewior return PTR_ERR(ctrl_usb->wkup); 177a1222639SSebastian Andrzej Siewior 1783bb869c8SSebastian Andrzej Siewior spin_lock_init(&ctrl_usb->lock); 1793bb869c8SSebastian Andrzej Siewior ctrl_usb->phy_ctrl = *phy_ctrl; 1803bb869c8SSebastian Andrzej Siewior 1813bb869c8SSebastian Andrzej Siewior dev_set_drvdata(ctrl_usb->dev, ctrl_usb); 1823bb869c8SSebastian Andrzej Siewior return 0; 1833bb869c8SSebastian Andrzej Siewior } 1843bb869c8SSebastian Andrzej Siewior 1853bb869c8SSebastian Andrzej Siewior static struct platform_driver am335x_control_driver = { 1863bb869c8SSebastian Andrzej Siewior .probe = am335x_control_usb_probe, 1873bb869c8SSebastian Andrzej Siewior .driver = { 1883bb869c8SSebastian Andrzej Siewior .name = "am335x-control-usb", 189c4df9ae0SSachin Kamat .of_match_table = omap_control_usb_id_table, 1903bb869c8SSebastian Andrzej Siewior }, 1913bb869c8SSebastian Andrzej Siewior }; 1923bb869c8SSebastian Andrzej Siewior 1933bb869c8SSebastian Andrzej Siewior module_platform_driver(am335x_control_driver); 1943bb869c8SSebastian Andrzej Siewior MODULE_LICENSE("GPL v2"); 195