1 /* $OpenBSD: octuctl.c,v 1.1 2016/03/18 05:38:10 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/octeon_model.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 int id; 112 113 id = octeon_get_chipid(); 114 switch (octeon_model_family(id)) { 115 case OCTEON_MODEL_FAMILY_CN61XX: 116 return (1); 117 default: 118 return (0); 119 } 120 } 121 122 int 123 octuctlprint(void *aux, const char *parentname) 124 { 125 return (QUIET); 126 } 127 128 void 129 octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl) 130 { 131 int div; 132 int lastdiv; 133 int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX }; 134 int i; 135 136 div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ; 137 138 /* start usb controller reset */ 139 ctl |= UCTL_CLK_RST_CTL_P_POR; 140 ctl &= ~(UCTL_CLK_RST_CTL_HRST | 141 UCTL_CLK_RST_CTL_P_PRST | 142 UCTL_CLK_RST_CTL_O_CLKDIV_EN | 143 UCTL_CLK_RST_CTL_H_CLKDIV_EN | 144 UCTL_CLK_RST_CTL_H_CLKDIV_RST | 145 UCTL_CLK_RST_CTL_O_CLKDIV_RST); 146 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 147 148 /* set up for 12mhz crystal */ 149 ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) | 150 (3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT)); 151 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 152 153 /* set clock divider */ 154 lastdiv = 1; 155 for (i = 0; i < nitems(validdiv); i++) { 156 if (div < validdiv[i]) { 157 div = lastdiv; 158 break; 159 } 160 lastdiv = validdiv[i]; 161 } 162 163 ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT); 164 ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT); 165 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 166 167 /* turn hclk on */ 168 ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, 169 UCTL_CLK_RST_CTL); 170 ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN; 171 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 172 ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST; 173 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 174 175 delay(1); 176 177 /* power-on-reset finished */ 178 ctl &= ~UCTL_CLK_RST_CTL_P_POR; 179 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 180 181 delay(1000); 182 183 /* set up ohci clocks */ 184 ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST; 185 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 186 ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN; 187 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 188 189 delay(1); 190 191 /* phy reset */ 192 ctl |= UCTL_CLK_RST_CTL_P_PRST; 193 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 194 195 delay(1); 196 197 /* clear host reset */ 198 ctl |= UCTL_CLK_RST_CTL_HRST; 199 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl); 200 } 201 202 void 203 octuctl_attach(struct device *parent, struct device *self, void *aux) 204 { 205 struct octuctl_softc *sc = (struct octuctl_softc *)self; 206 struct iobus_attach_args *aa = aux; 207 struct octuctl_attach_args uaa; 208 uint64_t port_ctl; 209 uint64_t ctl; 210 uint64_t preg; 211 uint64_t txvref; 212 int rc; 213 int port; 214 215 sc->sc_iot = aa->aa_bust; 216 rc = bus_space_map(sc->sc_iot, UCTL_BASE, UCTL_SIZE, 217 0, &sc->sc_ioh); 218 KASSERT(rc == 0); 219 220 /* do clock setup if not already done */ 221 bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA, 222 UCTL_IF_ENA_EN); 223 ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL); 224 if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0) 225 octuctl_clock_setup(sc, ctl); 226 227 /* port phy settings */ 228 for (port = 0; port < 2; port++) { 229 preg = UCTL_UPHY_PORTX_STATUS + (port * 8); 230 port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg); 231 txvref = 0xf; 232 port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE | 233 UCTL_UPHY_PORTX_STATUS_TXRISETUNE | 234 (txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT)); 235 bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl); 236 } 237 238 printf("\n"); 239 240 uaa.aa_octuctl_bust = aa->aa_bust; 241 uaa.aa_bust = &octuctl_tag; 242 uaa.aa_dmat = aa->aa_dmat; 243 uaa.aa_ioh = sc->sc_ioh; 244 245 uaa.aa_name = "ehci"; 246 config_found(self, &uaa, octuctlprint); 247 248 uaa.aa_name = "ohci"; 249 config_found(self, &uaa, octuctlprint); 250 } 251