[U-Boot] [PATCH 00/17] sunxi: musb otg controller support

Hi Marek, Ian, et al,
I'm very happy to present this patch series, which is the last pieze of the puzzle to make it possible for people to use u-boot on sunxi devices without needing to resort to putting a soldering iron to their tablet :)
With this series sunxi tablet owners can simply plug in a usb keyboard into the otg port of their tablet (how boring).
Interestingly enough getting the musb code to work on sunxi was quite east, most of my time went into fixing generic usb / musb issues, which should help on other boards too :)
This entire series is based on u-boot-sunxi/next and for some of the sunxi specific patches there will likely be conflicts when applied directly to master, therefor I would like to suggest the following scheme for getting this upstream:
1) Add patches 1-5 to u-boot-sunxi/next (once reviewed) 2) Add patches 6-16 to u-boot-usb/next (once reviewed) 3) Get both trees pulled by Tom 4) Add patch 17 to u-boot-sunxi
As for reviewing, Marek can you review patches 3-16 please (so all those intended for u-boot-usb/next + patch 3-5), and Ian can you review patches 1-5 and 17 please ?
Thanks & Regards,
Hans

The voltage setting code knows it needs to call axp221_init before calling the various voltage setting functions.
But users of axp utility functions like axp221_get_sid() do not know this, so the utility functions always call axp221_init() to ensure that the p2wi / rsb setup magic has been done.
Since doing this repeatedly is quite expensive, add a check to axp221_init so that it only does the initialization once.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/power/axp221.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 1fda19a..728727b 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -304,9 +304,14 @@ int axp221_set_aldo3(unsigned int mvolt)
int axp221_init(void) { + /* This cannot be 0 because it is used in SPL before BSS is ready */ + static int needs_init = 1; u8 axp_chip_id; int ret;
+ if (!needs_init) + return 0; + ret = pmic_bus_init(); if (ret) return ret; @@ -318,6 +323,7 @@ int axp221_init(void) if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17)) return -ENODEV;
+ needs_init = 0; return 0; }

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
The voltage setting code knows it needs to call axp221_init before calling the various voltage setting functions.
But users of axp utility functions like axp221_get_sid() do not know this, so the utility functions always call axp221_init() to ensure that the p2wi / rsb setup magic has been done.
Since doing this repeatedly is quite expensive, add a check to axp221_init so that it only does the initialization once.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
drivers/power/axp221.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 1fda19a..728727b 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -304,9 +304,14 @@ int axp221_set_aldo3(unsigned int mvolt)
int axp221_init(void) {
/* This cannot be 0 because it is used in SPL before BSS is ready */
static int needs_init = 1; u8 axp_chip_id; int ret;
if (!needs_init)
return 0;
ret = pmic_bus_init(); if (ret) return ret;
@@ -318,6 +323,7 @@ int axp221_init(void) if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17)) return -ENODEV;
- needs_init = 0; return 0;
}

Hi,
On 13-01-15 19:49, Ian Campbell wrote:
On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
The voltage setting code knows it needs to call axp221_init before calling the various voltage setting functions.
But users of axp utility functions like axp221_get_sid() do not know this, so the utility functions always call axp221_init() to ensure that the p2wi / rsb setup magic has been done.
Since doing this repeatedly is quite expensive, add a check to axp221_init so that it only does the initialization once.
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk
Thanks for the reviews. I've queued up the first 5 patches of this series in u-boot-sunxi/next. I'll likely send a pull-req with everything we've in next later today.
Regards,
Hans
drivers/power/axp221.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 1fda19a..728727b 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -304,9 +304,14 @@ int axp221_set_aldo3(unsigned int mvolt)
int axp221_init(void) {
/* This cannot be 0 because it is used in SPL before BSS is ready */
static int needs_init = 1; u8 axp_chip_id; int ret;
if (!needs_init)
return 0;
ret = pmic_bus_init(); if (ret) return ret;
@@ -318,6 +323,7 @@ int axp221_init(void) if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17)) return -ENODEV;
- needs_init = 0; return 0; }

The axp221 / axp223's N_VBUSEN pin can be configured as an output rather then an input, add axp_drivebus_enable() and _disable() functions to set the pin in output mode and control it.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/power/axp221.c | 36 ++++++++++++++++++++++++++++++++++++ include/axp221.h | 9 +++++++++ 2 files changed, 45 insertions(+)
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 728727b..4c86f09 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -353,3 +353,39 @@ int axp221_get_sid(unsigned int *sid)
return 0; } + +static int axp_drivebus_setup(void) +{ + int ret; + + ret = axp221_init(); + if (ret) + return ret; + + /* Set N_VBUSEN pin to output / DRIVEBUS function */ + return axp221_clrbits(AXP221_MISC_CTRL, AXP221_MISC_CTRL_N_VBUSEN_FUNC); +} + +int axp_drivebus_enable(void) +{ + int ret; + + ret = axp_drivebus_setup(); + if (ret) + return ret; + + /* Set DRIVEBUS high */ + return axp221_setbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS); +} + +int axp_drivebus_disable(void) +{ + int ret; + + ret = axp_drivebus_setup(); + if (ret) + return ret; + + /* Set DRIVEBUS low */ + return axp221_clrbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS); +} diff --git a/include/axp221.h b/include/axp221.h index e9552f6..e6639f1 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -45,11 +45,18 @@ #define AXP221_ALDO1_CTRL 0x28 #define AXP221_ALDO2_CTRL 0x29 #define AXP221_ALDO3_CTRL 0x2a +#define AXP221_VBUS_IPSOUT 0x30 +#define AXP221_VBUS_IPSOUT_DRIVEBUS (1 << 2) +#define AXP221_MISC_CTRL 0x8f +#define AXP221_MISC_CTRL_N_VBUSEN_FUNC (1 << 4) #define AXP221_PAGE 0xff
/* Page 1 addresses */ #define AXP221_SID 0x20
+/* We support drivebus control */ +#define AXP_DRIVEBUS + int axp221_set_dcdc1(unsigned int mvolt); int axp221_set_dcdc2(unsigned int mvolt); int axp221_set_dcdc3(unsigned int mvolt); @@ -64,3 +71,5 @@ int axp221_set_aldo2(unsigned int mvolt); int axp221_set_aldo3(unsigned int mvolt); int axp221_init(void); int axp221_get_sid(unsigned int *sid); +int axp_drivebus_enable(void); +int axp_drivebus_disable(void);

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote: Acked-by: Ian Campbell ijc@hellion.org.uk

