1 /* $OpenBSD: octuctl.c,v 1.2 2017/07/25 11:01:28 jmatthew Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Jonathan Matthew <jmatthew@openbsd.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <machine/intr.h> 24 #include <machine/bus.h> 25 #include <machine/octeonreg.h> 26 #include <machine/octeonvar.h> 27 #include <machine/fdt.h> 28 29 #include <octeon/dev/iobusvar.h> 30 #include <octeon/dev/octuctlreg.h> 31 #include <octeon/dev/octuctlvar.h> 32 33 struct octuctl_softc { 34 struct device sc_dev; 35 bus_space_tag_t sc_iot; 36 bus_space_handle_t sc_ioh; 37 }; 38 39 int octuctl_match(struct device *, void *, void *); 40 void octuctl_attach(struct device *, struct device *, void *); 41 42 const struct cfattach octuctl_ca = { 43 sizeof(struct octuctl_softc), octuctl_match, octuctl_attach, 44 }; 45 46 struct cfdriver octuctl_cd = { 47 NULL, "octuctl", DV_DULL 48 }; 49 50 uint8_t octuctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); 51 uint16_t octuctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t); 52 uint32_t octuctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t); 53 void octuctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t); 54 void octuctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t); 55 void octuctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t); 56 57 bus_space_t octuctl_tag = { 58 .bus_base = PHYS_TO_XKPHYS(0, CCA_NC), 59 .bus_private = NULL, 60 ._space_read_1 = octuctl_read_1, 61 ._space_write_1 = octuctl_write_1, 62 ._space_read_2 = octuctl_read_2, 63 ._space_write_2 = octuctl_write_2, 64 ._space_read_4 = octuctl_read_4, 65 ._space_write_4 = octuctl_write_4, 66 ._space_map = iobus_space_map, 67 ._space_unmap = iobus_space_unmap, 68 ._space_subregion = generic_space_region, 69 ._space_vaddr = generic_space_vaddr 70 }; 71 72 uint8_t 73 octuctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 74 { 75 return *(volatile uint8_t *)(h + (o^3)); 76 } 77 78 uint16_t 79 octuctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 80 { 81 return *(volatile uint16_t *)(h + (o^2)); 82 } 83 84 uint32_t 85 octuctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 86 { 87 return *(volatile uint32_t *)(h + o); 88 } 89 90 void 91 octuctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v) 92 { 93 *(volatile uint8_t *)(h + (o^3)) = v; 94 } 95 96 void 97 octuctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v) 98 { 99 *(volatile uint16_t *)(h + (o^2)) = v; 100 } 101 102 void 103 octuctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v) 104 { 105 *(volatile uint32_t *)(h + o) = v; 106 } 107 108 int 109 octuctl_match(struct device *parent, void *match, void *aux) 110 { 111 struct fdt_attach_args *faa = aux; 112 113 return OF_is_compatible(faa->fa_node, "cavium,octeon-6335-uctl"); 114 } 115 116 int 117 octuctlprint(void *aux, const char *parentname) 118 { 119 return (QUIET); 120 } 121 122 void 123 octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl) 124 { 125 int div; 126 int lastdiv; 127 int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX }; 128 int i; 129 130 div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ; 131 132 /* start usb controller reset */ 133 ctl |= UCTL_CLK_RST_CTL_P_POR; 134 ctl &= ~(UCTL_CLK_RST_CTL_HRST | 135 UCTL_CLK_RST_CTL_P_PRST | 136 UCTL_CLK_RST_CTL_O_CLKDIV_EN | 137 UCTL_CLK_RST_CTL_H_CLKDIV_EN | 138 UCTL_CLK_RST_CTL_H_CLKDIV_RST | 139 UCTL_CLK_RST_CTL_O_CLKDIV_RST); 140 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 141 142 /* set up for 12mhz crystal */ 143 ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) | 144 (3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT)); 145 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 146 147 /* set clock divider */ 148 lastdiv = 1; 149 for (i = 0; i < nitems(validdiv); i++) { 150 if (div < validdiv[i]) { 151 div = lastdiv; 152 break; 153 } 154 lastdiv = validdiv[i]; 155 } 156 157 ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT); 158 ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT); 159 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 160 161 /* turn hclk on */ 162 ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, 163 UCTL_CLK_RST_CTL); 164 ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN; 165 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 166 ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST; 167 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 168 169 delay(1); 170 171 /* power-on-reset finished */ 172 ctl &= ~UCTL_CLK_RST_CTL_P_POR; 173 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 174 175 delay(1000); 176 177 /* set up ohci clocks */ 178 ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST; 179 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 180 ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN; 181 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 182 183 delay(1); 184 185 /* phy reset */ 186 ctl |= UCTL_CLK_RST_CTL_P_PRST; 187 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 188 189 delay(1); 190 191 /* clear host reset */ 192 ctl |= UCTL_CLK_RST_CTL_HRST; 193 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 194 } 195 196 void 197 octuctl_attach(struct device *parent, struct device *self, void *aux) 198 { 199 struct fdt_attach_args *faa = aux; 200 struct octuctl_softc *sc = (struct octuctl_softc *)self; 201 struct octuctl_attach_args uaa; 202 uint64_t port_ctl; 203 uint64_t ctl; 204 uint64_t preg; 205 uint64_t txvref; 206 uint32_t reg[4]; 207 int port; 208 int node; 209 int rc; 210 211 if (faa->fa_nreg != 1) { 212 printf(": expected one IO space, got %d\n", faa->fa_nreg); 213 return; 214 } 215 216 sc->sc_iot = faa->fa_iot; 217 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 218 0, &sc->sc_ioh)) { 219 printf(": could not map IO space\n"); 220 return; 221 } 222 223 rc = OF_getpropint(faa->fa_node, "#address-cells", 0); 224 if (rc != 2) { 225 printf(": expected #address-cells 2, got %d\n", rc); 226 return; 227 } 228 rc = OF_getpropint(faa->fa_node, "#size-cells", 0); 229 if (rc != 2) { 230 printf(": expected #size-cells 2, got %d\n", rc); 231 return; 232 } 233 234 /* do clock setup if not already done */ 235 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA, 236 UCTL_IF_ENA_EN); 237 ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL); 238 if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0) 239 octuctl_clock_setup(sc, ctl); 240 241 /* port phy settings */ 242 for (port = 0; port < 2; port++) { 243 preg = UCTL_UPHY_PORTX_STATUS + (port * 8); 244 port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg); 245 txvref = 0xf; 246 port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE | 247 UCTL_UPHY_PORTX_STATUS_TXRISETUNE | 248 (txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT)); 249 bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl); 250 } 251 252 printf("\n"); 253 254 uaa.aa_octuctl_bust = sc->sc_iot; 255 uaa.aa_bust = &octuctl_tag; 256 uaa.aa_dmat = faa->fa_dmat; 257 uaa.aa_ioh = sc->sc_ioh; 258 259 for (node = OF_child(faa->fa_node); node != 0; node = OF_peer(node)) { 260 if (OF_getproplen(node, "reg") != sizeof(reg)) 261 continue; 262 263 OF_getpropintarray(node, "reg", reg, sizeof(reg)); 264 uaa.aa_reg.addr = (((uint64_t)reg[0]) << 32) | reg[1]; 265 uaa.aa_reg.size = (((uint64_t)reg[2]) << 32) | reg[3]; 266 uaa.aa_node = node; 267 config_found(self, &uaa, octuctlprint); 268 } 269 } 270