/** * \brief EHCI for OMAP4 * \author Sebastian Sumpf * \date 2012-06-20 */ /* * Copyright (C) 2012-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #include #include #include #include #include #include using namespace Genode; /** * Base addresses */ enum { EHCI_BASE = 0x4a064c00, UHH_BASE = 0x4a064000, TLL_BASE = 0x4a062000, SCRM_BASE = 0x4a30a000, CAM_BASE = 0x4a009000, /* used for L3INIT_CM2 */ }; /** * Inerrupt numbers */ enum { IRQ_GIC_START = 32, IRQ_EHCI = IRQ_GIC_START + 77, }; /** * Resources for platform device */ static resource _ehci[] = { { EHCI_BASE, EHCI_BASE + 0x400 - 1, "ehci", IORESOURCE_MEM }, { IRQ_EHCI, IRQ_EHCI, "ehci-irq", IORESOURCE_IRQ }, }; /** * Port informations for platform device */ static struct ehci_hcd_omap_platform_data _ehci_data; /** * Enables USB clocks */ struct Clocks : Genode::Mmio { Clocks(Genode::addr_t const mmio_base) : Mmio(mmio_base) { write(0x101); write(0x1008002); write(0x1); } struct Usb_host_clk : Register<0x358, 32> { }; struct Usb_tll_clk : Register<0x368, 32> { }; struct Usb_phy_clk : Register<0x3e0, 32> { }; template void update(unsigned val) { typename T::access_t access = read(); access |= val; write(access); }; void dump() { Usb_host_clk::access_t a1 = read(); Usb_tll_clk::access_t a3 = read(); Usb_phy_clk::access_t a4 = read(); } }; /** * Panda board reference USB clock */ struct Aux3 : Genode::Mmio { Aux3(addr_t const mmio_base) : Mmio(mmio_base) { enable(); } /* the clock register */ struct Aux3_clk : Register<0x31c, 32> { struct Src_select : Bitfield<1, 2> { }; struct Div : Bitfield<16, 4> { enum { DIV_2 = 1 }; }; struct Enable : Bitfield<8, 1> { enum { ON = 1 }; }; }; /* clock source register */ struct Aux_src : Register<0x110, 32, true> { }; void enable() { /* select system clock */ write(0); /* set to 19.2 Mhz */ write(Aux3_clk::Div::DIV_2); /* enable clock */ write(Aux3_clk::Enable::ON); /* enable_ext = 1 | enable_int = 1| mode = 0x01 */ write(0xd); } }; /** * ULPI transceiverless link */ struct Tll : Genode::Mmio { Tll(addr_t const mmio_base) : Mmio(mmio_base) { reset(); } struct Sys_config : Register<0x10, 32> { struct Soft_reset : Bitfield<1, 1> { }; struct Cactivity : Bitfield<8, 1> { }; struct Sidle_mode : Bitfield<3, 2> { }; struct Ena_wakeup : Bitfield<2, 1> { }; }; struct Sys_status : Register<0x14, 32> { }; void reset() { write(0x0); /* reset */ write(0x1); while(!read()) msleep(1); /* disable IDLE, enable wake up, enable auto gating */ write(1); write(1); write(1); } }; /** * USB high-speed host */ struct Uhh : Genode::Mmio { Uhh(addr_t const mmio_base) : Mmio(mmio_base) { /* diable idle and standby */ write(1); write(1); /* set ports to external phy */ write(0); write(0); } struct Sys_config : Register<0x10, 32> { struct Idle : Bitfield<2, 2> { }; struct Standby : Bitfield<4, 2> { }; }; struct Host_config : Register<0x40, 32> { struct P1_mode : Bitfield<16, 2> { }; struct P2_mode : Bitfield<18, 2> { }; }; }; /** * EHCI controller */ struct Ehci : Genode::Mmio { Ehci(addr_t const mmio_base) : Mmio(mmio_base) { write(0); /* reset */ write(1); while(read()) msleep(1); } struct Cmd : Register<0x10, 32> { struct Reset : Bitfield<1, 1> { }; }; }; /** * Panda board GPIO bases 1 - 6 */ static addr_t omap44xx_gpio_base[] = { 0x4A310000, 0x48055000, 0x48057000, 0x48059000, 0x4805B000, 0x4805D000 }; /** * General purpose I/O */ struct Gpio { enum { GPIO = 6 }; addr_t _io[GPIO]; Io_mem_session_capability _cap[GPIO]; void map() { for (int i = 0; i < GPIO; i++) { Io_mem_connection io(omap44xx_gpio_base[i], 0x1000); io.on_destruction(Io_mem_connection::KEEP_OPEN); _io[i] = (addr_t)env()->rm_session()->attach(io.dataspace()); _cap[i] = io.cap(); } } Gpio() { map(); } ~Gpio() { for (int i = 0; i < GPIO; i++) { env()->rm_session()->detach(_io[i]); env()->parent()->close(_cap[i]); } } addr_t base(unsigned gpio) { return _io[gpio >> 5]; } int index(unsigned gpio) { return gpio & 0x1f; } void _set_data_out(addr_t base, unsigned gpio, bool enable) { enum { SETDATAOUT = 0x194, CLEARDATAOUT = 0x190 }; writel(1U << gpio, base + (enable ? SETDATAOUT : CLEARDATAOUT)); } void _set_gpio_direction(addr_t base, unsigned gpio, bool input) { enum { OE = 0x134 }; base += OE; u32 val = readl(base); if (input) val |= 1U << gpio; else val &= ~(1U << gpio); writel(val, base); } void direction_output(unsigned gpio, bool enable) { _set_data_out(base(gpio), index(gpio), enable); _set_gpio_direction(base(gpio), index(gpio), false); } void direction_input(unsigned gpio) { _set_gpio_direction(base(gpio), index(gpio), true); } void set_value(unsigned gpio, int val) { _set_data_out(base(gpio), index(gpio), val); } unsigned get_value(int gpio) { enum { DATAIN = 0x138 }; return (readl(base(gpio) + DATAIN) & (1 << index(gpio))) != 0; } }; /** * Initialize the USB controller from scratch, since the boot loader might not * do it or even disable USB. */ static void omap_ehci_init() { /* taken from the Panda board manual */ enum { HUB_POWER = 1, HUB_NRESET = 62, ULPI_PHY_TYPE = 182 }; /* SCRM */ Io_mem_connection io_scrm(SCRM_BASE, 0x1000); addr_t scrm_base = (addr_t)env()->rm_session()->attach(io_scrm.dataspace()); /* enable reference clock */ Aux3 aux3(scrm_base); /* init GPIO */ Gpio gpio; /* disable the hub power and reset before init */ gpio.direction_output(HUB_POWER, false); gpio.direction_output(HUB_NRESET, false); gpio.set_value(HUB_POWER, 0); gpio.set_value(HUB_NRESET, 1); /* enable clocks */ Io_mem_connection io_clock(CAM_BASE, 0x1000); addr_t clock_base = (addr_t)env()->rm_session()->attach(io_clock.dataspace()); Clocks c(clock_base); /* reset TLL */ Io_mem_connection io_tll(TLL_BASE, 0x1000); addr_t tll_base = (addr_t)env()->rm_session()->attach(io_tll.dataspace()); Tll t(tll_base); /* reset host */ Io_mem_connection io_uhh(UHH_BASE, 0x1000); addr_t uhh_base = (addr_t)env()->rm_session()->attach(io_uhh.dataspace()); Uhh uhh(uhh_base); /* enable hub power */ gpio.set_value(HUB_POWER, 1); /* reset EHCI */ addr_t ehci_base = uhh_base + 0xc00; Ehci ehci(ehci_base); addr_t base[] = { scrm_base, clock_base, tll_base, uhh_base, 0 }; for (int i = 0; base[i]; i++) env()->rm_session()->detach(base[i]); } extern "C" void module_ehci_hcd_init(); extern "C" int module_usbnet_init(); extern "C" int module_smsc95xx_driver_init(); void platform_hcd_init(Services *services) { if (!services->ehci) return; /* register network */ if (services->nic) { module_usbnet_init(); module_smsc95xx_driver_init(); } /* register EHCI controller */ module_ehci_hcd_init(); /* initialize EHCI */ omap_ehci_init(); /* setup EHCI-controller platform device */ platform_device *pdev = (platform_device *)kzalloc(sizeof(platform_device), 0); pdev->name = (char *)"ehci-omap"; pdev->id = 0; pdev->num_resources = 2; pdev->resource = _ehci; _ehci_data.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY; _ehci_data.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED; _ehci_data.phy_reset = 0; pdev->dev.platform_data = &_ehci_data; /* * Needed for DMA buffer allocation. See 'hcd_buffer_alloc' in 'buffer.c' */ static u64 dma_mask = ~(u64)0; pdev->dev.dma_mask = &dma_mask; pdev->dev.coherent_dma_mask = ~0; platform_device_register(pdev); }