Most of the usb-controller init code found in ehci-sunxi.c also is necessary to init the otg usb controller, so move it to a common place.
While at it also update various #ifdefs / defines for sun8i support.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/Makefile | 1 + arch/arm/cpu/armv7/sunxi/usbc.c | 229 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 2 +- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 1 + arch/arm/include/asm/arch-sunxi/cpu.h | 4 +- arch/arm/include/asm/arch-sunxi/usbc.h | 20 +++ drivers/usb/host/ehci-sunxi.c | 213 ++---------------------- include/configs/sun8i.h | 5 + 8 files changed, 269 insertions(+), 206 deletions(-) create mode 100644 arch/arm/cpu/armv7/sunxi/usbc.c create mode 100644 arch/arm/include/asm/arch-sunxi/usbc.h
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 1e89937..1720f7d 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -12,6 +12,7 @@ obj-y += board.o obj-y += clock.o obj-y += cpu_info.o obj-y += pinmux.o +obj-y += usbc.o obj-$(CONFIG_MACH_SUN6I) += prcm.o obj-$(CONFIG_MACH_SUN8I) += prcm.o obj-$(CONFIG_MACH_SUN6I) += p2wi.o diff --git a/arch/arm/cpu/armv7/sunxi/usbc.c b/arch/arm/cpu/armv7/sunxi/usbc.c new file mode 100644 index 0000000..621992c --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/usbc.c @@ -0,0 +1,229 @@ +/* + * Sunxi usb-controller code shared between the ehci and musb controllers + * + * Copyright (C) 2014 Roman Byshko + * + * Roman Byshko rbyshko@gmail.com + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> +#include <asm/arch/usbc.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <common.h> + +#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 +#define SUNXI_USB_CSR 0x404 +#define SUNXI_USB_PASSBY_EN 1 + +#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) +#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) +#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) +#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) + +static struct sunxi_usbc_hcd { + struct usb_hcd *hcd; + int usb_rst_mask; + int ahb_clk_mask; + int gpio_vbus; + int irq; + int id; +} sunxi_usbc_hcd[] = { + { + .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + .irq = 72, +#else + .irq = 39, +#endif + .id = 1, + }, +#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) + { + .usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, +#ifdef CONFIG_MACH_SUN6I + .irq = 74, +#else + .irq = 40, +#endif + .id = 2, + } +#endif +}; + +static int enabled_hcd_count; + +void *sunxi_usbc_get_io_base(int index) +{ + switch (index) { + case 0: + return (void *)SUNXI_USB0_BASE; + case 1: + return (void *)SUNXI_USB1_BASE; + case 2: + return (void *)SUNXI_USB2_BASE; + default: + return NULL; + } +} + +static int get_vbus_gpio(int index) +{ + switch (index) { + case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); + case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN); + } + return -1; +} + +static void usb_phy_write(struct sunxi_usbc_hcd *sunxi_usbc, int addr, + int data, int len) +{ + int j = 0, usbc_bit = 0; + void *dest = sunxi_usbc_get_io_base(0) + SUNXI_USB_CSR; + + usbc_bit = 1 << (sunxi_usbc->id * 2); + for (j = 0; j < len; j++) { + /* set the bit address to be written */ + clrbits_le32(dest, 0xff << 8); + setbits_le32(dest, (addr + j) << 8); + + clrbits_le32(dest, usbc_bit); + /* set data bit */ + if (data & 0x1) + setbits_le32(dest, 1 << 7); + else + clrbits_le32(dest, 1 << 7); + + setbits_le32(dest, usbc_bit); + + clrbits_le32(dest, usbc_bit); + + data >>= 1; + } +} + +static void sunxi_usb_phy_init(struct sunxi_usbc_hcd *sunxi_usbc) +{ + /* The following comments are machine + * translated from Chinese, you have been warned! + */ + + /* adjust PHY's magnitude and rate */ + usb_phy_write(sunxi_usbc, 0x20, 0x14, 5); + + /* threshold adjustment disconnect */ +#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN6I + usb_phy_write(sunxi_usbc, 0x2a, 3, 2); +#else + usb_phy_write(sunxi_usbc, 0x2a, 2, 2); +#endif + + return; +} + +static void sunxi_usb_passby(struct sunxi_usbc_hcd *sunxi_usbc, int enable) +{ + unsigned long bits = 0; + void *addr = sunxi_usbc_get_io_base(sunxi_usbc->id) + + SUNXI_USB_PMU_IRQ_ENABLE; + + bits = SUNXI_EHCI_AHB_ICHR8_EN | + SUNXI_EHCI_AHB_INCR4_BURST_EN | + SUNXI_EHCI_AHB_INCRX_ALIGN_EN | + SUNXI_EHCI_ULPI_BYPASS_EN; + + if (enable) + setbits_le32(addr, bits); + else + clrbits_le32(addr, bits); + + return; +} + +int sunxi_usbc_request_resources(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + + sunxi_usbc->gpio_vbus = get_vbus_gpio(index); + if (sunxi_usbc->gpio_vbus != -1) + return gpio_request(sunxi_usbc->gpio_vbus, "usbc_vbus"); + + return 0; +} + +int sunxi_usbc_free_resources(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + + if (sunxi_usbc->gpio_vbus != -1) + return gpio_free(sunxi_usbc->gpio_vbus); + + return 0; +} + +void sunxi_usbc_enable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable common PHY only once */ + if (enabled_hcd_count == 0) + setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + setbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask); + setbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask); +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + setbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask); +#endif + + sunxi_usb_phy_init(sunxi_usbc); + + sunxi_usb_passby(sunxi_usbc, SUNXI_USB_PASSBY_EN); + + enabled_hcd_count++; +} + +void sunxi_usbc_disable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + sunxi_usb_passby(sunxi_usbc, !SUNXI_USB_PASSBY_EN); + +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask); +#endif + clrbits_le32(&ccm->ahb_gate0, sunxi_usbc->ahb_clk_mask); + clrbits_le32(&ccm->usb_clk_cfg, sunxi_usbc->usb_rst_mask); + + /* disable common PHY only once, for the last enabled hcd */ + if (enabled_hcd_count == 1) + clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + enabled_hcd_count--; +} + +void sunxi_usbc_vbus_enable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + + if (sunxi_usbc->gpio_vbus != -1) + gpio_direction_output(sunxi_usbc->gpio_vbus, 1); +} + +void sunxi_usbc_vbus_disable(int index) +{ + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + + if (sunxi_usbc->gpio_vbus != -1) + gpio_direction_output(sunxi_usbc->gpio_vbus, 0); +} diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 70b789e..a6d129c 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -182,7 +182,7 @@ struct sunxi_ccm_reg { #define AHB_GATE_OFFSET_USB_EHCI1 3 #define AHB_GATE_OFFSET_USB_OHCI0 2 #define AHB_GATE_OFFSET_USB_EHCI0 1 -#define AHB_GATE_OFFSET_USB 0 +#define AHB_GATE_OFFSET_USB0 0
/* ahb clock gate bit offset (second register) */ #define AHB_GATE_OFFSET_GMAC 17 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 653f63c..f85f94d 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -204,6 +204,7 @@ struct sunxi_ccm_reg { #define AHB_GATE_OFFSET_USB_OHCI0 29 #define AHB_GATE_OFFSET_USB_EHCI1 27 #define AHB_GATE_OFFSET_USB_EHCI0 26 +#define AHB_GATE_OFFSET_USB0 24 #define AHB_GATE_OFFSET_MCTL 14 #define AHB_GATE_OFFSET_GMAC 17 #define AHB_GATE_OFFSET_MMC3 11 diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index bcfa00d..82b3d46 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -37,7 +37,7 @@ #define SUNXI_MMC1_BASE 0x01c10000 #define SUNXI_MMC2_BASE 0x01c11000 #define SUNXI_MMC3_BASE 0x01c12000 -#ifndef CONFIG_MACH_SUN6I +#if !defined CONFIG_MACH_SUN6I && !defined CONFIG_MACH_SUN8I #define SUNXI_USB0_BASE 0x01c13000 #define SUNXI_USB1_BASE 0x01c14000 #endif @@ -45,7 +45,7 @@ #define SUNXI_HDMI_BASE 0x01c16000 #define SUNXI_SPI2_BASE 0x01c17000 #define SUNXI_SATA_BASE 0x01c18000 -#ifndef CONFIG_MACH_SUN6I +#if !defined CONFIG_MACH_SUN6I && !defined CONFIG_MACH_SUN8I #define SUNXI_PATA_BASE 0x01c19000 #define SUNXI_ACE_BASE 0x01c1a000 #define SUNXI_TVE1_BASE 0x01c1b000 diff --git a/arch/arm/include/asm/arch-sunxi/usbc.h b/arch/arm/include/asm/arch-sunxi/usbc.h new file mode 100644 index 0000000..8d20973 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/usbc.h @@ -0,0 +1,20 @@ +/* + * Sunxi usb-controller code shared between the ehci and musb controllers + * + * Copyright (C) 2014 Roman Byshko + * + * Roman Byshko rbyshko@gmail.com + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +void *sunxi_usbc_get_io_base(int index); +int sunxi_usbc_request_resources(int index); +int sunxi_usbc_free_resources(int index); +void sunxi_usbc_enable(int index); +void sunxi_usbc_disable(int index); +void sunxi_usbc_vbus_enable(int index); +void sunxi_usbc_vbus_disable(int index); diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index cc9a8fa..eda9f69 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -9,199 +9,23 @@ * SPDX-License-Identifier: GPL-2.0+ */
-#include <asm/arch/clock.h> -#include <asm/arch/cpu.h> -#include <asm/gpio.h> -#include <asm/io.h> +#include <asm/arch/usbc.h> #include <common.h> #include "ehci.h"
-#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 -#define SUNXI_USB_CSR 0x404 -#define SUNXI_USB_PASSBY_EN 1 - -#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) -#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) -#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) -#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) - -static struct sunxi_ehci_hcd { - struct usb_hcd *hcd; - int usb_rst_mask; - int ahb_clk_mask; - int gpio_vbus; - int irq; - int id; -} sunxi_echi_hcd[] = { - { - .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, - .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, -#ifndef CONFIG_MACH_SUN6I - .irq = 39, -#else - .irq = 72, -#endif - .id = 1, - }, -#if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) - { - .usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK, - .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1, -#ifndef CONFIG_MACH_SUN6I - .irq = 40, -#else - .irq = 74, -#endif - .id = 2, - } -#endif -}; - -static int enabled_hcd_count; - -static void *get_io_base(int hcd_id) -{ - switch (hcd_id) { - case 0: - return (void *)SUNXI_USB0_BASE; - case 1: - return (void *)SUNXI_USB1_BASE; - case 2: - return (void *)SUNXI_USB2_BASE; - default: - return NULL; - } -} - -static int get_vbus_gpio(int hcd_id) -{ - switch (hcd_id) { - case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); - case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN); - } - return -1; -} - -static void usb_phy_write(struct sunxi_ehci_hcd *sunxi_ehci, int addr, - int data, int len) -{ - int j = 0, usbc_bit = 0; - void *dest = get_io_base(0) + SUNXI_USB_CSR; - - usbc_bit = 1 << (sunxi_ehci->id * 2); - for (j = 0; j < len; j++) { - /* set the bit address to be written */ - clrbits_le32(dest, 0xff << 8); - setbits_le32(dest, (addr + j) << 8); - - clrbits_le32(dest, usbc_bit); - /* set data bit */ - if (data & 0x1) - setbits_le32(dest, 1 << 7); - else - clrbits_le32(dest, 1 << 7); - - setbits_le32(dest, usbc_bit); - - clrbits_le32(dest, usbc_bit); - - data >>= 1; - } -} - -static void sunxi_usb_phy_init(struct sunxi_ehci_hcd *sunxi_ehci) -{ - /* The following comments are machine - * translated from Chinese, you have been warned! - */ - - /* adjust PHY's magnitude and rate */ - usb_phy_write(sunxi_ehci, 0x20, 0x14, 5); - - /* threshold adjustment disconnect */ -#if defined CONFIG_MACH_SUN4I || defined CONFIG_MACH_SUN6I - usb_phy_write(sunxi_ehci, 0x2a, 3, 2); -#else - usb_phy_write(sunxi_ehci, 0x2a, 2, 2); -#endif - - return; -} - -static void sunxi_usb_passby(struct sunxi_ehci_hcd *sunxi_ehci, int enable) -{ - unsigned long bits = 0; - void *addr = get_io_base(sunxi_ehci->id) + SUNXI_USB_PMU_IRQ_ENABLE; - - bits = SUNXI_EHCI_AHB_ICHR8_EN | - SUNXI_EHCI_AHB_INCR4_BURST_EN | - SUNXI_EHCI_AHB_INCRX_ALIGN_EN | - SUNXI_EHCI_ULPI_BYPASS_EN; - - if (enable) - setbits_le32(addr, bits); - else - clrbits_le32(addr, bits); - - return; -} - -static void sunxi_ehci_enable(struct sunxi_ehci_hcd *sunxi_ehci) -{ - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - - setbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); - setbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); -#ifdef CONFIG_MACH_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, sunxi_ehci->ahb_clk_mask); -#endif - - sunxi_usb_phy_init(sunxi_ehci); - - sunxi_usb_passby(sunxi_ehci, SUNXI_USB_PASSBY_EN); - - if (sunxi_ehci->gpio_vbus != -1) - gpio_direction_output(sunxi_ehci->gpio_vbus, 1); -} - -static void sunxi_ehci_disable(struct sunxi_ehci_hcd *sunxi_ehci) -{ - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - - if (sunxi_ehci->gpio_vbus != -1) - gpio_direction_output(sunxi_ehci->gpio_vbus, 0); - - sunxi_usb_passby(sunxi_ehci, !SUNXI_USB_PASSBY_EN); - -#ifdef CONFIG_MACH_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_ehci->ahb_clk_mask); -#endif - clrbits_le32(&ccm->ahb_gate0, sunxi_ehci->ahb_clk_mask); - clrbits_le32(&ccm->usb_clk_cfg, sunxi_ehci->usb_rst_mask); -} - int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; int err;
- sunxi_ehci->gpio_vbus = get_vbus_gpio(sunxi_ehci->id); + err = sunxi_usbc_request_resources(index + 1); + if (err) + return err;
- /* enable common PHY only once */ - if (index == 0) - setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + sunxi_usbc_enable(index + 1); + sunxi_usbc_vbus_enable(index + 1);
- if (sunxi_ehci->gpio_vbus != -1) { - err = gpio_request(sunxi_ehci->gpio_vbus, "ehci_vbus"); - if (err) - return err; - } - - sunxi_ehci_enable(sunxi_ehci); - - *hccr = get_io_base(sunxi_ehci->id); + *hccr = sunxi_usbc_get_io_base(index + 1);
*hcor = (struct ehci_hcor *)((uint32_t) *hccr + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); @@ -210,30 +34,13 @@ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, (uint32_t)*hccr, (uint32_t)*hcor, (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
- enabled_hcd_count++; - return 0; }
int ehci_hcd_stop(int index) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - struct sunxi_ehci_hcd *sunxi_ehci = &sunxi_echi_hcd[index]; - int err; - - sunxi_ehci_disable(sunxi_ehci); - - if (sunxi_ehci->gpio_vbus != -1) { - err = gpio_free(sunxi_ehci->gpio_vbus); - if (err) - return err; - } + sunxi_usbc_vbus_disable(index + 1); + sunxi_usbc_disable(index + 1);
- /* disable common PHY only once, for the last enabled hcd */ - if (enabled_hcd_count == 1) - clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); - - enabled_hcd_count--; - - return 0; + return sunxi_usbc_free_resources(index + 1); } diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 792422d..f16e60b 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -16,6 +16,11 @@
#define CONFIG_SYS_PROMPT "sun8i# "
+#ifdef CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_SUNXI +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + /* * Include common sunxi configuration where most the settings are */

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
Most of the usb-controller init code found in ehci-sunxi.c also is necessary to init the otg usb controller, so move it to a common place.
While at it also update various #ifdefs / defines for sun8i support.
Signed-off-by: Hans de Goede hdegoede@redhat.com
On the assumption that the code motion is really just that: Acked-by: Ian Campbell ijc@hellion.org.uk
(I general prefer code motion patch to be just that, with precursor refactoring patch etc, but nevermind).

Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/usbc.c | 33 ++++++++++++++++++++------- arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 4 +++- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 ++ board/sunxi/Kconfig | 7 ++++++ 4 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/usbc.c b/arch/arm/cpu/armv7/sunxi/usbc.c index 621992c..4f11da5 100644 --- a/arch/arm/cpu/armv7/sunxi/usbc.c +++ b/arch/arm/cpu/armv7/sunxi/usbc.c @@ -36,6 +36,16 @@ static struct sunxi_usbc_hcd { int id; } sunxi_usbc_hcd[] = { { + .usb_rst_mask = CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK, + .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB0, +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I + .irq = 71, +#else + .irq = 38, +#endif + .id = 0, + }, + { .usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, .ahb_clk_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I @@ -78,6 +88,7 @@ void *sunxi_usbc_get_io_base(int index) static int get_vbus_gpio(int index) { switch (index) { + case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN); case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN); } @@ -117,6 +128,10 @@ static void sunxi_usb_phy_init(struct sunxi_usbc_hcd *sunxi_usbc) * translated from Chinese, you have been warned! */
+ /* Regulation 45 ohms */ + if (sunxi_usbc->id == 0) + usb_phy_write(sunxi_usbc, 0x0c, 0x01, 1); + /* adjust PHY's magnitude and rate */ usb_phy_write(sunxi_usbc, 0x20, 0x14, 5);
@@ -151,7 +166,7 @@ static void sunxi_usb_passby(struct sunxi_usbc_hcd *sunxi_usbc, int enable)
int sunxi_usbc_request_resources(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
sunxi_usbc->gpio_vbus = get_vbus_gpio(index); if (sunxi_usbc->gpio_vbus != -1) @@ -162,7 +177,7 @@ int sunxi_usbc_request_resources(int index)
int sunxi_usbc_free_resources(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
if (sunxi_usbc->gpio_vbus != -1) return gpio_free(sunxi_usbc->gpio_vbus); @@ -172,7 +187,7 @@ int sunxi_usbc_free_resources(int index)
void sunxi_usbc_enable(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* enable common PHY only once */ @@ -187,17 +202,19 @@ void sunxi_usbc_enable(int index)
sunxi_usb_phy_init(sunxi_usbc);
- sunxi_usb_passby(sunxi_usbc, SUNXI_USB_PASSBY_EN); + if (sunxi_usbc->id != 0) + sunxi_usb_passby(sunxi_usbc, SUNXI_USB_PASSBY_EN);
enabled_hcd_count++; }
void sunxi_usbc_disable(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index]; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- sunxi_usb_passby(sunxi_usbc, !SUNXI_USB_PASSBY_EN); + if (sunxi_usbc->id != 0) + sunxi_usb_passby(sunxi_usbc, !SUNXI_USB_PASSBY_EN);
#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I clrbits_le32(&ccm->ahb_reset0_cfg, sunxi_usbc->ahb_clk_mask); @@ -214,7 +231,7 @@ void sunxi_usbc_disable(int index)
void sunxi_usbc_vbus_enable(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
if (sunxi_usbc->gpio_vbus != -1) gpio_direction_output(sunxi_usbc->gpio_vbus, 1); @@ -222,7 +239,7 @@ void sunxi_usbc_vbus_enable(int index)
void sunxi_usbc_vbus_disable(int index) { - struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index - 1]; + struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
if (sunxi_usbc->gpio_vbus != -1) gpio_direction_output(sunxi_usbc->gpio_vbus, 0); diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index a6d129c..84a9a2b 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -302,10 +302,12 @@ struct sunxi_ccm_reg { #define CCM_GMAC_CTRL_GPIT_MII (0x0 << 2) #define CCM_GMAC_CTRL_GPIT_RGMII (0x1 << 2)
+#define CCM_USB_CTRL_PHY0_RST (0x1 << 0) #define CCM_USB_CTRL_PHY1_RST (0x1 << 1) #define CCM_USB_CTRL_PHY2_RST (0x1 << 2) #define CCM_USB_CTRL_PHYGATE (0x1 << 8) -/* These 2 are sun6i only, define them as 0 on sun4i */ +/* These 3 are sun6i only, define them as 0 on sun4i */ +#define CCM_USB_CTRL_PHY0_CLK 0 #define CCM_USB_CTRL_PHY1_CLK 0 #define CCM_USB_CTRL_PHY2_CLK 0
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index f85f94d..4711260 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -229,10 +229,12 @@ struct sunxi_ccm_reg { #define CCM_MMC_CTRL_PLL6 (0x1 << 24) #define CCM_MMC_CTRL_ENABLE (0x1 << 31)
+#define CCM_USB_CTRL_PHY0_RST (0x1 << 0) #define CCM_USB_CTRL_PHY1_RST (0x1 << 1) #define CCM_USB_CTRL_PHY2_RST (0x1 << 2) /* There is no global phy clk gate on sun6i, define as 0 */ #define CCM_USB_CTRL_PHYGATE 0 +#define CCM_USB_CTRL_PHY0_CLK (0x1 << 8) #define CCM_USB_CTRL_PHY1_CLK (0x1 << 9) #define CCM_USB_CTRL_PHY2_CLK (0x1 << 10)
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index ca68065..bece6b7 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -272,6 +272,13 @@ config MMC_SUNXI_SLOT_EXTRA slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable support for this.
+config USB0_VBUS_PIN + string "Vbus enable pin for usb0 (otg)" + default "" + ---help--- + Set the Vbus enable pin for usb0 (otg). This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + config USB1_VBUS_PIN string "Vbus enable pin for usb1 (ehci0)" default "PH6" if MACH_SUN4I || MACH_SUN7I

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
Signed-off-by: Hans de Goede hdegoede@redhat.com
Acked-by: Ian Campbell ijc@hellion.org.uk

The axp221 / axp223's N_VBUSEN pin can be configured as an output rather then an input, and this is used on some boards to control usb-vbus0, add support for this.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/cpu/armv7/sunxi/usbc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/arch/arm/cpu/armv7/sunxi/usbc.c b/arch/arm/cpu/armv7/sunxi/usbc.c index 4f11da5..14de9f9 100644 --- a/arch/arm/cpu/armv7/sunxi/usbc.c +++ b/arch/arm/cpu/armv7/sunxi/usbc.c @@ -17,6 +17,15 @@ #include <asm/gpio.h> #include <asm/io.h> #include <common.h> +#ifdef CONFIG_AXP152_POWER +#include <axp152.h> +#endif +#ifdef CONFIG_AXP209_POWER +#include <axp209.h> +#endif +#ifdef CONFIG_AXP221_POWER +#include <axp221.h> +#endif
#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 #define SUNXI_USB_CSR 0x404 @@ -71,6 +80,12 @@ static struct sunxi_usbc_hcd {
static int enabled_hcd_count;
+static bool use_axp_drivebus(int index) +{ + return index == 0 && + strcmp(CONFIG_USB0_VBUS_PIN, "axp_drivebus") == 0; +} + void *sunxi_usbc_get_io_base(int index) { switch (index) { @@ -87,6 +102,9 @@ void *sunxi_usbc_get_io_base(int index)
static int get_vbus_gpio(int index) { + if (use_axp_drivebus(index)) + return -1; + switch (index) { case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN); case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); @@ -233,6 +251,10 @@ void sunxi_usbc_vbus_enable(int index) { struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
+#ifdef AXP_DRIVEBUS + if (use_axp_drivebus(index)) + axp_drivebus_enable(); +#endif if (sunxi_usbc->gpio_vbus != -1) gpio_direction_output(sunxi_usbc->gpio_vbus, 1); } @@ -241,6 +263,10 @@ void sunxi_usbc_vbus_disable(int index) { struct sunxi_usbc_hcd *sunxi_usbc = &sunxi_usbc_hcd[index];
+#ifdef AXP_DRIVEBUS + if (use_axp_drivebus(index)) + axp_drivebus_disable(); +#endif if (sunxi_usbc->gpio_vbus != -1) gpio_direction_output(sunxi_usbc->gpio_vbus, 0); }

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
The axp221 / axp223's N_VBUSEN pin can be configured as an output rather then an input, and this is used on some boards to control usb-vbus0, add support for this.
Signed-off-by: Hans de Goede hdegoede@redhat.com
I wonder if we should expose this as a pseudo-gpio? Anyway, this'll do for starters:
Acked-by: Ian Campbell ijc@hellion.org.uk

When iomuxing is used we must not only deregister the device with stdio.c, but also remove the reference to the device in the console_devices array used by console-muxing. Add a call to iomux_doenv to usb_kbd_deregister to update console_devices, which will drop the reference.
This fixes the console filling with "Failed to enqueue URB to controller" messages after a "usb stop force", or when the USB keyboard is gone after a "usb reset".
Signed-off-by: Hans de Goede hdegoede@redhat.com --- common/usb_kbd.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/common/usb_kbd.c b/common/usb_kbd.c index bc7145e..732136e 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -542,6 +542,10 @@ int usb_kbd_deregister(int force) data = usb_kbd_dev->privptr; if (stdio_deregister_dev(dev, force) != 0) return 1; +#ifdef CONFIG_CONSOLE_MUX + if (iomux_doenv(stdin, getenv("stdin")) != 0) + return 1; +#endif #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE destroy_int_queue(usb_kbd_dev, data->intq); #endif

Currently create_int_queue is only implemented by the ehci code, and that does not honor interrupt intervals, but other drivers which might also want to implement create_int_queue may honor intervals, so add an interval param.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- common/usb_kbd.c | 6 ++++-- drivers/usb/host/ehci-hcd.c | 2 +- include/usb.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 732136e..ecc3085 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -332,7 +332,8 @@ static inline void usb_kbd_poll_for_event(struct usb_device *dev) /* We've consumed all queued int packets, create new */ destroy_int_queue(dev, data->intq); data->intq = create_int_queue(dev, data->intpipe, 1, - USB_KBD_BOOT_REPORT_SIZE, data->new); + USB_KBD_BOOT_REPORT_SIZE, data->new, + data->intinterval); } #endif } @@ -453,7 +454,8 @@ static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) debug("USB KBD: enable interrupt pipe...\n"); #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE data->intq = create_int_queue(dev, data->intpipe, 1, - USB_KBD_BOOT_REPORT_SIZE, data->new); + USB_KBD_BOOT_REPORT_SIZE, data->new, + data->intinterval); if (!data->intq) { #else if (usb_submit_int_msg(dev, data->intpipe, data->new, data->intpktsize, diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc76066..e7a4729 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1148,7 +1148,7 @@ disable_periodic(struct ehci_ctrl *ctrl)
struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, - int elementsize, void *buffer) + int elementsize, void *buffer, int interval) { struct ehci_ctrl *ctrl = dev->controller; struct int_queue *result = NULL; diff --git a/include/usb.h b/include/usb.h index d3c7415..3d33b2d 100644 --- a/include/usb.h +++ b/include/usb.h @@ -169,7 +169,7 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
#ifdef CONFIG_USB_EHCI /* Only the ehci code has pollable int support */ struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, - int queuesize, int elementsize, void *buffer); + int queuesize, int elementsize, void *buffer, int interval); int destroy_int_queue(struct usb_device *dev, struct int_queue *queue); void *poll_int_queue(struct usb_device *dev, struct int_queue *queue); #endif

Before this commit u-boot would print the following on boot with musb and no usb device plugged in:
starting USB... USB0: Port not available. USB error: all controllers failed lowlevel init
This commit changes this to:
starting USB... USB0: Port not available.
Which is the correct thing to do since the low-level init went fine.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- common/usb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 736cd9f..1eda099 100644 --- a/common/usb.c +++ b/common/usb.c @@ -59,6 +59,7 @@ int usb_init(void) void *ctrl; struct usb_device *dev; int i, start_index = 0; + int controllers_initialized = 0; int ret;
dev_index = 0; @@ -78,6 +79,7 @@ int usb_init(void) ret = usb_lowlevel_init(i, USB_INIT_HOST, &ctrl); if (ret == -ENODEV) { /* No such device. */ puts("Port not available.\n"); + controllers_initialized++; continue; }
@@ -89,6 +91,7 @@ int usb_init(void) * lowlevel init is OK, now scan the bus for devices * i.e. search HUBs and configure them */ + controllers_initialized++; start_index = dev_index; printf("scanning bus %d for devices... ", i); dev = usb_alloc_new_device(ctrl); @@ -110,12 +113,10 @@ int usb_init(void)
debug("scan end\n"); /* if we were not able to find at least one working bus, bail out */ - if (!usb_started) { + if (controllers_initialized == 0) puts("USB error: all controllers failed lowlevel init\n"); - return -1; - }
- return 0; + return usb_started ? 0 : -1; }
/******************************************************************************

The sunxi SoCs also have a musb controller, but with a different register layout.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_regs.h | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
diff --git a/drivers/usb/musb-new/musb_regs.h b/drivers/usb/musb-new/musb_regs.h index 03f2655..27e4ed4 100644 --- a/drivers/usb/musb-new/musb_regs.h +++ b/drivers/usb/musb-new/musb_regs.h @@ -216,6 +216,9 @@
#ifndef CONFIG_BLACKFIN
+/* SUNXI has different reg addresses, but identical r/w functions */ +#ifndef CONFIG_ARCH_SUNXI + /* * Common USB registers */ @@ -318,6 +321,85 @@ #define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ (0x80 + (8*(_epnum)) + (_offset))
+#else /* CONFIG_ARCH_SUNXI */ + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x0098 +#define MUSB_POWER 0x0040 + +#define MUSB_INTRTX 0x0044 +#define MUSB_INTRRX 0x0046 +#define MUSB_INTRTXE 0x0048 +#define MUSB_INTRRXE 0x004A +#define MUSB_INTRUSB 0x004C +#define MUSB_INTRUSBE 0x0050 +#define MUSB_FRAME 0x0054 +#define MUSB_INDEX 0x0042 +#define MUSB_TESTMODE 0x007C + +/* Get offset for a given FIFO from musb->mregs */ +#define MUSB_FIFO_OFFSET(epnum) (0x00 + ((epnum) * 4)) + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x0041 + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x0090 +#define MUSB_RXFIFOSZ 0x0094 +#define MUSB_TXFIFOADD 0x0092 +#define MUSB_RXFIFOADD 0x0096 + +#define MUSB_EPINFO 0x0078 +#define MUSB_RAMINFO 0x0079 +#define MUSB_LINKINFO 0x007A +#define MUSB_VPLEN 0x007B +#define MUSB_HS_EOF1 0x007C +#define MUSB_FS_EOF1 0x007D +#define MUSB_LS_EOF1 0x007E + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x0080 +#define MUSB_TXCSR 0x0082 +#define MUSB_CSR0 0x0082 +#define MUSB_RXMAXP 0x0084 +#define MUSB_RXCSR 0x0086 +#define MUSB_RXCOUNT 0x0088 +#define MUSB_COUNT0 0x0088 +#define MUSB_TXTYPE 0x008C +#define MUSB_TYPE0 0x008C +#define MUSB_TXINTERVAL 0x008D +#define MUSB_NAKLIMIT0 0x008D +#define MUSB_RXTYPE 0x008E +#define MUSB_RXINTERVAL 0x008F + +#define MUSB_CONFIGDATA 0x00b0 /* musb_read_configdata adds 0x10 ! */ +#define MUSB_FIFOSIZE 0x0090 + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) (_offset) + +#define MUSB_TXCSR_MODE 0x2000 + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x0098 +#define MUSB_TXHUBADDR 0x009A +#define MUSB_TXHUBPORT 0x009B + +#define MUSB_RXFUNCADDR 0x009C +#define MUSB_RXHUBADDR 0x009E +#define MUSB_RXHUBPORT 0x009F + +/* Endpoint is selected with MUSB_INDEX. */ +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) (_offset) + +#endif /* CONFIG_ARCH_SUNXI */ + static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -340,7 +422,9 @@ static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val) { +#ifndef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val); +#endif }
static inline u8 musb_read_txfifosz(void __iomem *mbase) @@ -365,7 +449,11 @@ static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI /* No ulpi on sunxi */ + return 0; +#else return musb_readb(mbase, MUSB_ULPI_BUSCONTROL); +#endif }
static inline u8 musb_read_configdata(void __iomem *mbase) @@ -376,7 +464,11 @@ static inline u8 musb_read_configdata(void __iomem *mbase)
static inline u16 musb_read_hwvers(void __iomem *mbase) { +#ifdef CONFIG_ARCH_SUNXI + return 0; /* Unknown version */ +#else return musb_readw(mbase, MUSB_HWVERS); +#endif }
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)

This is based on Jussi Kivilinna's work for the linux-sunxi-3.4 kernel to use the kernels musb driver instead of Allwinners own custom driver.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/Makefile | 1 + drivers/usb/musb-new/sunxi.c | 279 ++++++++++++++++++++++++++++++++++++++++++ include/usb.h | 4 +- 3 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/musb-new/sunxi.c
diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 3facf0f..9edeece 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o +obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c new file mode 100644 index 0000000..778916d --- /dev/null +++ b/drivers/usb/musb-new/sunxi.c @@ -0,0 +1,279 @@ +/* + * Allwinner SUNXI "glue layer" + * + * Copyright © 2015 Hans de Goede hdegoede@redhat.com + * Copyright © 2013 Jussi Kivilinna jussi.kivilinna@iki.fi + * + * Based on the sw_usb "Allwinner OTG Dual Role Controller" code. + * Copyright 2007-2012 (C) Allwinner Technology Co., Ltd. + * javen javen@allwinnertech.com + * + * Based on the DA8xx "glue layer" code. + * Copyright (c) 2008-2009 MontaVista Software, Inc. source@mvista.com + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + */ +#include <common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/usbc.h> +#include "linux-compat.h" +#include "musb_core.h" + +/****************************************************************************** + ****************************************************************************** + * From the Allwinner driver + ****************************************************************************** + ******************************************************************************/ + +/****************************************************************************** + * From include/sunxi_usb_bsp.h + ******************************************************************************/ + +/* reg offsets */ +#define USBC_REG_o_ISCR 0x0400 +#define USBC_REG_o_PHYCTL 0x0404 +#define USBC_REG_o_PHYBIST 0x0408 +#define USBC_REG_o_PHYTUNE 0x040c + +#define USBC_REG_o_VEND0 0x0043 + +/* Interface Status and Control */ +#define USBC_BP_ISCR_VBUS_VALID_FROM_DATA 30 +#define USBC_BP_ISCR_VBUS_VALID_FROM_VBUS 29 +#define USBC_BP_ISCR_EXT_ID_STATUS 28 +#define USBC_BP_ISCR_EXT_DM_STATUS 27 +#define USBC_BP_ISCR_EXT_DP_STATUS 26 +#define USBC_BP_ISCR_MERGED_VBUS_STATUS 25 +#define USBC_BP_ISCR_MERGED_ID_STATUS 24 + +#define USBC_BP_ISCR_ID_PULLUP_EN 17 +#define USBC_BP_ISCR_DPDM_PULLUP_EN 16 +#define USBC_BP_ISCR_FORCE_ID 14 +#define USBC_BP_ISCR_FORCE_VBUS_VALID 12 +#define USBC_BP_ISCR_VBUS_VALID_SRC 10 + +#define USBC_BP_ISCR_HOSC_EN 7 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT 6 +#define USBC_BP_ISCR_ID_CHANGE_DETECT 5 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT 4 +#define USBC_BP_ISCR_IRQ_ENABLE 3 +#define USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN 2 +#define USBC_BP_ISCR_ID_CHANGE_DETECT_EN 1 +#define USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN 0 + +/****************************************************************************** + * From usbc/usbc.c + ******************************************************************************/ + +static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) +{ + u32 temp = reg_val; + + temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); + temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); + + return temp; +} + +static void USBC_EnableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableIdPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_EnableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_DisableDpDmPullUp(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToLow(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceIdToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidDisable(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ForceVbusValidToHigh(__iomem void *base) +{ + u32 reg_val; + + reg_val = musb_readl(base, USBC_REG_o_ISCR); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); + musb_writel(base, USBC_REG_o_ISCR, reg_val); +} + +static void USBC_ConfigFIFO_Base(void) +{ + u32 reg_value; + + /* config usb fifo, 8kb mode */ + reg_value = readl(SUNXI_SRAMC_BASE + 0x04); + reg_value &= ~(0x03 << 0); + reg_value |= (1 << 0); + writel(reg_value, SUNXI_SRAMC_BASE + 0x04); +} + +/****************************************************************************** + * MUSB Glue code + ******************************************************************************/ + +static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) +{ + struct musb *musb = __hci; + irqreturn_t retval = IRQ_NONE; + + /* read and flush interrupts */ + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + if (musb->int_usb) + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + if (musb->int_tx) + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + if (musb->int_rx) + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval |= musb_interrupt(musb); + + return retval; +} + +static void sunxi_musb_enable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* select PIO mode */ + musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); + + if (is_host_enabled(musb)) { + /* port power on */ + sunxi_usbc_vbus_enable(0); + } +} + +static void sunxi_musb_disable(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + /* Put the controller back in a pristane state for "usb reset" */ + if (musb->is_active) { + sunxi_usbc_disable(0); + sunxi_usbc_enable(0); + musb->is_active = 0; + } +} + +static int sunxi_musb_init(struct musb *musb) +{ + int err; + + pr_debug("%s():\n", __func__); + + err = sunxi_usbc_request_resources(0); + if (err) + return err; + + musb->isr = sunxi_musb_interrupt; + sunxi_usbc_enable(0); + + USBC_ConfigFIFO_Base(); + USBC_EnableDpDmPullUp(musb->mregs); + USBC_EnableIdPullUp(musb->mregs); + + if (is_host_enabled(musb)) { + /* Host mode */ + USBC_ForceIdToLow(musb->mregs); + USBC_ForceVbusValidToHigh(musb->mregs); + } else { + /* Peripheral mode */ + USBC_ForceIdToHigh(musb->mregs); + USBC_ForceVbusValidDisable(musb->mregs); + } + + return 0; +} + +static int sunxi_musb_exit(struct musb *musb) +{ + pr_debug("%s():\n", __func__); + + USBC_DisableDpDmPullUp(musb->mregs); + USBC_DisableIdPullUp(musb->mregs); + sunxi_usbc_vbus_disable(0); + sunxi_usbc_disable(0); + + return sunxi_usbc_free_resources(0); +} + +const struct musb_platform_ops sunxi_musb_ops = { + .init = sunxi_musb_init, + .exit = sunxi_musb_exit, + + .enable = sunxi_musb_enable, + .disable = sunxi_musb_disable, +}; diff --git a/include/usb.h b/include/usb.h index 3d33b2d..b921a7f 100644 --- a/include/usb.h +++ b/include/usb.h @@ -154,8 +154,8 @@ enum usb_init_type { defined(CONFIG_USB_OMAP3) || defined(CONFIG_USB_DA8XX) || \ defined(CONFIG_USB_BLACKFIN) || defined(CONFIG_USB_AM35X) || \ defined(CONFIG_USB_MUSB_DSPS) || defined(CONFIG_USB_MUSB_AM35X) || \ - defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined(CONFIG_USB_XHCI) || \ - defined(CONFIG_USB_DWC2) + defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined(CONFIG_USB_MUSB_SUNXI) || \ + defined(CONFIG_USB_XHCI) || defined(CONFIG_USB_DWC2)
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index);

CPU cycle based timeouts are no good, because how long they use depends on CPU speed. Instead use time based timeouts, and wait one second for a device connection to show up (per the USB spec), and wait USB_TIMEOUT_MS for various urbs to complete.
This fixes "usb start" taking for ever when no device is plugged into the otg port.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_uboot.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 2676f09..f8a0346 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -57,13 +57,11 @@ static struct urb *construct_urb(struct usb_device *dev, int endpoint_type, return &urb; }
-#define MUSB_HOST_TIMEOUT 0x3ffffff - static int submit_urb(struct usb_hcd *hcd, struct urb *urb) { struct musb *host = hcd->hcd_priv; int ret; - int timeout; + unsigned long timeout;
ret = musb_urb_enqueue(hcd, urb, 0); if (ret < 0) { @@ -71,12 +69,13 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) return ret; }
- timeout = MUSB_HOST_TIMEOUT; + timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); do { if (ctrlc()) return -EIO; host->isr(0, host); - } while ((urb->dev->status & USB_ST_NOT_PROC) && --timeout); + } while ((urb->dev->status & USB_ST_NOT_PROC) && + get_timer(0) < timeout);
return urb->status; } @@ -115,7 +114,8 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) { u8 power; void *mbase; - int timeout = MUSB_HOST_TIMEOUT; + /* USB spec says it may take up to 1 second for a device to connect */ + unsigned long timeout = get_timer(0) + 1000;
if (!host) { printf("MUSB host is not registered\n"); @@ -127,8 +127,8 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) do { if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) break; - } while (--timeout); - if (!timeout) + } while (get_timer(0) < timeout); + if (get_timer(0) >= timeout) return -ENODEV;
power = musb_readb(mbase, MUSB_POWER);

