Changeset View
Changeset View
Standalone View
Standalone View
head/sys/arm/allwinner/a10_ehci.c
Show All 34 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/gpio.h> | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/usb/usb.h> | #include <dev/usb/usb.h> | ||||
#include <dev/usb/usbdi.h> | #include <dev/usb/usbdi.h> | ||||
#include <dev/usb/usb_core.h> | #include <dev/usb/usb_core.h> | ||||
#include <dev/usb/usb_busdma.h> | #include <dev/usb/usb_busdma.h> | ||||
#include <dev/usb/usb_process.h> | #include <dev/usb/usb_process.h> | ||||
#include <dev/usb/usb_util.h> | #include <dev/usb/usb_util.h> | ||||
#include <dev/usb/usb_controller.h> | #include <dev/usb/usb_controller.h> | ||||
#include <dev/usb/usb_bus.h> | #include <dev/usb/usb_bus.h> | ||||
#include <dev/usb/controller/ehci.h> | #include <dev/usb/controller/ehci.h> | ||||
#include <dev/usb/controller/ehcireg.h> | #include <dev/usb/controller/ehcireg.h> | ||||
#include "gpio_if.h" | #include <arm/allwinner/allwinner_machdep.h> | ||||
#include <arm/allwinner/a10_clk.h> | |||||
#include <arm/allwinner/a31/a31_clk.h> | |||||
#include "a10_clk.h" | |||||
#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" | #define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" | ||||
#define SW_USB_PMU_IRQ_ENABLE 0x800 | #define SW_USB_PMU_IRQ_ENABLE 0x800 | ||||
#define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) | #define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) | ||||
#define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) | #define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) | ||||
#define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) | #define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) | ||||
#define SW_ULPI_BYPASS (1 << 0) | #define SW_ULPI_BYPASS (1 << 0) | ||||
#define SW_AHB_INCRX_ALIGN (1 << 8) | #define SW_AHB_INCRX_ALIGN (1 << 8) | ||||
#define SW_AHB_INCR4 (1 << 9) | #define SW_AHB_INCR4 (1 << 9) | ||||
#define SW_AHB_INCR8 (1 << 10) | #define SW_AHB_INCR8 (1 << 10) | ||||
#define GPIO_USB1_PWR 230 | |||||
#define GPIO_USB2_PWR 227 | |||||
#define USB_CONF(d) \ | |||||
(void *)ofw_bus_search_compatible((d), compat_data)->ocd_data | |||||
#define A10_READ_4(sc, reg) \ | #define A10_READ_4(sc, reg) \ | ||||
bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) | bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) | ||||
#define A10_WRITE_4(sc, reg, data) \ | #define A10_WRITE_4(sc, reg, data) \ | ||||
bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) | bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) | ||||
static device_attach_t a10_ehci_attach; | static device_attach_t a10_ehci_attach; | ||||
static device_detach_t a10_ehci_detach; | static device_detach_t a10_ehci_detach; | ||||
bs_r_1_proto(reversed); | bs_r_1_proto(reversed); | ||||
bs_w_1_proto(reversed); | bs_w_1_proto(reversed); | ||||
struct aw_ehci_conf { | |||||
int (*clk_activate)(void); | |||||
int (*clk_deactivate)(void); | |||||
bool sdram_init; | |||||
}; | |||||
static const struct aw_ehci_conf a10_ehci_conf = { | |||||
#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) | |||||
.clk_activate = a10_clk_usb_activate, | |||||
.clk_deactivate = a10_clk_usb_deactivate, | |||||
#endif | |||||
.sdram_init = true, | |||||
}; | |||||
static const struct aw_ehci_conf a31_ehci_conf = { | |||||
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) | |||||
.clk_activate = a31_clk_ehci_activate, | |||||
.clk_deactivate = a31_clk_ehci_deactivate, | |||||
#endif | |||||
}; | |||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"allwinner,sun4i-a10-ehci", 1}, | { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, | ||||
{"allwinner,sun7i-a20-ehci", 1}, | { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, | ||||
{NULL, 0} | { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, | ||||
{ NULL, (uintptr_t)NULL } | |||||
}; | }; | ||||
static int | static int | ||||
a10_ehci_probe(device_t self) | a10_ehci_probe(device_t self) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(self)) | if (!ofw_bus_status_okay(self)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) | if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(self, EHCI_HC_DEVSTR); | device_set_desc(self, EHCI_HC_DEVSTR); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static int | static int | ||||
a10_ehci_attach(device_t self) | a10_ehci_attach(device_t self) | ||||
{ | { | ||||
ehci_softc_t *sc = device_get_softc(self); | ehci_softc_t *sc = device_get_softc(self); | ||||
const struct aw_ehci_conf *conf; | |||||
bus_space_handle_t bsh; | bus_space_handle_t bsh; | ||||
device_t sc_gpio_dev; | |||||
int err; | int err; | ||||
int rid; | int rid; | ||||
uint32_t reg_value = 0; | uint32_t reg_value = 0; | ||||
conf = USB_CONF(self); | |||||
if (conf->clk_activate == NULL) { | |||||
device_printf(self, "clock not supported\n"); | |||||
return (ENXIO); | |||||
} | |||||
/* initialise some bus fields */ | /* initialise some bus fields */ | ||||
sc->sc_bus.parent = self; | sc->sc_bus.parent = self; | ||||
sc->sc_bus.devices = sc->sc_devices; | sc->sc_bus.devices = sc->sc_devices; | ||||
sc->sc_bus.devices_max = EHCI_MAX_DEVICES; | sc->sc_bus.devices_max = EHCI_MAX_DEVICES; | ||||
sc->sc_bus.dma_bits = 32; | sc->sc_bus.dma_bits = 32; | ||||
/* get all DMA memory */ | /* get all DMA memory */ | ||||
if (usb_bus_mem_alloc_all(&sc->sc_bus, | if (usb_bus_mem_alloc_all(&sc->sc_bus, | ||||
Show All 33 Lines | if (!sc->sc_bus.bdev) { | ||||
device_printf(self, "Could not add USB device\n"); | device_printf(self, "Could not add USB device\n"); | ||||
goto error; | goto error; | ||||
} | } | ||||
device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); | device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); | ||||
device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); | device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); | ||||
sprintf(sc->sc_vendor, "Allwinner"); | sprintf(sc->sc_vendor, "Allwinner"); | ||||
/* Get the GPIO device, we need this to give power to USB */ | |||||
sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); | |||||
if (sc_gpio_dev == NULL) { | |||||
device_printf(self, "Error: failed to get the GPIO device\n"); | |||||
goto error; | |||||
} | |||||
err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, | err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, | ||||
NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); | NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); | ||||
if (err) { | if (err) { | ||||
device_printf(self, "Could not setup irq, %d\n", err); | device_printf(self, "Could not setup irq, %d\n", err); | ||||
sc->sc_intr_hdl = NULL; | sc->sc_intr_hdl = NULL; | ||||
goto error; | goto error; | ||||
} | } | ||||
sc->sc_flags |= EHCI_SCFLG_DONTRESET; | sc->sc_flags |= EHCI_SCFLG_DONTRESET; | ||||
/* Enable clock for USB */ | /* Enable clock for USB */ | ||||
a10_clk_usb_activate(); | if (conf->clk_activate() != 0) { | ||||
device_printf(self, "Could not activate clock\n"); | |||||
goto error; | |||||
} | |||||
/* Give power to USB */ | |||||
GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); | |||||
GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); | |||||
/* Give power to USB */ | |||||
GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); | |||||
GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); | |||||
/* Enable passby */ | /* Enable passby */ | ||||
reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); | reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); | ||||
reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ | reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ | ||||
reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ | reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ | ||||
reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ | reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ | ||||
reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ | reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ | ||||
A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); | A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); | ||||
/* Configure port */ | /* Configure port */ | ||||
if (conf->sdram_init) { | |||||
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); | reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); | ||||
reg_value |= SW_SDRAM_BP_HPCR_ACCESS; | reg_value |= SW_SDRAM_BP_HPCR_ACCESS; | ||||
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); | A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); | ||||
} | |||||
err = ehci_init(sc); | err = ehci_init(sc); | ||||
if (!err) { | if (!err) { | ||||
err = device_probe_and_attach(sc->sc_bus.bdev); | err = device_probe_and_attach(sc->sc_bus.bdev); | ||||
} | } | ||||
if (err) { | if (err) { | ||||
device_printf(self, "USB init failed err=%d\n", err); | device_printf(self, "USB init failed err=%d\n", err); | ||||
goto error; | goto error; | ||||
} | } | ||||
return (0); | return (0); | ||||
error: | error: | ||||
a10_ehci_detach(self); | a10_ehci_detach(self); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
static int | static int | ||||
a10_ehci_detach(device_t self) | a10_ehci_detach(device_t self) | ||||
{ | { | ||||
ehci_softc_t *sc = device_get_softc(self); | ehci_softc_t *sc = device_get_softc(self); | ||||
const struct aw_ehci_conf *conf; | |||||
device_t bdev; | device_t bdev; | ||||
int err; | int err; | ||||
uint32_t reg_value = 0; | uint32_t reg_value = 0; | ||||
conf = USB_CONF(self); | |||||
if (sc->sc_bus.bdev) { | if (sc->sc_bus.bdev) { | ||||
bdev = sc->sc_bus.bdev; | bdev = sc->sc_bus.bdev; | ||||
device_detach(bdev); | device_detach(bdev); | ||||
device_delete_child(self, bdev); | device_delete_child(self, bdev); | ||||
} | } | ||||
/* during module unload there are lots of children leftover */ | /* during module unload there are lots of children leftover */ | ||||
device_delete_children(self); | device_delete_children(self); | ||||
Show All 19 Lines | a10_ehci_detach(device_t self) | ||||
if (sc->sc_io_res) { | if (sc->sc_io_res) { | ||||
bus_release_resource(self, SYS_RES_MEMORY, 0, | bus_release_resource(self, SYS_RES_MEMORY, 0, | ||||
sc->sc_io_res); | sc->sc_io_res); | ||||
sc->sc_io_res = NULL; | sc->sc_io_res = NULL; | ||||
} | } | ||||
usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); | usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); | ||||
/* Disable configure port */ | /* Disable configure port */ | ||||
if (conf->sdram_init) { | |||||
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); | reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); | ||||
reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; | reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; | ||||
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); | A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); | ||||
} | |||||
/* Disable passby */ | /* Disable passby */ | ||||
reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); | reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); | ||||
reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ | reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ | ||||
reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ | reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ | ||||
reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ | reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ | ||||
reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ | reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ | ||||
A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); | A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); | ||||
/* Disable clock for USB */ | /* Disable clock for USB */ | ||||
a10_clk_usb_deactivate(); | conf->clk_deactivate(); | ||||
return (0); | return (0); | ||||
} | } | ||||
static device_method_t ehci_methods[] = { | static device_method_t ehci_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, a10_ehci_probe), | DEVMETHOD(device_probe, a10_ehci_probe), | ||||
DEVMETHOD(device_attach, a10_ehci_attach), | DEVMETHOD(device_attach, a10_ehci_attach), | ||||
Show All 18 Lines |