1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver 4 * 5 * Copyright (C) 2013 Linaro Limited 6 * Matt Porter <mporter@linaro.org> 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/delay.h> 11 #include <linux/err.h> 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/phy/phy.h> 16 #include <linux/platform_device.h> 17 18 #define OTGCTL (0) 19 #define OTGCTL_OTGSTAT2 BIT(31) 20 #define OTGCTL_OTGSTAT1 BIT(30) 21 #define OTGCTL_PRST_N_SW BIT(11) 22 #define OTGCTL_HRESET_N BIT(10) 23 #define OTGCTL_UTMI_LINE_STATE1 BIT(9) 24 #define OTGCTL_UTMI_LINE_STATE0 BIT(8) 25 26 #define P1CTL (8) 27 #define P1CTL_SOFT_RESET BIT(1) 28 #define P1CTL_NON_DRIVING BIT(0) 29 30 struct bcm_kona_usb { 31 void __iomem *regs; 32 }; 33 34 static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) 35 { 36 u32 val; 37 38 val = readl(phy->regs + OTGCTL); 39 if (on) { 40 /* Configure and power PHY */ 41 val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | 42 OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); 43 val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; 44 } else { 45 val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); 46 } 47 writel(val, phy->regs + OTGCTL); 48 } 49 50 static int bcm_kona_usb_phy_init(struct phy *gphy) 51 { 52 struct bcm_kona_usb *phy = phy_get_drvdata(gphy); 53 u32 val; 54 55 /* Soft reset PHY */ 56 val = readl(phy->regs + P1CTL); 57 val &= ~P1CTL_NON_DRIVING; 58 val |= P1CTL_SOFT_RESET; 59 writel(val, phy->regs + P1CTL); 60 writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); 61 /* Reset needs to be asserted for 2ms */ 62 mdelay(2); 63 writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); 64 65 return 0; 66 } 67 68 static int bcm_kona_usb_phy_power_on(struct phy *gphy) 69 { 70 struct bcm_kona_usb *phy = phy_get_drvdata(gphy); 71 72 bcm_kona_usb_phy_power(phy, 1); 73 74 return 0; 75 } 76 77 static int bcm_kona_usb_phy_power_off(struct phy *gphy) 78 { 79 struct bcm_kona_usb *phy = phy_get_drvdata(gphy); 80 81 bcm_kona_usb_phy_power(phy, 0); 82 83 return 0; 84 } 85 86 static const struct phy_ops ops = { 87 .init = bcm_kona_usb_phy_init, 88 .power_on = bcm_kona_usb_phy_power_on, 89 .power_off = bcm_kona_usb_phy_power_off, 90 .owner = THIS_MODULE, 91 }; 92 93 static int bcm_kona_usb2_probe(struct platform_device *pdev) 94 { 95 struct device *dev = &pdev->dev; 96 struct bcm_kona_usb *phy; 97 struct resource *res; 98 struct phy *gphy; 99 struct phy_provider *phy_provider; 100 101 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 102 if (!phy) 103 return -ENOMEM; 104 105 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 106 phy->regs = devm_ioremap_resource(&pdev->dev, res); 107 if (IS_ERR(phy->regs)) 108 return PTR_ERR(phy->regs); 109 110 platform_set_drvdata(pdev, phy); 111 112 gphy = devm_phy_create(dev, NULL, &ops); 113 if (IS_ERR(gphy)) 114 return PTR_ERR(gphy); 115 116 /* The Kona PHY supports an 8-bit wide UTMI interface */ 117 phy_set_bus_width(gphy, 8); 118 119 phy_set_drvdata(gphy, phy); 120 121 phy_provider = devm_of_phy_provider_register(dev, 122 of_phy_simple_xlate); 123 124 return PTR_ERR_OR_ZERO(phy_provider); 125 } 126 127 static const struct of_device_id bcm_kona_usb2_dt_ids[] = { 128 { .compatible = "brcm,kona-usb2-phy" }, 129 { /* sentinel */ } 130 }; 131 132 MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); 133 134 static struct platform_driver bcm_kona_usb2_driver = { 135 .probe = bcm_kona_usb2_probe, 136 .driver = { 137 .name = "bcm-kona-usb2", 138 .of_match_table = bcm_kona_usb2_dt_ids, 139 }, 140 }; 141 142 module_platform_driver(bcm_kona_usb2_driver); 143 144 MODULE_ALIAS("platform:bcm-kona-usb2"); 145 MODULE_AUTHOR("Matt Porter <mporter@linaro.org>"); 146 MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); 147 MODULE_LICENSE("GPL v2"); 148