From 4e2e8ee5fd2e52ce1ae029435beebca8085485f3 Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Wed, 25 Feb 2015 17:39:42 -0800 Subject: [PATCH] broadcom/cygnus: add usb phy driver The code originates from https://github.com/Broadcom/cygnus-linux/commit/d0752a61273decb16db0fe8d09291f9cc326ed24. BUG=chrome-os-partner:37439 BRANCH=purin TEST=booted kernel from a usb stick on the ref board Change-Id: I51ecf4e1d6890e4286402c26721f4d063ab04711 Signed-off-by: Patrick Georgi Original-Commit-Id: fac506e758cb63a947bbdcfbddf9b8edecf7cd2f Original-Signed-off-by: Daisuke Nojiri Original-Reviewed-on: https://chrome-internal-review.googlesource.com/202386 Original-Reviewed-by: Scott Branden Original-Commit-Queue: Daisuke Nojiri Original-Tested-by: Daisuke Nojiri Original-Change-Id: I027affea293af8744c997a2ed3dec741977bd328 Original-Reviewed-on: https://chromium-review.googlesource.com/264560 Original-Reviewed-by: Stefan Reinauer Reviewed-on: http://review.coreboot.org/9918 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/soc/broadcom/cygnus/Makefile.inc | 1 + src/soc/broadcom/cygnus/include/soc/cygnus.h | 26 +++ src/soc/broadcom/cygnus/soc.c | 2 + src/soc/broadcom/cygnus/usb.c | 166 +++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 src/soc/broadcom/cygnus/include/soc/cygnus.h create mode 100644 src/soc/broadcom/cygnus/usb.c diff --git a/src/soc/broadcom/cygnus/Makefile.inc b/src/soc/broadcom/cygnus/Makefile.inc index dce4e3d0b1..e22d38dc31 100644 --- a/src/soc/broadcom/cygnus/Makefile.inc +++ b/src/soc/broadcom/cygnus/Makefile.inc @@ -54,6 +54,7 @@ ramstage-y += soc.c ramstage-y += timer.c ramstage-$(CONFIG_SPI_FLASH) += spi.c ramstage-$(CONFIG_DRIVERS_UART) += ns16550.c +ramstage-y += usb.c CPPFLAGS_common += -Isrc/soc/broadcom/cygnus/include/ diff --git a/src/soc/broadcom/cygnus/include/soc/cygnus.h b/src/soc/broadcom/cygnus/include/soc/cygnus.h new file mode 100644 index 0000000000..8eb7dff910 --- /dev/null +++ b/src/soc/broadcom/cygnus/include/soc/cygnus.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_BROADCOM_CYGNUS_H__ +#define __SOC_BROADCOM_CYGNUS_H__ + +void usb_init(void); + +#endif + diff --git a/src/soc/broadcom/cygnus/soc.c b/src/soc/broadcom/cygnus/soc.c index c9597f2b08..399409f797 100644 --- a/src/soc/broadcom/cygnus/soc.c +++ b/src/soc/broadcom/cygnus/soc.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,7 @@ static void soc_init(device_t dev) { ram_resource(dev, 0, (uintptr_t)_dram/KiB, sdram_size_mb()*(MiB/KiB)); + usb_init(); } static void soc_noop(device_t dev) diff --git a/src/soc/broadcom/cygnus/usb.c b/src/soc/broadcom/cygnus/usb.c new file mode 100644 index 0000000000..d95efd1a87 --- /dev/null +++ b/src/soc/broadcom/cygnus/usb.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#define CDRU_USBPHY_CLK_RST_SEL_OFFSET 0x11b4 +#define CDRU_USBPHY2_HOST_DEV_SEL_OFFSET 0x11b8 +#define CDRU_SPARE_REG_0_OFFSET 0x1238 +#define CRMU_USB_PHY_AON_CTRL_OFFSET 0x00028 +#define CDRU_USB_DEV_SUSPEND_RESUME_CTRL_OFFSET 0x1210 +#define CDRU_USBPHY_P2_STATUS_OFFSET 0x1200 + +#define CDRU_USB_DEV_SUSPEND_RESUME_CTRL_DISABLE 0 +#define PHY2_DEV_HOST_CTRL_SEL_DEVICE 0 +#define PHY2_DEV_HOST_CTRL_SEL_HOST 1 +#define CDRU_USBPHY_P2_STATUS__USBPHY_ILDO_ON_FLAG 1 +#define CDRU_USBPHY_P2_STATUS__USBPHY_PLL_LOCK 0 +#define CRMU_USBPHY_P0_AFE_CORERDY_VDDC 1 +#define CRMU_USBPHY_P0_RESETB 2 +#define CRMU_USBPHY_P1_AFE_CORERDY_VDDC 9 +#define CRMU_USBPHY_P1_RESETB 10 +#define CRMU_USBPHY_P2_AFE_CORERDY_VDDC 17 +#define CRMU_USBPHY_P2_RESETB 18 + +#define USB2_IDM_IDM_IO_CONTROL_DIRECT_OFFSET 0x0408 +#define USB2_IDM_IDM_IO_CONTROL_DIRECT__clk_enable 0 +#define USB2_IDM_IDM_RESET_CONTROL_OFFSET 0x0800 +#define USB2_IDM_IDM_RESET_CONTROL__RESET 0 + +#define PLL_LOCK_RETRY_COUNT 1000 +#define MAX_REGULATOR_NAME_LEN 25 +#define NUM_PHYS 3 + +struct bcm_phy_instance { + struct phy *generic_phy; + int port; + int host_mode; /* 1 - Host , 0 - device */ + int power; /* 1 -powered_on 0 -powered off */ + struct regulator *vbus_supply; +}; + +struct bcm_phy_driver { + void *usbphy_regs; + void *usb2h_idm_regs; + void *usb2d_idm_regs; + int num_phys, idm_host_enabled; + struct bcm_phy_instance instances[NUM_PHYS]; +}; + +static struct bcm_phy_driver phy_driver; + +static int bcm_phy_init(struct bcm_phy_instance *instance_ptr) +{ + /* Only PORT 2 is capabale of being device and host + * Default setting is device, check if it is set to host */ + if (instance_ptr->port == 2) { + if (instance_ptr->host_mode == PHY2_DEV_HOST_CTRL_SEL_HOST) + write32(phy_driver.usbphy_regs + CDRU_USBPHY2_HOST_DEV_SEL_OFFSET, + PHY2_DEV_HOST_CTRL_SEL_HOST); + else + die("usb device mode unsupported\n"); + } + + return 0; +} + +static int bcm_phy_poweron(struct bcm_phy_instance *instance_ptr) +{ + int clock_reset_flag = 1; + u32 val; + + /* Bring the AFE block out of reset to start powering up the PHY */ + val = read32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET); + if (instance_ptr->port == 0) + val |= (1 << CRMU_USBPHY_P0_AFE_CORERDY_VDDC); + else if (instance_ptr->port == 1) + val |= (1 << CRMU_USBPHY_P1_AFE_CORERDY_VDDC); + else if (instance_ptr->port == 2) + val |= (1 << CRMU_USBPHY_P2_AFE_CORERDY_VDDC); + write32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET, val); + + instance_ptr->power = 1; + + /* Check if the port 2 is configured for device */ + if (instance_ptr->port == 2 && + instance_ptr->host_mode == PHY2_DEV_HOST_CTRL_SEL_DEVICE) + die("usb device mode unsupported\n"); + + val = read32(phy_driver.usbphy_regs + CDRU_USBPHY_CLK_RST_SEL_OFFSET); + + /* Check if the phy that is configured + * to provide clock and reset is powered on*/ + if (val >= 0 && val < phy_driver.num_phys) { + if (phy_driver.instances[val].power == 1) + clock_reset_flag = 0; + } + + /* if not set the current phy */ + if (clock_reset_flag) { + val = instance_ptr->port; + write32(phy_driver.usbphy_regs + CDRU_USBPHY_CLK_RST_SEL_OFFSET, + val); + } + + if (phy_driver.idm_host_enabled != 1) { + /* Enable clock to USB and get the USB out of reset */ + setbits_le32(phy_driver.usb2h_idm_regs + + USB2_IDM_IDM_IO_CONTROL_DIRECT_OFFSET, + (1 << USB2_IDM_IDM_IO_CONTROL_DIRECT__clk_enable)); + clrbits_le32(phy_driver.usb2h_idm_regs + + USB2_IDM_IDM_RESET_CONTROL_OFFSET, + (1 << USB2_IDM_IDM_RESET_CONTROL__RESET)); + phy_driver.idm_host_enabled = 1; + } + + return 0; +} + +static int bcm_phy_probe(void) +{ + int i; + + phy_driver.num_phys = NUM_PHYS; + phy_driver.usbphy_regs = (void *)0x0301c000; + phy_driver.usb2h_idm_regs = (void *)0x18115000; + phy_driver.usb2d_idm_regs = (void *)0x18111000; + phy_driver.idm_host_enabled = 0; + + /* Shutdown all ports. They can be powered up as required */ + clrbits_le32(phy_driver.usbphy_regs + CRMU_USB_PHY_AON_CTRL_OFFSET, + (1 << CRMU_USBPHY_P0_AFE_CORERDY_VDDC) | + (1 << CRMU_USBPHY_P0_RESETB) | + (1 << CRMU_USBPHY_P1_AFE_CORERDY_VDDC) | + (1 << CRMU_USBPHY_P1_RESETB) | + (1 << CRMU_USBPHY_P2_AFE_CORERDY_VDDC) | + (1 << CRMU_USBPHY_P2_RESETB)); + + for (i = 0; i < phy_driver.num_phys; i++) { + phy_driver.instances[i].port = i; + phy_driver.instances[i].host_mode = PHY2_DEV_HOST_CTRL_SEL_HOST; + } + + return 0; +} + +void usb_init(void) +{ + bcm_phy_probe(); + /* currently, we only need thus support port 0 */ + bcm_phy_init(&phy_driver.instances[0]); + bcm_phy_poweron(&phy_driver.instances[0]); + printk(BIOS_INFO, "usb phy[%d] is powered on\n", 0); +}