1 /* $OpenBSD: amlpciephy.c,v 1.4 2020/12/27 20:37:58 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_clock.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/fdt.h> 30 31 #define PHY_R0 0x00 32 #define PHY_R0_PCIE_POWER_MASK (0x1f << 0) 33 #define PHY_R0_PCIE_POWER_ON (0x1c << 0) 34 #define PHY_R0_PCIE_POWER_OFF (0x1d << 0) 35 #define PHY_R0_MODE_MASK (0x3 << 5) 36 #define PHY_R0_MODE_USB3 (0x3 << 5) 37 #define PHY_R4 0x10 38 #define PHY_R4_PHY_CR_WRITE (1 << 0) 39 #define PHY_R4_PHY_CR_READ (1 << 1) 40 #define PHY_R4_PHY_CR_CAP_DATA (1 << 18) 41 #define PHY_R4_PHY_CR_CAP_ADDR (1 << 19) 42 #define PHY_R5 0x14 43 #define PHY_R5_PHY_CR_ACK (1 << 16) 44 45 #define HREAD4(sc, reg) \ 46 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 47 #define HWRITE4(sc, reg, val) \ 48 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 49 #define HSET4(sc, reg, bits) \ 50 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 51 #define HCLR4(sc, reg, bits) \ 52 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 53 54 struct amlpciephy_softc { 55 struct device sc_dev; 56 bus_space_tag_t sc_iot; 57 bus_space_handle_t sc_ioh; 58 59 struct phy_device sc_pd; 60 }; 61 62 int amlpciephy_match(struct device *, void *, void *); 63 void amlpciephy_attach(struct device *, struct device *, void *); 64 65 struct cfattach amlpciephy_ca = { 66 sizeof (struct amlpciephy_softc), amlpciephy_match, amlpciephy_attach 67 }; 68 69 struct cfdriver amlpciephy_cd = { 70 NULL, "amlpciephy", DV_DULL 71 }; 72 73 int amlpciephy_enable(void *, uint32_t *); 74 uint16_t amlpciephy_read(struct amlpciephy_softc *, bus_addr_t); 75 void amlpciephy_write(struct amlpciephy_softc *, bus_addr_t, uint16_t); 76 77 int 78 amlpciephy_match(struct device *parent, void *match, void *aux) 79 { 80 struct fdt_attach_args *faa = aux; 81 82 return OF_is_compatible(faa->fa_node, "amlogic,g12a-usb3-pcie-phy"); 83 } 84 85 void 86 amlpciephy_attach(struct device *parent, struct device *self, void *aux) 87 { 88 struct amlpciephy_softc *sc = (struct amlpciephy_softc *)self; 89 struct fdt_attach_args *faa = aux; 90 91 if (faa->fa_nreg < 1) { 92 printf(": no registers\n"); 93 return; 94 } 95 96 sc->sc_iot = faa->fa_iot; 97 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 98 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 99 printf(": can't map registers\n"); 100 return; 101 } 102 103 printf("\n"); 104 105 sc->sc_pd.pd_node = faa->fa_node; 106 sc->sc_pd.pd_cookie = sc; 107 sc->sc_pd.pd_enable = amlpciephy_enable; 108 phy_register(&sc->sc_pd); 109 } 110 111 int 112 amlpciephy_enable(void *cookie, uint32_t *cells) 113 { 114 struct amlpciephy_softc *sc = cookie; 115 int node = sc->sc_pd.pd_node; 116 uint32_t type = cells[0]; 117 uint32_t reg; 118 119 /* Hardware can be switched between PCIe 2.0 and USB 3.0 mode. */ 120 if (type != PHY_TYPE_PCIE && type != PHY_TYPE_USB3) 121 return -1; 122 123 clock_set_assigned(node); 124 clock_enable_all(node); 125 126 switch (type) { 127 case PHY_TYPE_PCIE: 128 /* Power on. */ 129 reg = HREAD4(sc, PHY_R0); 130 reg &= ~PHY_R0_PCIE_POWER_MASK; 131 reg |= PHY_R0_PCIE_POWER_ON; 132 HWRITE4(sc, PHY_R0, reg); 133 134 reset_assert_all(node); 135 delay(500); 136 reset_deassert_all(node); 137 delay(500); 138 139 break; 140 case PHY_TYPE_USB3: 141 reset_assert_all(node); 142 delay(10); 143 reset_deassert_all(node); 144 145 /* Switch to USB 3.0 mode. */ 146 reg = HREAD4(sc, PHY_R0); 147 reg &= ~PHY_R0_MODE_MASK; 148 reg |= PHY_R0_MODE_USB3; 149 HWRITE4(sc, PHY_R0, reg); 150 151 /* Workaround for SuperSpeed PHY suspend bug. */ 152 reg = amlpciephy_read(sc, 0x102d); 153 reg |= (1 << 7); 154 amlpciephy_write(sc, 0x102d, reg); 155 156 reg = amlpciephy_read(sc, 0x1010); 157 reg &= ~0xff0; 158 reg |= 0x10; 159 amlpciephy_write(sc, 0x1010, reg); 160 161 /* Rx equalization magic. */ 162 reg = amlpciephy_read(sc, 0x1006); 163 reg &= (1 << 6); 164 reg |= (1 << 7); 165 reg &= ~(0x7 << 8); 166 reg |= (0x3 << 8); 167 reg |= (1 << 11); 168 amlpciephy_write(sc, 0x1006, reg); 169 170 /* Tx equalization magic. */ 171 reg = amlpciephy_read(sc, 0x1002); 172 reg &= ~0x3f80; 173 reg |= (0x16 << 7); 174 reg &= ~0x7f; 175 reg |= (0x7f | (1 << 14)); 176 amlpciephy_write(sc, 0x1002, reg); 177 178 /* MPLL loop magic. */ 179 reg = amlpciephy_read(sc, 0x30); 180 reg &= ~(0xf << 4); 181 reg |= (8 << 4); 182 amlpciephy_write(sc, 0x30, reg); 183 184 break; 185 } 186 187 return 0; 188 } 189 190 void 191 amlpciephy_addr(struct amlpciephy_softc *sc, bus_addr_t addr) 192 { 193 int timo; 194 195 HWRITE4(sc, PHY_R4, addr << 2); 196 HWRITE4(sc, PHY_R4, addr << 2); 197 HWRITE4(sc, PHY_R4, (addr << 2) | PHY_R4_PHY_CR_CAP_ADDR); 198 for (timo = 200; timo > 0; timo--) { 199 if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) 200 break; 201 delay(5); 202 } 203 if (timo == 0) { 204 printf("%s: timeout\n", __func__); 205 return; 206 } 207 HWRITE4(sc, PHY_R4, addr << 2); 208 for (timo = 200; timo > 0; timo--) { 209 if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0) 210 break; 211 delay(5); 212 } 213 if (timo == 0) { 214 printf("%s: timeout\n", __func__); 215 return; 216 } 217 } 218 219 uint16_t 220 amlpciephy_read(struct amlpciephy_softc *sc, bus_addr_t addr) 221 { 222 uint32_t reg; 223 int timo; 224 225 amlpciephy_addr(sc, addr); 226 HWRITE4(sc, PHY_R4, 0); 227 HWRITE4(sc, PHY_R4, PHY_R4_PHY_CR_READ); 228 for (timo = 200; timo > 0; timo--) { 229 if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) 230 break; 231 delay(5); 232 } 233 if (timo == 0) { 234 printf("%s: timeout\n", __func__); 235 return 0; 236 } 237 reg = HREAD4(sc, PHY_R5); 238 HWRITE4(sc, PHY_R4, 0); 239 for (timo = 200; timo > 0; timo--) { 240 if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0) 241 break; 242 delay(5); 243 } 244 if (timo == 0) { 245 printf("%s: timeout\n", __func__); 246 return 0; 247 } 248 return reg; 249 } 250 251 void 252 amlpciephy_write(struct amlpciephy_softc *sc, bus_addr_t addr, uint16_t data) 253 { 254 int timo; 255 256 amlpciephy_addr(sc, addr); 257 HWRITE4(sc, PHY_R4, data << 2); 258 HWRITE4(sc, PHY_R4, data << 2); 259 HWRITE4(sc, PHY_R4, data << 2 | PHY_R4_PHY_CR_CAP_DATA); 260 for (timo = 200; timo > 0; timo--) { 261 if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) 262 break; 263 delay(5); 264 } 265 if (timo == 0) { 266 printf("%s: timeout\n", __func__); 267 return; 268 } 269 HWRITE4(sc, PHY_R4, data << 2); 270 for (timo = 200; timo > 0; timo--) { 271 if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0) 272 break; 273 delay(5); 274 } 275 if (timo == 0) { 276 printf("%s: timeout\n", __func__); 277 return; 278 } 279 280 HWRITE4(sc, PHY_R4, data << 2); 281 HWRITE4(sc, PHY_R4, data << 2 | PHY_R4_PHY_CR_WRITE); 282 for (timo = 200; timo > 0; timo--) { 283 if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) 284 break; 285 delay(5); 286 } 287 if (timo == 0) { 288 printf("%s: timeout\n", __func__); 289 return; 290 } 291 HWRITE4(sc, PHY_R4, data << 2); 292 for (timo = 200; timo > 0; timo--) { 293 if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0) 294 break; 295 delay(5); 296 } 297 if (timo == 0) { 298 printf("%s: timeout\n", __func__); 299 return; 300 } 301 } 302