For bulk and ctrl transfers common/usb.c sets udev->status = USB_ST_NOT_PROC, but it does not do so for interrupt transfers.
musb_uboot.c: submit_urb() however was waiting for USB_ST_NOT_PROC to become 0, and thus without anyone setting USB_ST_NOT_PROC would exit immediately for interrupt urbs, returning the urb status of EINPROGRESS as error.
This commit fixes this, thereby also making usb_kbd.c work together with musb_new and CONFIG_SYS_USB_EVENT_POLL.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_uboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index f8a0346..28500bf 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -74,7 +74,7 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) if (ctrlc()) return -EIO; host->isr(0, host); - } while ((urb->dev->status & USB_ST_NOT_PROC) && + } while (urb->status == -EINPROGRESS && get_timer(0) < timeout);
return urb->status;

This commit fixes a number of issues with the reset sequence of musb-new in host mode:
1) Our usb device probe relies on a second device reset being done after the first descriptors read. Factor the musb reset code into a usb_reset_root_port function (and add this as an empty define for other controllers), and call this when a device has no parent.
2) Just like with normal usb controllers there needs to be a delay after reset, for normal usb controllers, this is handled in hub_port_reset, add a delay to usb_reset_root_port.
3) Sync the musb reset sequence with the upstream kernel, clear all bits of power except bits 4-7, and increase the time reset is asserted to 50 ms.
With these fixes an usb keyboard I have now always enumerates properly, where as earlier it would only enumerare properly once every 5 tries.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- common/usb.c | 2 ++ drivers/usb/musb-new/musb_uboot.c | 31 ++++++++++++++++++++----------- include/usb.h | 5 +++++ 3 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 1eda099..32e15cd 100644 --- a/common/usb.c +++ b/common/usb.c @@ -970,6 +970,8 @@ int usb_new_device(struct usb_device *dev) printf("\n Couldn't reset port %i\n", dev->portnr); return 1; } + } else { + usb_reset_root_port(); } #endif
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 28500bf..dab6f9b 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -110,9 +110,27 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, return submit_urb(&hcd, urb); }
-int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +void usb_reset_root_port(void) { + void *mbase = host->mregs; u8 power; + + power = musb_readb(mbase, MUSB_POWER); + power &= 0xf0; + musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); + mdelay(50); + power = musb_readb(mbase, MUSB_POWER); + musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); + host->isr(0, host); + host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? + USB_SPEED_HIGH : + (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? + USB_SPEED_FULL : USB_SPEED_LOW; + mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ void *mbase; /* USB spec says it may take up to 1 second for a device to connect */ unsigned long timeout = get_timer(0) + 1000; @@ -131,16 +149,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) if (get_timer(0) >= timeout) return -ENODEV;
- power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); - udelay(30000); - power = musb_readb(mbase, MUSB_POWER); - musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); - host->isr(0, host); - host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? - USB_SPEED_HIGH : - (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? - USB_SPEED_FULL : USB_SPEED_LOW; + usb_reset_root_port(); host->is_active = 1; hcd.hcd_priv = host;
diff --git a/include/usb.h b/include/usb.h index b921a7f..a083591 100644 --- a/include/usb.h +++ b/include/usb.h @@ -159,6 +159,11 @@ enum usb_init_type {
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index); +#ifdef CONFIG_MUSB_HOST +void usb_reset_root_port(void); +#else +#define usb_reset_root_port() +#endif
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len);

