1*a9beb1edSmglocker /* $OpenBSD: octdwctwo.c,v 1.15 2022/09/04 08:42:39 mglocker Exp $ */
2a3f1a0ccSuebayasi
3a3f1a0ccSuebayasi /*
4a3f1a0ccSuebayasi * Copyright (c) 2015 Masao Uebayashi <uebayasi@tombiinc.com>
5a3f1a0ccSuebayasi *
6a3f1a0ccSuebayasi * Permission to use, copy, modify, and/or distribute this software for any
7a3f1a0ccSuebayasi * purpose with or without fee is hereby granted, provided that the above
8a3f1a0ccSuebayasi * copyright notice and this permission notice appear in all copies.
9a3f1a0ccSuebayasi *
10a3f1a0ccSuebayasi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a3f1a0ccSuebayasi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a3f1a0ccSuebayasi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a3f1a0ccSuebayasi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a3f1a0ccSuebayasi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a3f1a0ccSuebayasi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a3f1a0ccSuebayasi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a3f1a0ccSuebayasi */
18a3f1a0ccSuebayasi
19a3f1a0ccSuebayasi #include <sys/param.h>
20a3f1a0ccSuebayasi #include <sys/systm.h>
21a3f1a0ccSuebayasi #include <sys/device.h>
22a3f1a0ccSuebayasi
23a3f1a0ccSuebayasi #include <machine/intr.h>
24a3f1a0ccSuebayasi #include <machine/bus.h>
25a3f1a0ccSuebayasi #include <machine/octeonreg.h>
26a3f1a0ccSuebayasi #include <machine/octeonvar.h>
2760af79c0Svisa #include <machine/octeon_model.h>
28a3f1a0ccSuebayasi
298e519055Smpi #include <octeon/dev/iobusvar.h>
308e519055Smpi #include <octeon/dev/octhcireg.h>
318e519055Smpi
32a3f1a0ccSuebayasi #include <dev/usb/usb.h>
33a3f1a0ccSuebayasi #include <dev/usb/usbdi.h>
34a3f1a0ccSuebayasi #include <dev/usb/usbdivar.h>
35a3f1a0ccSuebayasi
36a3f1a0ccSuebayasi #include <dev/usb/dwc2/dwc2var.h>
37a3f1a0ccSuebayasi #include <dev/usb/dwc2/dwc2.h>
38a3f1a0ccSuebayasi #include <dev/usb/dwc2/dwc2_core.h>
39a3f1a0ccSuebayasi
40a3f1a0ccSuebayasi struct octdwctwo_softc {
41a3f1a0ccSuebayasi struct dwc2_softc sc_dwc2;
428cf7e106Suebayasi
4335925fdeSuebayasi /* USBN bus space */
4435925fdeSuebayasi bus_space_tag_t sc_bust;
4535925fdeSuebayasi bus_space_handle_t sc_regh;
468cf7e106Suebayasi bus_space_handle_t sc_regh2;
478cf7e106Suebayasi
48a3f1a0ccSuebayasi void *sc_ih;
49a3f1a0ccSuebayasi };
50a3f1a0ccSuebayasi
51a3f1a0ccSuebayasi int octdwctwo_match(struct device *, void *, void *);
52a3f1a0ccSuebayasi void octdwctwo_attach(struct device *, struct device *,
53a3f1a0ccSuebayasi void *);
54a32dca6cSvisa int octdwctwo_activate(struct device *, int);
55cab3c9f3Smglocker int octdwctwo_set_dma_addr(struct device *, bus_addr_t, int);
568cf7e106Suebayasi u_int64_t octdwctwo_reg2_rd(struct octdwctwo_softc *, bus_size_t);
578cf7e106Suebayasi void octdwctwo_reg2_wr(struct octdwctwo_softc *, bus_size_t,
588cf7e106Suebayasi u_int64_t);
59489a03d0Sjmatthew void octdwctwo_reg_set(struct octdwctwo_softc *, bus_size_t,
60489a03d0Sjmatthew u_int64_t);
61489a03d0Sjmatthew void octdwctwo_reg_clear(struct octdwctwo_softc *,
62489a03d0Sjmatthew bus_size_t, u_int64_t);
63489a03d0Sjmatthew u_int32_t octdwctwo_read_4(bus_space_tag_t, bus_space_handle_t,
64489a03d0Sjmatthew bus_size_t);
65489a03d0Sjmatthew void octdwctwo_write_4(bus_space_tag_t, bus_space_handle_t,
66489a03d0Sjmatthew bus_size_t, u_int32_t);
67489a03d0Sjmatthew
68a3f1a0ccSuebayasi
69a3f1a0ccSuebayasi const struct cfattach octdwctwo_ca = {
70a3f1a0ccSuebayasi sizeof(struct octdwctwo_softc), octdwctwo_match, octdwctwo_attach,
71a32dca6cSvisa NULL, octdwctwo_activate
72a3f1a0ccSuebayasi };
73a3f1a0ccSuebayasi
74a3f1a0ccSuebayasi struct cfdriver dwctwo_cd = {
75a3f1a0ccSuebayasi NULL, "dwctwo", DV_DULL
76a3f1a0ccSuebayasi };
77a3f1a0ccSuebayasi
78a3f1a0ccSuebayasi static struct dwc2_core_params octdwctwo_params = {
79*a9beb1edSmglocker .otg_caps.hnp_support = 0,
80*a9beb1edSmglocker .otg_caps.srp_support = 0,
81*a9beb1edSmglocker .host_dma = 1,
8235925fdeSuebayasi .dma_desc_enable = 0,
8335925fdeSuebayasi .speed = 0,
8435925fdeSuebayasi .enable_dynamic_fifo = 1,
8535925fdeSuebayasi .en_multiple_tx_fifo = 0,
86f3e0e09dSvisa .host_rx_fifo_size = 456,
87f3e0e09dSvisa .host_nperio_tx_fifo_size = 912,
88f3e0e09dSvisa .host_perio_tx_fifo_size = 256,
8935925fdeSuebayasi .max_transfer_size = 65535,
9035925fdeSuebayasi .max_packet_count = 511,
9135925fdeSuebayasi .host_channels = 8,
9235925fdeSuebayasi .phy_type = 1,
9335925fdeSuebayasi .phy_utmi_width = 16,
9435925fdeSuebayasi .phy_ulpi_ddr = 0,
9535925fdeSuebayasi .phy_ulpi_ext_vbus = 0,
9635925fdeSuebayasi .i2c_enable = 0,
9735925fdeSuebayasi .ulpi_fs_ls = 0,
9835925fdeSuebayasi .host_support_fs_ls_low_power = 0,
9935925fdeSuebayasi .host_ls_low_power_phy_clk = 0,
10035925fdeSuebayasi .ts_dline = 0,
10135925fdeSuebayasi .reload_ctl = 0,
10235925fdeSuebayasi .ahbcfg = 0x7,
10335925fdeSuebayasi .uframe_sched = 1,
104*a9beb1edSmglocker .external_id_pin_ctl = 0,
105a3f1a0ccSuebayasi };
106a3f1a0ccSuebayasi
107489a03d0Sjmatthew /*
108489a03d0Sjmatthew * This bus space tag adjusts register addresses to account for
109489a03d0Sjmatthew * dwc2 using little endian addressing. dwc2 only does 32bit reads
110489a03d0Sjmatthew * and writes, so only those functions are provided.
111489a03d0Sjmatthew */
112489a03d0Sjmatthew bus_space_t octdwctwo_tag = {
113489a03d0Sjmatthew .bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
114489a03d0Sjmatthew .bus_private = NULL,
115489a03d0Sjmatthew ._space_read_4 = octdwctwo_read_4,
116489a03d0Sjmatthew ._space_write_4 = octdwctwo_write_4,
117489a03d0Sjmatthew ._space_map = iobus_space_map,
118489a03d0Sjmatthew ._space_unmap = iobus_space_unmap,
119489a03d0Sjmatthew ._space_subregion = generic_space_region,
120489a03d0Sjmatthew ._space_vaddr = generic_space_vaddr
121489a03d0Sjmatthew };
122489a03d0Sjmatthew
123a3f1a0ccSuebayasi int
octdwctwo_match(struct device * parent,void * match,void * aux)124a3f1a0ccSuebayasi octdwctwo_match(struct device *parent, void *match, void *aux)
125a3f1a0ccSuebayasi {
12660af79c0Svisa int id;
12760af79c0Svisa
12860af79c0Svisa id = octeon_get_chipid();
12960af79c0Svisa switch (octeon_model_family(id)) {
13060af79c0Svisa case OCTEON_MODEL_FAMILY_CN30XX:
13160af79c0Svisa case OCTEON_MODEL_FAMILY_CN31XX:
13260af79c0Svisa case OCTEON_MODEL_FAMILY_CN50XX:
133a3f1a0ccSuebayasi return (1);
13460af79c0Svisa default:
13560af79c0Svisa return (0);
13660af79c0Svisa }
137a3f1a0ccSuebayasi }
138a3f1a0ccSuebayasi
139a3f1a0ccSuebayasi void
octdwctwo_attach(struct device * parent,struct device * self,void * aux)140a3f1a0ccSuebayasi octdwctwo_attach(struct device *parent, struct device *self, void *aux)
141a3f1a0ccSuebayasi {
142a3f1a0ccSuebayasi struct octdwctwo_softc *sc = (struct octdwctwo_softc *)self;
143a3f1a0ccSuebayasi struct iobus_attach_args *aa = aux;
144489a03d0Sjmatthew uint64_t clk;
145a3f1a0ccSuebayasi int rc;
146a3f1a0ccSuebayasi
147489a03d0Sjmatthew sc->sc_dwc2.sc_iot = &octdwctwo_tag;
14835925fdeSuebayasi sc->sc_dwc2.sc_bus.pipe_size = sizeof(struct usbd_pipe);
14935925fdeSuebayasi sc->sc_dwc2.sc_bus.dmatag = aa->aa_dmat;
15035925fdeSuebayasi sc->sc_dwc2.sc_params = &octdwctwo_params;
151cab3c9f3Smglocker sc->sc_dwc2.sc_set_dma_addr = octdwctwo_set_dma_addr;
152a3f1a0ccSuebayasi
153489a03d0Sjmatthew rc = bus_space_map(sc->sc_dwc2.sc_iot, USBC_BASE, USBC_SIZE,
15435925fdeSuebayasi 0, &sc->sc_dwc2.sc_ioh);
15535925fdeSuebayasi KASSERT(rc == 0);
15635925fdeSuebayasi
15735925fdeSuebayasi sc->sc_bust = aa->aa_bust;
1588cf7e106Suebayasi rc = bus_space_map(sc->sc_bust, USBN_BASE, USBN_SIZE,
15935925fdeSuebayasi 0, &sc->sc_regh);
16035925fdeSuebayasi KASSERT(rc == 0);
1618cf7e106Suebayasi rc = bus_space_map(sc->sc_bust, USBN_2_BASE, USBN_2_SIZE,
1628cf7e106Suebayasi 0, &sc->sc_regh2);
1638cf7e106Suebayasi KASSERT(rc == 0);
164a3f1a0ccSuebayasi
165489a03d0Sjmatthew /*
166489a03d0Sjmatthew * Clock setup.
167489a03d0Sjmatthew */
168489a03d0Sjmatthew clk = bus_space_read_8(sc->sc_bust, sc->sc_regh, USBN_CLK_CTL_OFFSET);
169489a03d0Sjmatthew clk |= USBN_CLK_CTL_POR;
170489a03d0Sjmatthew clk &= ~(USBN_CLK_CTL_HRST | USBN_CLK_CTL_PRST | USBN_CLK_CTL_HCLK_RST |
171489a03d0Sjmatthew USBN_CLK_CTL_ENABLE | USBN_CLK_CTL_P_C_SEL | USBN_CLK_CTL_P_RTYPE);
172489a03d0Sjmatthew clk |= SET_USBN_CLK_CTL_DIVIDE(0x4ULL)
173489a03d0Sjmatthew | SET_USBN_CLK_CTL_DIVIDE2(0x0ULL);
174489a03d0Sjmatthew
175489a03d0Sjmatthew bus_space_write_8(sc->sc_bust, sc->sc_regh, USBN_CLK_CTL_OFFSET, clk);
176489a03d0Sjmatthew bus_space_read_8(sc->sc_bust, sc->sc_regh, USBN_CLK_CTL_OFFSET);
177489a03d0Sjmatthew
178489a03d0Sjmatthew /*
179489a03d0Sjmatthew * Reset HCLK and wait for it to stabilize.
180489a03d0Sjmatthew */
181489a03d0Sjmatthew octdwctwo_reg_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_HCLK_RST);
182489a03d0Sjmatthew delay(64);
183489a03d0Sjmatthew
184489a03d0Sjmatthew octdwctwo_reg_clear(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_POR);
185489a03d0Sjmatthew
186489a03d0Sjmatthew /*
187489a03d0Sjmatthew * Wait for the PHY clock to start.
188489a03d0Sjmatthew */
189489a03d0Sjmatthew delay(1000);
190489a03d0Sjmatthew
191489a03d0Sjmatthew octdwctwo_reg_set(sc, USBN_USBP_CTL_STATUS_OFFSET,
192489a03d0Sjmatthew USBN_USBP_CTL_STATUS_ATE_RESET);
193489a03d0Sjmatthew delay(10);
194489a03d0Sjmatthew
195489a03d0Sjmatthew octdwctwo_reg_clear(sc, USBN_USBP_CTL_STATUS_OFFSET,
196489a03d0Sjmatthew USBN_USBP_CTL_STATUS_ATE_RESET);
197489a03d0Sjmatthew octdwctwo_reg_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_PRST);
198489a03d0Sjmatthew
199489a03d0Sjmatthew /*
200489a03d0Sjmatthew * Select host mode.
201489a03d0Sjmatthew */
202489a03d0Sjmatthew octdwctwo_reg_clear(sc, USBN_USBP_CTL_STATUS_OFFSET,
203489a03d0Sjmatthew USBN_USBP_CTL_STATUS_HST_MODE);
204489a03d0Sjmatthew delay(1);
205489a03d0Sjmatthew
206489a03d0Sjmatthew octdwctwo_reg_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_HRST);
207489a03d0Sjmatthew
208489a03d0Sjmatthew /*
209489a03d0Sjmatthew * Enable clock.
210489a03d0Sjmatthew */
211489a03d0Sjmatthew octdwctwo_reg_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_ENABLE);
212489a03d0Sjmatthew delay(1);
213489a03d0Sjmatthew
214ae91d3eeSjasper strlcpy(sc->sc_dwc2.sc_vendor, "Octeon", sizeof(sc->sc_dwc2.sc_vendor));
215ae91d3eeSjasper
216dc96afe4Suebayasi rc = dwc2_init(&sc->sc_dwc2);
217dc96afe4Suebayasi if (rc != 0)
218dc96afe4Suebayasi return;
21981a51520Smiod
22081a51520Smiod printf("\n");
22181a51520Smiod
222dc96afe4Suebayasi sc->sc_dwc2.sc_child = config_found(&sc->sc_dwc2.sc_bus.bdev,
223dc96afe4Suebayasi &sc->sc_dwc2.sc_bus, usbctlprint);
224dc96afe4Suebayasi
225*a9beb1edSmglocker sc->sc_ih = octeon_intr_establish(CIU_INT_USB, IPL_VM | IPL_MPSAFE,
2265c30878dSvisa dwc2_intr, (void *)&sc->sc_dwc2, sc->sc_dwc2.sc_bus.bdev.dv_xname);
22735925fdeSuebayasi KASSERT(sc->sc_ih != NULL);
228a3f1a0ccSuebayasi }
2298cf7e106Suebayasi
2308cf7e106Suebayasi int
octdwctwo_activate(struct device * self,int act)231a32dca6cSvisa octdwctwo_activate(struct device *self, int act)
232a32dca6cSvisa {
233a32dca6cSvisa struct octdwctwo_softc *sc = (struct octdwctwo_softc *)self;
234a32dca6cSvisa uint64_t clk;
235a32dca6cSvisa int rv = 0;
236a32dca6cSvisa
237a32dca6cSvisa switch (act) {
238a32dca6cSvisa case DVACT_POWERDOWN:
239a32dca6cSvisa /*
240a32dca6cSvisa * Put the controller into reset mode.
241a32dca6cSvisa * It appears necessary to hold this state for a moment.
242a32dca6cSvisa * Otherwise subsequent attempts to reinitialize the controller
243a32dca6cSvisa * may fail because of hanging or trapping access
244a32dca6cSvisa * of DWC2 core registers.
245a32dca6cSvisa */
246a32dca6cSvisa clk = bus_space_read_8(sc->sc_bust, sc->sc_regh,
247a32dca6cSvisa USBN_CLK_CTL_OFFSET);
248a32dca6cSvisa clk |= USBN_CLK_CTL_POR;
249a32dca6cSvisa clk |= USBN_CLK_CTL_HCLK_RST;
250a32dca6cSvisa clk |= USBN_CLK_CTL_ENABLE;
251a32dca6cSvisa clk &= ~USBN_CLK_CTL_HRST;
252a32dca6cSvisa clk &= ~USBN_CLK_CTL_PRST;
253a32dca6cSvisa bus_space_write_8(sc->sc_bust, sc->sc_regh,
254a32dca6cSvisa USBN_CLK_CTL_OFFSET, clk);
255a32dca6cSvisa (void)bus_space_read_8(sc->sc_bust, sc->sc_regh,
256a32dca6cSvisa USBN_CLK_CTL_OFFSET);
257a32dca6cSvisa delay(50000);
258a32dca6cSvisa break;
259a32dca6cSvisa default:
260a32dca6cSvisa break;
261a32dca6cSvisa }
262a32dca6cSvisa return rv;
263a32dca6cSvisa }
264a32dca6cSvisa
265a32dca6cSvisa int
octdwctwo_set_dma_addr(struct device * data,bus_addr_t dma_addr,int ch)266cab3c9f3Smglocker octdwctwo_set_dma_addr(struct device *data, bus_addr_t dma_addr, int ch)
2678cf7e106Suebayasi {
268cab3c9f3Smglocker struct octdwctwo_softc *sc = (struct octdwctwo_softc *)data;
2698cf7e106Suebayasi
2708cf7e106Suebayasi octdwctwo_reg2_wr(sc,
2718cf7e106Suebayasi USBN_DMA0_INB_CHN0_OFFSET + ch * 0x8, dma_addr);
2728cf7e106Suebayasi octdwctwo_reg2_wr(sc,
2738cf7e106Suebayasi USBN_DMA0_OUTB_CHN0_OFFSET + ch * 0x8, dma_addr);
2748cf7e106Suebayasi return 0;
2758cf7e106Suebayasi }
2768cf7e106Suebayasi
2778cf7e106Suebayasi u_int64_t
octdwctwo_reg2_rd(struct octdwctwo_softc * sc,bus_size_t offset)2788cf7e106Suebayasi octdwctwo_reg2_rd(struct octdwctwo_softc *sc, bus_size_t offset)
2798cf7e106Suebayasi {
2808cf7e106Suebayasi u_int64_t value;
2818cf7e106Suebayasi
2828cf7e106Suebayasi value = bus_space_read_8(sc->sc_bust, sc->sc_regh2, offset);
2838cf7e106Suebayasi return value;
2848cf7e106Suebayasi }
2858cf7e106Suebayasi
2868cf7e106Suebayasi void
octdwctwo_reg2_wr(struct octdwctwo_softc * sc,bus_size_t offset,u_int64_t value)2878cf7e106Suebayasi octdwctwo_reg2_wr(struct octdwctwo_softc *sc, bus_size_t offset, u_int64_t value)
2888cf7e106Suebayasi {
2898cf7e106Suebayasi bus_space_write_8(sc->sc_bust, sc->sc_regh2, offset, value);
2908cf7e106Suebayasi /* guarantee completion of the store operation on RSL registers*/
2918cf7e106Suebayasi bus_space_read_8(sc->sc_bust, sc->sc_regh2, offset);
2928cf7e106Suebayasi }
293489a03d0Sjmatthew
294489a03d0Sjmatthew void
octdwctwo_reg_set(struct octdwctwo_softc * sc,bus_size_t offset,u_int64_t bits)295489a03d0Sjmatthew octdwctwo_reg_set(struct octdwctwo_softc *sc, bus_size_t offset,
296489a03d0Sjmatthew u_int64_t bits)
297489a03d0Sjmatthew {
298489a03d0Sjmatthew u_int64_t value;
299489a03d0Sjmatthew value = bus_space_read_8(sc->sc_bust, sc->sc_regh, offset);
300489a03d0Sjmatthew value |= bits;
301489a03d0Sjmatthew
302489a03d0Sjmatthew bus_space_write_8(sc->sc_bust, sc->sc_regh, offset, value);
303489a03d0Sjmatthew bus_space_read_8(sc->sc_bust, sc->sc_regh, offset);
304489a03d0Sjmatthew }
305489a03d0Sjmatthew
306489a03d0Sjmatthew void
octdwctwo_reg_clear(struct octdwctwo_softc * sc,bus_size_t offset,u_int64_t bits)307489a03d0Sjmatthew octdwctwo_reg_clear(struct octdwctwo_softc *sc, bus_size_t offset,
308489a03d0Sjmatthew u_int64_t bits)
309489a03d0Sjmatthew {
310489a03d0Sjmatthew u_int64_t value;
311489a03d0Sjmatthew value = bus_space_read_8(sc->sc_bust, sc->sc_regh, offset);
312489a03d0Sjmatthew value &= ~bits;
313489a03d0Sjmatthew
314489a03d0Sjmatthew bus_space_write_8(sc->sc_bust, sc->sc_regh, offset, value);
315489a03d0Sjmatthew bus_space_read_8(sc->sc_bust, sc->sc_regh, offset);
316489a03d0Sjmatthew }
317489a03d0Sjmatthew
318489a03d0Sjmatthew u_int32_t
octdwctwo_read_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)319489a03d0Sjmatthew octdwctwo_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
320489a03d0Sjmatthew {
321489a03d0Sjmatthew return *(volatile u_int32_t *)(h + (o^4));
322489a03d0Sjmatthew }
323489a03d0Sjmatthew
324489a03d0Sjmatthew void
octdwctwo_write_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,u_int32_t v)325489a03d0Sjmatthew octdwctwo_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
326489a03d0Sjmatthew u_int32_t v)
327489a03d0Sjmatthew {
328489a03d0Sjmatthew *(volatile u_int32_t *)(h + (o^4)) = v;
329489a03d0Sjmatthew }
330