ehci_ixp4xx.c revision 188410
1188410Sthompsa/*- 2188410Sthompsa * Copyright (c) 2008 Sam Leffler. All rights reserved. 3188410Sthompsa * 4188410Sthompsa * Redistribution and use in source and binary forms, with or without 5188410Sthompsa * modification, are permitted provided that the following conditions 6188410Sthompsa * are met: 7188410Sthompsa * 1. Redistributions of source code must retain the above copyright 8188410Sthompsa * notice, this list of conditions and the following disclaimer. 9188410Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 10188410Sthompsa * notice, this list of conditions and the following disclaimer in the 11188410Sthompsa * documentation and/or other materials provided with the distribution. 12188410Sthompsa * 13188410Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14188410Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15188410Sthompsa * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16188410Sthompsa * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17188410Sthompsa * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18188410Sthompsa * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19188410Sthompsa * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20188410Sthompsa * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21188410Sthompsa * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22188410Sthompsa * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23188410Sthompsa */ 24188410Sthompsa 25188410Sthompsa/* 26188410Sthompsa * IXP435 attachment driver for the USB Enhanced Host Controller. 27188410Sthompsa */ 28188410Sthompsa 29188410Sthompsa#include <sys/cdefs.h> 30188410Sthompsa__FBSDID("$FreeBSD: head/sys/dev/usb2/controller/ehci2_ixp4xx.c 188410 2009-02-09 21:50:04Z thompsa $"); 31188410Sthompsa 32188410Sthompsa#include "opt_bus.h" 33188410Sthompsa 34188410Sthompsa#include <dev/usb2/include/usb2_mfunc.h> 35188410Sthompsa#include <dev/usb2/include/usb2_defs.h> 36188410Sthompsa#include <dev/usb2/include/usb2_standard.h> 37188410Sthompsa 38188410Sthompsa#include <dev/usb2/core/usb2_core.h> 39188410Sthompsa#include <dev/usb2/core/usb2_busdma.h> 40188410Sthompsa#include <dev/usb2/core/usb2_process.h> 41188410Sthompsa#include <dev/usb2/core/usb2_sw_transfer.h> 42188410Sthompsa#include <dev/usb2/core/usb2_util.h> 43188410Sthompsa 44188410Sthompsa#include <dev/usb2/controller/usb2_controller.h> 45188410Sthompsa#include <dev/usb2/controller/usb2_bus.h> 46188410Sthompsa#include <dev/usb2/controller/ehci2.h> 47188410Sthompsa 48188410Sthompsa#include <arm/xscale/ixp425/ixp425reg.h> 49188410Sthompsa#include <arm/xscale/ixp425/ixp425var.h> 50188410Sthompsa 51188410Sthompsa#define EHCI_VENDORID_IXP4XX 0x42fa05 52188410Sthompsa#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller" 53188410Sthompsa 54188410Sthompsastruct ixp_ehci_softc { 55188410Sthompsa ehci_softc_t base; /* storage for EHCI code */ 56188410Sthompsa bus_space_tag_t iot; 57188410Sthompsa bus_space_handle_t ioh; 58188410Sthompsa struct bus_space tag; /* tag for private bus space ops */ 59188410Sthompsa}; 60188410Sthompsa 61188410Sthompsastatic device_attach_t ehci_ixp_attach; 62188410Sthompsastatic device_detach_t ehci_ixp_detach; 63188410Sthompsastatic device_shutdown_t ehci_ixp_shutdown; 64188410Sthompsastatic device_suspend_t ehci_ixp_suspend; 65188410Sthompsastatic device_resume_t ehci_ixp_resume; 66188410Sthompsa 67188410Sthompsastatic uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t); 68188410Sthompsastatic void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t); 69188410Sthompsastatic uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t); 70188410Sthompsastatic void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t); 71188410Sthompsastatic uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t); 72188410Sthompsastatic void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t); 73188410Sthompsa 74188410Sthompsastatic int 75188410Sthompsaehci_ixp_suspend(device_t self) 76188410Sthompsa{ 77188410Sthompsa ehci_softc_t *sc = device_get_softc(self); 78188410Sthompsa int err; 79188410Sthompsa 80188410Sthompsa err = bus_generic_suspend(self); 81188410Sthompsa if (err) 82188410Sthompsa return (err); 83188410Sthompsa ehci_suspend(sc); 84188410Sthompsa return (0); 85188410Sthompsa} 86188410Sthompsa 87188410Sthompsastatic int 88188410Sthompsaehci_ixp_resume(device_t self) 89188410Sthompsa{ 90188410Sthompsa ehci_softc_t *sc = device_get_softc(self); 91188410Sthompsa 92188410Sthompsa ehci_resume(sc); 93188410Sthompsa 94188410Sthompsa bus_generic_resume(self); 95188410Sthompsa 96188410Sthompsa return (0); 97188410Sthompsa} 98188410Sthompsa 99188410Sthompsastatic int 100188410Sthompsaehci_ixp_shutdown(device_t self) 101188410Sthompsa{ 102188410Sthompsa ehci_softc_t *sc = device_get_softc(self); 103188410Sthompsa int err; 104188410Sthompsa 105188410Sthompsa err = bus_generic_shutdown(self); 106188410Sthompsa if (err) 107188410Sthompsa return (err); 108188410Sthompsa ehci_shutdown(sc); 109188410Sthompsa 110188410Sthompsa return (0); 111188410Sthompsa} 112188410Sthompsa 113188410Sthompsastatic int 114188410Sthompsaehci_ixp_probe(device_t self) 115188410Sthompsa{ 116188410Sthompsa 117188410Sthompsa device_set_desc(self, EHCI_HC_DEVSTR); 118188410Sthompsa 119188410Sthompsa return (BUS_PROBE_DEFAULT); 120188410Sthompsa} 121188410Sthompsa 122188410Sthompsastatic int 123188410Sthompsaehci_ixp_attach(device_t self) 124188410Sthompsa{ 125188410Sthompsa struct ixp_ehci_softc *isc = device_get_softc(self); 126188410Sthompsa ehci_softc_t *sc = &isc->base; 127188410Sthompsa int err; 128188410Sthompsa int rid; 129188410Sthompsa 130188410Sthompsa /* initialise some bus fields */ 131188410Sthompsa sc->sc_bus.parent = self; 132188410Sthompsa sc->sc_bus.devices = sc->sc_devices; 133188410Sthompsa sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 134188410Sthompsa 135188410Sthompsa /* get all DMA memory */ 136188410Sthompsa if (usb2_bus_mem_alloc_all(&sc->sc_bus, 137188410Sthompsa USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 138188410Sthompsa return (ENOMEM); 139188410Sthompsa } 140188410Sthompsa 141188410Sthompsa sc->sc_bus.usbrev = USB_REV_2_0; 142188410Sthompsa 143188410Sthompsa /* NB: hints fix the memory location and irq */ 144188410Sthompsa 145188410Sthompsa rid = 0; 146188410Sthompsa sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 147188410Sthompsa if (!sc->sc_io_res) { 148188410Sthompsa device_printf(self, "Could not map memory\n"); 149188410Sthompsa goto error; 150188410Sthompsa } 151188410Sthompsa 152188410Sthompsa /* 153188410Sthompsa * Craft special resource for bus space ops that handle 154188410Sthompsa * byte-alignment of non-word addresses. Also, since 155188410Sthompsa * we're already intercepting bus space ops we handle 156188410Sthompsa * the register window offset that could otherwise be 157188410Sthompsa * done with bus_space_subregion. 158188410Sthompsa */ 159188410Sthompsa isc->iot = rman_get_bustag(sc->sc_io_res); 160188410Sthompsa isc->tag.bs_cookie = isc->iot; 161188410Sthompsa /* read single */ 162188410Sthompsa isc->tag.bs_r_1 = ehci_bs_r_1, 163188410Sthompsa isc->tag.bs_r_2 = ehci_bs_r_2, 164188410Sthompsa isc->tag.bs_r_4 = ehci_bs_r_4, 165188410Sthompsa /* write (single) */ 166188410Sthompsa isc->tag.bs_w_1 = ehci_bs_w_1, 167188410Sthompsa isc->tag.bs_w_2 = ehci_bs_w_2, 168188410Sthompsa isc->tag.bs_w_4 = ehci_bs_w_4, 169188410Sthompsa 170188410Sthompsa sc->sc_io_tag = &isc->tag; 171188410Sthompsa sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 172188410Sthompsa sc->sc_io_size = IXP435_USB1_SIZE - 0x100; 173188410Sthompsa 174188410Sthompsa rid = 0; 175188410Sthompsa sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 176188410Sthompsa RF_ACTIVE); 177188410Sthompsa if (sc->sc_irq_res == NULL) { 178188410Sthompsa device_printf(self, "Could not allocate irq\n"); 179188410Sthompsa goto error; 180188410Sthompsa } 181188410Sthompsa sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 182188410Sthompsa if (!sc->sc_bus.bdev) { 183188410Sthompsa device_printf(self, "Could not add USB device\n"); 184188410Sthompsa goto error; 185188410Sthompsa } 186188410Sthompsa device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 187188410Sthompsa device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 188188410Sthompsa 189188410Sthompsa sprintf(sc->sc_vendor, "Intel"); 190188410Sthompsa 191188410Sthompsa 192188410Sthompsa err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 193188410Sthompsa NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); 194188410Sthompsa if (err) { 195188410Sthompsa device_printf(self, "Could not setup irq, %d\n", err); 196188410Sthompsa sc->sc_intr_hdl = NULL; 197188410Sthompsa goto error; 198188410Sthompsa } 199188410Sthompsa 200188410Sthompsa /* 201188410Sthompsa * Arrange to force Host mode, select big-endian byte alignment, 202188410Sthompsa * and arrange to not terminate reset operations (the adapter 203188410Sthompsa * will ignore it if we do but might as well save a reg write). 204188410Sthompsa * Also, the controller has an embedded Transaction Translator 205188410Sthompsa * which means port speed must be read from the Port Status 206188410Sthompsa * register following a port enable. 207188410Sthompsa */ 208188410Sthompsa sc->sc_flags |= EHCI_SCFLG_TT 209188410Sthompsa | EHCI_SCFLG_SETMODE 210188410Sthompsa | EHCI_SCFLG_BIGEDESC 211188410Sthompsa | EHCI_SCFLG_BIGEMMIO 212188410Sthompsa | EHCI_SCFLG_NORESTERM 213188410Sthompsa ; 214188410Sthompsa 215188410Sthompsa err = ehci_init(sc); 216188410Sthompsa if (!err) { 217188410Sthompsa err = device_probe_and_attach(sc->sc_bus.bdev); 218188410Sthompsa } 219188410Sthompsa if (err) { 220188410Sthompsa device_printf(self, "USB init failed err=%d\n", err); 221188410Sthompsa goto error; 222188410Sthompsa } 223188410Sthompsa return (0); 224188410Sthompsa 225188410Sthompsaerror: 226188410Sthompsa ehci_ixp_detach(self); 227188410Sthompsa return (ENXIO); 228188410Sthompsa} 229188410Sthompsa 230188410Sthompsastatic int 231188410Sthompsaehci_ixp_detach(device_t self) 232188410Sthompsa{ 233188410Sthompsa struct ixp_ehci_softc *isc = device_get_softc(self); 234188410Sthompsa ehci_softc_t *sc = &isc->base; 235188410Sthompsa device_t bdev; 236188410Sthompsa int err; 237188410Sthompsa 238188410Sthompsa if (sc->sc_bus.bdev) { 239188410Sthompsa bdev = sc->sc_bus.bdev; 240188410Sthompsa device_detach(bdev); 241188410Sthompsa device_delete_child(self, bdev); 242188410Sthompsa } 243188410Sthompsa /* during module unload there are lots of children leftover */ 244188410Sthompsa device_delete_all_children(self); 245188410Sthompsa 246188410Sthompsa /* 247188410Sthompsa * disable interrupts that might have been switched on in ehci_init 248188410Sthompsa */ 249188410Sthompsa if (sc->sc_io_res) { 250188410Sthompsa EWRITE4(sc, EHCI_USBINTR, 0); 251188410Sthompsa } 252188410Sthompsa 253188410Sthompsa if (sc->sc_irq_res && sc->sc_intr_hdl) { 254188410Sthompsa /* 255188410Sthompsa * only call ehci_detach() after ehci_init() 256188410Sthompsa */ 257188410Sthompsa ehci_detach(sc); 258188410Sthompsa 259188410Sthompsa err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 260188410Sthompsa 261188410Sthompsa if (err) 262188410Sthompsa /* XXX or should we panic? */ 263188410Sthompsa device_printf(self, "Could not tear down irq, %d\n", 264188410Sthompsa err); 265188410Sthompsa sc->sc_intr_hdl = NULL; 266188410Sthompsa } 267188410Sthompsa 268188410Sthompsa if (sc->sc_irq_res) { 269188410Sthompsa bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 270188410Sthompsa sc->sc_irq_res = NULL; 271188410Sthompsa } 272188410Sthompsa if (sc->sc_io_res) { 273188410Sthompsa bus_release_resource(self, SYS_RES_MEMORY, 0, 274188410Sthompsa sc->sc_io_res); 275188410Sthompsa sc->sc_io_res = NULL; 276188410Sthompsa } 277188410Sthompsa usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 278188410Sthompsa 279188410Sthompsa return (0); 280188410Sthompsa} 281188410Sthompsa 282188410Sthompsa/* 283188410Sthompsa * Bus space accessors for PIO operations. 284188410Sthompsa */ 285188410Sthompsa 286188410Sthompsastatic uint8_t 287188410Sthompsaehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o) 288188410Sthompsa{ 289188410Sthompsa return bus_space_read_1((bus_space_tag_t) t, h, 290188410Sthompsa 0x100 + (o &~ 3) + (3 - (o & 3))); 291188410Sthompsa} 292188410Sthompsa 293188410Sthompsastatic void 294188410Sthompsaehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v) 295188410Sthompsa{ 296188410Sthompsa panic("%s", __func__); 297188410Sthompsa} 298188410Sthompsa 299188410Sthompsastatic uint16_t 300188410Sthompsaehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o) 301188410Sthompsa{ 302188410Sthompsa return bus_space_read_2((bus_space_tag_t) t, h, 303188410Sthompsa 0x100 + (o &~ 3) + (2 - (o & 3))); 304188410Sthompsa} 305188410Sthompsa 306188410Sthompsastatic void 307188410Sthompsaehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v) 308188410Sthompsa{ 309188410Sthompsa panic("%s", __func__); 310188410Sthompsa} 311188410Sthompsa 312188410Sthompsastatic uint32_t 313188410Sthompsaehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o) 314188410Sthompsa{ 315188410Sthompsa return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o); 316188410Sthompsa} 317188410Sthompsa 318188410Sthompsastatic void 319188410Sthompsaehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v) 320188410Sthompsa{ 321188410Sthompsa bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v); 322188410Sthompsa} 323188410Sthompsa 324188410Sthompsastatic device_method_t ehci_methods[] = { 325188410Sthompsa /* Device interface */ 326188410Sthompsa DEVMETHOD(device_probe, ehci_ixp_probe), 327188410Sthompsa DEVMETHOD(device_attach, ehci_ixp_attach), 328188410Sthompsa DEVMETHOD(device_detach, ehci_ixp_detach), 329188410Sthompsa DEVMETHOD(device_suspend, ehci_ixp_suspend), 330188410Sthompsa DEVMETHOD(device_resume, ehci_ixp_resume), 331188410Sthompsa DEVMETHOD(device_shutdown, ehci_ixp_shutdown), 332188410Sthompsa 333188410Sthompsa /* Bus interface */ 334188410Sthompsa DEVMETHOD(bus_print_child, bus_generic_print_child), 335188410Sthompsa 336188410Sthompsa {0, 0} 337188410Sthompsa}; 338188410Sthompsa 339188410Sthompsastatic driver_t ehci_driver = { 340188410Sthompsa "ehci", 341188410Sthompsa ehci_methods, 342188410Sthompsa sizeof(struct ixp_ehci_softc), 343188410Sthompsa}; 344188410Sthompsa 345188410Sthompsastatic devclass_t ehci_devclass; 346188410Sthompsa 347188410SthompsaDRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0); 348188410SthompsaMODULE_DEPEND(ehci, usb2_controller, 1, 1, 1); 349188410SthompsaMODULE_DEPEND(ehci, usb2_core, 1, 1, 1); 350