If a transfer / urb times-out, properly remove it from the schedule, rather then letting it sit on the ep head. This stops the musb code from getting confused and refusing to queue further transfers after a timeout.
Tested by unplugging a usb-keyboard, replugging it and doing a usb-reset, before this commit the keyboard would not work after the usb-reset.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_host.c | 12 +++++++++--- drivers/usb/musb-new/musb_host.h | 1 + drivers/usb/musb-new/musb_uboot.c | 3 +++ drivers/usb/musb-new/usb-compat.h | 1 + 4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/musb-new/musb_host.c b/drivers/usb/musb-new/musb_host.c index bbcee88..437309c 100644 --- a/drivers/usb/musb-new/musb_host.c +++ b/drivers/usb/musb-new/musb_host.c @@ -2130,8 +2130,6 @@ done: return ret; }
- -#ifndef __UBOOT__ /* * abort a transfer that's at the head of a hardware queue. * called with controller locked, irqs blocked @@ -2195,7 +2193,14 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) return status; }
-static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +#ifndef __UBOOT__ +static int musb_urb_dequeue( +#else +int musb_urb_dequeue( +#endif + struct usb_hcd *hcd, + struct urb *urb, + int status) { struct musb *musb = hcd_to_musb(hcd); struct musb_qh *qh; @@ -2253,6 +2258,7 @@ done: return ret; }
+#ifndef __UBOOT__ /* disable an endpoint */ static void musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) diff --git a/drivers/usb/musb-new/musb_host.h b/drivers/usb/musb-new/musb_host.h index ebebe0c..546b4a2 100644 --- a/drivers/usb/musb-new/musb_host.h +++ b/drivers/usb/musb-new/musb_host.h @@ -110,5 +110,6 @@ static inline struct urb *next_urb(struct musb_qh *qh)
#ifdef __UBOOT__ int musb_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); +int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); #endif #endif /* _MUSB_HOST_H */ diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index dab6f9b..e823ad1 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -77,6 +77,9 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) } while (urb->status == -EINPROGRESS && get_timer(0) < timeout);
+ if (urb->status == -EINPROGRESS) + musb_urb_dequeue(hcd, urb, -ETIME); + return urb->status; }
diff --git a/drivers/usb/musb-new/usb-compat.h b/drivers/usb/musb-new/usb-compat.h index 27f656f..50bad37 100644 --- a/drivers/usb/musb-new/usb-compat.h +++ b/drivers/usb/musb-new/usb-compat.h @@ -48,6 +48,7 @@ struct urb { list_add_tail(&urb->urb_list, &urb->ep->urb_list); \ ret; }) #define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list) +#define usb_hcd_check_unlink_urb(hdc, urb, status) 0
static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb,

