From 8b909622c1d507518a617c58d5fe42470bc7f73a Mon Sep 17 00:00:00 2001 From: Lionel VITTE Date: Thu, 11 Jul 2019 14:12:06 +0200 Subject: [PATCH 25/30] ARM stm32mp1 r2 TTY USB --- drivers/tty/serial/stm32-usart.c | 585 +++++++++++++++++++------ drivers/tty/serial/stm32-usart.h | 48 +- drivers/usb/dwc2/Makefile | 2 +- drivers/usb/dwc2/core.c | 123 +++--- drivers/usb/dwc2/core.h | 50 +++ drivers/usb/dwc2/debugfs.c | 1 + drivers/usb/dwc2/drd.c | 191 ++++++++ drivers/usb/dwc2/gadget.c | 140 +++++- drivers/usb/dwc2/hcd.c | 63 ++- drivers/usb/dwc2/hw.h | 25 ++ drivers/usb/dwc2/params.c | 40 ++ drivers/usb/dwc2/platform.c | 166 ++++++- drivers/usb/host/ehci-platform.c | 59 +++ drivers/usb/typec/Kconfig | 9 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/class.c | 15 + drivers/usb/typec/typec_stusb.c | 918 +++++++++++++++++++++++++++++++++++++++ include/linux/usb/typec.h | 1 + 18 files changed, 2182 insertions(+), 255 deletions(-) create mode 100644 drivers/usb/dwc2/drd.c create mode 100644 drivers/usb/typec/typec_stusb.c diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e8d7a7b..fdcc214 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -105,9 +107,7 @@ static int stm32_config_rs485(struct uart_port *port, struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 usartdiv, baud, cr1, cr3; bool over8; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); port->rs485 = *rs485conf; @@ -147,7 +147,6 @@ static int stm32_config_rs485(struct uart_port *port, } stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - spin_unlock_irqrestore(&port->lock, flags); return 0; } @@ -180,22 +179,26 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, *sr = readl_relaxed(port->membase + ofs->isr); if (threaded && stm32_port->rx_ch) { + if (stm32_port->rx_dma_cb == CALLBACK_CALLED) + return 1; status = dmaengine_tx_status(stm32_port->rx_ch, stm32_port->rx_ch->cookie, &state); if ((status == DMA_IN_PROGRESS) && - (*last_res != state.residue)) + (*last_res != state.residue)) { + stm32_port->rx_dma_cb = CALLBACK_IGNORED; return 1; - else + } else { return 0; + } } else if (*sr & USART_SR_RXNE) { return 1; } return 0; } -static unsigned long -stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +static unsigned long stm32_get_char(struct uart_port *port, u32 *sr, + int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -203,12 +206,18 @@ stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) if (stm32_port->rx_ch) { c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; - if ((*last_res) == 0) + if ((*last_res) == 0) { *last_res = RX_BUF_L; - return c; + if (stm32_port->rx_dma_cb == CALLBACK_CALLED) + stm32_port->rx_dma_cb = CALLBACK_NOT_CALLED; + } } else { - return readl_relaxed(port->membase + ofs->rdr); + c = readl_relaxed(port->membase + ofs->rdr); + /* apply RDR data mask */ + c &= stm32_port->rdr_mask; } + + return c; } static void stm32_receive_chars(struct uart_port *port, bool threaded) @@ -216,44 +225,65 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - unsigned long c; + unsigned long c, flags; u32 sr; char flag; if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) pm_wakeup_event(tport->tty->dev, 0); + if (threaded) + spin_lock_irqsave(&port->lock, flags); + else + spin_lock(&port->lock); + while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { sr |= USART_SR_DUMMY_RX; - c = stm32_get_char(port, &sr, &stm32_port->last_res); flag = TTY_NORMAL; - port->icount.rx++; + /* + * Status bits has to be cleared before reading the RDR: + * In FIFO mode, reading the RDR will pop the next data + * (if any) along with its status bits into the SR. + * Not doing so leads to misalignement between RDR and SR, + * and clear status bits of the next rx data. + * + * Clear errors flags for stm32f7 and stm32h7 compatible + * devices. On stm32f4 compatible devices, the error bit is + * cleared by the sequence [read SR - read DR]. + */ + if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG) + writel_relaxed(sr & USART_SR_ERR_MASK, + port->membase + ofs->icr); + + c = stm32_get_char(port, &sr, &stm32_port->last_res); + port->icount.rx++; if (sr & USART_SR_ERR_MASK) { - if (sr & USART_SR_LBD) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (sr & USART_SR_ORE) { - if (ofs->icr != UNDEF_REG) - writel_relaxed(USART_ICR_ORECF, - port->membase + - ofs->icr); + if (sr & USART_SR_ORE) { port->icount.overrun++; } else if (sr & USART_SR_PE) { port->icount.parity++; } else if (sr & USART_SR_FE) { - port->icount.frame++; + /* Break detection if character is null */ + if (!c) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else { + port->icount.frame++; + } } sr &= port->read_status_mask; - if (sr & USART_SR_LBD) - flag = TTY_BREAK; - else if (sr & USART_SR_PE) + if (sr & USART_SR_PE) { flag = TTY_PARITY; - else if (sr & USART_SR_FE) - flag = TTY_FRAME; + } else if (sr & USART_SR_FE) { + if (!c) + flag = TTY_BREAK; + else + flag = TTY_FRAME; + } } if (uart_handle_sysrq_char(port, c)) @@ -261,9 +291,12 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) uart_insert_char(port, sr, USART_SR_ORE, c, flag); } - spin_unlock(&port->lock); + if (threaded) + spin_unlock_irqrestore(&port->lock, flags); + else + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void stm32_tx_dma_complete(void *arg) @@ -271,27 +304,39 @@ static void stm32_tx_dma_complete(void *arg) struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32port->info->ofs; - unsigned int isr; - int ret; - - ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, - isr, - (isr & USART_SR_TC), - 10, 100000); - - if (ret) - dev_err(port->dev, "terminal count not set\n"); - - if (ofs->icr == UNDEF_REG) - stm32_clr_bits(port, ofs->isr, USART_SR_TC); - else - stm32_set_bits(port, ofs->icr, USART_CR_TC); + unsigned long flags; + dmaengine_terminate_async(stm32port->tx_ch); stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; /* Let's see if we have pending data to send */ + spin_lock_irqsave(&port->lock, flags); stm32_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void stm32_rx_dma_complete(void *arg) +{ + struct uart_port *port = arg; + struct stm32_port *stm32port = to_stm32_port(port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* + * If the dma controller is sending the data on + * the fly then we have CALLBACK_IGNORED + */ + + if (stm32port->rx_dma_cb == CALLBACK_NOT_CALLED) { + stm32port->rx_dma_cb = CALLBACK_CALLED; + spin_unlock_irqrestore(&port->lock, flags); + stm32_receive_chars(port, true); + return; + } + + spin_unlock_irqrestore(&port->lock, flags); } static void stm32_transmit_chars_pio(struct uart_port *port) @@ -299,27 +344,30 @@ static void stm32_transmit_chars_pio(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; - unsigned int isr; - int ret; if (stm32_port->tx_dma_busy) { stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32_port->tx_dma_busy = false; } - ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, - isr, - (isr & USART_SR_TXE), - 10, 100000); - - if (ret) - dev_err(port->dev, "tx empty not set\n"); - - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + while (!uart_circ_empty(xmit)) { + if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) + break; + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; + if (uart_circ_empty(xmit)) + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + else + if (stm32_port->fifoen) + stm32_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); } static void stm32_transmit_chars_dma(struct uart_port *port) @@ -377,7 +425,6 @@ static void stm32_transmit_chars_dma(struct uart_port *port) /* Issue pending DMA TX requests */ dma_async_issue_pending(stm32port->tx_ch); - stm32_clr_bits(port, ofs->isr, USART_SR_TC); stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); @@ -401,15 +448,18 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - if (uart_tx_stopped(port)) { - stm32_stop_tx(port); + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); return; } - if (uart_circ_empty(xmit)) { - stm32_stop_tx(port); - return; - } + if (ofs->icr == UNDEF_REG) + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + else + writel_relaxed(USART_ICR_TCCF, port->membase + ofs->icr); if (stm32_port->tx_ch) stm32_transmit_chars_dma(port); @@ -419,8 +469,12 @@ static void stm32_transmit_chars(struct uart_port *port) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) - stm32_stop_tx(port); + if (uart_circ_empty(xmit)) { + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + } } static irqreturn_t stm32_interrupt(int irq, void *ptr) @@ -430,10 +484,12 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; - spin_lock(&port->lock); - sr = readl_relaxed(port->membase + ofs->isr); + if ((sr & USART_SR_RTOF) && (ofs->icr != UNDEF_REG)) + writel_relaxed(USART_ICR_RTOCF, + port->membase + ofs->icr); + if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG)) writel_relaxed(USART_ICR_WUCF, port->membase + ofs->icr); @@ -441,10 +497,11 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) stm32_receive_chars(port, false); - if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { + spin_lock(&port->lock); stm32_transmit_chars(port); - - spin_unlock(&port->lock); + spin_unlock(&port->lock); + } if (stm32_port->rx_ch) return IRQ_WAKE_THREAD; @@ -456,13 +513,24 @@ static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + unsigned long flags; - spin_lock(&port->lock); - - if (stm32_port->rx_ch) + if (stm32_port->rx_ch) { + spin_lock_irqsave(&port->lock, flags); + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + spin_unlock_irqrestore(&port->lock, flags); + dma_sync_single_for_cpu(port->dev, + stm32_port->rx_dma_buf, + RX_BUF_L, DMA_FROM_DEVICE); stm32_receive_chars(port, true); - - spin_unlock(&port->lock); + dma_sync_single_for_device(port->dev, + stm32_port->rx_dma_buf, + RX_BUF_L, DMA_FROM_DEVICE); + spin_lock_irqsave(&port->lock, flags); + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAR); + spin_unlock_irqrestore(&port->lock, flags); + } return IRQ_HANDLED; } @@ -472,7 +540,10 @@ static unsigned int stm32_tx_empty(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; + if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) + return TIOCSER_TEMT; + + return 0; } static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -498,7 +569,15 @@ static void stm32_stop_tx(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); + else + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + + if (stm32_port->tx_dma_busy) { + dmaengine_terminate_async(stm32_port->tx_ch); + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + } } /* There are probably characters waiting to be transmitted. */ @@ -512,6 +591,22 @@ static void stm32_start_tx(struct uart_port *port) stm32_transmit_chars(port); } +/* Flush the transmit buffer. */ +static void stm32_flush_buffer(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + if (stm32_port->tx_ch) { + spin_lock(&port->lock); + dmaengine_terminate_async(stm32_port->tx_ch); + spin_unlock(&port->lock); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } +} + /* Throttle the remote when input buffer is about to overflow. */ static void stm32_throttle(struct uart_port *port) { @@ -520,7 +615,10 @@ static void stm32_throttle(struct uart_port *port) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); + if (stm32_port->cr3_irq) + stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + + stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); spin_unlock_irqrestore(&port->lock, flags); } @@ -532,7 +630,10 @@ static void stm32_unthrottle(struct uart_port *port) unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE); + if (stm32_port->cr3_irq) + stm32_set_bits(port, ofs->cr3, stm32_port->cr3_irq); + + stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq); spin_unlock_irqrestore(&port->lock, flags); } @@ -542,7 +643,10 @@ static void stm32_stop_rx(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); + if (stm32_port->cr3_irq) + stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + + stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); } /* Handle breaks - ignored by us */ @@ -554,7 +658,6 @@ static int stm32_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct stm32_usart_config *cfg = &stm32_port->info->cfg; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; @@ -565,18 +668,12 @@ static int stm32_startup(struct uart_port *port) if (ret) return ret; - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { - ret = dev_pm_set_dedicated_wake_irq(port->dev, - stm32_port->wakeirq); - if (ret) { - free_irq(port->irq, port); - return ret; - } - } + /* RX FIFO Flush */ + if (ofs->rqr != UNDEF_REG) + stm32_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); - val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - if (stm32_port->fifoen) - val |= USART_CR1_FIFOEN; + /* RX enabling */ + val = stm32_port->cr1_irq | USART_CR1_RE; stm32_set_bits(port, ofs->cr1, val); return 0; @@ -587,18 +684,62 @@ static void stm32_shutdown(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; - u32 val; + u32 val, isr; + int ret; - val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + val = USART_CR1_TXEIE | USART_CR1_TE; + val |= stm32_port->cr1_irq | USART_CR1_RE; val |= BIT(cfg->uart_enable_bit); if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; + + ret = readl_relaxed_poll_timeout(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "transmission complete not set\n"); + stm32_clr_bits(port, ofs->cr1, val); - dev_pm_clear_wake_irq(port->dev); + if (stm32_port->fifoen) + stm32_clr_bits(port, ofs->cr3, + USART_CR3_TXFTIE | USART_CR3_RXFTIE); + free_irq(port->irq, port); } +static int stm32_get_databits(struct ktermios *termios) +{ + unsigned int bits; + + tcflag_t cflag = termios->c_cflag; + + switch (cflag & CSIZE) { + /* + * CSIZE settings are not necessarily supported in hardware. + * CSIZE unsupported configurations are handled here to set word length + * to 8 bits word as default configuration and to print debug message. + */ + case CS5: + bits = 5; + break; + case CS6: + bits = 6; + break; + case CS7: + bits = 7; + break; + /* default including CS8 */ + default: + bits = 8; + break; + } + + return (bits); +} + static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -606,11 +747,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; struct serial_rs485 *rs485conf = &port->rs485; - unsigned int baud; + unsigned int baud, bits; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; - u32 cr1, cr2, cr3; + u32 cr1, cr2, cr3, isr; unsigned long flags; + int ret; if (!stm32_port->hw_flow_control) cflag &= ~CRTSCTS; @@ -619,29 +761,80 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&port->lock, flags); + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "transmission complete not set\n"); + /* Stop serial port and reset value */ writel_relaxed(0, port->membase + ofs->cr1); - cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + /* flush RX & TX FIFO */ + if (ofs->rqr != UNDEF_REG) + stm32_set_bits(port, ofs->rqr, + USART_RQR_TXFRQ | USART_RQR_RXFRQ); + cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; cr2 = 0; - cr3 = 0; + + /* Tx and RX FIFO configuration */ + cr3 = readl_relaxed(port->membase + ofs->cr3); + cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTIE; + if (stm32_port->fifoen) { + cr3 |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; + cr3 |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT; + } if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; + bits = stm32_get_databits(termios); + stm32_port->rdr_mask = (BIT(bits) - 1); + if (cflag & PARENB) { + bits++; cr1 |= USART_CR1_PCE; - if ((cflag & CSIZE) == CS8) { - if (cfg->has_7bits_data) - cr1 |= USART_CR1_M0; - else - cr1 |= USART_CR1_M; - } } + /* Word length configuration: + * CS8 + parity, 9 bits word aka [M1:M0] = 0b01 + * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10 + * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00 + * M0 and M1 already cleared by cr1 initialization. + */ + if (bits == 9) + cr1 |= USART_CR1_M0; + else if ((bits == 7) && cfg->has_7bits_data) + cr1 |= USART_CR1_M1; + else if (bits != 8) + dev_dbg(port->dev, "Unsupported data bits config: %u bits\n" + , bits); + + if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch || + stm32_port->fifoen)) { + if (cflag & CSTOPB) + bits = bits + 3; /* 1 start bit + 2 stop bits */ + else + bits = bits + 2; /* 1 start bit + 1 stop bit */ + + /* RX timeout irq to occur after last stop bit + bits */ + stm32_port->cr1_irq = USART_CR1_RTOIE; + writel_relaxed(bits, port->membase + ofs->rtor); + cr2 |= USART_CR2_RTOEN; + + /* Not using dma, enable fifo threshold irq */ + if (!stm32_port->rx_ch) + stm32_port->cr3_irq = USART_CR3_RXFTIE; + } + + cr1 |= stm32_port->cr1_irq; + cr3 |= stm32_port->cr3_irq; + if (cflag & PARODD) cr1 |= USART_CR1_PS; @@ -679,14 +872,14 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & INPCK) port->read_status_mask |= USART_SR_PE | USART_SR_FE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= USART_SR_LBD; + port->read_status_mask |= USART_SR_FE; /* Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) port->ignore_status_mask = USART_SR_PE | USART_SR_FE; if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= USART_SR_LBD; + port->ignore_status_mask |= USART_SR_FE; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). @@ -715,8 +908,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, } } else { - cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP); - cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); + cr3 &= ~USART_CR3_DEM; + cr3 &= ~USART_CR3_DEP; + cr1 &= ~USART_CR1_DEDT_MASK; + cr1 &= ~USART_CR1_DEAT_MASK; } writel_relaxed(cr3, port->membase + ofs->cr3); @@ -765,13 +960,13 @@ static void stm32_pm(struct uart_port *port, unsigned int state, switch (state) { case UART_PM_STATE_ON: - clk_prepare_enable(stm32port->clk); + pm_runtime_get_sync(port->dev); break; case UART_PM_STATE_OFF: spin_lock_irqsave(&port->lock, flags); stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); - clk_disable_unprepare(stm32port->clk); + pm_runtime_put_sync(port->dev); break; } } @@ -788,6 +983,7 @@ static const struct uart_ops stm32_uart_ops = { .break_ctl = stm32_break_ctl, .startup = stm32_startup, .shutdown = stm32_shutdown, + .flush_buffer = stm32_flush_buffer, .set_termios = stm32_set_termios, .pm = stm32_pm, .type = stm32_type, @@ -802,20 +998,60 @@ static int stm32_init_port(struct stm32_port *stm32port, { struct uart_port *port = &stm32port->port; struct resource *res; + struct pinctrl *uart_pinctrl; int ret; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &stm32_uart_ops; port->dev = &pdev->dev; - port->irq = platform_get_irq(pdev, 0); - port->rs485_config = stm32_config_rs485; + ret = platform_get_irq_byname(pdev, "event"); + if (ret <= 0) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get event IRQ: %d\n", + ret); + return ret ? ret : -ENODEV; + } + port->irq = ret; + + port->fifosize = stm32port->info->cfg.fifosize; + + port->rs485_config = stm32_config_rs485; stm32_init_rs485(port, pdev); - stm32port->wakeirq = platform_get_irq(pdev, 1); + if (stm32port->info->cfg.has_wakeup) { + stm32port->wakeirq = platform_get_irq_byname(pdev, "wakeup"); + if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO) { + if (stm32port->wakeirq != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Can't get event wake IRQ: %d\n", + stm32port->wakeirq); + return stm32port->wakeirq ? stm32port->wakeirq : + -ENODEV; + } + } + stm32port->fifoen = stm32port->info->cfg.has_fifo; + uart_pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(uart_pinctrl)) { + ret = PTR_ERR(uart_pinctrl); + if (ret != -ENODEV) { + dev_err(&pdev->dev,"Can't get pinctrl, error %d\n", + ret); + return ret; + } + stm32port->console_pins = ERR_PTR(-ENODEV); + } + else + stm32port->console_pins = pinctrl_lookup_state + (uart_pinctrl,"no_console_suspend"); + + if (IS_ERR(stm32port->console_pins) + && PTR_ERR(stm32port->console_pins) != -ENODEV) + return PTR_ERR(stm32port->console_pins); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); port->membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->membase)) @@ -862,7 +1098,11 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) stm32_ports[id].hw_flow_control = of_property_read_bool(np, "st,hw-flow-ctrl"); stm32_ports[id].port.line = id; + stm32_ports[id].cr1_irq = USART_CR1_RXNEIE; + stm32_ports[id].cr3_irq = 0; stm32_ports[id].last_res = RX_BUF_L; + stm32_ports[id].rx_dma_buf = 0; + stm32_ports[id].tx_dma_buf = 0; return &stm32_ports[id]; } @@ -890,10 +1130,8 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, /* Request DMA RX channel */ stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); - if (!stm32port->rx_ch) { - dev_info(dev, "rx dma alloc failed\n"); + if (!stm32port->rx_ch) return -ENODEV; - } stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L, &stm32port->rx_dma_buf, GFP_KERNEL); @@ -925,9 +1163,8 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, goto config_err; } - /* No callback as dma buffer is drained on usart interrupt */ - desc->callback = NULL; - desc->callback_param = NULL; + desc->callback = stm32_rx_dma_complete; + desc->callback_param = port; /* Push current DMA transaction in the pending queue */ cookie = dmaengine_submit(desc); @@ -962,10 +1199,8 @@ static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, /* Request DMA TX channel */ stm32port->tx_ch = dma_request_slave_channel(dev, "tx"); - if (!stm32port->tx_ch) { - dev_info(dev, "tx dma alloc failed\n"); + if (!stm32port->tx_ch) return -ENODEV; - } stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L, &stm32port->tx_dma_buf, GFP_KERNEL); @@ -1020,15 +1255,22 @@ static int stm32_serial_probe(struct platform_device *pdev) if (ret) return ret; - if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) { + if (stm32port->wakeirq > 0) { ret = device_init_wakeup(&pdev->dev, true); if (ret) goto err_uninit; + + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, + stm32port->wakeirq); + if (ret) + goto err_nowup; + + device_set_wakeup_enable(&pdev->dev, false); } ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); if (ret) - goto err_nowup; + goto err_wirq; ret = stm32_of_dma_rx_probe(stm32port, pdev); if (ret) @@ -1040,10 +1282,19 @@ static int stm32_serial_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &stm32port->port); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + return 0; +err_wirq: + if (stm32port->wakeirq > 0) + dev_pm_clear_wake_irq(&pdev->dev); + err_nowup: - if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + if (stm32port->wakeirq > 0) device_init_wakeup(&pdev->dev, false); err_uninit: @@ -1057,12 +1308,16 @@ static int stm32_serial_remove(struct platform_device *pdev) struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct stm32_usart_config *cfg = &stm32_port->info->cfg; + int err; + + pm_runtime_get_sync(&pdev->dev); stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); - if (stm32_port->rx_ch) + if (stm32_port->rx_ch) { + dmaengine_terminate_async(stm32_port->rx_ch); dma_release_channel(stm32_port->rx_ch); + } if (stm32_port->rx_dma_buf) dma_free_coherent(&pdev->dev, @@ -1071,20 +1326,29 @@ static int stm32_serial_remove(struct platform_device *pdev) stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); - if (stm32_port->tx_ch) + if (stm32_port->tx_ch) { + dmaengine_terminate_async(stm32_port->tx_ch); dma_release_channel(stm32_port->tx_ch); + } if (stm32_port->tx_dma_buf) dma_free_coherent(&pdev->dev, TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) + if (stm32_port->wakeirq > 0) { + dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); + } clk_disable_unprepare(stm32_port->clk); - return uart_remove_one_port(&stm32_usart_driver, port); + err = uart_remove_one_port(&stm32_usart_driver, port); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + return err; } @@ -1195,7 +1459,7 @@ static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable) struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 val; - if (!cfg->has_wakeup || stm32_port->wakeirq < 0) + if (stm32_port->wakeirq <= 0) return; if (enable) { @@ -1207,21 +1471,36 @@ static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable) val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE; writel_relaxed(val, port->membase + ofs->cr3); stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + enable_irq_wake(stm32_port->wakeirq); } else { stm32_clr_bits(port, ofs->cr1, USART_CR1_UESM); + disable_irq_wake(stm32_port->wakeirq); } } static int stm32_serial_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); + struct stm32_port *stm32_port = to_stm32_port(port); + + if (device_may_wakeup(dev) || dev->power.wakeup_path) + stm32_serial_enable_wakeup(port, true); uart_suspend_port(&stm32_usart_driver, port); - if (device_may_wakeup(dev)) - stm32_serial_enable_wakeup(port, true); - else - stm32_serial_enable_wakeup(port, false); + if (uart_console(port) && !console_suspend_enabled) { + if (IS_ERR(stm32_port->console_pins)) { + dev_err(dev, "no_console_suspend pinctrl not found\n"); + return PTR_ERR(stm32_port->console_pins); + } + + pinctrl_select_state(dev->pins->p, stm32_port->console_pins); + } else { + if (device_may_wakeup(dev) || dev->power.wakeup_path) + pinctrl_pm_select_idle_state(dev); + else + pinctrl_pm_select_sleep_state(dev); + } return 0; } @@ -1230,14 +1509,40 @@ static int stm32_serial_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) + pinctrl_pm_select_default_state(dev); + + if (device_may_wakeup(dev) || dev->power.wakeup_path) stm32_serial_enable_wakeup(port, false); return uart_resume_port(&stm32_usart_driver, port); } #endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM +static int stm32_serial_runtime_suspend(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct stm32_port *stm32port = container_of(port, + struct stm32_port, port); + + clk_disable_unprepare(stm32port->clk); + + return 0; +} + +static int stm32_serial_runtime_resume(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct stm32_port *stm32port = container_of(port, + struct stm32_port, port); + + return clk_prepare_enable(stm32port->clk); +} +#endif /* CONFIG_PM */ + static const struct dev_pm_ops stm32_serial_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_serial_runtime_suspend, + stm32_serial_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume) }; diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 6f294e2..2a68bc4 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -27,6 +27,7 @@ struct stm32_usart_config { bool has_7bits_data; bool has_wakeup; bool has_fifo; + int fifosize; }; struct stm32_usart_info { @@ -54,6 +55,7 @@ struct stm32_usart_info stm32f4_info = { .cfg = { .uart_enable_bit = 13, .has_7bits_data = false, + .fifosize = 1, } }; @@ -74,6 +76,7 @@ struct stm32_usart_info stm32f7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .fifosize = 1, } }; @@ -96,6 +99,7 @@ struct stm32_usart_info stm32h7_info = { .has_7bits_data = true, .has_wakeup = true, .has_fifo = true, + .fifosize = 16, } }; @@ -108,7 +112,6 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_RXNE BIT(5) #define USART_SR_TC BIT(6) #define USART_SR_TXE BIT(7) -#define USART_SR_LBD BIT(8) #define USART_SR_CTSIF BIT(9) #define USART_SR_CTS BIT(10) /* F7 */ #define USART_SR_RTOF BIT(11) /* F7 */ @@ -120,14 +123,10 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_SBKF BIT(18) /* F7 */ #define USART_SR_WUF BIT(20) /* H7 */ #define USART_SR_TEACK BIT(21) /* F7 */ -#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ - USART_SR_FE | USART_SR_PE) +#define USART_SR_ERR_MASK (USART_SR_ORE | USART_SR_FE | USART_SR_PE) /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) -/* USART_ICR (F7) */ -#define USART_CR_TC BIT(6) - /* USART_DR */ #define USART_DR_MASK GENMASK(8, 0) @@ -151,8 +150,7 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR1_PS BIT(9) #define USART_CR1_PCE BIT(10) #define USART_CR1_WAKE BIT(11) -#define USART_CR1_M BIT(12) -#define USART_CR1_M0 BIT(12) /* F7 */ +#define USART_CR1_M0 BIT(12) /* F7 (CR1_M for F4) */ #define USART_CR1_MME BIT(13) /* F7 */ #define USART_CR1_CMIE BIT(14) /* F7 */ #define USART_CR1_OVER8 BIT(15) @@ -169,8 +167,6 @@ struct stm32_usart_info stm32h7_info = { /* USART_CR2 */ #define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ #define USART_CR2_ADDM7 BIT(4) /* F7 */ -#define USART_CR2_LBDL BIT(5) -#define USART_CR2_LBDIE BIT(6) #define USART_CR2_LBCL BIT(8) #define USART_CR2_CPHA BIT(9) #define USART_CR2_CPOL BIT(10) @@ -209,6 +205,19 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR3_WUS_MASK GENMASK(21, 20) /* H7 */ #define USART_CR3_WUS_START_BIT BIT(21) /* H7 */ #define USART_CR3_WUFIE BIT(22) /* H7 */ +#define USART_CR3_TXFTIE BIT(23) /* H7 */ +#define USART_CR3_TCBGTIE BIT(24) /* H7 */ +#define USART_CR3_RXFTCFG_MASK GENMASK(27, 25) /* H7 */ +#define USART_CR3_RXFTCFG_SHIFT 25 /* H7 */ +#define USART_CR3_RXFTIE BIT(28) /* H7 */ +#define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ +#define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ + +/* TX FIFO threashold set to half of its depth */ +#define USART_CR3_TXFTCFG_HALF 0x2 + +/* RX FIFO threashold set to half of its depth */ +#define USART_CR3_RXFTCFG_HALF 0x2 /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) @@ -227,12 +236,10 @@ struct stm32_usart_info stm32h7_info = { /* USART_ICR */ #define USART_ICR_PECF BIT(0) /* F7 */ -#define USART_ICR_FFECF BIT(1) /* F7 */ -#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_FECF BIT(1) /* F7 */ #define USART_ICR_ORECF BIT(3) /* F7 */ #define USART_ICR_IDLECF BIT(4) /* F7 */ #define USART_ICR_TCCF BIT(6) /* F7 */ -#define USART_ICR_LBDCF BIT(8) /* F7 */ #define USART_ICR_CTSCF BIT(9) /* F7 */ #define USART_ICR_RTOCF BIT(11) /* F7 */ #define USART_ICR_EOBCF BIT(12) /* F7 */ @@ -242,9 +249,15 @@ struct stm32_usart_info stm32h7_info = { #define STM32_SERIAL_NAME "ttySTM" #define STM32_MAX_PORTS 8 -#define RX_BUF_L 200 /* dma rx buffer length */ +#define RX_BUF_L 160 /* dma rx buffer length */ #define RX_BUF_P RX_BUF_L /* dma rx buffer period */ -#define TX_BUF_L 200 /* dma tx buffer length */ +#define TX_BUF_L RX_BUF_L /* dma tx buffer length */ + +enum dma_cb { + CALLBACK_NOT_CALLED, + CALLBACK_CALLED, + CALLBACK_IGNORED, +}; struct stm32_port { struct uart_port port; @@ -256,11 +269,16 @@ struct stm32_port { struct dma_chan *tx_ch; /* dma tx channel */ dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ unsigned char *tx_buf; /* dma tx buffer cpu address */ + u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */ + u32 cr3_irq; /* USART_CR3_RXFTIE */ int last_res; + enum dma_cb rx_dma_cb; /* dma rx callback status */ bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; bool fifoen; int wakeirq; + struct pinctrl_state *console_pins; + int rdr_mask; /* receive data register mask */ }; static struct stm32_port stm32_ports[STM32_MAX_PORTS]; diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 440320c..2bcd694 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o platform.o +dwc2-y := core.o core_intr.o platform.o drd.o dwc2-y += params.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 55d5ae2..712cef9 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -83,6 +83,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1); gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG); gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL); + gr->ggpio = dwc2_readl(hsotg, GGPIO); gr->pcgcctl = dwc2_readl(hsotg, PCGCTL); gr->valid = true; @@ -112,21 +113,82 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) gr->valid = false; dwc2_writel(hsotg, 0xffffffff, GINTSTS); + dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); dwc2_writel(hsotg, gr->gotgctl, GOTGCTL); dwc2_writel(hsotg, gr->gintmsk, GINTMSK); - dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); - dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG); dwc2_writel(hsotg, gr->grxfsiz, GRXFSIZ); dwc2_writel(hsotg, gr->gnptxfsiz, GNPTXFSIZ); dwc2_writel(hsotg, gr->gdfifocfg, GDFIFOCFG); dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1); dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG); dwc2_writel(hsotg, gr->pcgcctl, PCGCTL); + dwc2_writel(hsotg, gr->ggpio, GGPIO); dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL); return 0; } +int dwc2_backup_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + return 0; +} + +int dwc2_restore_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg, 0); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + + return 0; +} + /** * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down. * @@ -136,7 +198,6 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) { u32 pcgcctl; - int ret = 0; if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) return -ENOTSUPP; @@ -154,31 +215,11 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(100); - if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_restore_device_registers(hsotg, 0); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); - return ret; - } - } - } - return ret; + if (restore) + return dwc2_restore_registers(hsotg); + + return 0; } /** @@ -189,34 +230,14 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) { u32 pcgcctl; - int ret = 0; + int ret; if (!hsotg->params.power_down) return -ENOTSUPP; - /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); + ret = dwc2_backup_registers(hsotg); + if (ret) return ret; - } - - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); - return ret; - } - } /* * Clear any pending interrupts since dwc2 will not be able to @@ -238,7 +259,7 @@ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) pcgcctl |= PCGCTL_STOPPCLK; dwc2_writel(hsotg, pcgcctl, PCGCTL); - return ret; + return 0; } /** diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index cc9c93a..74d81f6 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -393,10 +393,28 @@ enum dwc2_ep0_state { * 0 - No * 1 - Yes * @hird_threshold: Value of BESL or HIRD Threshold. + * @ref_clk_per: Indicates in terms of pico seconds the period + * of ref_clk. + * 62500 - 16MHz + * 58823 - 17MHz + * 52083 - 19.2MHz + * 50000 - 20MHz + * 41666 - 24MHz + * 33333 - 30MHz (default) + * 25000 - 40MHz + * @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which + * the controller should generate an interrupt if the + * device had been in L1 state until that period. + * This is used by SW to initiate Remote WakeUp in the + * controller so as to sync to the uF number from the host. * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO * register. * 0 - Deactivate the transceiver (default) * 1 - Activate the transceiver + * @activate_stm_id_vb_detection: Activate external ID pin and Vbuslevel + * detection using GGPIO register. + * 0 - Deactivate the external level detection (default) + * 1 - Activate the external level detection * @g_dma: Enables gadget dma usage (default: autodetect). * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). * @g_rx_fifo_size: The periodic rx fifo size for the device, in @@ -416,6 +434,9 @@ enum dwc2_ep0_state { * back to DWC2_SPEED_PARAM_HIGH while device is gone. * 0 - No (default) * 1 - Yes + * @service_interval: Enable service interval based scheduling. + * 0 - No + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -461,13 +482,19 @@ struct dwc2_core_params { bool lpm_clock_gating; bool besl; bool hird_threshold_en; + bool service_interval; u8 hird_threshold; bool activate_stm_fs_transceiver; + bool activate_stm_id_vb_detection; bool ipg_isoc_en; u16 max_packet_count; u32 max_transfer_size; u32 ahbcfg; + /* GREFCLK parameters */ + u32 ref_clk_per; + u16 sof_cnt_wkup_alert; + /* Host parameters */ bool host_dma; bool dma_desc_enable; @@ -605,6 +632,10 @@ struct dwc2_core_params { * FIFO sizing is enabled 16 to 32768 * Actual maximum value is autodetected and also * the default. + * @service_interval_mode: For enabling service interval based scheduling in the + * controller. + * 0 - Disable + * 1 - Enable */ struct dwc2_hw_params { unsigned op_mode:3; @@ -635,6 +666,7 @@ struct dwc2_hw_params { unsigned utmi_phy_data_width:2; unsigned lpm_mode:1; unsigned ipg_isoc_en:1; + unsigned service_interval_mode:1; u32 snpsid; u32 dev_ep_dirs; u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; @@ -653,6 +685,7 @@ struct dwc2_hw_params { * @grxfsiz: Backup of GRXFSIZ register * @gnptxfsiz: Backup of GNPTXFSIZ register * @gi2cctl: Backup of GI2CCTL register + * @ggpio: Backup of GGPIO register * @glpmcfg: Backup of GLPMCFG register * @gdfifocfg: Backup of GDFIFOCFG register * @pcgcctl: Backup of PCGCCTL register @@ -669,6 +702,7 @@ struct dwc2_gregs_backup { u32 grxfsiz; u32 gnptxfsiz; u32 gi2cctl; + u32 ggpio; u32 glpmcfg; u32 pcgcctl; u32 pcgcctl1; @@ -828,6 +862,8 @@ struct dwc2_hregs_backup { * - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_HOST * - USB_DR_MODE_OTG + * @edev: extcon handle + * @edev_nb: extcon notifier * @hcd_enabled: Host mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. @@ -842,6 +878,8 @@ struct dwc2_hregs_backup { * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies * @vbus_supply: Regulator supplying vbus. + * @usb33d: Optional 3.3v regulator used on some stm32 devices to + * supply ID and VBUS detection hardware. * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd @@ -974,6 +1012,7 @@ struct dwc2_hregs_backup { * @ctrl_out_desc_dma: EP0 OUT data phase desc chain DMA address * @ctrl_out_desc: EP0 OUT data phase desc chain pointer * @irq: Interrupt request line number + * @wakeirq: Wakeup interrupt request line number * @clk: Pointer to otg clock * @reset: Pointer to dwc2 reset controller * @reset_ecc: Pointer to dwc2 optional reset controller in Stratix10. @@ -1014,6 +1053,8 @@ struct dwc2_hsotg { struct dwc2_core_params params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + struct extcon_dev *edev; + struct notifier_block edev_nb; unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; @@ -1025,11 +1066,13 @@ struct dwc2_hsotg { struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; struct regulator *vbus_supply; + struct regulator *usb33d; u32 phyif; spinlock_t lock; void *priv; int irq; + int wakeirq; struct clk *clk; struct reset_control *reset; struct reset_control *reset_ecc; @@ -1276,6 +1319,8 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd); void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, int is_host); +int dwc2_backup_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_registers(struct dwc2_hsotg *hsotg); int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg); @@ -1325,6 +1370,8 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; } +int dwc2_drd_init(struct dwc2_hsotg *hsotg); + /* * Dump core registers and SPRAM */ @@ -1341,6 +1388,7 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); int dwc2_gadget_init(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); @@ -1354,6 +1402,7 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); +void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1388,6 +1437,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 22d015b..7f62f4c 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -701,6 +701,7 @@ static int params_show(struct seq_file *seq, void *v) print_param(seq, p, besl); print_param(seq, p, hird_threshold_en); print_param(seq, p, hird_threshold); + print_param(seq, p, service_interval); print_param(seq, p, host_dma); print_param(seq, p, g_dma); print_param(seq, p, g_dma_desc); diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c new file mode 100644 index 0000000..7d812b7 --- /dev/null +++ b/drivers/usb/dwc2/drd.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drd.c - DesignWare USB2 DRD Controller Dual-role support + * + * Copyright (C) 2019 STMicroelectronics + * + * Author(s): Amelie Delaunay + */ + +#include +#include +#include +#include "core.h" + +static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + u32 gotgctl; + + spin_lock_irqsave(&hsotg->lock, flags); + + gotgctl = dwc2_readl(hsotg, GOTGCTL); + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN; + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if A-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_ASESVLD)) || + (!valid && !(gotgctl & GOTGCTL_ASESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if B-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_BSESVLD)) || + (!valid && !(gotgctl & GOTGCTL_BSESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static void dwc2_drd_update(struct dwc2_hsotg *hsotg) +{ + int avalid, bvalid; + unsigned long flags; + + avalid = extcon_get_state(hsotg->edev, EXTCON_USB_HOST); + if (avalid < 0) + avalid = 0; + + bvalid = extcon_get_state(hsotg->edev, EXTCON_USB); + if (bvalid < 0) + bvalid = 0; + + /* Skip session not in line with dr_mode */ + if ((avalid && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) || + (bvalid && hsotg->dr_mode == USB_DR_MODE_HOST)) + return; + + /* Skip session if core is in test mode */ + if (!avalid && !bvalid && hsotg->test_mode) { + dev_dbg(hsotg->dev, "Core is in test mode\n"); + return; + } + + spin_lock_irqsave(&hsotg->lock, flags); + + if (avalid) { + if (dwc2_ovr_avalid(hsotg, true)) + goto unlock; + + if (hsotg->dr_mode == USB_DR_MODE_OTG) + /* + * This will raise a Connector ID Status Change + * Interrupt - connID A + */ + dwc2_force_mode(hsotg, true); + } else if (bvalid) { + if (dwc2_ovr_bvalid(hsotg, true)) + goto unlock; + + if (hsotg->dr_mode == USB_DR_MODE_OTG) + /* + * This will raise a Connector ID Status Change + * Interrupt - connID B + */ + dwc2_force_mode(hsotg, false); + + /* This clear DCTL.SFTDISCON bit */ + dwc2_hsotg_core_connect(hsotg); + } else { + if (dwc2_ovr_avalid(hsotg, false) && + dwc2_ovr_bvalid(hsotg, false)) + goto unlock; + + if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) + /* This set DCTL.SFTDISCON bit */ + dwc2_hsotg_core_disconnect(hsotg); + + dwc2_force_dr_mode(hsotg); + } + + dev_dbg(hsotg->dev, "%s-session valid\n", + avalid ? "A" : bvalid ? "B" : "No"); + +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static int dwc2_drd_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc2_hsotg *hsotg = container_of(nb, struct dwc2_hsotg, edev_nb); + + dwc2_drd_update(hsotg); + + return NOTIFY_DONE; +} + +int dwc2_drd_init(struct dwc2_hsotg *hsotg) +{ + struct extcon_dev *edev; + int ret; + + if (of_property_read_bool(hsotg->dev->of_node, "extcon")) { + edev = extcon_get_edev_by_phandle(hsotg->dev, 0); + if (IS_ERR(edev)) { + ret = PTR_ERR(edev); + if (ret != -EPROBE_DEFER) + dev_err(hsotg->dev, + "couldn't get extcon device: %d\n", + ret); + return ret; + } + + hsotg->edev_nb.notifier_call = dwc2_drd_notifier; + ret = devm_extcon_register_notifier(hsotg->dev, edev, + EXTCON_USB, + &hsotg->edev_nb); + if (ret < 0) { + dev_err(hsotg->dev, + "USB cable notifier register failed: %d\n", + ret); + return ret; + } + + ret = devm_extcon_register_notifier(hsotg->dev, edev, + EXTCON_USB_HOST, + &hsotg->edev_nb); + if (ret < 0) { + dev_err(hsotg->dev, + "USB-HOST cable notifier register failed: %d\n", + ret); + return ret; + } + + hsotg->edev = edev; + + /* Enable override and initialize values */ + dwc2_ovr_init(hsotg); + + dwc2_drd_update(hsotg); + } + + return 0; +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 03614ef..3ebd5dd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -123,6 +123,24 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) } /** + * dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number + * by one. + * @hs_ep: The endpoint. + * + * This function used in service interval based scheduling flow to calculate + * descriptor frame number filed value. For service interval mode frame + * number in descriptor should point to last (u)frame in the interval. + * + */ +static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep) +{ + if (hs_ep->target_frame) + hs_ep->target_frame -= 1; + else + hs_ep->target_frame = DSTS_SOFFN_LIMIT; +} + +/** * dwc2_hsotg_en_gsint - enable one or more of the general interrupt * @hsotg: The device state * @ints: A bitmask of the interrupts to enable @@ -228,6 +246,27 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) } /** + * dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt + * + * @hsotg: Programming view of the DWC_otg controller + * + */ +static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg) +{ + u32 gintsts2; + u32 gintmsk2; + + gintsts2 = dwc2_readl(hsotg, GINTSTS2); + gintmsk2 = dwc2_readl(hsotg, GINTMSK2); + + if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) { + dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__); + dwc2_set_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT); + dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); + } +} + +/** * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode * TX FIFOs * @@ -2810,6 +2849,23 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) if (using_desc_dma(hsotg)) { hs_ep->target_frame = hsotg->frame_number; dwc2_gadget_incr_frame_num(hs_ep); + + /* In service interval mode target_frame must + * be set to last (u)frame of the service interval. + */ + if (hsotg->params.service_interval) { + /* Set target_frame to the first (u)frame of + * the service interval + */ + hs_ep->target_frame &= ~hs_ep->interval + 1; + + /* Set target_frame to the last (u)frame of + * the service interval + */ + dwc2_gadget_incr_frame_num(hs_ep); + dwc2_gadget_dec_frame_num_by_one(hs_ep); + } + dwc2_gadget_start_isoc_ddma(hs_ep); return; } @@ -3125,6 +3181,7 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) hsotg->connected = 0; hsotg->test_mode = 0; + /* all endpoints should be shutdown */ for (ep = 0; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) kill_all_requests(hsotg, hsotg->eps_in[ep], @@ -3175,6 +3232,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) GINTSTS_PTXFEMP | \ GINTSTS_RXFLVL) +static int dwc2_hsotg_ep_disable(struct usb_ep *ep); /** * dwc2_hsotg_core_init - issue softreset to the core * @hsotg: The device state @@ -3189,13 +3247,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, u32 val; u32 usbcfg; u32 dcfg = 0; + int ep; /* Kill any ep0 requests as controller will be reinitialized */ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); - if (!is_usb_reset) + if (!is_usb_reset) { if (dwc2_core_reset(hsotg, true)) return; + } else { + /* all endpoints should be shutdown */ + for (ep = 1; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) + dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + if (hsotg->eps_out[ep]) + dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + } + } /* * we must now enable ep0 ready for host detection and then @@ -3310,6 +3378,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK); } + /* Enable Service Interval mode if supported */ + if (using_desc_dma(hsotg) && hsotg->params.service_interval) + dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED); + dwc2_writel(hsotg, 0, DAINTMSK); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", @@ -3366,6 +3438,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* configure the core to support LPM */ dwc2_gadget_init_lpm(hsotg); + /* program GREFCLK register if needed */ + if (using_desc_dma(hsotg) && hsotg->params.service_interval) + dwc2_gadget_program_ref_clk(hsotg); + /* must be at-least 3ms to allow bus to see disconnect */ mdelay(3); @@ -3378,7 +3454,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_readl(hsotg, DOEPCTL0)); } -static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); @@ -3387,7 +3463,8 @@ static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); + if (!hsotg->edev || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD)) + dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); } /** @@ -3674,6 +3751,10 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) goto irq_retry; + /* Check WKUP_ALERT interrupt*/ + if (hsotg->params.service_interval) + dwc2_gadget_wkup_alert_handler(hsotg); + spin_unlock(&hsotg->lock); return IRQ_HANDLED; @@ -3993,7 +4074,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; int index = hs_ep->index; - unsigned long flags; u32 epctrl_reg; u32 ctrl; @@ -4011,8 +4091,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - spin_lock_irqsave(&hsotg->lock, flags); - ctrl = dwc2_readl(hsotg, epctrl_reg); if (ctrl & DXEPCTL_EPENA) @@ -4035,10 +4113,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) hs_ep->fifo_index = 0; hs_ep->fifo_size = 0; - spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } +static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep) +{ + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg *hsotg = hs_ep->parent; + unsigned long flags; + int ret; + + spin_lock_irqsave(&hsotg->lock, flags); + ret = dwc2_hsotg_ep_disable(ep); + spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; +} + /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -4186,7 +4276,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) static const struct usb_ep_ops dwc2_hsotg_ep_ops = { .enable = dwc2_hsotg_ep_enable, - .disable = dwc2_hsotg_ep_disable, + .disable = dwc2_hsotg_ep_disable_lock, .alloc_request = dwc2_hsotg_ep_alloc_request, .free_request = dwc2_hsotg_ep_free_request, .queue = dwc2_hsotg_ep_queue_lock, @@ -4326,9 +4416,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) /* all endpoints should be shutdown */ for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); } spin_lock_irqsave(&hsotg->lock, flags); @@ -4774,11 +4864,11 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) hsotg->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock_irqrestore(&hsotg->lock, flags); - for (ep = 0; ep < hsotg->num_of_eps; ep++) { + for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); } } @@ -4945,8 +5035,32 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; dwc2_writel(hsotg, val, GLPMCFG); dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); + + /* Unmask WKUP_ALERT Interrupt */ + if (hsotg->params.service_interval) + dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK); +} + +/** + * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode + * + * @hsotg: Programming view of DWC_otg controller + * + */ +void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) +{ + u32 val = 0; + + val |= GREFCLK_REF_CLK_MODE; + val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT; + val |= hsotg->params.sof_cnt_wkup_alert << + GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT; + + dwc2_writel(hsotg, val, GREFCLK); + dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK)); } /** diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 260010a..7f128b1 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -125,7 +125,7 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) { - u32 usbcfg, ggpio, i2cctl; + u32 usbcfg, i2cctl; int retval = 0; /* @@ -149,19 +149,6 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) return retval; } } - - if (hsotg->params.activate_stm_fs_transceiver) { - ggpio = dwc2_readl(hsotg, GGPIO); - if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { - dev_dbg(hsotg->dev, "Activating transceiver\n"); - /* - * STM32F4x9 uses the GGPIO register as general - * core configuration register. - */ - ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; - dwc2_writel(hsotg, ggpio, GGPIO); - } - } } /* @@ -358,16 +345,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg) { - int ret; - - hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); - if (IS_ERR(hsotg->vbus_supply)) { - ret = PTR_ERR(hsotg->vbus_supply); - hsotg->vbus_supply = NULL; - return ret == -ENODEV ? 0 : ret; - } + if (hsotg->vbus_supply) + return regulator_enable(hsotg->vbus_supply); - return regulator_enable(hsotg->vbus_supply); + return 0; } static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg) @@ -1328,14 +1309,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, u32 remaining_count; u32 byte_count; u32 dword_count; - u32 __iomem *data_fifo; u32 *data_buf = (u32 *)chan->xfer_buf; if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "%s()\n", __func__); - data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num)); - remaining_count = chan->xfer_len - chan->xfer_count; if (remaining_count > chan->max_packet) byte_count = chan->max_packet; @@ -3564,6 +3542,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, u32 port_status; u32 speed; u32 pcgctl; + u32 pwr; switch (typereq) { case ClearHubFeature: @@ -3612,8 +3591,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); + pwr = hprt0 & HPRT0_PWR; hprt0 &= ~HPRT0_PWR; dwc2_writel(hsotg, hprt0, HPRT0); + if (pwr) + dwc2_vbus_supply_exit(hsotg); break; case USB_PORT_FEAT_INDICATOR: @@ -3823,8 +3805,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); + pwr = hprt0 & HPRT0_PWR; hprt0 |= HPRT0_PWR; dwc2_writel(hsotg, hprt0, HPRT0); + if (!pwr) + dwc2_vbus_supply_init(hsotg); break; case USB_PORT_FEAT_RESET: @@ -3841,6 +3826,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dwc2_writel(hsotg, 0, PCGCTL); hprt0 = dwc2_read_hprt0(hsotg); + pwr = hprt0 & HPRT0_PWR; /* Clear suspend bit if resetting from suspend state */ hprt0 &= ~HPRT0_SUSP; @@ -3854,6 +3840,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "In host mode, hprt0=%08x\n", hprt0); dwc2_writel(hsotg, hprt0, HPRT0); + if (!pwr) + dwc2_vbus_supply_init(hsotg); } /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ @@ -4393,6 +4381,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); struct usb_bus *bus = hcd_to_bus(hcd); unsigned long flags; + u32 hprt0; int ret; dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); @@ -4409,12 +4398,16 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) dwc2_hcd_reinit(hsotg); - /* enable external vbus supply before resuming root hub */ - spin_unlock_irqrestore(&hsotg->lock, flags); - ret = dwc2_vbus_supply_init(hsotg); - if (ret) - return ret; - spin_lock_irqsave(&hsotg->lock, flags); + hprt0 = dwc2_read_hprt0(hsotg); + /* Has vbus power been turned on in dwc2_core_host_init ? */ + if (hprt0 & HPRT0_PWR) { + /* Enable external vbus supply before resuming root hub */ + spin_unlock_irqrestore(&hsotg->lock, flags); + ret = dwc2_vbus_supply_init(hsotg); + if (ret) + return ret; + spin_lock_irqsave(&hsotg->lock, flags); + } /* Initialize and connect root hub if one is not already attached */ if (bus->root_hub) { @@ -4436,6 +4429,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + u32 hprt0; /* Turn off all host-specific interrupts */ dwc2_disable_host_interrupts(hsotg); @@ -4444,6 +4438,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) synchronize_irq(hcd->irq); spin_lock_irqsave(&hsotg->lock, flags); + hprt0 = dwc2_read_hprt0(hsotg); /* Ensure hcd is disconnected */ dwc2_hcd_disconnect(hsotg, true); dwc2_hcd_stop(hsotg); @@ -4452,7 +4447,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&hsotg->lock, flags); - dwc2_vbus_supply_exit(hsotg); + /* keep balanced supply init/exit by checking HPRT0_PWR */ + if (hprt0 & HPRT0_PWR) + dwc2_vbus_supply_exit(hsotg); usleep_range(1000, 3000); } diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 0ca8e7b..7500954 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -54,6 +54,12 @@ #define GOTGCTL_HSTSETHNPEN BIT(10) #define GOTGCTL_HNPREQ BIT(9) #define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_BVALOVAL BIT(7) +#define GOTGCTL_BVALOEN BIT(6) +#define GOTGCTL_AVALOVAL BIT(5) +#define GOTGCTL_AVALOEN BIT(4) +#define GOTGCTL_VBVALOVAL BIT(3) +#define GOTGCTL_VBVALOEN BIT(2) #define GOTGCTL_SESREQ BIT(1) #define GOTGCTL_SESREQSCS BIT(0) @@ -227,6 +233,8 @@ #define GPVNDCTL HSOTG_REG(0x0034) #define GGPIO HSOTG_REG(0x0038) #define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) +#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21) +#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22) #define GUID HSOTG_REG(0x003c) #define GSNPSID HSOTG_REG(0x0040) @@ -312,6 +320,7 @@ #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 #define GHWCFG4_ACG_SUPPORTED BIT(12) #define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 @@ -332,6 +341,8 @@ #define GLPMCFG_SNDLPM BIT(24) #define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) #define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) +#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) #define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) #define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 #define GLPMCFG_L1RESUMEOK BIT(16) @@ -404,6 +415,19 @@ #define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) #define ADPCTL_PRB_DSCHRG_SHIFT 0 +#define GREFCLK HSOTG_REG(0x0064) +#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15) +#define GREFCLK_REFCLKPER_SHIFT 15 +#define GREFCLK_REF_CLK_MODE BIT(14) +#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff) +#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0 + +#define GINTMSK2 HSOTG_REG(0x0068) +#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0) + +#define GINTSTS2 HSOTG_REG(0x006c) +#define GINTSTS2_WKUP_ALERT_INT BIT(0) + #define HPTXFSIZ HSOTG_REG(0x100) /* Use FIFOSIZE_* constants to access this register */ @@ -443,6 +467,7 @@ #define DCFG_DEVSPD_FS48 3 #define DCTL HSOTG_REG(0x804) +#define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19) #define DCTL_PWRONPRGDONE BIT(11) #define DCTL_CGOUTNAK BIT(10) #define DCTL_SGOUTNAK BIT(9) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index dff2c6e..ce4657c 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -88,6 +88,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) p->host_perio_tx_fifo_size = 256; p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = 0; } static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg) @@ -151,6 +152,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg) p->host_perio_tx_fifo_size = 256; } +static void dwc2_set_stm32mp1_fsotg_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->speed = DWC2_SPEED_PARAM_FULL; + p->host_rx_fifo_size = 128; + p->host_nperio_tx_fifo_size = 96; + p->host_perio_tx_fifo_size = 96; + p->max_packet_count = 256; + p->phy_type = DWC2_PHY_TYPE_PARAM_FS; + p->i2c_enable = false; + p->activate_stm_fs_transceiver = true; + p->activate_stm_id_vb_detection = true; +} + +static void dwc2_set_stm32mp1_hsotg_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + struct device_node *np = hsotg->dev->of_node; + + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->activate_stm_id_vb_detection = !of_property_read_bool(np, "extcon"); + p->host_rx_fifo_size = 440; + p->host_nperio_tx_fifo_size = 256; + p->host_perio_tx_fifo_size = 256; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; +} + const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, @@ -172,6 +202,10 @@ const struct of_device_id dwc2_of_match_table[] = { { .compatible = "st,stm32f4x9-hsotg" }, { .compatible = "st,stm32f7-hsotg", .data = dwc2_set_stm32f7_hsotg_params }, + { .compatible = "st,stm32mp1-fsotg", + .data = dwc2_set_stm32mp1_fsotg_params }, + { .compatible = "st,stm32mp1-hsotg", + .data = dwc2_set_stm32mp1_hsotg_params }, {}, }; MODULE_DEVICE_TABLE(of, dwc2_of_match_table); @@ -308,9 +342,12 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) p->hird_threshold_en = true; p->hird_threshold = 4; p->ipg_isoc_en = false; + p->service_interval = false; p->max_packet_count = hw->max_packet_count; p->max_transfer_size = hw->max_transfer_size; p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; + p->ref_clk_per = 33333; + p->sof_cnt_wkup_alert = 100; if ((hsotg->dr_mode == USB_DR_MODE_HOST) || (hsotg->dr_mode == USB_DR_MODE_OTG)) { @@ -601,6 +638,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)); CHECK_BOOL(hird_threshold_en, hsotg->params.lpm); CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0); + CHECK_BOOL(service_interval, hw->service_interval_mode); CHECK_RANGE(max_packet_count, 15, hw->max_packet_count, hw->max_packet_count); @@ -789,6 +827,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED); + hw->service_interval_mode = !!(hwcfg4 & + GHWCFG4_SERVICE_INTERVAL_SUPPORTED); /* fifo sizes */ hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5776428..3d61100 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -318,12 +319,18 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + if (hsotg->wakeirq > 0) + dev_pm_clear_wake_irq(&dev->dev); + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); + if (hsotg->params.activate_stm_id_vb_detection) + regulator_disable(hsotg->usb33d); + if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); @@ -432,6 +439,24 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) return retval; + hsotg->wakeirq = platform_get_irq(dev, 1); + if (hsotg->wakeirq > 0) { + retval = dev_pm_set_dedicated_wake_irq(&dev->dev, + hsotg->wakeirq); + if (retval) + return retval; + } else if (hsotg->wakeirq == -EPROBE_DEFER) { + return hsotg->wakeirq; + } + + hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); + if (IS_ERR(hsotg->vbus_supply)) { + retval = PTR_ERR(hsotg->vbus_supply); + hsotg->vbus_supply = NULL; + if (retval != -ENODEV) + return retval; + } + retval = dwc2_lowlevel_hw_enable(hsotg); if (retval) return retval; @@ -466,10 +491,55 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; + if (hsotg->params.activate_stm_id_vb_detection) { + u32 ggpio; + + hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); + if (IS_ERR(hsotg->usb33d)) { + retval = PTR_ERR(hsotg->usb33d); + dev_err(hsotg->dev, + "can't get voltage level detector supply\n"); + goto error; + } + retval = regulator_enable(hsotg->usb33d); + if (retval) { + dev_err(hsotg->dev, + "can't enable voltage level detector supply\n"); + goto error; + } + + ggpio = dwc2_readl(hsotg, GGPIO); + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(hsotg, ggpio, GGPIO); + } + + retval = dwc2_drd_init(hsotg); + if (retval) { + if (retval != -EPROBE_DEFER) + dev_err(hsotg->dev, "failed to initialize dual-role\n"); + goto error_init; + } + + if (hsotg->params.activate_stm_fs_transceiver) { + u32 ggpio; + + ggpio = dwc2_readl(hsotg, GGPIO); + if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { + dev_dbg(hsotg->dev, "Activating transceiver\n"); + /* + * STM32 uses the GGPIO register as general + * core configuration register. + */ + ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; + dwc2_writel(hsotg, ggpio, GGPIO); + } + } + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg); if (retval) - goto error; + goto error_init; hsotg->gadget_enabled = 1; } @@ -478,7 +548,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) { if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); - goto error; + goto error_init; } hsotg->hcd_enabled = 1; } @@ -494,7 +564,13 @@ static int dwc2_driver_probe(struct platform_device *dev) return 0; +error_init: + if (hsotg->params.activate_stm_id_vb_detection) + regulator_disable(hsotg->usb33d); error: + if (hsotg->wakeirq > 0) + dev_pm_clear_wake_irq(&dev->dev); + dwc2_lowlevel_hw_disable(hsotg); return retval; } @@ -507,9 +583,58 @@ static int __maybe_unused dwc2_suspend(struct device *dev) if (dwc2_is_device_mode(dwc2)) dwc2_hsotg_suspend(dwc2); + if (dwc2->params.power_down == DWC2_POWER_DOWN_PARAM_NONE) { + /* + * Backup host registers when power_down param is 'none', if + * controller power is disabled. + * This shouldn't be needed, when using other power_down modes. + */ + ret = dwc2_backup_registers(dwc2); + if (ret) { + dev_err(dwc2->dev, "backup regs failed %d\n", ret); + return ret; + } + } + + if (dwc2->params.activate_stm_id_vb_detection) { + unsigned long flags; + u32 ggpio, gotgctl; + int is_host = dwc2_is_host_mode(dwc2); + + /* + * Need to force the mode to the current mode to avoid Mode + * Mismatch Interrupt when ID detection will be disabled. + */ + dwc2_force_mode(dwc2, is_host); + + spin_lock_irqsave(&dwc2->lock, flags); + gotgctl = dwc2_readl(dwc2, GOTGCTL); + /* bypass debounce filter, enable overrides */ + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN; + /* Force A / B session if needed */ + if (gotgctl & GOTGCTL_ASESVLD) + gotgctl |= GOTGCTL_AVALOVAL; + if (gotgctl & GOTGCTL_BSESVLD) + gotgctl |= GOTGCTL_BVALOVAL; + dwc2_writel(dwc2, gotgctl, GOTGCTL); + spin_unlock_irqrestore(&dwc2->lock, flags); + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + regulator_disable(dwc2->usb33d); + } + if (dwc2->ll_hw_enabled) ret = __dwc2_lowlevel_hw_disable(dwc2); + if (dwc2->wakeirq > 0 && + (device_may_wakeup(dev) || dev->power.wakeup_path)) + enable_irq_wake(dwc2->wakeirq); + return ret; } @@ -518,12 +643,49 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; + if (dwc2->wakeirq > 0 && + (device_may_wakeup(dev) || dev->power.wakeup_path)) + disable_irq_wake(dwc2->wakeirq); + if (dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) return ret; } + if (dwc2->params.activate_stm_id_vb_detection) { + unsigned long flags; + u32 ggpio, gotgctl; + + ret = regulator_enable(dwc2->usb33d); + if (ret) + return ret; + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + /* ID/VBUS detection startup time */ + usleep_range(5000, 7000); + + spin_lock_irqsave(&dwc2->lock, flags); + gotgctl = dwc2_readl(dwc2, GOTGCTL); + gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | + GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL); + dwc2_writel(dwc2, gotgctl, GOTGCTL); + spin_unlock_irqrestore(&dwc2->lock, flags); + } + + if (dwc2->params.power_down == DWC2_POWER_DOWN_PARAM_NONE) { + ret = dwc2_restore_registers(dwc2); + if (ret) { + dev_err(dwc2->dev, "restore regs failed %d\n", ret); + return ret; + } + } + if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 4c306fb..b915c0f 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,6 +45,8 @@ struct ehci_platform_priv { struct clk *clks[EHCI_MAX_CLKS]; struct reset_control *rsts; + struct regulator *vbus_supply; + int wakeirq; bool reset_on_resume; }; @@ -73,6 +77,26 @@ static int ehci_platform_reset(struct usb_hcd *hcd) return 0; } +static int ehci_platform_port_power(struct usb_hcd *hcd, int portnum, + bool enable) +{ + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); + int ret; + + if (!priv->vbus_supply) + return 0; + + if (enable) + ret = regulator_enable(priv->vbus_supply); + else + ret = regulator_disable(priv->vbus_supply); + if (ret) + dev_err(hcd->self.controller, "failed to %s vbus supply: %d\n", + enable ? "enable" : "disable", ret); + + return ret; +} + static int ehci_platform_power_on(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); @@ -110,6 +134,7 @@ static struct hc_driver __read_mostly ehci_platform_hc_driver; static const struct ehci_driver_overrides platform_overrides __initconst = { .reset = ehci_platform_reset, .extra_priv_size = sizeof(struct ehci_platform_priv), + .port_power = ehci_platform_port_power, }; static struct usb_ehci_pdata ehci_platform_defaults = { @@ -200,6 +225,15 @@ static int ehci_platform_probe(struct platform_device *dev) if (err) goto err_put_clks; + priv->vbus_supply = devm_regulator_get_optional(&dev->dev, "vbus"); + if (IS_ERR(priv->vbus_supply)) { + err = PTR_ERR(priv->vbus_supply); + if (err == -ENODEV) + priv->vbus_supply = NULL; + else + goto err_reset; + } + if (pdata->big_endian_desc) ehci->big_endian_desc = 1; if (pdata->big_endian_mmio) @@ -245,12 +279,24 @@ static int ehci_platform_probe(struct platform_device *dev) if (err) goto err_power; + priv->wakeirq = platform_get_irq(dev, 1); + if (priv->wakeirq > 0) { + err = dev_pm_set_dedicated_wake_irq(hcd->self.controller, + priv->wakeirq); + if (err) + goto err_hcd; + } else if (priv->wakeirq == -EPROBE_DEFER) { + goto err_hcd; + } + device_wakeup_enable(hcd->self.controller); device_enable_async_suspend(hcd->self.controller); platform_set_drvdata(dev, hcd); return err; +err_hcd: + usb_remove_hcd(hcd); err_power: if (pdata->power_off) pdata->power_off(dev); @@ -275,6 +321,9 @@ static int ehci_platform_remove(struct platform_device *dev) struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); int clk; + if (priv->wakeirq > 0) + dev_pm_clear_wake_irq(hcd->self.controller); + usb_remove_hcd(hcd); if (pdata->power_off) @@ -299,9 +348,14 @@ static int ehci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; + if (priv->wakeirq > 0 && + (do_wakeup || dev->power.wakeup_path)) + enable_irq_wake(priv->wakeirq); + ret = ehci_suspend(hcd, do_wakeup); if (ret) return ret; @@ -333,6 +387,11 @@ static int ehci_platform_resume(struct device *dev) } ehci_resume(hcd, priv->reset_on_resume); + + if (priv->wakeirq > 0 && + (device_may_wakeup(dev) || dev->power.wakeup_path)) + disable_irq_wake(priv->wakeirq); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 00878c3..1dbbf16 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -102,6 +102,15 @@ config TYPEC_TPS6598X If you choose to build this driver as a dynamically linked module, the module will be called tps6598x.ko. +config TYPEC_STUSB + tristate "STMicroelectronics STUSB Type-C controller driver" + depends on I2C + select EXTCON + help + The STMicroelectronics STUSB Type-C controller driver that works + with Type-C Port Controller Manager to provide USB Type-C + functionalities. + source "drivers/usb/typec/mux/Kconfig" source "drivers/usb/typec/altmodes/Kconfig" diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 45b0aef..aedb153 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -7,6 +7,7 @@ obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o +obj-$(CONFIG_TYPEC_STUSB) += typec_stusb.o obj-$(CONFIG_TYPEC) += mux/ obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 00141e0..4813b2f 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1382,6 +1382,21 @@ void typec_set_pwr_opmode(struct typec_port *port, EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); /** + * typec_find_power_opmode - Get the typec port power operation mode + * @name: port power operation mode string + * + * This routine is used to find the typec_pwr_opmodes by its string name. + * + * Returns typec_pwr_opmodes if success, otherwise negative error code. + */ +int typec_find_port_power_opmode(const char *name) +{ + return match_string(typec_pwr_opmodes, + ARRAY_SIZE(typec_pwr_opmodes), name); +} +EXPORT_SYMBOL_GPL(typec_find_port_power_opmode); + +/** * typec_find_port_power_role - Get the typec port power capability * @name: port power capability string * diff --git a/drivers/usb/typec/typec_stusb.c b/drivers/usb/typec/typec_stusb.c new file mode 100644 index 0000000..86737dc --- /dev/null +++ b/drivers/usb/typec/typec_stusb.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * STMicroelectronics STUSB Type-C controller family driver + * + * Copyright (C) 2019, STMicroelectronics + * Author(s): Amelie Delaunay + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STUSB_ALERT_STATUS 0x0B /* RC */ +#define STUSB_ALERT_STATUS_MASK_CTRL 0x0C /* RW */ +#define STUSB_CC_CONNECTION_STATUS_TRANS 0x0D /* RC */ +#define STUSB_CC_CONNECTION_STATUS 0x0E /* RO */ +#define STUSB_MONITORING_STATUS_TRANS 0x0F /* RC */ +#define STUSB_MONITORING_STATUS 0x10 /* RO */ +#define STUSB_CC_OPERATION_STATUS 0x11 /* RO */ +#define STUSB_HW_FAULT_STATUS_TRANS 0x12 /* RC */ +#define STUSB_HW_FAULT_STATUS 0x13 /* RO */ +#define STUSB_CC_CAPABILITY_CTRL 0x18 /* RW */ +#define STUSB_CC_VCONN_SWITCH_CTRL 0x1E /* RW */ +#define STUSB_VCONN_MONITORING_CTRL 0x20 /* RW */ +#define STUSB_VBUS_MONITORING_RANGE_CTRL 0x22 /* RW */ +#define STUSB_RESET_CTRL 0x23 /* RW */ +#define STUSB_VBUS_DISCHARGE_TIME_CTRL 0x25 /* RW */ +#define STUSB_VBUS_DISCHARGE_STATUS 0x26 /* RO */ +#define STUSB_VBUS_ENABLE_STATUS 0x27 /* RO */ +#define STUSB_CC_POWER_MODE_CTRL 0x28 /* RW */ +#define STUSB_VBUS_MONITORING_CTRL 0x2E /* RW */ +#define STUSB1600_REG_MAX 0x2F /* RO - Reserved */ + +/* STUSB_ALERT_STATUS/STUSB_ALERT_STATUS_MASK_CTRL bitfields */ +#define STUSB_HW_FAULT BIT(4) +#define STUSB_MONITORING BIT(5) +#define STUSB_CC_CONNECTION BIT(6) +#define STUSB_ALL_ALERTS GENMASK(6, 4) + +/* STUSB_CC_CONNECTION_STATUS_TRANS bitfields */ +#define STUSB_CC_ATTACH_TRANS BIT(0) + +/* STUSB_CC_CONNECTION_STATUS bitfields */ +#define STUSB_CC_ATTACH BIT(0) +#define STUSB_CC_VCONN_SUPPLY BIT(1) +#define STUSB_CC_DATA_ROLE(s) (!!((s) & BIT(2))) +#define STUSB_CC_POWER_ROLE(s) (!!((s) & BIT(3))) +#define STUSB_CC_ATTACHED_MODE GENMASK(7, 5) + +/* STUSB_MONITORING_STATUS_TRANS bitfields */ +#define STUSB_VCONN_PRESENCE_TRANS BIT(0) +#define STUSB_VBUS_PRESENCE_TRANS BIT(1) +#define STUSB_VBUS_VSAFE0V_TRANS BIT(2) +#define STUSB_VBUS_VALID_TRANS BIT(3) + +/* STUSB_MONITORING_STATUS bitfields */ +#define STUSB_VCONN_PRESENCE BIT(0) +#define STUSB_VBUS_PRESENCE BIT(1) +#define STUSB_VBUS_VSAFE0V BIT(2) +#define STUSB_VBUS_VALID BIT(3) + +/* STUSB_CC_OPERATION_STATUS bitfields */ +#define STUSB_TYPEC_FSM_STATE GENMASK(4, 0) +#define STUSB_SINK_POWER_STATE GENMASK(6, 5) +#define STUSB_CC_ATTACHED BIT(7) + +/* STUSB_HW_FAULT_STATUS_TRANS bitfields */ +#define STUSB_VCONN_SW_OVP_FAULT_TRANS BIT(0) +#define STUSB_VCONN_SW_OCP_FAULT_TRANS BIT(1) +#define STUSB_VCONN_SW_RVP_FAULT_TRANS BIT(2) +#define STUSB_VPU_VALID_TRANS BIT(4) +#define STUSB_VPU_OVP_FAULT_TRANS BIT(5) +#define STUSB_THERMAL_FAULT BIT(7) + +/* STUSB_HW_FAULT_STATUS bitfields */ +#define STUSB_VCONN_SW_OVP_FAULT_CC2 BIT(0) +#define STUSB_VCONN_SW_OVP_FAULT_CC1 BIT(1) +#define STUSB_VCONN_SW_OCP_FAULT_CC2 BIT(2) +#define STUSB_VCONN_SW_OCP_FAULT_CC1 BIT(3) +#define STUSB_VCONN_SW_RVP_FAULT_CC2 BIT(4) +#define STUSB_VCONN_SW_RVP_FAULT_CC1 BIT(5) +#define STUSB_VPU_VALID BIT(6) +#define STUSB_VPU_OVP_FAULT BIT(7) + +/* STUSB_CC_CAPABILITY_CTRL bitfields */ +#define STUSB_CC_VCONN_SUPPLY_EN BIT(0) +#define STUSB_CC_VCONN_DISCHARGE_EN BIT(4) +#define STUSB_CC_CURRENT_ADVERTISED GENMASK(7, 6) + +/* STUSB_VCONN_SWITCH_CTRL bitfields */ +#define STUSB_CC_VCONN_SWITCH_ILIM GENMASK(3, 0) + +/* STUSB_VCONN_MONITORING_CTRL bitfields */ +#define STUSB_VCONN_UVLO_THRESHOLD BIT(6) +#define STUSB_VCONN_MONITORING_EN BIT(7) + +/* STUSB_VBUS_MONITORING_RANGE_CTRL bitfields */ +#define STUSB_SHIFT_LOW_VBUS_LIMIT GENMASK(3, 0) +#define STUSB_SHIFT_HIGH_VBUS_LIMIT GENMASK(7, 4) + +/* STUSB_RESET_CTRL bitfields */ +#define STUSB_SW_RESET_EN BIT(0) + +/* STUSB_VBUS_DISCHARGE_TIME_CTRL bitfields */ +#define STUSBXX02_VBUS_DISCHARGE_TIME_TO_PDO GENMASK(3, 0) +#define STUSB_VBUS_DISCHARGE_TIME_TO_0V GENMASK(7, 4) + +/* STUSB_VBUS_DISCHARGE_STATUS bitfields */ +#define STUSB_VBUS_DISCHARGE_EN BIT(7) + +/* STUSB_VBUS_ENABLE_STATUS bitfields */ +#define STUSB_VBUS_SOURCE_EN BIT(0) +#define STUSB_VBUS_SINK_EN BIT(1) + +/* STUSB_CC_POWER_MODE_CTRL bitfields */ +#define STUSB_CC_POWER_MODE GENMASK(2, 0) + +/* STUSB_VBUS_MONITORING_CTRL bitfields */ +#define STUSB_VDD_UVLO_DISABLE BIT(0) +#define STUSB_VBUS_VSAFE0V_THRESHOLD GENMASK(2, 1) +#define STUSB_VBUS_RANGE_DISABLE BIT(4) +#define STUSB_VDD_OVLO_DISABLE BIT(6) + +enum stusb_pwr_mode { + SOURCE_WITH_ACCESSORY, + SINK_WITH_ACCESSORY, + SINK_WITHOUT_ACCESSORY, + DUAL_WITH_ACCESSORY, + DUAL_WITH_ACCESSORY_AND_TRY_SRC, + DUAL_WITH_ACCESSORY_AND_TRY_SNK, +}; + +enum stusb_attached_mode { + NO_DEVICE_ATTACHED, + SINK_ATTACHED, + SOURCE_ATTACHED, + DEBUG_ACCESSORY_ATTACHED, + AUDIO_ACCESSORY_ATTACHED, +}; + +struct stusb { + struct device *dev; + struct regmap *regmap; + struct regulator *vdd_supply; + struct regulator *vsys_supply; + struct regulator *vconn_supply; + struct regulator *main_supply; + + struct typec_port *port; + struct typec_capability capability; + struct typec_partner *partner; + + enum typec_port_type port_type; + enum typec_pwr_opmode pwr_opmode; + bool vbus_on; + struct extcon_dev *edev; + struct work_struct wq_detcable; +}; + +static const unsigned int stusb_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static bool stusb_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STUSB_ALERT_STATUS_MASK_CTRL: + case STUSB_CC_CAPABILITY_CTRL: + case STUSB_CC_VCONN_SWITCH_CTRL: + case STUSB_VCONN_MONITORING_CTRL: + case STUSB_VBUS_MONITORING_RANGE_CTRL: + case STUSB_RESET_CTRL: + case STUSB_VBUS_DISCHARGE_TIME_CTRL: + case STUSB_CC_POWER_MODE_CTRL: + case STUSB_VBUS_MONITORING_CTRL: + return true; + default: + return false; + } +} + +static bool stusb_reg_readable(struct device *dev, unsigned int reg) +{ + if (reg <= 0x0A || + (reg >= 0x14 && reg <= 0x17) || + (reg >= 0x19 && reg <= 0x1D) || + (reg >= 0x29 && reg <= 0x2D) || + (reg == 0x1F || reg == 0x21 || reg == 0x24 || reg == 0x2F)) + return false; + else + return true; +} + +static bool stusb_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STUSB_ALERT_STATUS: + case STUSB_CC_CONNECTION_STATUS_TRANS: + case STUSB_CC_CONNECTION_STATUS: + case STUSB_MONITORING_STATUS_TRANS: + case STUSB_MONITORING_STATUS: + case STUSB_CC_OPERATION_STATUS: + case STUSB_HW_FAULT_STATUS_TRANS: + case STUSB_HW_FAULT_STATUS: + case STUSB_VBUS_DISCHARGE_STATUS: + case STUSB_VBUS_ENABLE_STATUS: + return true; + default: + return false; + } +} + +static bool stusb_reg_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STUSB_ALERT_STATUS: + case STUSB_CC_CONNECTION_STATUS_TRANS: + case STUSB_MONITORING_STATUS_TRANS: + case STUSB_HW_FAULT_STATUS_TRANS: + return true; + default: + return false; + } +} + +static const struct regmap_config stusb1600_regmap_config = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .max_register = STUSB1600_REG_MAX, + .writeable_reg = stusb_reg_writeable, + .readable_reg = stusb_reg_readable, + .volatile_reg = stusb_reg_volatile, + .precious_reg = stusb_reg_precious, + .cache_type = REGCACHE_RBTREE, +}; + +static void stusb_extcon_detect_cable(struct work_struct *work) +{ + struct stusb *chip = container_of(work, struct stusb, wq_detcable); + u32 conn_status, vbus_status; + bool id, vbus; + int ret; + + /* Check ID and Vbus to update cable state */ + ret = regmap_read(chip->regmap, STUSB_CC_CONNECTION_STATUS, + &conn_status); + if (ret) + return; + + ret = regmap_read(chip->regmap, STUSB_VBUS_ENABLE_STATUS, + &vbus_status); + if (ret) + return; + + /* 0 = Device, 1 = Host */ + id = STUSB_CC_DATA_ROLE(conn_status); + + if (STUSB_CC_POWER_ROLE(conn_status)) /* Source */ + vbus = !!(vbus_status & STUSB_VBUS_SOURCE_EN); + else /* Sink */ + vbus = !!(vbus_status & STUSB_VBUS_SINK_EN); + + dev_dbg(chip->dev, "role=%s vbus=%sable\n", + id ? "Host" : "Device", vbus ? "en" : "dis"); + + /* + * !vbus = detached, so neither B-Session Valid nor A-Session Valid + * !vbus = !EXTCON_USB && !EXTCON_USB_HOST + * vbus = attached, so either B-Session Valid or A-Session Valid + * vbus && !id = B-Session Valid = EXTCON_USB && !EXTCON_USB_HOST + * vbus && id = A-Session Valid = !EXTCON_USB && EXTCON_USB_HOST + */ + + if (!vbus || !id) /* Detached or B-Session */ + extcon_set_state_sync(chip->edev, EXTCON_USB_HOST, false); + if (!vbus || id) /* Detached or A-Session */ + extcon_set_state_sync(chip->edev, EXTCON_USB, false); + + if (vbus && id) /* Attached and A-Session Valid */ + extcon_set_state_sync(chip->edev, EXTCON_USB_HOST, true); + if (vbus && !id) /* Attached and B-Session Valid */ + extcon_set_state_sync(chip->edev, EXTCON_USB, true); +} + +static bool stusb_get_vconn(struct stusb *chip) +{ + u32 val; + int ret; + + ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val); + if (ret) { + dev_err(chip->dev, "Unable to get Vconn status: %d\n", ret); + return false; + } + + return !!FIELD_GET(STUSB_CC_VCONN_SUPPLY_EN, val); +} + +static int stusb_set_vconn(struct stusb *chip, bool on) +{ + int ret; + + /* Manage VCONN input supply */ + if (chip->vconn_supply) { + if (on) { + ret = regulator_enable(chip->vconn_supply); + if (ret) { + dev_err(chip->dev, + "failed to enable vconn supply: %d\n", + ret); + return ret; + } + } else { + regulator_disable(chip->vconn_supply); + } + } + + /* Manage VCONN monitoring and power path */ + ret = regmap_update_bits(chip->regmap, STUSB_VCONN_MONITORING_CTRL, + STUSB_VCONN_MONITORING_EN, + on ? STUSB_VCONN_MONITORING_EN : 0); + if (ret) + goto vconn_reg_disable; + + return 0; + +vconn_reg_disable: + if (chip->vconn_supply && on) + regulator_disable(chip->vconn_supply); + + return ret; +} + +static enum typec_pwr_opmode stusb_get_pwr_opmode(struct stusb *chip) +{ + u32 val; + int ret; + + ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val); + if (ret) { + dev_err(chip->dev, "Unable to get pwr opmode: %d\n", ret); + return TYPEC_PWR_MODE_USB; + } + + return FIELD_GET(STUSB_CC_CURRENT_ADVERTISED, val); +} + +static enum typec_accessory stusb_get_accessory(u32 status) +{ + enum stusb_attached_mode mode; + + mode = FIELD_GET(STUSB_CC_ATTACHED_MODE, status); + + switch (mode) { + case DEBUG_ACCESSORY_ATTACHED: + return TYPEC_ACCESSORY_DEBUG; + case AUDIO_ACCESSORY_ATTACHED: + return TYPEC_ACCESSORY_AUDIO; + default: + return TYPEC_ACCESSORY_NONE; + } +} + +static enum typec_role stusb_get_vconn_role(u32 status) +{ + if (FIELD_GET(STUSB_CC_VCONN_SUPPLY, status)) + return TYPEC_SOURCE; + else + return TYPEC_SINK; +} + +static int stusb_attach(struct stusb *chip, u32 status) +{ + struct typec_partner_desc desc; + int ret; + + if ((STUSB_CC_POWER_ROLE(status) == TYPEC_SOURCE) && + chip->vdd_supply) { + ret = regulator_enable(chip->vdd_supply); + if (ret) { + dev_err(chip->dev, + "Failed to enable Vbus supply: %d\n", ret); + return ret; + } + chip->vbus_on = true; + } + + desc.usb_pd = false; + desc.accessory = stusb_get_accessory(status); + desc.identity = NULL; + + chip->partner = typec_register_partner(chip->port, &desc); + if (IS_ERR(chip->partner)) { + ret = PTR_ERR(chip->partner); + goto vbus_disable; + } + + typec_set_pwr_role(chip->port, STUSB_CC_POWER_ROLE(status)); + typec_set_pwr_opmode(chip->port, stusb_get_pwr_opmode(chip)); + typec_set_vconn_role(chip->port, stusb_get_vconn_role(status)); + typec_set_data_role(chip->port, STUSB_CC_DATA_ROLE(status)); + + queue_work(system_power_efficient_wq, &chip->wq_detcable); + + return 0; + +vbus_disable: + if (chip->vbus_on) { + regulator_disable(chip->vdd_supply); + chip->vbus_on = false; + } + + return ret; +} + +static void stusb_detach(struct stusb *chip, u32 status) +{ + typec_unregister_partner(chip->partner); + chip->partner = NULL; + + queue_work(system_power_efficient_wq, &chip->wq_detcable); + + typec_set_pwr_role(chip->port, STUSB_CC_POWER_ROLE(status)); + typec_set_pwr_opmode(chip->port, TYPEC_PWR_MODE_USB); + typec_set_vconn_role(chip->port, stusb_get_vconn_role(status)); + typec_set_data_role(chip->port, STUSB_CC_DATA_ROLE(status)); + + if (chip->vbus_on) { + regulator_disable(chip->vdd_supply); + chip->vbus_on = false; + } +} + +static irqreturn_t stusb_irq_handler(int irq, void *data) +{ + struct stusb *chip = data; + u32 pending, trans, status; + int ret; + + ret = regmap_read(chip->regmap, STUSB_ALERT_STATUS, &pending); + if (ret) + return IRQ_NONE; + + if (pending & STUSB_CC_CONNECTION) { + ret = regmap_read(chip->regmap, + STUSB_CC_CONNECTION_STATUS_TRANS, &trans); + if (ret) + goto err; + ret = regmap_read(chip->regmap, STUSB_CC_CONNECTION_STATUS, + &status); + if (ret) + goto err; + + if (trans & STUSB_CC_ATTACH_TRANS) { + if (status & STUSB_CC_ATTACH) { + ret = stusb_attach(chip, status); + if (ret) + goto err; + } else { + stusb_detach(chip, status); + } + } + } +err: + return IRQ_HANDLED; +} + +static int stusb_irq_init(struct stusb *chip, int irq) +{ + u32 status; + int ret; + + ret = regmap_read(chip->regmap, STUSB_CC_CONNECTION_STATUS, &status); + if (ret) + return ret; + + if (status & STUSB_CC_ATTACH) { + ret = stusb_attach(chip, status); + if (ret) + dev_err(chip->dev, "attach failed: %d\n", ret); + } + + ret = devm_request_threaded_irq(chip->dev, irq, NULL, stusb_irq_handler, + IRQF_ONESHOT, dev_name(chip->dev), + chip); + if (ret) + goto partner_unregister; + + /* Unmask CC_CONNECTION events */ + ret = regmap_write_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL, + STUSB_CC_CONNECTION, 0); + if (ret) + goto partner_unregister; + + return 0; + +partner_unregister: + if (chip->partner) { + typec_unregister_partner(chip->partner); + chip->partner = NULL; + } + + return ret; +} + +static int stusb_init(struct stusb *chip) +{ + u32 val; + int ret; + + /* Change the default Type-C power mode */ + if (chip->port_type == TYPEC_PORT_SRC) + ret = regmap_update_bits(chip->regmap, + STUSB_CC_POWER_MODE_CTRL, + STUSB_CC_POWER_MODE, + SOURCE_WITH_ACCESSORY); + else if (chip->port_type == TYPEC_PORT_SNK) + ret = regmap_update_bits(chip->regmap, + STUSB_CC_POWER_MODE_CTRL, + STUSB_CC_POWER_MODE, + SINK_WITH_ACCESSORY); + else /* (capability->type == TYPEC_PORT_DRP) */ + ret = regmap_update_bits(chip->regmap, + STUSB_CC_POWER_MODE_CTRL, + STUSB_CC_POWER_MODE, + DUAL_WITH_ACCESSORY); + if (ret) + return ret; + + if (chip->port_type == TYPEC_PORT_SNK) + goto skip_src; + + /* Change the default Type-C Source power operation mode capability */ + ret = regmap_update_bits(chip->regmap, STUSB_CC_CAPABILITY_CTRL, + STUSB_CC_CURRENT_ADVERTISED, + FIELD_PREP(STUSB_CC_CURRENT_ADVERTISED, + chip->pwr_opmode)); + if (ret) + return ret; + + /* Manage Type-C Source Vconn supply */ + if (stusb_get_vconn(chip)) { + ret = stusb_set_vconn(chip, true); + if (ret) + return ret; + } + +skip_src: + /* Mask all events interrupts - to be unmasked with interrupt support */ + ret = regmap_update_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL, + STUSB_ALL_ALERTS, STUSB_ALL_ALERTS); + if (ret) + return ret; + + /* Read status at least once to clear any stale interrupts */ + regmap_read(chip->regmap, STUSB_ALERT_STATUS, &val); + regmap_read(chip->regmap, STUSB_CC_CONNECTION_STATUS_TRANS, &val); + regmap_read(chip->regmap, STUSB_MONITORING_STATUS_TRANS, &val); + regmap_read(chip->regmap, STUSB_HW_FAULT_STATUS_TRANS, &val); + + return 0; +} + +static int stusb_fw_get_caps(struct stusb *chip) +{ + struct fwnode_handle *fwnode = device_get_named_child_node(chip->dev, + "connector"); + const char *cap_str; + int ret; + + if (!fwnode) + return -EINVAL; + + chip->capability.fwnode = fwnode; + + ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); + if (!ret) { + chip->port_type = typec_find_port_power_role(cap_str); + if (chip->port_type < 0) + return -EINVAL; + + chip->capability.type = chip->port_type; + } + + if (chip->port_type == TYPEC_PORT_SNK) + goto sink; + + if (chip->port_type == TYPEC_PORT_DRP) + chip->capability.prefer_role = TYPEC_SINK; + + ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str); + if (!ret) { + chip->pwr_opmode = typec_find_port_power_opmode(cap_str); + + /* Power delivery not yet supported */ + if (chip->pwr_opmode < 0 || + chip->pwr_opmode == TYPEC_PWR_MODE_PD) { + dev_err(chip->dev, "bad power operation mode: %d\n", + chip->pwr_opmode); + return -EINVAL; + } + + } else { + chip->pwr_opmode = stusb_get_pwr_opmode(chip); + } + +sink: + return 0; +} + +static int stusb_get_caps(struct stusb *chip, bool *try) +{ + enum typec_port_type *type = &chip->capability.type; + enum typec_port_data *data = &chip->capability.data; + enum typec_accessory *accessory = chip->capability.accessory; + u32 val; + int ret; + + chip->capability.revision = USB_TYPEC_REV_1_2; + + ret = regmap_read(chip->regmap, STUSB_CC_POWER_MODE_CTRL, &val); + if (ret) + return ret; + + *try = false; + + switch (FIELD_GET(STUSB_CC_POWER_MODE, val)) { + case SOURCE_WITH_ACCESSORY: + *type = TYPEC_PORT_SRC; + *data = TYPEC_PORT_DFP; + *accessory++ = TYPEC_ACCESSORY_AUDIO; + *accessory++ = TYPEC_ACCESSORY_DEBUG; + break; + case SINK_WITH_ACCESSORY: + *type = TYPEC_PORT_SNK; + *data = TYPEC_PORT_UFP; + *accessory++ = TYPEC_ACCESSORY_AUDIO; + *accessory++ = TYPEC_ACCESSORY_DEBUG; + break; + case SINK_WITHOUT_ACCESSORY: + *type = TYPEC_PORT_SNK; + *data = TYPEC_PORT_UFP; + break; + case DUAL_WITH_ACCESSORY: + *type = TYPEC_PORT_DRP; + *data = TYPEC_PORT_DRD; + *accessory++ = TYPEC_ACCESSORY_AUDIO; + *accessory++ = TYPEC_ACCESSORY_DEBUG; + break; + case DUAL_WITH_ACCESSORY_AND_TRY_SRC: + case DUAL_WITH_ACCESSORY_AND_TRY_SNK: + *type = TYPEC_PORT_DRP; + *data = TYPEC_PORT_DRD; + *accessory++ = TYPEC_ACCESSORY_AUDIO; + *accessory++ = TYPEC_ACCESSORY_DEBUG; + *try = true; + break; + default: + return -EINVAL; + } + + chip->port_type = *type; + + return stusb_fw_get_caps(chip); +} + +static const struct of_device_id stusb_of_match[] = { + { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config}, + {}, +}; + +static int stusb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct stusb *chip; + const struct of_device_id *match; + struct regmap_config *regmap_config; + bool try_role; + int ret; + + chip = devm_kzalloc(&client->dev, sizeof(struct stusb), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + + match = i2c_of_match_device(stusb_of_match, client); + regmap_config = (struct regmap_config *)match->data; + chip->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "Failed to allocate register map:%d\n", ret); + return ret; + } + + chip->dev = &client->dev; + + chip->vsys_supply = devm_regulator_get_optional(chip->dev, "vsys"); + if (IS_ERR(chip->vsys_supply)) { + ret = PTR_ERR(chip->vsys_supply); + if (ret != -ENODEV) + return ret; + chip->vsys_supply = NULL; + } + + chip->vdd_supply = devm_regulator_get_optional(chip->dev, "vdd"); + if (IS_ERR(chip->vdd_supply)) { + ret = PTR_ERR(chip->vdd_supply); + if (ret != -ENODEV) + return ret; + chip->vdd_supply = NULL; + } + + chip->vconn_supply = devm_regulator_get_optional(chip->dev, "vconn"); + if (IS_ERR(chip->vconn_supply)) { + ret = PTR_ERR(chip->vconn_supply); + if (ret != -ENODEV) + return ret; + chip->vconn_supply = NULL; + } + + /* + * When both VDD and VSYS power supplies are present, the low power + * supply VSYS is selected when VSYS voltage is above 3.1 V. + * Otherwise VDD is selected. + */ + if (chip->vdd_supply && + (!chip->vsys_supply || + (regulator_get_voltage(chip->vsys_supply) <= 3100000))) + chip->main_supply = chip->vdd_supply; + else + chip->main_supply = chip->vsys_supply; + + if (chip->main_supply) { + ret = regulator_enable(chip->main_supply); + if (ret) { + dev_err(chip->dev, + "Failed to enable main supply: %d\n", ret); + return ret; + } + } + + ret = stusb_get_caps(chip, &try_role); + if (ret) { + dev_err(chip->dev, "Failed to get port caps: %d\n", ret); + goto main_reg_disable; + } + + ret = stusb_init(chip); + if (ret) { + dev_err(chip->dev, "Failed to init port: %d\n", ret); + goto main_reg_disable; + } + + chip->port = typec_register_port(chip->dev, &chip->capability); + if (!chip->port) { + ret = -ENODEV; + goto all_reg_disable; + } + + /* + * Default power operation mode initialization: will be updated upon + * attach/detach interrupt + */ + typec_set_pwr_opmode(chip->port, chip->pwr_opmode); + + if (!client->irq) { + /* + * If Source or Dual power role, need to enable VDD supply + * providing Vbus if present. In case of interrupt support, + * VDD supply will be dynamically managed upon attach/detach + * interrupt. + */ + if ((chip->port_type != TYPEC_PORT_SNK) && chip->vdd_supply) { + ret = regulator_enable(chip->vdd_supply); + if (ret) { + dev_err(chip->dev, + "Failed to enable VDD supply: %d\n", + ret); + goto port_unregister; + } + chip->vbus_on = true; + } + + return 0; + } + + chip->edev = devm_extcon_dev_allocate(chip->dev, stusb_extcon_cable); + if (IS_ERR(chip->edev)) { + ret = PTR_ERR(chip->edev); + dev_err(chip->dev, + "Failed to allocate extcon device: %d\n", ret); + goto port_unregister; + } + + ret = devm_extcon_dev_register(chip->dev, chip->edev); + if (ret) { + dev_err(chip->dev, + "Failed to register extcon device: %d\n", ret); + goto port_unregister; + } + + INIT_WORK(&chip->wq_detcable, stusb_extcon_detect_cable); + + ret = stusb_irq_init(chip, client->irq); + if (ret) + goto cancel_work_sync; + + return 0; + +cancel_work_sync: + cancel_work_sync(&chip->wq_detcable); +port_unregister: + typec_unregister_port(chip->port); +all_reg_disable: + if (stusb_get_vconn(chip)) + stusb_set_vconn(chip, false); +main_reg_disable: + if (chip->main_supply) + regulator_disable(chip->main_supply); + + return ret; +} + +static int stusb_remove(struct i2c_client *client) +{ + struct stusb *chip = i2c_get_clientdata(client); + + if (chip->partner) { + typec_unregister_partner(chip->partner); + chip->partner = NULL; + } + + if (chip->vbus_on) + regulator_disable(chip->vdd_supply); + + if (chip->edev) + cancel_work_sync(&chip->wq_detcable); + + typec_unregister_port(chip->port); + + if (stusb_get_vconn(chip)) + stusb_set_vconn(chip, false); + + if (chip->main_supply) + regulator_disable(chip->main_supply); + + return 0; +} + +static int __maybe_unused stusb_suspend(struct device *dev) +{ + struct stusb *chip = dev_get_drvdata(dev); + + /* Mask interrupts */ + return regmap_update_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL, + STUSB_ALL_ALERTS, STUSB_ALL_ALERTS); +} + +static int __maybe_unused stusb_resume(struct device *dev) +{ + struct stusb *chip = dev_get_drvdata(dev); + u32 status; + int ret; + + ret = regcache_sync(chip->regmap); + if (ret) + return ret; + + /* Unmask CC_CONNECTION events - chip->edev implies IRQ support */ + if (chip->edev) + return regmap_write_bits(chip->regmap, + STUSB_ALERT_STATUS_MASK_CTRL, + STUSB_CC_CONNECTION, 0); + + /* Check if attach/detach occurred during low power */ + ret = regmap_read(chip->regmap, STUSB_CC_CONNECTION_STATUS, &status); + if (ret) + return ret; + + if (chip->partner && !(status & STUSB_CC_ATTACH)) + stusb_detach(chip, status); + + if (!chip->partner && (status & STUSB_CC_ATTACH)) { + ret = stusb_attach(chip, status); + if (ret) + dev_err(chip->dev, "attach failed: %d\n", ret); + } + + return ret; +} + +static SIMPLE_DEV_PM_OPS(stusb_pm_ops, stusb_suspend, stusb_resume); + +static struct i2c_driver stusb_driver = { + .driver = { + .name = "typec_stusb", + .pm = &stusb_pm_ops, + .of_match_table = stusb_of_match, + }, + .probe = stusb_probe, + .remove = stusb_remove, +}; +module_i2c_driver(stusb_driver); + +MODULE_AUTHOR("Amelie Delaunay "); +MODULE_DESCRIPTION("STMicroelectronics STUSB Type-C controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 7df4eca..2671776 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -241,6 +241,7 @@ int typec_set_orientation(struct typec_port *port, enum typec_orientation typec_get_orientation(struct typec_port *port); int typec_set_mode(struct typec_port *port, int mode); +int typec_find_port_power_opmode(const char *name); int typec_find_port_power_role(const char *name); int typec_find_power_role(const char *name); int typec_find_port_data_role(const char *name); -- 2.7.4