From aaf98d1380d04fa16584f41b5b4fcf184b9eac85 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Tue, 15 Feb 2011 16:18:59 -0600 Subject: [PATCH 01/12] serial: tegra_hsuart: Clean-up FIFO flush and set baudrate operations. Do necessary waits and fifo clear according to NV. Also use 115200 as the rate set while bringing-up rx dma during init, to decrease start-up time. Change-Id: I5211c75607cf2880ab2b1c22ab30f2c265534a1c Signed-off-by: Andrei Warkentin --- drivers/serial/tegra_hsuart.c | 87 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 09f5f454683c..2c70ac7a7893 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -318,6 +318,51 @@ static void do_handle_rx_dma(struct tegra_uart_port *t) set_rts(t, true); } +/* Wait for a symbol-time. */ +static void wait_sym_time(struct tegra_uart_port *t, unsigned int syms) +{ + + /* Definitely have a start bit. */ + unsigned int bits = 1; + switch (t->lcr_shadow & 3) { + case UART_LCR_WLEN5: + bits += 5; + break; + case UART_LCR_WLEN6: + bits += 6; + break; + case UART_LCR_WLEN7: + bits += 7; + break; + default: + bits += 8; + break; + } + + /* Technically 5 bits gets 1.5 bits of stop... */ + if (t->lcr_shadow & UART_LCR_STOP) { + bits += 2; + } else { + bits++; + } + + if (t->lcr_shadow & UART_LCR_PARITY) + bits++; + + if (likely(t->baud)) + udelay(DIV_ROUND_UP(syms * bits * 1000000, t->baud)); +} + +/* Flush desired FIFO. */ +static void tegra_fifo_reset(struct tegra_uart_port *t, u8 fcr_bits) +{ + unsigned char fcr = t->fcr_shadow; + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + uart_writeb(t, fcr, UART_FCR); + uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */ + wait_sym_time(t, 1); /* Wait for the flush to propagate. */ +} + static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr) { char flag = TTY_NORMAL; @@ -341,11 +386,8 @@ static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr) dev_err(t->uport.dev, "Got Break\n"); t->uport.icount.brk++; /* If FIFO read error without any data, reset Rx FIFO */ - if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) { - unsigned char fcr = t->fcr_shadow; - fcr |= UART_FCR_CLEAR_RCVR; - uart_writeb(t, fcr, UART_FCR); - } + if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) + tegra_fifo_reset(t, UART_FCR_CLEAR_RCVR); } } return flag; @@ -558,7 +600,6 @@ static void tegra_stop_rx(struct uart_port *u) static void tegra_uart_hw_deinit(struct tegra_uart_port *t) { - unsigned char fcr; unsigned long flags; flush_work(&t->tx_work); @@ -572,11 +613,7 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t) spin_lock_irqsave(&t->uport.lock, flags); /* Reset the Rx and Tx FIFOs */ - fcr = t->fcr_shadow; - fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR; - uart_writeb(t, fcr, UART_FCR); - - udelay(200); + tegra_fifo_reset(t, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); clk_disable(t->clk); t->baud = 0; @@ -603,7 +640,6 @@ static void tegra_uart_free_rx_dma(struct tegra_uart_port *t) static int tegra_uart_hw_init(struct tegra_uart_port *t) { - unsigned char fcr; unsigned char ier; dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n"); @@ -624,20 +660,6 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) t->rx_in_progress = 0; - /* Reset the FIFO twice with some delay to make sure that the FIFOs are - * really flushed. Wait is needed as the clearing needs to cross - * multiple clock domains. - * */ - t->fcr_shadow = UART_FCR_ENABLE_FIFO; - - fcr = t->fcr_shadow; - fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR; - uart_writeb(t, fcr, UART_FCR); - - udelay(100); - uart_writeb(t, t->fcr_shadow, UART_FCR); - udelay(100); - /* Set the trigger level * * For PIO mode: @@ -659,6 +681,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) * Set the Tx trigger to 4. This should match the DMA burst size that * programmed in the DMA registers. * */ + t->fcr_shadow = UART_FCR_ENABLE_FIFO; t->fcr_shadow |= UART_FCR_R_TRIG_01; t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B; uart_writeb(t, t->fcr_shadow, UART_FCR); @@ -667,7 +690,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) /* initialize the UART for a simple default configuration * so that the receive DMA buffer may be enqueued */ t->lcr_shadow = 3; /* no parity, stop, 8 data bits */ - tegra_set_baudrate(t, 9600); + tegra_set_baudrate(t, 115200); t->fcr_shadow |= UART_FCR_DMA_SELECT; uart_writeb(t, t->fcr_shadow, UART_FCR); if (tegra_start_dma_rx(t)) { @@ -995,8 +1018,10 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud) lcr &= ~UART_LCR_DLAB; uart_writeb(t, lcr, UART_LCR); + uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */ t->baud = baud; + wait_sym_time(t, 2); /* wait two character intervals at new rate */ dev_dbg(t->uport.dev, "Baud %u clock freq %lu and divisor of %u\n", baud, rate, divisor); } @@ -1020,10 +1045,6 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, if (t->rts_active) set_rts(t, false); - /* Baud rate */ - baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000); - tegra_set_baudrate(t, baud); - /* Parity */ lcr = t->lcr_shadow; lcr &= ~UART_LCR_PARITY; @@ -1067,6 +1088,10 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, uart_writeb(t, lcr, UART_LCR); t->lcr_shadow = lcr; + /* Baud rate. */ + baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000); + tegra_set_baudrate(t, baud); + /* Flow control */ if (termios->c_cflag & CRTSCTS) { mcr = t->mcr_shadow; From 730a86cc352c22c484686bcad62ce24d5d3f9d7e Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 28 Feb 2011 17:40:54 -0800 Subject: [PATCH 02/12] Revert "usb: host: ehci-hcd: add controller_resets_phy quirk" This reverts commit 2c7d591aa11086ba841f8712b7cee33e245b62f6. Change-Id: Ic9994dafc424e16d5d8251c287f8be65832b611b --- drivers/usb/host/ehci-hcd.c | 3 +-- drivers/usb/host/ehci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 74cc97b80c4b..794a104a1e4c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -259,8 +259,7 @@ static int ehci_reset (struct ehci_hcd *ehci) command |= CMD_RESET; dbg_cmd (ehci, "reset", command); - if (!ehci->controller_resets_phy) - ehci_writel(ehci, command, &ehci->regs->command); + ehci_writel(ehci, command, &ehci->regs->command); ehci_to_hcd(ehci)->state = HC_STATE_HALT; ehci->next_statechange = jiffies; retval = handshake (ehci, &ehci->regs->command, diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 530540a4bdd4..6f62e68e0e6f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -132,7 +132,6 @@ struct ehci_hcd { /* one per controller */ unsigned need_io_watchdog:1; unsigned broken_periodic:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ - unsigned controller_resets_phy:1; unsigned port_reset_no_wait:1; /* required for usb32 quirk */ From adbe9c4871446bfefb4e45d306939dd7d18b869b Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Tue, 1 Mar 2011 16:58:44 -0800 Subject: [PATCH 03/12] usb: host: Moved tegra_usb.h to include/linux/platform_data Change-Id: Ic093d07858eb6e27aae9cd04d4c59407b5b1d76d Signed-off-by: Benoit Goby --- drivers/usb/host/ehci-tegra.c | 2 +- include/linux/{ => platform_data}/tegra_usb.h | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename include/linux/{ => platform_data}/tegra_usb.h (100%) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a4f297544a1c..de3dcf0321eb 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/linux/tegra_usb.h b/include/linux/platform_data/tegra_usb.h similarity index 100% rename from include/linux/tegra_usb.h rename to include/linux/platform_data/tegra_usb.h From 79c37f01939d1a98d8d52453658a4268d008a600 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Tue, 1 Mar 2011 17:03:51 -0800 Subject: [PATCH 04/12] usb: otg: tegra: Moved tegra_usb.h to include/linux/platform_data Change-Id: I201b6476979f6e1ef8ffadb4398b99a1728f3083 Signed-off-by: Benoit Goby --- drivers/usb/otg/tegra-otg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 542a184824a3..83d3369f1b27 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include From a0c8749ef1c52db2b814d9b831af273f3e0aec69 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 7 Feb 2011 19:23:25 -0800 Subject: [PATCH 05/12] ARM: tegra: usb_phy: Cleanup before sending upstream Change-Id: I2548ca931f8ae37757115b7753d520d576bb173d Signed-off-by: Benoit Goby --- arch/arm/mach-tegra/include/mach/usb_phy.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index cc42e7b044b4..bf9766785a09 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -20,8 +20,6 @@ #include #include -#define USB_PHY_MAX_CONTEXT_REGS 10 - struct tegra_utmip_config { u8 hssync_start_delay; u8 elastic_limit; @@ -40,7 +38,7 @@ struct tegra_ulpi_config { enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_FULL = 0, TEGRA_USB_PHY_PORT_SPEED_LOW, - TEGRA_USB_PHY_PORT_HIGH, + TEGRA_USB_PHY_PORT_SPEED_HIGH, }; enum tegra_usb_phy_mode { @@ -82,4 +80,4 @@ int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); int tegra_usb_phy_close(struct tegra_usb_phy *phy); -#endif //__MACH_USB_PHY_H +#endif /* __MACH_USB_PHY_H */ From 27c50127f6f64609b7eb3d43bcfd22e85a3e80cf Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 10 Feb 2011 17:41:00 -0800 Subject: [PATCH 06/12] ARM: tegra: usb_phy: Fix return values Changed frequency table into a struct Change-Id: I202f808b912ce61e1f804103ea6d1197a57c1e62 Signed-off-by: Benoit Goby --- arch/arm/mach-tegra/include/mach/usb_phy.h | 22 +-- arch/arm/mach-tegra/usb_phy.c | 156 ++++++++++++--------- 2 files changed, 98 insertions(+), 80 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index bf9766785a09..335a2738d518 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -17,8 +17,8 @@ #ifndef __MACH_USB_PHY_H #define __MACH_USB_PHY_H -#include #include +#include struct tegra_utmip_config { u8 hssync_start_delay; @@ -46,9 +46,11 @@ enum tegra_usb_phy_mode { TEGRA_USB_PHY_MODE_HOST, }; +struct tegra_xtal_freq; + struct tegra_usb_phy { int instance; - int freq_sel; + const struct tegra_xtal_freq *freq; void __iomem *regs; void __iomem *pad_regs; struct clk *clk; @@ -63,21 +65,21 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, int tegra_usb_phy_power_on(struct tegra_usb_phy *phy); -int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy); +void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy); -int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); +void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy); -int tegra_usb_phy_power_off(struct tegra_usb_phy *phy); +void tegra_usb_phy_power_off(struct tegra_usb_phy *phy); -int tegra_usb_phy_preresume(struct tegra_usb_phy *phy); +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy); -int tegra_usb_phy_postresume(struct tegra_usb_phy *phy); +void tegra_usb_phy_postresume(struct tegra_usb_phy *phy); -int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, +void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed); -int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); +void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy); -int tegra_usb_phy_close(struct tegra_usb_phy *phy); +void tegra_usb_phy_close(struct tegra_usb_phy *phy); #endif /* __MACH_USB_PHY_H */ diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index 7242dda542c2..e2c733ff547b 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -29,9 +29,6 @@ #include #include -#define USB_USBSTS 0x144 -#define USB_USBSTS_PCI (1 << 2) - #define ULPI_VIEWPORT 0x170 #define ULPI_WAKEUP (1 << 31) #define ULPI_RUN (1 << 30) @@ -150,26 +147,48 @@ static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; -static const int udc_freq_table[] = { - 12000000, - 13000000, - 19200000, - 26000000, +struct tegra_xtal_freq { + int freq; + u8 enable_delay; + u8 stable_count; + u8 active_delay; + u8 xtal_freq_count; + u16 debounce; }; -static const u8 udc_delay_table[][4] = { - /* ENABLE_DLY, STABLE_CNT, ACTIVE_DLY, XTAL_FREQ_CNT */ - {0x02, 0x2F, 0x04, 0x76}, /* 12 MHz */ - {0x02, 0x33, 0x05, 0x7F}, /* 13 MHz */ - {0x03, 0x4B, 0x06, 0xBB}, /* 19.2 MHz */ - {0x04, 0x66, 0x09, 0xFE}, /* 26 Mhz */ -}; - -static const u16 udc_debounce_table[] = { - 0x7530, /* 12 MHz */ - 0x7EF4, /* 13 MHz */ - 0xBB80, /* 19.2 MHz */ - 0xFDE8, /* 26 MHz */ +static const struct tegra_xtal_freq tegra_freq_table[] = { + { + .freq = 12000000, + .enable_delay = 0x02, + .stable_count = 0x2F, + .active_delay = 0x04, + .xtal_freq_count = 0x76, + .debounce = 0x7530, + }, + { + .freq = 13000000, + .enable_delay = 0x02, + .stable_count = 0x33, + .active_delay = 0x05, + .xtal_freq_count = 0x7F, + .debounce = 0x7EF4, + }, + { + .freq = 19200000, + .enable_delay = 0x03, + .stable_count = 0x4B, + .active_delay = 0x06, + .xtal_freq_count = 0xBB, + .debounce = 0xBB80, + }, + { + .freq = 26000000, + .enable_delay = 0x04, + .stable_count = 0x66, + .active_delay = 0x09, + .xtal_freq_count = 0xFE, + .debounce = 0xFDE8, + }, }; static struct tegra_utmip_config utmip_default[] = { @@ -193,12 +212,17 @@ static struct tegra_utmip_config utmip_default[] = { }, }; +static inline bool phy_is_ulpi(struct tegra_usb_phy *phy) +{ + return (phy->instance == 1); +} + static int utmip_pad_open(struct tegra_usb_phy *phy) { phy->pad_clk = clk_get_sys("utmip-pad", NULL); if (IS_ERR(phy->pad_clk)) { pr_err("%s: can't get utmip pad clock\n", __func__); - return -1; + return PTR_ERR(phy->pad_clk); } if (phy->instance == 0) { @@ -248,7 +272,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) if (!utmip_pad_count) { pr_err("%s: utmip pad already powered off\n", __func__); - return -1; + return -EINVAL; } clk_enable(phy->pad_clk); @@ -335,7 +359,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) pr_err("%s: timeout waiting for phy to stabilize\n", __func__); } -static void utmi_phy_power_on(struct tegra_usb_phy *phy) +static int utmi_phy_power_on(struct tegra_usb_phy *phy) { unsigned long val; void __iomem *base = phy->regs; @@ -368,7 +392,7 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + UTMIP_DEBOUNCE_CFG0); val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); - val |= UTMIP_BIAS_DEBOUNCE_A(udc_debounce_table[phy->freq_sel]); + val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); writel(val, base + UTMIP_DEBOUNCE_CFG0); val = readl(base + UTMIP_MISC_CFG0); @@ -377,14 +401,14 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + UTMIP_MISC_CFG1); val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); - val |= UTMIP_PLL_ACTIVE_DLY_COUNT(udc_delay_table[phy->freq_sel][2]) | - UTMIP_PLLU_STABLE_COUNT(udc_delay_table[phy->freq_sel][1]); + val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | + UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); writel(val, base + UTMIP_MISC_CFG1); val = readl(base + UTMIP_PLL_CFG1); val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(udc_delay_table[phy->freq_sel][3]) | - UTMIP_PLLU_ENABLE_DLY_COUNT(udc_delay_table[phy->freq_sel][0]); + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); writel(val, base + UTMIP_PLL_CFG1); if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { @@ -457,6 +481,8 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~USB_PORTSC1_PTS(~0); writel(val, base + USB_PORTSC1); } + + return 0; } static void utmi_phy_power_off(struct tegra_usb_phy *phy) @@ -559,7 +585,7 @@ static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data) pr_err("%s: timeout accessing ulpi phy\n", __func__); } -static void ulpi_phy_power_on(struct tegra_usb_phy *phy) +static int ulpi_phy_power_on(struct tegra_usb_phy *phy) { unsigned long val; void __iomem *base = phy->regs; @@ -603,7 +629,7 @@ static void ulpi_phy_power_on(struct tegra_usb_phy *phy) if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) { pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__); - return; + return -EIO; } /* Fix VbusInvalid due to floating VBUS */ @@ -622,6 +648,8 @@ static void ulpi_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); + + return 0; } static void ulpi_phy_power_off(struct tegra_usb_phy *phy) @@ -647,7 +675,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, struct tegra_usb_phy *phy; struct tegra_ulpi_config *ulpi_config; unsigned long parent_rate; - int freq_sel; + int i; int err; phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); @@ -660,7 +688,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, phy->mode = phy_mode; if (!phy->config) { - if (instance == 1) { + if (phy_is_ulpi(phy)) { pr_err("%s: ulpi phy configuration missing", __func__); err = -EINVAL; goto err0; @@ -678,18 +706,19 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, clk_enable(phy->pll_u); parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); - for (freq_sel = 0; freq_sel < ARRAY_SIZE(udc_freq_table); freq_sel++) { - if (udc_freq_table[freq_sel] == parent_rate) + for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { + if (tegra_freq_table[i].freq == parent_rate) { + phy->freq = &tegra_freq_table[i]; break; + } } - if (freq_sel == ARRAY_SIZE(udc_freq_table)) { + if (!phy->freq) { pr_err("invalid pll_u parent rate %ld\n", parent_rate); err = -EINVAL; goto err1; } - phy->freq_sel = freq_sel; - if (phy->instance == 1) { + if (phy_is_ulpi(phy)) { ulpi_config = config; phy->clk = clk_get_sys(NULL, ulpi_config->clk); if (IS_ERR(phy->clk)) { @@ -718,77 +747,64 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { - if (phy->instance == 1) - ulpi_phy_power_on(phy); + if (phy_is_ulpi(phy)) + return ulpi_phy_power_on(phy); else - utmi_phy_power_on(phy); - - return 0; + return utmi_phy_power_on(phy); } -int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) +void tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { - if (phy->instance == 1) + if (phy_is_ulpi(phy)) ulpi_phy_power_off(phy); else utmi_phy_power_off(phy); - - return 0; } -int tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_preresume(phy); - return 0; } -int tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_postresume(phy); - return 0; } -int tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, +void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_restore_start(phy, port_speed); - return 0; } -int tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) +void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_restore_end(phy); - return 0; } -int tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) +void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_clk_disable(phy); - - return 0; } -int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) +void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (!phy_is_ulpi(phy)) utmi_phy_clk_enable(phy); - - return 0; } -int tegra_usb_phy_close(struct tegra_usb_phy *phy) +void tegra_usb_phy_close(struct tegra_usb_phy *phy) { - if (phy->instance == 1) + if (phy_is_ulpi(phy)) clk_put(phy->clk); else utmip_pad_close(phy); clk_disable(phy->pll_u); clk_put(phy->pll_u); kfree(phy); - return 0; } From 54463de0ff7a0784acd786e412b774a8e9aac5f6 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 7 Feb 2011 18:40:47 -0800 Subject: [PATCH 07/12] usb: host: tegra: Cleanup before sending upstream Change-Id: I846d43b1ecbe6c726f024b6a0fb318d6300a3746 Signed-off-by: Benoit Goby --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-tegra.c | 215 +++++++----------------- include/linux/platform_data/tegra_usb.h | 18 +- include/linux/usb/ehci_def.h | 4 +- 4 files changed, 72 insertions(+), 167 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 794a104a1e4c..66505a0d9703 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1199,7 +1199,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_ARCH_TEGRA #include "ehci-tegra.c" -#define PLATFORM_DRIVER tegra_ehci_driver +#define PLATFORM_DRIVER tegra_ehci_driver #endif #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index de3dcf0321eb..a516af28c29b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -14,9 +14,6 @@ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -26,24 +23,8 @@ #include #include -#define TEGRA_USB_USBCMD_REG_OFFSET 0x140 -#define TEGRA_USB_USBCMD_RESET (1 << 1) -#define TEGRA_USB_USBMODE_REG_OFFSET 0x1a8 -#define TEGRA_USB_USBMODE_HOST (3 << 0) -#define TEGRA_USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) - #define TEGRA_USB_DMA_ALIGN 32 -struct tegra_ehci_context { - bool valid; - u32 command; - u32 frame_list; - u32 async_next; - u32 txfilltunning; - u32 otgsc; - enum tegra_usb_phy_port_speed port_speed; -}; - struct tegra_ehci_hcd { struct ehci_hcd *ehci; struct tegra_usb_phy *phy; @@ -53,8 +34,8 @@ struct tegra_ehci_hcd { int host_resumed; int bus_suspended; int port_resuming; - struct tegra_ehci_context context; int power_down_on_bus_suspend; + enum tegra_usb_phy_port_speed port_speed; }; static void tegra_ehci_power_up(struct usb_hcd *hcd) @@ -103,15 +84,15 @@ static int tegra_ehci_hub_control( * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { - temp = ehci_readl(ehci, status_reg); - ehci_writel(ehci, (temp & ~PORT_RWC_BITS) & ~PORT_PE, status_reg); + temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; + ehci_writel(ehci, temp & ~PORT_PE, status_reg); goto done; } else if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); if (tegra->port_resuming && !(temp & PORT_SUSPEND)) { - /* resume completed */ + /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; tegra_usb_phy_postresume(tegra->phy); } @@ -124,16 +105,18 @@ static int tegra_ehci_hub_control( goto done; } - /* After above check the port must be connected. - * Set appropriate bit thus could put phy into low power - * mode if we have hostpc feature - */ temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + + /* + * If a transaction is in progress, there may be a delay in + * suspending the port. Poll until the port is suspended. + */ if (handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) - pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + pr_err("%s: timeout waiting for SUSPEND\n", __func__); + set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); goto done; } @@ -145,7 +128,8 @@ static int tegra_ehci_hub_control( * to set this bit to a zero after the resume duration is timed in the * driver. */ - else if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + else if (typeReq == ClearPortFeature && + wValue == USB_PORT_FEAT_SUSPEND) { temp = ehci_readl(ehci, status_reg); if ((temp & PORT_RESET) || !(temp & PORT_PE)) { retval = -EPIPE; @@ -155,6 +139,7 @@ static int tegra_ehci_hub_control( if (!(temp & PORT_SUSPEND)) goto done; + /* Disable disconnect detection during port resume */ tegra_usb_phy_preresume(tegra->phy); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); @@ -167,13 +152,11 @@ static int tegra_ehci_hub_control( msleep(20); spin_lock_irqsave(&ehci->lock, flags); - /* polling PORT_RESUME until the controller clear this bit */ + /* Poll until the controller clears RESUME and SUSPEND */ if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000)) - pr_err("%s: timeout waiting for PORT_RESUME\n", __func__); - - /* polling PORT_SUSPEND until the controller clear this bit */ + pr_err("%s: timeout waiting for RESUME\n", __func__); if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) - pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); + pr_err("%s: timeout waiting for SUSPEND\n", __func__); ehci->reset_done[wIndex-1] = 0; @@ -192,18 +175,9 @@ static int tegra_ehci_hub_control( static void tegra_ehci_restart(struct usb_hcd *hcd) { - unsigned int temp; struct ehci_hcd *ehci = hcd_to_ehci(hcd); - /* reset the ehci controller */ - ehci->controller_resets_phy = 0; ehci_reset(ehci); - ehci->controller_resets_phy = 1; - - /* Set to Host mode by setting bit 0-1 of USB device mode register */ - temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); - writel((temp | TEGRA_USB_USBMODE_HOST), - (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); /* setup the frame list and Async q heads */ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); @@ -224,67 +198,42 @@ static int tegra_usb_suspend(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); struct ehci_regs __iomem *hw = tegra->ehci->regs; - struct tegra_ehci_context *context = &tegra->context; unsigned long flags; spin_lock_irqsave(&tegra->ehci->lock, flags); - context->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - - if (context->port_speed > TEGRA_USB_PHY_PORT_HIGH) { - /* If no device connection or invalid speeds, - * don't save the context */ - context->valid = false; - } else { - context->command = readl(&hw->command); - context->frame_list = readl(&hw->frame_list); - context->async_next = readl(&hw->async_next); - context->txfilltunning = readl(&hw->reserved[2]); - context->otgsc = readl(&hw->reserved[18]); - context->valid = true; - } - + tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; ehci_halt(tegra->ehci); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&tegra->ehci->lock, flags); - tegra_ehci_power_down(ehci_to_hcd(tegra->ehci)); + tegra_ehci_power_down(hcd); return 0; } static int tegra_usb_resume(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - struct tegra_ehci_context *context = &tegra->context; - struct ehci_regs __iomem *hw = tegra->ehci->regs; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_regs __iomem *hw = ehci->regs; unsigned long val; - int lp0_resume = 0; set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - tegra_ehci_power_up(ehci_to_hcd(tegra->ehci)); + tegra_ehci_power_up(hcd); - if (!context->valid) { + if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) { /* Wait for the phy to detect new devices * before we restart the controller */ msleep(10); goto restart; } - tegra_ehci_phy_restore_start(tegra->phy, context->port_speed); + /* Force the phy to keep data lines in suspend state */ + tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed); - /* Check if the phy resume from LP0. When the phy resume from LP0 - * USB register will be reset. */ - if (!readl(&hw->async_next)) - lp0_resume = 1; - - /* Restore register context */ - writel(TEGRA_USB_USBMODE_HOST, &hw->reserved[19]); - writel(context->otgsc, &hw->reserved[18]); - writel(context->txfilltunning, &hw->reserved[2]); - writel(context->async_next, &hw->async_next); - writel(context->frame_list, &hw->frame_list); - writel(context->command, &hw->command); + /* Enable host mode */ + tdi_reset(ehci); /* Enable Port Power */ val = readl(&hw->port_status[0]); @@ -292,36 +241,38 @@ static int tegra_usb_resume(struct usb_hcd *hcd) writel(val, &hw->port_status[0]); udelay(10); - if (lp0_resume) { - /* Program the field PTC in PORTSC based on the saved speed mode */ + /* Check if the phy resume from LP0. When the phy resume from LP0 + * USB register will be reset. */ + if (!readl(&hw->async_next)) { + /* Program the field PTC based on the saved speed mode */ val = readl(&hw->port_status[0]); - val &= ~(TEGRA_USB_PORTSC1_PTC(~0)); - if (context->port_speed == TEGRA_USB_PHY_PORT_HIGH) - val |= TEGRA_USB_PORTSC1_PTC(5); - else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) - val |= TEGRA_USB_PORTSC1_PTC(6); - else if (context->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= TEGRA_USB_PORTSC1_PTC(7); + val &= ~PORT_TEST(~0); + if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH) + val |= PORT_TEST_FORCE; + else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL) + val |= PORT_TEST(6); + else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) + val |= PORT_TEST(7); + writel(val, &hw->port_status[0]); + udelay(10); + + /* Disable test mode by setting PTC field to NORMAL_OP */ + val = readl(&hw->port_status[0]); + val &= ~PORT_TEST(~0); writel(val, &hw->port_status[0]); udelay(10); } - /* Disable test mode by setting PTC field to NORMAL_OP */ - val = readl(&hw->port_status[0]); - val &= ~(TEGRA_USB_PORTSC1_PTC(~0)); - writel(val, &hw->port_status[0]); - udelay(10); - /* Poll until CCS is enabled */ - if (handshake(tegra->ehci, &hw->port_status[0], PORT_CONNECT, - PORT_CONNECT, 2000)) { + if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, + PORT_CONNECT, 2000)) { pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); goto restart; } /* Poll until PE is enabled */ - if (handshake(tegra->ehci, &hw->port_status[0], PORT_PE, - PORT_PE, 2000)) { + if (handshake(ehci, &hw->port_status[0], PORT_PE, + PORT_PE, 2000)) { pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); goto restart; } @@ -338,8 +289,8 @@ static int tegra_usb_resume(struct usb_hcd *hcd) writel(val, &hw->port_status[0]); /* Wait until port suspend completes */ - if (handshake(tegra->ehci, &hw->port_status[0], PORT_SUSPEND, - PORT_SUSPEND, 1000)) { + if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, + PORT_SUSPEND, 1000)) { pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); goto restart; @@ -347,54 +298,25 @@ static int tegra_usb_resume(struct usb_hcd *hcd) } tegra_ehci_phy_restore_end(tegra->phy); - return 0; restart: - if (context->valid) + if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) tegra_ehci_phy_restore_end(tegra->phy); tegra_ehci_restart(hcd); return 0; } -static int tegra_ehci_reset(struct usb_hcd *hcd) -{ - unsigned long temp; - int usec = 250*1000; /* see ehci_reset */ - - temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET); - temp |= TEGRA_USB_USBCMD_RESET; - writel(temp, hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET); - - do { - temp = readl(hcd->regs + TEGRA_USB_USBCMD_REG_OFFSET); - if (!(temp & TEGRA_USB_USBCMD_RESET)) - break; - udelay(1); - usec--; - } while (usec); - - if (!usec) - return -ETIMEDOUT; - - /* Set to Host mode by setting bit 0-1 of USB device mode register */ - temp = readl(hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET); - writel((temp | TEGRA_USB_USBMODE_HOST), - (hcd->regs + TEGRA_USB_USBMODE_REG_OFFSET)); - - return 0; -} - static void tegra_ehci_shutdown(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); + /* ehci_shutdown touches the USB controller registers, make sure * controller has clocks to it */ if (!tegra->host_resumed) tegra_ehci_power_up(hcd); - /* call ehci shut down */ ehci_shutdown(hcd); } @@ -414,6 +336,10 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); + /* switch to host mode */ + hcd->has_tt = 1; + ehci_reset(ehci); + retval = ehci_halt(ehci); if (retval) return retval; @@ -423,19 +349,8 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) if (retval) return retval; - hcd->has_tt = 1; ehci->sbrn = 0x20; - ehci_reset(ehci); - - /* - * Resetting the controller has the side effect of resetting the PHY. - * So, never reset the controller after the calling - * tegra_ehci_reinit API. - */ - ehci->controller_resets_phy = 1; - ehci->port_reset_no_wait = 1; - ehci_port_power(ehci, 1); return retval; } @@ -591,10 +506,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) { struct resource *res; struct usb_hcd *hcd; - struct ehci_hcd *ehci; struct tegra_ehci_hcd *tegra; struct tegra_ehci_platform_data *pdata; - struct tegra_utmip_config *config; int err = 0; int irq; int instance = pdev->id; @@ -655,9 +568,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_io; } - config = pdata->phy_config; - - tegra->phy = tegra_usb_phy_open(instance, hcd->regs, config, + tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config, TEGRA_USB_PHY_MODE_HOST); if (IS_ERR(tegra->phy)) { dev_err(&pdev->dev, "Failed to open USB phy\n"); @@ -665,15 +576,15 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_phy; } - err = tegra_ehci_reset(hcd); + err = tegra_usb_phy_power_on(tegra->phy); if (err) { - dev_err(&pdev->dev, "Failed to reset controller\n"); + dev_err(&pdev->dev, "Failed to power on the phy\n"); goto fail; } - tegra_usb_phy_power_on(tegra->phy); tegra->host_resumed = 1; tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend; + tegra->ehci = hcd_to_ehci(hcd); irq = platform_get_irq(pdev, 0); if (!irq) { @@ -681,12 +592,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) err = -ENODEV; goto fail; } - set_irq_flags(irq, IRQF_VALID); - ehci = hcd_to_ehci(hcd); - tegra->ehci = ehci; - #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = otg_get_transceiver(); @@ -696,7 +603,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #endif err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); - if (err != 0) { + if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; } diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h index 2947ed26879a..6bca5b569acb 100644 --- a/include/linux/platform_data/tegra_usb.h +++ b/include/linux/platform_data/tegra_usb.h @@ -1,19 +1,15 @@ /* * Copyright (C) 2010 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; either version 2 of the License, or (at your - * option) any later version. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. * - * 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. + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _TEGRA_USB_H_ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 2e262cb15425..656380245198 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -127,7 +127,9 @@ struct ehci_regs { #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ #define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ /* 19:16 for port testing */ -#define PORT_TEST_PKT (0x4<<16) /* Port Test Control - packet test */ +#define PORT_TEST(x) (((x)&0xf)<<16) /* Port Test Control */ +#define PORT_TEST_PKT PORT_TEST(0x4) /* Port Test Control - packet test */ +#define PORT_TEST_FORCE PORT_TEST(0x5) /* Port Test Control - force enable */ #define PORT_LED_OFF (0<<14) #define PORT_LED_AMBER (1<<14) #define PORT_LED_GREEN (2<<14) From 406248bb217d94ac012722534fb47885f52c0d07 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 11 Feb 2011 15:20:43 -0800 Subject: [PATCH 08/12] usb: otg: Add ulpi viewport access ops Add generic access ops for controllers with a ulpi viewport register (e.g. Chipidea based controller). Change-Id: I53d50857d4716f8df708af199ad1def1ff9d4beb Signed-off-by: Benoit Goby --- drivers/usb/otg/Kconfig | 7 +++ drivers/usb/otg/Makefile | 1 + drivers/usb/otg/ulpi_viewport.c | 80 +++++++++++++++++++++++++++++++++ include/linux/usb/ulpi.h | 5 +++ 4 files changed, 93 insertions(+) create mode 100644 drivers/usb/otg/ulpi_viewport.c diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 2240602fc81b..5ea7329e7f98 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -49,6 +49,13 @@ config USB_ULPI Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. +config USB_ULPI_VIEWPORT + bool + depends on USB_ULPI + help + Provides read/write operations to the ULPI phy register set for + controllers with a viewport register (e.g. Chipidea/ARC controllers). + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index fbf2a25a2e8c..8ca50cf114f2 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o +obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c new file mode 100644 index 000000000000..e9a37f90994f --- /dev/null +++ b/drivers/usb/otg/ulpi_viewport.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#define ULPI_VIEW_WAKEUP (1 << 31) +#define ULPI_VIEW_RUN (1 << 30) +#define ULPI_VIEW_WRITE (1 << 29) +#define ULPI_VIEW_READ (0 << 29) +#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) +#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) +#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) + +static int ulpi_viewport_wait(void __iomem *view, u32 mask) +{ + unsigned long usec = 2000; + + while (usec--) { + if (!(readl(view) & mask)) + return 0; + + udelay(1); + }; + + return -ETIMEDOUT; +} + +static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); + if (ret) + return ret; + + return ULPI_VIEW_DATA_READ(readl(view)); +} + +static int ulpi_viewport_write(struct otg_transceiver *otg, u32 val, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | + ULPI_VIEW_ADDR(reg), view); + + return ulpi_viewport_wait(view, ULPI_VIEW_RUN); +} + +struct otg_io_access_ops ulpi_viewport_access_ops = { + .read = ulpi_viewport_read, + .write = ulpi_viewport_write, +}; diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 82b1507f4735..9595796d62ed 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -184,4 +184,9 @@ struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops, unsigned int flags); +#ifdef CONFIG_USB_ULPI_VIEWPORT +/* access ops for controllers with a viewport register */ +extern struct otg_io_access_ops ulpi_viewport_access_ops; +#endif + #endif /* __LINUX_USB_ULPI_H */ From 614d2e5a2721e0eaa8490b8d504c504c5927dde1 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 11 Feb 2011 15:55:38 -0800 Subject: [PATCH 09/12] ARM: tegra: use generic ulpi operations Change-Id: I5b56a8a1b10532683accfe885387afb309b26b19 Signed-off-by: Benoit Goby --- arch/arm/mach-tegra/include/mach/usb_phy.h | 1 + arch/arm/mach-tegra/usb_phy.c | 45 ++++++++-------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index 335a2738d518..d4b8f9e298a8 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -58,6 +58,7 @@ struct tegra_usb_phy { struct clk *pad_clk; enum tegra_usb_phy_mode mode; void *config; + struct otg_transceiver *ulpi; }; struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index e2c733ff547b..88081bb3ec52 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -25,19 +25,13 @@ #include #include #include +#include +#include #include #include #include #define ULPI_VIEWPORT 0x170 -#define ULPI_WAKEUP (1 << 31) -#define ULPI_RUN (1 << 30) -#define ULPI_RD_RW_WRITE (1 << 29) -#define ULPI_RD_RW_READ (0 << 29) -#define ULPI_PORT(x) (((x) & 0x7) << 24) -#define ULPI_ADDR(x) (((x) & 0xff) << 16) -#define ULPI_DATA_RD(x) (((x) & 0xff) << 8) -#define ULPI_DATA_WR(x) (((x) & 0xff) << 0) #define USB_PORTSC1 0x184 #define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) @@ -572,21 +566,9 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) udelay(10); } -static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0); - val |= ULPI_ADDR(addr) | ULPI_DATA_WR(data); - writel(val, base + ULPI_VIEWPORT); - - if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_RUN, 0)) - pr_err("%s: timeout accessing ulpi phy\n", __func__); -} - static int ulpi_phy_power_on(struct tegra_usb_phy *phy) { + int ret; unsigned long val; void __iomem *base = phy->regs; struct tegra_ulpi_config *config = phy->config; @@ -624,17 +606,18 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) val |= ULPI_DIR_TRIMMER_LOAD; writel(val, base + ULPI_TIMING_CTRL_1); - val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0); - writel(val, base + ULPI_VIEWPORT); - - if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) { - pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__); - return -EIO; + /* Fix VbusInvalid due to floating VBUS */ + ret = otg_io_write(phy->ulpi, 0x40, 0x08); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; } - /* Fix VbusInvalid due to floating VBUS */ - ulpi_viewport_write(phy, 0x08, 0x40); - ulpi_viewport_write(phy, 0x0B, 0x80); + ret = otg_io_write(phy->ulpi, 0x80, 0x0B); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } val = readl(base + USB_PORTSC1); val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; @@ -729,6 +712,8 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, tegra_gpio_enable(ulpi_config->reset_gpio); gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); gpio_direction_output(ulpi_config->reset_gpio, 0); + phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); + phy->ulpi->io_priv = regs + ULPI_VIEWPORT; } else { err = utmip_pad_open(phy); if (err < 0) From bca6fceaeb209cee15dbe5de17ce703be1ad2b4c Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 10 Feb 2011 18:47:20 -0800 Subject: [PATCH 10/12] usb: do not force select has_ehci for tegra Change-Id: If6219a3ba9671a2c4c10b35461e1cc2ffe50ab0a Signed-off-by: Benoit Goby --- drivers/usb/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 69e8a096c35a..4aa00e6e57ad 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -61,7 +61,6 @@ config USB_ARCH_HAS_EHCI default y if PPC_83xx default y if SOC_AU1200 default y if ARCH_IXP4XX - default y if ARCH_TEGRA default y if ARCH_W90X900 default y if ARCH_AT91SAM9G45 default y if ARCH_MXC From e859d82480517512fd60ab4dd40b6ef00a8a5928 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 10 Feb 2011 18:47:46 -0800 Subject: [PATCH 11/12] ARM: tegra: select USB_ARCH_HAS_EHCI usb_phy now depends on USB_ULPI_VIEWPORT Change-Id: I5f3c996b41b50e74aa2b53eb246e100926108bff Signed-off-by: Benoit Goby --- arch/arm/mach-tegra/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 983297dfa25e..636876fb9e78 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -11,6 +11,9 @@ config ARCH_TEGRA_2x_SOC select ARM_GIC select ARCH_REQUIRE_GPIOLIB select ARM_ERRATA_742230 + select USB_ARCH_HAS_EHCI if USB_SUPPORT + select USB_ULPI if USB_SUPPORT + select USB_ULPI_VIEWPORT if USB_SUPPORT help Support for NVIDIA Tegra AP20 and T20 processors, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller From 58570bc0c45d65b4c4b2e58ccff3e9a71d0ef5ac Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 28 Feb 2011 17:45:34 -0800 Subject: [PATCH 12/12] usb: host: Renamed USB_TEGRA_HCD -> USB_EHCI_TEGRA Change-Id: I2eec79186d6dc258caa2f3c8f3cd071222d1c5c7 Signed-off-by: Benoit Goby --- drivers/usb/host/Kconfig | 16 ++++++++-------- drivers/usb/host/ehci-hcd.c | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 10f6ab5f9150..28deb1ac09b0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -126,6 +126,14 @@ config USB_EHCI_MXC ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_TEGRA + boolean "NVIDIA Tegra HCD support" + depends on USB_EHCI_HCD && ARCH_TEGRA + select USB_EHCI_ROOT_HUB_TT + help + This driver enables support for the internal USB Host Controller + found in NVIDIA Tegra SoCs. The Tegra controller is EHCI compliant. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF @@ -418,14 +426,6 @@ config USB_HWA_HCD To compile this driver a module, choose M here: the module will be called "hwa-hc". -config USB_TEGRA_HCD - boolean "NVIDIA Tegra HCD support" - depends on USB && ARCH_TEGRA && USB_EHCI_HCD - select USB_EHCI_ROOT_HUB_TT - help - This driver enables support for the internal USB Host Controller - found in NVIDIA Tegra SoCs. The Tegra controller is EHCI compliant. - config USB_IMX21_HCD tristate "iMX21 HCD support" depends on USB && ARM && MACH_MX21 diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 66505a0d9703..36ec66bb995e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1197,7 +1197,7 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_atmel_driver #endif -#ifdef CONFIG_ARCH_TEGRA +#ifdef CONFIG_USB_EHCI_TEGRA #include "ehci-tegra.c" #define PLATFORM_DRIVER tegra_ehci_driver #endif