Make construct_urb take an urb and hep parameter, rather then having it always operate on the file global urb and hep structs. This is a preperation patch for adding interrupt queue support.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_uboot.c | 63 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 32 deletions(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index e823ad1..3f9a98c 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -25,36 +25,35 @@ static void musb_host_complete_urb(struct urb *urb) static struct usb_host_endpoint hep; static struct urb urb;
-static struct urb *construct_urb(struct usb_device *dev, int endpoint_type, - unsigned long pipe, void *buffer, int len, - struct devrequest *setup, int interval) +static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, + struct usb_device *dev, int endpoint_type, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int interval) { int epnum = usb_pipeendpoint(pipe); int is_in = usb_pipein(pipe);
- memset(&urb, 0, sizeof(struct urb)); - memset(&hep, 0, sizeof(struct usb_host_endpoint)); - INIT_LIST_HEAD(&hep.urb_list); - INIT_LIST_HEAD(&urb.urb_list); - urb.ep = &hep; - urb.complete = musb_host_complete_urb; - urb.status = -EINPROGRESS; - urb.dev = dev; - urb.pipe = pipe; - urb.transfer_buffer = buffer; - urb.transfer_dma = (unsigned long)buffer; - urb.transfer_buffer_length = len; - urb.setup_packet = (unsigned char *)setup; - - urb.ep->desc.wMaxPacketSize = + memset(urb, 0, sizeof(struct urb)); + memset(hep, 0, sizeof(struct usb_host_endpoint)); + INIT_LIST_HEAD(&hep->urb_list); + INIT_LIST_HEAD(&urb->urb_list); + urb->ep = hep; + urb->complete = musb_host_complete_urb; + urb->status = -EINPROGRESS; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_dma = (unsigned long)buffer; + urb->transfer_buffer_length = len; + urb->setup_packet = (unsigned char *)setup; + + urb->ep->desc.wMaxPacketSize = __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : dev->epmaxpacketout[epnum]); - urb.ep->desc.bmAttributes = endpoint_type; - urb.ep->desc.bEndpointAddress = + urb->ep->desc.bmAttributes = endpoint_type; + urb->ep->desc.bEndpointAddress = (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; - urb.ep->desc.bInterval = interval; - - return &urb; + urb->ep->desc.bInterval = interval; }
static int submit_urb(struct usb_hcd *hcd, struct urb *urb) @@ -86,31 +85,31 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, struct devrequest *setup) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_CONTROL, pipe, - buffer, len, setup, 0); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, + buffer, len, setup, 0);
/* Fix speed for non hub-attached devices */ if (!dev->parent) dev->speed = host_speed;
- return submit_urb(&hcd, urb); + return submit_urb(&hcd, &urb); }
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_BULK, pipe, - buffer, len, NULL, 0); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_BULK, pipe, + buffer, len, NULL, 0); + return submit_urb(&hcd, &urb); }
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval) { - struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_INT, pipe, - buffer, len, NULL, interval); - return submit_urb(&hcd, urb); + construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_INT, pipe, + buffer, len, NULL, interval); + return submit_urb(&hcd, &urb); }
void usb_reset_root_port(void)

