Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: phy: Add driver for the Ingenic JZ4770 USB transceiver

Add a driver to support the USB PHY found in the JZ4770 SoC from
Ingenic.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Link: https://lore.kernel.org/r/20200229161820.17824-2-paul@crapouillou.net
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Paul Cercueil and committed by
Greg Kroah-Hartman
541368b4 adc221b5

+252
+8
drivers/usb/phy/Kconfig
··· 184 184 Provides read/write operations to the ULPI phy register set for 185 185 controllers with a viewport register (e.g. Chipidea/ARC controllers). 186 186 187 + config JZ4770_PHY 188 + tristate "Ingenic JZ4770 Transceiver Driver" 189 + depends on MIPS || COMPILE_TEST 190 + select USB_PHY 191 + help 192 + This driver provides PHY support for the USB controller found 193 + on the JZ4770 SoC from Ingenic. 194 + 187 195 endmenu
+1
drivers/usb/phy/Makefile
··· 24 24 obj-$(CONFIG_USB_ULPI) += phy-ulpi.o 25 25 obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o 26 26 obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o 27 + obj-$(CONFIG_JZ4770_PHY) += phy-jz4770.o
+243
drivers/usb/phy/phy-jz4770.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Ingenic JZ4770 USB PHY driver 4 + * Copyright (c) Paul Cercueil <paul@crapouillou.net> 5 + */ 6 + 7 + #include <linux/clk.h> 8 + #include <linux/io.h> 9 + #include <linux/module.h> 10 + #include <linux/platform_device.h> 11 + #include <linux/regulator/consumer.h> 12 + #include <linux/usb/otg.h> 13 + #include <linux/usb/phy.h> 14 + 15 + #define REG_USBPCR_OFFSET 0x00 16 + #define REG_USBRDT_OFFSET 0x04 17 + #define REG_USBVBFIL_OFFSET 0x08 18 + #define REG_USBPCR1_OFFSET 0x0c 19 + 20 + /* USBPCR */ 21 + #define USBPCR_USB_MODE BIT(31) 22 + #define USBPCR_AVLD_REG BIT(30) 23 + #define USBPCR_INCRM BIT(27) 24 + #define USBPCR_CLK12_EN BIT(26) 25 + #define USBPCR_COMMONONN BIT(25) 26 + #define USBPCR_VBUSVLDEXT BIT(24) 27 + #define USBPCR_VBUSVLDEXTSEL BIT(23) 28 + #define USBPCR_POR BIT(22) 29 + #define USBPCR_SIDDQ BIT(21) 30 + #define USBPCR_OTG_DISABLE BIT(20) 31 + #define USBPCR_TXPREEMPHTUNE BIT(6) 32 + 33 + #define USBPCR_IDPULLUP_LSB 28 34 + #define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB) 35 + #define USBPCR_IDPULLUP_ALWAYS (3 << USBPCR_IDPULLUP_LSB) 36 + #define USBPCR_IDPULLUP_SUSPEND (1 << USBPCR_IDPULLUP_LSB) 37 + #define USBPCR_IDPULLUP_OTG (0 << USBPCR_IDPULLUP_LSB) 38 + 39 + #define USBPCR_COMPDISTUNE_LSB 17 40 + #define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB) 41 + #define USBPCR_COMPDISTUNE_DFT 4 42 + 43 + #define USBPCR_OTGTUNE_LSB 14 44 + #define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB) 45 + #define USBPCR_OTGTUNE_DFT 4 46 + 47 + #define USBPCR_SQRXTUNE_LSB 11 48 + #define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB) 49 + #define USBPCR_SQRXTUNE_DFT 3 50 + 51 + #define USBPCR_TXFSLSTUNE_LSB 7 52 + #define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB) 53 + #define USBPCR_TXFSLSTUNE_DFT 3 54 + 55 + #define USBPCR_TXRISETUNE_LSB 4 56 + #define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB) 57 + #define USBPCR_TXRISETUNE_DFT 3 58 + 59 + #define USBPCR_TXVREFTUNE_LSB 0 60 + #define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB) 61 + #define USBPCR_TXVREFTUNE_DFT 5 62 + 63 + /* USBRDT */ 64 + #define USBRDT_VBFIL_LD_EN BIT(25) 65 + #define USBRDT_IDDIG_EN BIT(24) 66 + #define USBRDT_IDDIG_REG BIT(23) 67 + 68 + #define USBRDT_USBRDT_LSB 0 69 + #define USBRDT_USBRDT_MASK GENMASK(22, USBRDT_USBRDT_LSB) 70 + 71 + /* USBPCR1 */ 72 + #define USBPCR1_UHC_POWON BIT(5) 73 + 74 + struct jz4770_phy { 75 + struct usb_phy phy; 76 + struct usb_otg otg; 77 + struct device *dev; 78 + void __iomem *base; 79 + struct clk *clk; 80 + struct regulator *vcc_supply; 81 + }; 82 + 83 + static inline struct jz4770_phy *otg_to_jz4770_phy(struct usb_otg *otg) 84 + { 85 + return container_of(otg, struct jz4770_phy, otg); 86 + } 87 + 88 + static inline struct jz4770_phy *phy_to_jz4770_phy(struct usb_phy *phy) 89 + { 90 + return container_of(phy, struct jz4770_phy, phy); 91 + } 92 + 93 + static int jz4770_phy_set_peripheral(struct usb_otg *otg, 94 + struct usb_gadget *gadget) 95 + { 96 + struct jz4770_phy *priv = otg_to_jz4770_phy(otg); 97 + u32 reg; 98 + 99 + reg = readl(priv->base + REG_USBPCR_OFFSET); 100 + reg &= ~USBPCR_USB_MODE; 101 + reg |= USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE; 102 + writel(reg, priv->base + REG_USBPCR_OFFSET); 103 + 104 + return 0; 105 + } 106 + 107 + static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host) 108 + { 109 + struct jz4770_phy *priv = otg_to_jz4770_phy(otg); 110 + u32 reg; 111 + 112 + reg = readl(priv->base + REG_USBPCR_OFFSET); 113 + reg &= ~(USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE); 114 + reg |= USBPCR_USB_MODE; 115 + writel(reg, priv->base + REG_USBPCR_OFFSET); 116 + 117 + return 0; 118 + } 119 + 120 + static int jz4770_phy_init(struct usb_phy *phy) 121 + { 122 + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); 123 + int err; 124 + u32 reg; 125 + 126 + err = regulator_enable(priv->vcc_supply); 127 + if (err) { 128 + dev_err(priv->dev, "Unable to enable VCC: %d", err); 129 + return err; 130 + } 131 + 132 + err = clk_prepare_enable(priv->clk); 133 + if (err) { 134 + dev_err(priv->dev, "Unable to start clock: %d", err); 135 + return err; 136 + } 137 + 138 + reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS | 139 + (USBPCR_COMPDISTUNE_DFT << USBPCR_COMPDISTUNE_LSB) | 140 + (USBPCR_OTGTUNE_DFT << USBPCR_OTGTUNE_LSB) | 141 + (USBPCR_SQRXTUNE_DFT << USBPCR_SQRXTUNE_LSB) | 142 + (USBPCR_TXFSLSTUNE_DFT << USBPCR_TXFSLSTUNE_LSB) | 143 + (USBPCR_TXRISETUNE_DFT << USBPCR_TXRISETUNE_LSB) | 144 + (USBPCR_TXVREFTUNE_DFT << USBPCR_TXVREFTUNE_LSB) | 145 + USBPCR_POR; 146 + writel(reg, priv->base + REG_USBPCR_OFFSET); 147 + 148 + /* Wait for PHY to reset */ 149 + usleep_range(30, 300); 150 + writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET); 151 + usleep_range(300, 1000); 152 + 153 + return 0; 154 + } 155 + 156 + static void jz4770_phy_shutdown(struct usb_phy *phy) 157 + { 158 + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); 159 + 160 + clk_disable_unprepare(priv->clk); 161 + regulator_disable(priv->vcc_supply); 162 + } 163 + 164 + static void jz4770_phy_remove(void *phy) 165 + { 166 + usb_remove_phy(phy); 167 + } 168 + 169 + static int jz4770_phy_probe(struct platform_device *pdev) 170 + { 171 + struct device *dev = &pdev->dev; 172 + struct jz4770_phy *priv; 173 + int err; 174 + 175 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 176 + if (!priv) 177 + return -ENOMEM; 178 + 179 + platform_set_drvdata(pdev, priv); 180 + priv->dev = dev; 181 + priv->phy.dev = dev; 182 + priv->phy.otg = &priv->otg; 183 + priv->phy.label = "jz4770-phy"; 184 + priv->phy.init = jz4770_phy_init; 185 + priv->phy.shutdown = jz4770_phy_shutdown; 186 + 187 + priv->otg.state = OTG_STATE_UNDEFINED; 188 + priv->otg.usb_phy = &priv->phy; 189 + priv->otg.set_host = jz4770_phy_set_host; 190 + priv->otg.set_peripheral = jz4770_phy_set_peripheral; 191 + 192 + priv->base = devm_platform_ioremap_resource(pdev, 0); 193 + if (IS_ERR(priv->base)) { 194 + dev_err(dev, "Failed to map registers"); 195 + return PTR_ERR(priv->base); 196 + } 197 + 198 + priv->clk = devm_clk_get(dev, NULL); 199 + if (IS_ERR(priv->clk)) { 200 + err = PTR_ERR(priv->clk); 201 + if (err != -EPROBE_DEFER) 202 + dev_err(dev, "Failed to get clock"); 203 + return err; 204 + } 205 + 206 + priv->vcc_supply = devm_regulator_get(dev, "vcc"); 207 + if (IS_ERR(priv->vcc_supply)) { 208 + err = PTR_ERR(priv->vcc_supply); 209 + if (err != -EPROBE_DEFER) 210 + dev_err(dev, "failed to get regulator"); 211 + return err; 212 + } 213 + 214 + err = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); 215 + if (err) { 216 + if (err != -EPROBE_DEFER) 217 + dev_err(dev, "Unable to register PHY"); 218 + return err; 219 + } 220 + 221 + return devm_add_action_or_reset(dev, jz4770_phy_remove, &priv->phy); 222 + } 223 + 224 + #ifdef CONFIG_OF 225 + static const struct of_device_id jz4770_phy_of_matches[] = { 226 + { .compatible = "ingenic,jz4770-phy" }, 227 + { } 228 + }; 229 + MODULE_DEVICE_TABLE(of, jz4770_phy_of_matches); 230 + #endif 231 + 232 + static struct platform_driver jz4770_phy_driver = { 233 + .probe = jz4770_phy_probe, 234 + .driver = { 235 + .name = "jz4770-phy", 236 + .of_match_table = of_match_ptr(jz4770_phy_of_matches), 237 + }, 238 + }; 239 + module_platform_driver(jz4770_phy_driver); 240 + 241 + MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 242 + MODULE_DESCRIPTION("Ingenic JZ4770 USB PHY driver"); 243 + MODULE_LICENSE("GPL");