1236120Sraj/*- 2236120Sraj * Copyright (c) 2010-2012 Semihalf 3236120Sraj * All rights reserved. 4236120Sraj * 5236120Sraj * Redistribution and use in source and binary forms, with or without 6236120Sraj * modification, are permitted provided that the following conditions 7236120Sraj * are met: 8236120Sraj * 1. Redistributions of source code must retain the above copyright 9236120Sraj * notice, this list of conditions and the following disclaimer. 10236120Sraj * 2. Redistributions in binary form must reproduce the above copyright 11236120Sraj * notice, this list of conditions and the following disclaimer in the 12236120Sraj * documentation and/or other materials provided with the distribution. 13236120Sraj * 14236120Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15236120Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16236120Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17236120Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18236120Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19236120Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20236120Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21236120Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22236120Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23236120Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24236120Sraj * SUCH DAMAGE. 25236120Sraj */ 26236120Sraj 27236120Sraj#include <sys/cdefs.h> 28236120Sraj__FBSDID("$FreeBSD$"); 29236120Sraj 30236120Sraj#include "opt_bus.h" 31236120Sraj 32236120Sraj#include <sys/param.h> 33236120Sraj#include <sys/systm.h> 34236120Sraj#include <sys/kernel.h> 35236120Sraj#include <sys/module.h> 36236120Sraj#include <sys/bus.h> 37236120Sraj#include <sys/queue.h> 38236120Sraj#include <sys/lock.h> 39236120Sraj#include <sys/lockmgr.h> 40236120Sraj#include <sys/condvar.h> 41236120Sraj#include <sys/rman.h> 42236120Sraj 43236120Sraj#include <dev/ofw/ofw_bus.h> 44236120Sraj#include <dev/ofw/ofw_bus_subr.h> 45236120Sraj 46236120Sraj#include <dev/usb/usb.h> 47236120Sraj#include <dev/usb/usbdi.h> 48236120Sraj#include <dev/usb/usb_core.h> 49236120Sraj#include <dev/usb/usb_busdma.h> 50236120Sraj#include <dev/usb/usb_process.h> 51236120Sraj#include <dev/usb/usb_util.h> 52236120Sraj#include <dev/usb/usb_controller.h> 53236120Sraj#include <dev/usb/usb_bus.h> 54236120Sraj#include <dev/usb/controller/ehci.h> 55236120Sraj#include <dev/usb/controller/ehcireg.h> 56236120Sraj 57236120Sraj#include <machine/bus.h> 58236120Sraj#include <machine/clock.h> 59236120Sraj#include <machine/resource.h> 60236120Sraj 61236120Sraj#include <powerpc/include/tlb.h> 62236120Sraj 63236120Sraj#include "opt_platform.h" 64236120Sraj 65236120Sraj/* 66236120Sraj * Register the driver 67236120Sraj */ 68236120Sraj/* Forward declarations */ 69236120Srajstatic int fsl_ehci_attach(device_t self); 70236120Srajstatic int fsl_ehci_detach(device_t self); 71236120Srajstatic int fsl_ehci_probe(device_t self); 72236120Sraj 73236120Srajstatic device_method_t ehci_methods[] = { 74236120Sraj /* Device interface */ 75236120Sraj DEVMETHOD(device_probe, fsl_ehci_probe), 76236120Sraj DEVMETHOD(device_attach, fsl_ehci_attach), 77236120Sraj DEVMETHOD(device_detach, fsl_ehci_detach), 78236120Sraj DEVMETHOD(device_suspend, bus_generic_suspend), 79236120Sraj DEVMETHOD(device_resume, bus_generic_resume), 80236120Sraj DEVMETHOD(device_shutdown, bus_generic_shutdown), 81236120Sraj 82236120Sraj /* Bus interface */ 83236120Sraj DEVMETHOD(bus_print_child, bus_generic_print_child), 84236120Sraj 85236120Sraj { 0, 0 } 86236120Sraj}; 87236120Sraj 88236120Sraj/* kobj_class definition */ 89236120Srajstatic driver_t ehci_driver = { 90236120Sraj "ehci", 91236120Sraj ehci_methods, 92236120Sraj sizeof(struct ehci_softc) 93236120Sraj}; 94236120Sraj 95236120Srajstatic devclass_t ehci_devclass; 96236120Sraj 97236120SrajDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 98236120SrajMODULE_DEPEND(ehci, usb, 1, 1, 1); 99236120Sraj 100236120Sraj/* 101236120Sraj * Private defines 102236120Sraj */ 103236120Sraj#define FSL_EHCI_REG_OFF 0x100 104236120Sraj#define FSL_EHCI_REG_SIZE 0x300 105236120Sraj 106236120Sraj/* 107236120Sraj * Internal interface registers' offsets. 108236120Sraj * Offsets from 0x000 ehci dev space, big-endian access. 109236120Sraj */ 110236120Srajenum internal_reg { 111236120Sraj SNOOP1 = 0x400, 112236120Sraj SNOOP2 = 0x404, 113236120Sraj AGE_CNT_THRESH = 0x408, 114236120Sraj SI_CTRL = 0x410, 115236120Sraj CONTROL = 0x500 116236120Sraj}; 117236120Sraj 118236120Sraj/* CONTROL register bit flags */ 119236120Srajenum control_flags { 120236120Sraj USB_EN = 0x00000004, 121236120Sraj UTMI_PHY_EN = 0x00000200, 122236120Sraj ULPI_INT_EN = 0x00000001 123236120Sraj}; 124236120Sraj 125236120Sraj/* SI_CTRL register bit flags */ 126236120Srajenum si_ctrl_flags { 127236120Sraj FETCH_32 = 1, 128236120Sraj FETCH_64 = 0 129236120Sraj}; 130236120Sraj 131236120Sraj#define SNOOP_RANGE_2GB 0x1E 132236120Sraj 133236120Sraj/* 134236120Sraj * Operational registers' offsets. 135236120Sraj * Offsets from USBCMD register, little-endian access. 136236120Sraj */ 137236120Srajenum special_op_reg { 138236120Sraj USBMODE = 0x0A8, 139236120Sraj PORTSC = 0x084, 140236120Sraj ULPI_VIEWPORT = 0x70 141236120Sraj}; 142236120Sraj 143236120Sraj/* USBMODE register bit flags */ 144236120Srajenum usbmode_flags { 145236120Sraj HOST_MODE = 0x3, 146236120Sraj DEVICE_MODE = 0x2 147236120Sraj}; 148236120Sraj 149236120Sraj#define PORT_POWER_MASK 0x00001000 150236120Sraj 151236120Sraj/* 152236120Sraj * Private methods 153236120Sraj */ 154236120Sraj 155236120Srajstatic void 156236120Srajset_to_host_mode(ehci_softc_t *sc) 157236120Sraj{ 158236120Sraj int tmp; 159236120Sraj 160236120Sraj tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE); 161236120Sraj bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, USBMODE, tmp | HOST_MODE); 162236120Sraj} 163236120Sraj 164236120Srajstatic void 165236120Srajenable_usb(device_t dev, bus_space_tag_t iot, bus_space_handle_t ioh) 166236120Sraj{ 167236120Sraj int tmp; 168236120Sraj phandle_t node; 169236120Sraj char *phy_type; 170236120Sraj 171236120Sraj phy_type = NULL; 172236120Sraj tmp = bus_space_read_4(iot, ioh, CONTROL) | USB_EN; 173236120Sraj 174236120Sraj node = ofw_bus_get_node(dev); 175236120Sraj if ((node != 0) && 176236120Sraj (OF_getprop_alloc(node, "phy_type", 1, (void **)&phy_type) > 0)) { 177236120Sraj if (strncasecmp(phy_type, "utmi", strlen("utmi")) == 0) 178236120Sraj tmp |= UTMI_PHY_EN; 179299747Sgonzo OF_prop_free(phy_type); 180236120Sraj } 181236120Sraj bus_space_write_4(iot, ioh, CONTROL, tmp); 182236120Sraj} 183236120Sraj 184236120Srajstatic void 185236120Srajset_32b_prefetch(bus_space_tag_t iot, bus_space_handle_t ioh) 186236120Sraj{ 187236120Sraj 188236120Sraj bus_space_write_4(iot, ioh, SI_CTRL, FETCH_32); 189236120Sraj} 190236120Sraj 191236120Srajstatic void 192236120Srajset_snooping(bus_space_tag_t iot, bus_space_handle_t ioh) 193236120Sraj{ 194236120Sraj 195236120Sraj bus_space_write_4(iot, ioh, SNOOP1, SNOOP_RANGE_2GB); 196236120Sraj bus_space_write_4(iot, ioh, SNOOP2, 0x80000000 | SNOOP_RANGE_2GB); 197236120Sraj} 198236120Sraj 199236120Srajstatic void 200236120Srajclear_port_power(ehci_softc_t *sc) 201236120Sraj{ 202236120Sraj int tmp; 203236120Sraj 204236120Sraj tmp = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC); 205236120Sraj bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, PORTSC, tmp & ~PORT_POWER_MASK); 206236120Sraj} 207236120Sraj 208236120Sraj/* 209236120Sraj * Public methods 210236120Sraj */ 211236120Srajstatic int 212236120Srajfsl_ehci_probe(device_t dev) 213236120Sraj{ 214236120Sraj 215261410Sian if (!ofw_bus_status_okay(dev)) 216261410Sian return (ENXIO); 217261410Sian 218236120Sraj if (((ofw_bus_is_compatible(dev, "fsl-usb2-dr")) == 0) && 219236120Sraj ((ofw_bus_is_compatible(dev, "fsl-usb2-mph")) == 0)) 220236120Sraj return (ENXIO); 221236120Sraj 222236120Sraj device_set_desc(dev, "Freescale integrated EHCI controller"); 223236120Sraj 224236120Sraj return (BUS_PROBE_DEFAULT); 225236120Sraj} 226236120Sraj 227236120Srajstatic int 228236120Srajfsl_ehci_attach(device_t self) 229236120Sraj{ 230236120Sraj ehci_softc_t *sc; 231236120Sraj int rid; 232236120Sraj int err; 233236120Sraj bus_space_handle_t ioh; 234236120Sraj bus_space_tag_t iot; 235236120Sraj 236236120Sraj sc = device_get_softc(self); 237236120Sraj rid = 0; 238236120Sraj 239236120Sraj sc->sc_bus.parent = self; 240236120Sraj sc->sc_bus.devices = sc->sc_devices; 241236120Sraj sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 242276717Shselasky sc->sc_bus.dma_bits = 32; 243236120Sraj 244236120Sraj if (usb_bus_mem_alloc_all(&sc->sc_bus, 245236120Sraj USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) 246236120Sraj return (ENOMEM); 247236120Sraj 248236120Sraj /* Allocate io resource for EHCI */ 249236120Sraj sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, 250236120Sraj RF_ACTIVE); 251236120Sraj if (sc->sc_io_res == NULL) { 252236120Sraj err = fsl_ehci_detach(self); 253236120Sraj if (err) { 254236120Sraj device_printf(self, 255236120Sraj "Detach of the driver failed with error %d\n", 256236120Sraj err); 257236120Sraj } 258236120Sraj return (ENXIO); 259236120Sraj } 260236120Sraj iot = rman_get_bustag(sc->sc_io_res); 261236120Sraj 262236120Sraj /* 263236120Sraj * Set handle to USB related registers subregion used by generic 264236120Sraj * EHCI driver 265236120Sraj */ 266236120Sraj ioh = rman_get_bushandle(sc->sc_io_res); 267236120Sraj 268236120Sraj err = bus_space_subregion(iot, ioh, FSL_EHCI_REG_OFF, FSL_EHCI_REG_SIZE, 269236120Sraj &sc->sc_io_hdl); 270236120Sraj if (err != 0) { 271236120Sraj err = fsl_ehci_detach(self); 272236120Sraj if (err) { 273236120Sraj device_printf(self, 274236120Sraj "Detach of the driver failed with error %d\n", 275236120Sraj err); 276236120Sraj } 277236120Sraj return (ENXIO); 278236120Sraj } 279236120Sraj 280236120Sraj /* Set little-endian tag for use by the generic EHCI driver */ 281236120Sraj sc->sc_io_tag = &bs_le_tag; 282236120Sraj 283236120Sraj /* Allocate irq */ 284236120Sraj sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 285236120Sraj RF_ACTIVE); 286236120Sraj if (sc->sc_irq_res == NULL) { 287236120Sraj err = fsl_ehci_detach(self); 288236120Sraj if (err) { 289236120Sraj device_printf(self, 290236120Sraj "Detach of the driver failed with error %d\n", 291236120Sraj err); 292236120Sraj } 293236120Sraj return (ENXIO); 294236120Sraj } 295236120Sraj 296236120Sraj /* Setup interrupt handler */ 297297579Smmel err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 298236120Sraj NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 299236120Sraj if (err) { 300236120Sraj device_printf(self, "Could not setup irq, %d\n", err); 301236120Sraj sc->sc_intr_hdl = NULL; 302236120Sraj err = fsl_ehci_detach(self); 303236120Sraj if (err) { 304236120Sraj device_printf(self, 305236120Sraj "Detach of the driver failed with error %d\n", 306236120Sraj err); 307236120Sraj } 308236120Sraj return (ENXIO); 309236120Sraj } 310236120Sraj 311236120Sraj /* Add USB device */ 312236120Sraj sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 313236120Sraj if (!sc->sc_bus.bdev) { 314236120Sraj device_printf(self, "Could not add USB device\n"); 315236120Sraj err = fsl_ehci_detach(self); 316236120Sraj if (err) { 317236120Sraj device_printf(self, 318236120Sraj "Detach of the driver failed with error %d\n", 319236120Sraj err); 320236120Sraj } 321236120Sraj return (ENOMEM); 322236120Sraj } 323236120Sraj device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 324236120Sraj 325236120Sraj sc->sc_id_vendor = 0x1234; 326236120Sraj strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor)); 327236120Sraj 328236120Sraj /* Enable USB */ 329236120Sraj err = ehci_reset(sc); 330236120Sraj if (err) { 331236120Sraj device_printf(self, "Could not reset the controller\n"); 332236120Sraj err = fsl_ehci_detach(self); 333236120Sraj if (err) { 334236120Sraj device_printf(self, 335236120Sraj "Detach of the driver failed with error %d\n", 336236120Sraj err); 337236120Sraj } 338236120Sraj return (ENXIO); 339236120Sraj } 340236120Sraj 341236120Sraj enable_usb(self, iot, ioh); 342236120Sraj set_snooping(iot, ioh); 343236120Sraj set_to_host_mode(sc); 344236120Sraj set_32b_prefetch(iot, ioh); 345236120Sraj 346236120Sraj /* 347236120Sraj * If usb subsystem is enabled in U-Boot, port power has to be turned 348236120Sraj * off to allow proper discovery of devices during boot up. 349236120Sraj */ 350236120Sraj clear_port_power(sc); 351236120Sraj 352236120Sraj /* Set flags */ 353236120Sraj sc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM; 354236120Sraj 355236120Sraj err = ehci_init(sc); 356236120Sraj if (!err) { 357236120Sraj sc->sc_flags |= EHCI_SCFLG_DONEINIT; 358236120Sraj err = device_probe_and_attach(sc->sc_bus.bdev); 359236120Sraj } 360236120Sraj 361236120Sraj if (err) { 362236120Sraj device_printf(self, "USB init failed err=%d\n", err); 363236120Sraj err = fsl_ehci_detach(self); 364236120Sraj if (err) { 365236120Sraj device_printf(self, 366236120Sraj "Detach of the driver failed with error %d\n", 367236120Sraj err); 368236120Sraj } 369236120Sraj return (EIO); 370236120Sraj } 371236120Sraj 372236120Sraj return (0); 373236120Sraj} 374236120Sraj 375236120Srajstatic int 376236120Srajfsl_ehci_detach(device_t self) 377236120Sraj{ 378236120Sraj 379236120Sraj int err; 380236120Sraj ehci_softc_t *sc; 381236120Sraj 382236120Sraj sc = device_get_softc(self); 383236120Sraj /* 384236120Sraj * only call ehci_detach() after ehci_init() 385236120Sraj */ 386236120Sraj if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { 387236120Sraj ehci_detach(sc); 388236120Sraj sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 389236120Sraj } 390236120Sraj 391236120Sraj /* Disable interrupts that might have been switched on in ehci_init */ 392236120Sraj if (sc->sc_io_tag && sc->sc_io_hdl) 393236120Sraj bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); 394236120Sraj 395236120Sraj if (sc->sc_irq_res && sc->sc_intr_hdl) { 396236120Sraj err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 397236120Sraj if (err) { 398236120Sraj device_printf(self, "Could not tear down irq, %d\n", 399236120Sraj err); 400236120Sraj return (err); 401236120Sraj } 402236120Sraj sc->sc_intr_hdl = NULL; 403236120Sraj } 404236120Sraj 405236120Sraj if (sc->sc_bus.bdev) { 406236120Sraj device_delete_child(self, sc->sc_bus.bdev); 407236120Sraj sc->sc_bus.bdev = NULL; 408236120Sraj } 409236120Sraj 410236120Sraj /* During module unload there are lots of children leftover */ 411236120Sraj device_delete_children(self); 412236120Sraj 413236120Sraj if (sc->sc_irq_res) { 414236120Sraj bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 415236120Sraj sc->sc_irq_res = NULL; 416236120Sraj } 417236120Sraj 418236120Sraj if (sc->sc_io_res) { 419236120Sraj bus_release_resource(self, SYS_RES_MEMORY, 0, sc->sc_io_res); 420236120Sraj sc->sc_io_res = NULL; 421236120Sraj sc->sc_io_tag = 0; 422236120Sraj sc->sc_io_hdl = 0; 423236120Sraj } 424236120Sraj 425236120Sraj return (0); 426236120Sraj} 427236120Sraj 428