Add interrupt queue support, so that a usb keyboard can be used without causing huge latencies.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- drivers/usb/musb-new/musb_uboot.c | 65 +++++++++++++++++++++++++++++++++++++++ include/usb.h | 3 +- 2 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 3f9a98c..6e58ddf 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -12,6 +12,11 @@ #include "musb_gadget.h"
#ifdef CONFIG_MUSB_HOST +struct int_queue { + struct usb_host_endpoint hep; + struct urb urb; +}; + static struct musb *host; static struct usb_hcd hcd; static enum usb_device_speed host_speed; @@ -112,6 +117,66 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, return submit_urb(&hcd, &urb); }
+struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, + int queuesize, int elementsize, void *buffer, int interval) +{ + struct int_queue *queue; + int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); + + if (queuesize != 1) { + printf("ERROR musb int-queues only support queuesize 1\n"); + return NULL; + } + + if (dev->int_pending & (1 << index)) { + printf("ERROR int-urb is already pending on pipe %lx\n", pipe); + return NULL; + } + + queue = malloc(sizeof(*queue)); + if (!queue) + return NULL; + + construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, + pipe, buffer, elementsize, NULL, interval); + + ret = musb_urb_enqueue(&hcd, &queue->urb, 0); + if (ret < 0) { + printf("Failed to enqueue URB to controller\n"); + free(queue); + return NULL; + } + + dev->int_pending |= 1 << index; + return queue; +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + int index = usb_pipein(queue->urb.pipe) * 16 + + usb_pipeendpoint(queue->urb.pipe); + + if (queue->urb.status == -EINPROGRESS) + musb_urb_dequeue(&hcd, &queue->urb, -ETIME); + + dev->int_pending &= ~(1 << index); + free(queue); + return 0; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + if (queue->urb.status != -EINPROGRESS) + return NULL; /* URB has already completed in a prev. poll */ + + host->isr(0, host); + + if (queue->urb.status != -EINPROGRESS) + return queue->urb.transfer_buffer; /* Done */ + + return NULL; /* URB still pending */ +} + void usb_reset_root_port(void) { void *mbase = host->mregs; diff --git a/include/usb.h b/include/usb.h index a083591..a8fee0b 100644 --- a/include/usb.h +++ b/include/usb.h @@ -120,6 +120,7 @@ struct usb_device { * Each instance needs its own set of data structures. */ unsigned long status; + unsigned long int_pending; /* 1 bit per ep, used by int_queue */ int act_len; /* transfered bytes */ int maxchild; /* Number of ports if hub */ int portnr; @@ -172,7 +173,7 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval);
-#ifdef CONFIG_USB_EHCI /* Only the ehci code has pollable int support */ +#if defined CONFIG_USB_EHCI || defined CONFIG_MUSB_HOST struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, void *buffer, int interval); int destroy_int_queue(struct usb_device *dev, struct int_queue *queue);

Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Signed-off-by: Hans de Goede hdegoede@redhat.com --- arch/arm/include/asm/arch-sunxi/usbc.h | 2 ++ board/sunxi/Kconfig | 9 +++++++++ board/sunxi/board.c | 25 +++++++++++++++++++++++++ configs/Ippo_q8h_v1_2_defconfig | 3 ++- configs/Ippo_q8h_v5_defconfig | 3 ++- include/configs/sunxi-common.h | 10 +++++++++- 6 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/usbc.h b/arch/arm/include/asm/arch-sunxi/usbc.h index 8d20973..cb538cd 100644 --- a/arch/arm/include/asm/arch-sunxi/usbc.h +++ b/arch/arm/include/asm/arch-sunxi/usbc.h @@ -11,6 +11,8 @@ * SPDX-License-Identifier: GPL-2.0+ */
+extern const struct musb_platform_ops sunxi_musb_ops; + void *sunxi_usbc_get_io_base(int index); int sunxi_usbc_request_resources(int index); int sunxi_usbc_free_resources(int index); diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index bece6b7..89c51a2 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -391,6 +391,15 @@ config VIDEO_LCD_PANEL_HITACHI_TX18D42VM endchoice
+config USB_MUSB_SUNXI + bool "Enable sunxi OTG / DRC USB controller in host mode" + default n + ---help--- + Say y here to enable support for the sunxi OTG / DRC USB controller + used on almost all sunxi boards. Note currently u-boot can only have + one usb host controller enabled at a time, so enabling this on boards + which also use the ehci host controller will result in build errors. + config USB_KEYBOARD boolean "Enable USB keyboard support" default y diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 7d6d075..569ac84 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -28,7 +28,9 @@ #include <asm/arch/dram.h> #include <asm/arch/gpio.h> #include <asm/arch/mmc.h> +#include <asm/arch/usbc.h> #include <asm/io.h> +#include <linux/usb/musb.h> #include <net.h>
DECLARE_GLOBAL_DATA_PTR; @@ -208,6 +210,26 @@ void sunxi_board_init(void) } #endif
+#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_MUSB_GADGET) +static struct musb_hdrc_config musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 6, + .ram_bits = 11, +}; + +static struct musb_hdrc_platform_data musb_plat = { +#if defined(CONFIG_MUSB_HOST) + .mode = MUSB_HOST, +#else + .mode = MUSB_PERIPHERAL, +#endif + .config = &musb_config, + .power = 250, + .platform_ops = &sunxi_musb_ops, +}; +#endif + #ifdef CONFIG_MISC_INIT_R int misc_init_r(void) { @@ -227,6 +249,9 @@ int misc_init_r(void) eth_setenv_enetaddr("ethaddr", mac_addr); }
+#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_MUSB_GADGET) + musb_register(&musb_plat, NULL, (void *)SUNXI_USB0_BASE); +#endif return 0; } #endif diff --git a/configs/Ippo_q8h_v1_2_defconfig b/configs/Ippo_q8h_v1_2_defconfig index 0447b06..3e6d3cc 100644 --- a/configs/Ippo_q8h_v1_2_defconfig +++ b/configs/Ippo_q8h_v1_2_defconfig @@ -1,11 +1,12 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v1.2.dtb" +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB0_VBUS_PIN="axp_drivebus" CONFIG_VIDEO_LCD_MODE="x:800,y:480,depth:18,pclk_khz:33000,le:87,ri:167,up:31,lo:13,hs:1,vs:1,sync:3,vmode:0" CONFIG_VIDEO_LCD_POWER="PH7" CONFIG_VIDEO_LCD_BL_EN="PH6" CONFIG_VIDEO_LCD_BL_PWM="PH0" -CONFIG_USB_KEYBOARD=n +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN8I=y diff --git a/configs/Ippo_q8h_v5_defconfig b/configs/Ippo_q8h_v5_defconfig index 4e82bf9..b96985e 100644 --- a/configs/Ippo_q8h_v5_defconfig +++ b/configs/Ippo_q8h_v5_defconfig @@ -1,11 +1,12 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CONS_INDEX=5" CONFIG_FDTFILE="sun8i-a23-ippo-q8h-v5.dtb" +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB0_VBUS_PIN="axp_drivebus" CONFIG_VIDEO_LCD_MODE="x:800,y:480,depth:18,pclk_khz:33000,le:87,ri:168,up:31,lo:13,hs:1,vs:1,sync:3,vmode:0" CONFIG_VIDEO_LCD_POWER="PH7" CONFIG_VIDEO_LCD_BL_EN="PH6" CONFIG_VIDEO_LCD_BL_PWM="PH0" -CONFIG_USB_KEYBOARD=n +S:CONFIG_ARM=y +S:CONFIG_ARCH_SUNXI=y +S:CONFIG_MACH_SUN8I=y diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 64d379a..121bcf4 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -247,8 +247,16 @@ #endif
#ifdef CONFIG_USB_EHCI -#define CONFIG_CMD_USB #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 1 +#endif + +#ifdef CONFIG_USB_MUSB_SUNXI +#define CONFIG_MUSB_HOST +#define CONFIG_MUSB_PIO_ONLY +#endif + +#if defined CONFIG_USB_EHCI || defined CONFIG_USB_MUSB_SUNXI +#define CONFIG_CMD_USB #define CONFIG_USB_STORAGE #endif

