Path: blob/main/sys/arm/freescale/vybrid/vf_ehci.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2013 Ruslan Bukin <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728/*29* Vybrid Family Universal Serial Bus (USB) Controller30* Chapter 44-45, Vybrid Reference Manual, Rev. 5, 07/201331*/3233#include <sys/cdefs.h>34#include "opt_bus.h"3536#include <sys/param.h>37#include <sys/systm.h>38#include <sys/kernel.h>39#include <sys/module.h>40#include <sys/bus.h>41#include <sys/condvar.h>42#include <sys/rman.h>43#include <sys/gpio.h>4445#include <dev/ofw/ofw_bus.h>46#include <dev/ofw/ofw_bus_subr.h>4748#include <dev/usb/usb.h>49#include <dev/usb/usbdi.h>50#include <dev/usb/usb_busdma.h>51#include <dev/usb/usb_process.h>52#include <dev/usb/usb_controller.h>53#include <dev/usb/usb_bus.h>54#include <dev/usb/controller/ehci.h>55#include <dev/usb/controller/ehcireg.h>5657#include <machine/bus.h>58#include <machine/resource.h>5960#include "gpio_if.h"61#include "opt_platform.h"6263#define ENUTMILEVEL3 (1 << 15)64#define ENUTMILEVEL2 (1 << 14)6566#define GPIO_USB_PWR 1346768#define USB_ID 0x000 /* Identification register */69#define USB_HWGENERAL 0x004 /* Hardware General */70#define USB_HWHOST 0x008 /* Host Hardware Parameters */71#define USB_HWDEVICE 0x00C /* Device Hardware Parameters */72#define USB_HWTXBUF 0x010 /* TX Buffer Hardware Parameters */73#define USB_HWRXBUF 0x014 /* RX Buffer Hardware Parameters */74#define USB_HCSPARAMS 0x104 /* Host Controller Structural Parameters */7576#define USBPHY_PWD 0x00 /* PHY Power-Down Register */77#define USBPHY_PWD_SET 0x04 /* PHY Power-Down Register */78#define USBPHY_PWD_CLR 0x08 /* PHY Power-Down Register */79#define USBPHY_PWD_TOG 0x0C /* PHY Power-Down Register */80#define USBPHY_TX 0x10 /* PHY Transmitter Control Register */81#define USBPHY_RX 0x20 /* PHY Receiver Control Register */82#define USBPHY_RX_SET 0x24 /* PHY Receiver Control Register */83#define USBPHY_RX_CLR 0x28 /* PHY Receiver Control Register */84#define USBPHY_RX_TOG 0x2C /* PHY Receiver Control Register */85#define USBPHY_CTRL 0x30 /* PHY General Control Register */86#define USBPHY_CTRL_SET 0x34 /* PHY General Control Register */87#define USBPHY_CTRL_CLR 0x38 /* PHY General Control Register */88#define USBPHY_CTRL_TOG 0x3C /* PHY General Control Register */89#define USBPHY_STATUS 0x40 /* PHY Status Register */90#define USBPHY_DEBUG 0x50 /* PHY Debug Register */91#define USBPHY_DEBUG_SET 0x54 /* PHY Debug Register */92#define USBPHY_DEBUG_CLR 0x58 /* PHY Debug Register */93#define USBPHY_DEBUG_TOG 0x5C /* PHY Debug Register */94#define USBPHY_DEBUG0_STATUS 0x60 /* UTMI Debug Status Register 0 */95#define USBPHY_DEBUG1 0x70 /* UTMI Debug Status Register 1 */96#define USBPHY_DEBUG1_SET 0x74 /* UTMI Debug Status Register 1 */97#define USBPHY_DEBUG1_CLR 0x78 /* UTMI Debug Status Register 1 */98#define USBPHY_DEBUG1_TOG 0x7C /* UTMI Debug Status Register 1 */99#define USBPHY_VERSION 0x80 /* UTMI RTL Version */100#define USBPHY_IP 0x90 /* PHY IP Block Register */101#define USBPHY_IP_SET 0x94 /* PHY IP Block Register */102#define USBPHY_IP_CLR 0x98 /* PHY IP Block Register */103#define USBPHY_IP_TOG 0x9C /* PHY IP Block Register */104105#define USBPHY_CTRL_SFTRST (1U << 31)106#define USBPHY_CTRL_CLKGATE (1 << 30)107#define USBPHY_DEBUG_CLKGATE (1 << 30)108109#define PHY_READ4(_sc, _reg) \110bus_space_read_4(_sc->bst_phy, _sc->bsh_phy, _reg)111#define PHY_WRITE4(_sc, _reg, _val) \112bus_space_write_4(_sc->bst_phy, _sc->bsh_phy, _reg, _val)113114#define USBC_READ4(_sc, _reg) \115bus_space_read_4(_sc->bst_usbc, _sc->bsh_usbc, _reg)116#define USBC_WRITE4(_sc, _reg, _val) \117bus_space_write_4(_sc->bst_usbc, _sc->bsh_usbc, _reg, _val)118119/* Forward declarations */120static int vybrid_ehci_attach(device_t dev);121static int vybrid_ehci_detach(device_t dev);122static int vybrid_ehci_probe(device_t dev);123124struct vybrid_ehci_softc {125ehci_softc_t base;126device_t dev;127struct resource *res[6];128bus_space_tag_t bst_phy;129bus_space_handle_t bsh_phy;130bus_space_tag_t bst_usbc;131bus_space_handle_t bsh_usbc;132};133134static struct resource_spec vybrid_ehci_spec[] = {135{ SYS_RES_MEMORY, 0, RF_ACTIVE },136{ SYS_RES_MEMORY, 1, RF_ACTIVE },137{ SYS_RES_MEMORY, 2, RF_ACTIVE },138{ SYS_RES_IRQ, 0, RF_ACTIVE },139{ -1, 0 }140};141142static device_method_t ehci_methods[] = {143/* Device interface */144DEVMETHOD(device_probe, vybrid_ehci_probe),145DEVMETHOD(device_attach, vybrid_ehci_attach),146DEVMETHOD(device_detach, vybrid_ehci_detach),147DEVMETHOD(device_suspend, bus_generic_suspend),148DEVMETHOD(device_resume, bus_generic_resume),149DEVMETHOD(device_shutdown, bus_generic_shutdown),150151/* Bus interface */152DEVMETHOD(bus_print_child, bus_generic_print_child),153{ 0, 0 }154};155156/* kobj_class definition */157static driver_t ehci_driver = {158"ehci",159ehci_methods,160sizeof(ehci_softc_t)161};162163DRIVER_MODULE(vybrid_ehci, simplebus, ehci_driver, 0, 0);164MODULE_DEPEND(vybrid_ehci, usb, 1, 1, 1);165166static void167vybrid_ehci_post_reset(struct ehci_softc *ehci_softc)168{169uint32_t usbmode;170171/* Force HOST mode */172usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);173usbmode &= ~EHCI_UM_CM;174usbmode |= EHCI_UM_CM_HOST;175EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);176}177178/*179* Public methods180*/181static int182vybrid_ehci_probe(device_t dev)183{184185if (!ofw_bus_status_okay(dev))186return (ENXIO);187188if (ofw_bus_is_compatible(dev, "fsl,mvf600-usb-ehci") == 0)189return (ENXIO);190191device_set_desc(dev, "Vybrid Family integrated USB controller");192return (BUS_PROBE_DEFAULT);193}194195static int196phy_init(struct vybrid_ehci_softc *esc)197{198device_t sc_gpio_dev;199int reg;200201/* Reset phy */202reg = PHY_READ4(esc, USBPHY_CTRL);203reg |= (USBPHY_CTRL_SFTRST);204PHY_WRITE4(esc, USBPHY_CTRL, reg);205206/* Minimum reset time */207DELAY(10000);208209reg &= ~(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE);210PHY_WRITE4(esc, USBPHY_CTRL, reg);211212reg = (ENUTMILEVEL2 | ENUTMILEVEL3);213PHY_WRITE4(esc, USBPHY_CTRL_SET, reg);214215/* Get the GPIO device, we need this to give power to USB */216sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0);217if (sc_gpio_dev == NULL) {218device_printf(esc->dev, "Error: failed to get the GPIO dev\n");219return (1);220}221222/* Give power to USB */223GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_OUTPUT);224GPIO_PIN_SET(sc_gpio_dev, GPIO_USB_PWR, GPIO_PIN_HIGH);225226/* Power up PHY */227PHY_WRITE4(esc, USBPHY_PWD, 0x00);228229/* Ungate clocks */230reg = PHY_READ4(esc, USBPHY_DEBUG);231reg &= ~(USBPHY_DEBUG_CLKGATE);232PHY_WRITE4(esc, USBPHY_DEBUG, reg);233234#if 0235printf("USBPHY_CTRL == 0x%08x\n",236PHY_READ4(esc, USBPHY_CTRL));237printf("USBPHY_IP == 0x%08x\n",238PHY_READ4(esc, USBPHY_IP));239printf("USBPHY_STATUS == 0x%08x\n",240PHY_READ4(esc, USBPHY_STATUS));241printf("USBPHY_DEBUG == 0x%08x\n",242PHY_READ4(esc, USBPHY_DEBUG));243printf("USBPHY_DEBUG0_STATUS == 0x%08x\n",244PHY_READ4(esc, USBPHY_DEBUG0_STATUS));245printf("USBPHY_DEBUG1 == 0x%08x\n",246PHY_READ4(esc, USBPHY_DEBUG1));247#endif248249return (0);250}251252static int253vybrid_ehci_attach(device_t dev)254{255struct vybrid_ehci_softc *esc;256ehci_softc_t *sc;257bus_space_handle_t bsh;258int err;259int reg;260261esc = device_get_softc(dev);262esc->dev = dev;263264sc = &esc->base;265sc->sc_bus.parent = dev;266sc->sc_bus.devices = sc->sc_devices;267sc->sc_bus.devices_max = EHCI_MAX_DEVICES;268sc->sc_bus.dma_bits = 32;269270if (bus_alloc_resources(dev, vybrid_ehci_spec, esc->res)) {271device_printf(dev, "could not allocate resources\n");272return (ENXIO);273}274275/* EHCI registers */276sc->sc_io_tag = rman_get_bustag(esc->res[0]);277bsh = rman_get_bushandle(esc->res[0]);278sc->sc_io_size = rman_get_size(esc->res[0]);279280esc->bst_usbc = rman_get_bustag(esc->res[1]);281esc->bsh_usbc = rman_get_bushandle(esc->res[1]);282283esc->bst_phy = rman_get_bustag(esc->res[2]);284esc->bsh_phy = rman_get_bushandle(esc->res[2]);285286/* get all DMA memory */287if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),288&ehci_iterate_hw_softc))289return (ENXIO);290291#if 0292printf("USBx_HCSPARAMS is 0x%08x\n",293bus_space_read_4(sc->sc_io_tag, bsh, USB_HCSPARAMS));294printf("USB_ID == 0x%08x\n",295bus_space_read_4(sc->sc_io_tag, bsh, USB_ID));296printf("USB_HWGENERAL == 0x%08x\n",297bus_space_read_4(sc->sc_io_tag, bsh, USB_HWGENERAL));298printf("USB_HWHOST == 0x%08x\n",299bus_space_read_4(sc->sc_io_tag, bsh, USB_HWHOST));300printf("USB_HWDEVICE == 0x%08x\n",301bus_space_read_4(sc->sc_io_tag, bsh, USB_HWDEVICE));302printf("USB_HWTXBUF == 0x%08x\n",303bus_space_read_4(sc->sc_io_tag, bsh, USB_HWTXBUF));304printf("USB_HWRXBUF == 0x%08x\n",305bus_space_read_4(sc->sc_io_tag, bsh, USB_HWRXBUF));306#endif307308if (phy_init(esc)) {309device_printf(dev, "Could not setup PHY\n");310return (1);311}312313/*314* Set handle to USB related registers subregion used by315* generic EHCI driver.316*/317err = bus_space_subregion(sc->sc_io_tag, bsh, 0x100,318sc->sc_io_size, &sc->sc_io_hdl);319if (err != 0)320return (ENXIO);321322/* Setup interrupt handler */323err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE,324NULL, (driver_intr_t *)ehci_interrupt, sc,325&sc->sc_intr_hdl);326if (err) {327device_printf(dev, "Could not setup irq, "328"%d\n", err);329return (1);330}331332/* Add USB device */333sc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY);334if (!sc->sc_bus.bdev) {335device_printf(dev, "Could not add USB device\n");336err = bus_teardown_intr(dev, esc->res[5],337sc->sc_intr_hdl);338if (err)339device_printf(dev, "Could not tear down irq,"340" %d\n", err);341return (1);342}343device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);344345strlcpy(sc->sc_vendor, "Freescale", sizeof(sc->sc_vendor));346347/* Set host mode */348reg = bus_space_read_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8);349reg |= 0x3;350bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8, reg);351352/* Set flags and callbacks*/353sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM;354sc->sc_vendor_post_reset = vybrid_ehci_post_reset;355sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;356357err = ehci_init(sc);358if (!err) {359sc->sc_flags |= EHCI_SCFLG_DONEINIT;360err = device_probe_and_attach(sc->sc_bus.bdev);361} else {362device_printf(dev, "USB init failed err=%d\n", err);363364device_delete_child(dev, sc->sc_bus.bdev);365sc->sc_bus.bdev = NULL;366367err = bus_teardown_intr(dev, esc->res[5],368sc->sc_intr_hdl);369if (err)370device_printf(dev, "Could not tear down irq,"371" %d\n", err);372return (1);373}374return (0);375}376377static int378vybrid_ehci_detach(device_t dev)379{380struct vybrid_ehci_softc *esc;381ehci_softc_t *sc;382int err;383384esc = device_get_softc(dev);385sc = &esc->base;386387/* First detach all children; we can't detach if that fails. */388if ((err = bus_generic_detach(dev)) != 0)389return (err);390391/*392* only call ehci_detach() after ehci_init()393*/394if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {395ehci_detach(sc);396sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;397}398399/*400* Disable interrupts that might have been switched on in401* ehci_init.402*/403if (sc->sc_io_tag && sc->sc_io_hdl)404bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,405EHCI_USBINTR, 0);406407if (esc->res[5] && sc->sc_intr_hdl) {408err = bus_teardown_intr(dev, esc->res[5],409sc->sc_intr_hdl);410if (err) {411device_printf(dev, "Could not tear down irq,"412" %d\n", err);413return (err);414}415sc->sc_intr_hdl = NULL;416}417418usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);419420bus_release_resources(dev, vybrid_ehci_spec, esc->res);421422return (0);423}424425426