blob: ba8196047e7eeb40229e2709d2c272fb091bde2a [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <platsupport/clock.h>
#include <platsupport/gpio.h>
#include <platsupport/plat/gpio.h>
#include <platsupport/mach/pmic.h>
#include <platsupport/plat/sysreg.h>
#include <usb/usb_host.h>
#include "../../ehci/ehci.h"
#include "../../services.h"
#include "../usb_otg.h"
#include <stdio.h>
#include <stddef.h>
#include <usb/drivers/usb3503_hub.h>
#define USB2_HOST_CTRL_PADDR 0x12130000
#define USB2_HOST_CTRL_SIZE 0x1000
#define USB2_HOST_EHCI_PADDR 0x12110000
#define USB2_HOST_EHCI_SIZE 0x1000
#define USB2_HOST_OHCI_PADDR 0x12120000
#define USB2_HOST_OHCI_SIZE 0x1000
#define USB2_DEV_LINK_PADDR 0x12140000
#define USB2_DEV_LINK_SIZE 0x1000
#define USB2_HOST_IRQ 103
#define USBPHY_CTRL0_OFFSET 0x000
#define USBPHY_CTRL1_OFFSET 0x010
#define USBPHY_CTRL2_OFFSET 0x020
#define USBPHY_EHCI_OFFSET 0x030
#define USBPHY_CTRL_RESETALL BIT(31)
#define USBPHY_CTRL0_SRCSEL(x) (((x) & 0x03) << 19)
#define USBPHY_CTRLX_SRCSEL(x) (((x) & 0x03) << 23)
#define USBPHY_CTRL0_FREQ_SEL(x) (((x) & 0x07) << 16)
#define USBPHY_CTRLX_FREQ_SEL(x) (((x) & 0x7F) << 16)
#define USBPHY_CTRL_UTMI_RESET BIT(2)
#define USBPHY_CTRL_LINK_RESET BIT(1)
#define USBPHY_CTRL_PHY_RESET BIT(0)
#define USBPHY_CTRL_RESET ( USBPHY_CTRL_UTMI_RESET | \
USBPHY_CTRL_LINK_RESET | \
USBPHY_CTRL_PHY_RESET | \
USBPHY_CTRL_RESETALL )
#define USBPHY_EHCI_ENABLE (BIT(29) | BIT(28) | BIT(27) | BIT(26))
#define REG32(base, offset) (volatile uint32_t*)((void*)(base) + (offset))
#define PHYREG32(base, o) REG32(base, USBPHY_##o##_OFFSET)
#define NRESET_GPIO XEINT12
#define HUBCONNECT_GPIO XEINT6
#define NINT_GPIO XEINT7
static volatile void *_phy_regs = NULL;
static sysreg_t _sysreg;
/* EHCI registers */
static void *_usb_regs = NULL;
static const int _usb_irqs[] = {
[USB_HOST0] = USB2_HOST_IRQ
};
/* GPIO subsystem for bit-bangined I2C and HUB control */
static gpio_sys_t gpio_sys;
/* HUB and PMIC on I2C4 */
static struct i2c_bb i2c_bb;
static i2c_bus_t i2c_bus;
/* Eth power control */
static pmic_t pmic;
/* Hub control */
static usb3503_t usb3503_hub;
static int usb_init_phy(ps_io_ops_t *io_ops)
{
/* Map phy regs */
if (_phy_regs == NULL) {
_phy_regs = GET_RESOURCE(io_ops, USB2_HOST_CTRL);
}
exynos5_sysreg_init(io_ops, &_sysreg);
exynos5_sysreg_usbphy_enable(USBPHY_USB2, &_sysreg);
/* Hold in reset */
*PHYREG32(_phy_regs, CTRL0) |= USBPHY_CTRL_RESET;
*PHYREG32(_phy_regs, CTRL1) |= USBPHY_CTRL_RESET;
*PHYREG32(_phy_regs, CTRL2) |= USBPHY_CTRL_RESET;
ps_udelay(10);
/* Setup clocks and enable PHY */
*PHYREG32(_phy_regs, CTRL0) = USBPHY_CTRL0_FREQ_SEL(0x05) | USBPHY_CTRL0_SRCSEL(2) | BIT(10);
*PHYREG32(_phy_regs, CTRL1) = USBPHY_CTRLX_FREQ_SEL(0x24) | USBPHY_CTRLX_SRCSEL(2);
*PHYREG32(_phy_regs, CTRL2) = USBPHY_CTRLX_FREQ_SEL(0x24) | USBPHY_CTRLX_SRCSEL(2);
/* Enable the EHCI controller */
*PHYREG32(_phy_regs, EHCI) |= USBPHY_EHCI_ENABLE;
ps_udelay(40);
return 0;
}
/*******************************************/
static void hub_pwren(int state)
{
if (state) {
usb3503_connect(&usb3503_hub);
} else {
usb3503_disconnect(&usb3503_hub);
}
}
static void eth_pwren(int state)
{
if (state) {
pmic_ldo_cfg(&pmic, LDO_ETH, LDO_ON, 3300);
ps_mdelay(40);
} else {
pmic_ldo_cfg(&pmic, LDO_ETH, LDO_OFF, 3300);
}
}
static void board_pwren(int port, int state)
{
switch (port) {
case 1:
/* USB2 */
break;
case 2:
/* HSIC Ethernet */
eth_pwren(state);
break;
case 3:
/* USB hub */
hub_pwren(state);
break;
default:
ZF_LOGF("Invalid port for power change\n");
}
}
static int usb_plat_gpio_init(ps_io_ops_t *io_ops)
{
int err;
err = gpio_sys_init(io_ops, &gpio_sys);
if (err) {
ZF_LOGF("GPIO error\n");
}
err = i2c_bb_init(&gpio_sys, GPIOID(GPA2, 1), GPIOID(GPA2, 0), &i2c_bb, &i2c_bus);
if (err) {
ZF_LOGF("I2C error\n");
}
/* USB hub */
err = usb3503_init(&i2c_bus, &gpio_sys, NRESET_GPIO, HUBCONNECT_GPIO,
NINT_GPIO, &usb3503_hub);
if (err) {
ZF_LOGF("Hub3503 error\n");
}
/* PMIC for ethernet power change */
err = pmic_init(&i2c_bus, PMIC_BUSADDR, &pmic);
if (err) {
ZF_LOGF("PMIC error\n");
}
/* Turn off the eth chip */
eth_pwren(0);
return err;
}
int usb_host_init(enum usb_host_id id, ps_io_ops_t *io_ops, ps_mutex_ops_t *sync,
usb_host_t *hdev)
{
int err;
if (id < 0 || id > USB_NHOSTS) {
return -1;
}
if (!io_ops || !hdev) {
ZF_LOGF("Invalid arguments\n");
}
hdev->id = id;
hdev->dman = &io_ops->dma_manager;
hdev->sync = sync;
/* Check device mappings */
if (_usb_regs == NULL) {
_usb_regs = GET_RESOURCE(io_ops, USB2_HOST_EHCI);
}
if (_usb_regs == NULL) {
return -1;
}
/* Initialise GPIOs */
if (usb_plat_gpio_init(io_ops)) {
return -1;
}
/* Initialise the phy */
if (usb_init_phy(io_ops)) {
ZF_LOGE("PHY transceiver error");
return -1;
}
err = ehci_host_init(hdev, (uintptr_t)_usb_regs, &board_pwren);
return err;
}
const int *usb_host_irqs(usb_host_t *host, int *nirqs)
{
if (host->id < 0 || host->id > USB_NHOSTS) {
return NULL;
}
if (nirqs) {
*nirqs = 1;
}
host->irqs = &_usb_irqs[host->id];
return host->irqs;
}
int usb_plat_otg_init(usb_otg_t odev, ps_io_ops_t *io_ops)
{
return -1;
}