On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Looks good, except I can't for the life of me find where CONFIG_MUSB_GADGET is ever set for a sunxi platform.
I suspect it's just there in preparation for some future gadget mode work (what we care about here is host mode), is that right?
If so: Acked-by: Ian Campbell ijc@hellion.org.uk
+#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_MUSB_GADGET)
Not just #ifdef CONFIG_USB_MUSB_SUNXI? (Ack either way)
Ian.

Hi,
On 13-01-15 20:59, Ian Campbell wrote:
On Sun, 2015-01-11 at 20:34 +0100, Hans de Goede wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Looks good, except I can't for the life of me find where CONFIG_MUSB_GADGET is ever set for a sunxi platform.
I suspect it's just there in preparation for some future gadget mode work (what we care about here is host mode), is that right?
Right, in theory we have everything in place for gadget mode when this patch-set lands and all someone needs to do is add a Kconfig option for it. Note I do not plan to work on that ATM, only so much hours in a day, etc. but I do invite others who are interested in this to take a look at the gadget stuff and patches are welcome :)
If so: Acked-by: Ian Campbell ijc@hellion.org.uk
+#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_MUSB_GADGET)
Not just #ifdef CONFIG_USB_MUSB_SUNXI? (Ack either way)
This is the construct the other MUSB users use in their board code, but yes either would work.
Regards,
Hans

