lpc_ohci.c revision 239278
1239278Sgonzo/*- 2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> 3239278Sgonzo * All rights reserved. 4239278Sgonzo * 5239278Sgonzo * Redistribution and use in source and binary forms, with or without 6239278Sgonzo * modification, are permitted provided that the following conditions 7239278Sgonzo * are met: 8239278Sgonzo * 1. Redistributions of source code must retain the above copyright 9239278Sgonzo * notice, this list of conditions and the following disclaimer. 10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11239278Sgonzo * notice, this list of conditions and the following disclaimer in the 12239278Sgonzo * documentation and/or other materials provided with the distribution. 13239278Sgonzo * 14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239278Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239278Sgonzo * SUCH DAMAGE. 25239278Sgonzo * 26239278Sgonzo */ 27239278Sgonzo#include <sys/cdefs.h> 28239278Sgonzo__FBSDID("$FreeBSD: head/sys/arm/lpc/lpc_ohci.c 239278 2012-08-15 05:37:10Z gonzo $"); 29239278Sgonzo 30239278Sgonzo#include <sys/stdint.h> 31239278Sgonzo#include <sys/stddef.h> 32239278Sgonzo#include <sys/param.h> 33239278Sgonzo#include <sys/queue.h> 34239278Sgonzo#include <sys/types.h> 35239278Sgonzo#include <sys/systm.h> 36239278Sgonzo#include <sys/kernel.h> 37239278Sgonzo#include <sys/bus.h> 38239278Sgonzo#include <sys/module.h> 39239278Sgonzo#include <sys/lock.h> 40239278Sgonzo#include <sys/mutex.h> 41239278Sgonzo#include <sys/condvar.h> 42239278Sgonzo#include <sys/sysctl.h> 43239278Sgonzo#include <sys/rman.h> 44239278Sgonzo#include <sys/sx.h> 45239278Sgonzo#include <sys/unistd.h> 46239278Sgonzo#include <sys/callout.h> 47239278Sgonzo#include <sys/malloc.h> 48239278Sgonzo#include <sys/priv.h> 49239278Sgonzo 50239278Sgonzo#include <sys/kdb.h> 51239278Sgonzo 52239278Sgonzo#include <dev/ofw/ofw_bus.h> 53239278Sgonzo#include <dev/ofw/ofw_bus_subr.h> 54239278Sgonzo 55239278Sgonzo#include <dev/usb/usb.h> 56239278Sgonzo#include <dev/usb/usbdi.h> 57239278Sgonzo 58239278Sgonzo#include <dev/usb/usb_core.h> 59239278Sgonzo#include <dev/usb/usb_busdma.h> 60239278Sgonzo#include <dev/usb/usb_process.h> 61239278Sgonzo#include <dev/usb/usb_util.h> 62239278Sgonzo 63239278Sgonzo#include <dev/usb/usb_controller.h> 64239278Sgonzo#include <dev/usb/usb_bus.h> 65239278Sgonzo#include <dev/usb/controller/ohci.h> 66239278Sgonzo#include <dev/usb/controller/ohcireg.h> 67239278Sgonzo 68239278Sgonzo#include <arm/lpc/lpcreg.h> 69239278Sgonzo#include <arm/lpc/lpcvar.h> 70239278Sgonzo 71239278Sgonzo#define I2C_START_BIT (1 << 8) 72239278Sgonzo#define I2C_STOP_BIT (1 << 9) 73239278Sgonzo#define I2C_READ 0x01 74239278Sgonzo#define I2C_WRITE 0x00 75239278Sgonzo#define DUMMY_BYTE 0x55 76239278Sgonzo 77239278Sgonzo#define lpc_otg_read_4(_sc, _reg) \ 78239278Sgonzo bus_space_read_4(_sc->sc_io_tag, _sc->sc_io_hdl, _reg) 79239278Sgonzo#define lpc_otg_write_4(_sc, _reg, _value) \ 80239278Sgonzo bus_space_write_4(_sc->sc_io_tag, _sc->sc_io_hdl, _reg, _value) 81239278Sgonzo#define lpc_otg_wait_write_4(_sc, _wreg, _sreg, _value) \ 82239278Sgonzo do { \ 83239278Sgonzo lpc_otg_write_4(_sc, _wreg, _value); \ 84239278Sgonzo while ((lpc_otg_read_4(_sc, _sreg) & _value) != _value); \ 85239278Sgonzo } while (0); 86239278Sgonzo 87239278Sgonzostatic int lpc_ohci_probe(device_t dev); 88239278Sgonzostatic int lpc_ohci_attach(device_t dev); 89239278Sgonzostatic int lpc_ohci_detach(device_t dev); 90239278Sgonzo 91239278Sgonzostatic void lpc_otg_i2c_reset(struct ohci_softc *); 92239278Sgonzo 93239278Sgonzostatic int lpc_isp3101_read(struct ohci_softc *, int); 94239278Sgonzostatic void lpc_isp3101_write(struct ohci_softc *, int, int); 95239278Sgonzostatic void lpc_isp3101_clear(struct ohci_softc *, int, int); 96239278Sgonzostatic void lpc_isp3101_configure(device_t dev, struct ohci_softc *); 97239278Sgonzo 98239278Sgonzostatic int 99239278Sgonzolpc_ohci_probe(device_t dev) 100239278Sgonzo{ 101239278Sgonzo if (!ofw_bus_is_compatible(dev, "lpc,usb-ohci")) 102239278Sgonzo return (ENXIO); 103239278Sgonzo 104239278Sgonzo device_set_desc(dev, "LPC32x0 USB OHCI controller"); 105239278Sgonzo return (BUS_PROBE_DEFAULT); 106239278Sgonzo} 107239278Sgonzo 108239278Sgonzostatic int 109239278Sgonzolpc_ohci_attach(device_t dev) 110239278Sgonzo{ 111239278Sgonzo struct ohci_softc *sc = device_get_softc(dev); 112239278Sgonzo int err; 113239278Sgonzo int rid; 114239278Sgonzo int i = 0; 115239278Sgonzo uint32_t usbctrl; 116239278Sgonzo uint32_t otgstatus; 117239278Sgonzo 118239278Sgonzo sc->sc_bus.parent = dev; 119239278Sgonzo sc->sc_bus.devices = sc->sc_devices; 120239278Sgonzo sc->sc_bus.devices_max = OHCI_MAX_DEVICES; 121239278Sgonzo 122239278Sgonzo if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 123239278Sgonzo &ohci_iterate_hw_softc)) 124239278Sgonzo return (ENOMEM); 125239278Sgonzo 126239278Sgonzo rid = 0; 127239278Sgonzo sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 128239278Sgonzo if (!sc->sc_io_res) { 129239278Sgonzo device_printf(dev, "cannot map OHCI register space\n"); 130239278Sgonzo goto fail; 131239278Sgonzo } 132239278Sgonzo 133239278Sgonzo sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 134239278Sgonzo sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 135239278Sgonzo sc->sc_io_size = rman_get_size(sc->sc_io_res); 136239278Sgonzo 137239278Sgonzo rid = 0; 138239278Sgonzo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 139239278Sgonzo if (sc->sc_irq_res == NULL) { 140239278Sgonzo device_printf(dev, "cannot allocate interrupt\n"); 141239278Sgonzo goto fail; 142239278Sgonzo } 143239278Sgonzo 144239278Sgonzo sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 145239278Sgonzo if (!(sc->sc_bus.bdev)) 146239278Sgonzo goto fail; 147239278Sgonzo 148239278Sgonzo device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 149239278Sgonzo strlcpy(sc->sc_vendor, "NXP", sizeof(sc->sc_vendor)); 150239278Sgonzo 151239278Sgonzo err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 152239278Sgonzo NULL, (void *)ohci_interrupt, sc, &sc->sc_intr_hdl); 153239278Sgonzo if (err) { 154239278Sgonzo sc->sc_intr_hdl = NULL; 155239278Sgonzo goto fail; 156239278Sgonzo } 157239278Sgonzo 158239278Sgonzo usbctrl = lpc_pwr_read(dev, LPC_CLKPWR_USB_CTRL); 159239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_SLAVE_HCLK | LPC_CLKPWR_USB_CTRL_BUSKEEPER; 160239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); 161239278Sgonzo 162239278Sgonzo /* Enable OTG I2C clock */ 163239278Sgonzo lpc_otg_wait_write_4(sc, LPC_OTG_CLOCK_CTRL, 164239278Sgonzo LPC_OTG_CLOCK_STATUS, LPC_OTG_CLOCK_CTRL_I2C_EN); 165239278Sgonzo 166239278Sgonzo /* Reset OTG I2C bus */ 167239278Sgonzo lpc_otg_i2c_reset(sc); 168239278Sgonzo 169239278Sgonzo lpc_isp3101_configure(dev, sc); 170239278Sgonzo 171239278Sgonzo /* Configure PLL */ 172239278Sgonzo usbctrl &= ~(LPC_CLKPWR_USB_CTRL_CLK_EN1 | LPC_CLKPWR_USB_CTRL_CLK_EN2); 173239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); 174239278Sgonzo 175239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_CLK_EN1; 176239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); 177239278Sgonzo 178239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_FDBKDIV(192-1); 179239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_POSTDIV(1); 180239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_PLL_PDOWN; 181239278Sgonzo 182239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); 183239278Sgonzo do { 184239278Sgonzo usbctrl = lpc_pwr_read(dev, LPC_CLKPWR_USB_CTRL); 185239278Sgonzo if (i++ > 100000) { 186239278Sgonzo device_printf(dev, "USB OTG PLL doesn't lock!\n"); 187239278Sgonzo goto fail; 188239278Sgonzo } 189239278Sgonzo } while ((usbctrl & LPC_CLKPWR_USB_CTRL_PLL_LOCK) == 0); 190239278Sgonzo 191239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_CLK_EN2; 192239278Sgonzo usbctrl |= LPC_CLKPWR_USB_CTRL_HOST_NEED_CLK_EN; 193239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_USB_CTRL, usbctrl); 194239278Sgonzo lpc_otg_wait_write_4(sc, LPC_OTG_CLOCK_CTRL, LPC_OTG_CLOCK_STATUS, 195239278Sgonzo (LPC_OTG_CLOCK_CTRL_AHB_EN | LPC_OTG_CLOCK_CTRL_OTG_EN | 196239278Sgonzo LPC_OTG_CLOCK_CTRL_I2C_EN | LPC_OTG_CLOCK_CTRL_HOST_EN)); 197239278Sgonzo 198239278Sgonzo otgstatus = lpc_otg_read_4(sc, LPC_OTG_STATUS); 199239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_STATUS, otgstatus | 200239278Sgonzo LPC_OTG_STATUS_HOST_EN); 201239278Sgonzo 202239278Sgonzo lpc_isp3101_write(sc, LPC_ISP3101_OTG_CONTROL_1, 203239278Sgonzo LPC_ISP3101_OTG1_VBUS_DRV); 204239278Sgonzo 205239278Sgonzo err = ohci_init(sc); 206239278Sgonzo if (err) 207239278Sgonzo goto fail; 208239278Sgonzo 209239278Sgonzo err = device_probe_and_attach(sc->sc_bus.bdev); 210239278Sgonzo if (err) 211239278Sgonzo goto fail; 212239278Sgonzo 213239278Sgonzo return (0); 214239278Sgonzo 215239278Sgonzofail: 216239278Sgonzo if (sc->sc_intr_hdl) 217239278Sgonzo bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); 218239278Sgonzo if (sc->sc_irq_res) 219239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 220239278Sgonzo if (sc->sc_io_res) 221239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); 222239278Sgonzo 223239278Sgonzo return (ENXIO); 224239278Sgonzo} 225239278Sgonzo 226239278Sgonzostatic int 227239278Sgonzolpc_isp3101_read(struct ohci_softc *sc, int reg) 228239278Sgonzo{ 229239278Sgonzo int status; 230239278Sgonzo int i = 0; 231239278Sgonzo 232239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, 233239278Sgonzo (LPC_ISP3101_I2C_ADDR << 1) | I2C_START_BIT); 234239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, reg); 235239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, (LPC_ISP3101_I2C_ADDR << 1) | 236239278Sgonzo I2C_START_BIT | I2C_READ); 237239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_TXRX, I2C_STOP_BIT | DUMMY_BYTE); 238239278Sgonzo 239239278Sgonzo do { 240239278Sgonzo status = lpc_otg_read_4(sc, LPC_OTG_I2C_STATUS); 241239278Sgonzo i++; 242239278Sgonzo } while ((status & LPC_OTG_I2C_STATUS_TDI) == 0 || i < 100000); 243239278Sgonzo 244239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_STATUS, LPC_OTG_I2C_STATUS_TDI); 245239278Sgonzo 246239278Sgonzo return (lpc_otg_read_4(sc, LPC_OTG_I2C_TXRX) & 0xff); 247239278Sgonzo} 248239278Sgonzo 249239278Sgonzostatic void 250239278Sgonzolpc_otg_i2c_reset(struct ohci_softc *sc) 251239278Sgonzo{ 252239278Sgonzo int ctrl; 253239278Sgonzo int i = 0; 254239278Sgonzo 255239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_CLKHI, 0x3f); 256239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_CLKLO, 0x3f); 257239278Sgonzo 258239278Sgonzo ctrl = lpc_otg_read_4(sc, LPC_OTG_I2C_CTRL); 259239278Sgonzo lpc_otg_write_4(sc, LPC_OTG_I2C_CTRL, ctrl | LPC_OTG_I2C_CTRL_SRST); 260239278Sgonzo 261239278Sgonzo do { 262239278Sgonzo ctrl = lpc_otg_read_4(sc, LPC_OTG_I2C_CTRL); 263239278Sgonzo i++; 264239278Sgonzo } while (ctrl & LPC_OTG_I2C_CTRL_SRST); 265239278Sgonzo} 266239278Sgonzo 267239278Sgonzostatic void 268239278Sgonzolpc_isp3101_write(struct ohci_softc *sc, int reg, int value) 269239278Sgonzo{ 270239278Sgonzo int status; 271239278Sgonzo int i = 0; 272239278Sgonzo 273239278Sgonzo bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, 274239278Sgonzo (LPC_ISP3101_I2C_ADDR << 1) | I2C_START_BIT); 275239278Sgonzo bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, 276239278Sgonzo (reg | I2C_WRITE)); 277239278Sgonzo bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_TXRX, 278239278Sgonzo (value | I2C_STOP_BIT)); 279239278Sgonzo 280239278Sgonzo do { 281239278Sgonzo status = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, 282239278Sgonzo LPC_OTG_I2C_STATUS); 283239278Sgonzo i++; 284239278Sgonzo } while ((status & LPC_OTG_I2C_STATUS_TDI) == 0 || i < 100000); 285239278Sgonzo 286239278Sgonzo bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, LPC_OTG_I2C_STATUS, 287239278Sgonzo LPC_OTG_I2C_STATUS_TDI); 288239278Sgonzo} 289239278Sgonzo 290239278Sgonzostatic __inline void 291239278Sgonzolpc_isp3101_clear(struct ohci_softc *sc, int reg, int value) 292239278Sgonzo{ 293239278Sgonzo lpc_isp3101_write(sc, (reg | LPC_ISP3101_REG_CLEAR_ADDR), value); 294239278Sgonzo} 295239278Sgonzo 296239278Sgonzostatic void 297239278Sgonzolpc_isp3101_configure(device_t dev, struct ohci_softc *sc) 298239278Sgonzo{ 299239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_UART_EN); 300239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_1, ~LPC_ISP3101_MC1_SPEED_REG); 301239278Sgonzo lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_SPEED_REG); 302239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_MODE_CONTROL_2, ~0); 303239278Sgonzo lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_2, 304239278Sgonzo (LPC_ISP3101_MC2_BI_DI | LPC_ISP3101_MC2_PSW_EN 305239278Sgonzo | LPC_ISP3101_MC2_SPD_SUSP_CTRL)); 306239278Sgonzo 307239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_OTG_CONTROL_1, ~0); 308239278Sgonzo lpc_isp3101_write(sc, LPC_ISP3101_MODE_CONTROL_1, LPC_ISP3101_MC1_DAT_SE0); 309239278Sgonzo lpc_isp3101_write(sc, LPC_ISP3101_OTG_CONTROL_1, 310239278Sgonzo (LPC_ISP3101_OTG1_DM_PULLDOWN | LPC_ISP3101_OTG1_DP_PULLDOWN)); 311239278Sgonzo 312239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_OTG_CONTROL_1, 313239278Sgonzo (LPC_ISP3101_OTG1_DM_PULLUP | LPC_ISP3101_OTG1_DP_PULLUP)); 314239278Sgonzo 315239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_LATCH, ~0); 316239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_FALLING, ~0); 317239278Sgonzo lpc_isp3101_clear(sc, LPC_ISP3101_OTG_INTR_RISING, ~0); 318239278Sgonzo 319239278Sgonzo device_printf(dev, 320239278Sgonzo "ISP3101 PHY <vendor:0x%04x, product:0x%04x, version:0x%04x>\n", 321239278Sgonzo (lpc_isp3101_read(sc, 0x00) | (lpc_isp3101_read(sc, 0x01) << 8)), 322239278Sgonzo (lpc_isp3101_read(sc, 0x03) | (lpc_isp3101_read(sc, 0x04) << 8)), 323239278Sgonzo (lpc_isp3101_read(sc, 0x14) | (lpc_isp3101_read(sc, 0x15) << 8))); 324239278Sgonzo} 325239278Sgonzo 326239278Sgonzostatic int 327239278Sgonzolpc_ohci_detach(device_t dev) 328239278Sgonzo{ 329239278Sgonzo return (0); 330239278Sgonzo} 331239278Sgonzo 332239278Sgonzo 333239278Sgonzostatic device_method_t lpc_ohci_methods[] = { 334239278Sgonzo /* Device interface */ 335239278Sgonzo DEVMETHOD(device_probe, lpc_ohci_probe), 336239278Sgonzo DEVMETHOD(device_attach, lpc_ohci_attach), 337239278Sgonzo DEVMETHOD(device_detach, lpc_ohci_detach), 338239278Sgonzo DEVMETHOD(device_shutdown, bus_generic_shutdown), 339239278Sgonzo 340239278Sgonzo /* Bus interface */ 341239278Sgonzo DEVMETHOD(bus_print_child, bus_generic_print_child), 342239278Sgonzo { 0, 0 } 343239278Sgonzo}; 344239278Sgonzo 345239278Sgonzostatic driver_t lpc_ohci_driver = { 346239278Sgonzo "ohci", 347239278Sgonzo lpc_ohci_methods, 348239278Sgonzo sizeof(struct ohci_softc), 349239278Sgonzo}; 350239278Sgonzo 351239278Sgonzostatic devclass_t lpc_ohci_devclass; 352239278Sgonzo 353239278SgonzoDRIVER_MODULE(ohci, simplebus, lpc_ohci_driver, lpc_ohci_devclass, 0, 0); 354239278SgonzoMODULE_DEPEND(ohci, usb, 1, 1, 1); 355