On Sun, 11 Jan 2015 20:34:55 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Thanks. Works great on my Primo73 and Primo81 tablets. However I have just one naive question before happily taking it into use by default.
Is the hardcoded otg host mode (without checking the id pin) always safe? For example, what happens if somebody connects a charger instead of a usb keyboard to the tablet?

On Sat, 31 Jan 2015 04:54:47 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
On Sun, 11 Jan 2015 20:34:55 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Thanks. Works great on my Primo73 and Primo81 tablets. However I have just one naive question before happily taking it into use by default.
Is the hardcoded otg host mode (without checking the id pin) always safe? For example, what happens if somebody connects a charger instead of a usb keyboard to the tablet?
So, does anyone have any idea if it is theoretically safe to have two USB hosts connected to each other and both driving VBUS at +5V?
A somewhat realistic scenario is booting an Allwinner tablet over USB using the FEL mode. Is it a good idea for such tablet to have CONFIG_USB_MUSB_SUNXI=y and CONFIG_USB0_VBUS_PIN="axp_drivebus" in defconfig?

On 9 February 2015 at 04:54, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
On Sat, 31 Jan 2015 04:54:47 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
On Sun, 11 Jan 2015 20:34:55 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Thanks. Works great on my Primo73 and Primo81 tablets. However I have just one naive question before happily taking it into use by default.
Is the hardcoded otg host mode (without checking the id pin) always safe? For example, what happens if somebody connects a charger instead of a usb keyboard to the tablet?
So, does anyone have any idea if it is theoretically safe to have two USB hosts connected to each other and both driving VBUS at +5V?
A somewhat realistic scenario is booting an Allwinner tablet over USB using the FEL mode. Is it a good idea for such tablet to have CONFIG_USB_MUSB_SUNXI=y and CONFIG_USB0_VBUS_PIN="axp_drivebus" in defconfig?
From my past experiments with some non-standard-compliant cables which
allow connecting a tablet with a PC without telling the tablet to go OTG mode it seems the tablet battery is drained really quick in this case.
There is a protocol for telling the tablet not to power the bus (or telling the PC not to power it for that matter) but both sides have to support matching part of the protocol. Either way, ideal case is that tablet gets power form PC or powered hub even in host mode which is not standards-compliant and obviously does not work with stuff like keyboards.
However, it should be possible to detect that there is bus power at the moment a device is connected and not power the device in that case regardless of usb mode negotiated after the connection.
Thanks
Michal

Hi,
On 09-02-15 23:10, Michal Suchanek wrote:
On 9 February 2015 at 04:54, Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
On Sat, 31 Jan 2015 04:54:47 +0200 Siarhei Siamashka siarhei.siamashka@gmail.com wrote:
On Sun, 11 Jan 2015 20:34:55 +0100 Hans de Goede hdegoede@redhat.com wrote:
Hookup OTG USB controller support and enable the otg controller + USB-keyb on various tablets.
This allows tablet owners to interact with u-boot without needing to solder a serial console onto their tablet PCB.
Thanks. Works great on my Primo73 and Primo81 tablets. However I have just one naive question before happily taking it into use by default.
Is the hardcoded otg host mode (without checking the id pin) always safe? For example, what happens if somebody connects a charger instead of a usb keyboard to the tablet?
So, does anyone have any idea if it is theoretically safe to have two USB hosts connected to each other and both driving VBUS at +5V?
A somewhat realistic scenario is booting an Allwinner tablet over USB using the FEL mode. Is it a good idea for such tablet to have CONFIG_USB_MUSB_SUNXI=y and CONFIG_USB0_VBUS_PIN="axp_drivebus" in defconfig?
From my past experiments with some non-standard-compliant cables which allow connecting a tablet with a PC without telling the tablet to go OTG mode it seems the tablet battery is drained really quick in this case.
There is a protocol for telling the tablet not to power the bus (or telling the PC not to power it for that matter) but both sides have to support matching part of the protocol. Either way, ideal case is that tablet gets power form PC or powered hub even in host mode which is not standards-compliant and obviously does not work with stuff like keyboards.
However, it should be possible to detect that there is bus power at the moment a device is connected and not power the device in that case regardless of usb mode negotiated after the connection.
Right, I've fixing this on my todo list, the plan is to add vbus_det support, and first check if there is an external vbus before enabling vbus.
Regards,
Hans

On Sunday, January 11, 2015 at 08:34:38 PM, Hans de Goede wrote:
Hi Marek, Ian, et al,
I'm very happy to present this patch series, which is the last pieze of the puzzle to make it possible for people to use u-boot on sunxi devices without needing to resort to putting a soldering iron to their tablet :)
With this series sunxi tablet owners can simply plug in a usb keyboard into the otg port of their tablet (how boring).
Interestingly enough getting the musb code to work on sunxi was quite east, most of my time went into fixing generic usb / musb issues, which should help on other boards too :)
This entire series is based on u-boot-sunxi/next and for some of the sunxi specific patches there will likely be conflicts when applied directly to master, therefor I would like to suggest the following scheme for getting this upstream:
- Add patches 1-5 to u-boot-sunxi/next (once reviewed)
- Add patches 6-16 to u-boot-usb/next (once reviewed)
Applied 6-16 to u-boot-usb/next (V2 of 07 is applied instead).
Thanks!
Best regards, Marek Vasut
participants (5)
-
Hans de Goede
-
Ian Campbell
-
Marek Vasut
-
Michal Suchanek
-
Siarhei Siamashka