mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "serial: msm_geni_serial: Add missing changes from 4.19 kernel"
This commit is contained in:
commit
49a3ddbf02
1 changed files with 332 additions and 92 deletions
|
|
@ -109,7 +109,7 @@
|
|||
#define UART_CONSOLE_CORE2X_VOTE (960)
|
||||
|
||||
#define WAKEBYTE_TIMEOUT_MSEC (2000)
|
||||
#define WAIT_XFER_MAX_ITER (50)
|
||||
#define WAIT_XFER_MAX_ITER (2)
|
||||
#define WAIT_XFER_MAX_TIMEOUT_US (10000)
|
||||
#define WAIT_XFER_MIN_TIMEOUT_US (9000)
|
||||
#define IPC_LOG_PWR_PAGES (6)
|
||||
|
|
@ -121,7 +121,15 @@
|
|||
|
||||
#define DMA_RX_BUF_SIZE (2048)
|
||||
#define UART_CONSOLE_RX_WM (2)
|
||||
#define QUP_VER (0x20050000)
|
||||
|
||||
struct msm_geni_serial_ver_info {
|
||||
int hw_major_ver;
|
||||
int hw_minor_ver;
|
||||
int hw_step_ver;
|
||||
int m_fw_ver;
|
||||
int s_fw_ver;
|
||||
};
|
||||
|
||||
struct msm_geni_serial_port {
|
||||
struct uart_port uport;
|
||||
char name[20];
|
||||
|
|
@ -154,10 +162,14 @@ struct msm_geni_serial_port {
|
|||
void *ipc_log_rx;
|
||||
void *ipc_log_pwr;
|
||||
void *ipc_log_misc;
|
||||
void *console_log;
|
||||
unsigned int cur_baud;
|
||||
int ioctl_count;
|
||||
int edge_count;
|
||||
bool manual_flow;
|
||||
struct msm_geni_serial_ver_info ver_info;
|
||||
u32 cur_tx_remaining;
|
||||
bool startup_in_progress;
|
||||
};
|
||||
|
||||
static const struct uart_ops msm_geni_serial_pops;
|
||||
|
|
@ -181,8 +193,8 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport,
|
|||
static void msm_geni_serial_stop_rx(struct uart_port *uport);
|
||||
static int msm_geni_serial_runtime_resume(struct device *dev);
|
||||
static int msm_geni_serial_runtime_suspend(struct device *dev);
|
||||
|
||||
static atomic_t uart_line_id = ATOMIC_INIT(0);
|
||||
static int msm_geni_serial_get_ver_info(struct uart_port *uport);
|
||||
static int uart_line_id;
|
||||
|
||||
#define GET_DEV_PORT(uport) \
|
||||
container_of(uport, struct msm_geni_serial_port, uport)
|
||||
|
|
@ -190,11 +202,6 @@ static atomic_t uart_line_id = ATOMIC_INIT(0);
|
|||
static struct msm_geni_serial_port msm_geni_console_port;
|
||||
static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS];
|
||||
|
||||
static int hw_version_info(void __iomem *base_addr)
|
||||
{
|
||||
return geni_read_reg(base_addr, QUPV3_HW_VER);
|
||||
}
|
||||
|
||||
static void msm_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
|
||||
{
|
||||
if (cfg_flags & UART_CONFIG_TYPE)
|
||||
|
|
@ -313,7 +320,7 @@ static void wait_for_transfers_inflight(struct uart_port *uport)
|
|||
static int vote_clock_on(struct uart_port *uport)
|
||||
{
|
||||
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
|
||||
int usage_count = atomic_read(&uport->dev->power.usage_count);
|
||||
int usage_count;
|
||||
int ret = 0;
|
||||
|
||||
ret = msm_geni_serial_power_on(uport);
|
||||
|
|
@ -322,15 +329,18 @@ static int vote_clock_on(struct uart_port *uport)
|
|||
return ret;
|
||||
}
|
||||
port->ioctl_count++;
|
||||
IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d usage_count %d\n",
|
||||
__func__, current->comm, port->ioctl_count, usage_count);
|
||||
usage_count = atomic_read(&uport->dev->power.usage_count);
|
||||
IPC_LOG_MSG(port->ipc_log_pwr,
|
||||
"%s :%s ioctl:%d usage_count:%d edge-Count:%d\n",
|
||||
__func__, current->comm, port->ioctl_count,
|
||||
usage_count, port->edge_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vote_clock_off(struct uart_port *uport)
|
||||
{
|
||||
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
|
||||
int usage_count = atomic_read(&uport->dev->power.usage_count);
|
||||
int usage_count;
|
||||
|
||||
if (!pm_runtime_enabled(uport->dev)) {
|
||||
dev_err(uport->dev, "RPM not available.Can't enable clocks\n");
|
||||
|
|
@ -347,7 +357,8 @@ static int vote_clock_off(struct uart_port *uport)
|
|||
wait_for_transfers_inflight(uport);
|
||||
port->ioctl_count--;
|
||||
msm_geni_serial_power_off(uport);
|
||||
IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d usage_count %d\n",
|
||||
usage_count = atomic_read(&uport->dev->power.usage_count);
|
||||
IPC_LOG_MSG(port->ipc_log_pwr, "%s:%s ioctl:%d usage_count:%d\n",
|
||||
__func__, current->comm, port->ioctl_count, usage_count);
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -382,7 +393,8 @@ static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl)
|
|||
|
||||
if (!uart_console(uport) && device_pending_suspend(uport)) {
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s.Device is suspended.\n", __func__);
|
||||
"%s.Device is suspended, %s\n",
|
||||
__func__, current->comm);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -405,9 +417,14 @@ static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport)
|
|||
{
|
||||
u32 geni_ios = 0;
|
||||
unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
|
||||
struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
|
||||
|
||||
if (device_pending_suspend(uport))
|
||||
if (!uart_console(uport) && device_pending_suspend(uport)) {
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s.Device is suspended, %s\n",
|
||||
__func__, current->comm);
|
||||
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
|
||||
}
|
||||
|
||||
geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
|
||||
if (!(geni_ios & IO2_DATA_IN))
|
||||
|
|
@ -429,7 +446,8 @@ static void msm_geni_serial_set_mctrl(struct uart_port *uport,
|
|||
|
||||
if (device_pending_suspend(uport)) {
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s.Device is suspended.\n", __func__);
|
||||
"%s.Device is suspended, %s: mctrl=0x%x\n",
|
||||
__func__, current->comm, mctrl);
|
||||
return;
|
||||
}
|
||||
if (!(mctrl & TIOCM_RTS)) {
|
||||
|
|
@ -442,6 +460,10 @@ static void msm_geni_serial_set_mctrl(struct uart_port *uport,
|
|||
SE_UART_MANUAL_RFR);
|
||||
/* Write to flow control must complete before return to client*/
|
||||
mb();
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s:%s, mctrl=0x%x, manual_rfr=0x%x, flow=%s\n",
|
||||
__func__, current->comm, mctrl, uart_manual_rfr,
|
||||
(port->manual_flow ? "OFF" : "ON"));
|
||||
}
|
||||
|
||||
static const char *msm_geni_serial_get_type(struct uart_port *uport)
|
||||
|
|
@ -455,8 +477,9 @@ static struct msm_geni_serial_port *get_port_from_line(int line,
|
|||
struct msm_geni_serial_port *port = NULL;
|
||||
|
||||
if (is_console) {
|
||||
/* Max 1 port supported as of now */
|
||||
if ((line < 0) || (line >= GENI_UART_CONS_PORTS))
|
||||
port = ERR_PTR(-ENXIO);
|
||||
return ERR_PTR(-ENXIO);
|
||||
port = &msm_geni_console_port;
|
||||
} else {
|
||||
if ((line < 0) || (line >= GENI_UART_NR_PORTS))
|
||||
|
|
@ -747,10 +770,13 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
|
|||
{
|
||||
struct uart_port *uport;
|
||||
struct msm_geni_serial_port *port;
|
||||
int locked = 1;
|
||||
bool locked = true;
|
||||
unsigned long flags;
|
||||
unsigned int geni_status;
|
||||
int irq_en;
|
||||
|
||||
WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
|
||||
/* Max 1 port supported as of now */
|
||||
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
|
||||
|
||||
port = get_port_from_line(co->index, true);
|
||||
if (IS_ERR_OR_NULL(port))
|
||||
|
|
@ -762,10 +788,44 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
|
|||
else
|
||||
spin_lock_irqsave(&uport->lock, flags);
|
||||
|
||||
if (locked) {
|
||||
__msm_geni_serial_console_write(uport, s, count);
|
||||
spin_unlock_irqrestore(&uport->lock, flags);
|
||||
geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
|
||||
|
||||
/* Cancel the current write to log the fault */
|
||||
if (!locked) {
|
||||
geni_cancel_m_cmd(uport->membase);
|
||||
if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
|
||||
M_CMD_CANCEL_EN, true)) {
|
||||
geni_abort_m_cmd(uport->membase);
|
||||
msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
|
||||
M_CMD_ABORT_EN, true);
|
||||
geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
|
||||
SE_GENI_M_IRQ_CLEAR);
|
||||
}
|
||||
writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
|
||||
SE_GENI_M_IRQ_CLEAR);
|
||||
} else if ((geni_status & M_GENI_CMD_ACTIVE) &&
|
||||
!port->cur_tx_remaining) {
|
||||
/* It seems we can interrupt existing transfers unless all data
|
||||
* has been sent, in which case we need to look for done first.
|
||||
*/
|
||||
msm_geni_serial_poll_cancel_tx(uport);
|
||||
|
||||
/* Enable WM interrupt for every new console write op */
|
||||
if (uart_circ_chars_pending(&uport->state->xmit)) {
|
||||
irq_en = geni_read_reg_nolog(uport->membase,
|
||||
SE_GENI_M_IRQ_EN);
|
||||
geni_write_reg_nolog(irq_en | M_TX_FIFO_WATERMARK_EN,
|
||||
uport->membase, SE_GENI_M_IRQ_EN);
|
||||
}
|
||||
}
|
||||
|
||||
__msm_geni_serial_console_write(uport, s, count);
|
||||
|
||||
if (port->cur_tx_remaining)
|
||||
msm_geni_serial_setup_tx(uport, port->cur_tx_remaining);
|
||||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&uport->lock, flags);
|
||||
}
|
||||
|
||||
static int handle_rx_console(struct uart_port *uport,
|
||||
|
|
@ -871,6 +931,7 @@ static void msm_geni_serial_start_tx(struct uart_port *uport)
|
|||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
unsigned int geni_status;
|
||||
unsigned int geni_ios;
|
||||
static unsigned int ios_log_limit;
|
||||
|
||||
if (!uart_console(uport) && !pm_runtime_active(uport->dev)) {
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc,
|
||||
|
|
@ -913,9 +974,11 @@ static void msm_geni_serial_start_tx(struct uart_port *uport)
|
|||
return;
|
||||
check_flow_ctrl:
|
||||
geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
|
||||
if (!(geni_ios & IO2_DATA_IN))
|
||||
if (++ios_log_limit % 5 == 0) {
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ios: 0x%08x\n",
|
||||
__func__, geni_ios);
|
||||
__func__, geni_ios);
|
||||
ios_log_limit = 0;
|
||||
}
|
||||
exit_start_tx:
|
||||
if (!uart_console(uport))
|
||||
msm_geni_serial_power_off(uport);
|
||||
|
|
@ -1015,9 +1078,25 @@ static void start_rx_sequencer(struct uart_port *uport)
|
|||
int ret;
|
||||
u32 geni_se_param = UART_PARAM_RFR_OPEN;
|
||||
|
||||
if (port->startup_in_progress)
|
||||
return;
|
||||
|
||||
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
|
||||
if (geni_status & S_GENI_CMD_ACTIVE)
|
||||
if (geni_status & S_GENI_CMD_ACTIVE) {
|
||||
if (port->xfer_mode == SE_DMA && !port->rx_dma) {
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s: GENI: 0x%x\n", __func__, geni_status);
|
||||
ret = geni_se_rx_dma_prep(port->wrapper_dev,
|
||||
uport->membase, port->rx_buf, DMA_RX_BUF_SIZE,
|
||||
&port->rx_dma);
|
||||
if (ret) {
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s: RX buff Fail %d\n", __func__, ret);
|
||||
goto exit_start_rx_sequencer;
|
||||
}
|
||||
}
|
||||
msm_geni_serial_stop_rx(uport);
|
||||
}
|
||||
|
||||
/* Start RX with the RFR_OPEN to keep RFR in always ready state */
|
||||
geni_setup_s_cmd(uport->membase, UART_START_READ, geni_se_param);
|
||||
|
|
@ -1050,9 +1129,10 @@ static void start_rx_sequencer(struct uart_port *uport)
|
|||
* go through.
|
||||
*/
|
||||
mb();
|
||||
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
|
||||
exit_start_rx_sequencer:
|
||||
IPC_LOG_MSG(port->ipc_log_misc, "%s 0x%x\n", __func__, geni_status);
|
||||
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
|
||||
IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x, dma_dbg:0x%x\n", __func__,
|
||||
geni_status, geni_read_reg(uport->membase, SE_DMA_DEBUG_REG0));
|
||||
}
|
||||
|
||||
static void msm_geni_serial_start_rx(struct uart_port *uport)
|
||||
|
|
@ -1088,6 +1168,38 @@ static void msm_geni_serial_rx_fsm_rst(struct uart_port *uport)
|
|||
geni_write_reg_nolog(rx_irq_en, uport->membase, SE_DMA_RX_IRQ_EN_SET);
|
||||
}
|
||||
|
||||
static void msm_geni_serial_set_manual_flow(bool enable,
|
||||
struct msm_geni_serial_port *port)
|
||||
{
|
||||
u32 uart_manual_rfr = 0;
|
||||
|
||||
if (!enable) {
|
||||
uart_manual_rfr |= (UART_MANUAL_RFR_EN);
|
||||
geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
|
||||
SE_UART_MANUAL_RFR);
|
||||
/* UART FW needs delay per HW experts recommendation */
|
||||
udelay(10);
|
||||
|
||||
uart_manual_rfr |= (UART_RFR_NOT_READY);
|
||||
geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
|
||||
SE_UART_MANUAL_RFR);
|
||||
/*
|
||||
* Ensure that the manual flow on writes go through before
|
||||
* doing a stop_rx.
|
||||
*/
|
||||
mb();
|
||||
IPC_LOG_MSG(port->ipc_log_pwr,
|
||||
"%s: Manual Flow Enabled, HW Flow OFF\n", __func__);
|
||||
} else {
|
||||
geni_write_reg_nolog(0, port->uport.membase,
|
||||
SE_UART_MANUAL_RFR);
|
||||
/* Ensure that the manual flow off writes go through */
|
||||
mb();
|
||||
IPC_LOG_MSG(port->ipc_log_pwr,
|
||||
"%s: Manual Flow Disabled, HW Flow ON\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_rx_sequencer(struct uart_port *uport)
|
||||
{
|
||||
unsigned int geni_s_irq_en;
|
||||
|
|
@ -1150,6 +1262,8 @@ exit_rx_seq:
|
|||
DMA_RX_BUF_SIZE);
|
||||
port->rx_dma = (dma_addr_t)NULL;
|
||||
}
|
||||
geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
|
||||
IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x\n", __func__, geni_status);
|
||||
}
|
||||
|
||||
static void msm_geni_serial_stop_rx(struct uart_port *uport)
|
||||
|
|
@ -1223,40 +1337,59 @@ static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int msm_geni_serial_handle_tx(struct uart_port *uport)
|
||||
static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done,
|
||||
bool active)
|
||||
{
|
||||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
struct circ_buf *xmit = &uport->state->xmit;
|
||||
unsigned int avail_fifo_bytes = 0;
|
||||
int avail_fifo_bytes = 0;
|
||||
unsigned int bytes_remaining = 0;
|
||||
unsigned int pending;
|
||||
int i = 0;
|
||||
unsigned int tx_fifo_status;
|
||||
unsigned int xmit_size;
|
||||
unsigned int fifo_width_bytes =
|
||||
(uart_console(uport) ? 1 : (msm_port->tx_fifo_width >> 3));
|
||||
int temp_tail = 0;
|
||||
int irq_en;
|
||||
|
||||
xmit_size = uart_circ_chars_pending(xmit);
|
||||
tx_fifo_status = geni_read_reg_nolog(uport->membase,
|
||||
SE_GENI_TX_FIFO_STATUS);
|
||||
/* Both FIFO and framework buffer are drained */
|
||||
if (!xmit_size && !tx_fifo_status) {
|
||||
|
||||
/* Complete the current tx command before taking newly added data */
|
||||
if (active)
|
||||
pending = msm_port->cur_tx_remaining;
|
||||
else
|
||||
pending = uart_circ_chars_pending(xmit);
|
||||
|
||||
/* All data has been transmitted and acknowledged as received */
|
||||
if (!pending && !tx_fifo_status && done) {
|
||||
msm_geni_serial_stop_tx(uport);
|
||||
goto exit_handle_tx;
|
||||
}
|
||||
|
||||
avail_fifo_bytes = (msm_port->tx_fifo_depth - msm_port->tx_wm) *
|
||||
fifo_width_bytes;
|
||||
temp_tail = xmit->tail & (UART_XMIT_SIZE - 1);
|
||||
avail_fifo_bytes = msm_port->tx_fifo_depth - (tx_fifo_status &
|
||||
TX_FIFO_WC);
|
||||
avail_fifo_bytes *= fifo_width_bytes;
|
||||
if (avail_fifo_bytes < 0)
|
||||
avail_fifo_bytes = 0;
|
||||
|
||||
if (xmit_size > (UART_XMIT_SIZE - temp_tail))
|
||||
xmit_size = (UART_XMIT_SIZE - temp_tail);
|
||||
if (xmit_size > avail_fifo_bytes)
|
||||
xmit_size = avail_fifo_bytes;
|
||||
temp_tail = xmit->tail;
|
||||
xmit_size = min_t(unsigned int, avail_fifo_bytes, pending);
|
||||
if (!xmit_size)
|
||||
goto exit_handle_tx;
|
||||
|
||||
msm_geni_serial_setup_tx(uport, xmit_size);
|
||||
if (!msm_port->cur_tx_remaining) {
|
||||
msm_geni_serial_setup_tx(uport, pending);
|
||||
msm_port->cur_tx_remaining = pending;
|
||||
|
||||
/* Re-enable WM interrupt when starting new transfer */
|
||||
irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
|
||||
if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
|
||||
geni_write_reg_nolog(irq_en | M_TX_FIFO_WATERMARK_EN,
|
||||
uport->membase, SE_GENI_M_IRQ_EN);
|
||||
}
|
||||
|
||||
bytes_remaining = xmit_size;
|
||||
while (i < xmit_size) {
|
||||
unsigned int tx_bytes;
|
||||
|
|
@ -1266,20 +1399,39 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport)
|
|||
tx_bytes = ((bytes_remaining < fifo_width_bytes) ?
|
||||
bytes_remaining : fifo_width_bytes);
|
||||
|
||||
for (c = 0; c < tx_bytes ; c++)
|
||||
buf |= (xmit->buf[temp_tail + c] << (c * 8));
|
||||
for (c = 0; c < tx_bytes ; c++) {
|
||||
buf |= (xmit->buf[temp_tail++] << (c * 8));
|
||||
temp_tail &= UART_XMIT_SIZE - 1;
|
||||
}
|
||||
|
||||
geni_write_reg_nolog(buf, uport->membase, SE_GENI_TX_FIFOn);
|
||||
|
||||
i += tx_bytes;
|
||||
bytes_remaining -= tx_bytes;
|
||||
uport->icount.tx += tx_bytes;
|
||||
temp_tail += tx_bytes;
|
||||
msm_port->cur_tx_remaining -= tx_bytes;
|
||||
/* Ensure FIFO write goes through */
|
||||
wmb();
|
||||
}
|
||||
xmit->tail = temp_tail & (UART_XMIT_SIZE - 1);
|
||||
if (uart_console(uport))
|
||||
msm_geni_serial_poll_cancel_tx(uport);
|
||||
xmit->tail = temp_tail;
|
||||
|
||||
/*
|
||||
* The tx fifo watermark is level triggered and latched. Though we had
|
||||
* cleared it in qcom_geni_serial_isr it will have already reasserted
|
||||
* so we must clear it again here after our writes.
|
||||
*/
|
||||
geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
|
||||
SE_GENI_M_IRQ_CLEAR);
|
||||
|
||||
exit_handle_tx:
|
||||
if (!msm_port->cur_tx_remaining) {
|
||||
irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
|
||||
/* Clear WM interrupt post each transfer completion */
|
||||
if (irq_en & M_TX_FIFO_WATERMARK_EN)
|
||||
geni_write_reg_nolog(irq_en & ~M_TX_FIFO_WATERMARK_EN,
|
||||
uport->membase, SE_GENI_M_IRQ_EN);
|
||||
}
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(uport);
|
||||
return 0;
|
||||
|
|
@ -1375,13 +1527,17 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
|
|||
struct uart_port *uport = dev;
|
||||
unsigned long flags;
|
||||
unsigned int m_irq_en;
|
||||
unsigned int geni_status;
|
||||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
struct tty_port *tport = &uport->state->port;
|
||||
bool drop_rx = false;
|
||||
|
||||
spin_lock_irqsave(&uport->lock, flags);
|
||||
if (uart_console(uport) && uport->suspended)
|
||||
if (uart_console(uport) && uport->suspended) {
|
||||
IPC_LOG_MSG(msm_port->console_log,
|
||||
"%s. Console in suspend state\n", __func__);
|
||||
goto exit_geni_serial_isr;
|
||||
}
|
||||
if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
|
||||
dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc,
|
||||
|
|
@ -1392,10 +1548,15 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
|
|||
SE_GENI_M_IRQ_STATUS);
|
||||
s_irq_status = geni_read_reg_nolog(uport->membase,
|
||||
SE_GENI_S_IRQ_STATUS);
|
||||
if (uart_console(uport))
|
||||
IPC_LOG_MSG(msm_port->console_log,
|
||||
"%s. sirq 0x%x mirq:0x%x\n", __func__, s_irq_status,
|
||||
m_irq_status);
|
||||
m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
|
||||
dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN);
|
||||
dma_tx_status = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_STAT);
|
||||
dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT);
|
||||
geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
|
||||
|
||||
geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR);
|
||||
geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
|
||||
|
|
@ -1416,7 +1577,9 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
|
|||
if (!dma) {
|
||||
if ((m_irq_status & m_irq_en) &
|
||||
(M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
|
||||
msm_geni_serial_handle_tx(uport);
|
||||
msm_geni_serial_handle_tx(uport,
|
||||
m_irq_status & M_CMD_DONE_EN,
|
||||
geni_status & M_GENI_CMD_ACTIVE);
|
||||
|
||||
if ((s_irq_status & S_GP_IRQ_0_EN) ||
|
||||
(s_irq_status & S_GP_IRQ_1_EN)) {
|
||||
|
|
@ -1557,6 +1720,7 @@ static void msm_geni_serial_shutdown(struct uart_port *uport)
|
|||
{
|
||||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Stop the console before stopping the current tx */
|
||||
if (uart_console(uport)) {
|
||||
|
|
@ -1585,7 +1749,13 @@ static void msm_geni_serial_shutdown(struct uart_port *uport)
|
|||
}
|
||||
msm_port->ioctl_count = 0;
|
||||
}
|
||||
msm_geni_serial_power_off(uport);
|
||||
|
||||
ret = pm_runtime_put_sync_suspend(uport->dev);
|
||||
if (ret) {
|
||||
IPC_LOG_MSG(msm_port->ipc_log_pwr,
|
||||
"%s: Failed to suspend:%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
if (msm_port->wakeup_irq > 0) {
|
||||
irq_set_irq_wake(msm_port->wakeup_irq, 0);
|
||||
disable_irq(msm_port->wakeup_irq);
|
||||
|
|
@ -1679,6 +1849,8 @@ static int msm_geni_serial_startup(struct uart_port *uport)
|
|||
scnprintf(msm_port->name, sizeof(msm_port->name), "msm_serial_geni%d",
|
||||
uport->line);
|
||||
|
||||
msm_port->startup_in_progress = true;
|
||||
|
||||
if (likely(!uart_console(uport))) {
|
||||
ret = msm_geni_serial_power_on(&msm_port->uport);
|
||||
if (ret) {
|
||||
|
|
@ -1688,16 +1860,6 @@ static int msm_geni_serial_startup(struct uart_port *uport)
|
|||
}
|
||||
}
|
||||
|
||||
if (unlikely(get_se_proto(uport->membase) != UART)) {
|
||||
dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
|
||||
__func__, get_se_proto(uport->membase));
|
||||
ret = -ENXIO;
|
||||
goto exit_startup;
|
||||
}
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: FW Ver:0x%x%x\n",
|
||||
__func__,
|
||||
get_se_m_fw(uport->membase), get_se_s_fw(uport->membase));
|
||||
|
||||
get_tx_fifo_size(msm_port);
|
||||
if (!msm_port->port_setup) {
|
||||
if (msm_geni_serial_port_setup(uport))
|
||||
|
|
@ -1738,6 +1900,8 @@ static int msm_geni_serial_startup(struct uart_port *uport)
|
|||
exit_startup:
|
||||
if (likely(!uart_console(uport)))
|
||||
msm_geni_serial_power_off(&msm_port->uport);
|
||||
msm_port->startup_in_progress = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1809,9 +1973,6 @@ static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate)
|
|||
clk_div = ser_clk / *desired_clk_rate;
|
||||
*desired_clk_rate = ser_clk;
|
||||
exit_get_clk_div_rate:
|
||||
if (clk_div)
|
||||
clk_div = clk_div*2;
|
||||
|
||||
return clk_div;
|
||||
}
|
||||
|
||||
|
|
@ -1840,6 +2001,8 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
|
|||
__func__, ret);
|
||||
return;
|
||||
}
|
||||
disable_irq(uport->irq);
|
||||
msm_geni_serial_set_manual_flow(false, port);
|
||||
}
|
||||
/* Take a spinlock else stop_rx causes a race with an ISR due to Cancel
|
||||
* and FSM_RESET. This also has a potential race with the dma_map/unmap
|
||||
|
|
@ -1855,7 +2018,7 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
|
|||
if (clk_div <= 0)
|
||||
goto exit_set_termios;
|
||||
|
||||
if (hw_version_info(uport->membase) >= QUP_VER)
|
||||
if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING))
|
||||
clk_div *= 2;
|
||||
uport->uartclk = clk_rate;
|
||||
clk_set_rate(port->serial_rsc.se_clk, clk_rate);
|
||||
|
|
@ -1935,8 +2098,8 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
|
|||
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
geni_write_reg_nolog(0x0, uport->membase, SE_UART_MANUAL_RFR);
|
||||
IPC_LOG_MSG(port->ipc_log_misc, "%s: Manual flow off\n",
|
||||
__func__);
|
||||
IPC_LOG_MSG(port->ipc_log_misc,
|
||||
"%s: Manual flow Disabled, HW Flow ON\n", __func__);
|
||||
}
|
||||
|
||||
IPC_LOG_MSG(port->ipc_log_misc, "%s: baud %d\n", __func__, baud);
|
||||
|
|
@ -1947,6 +2110,10 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
|
|||
IPC_LOG_MSG(port->ipc_log_misc, "BitsChar%d stop bit%d\n",
|
||||
bits_per_char, stop_bit_len);
|
||||
exit_set_termios:
|
||||
if (!uart_console(uport)) {
|
||||
msm_geni_serial_set_manual_flow(true, port);
|
||||
enable_irq(uport->irq);
|
||||
}
|
||||
msm_geni_serial_start_rx(uport);
|
||||
if (!uart_console(uport))
|
||||
msm_geni_serial_power_off(uport);
|
||||
|
|
@ -1982,9 +2149,9 @@ static ssize_t xfer_mode_show(struct device *dev,
|
|||
ssize_t ret = 0;
|
||||
|
||||
if (port->xfer_mode == FIFO_MODE)
|
||||
ret = scnprintf(buf, PAGE_SIZE, "FIFO\n");
|
||||
ret = snprintf(buf, sizeof("FIFO\n"), "FIFO\n");
|
||||
else if (port->xfer_mode == SE_DMA)
|
||||
ret = scnprintf(buf, PAGE_SIZE, "SE_DMA\n");
|
||||
ret = snprintf(buf, sizeof("SE_DMA\n"), "SE_DMA\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2028,6 +2195,23 @@ static ssize_t xfer_mode_store(struct device *dev,
|
|||
|
||||
static DEVICE_ATTR_RW(xfer_mode);
|
||||
|
||||
static ssize_t ver_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_geni_serial_port *port = platform_get_drvdata(pdev);
|
||||
ssize_t ret = 0;
|
||||
int len = (sizeof(struct msm_geni_serial_ver_info) * 2);
|
||||
|
||||
ret = snprintf(buf, len, "FW ver=0x%x%x, HW ver=%d.%d.%d\n",
|
||||
port->ver_info.m_fw_ver, port->ver_info.m_fw_ver,
|
||||
port->ver_info.hw_major_ver, port->ver_info.hw_minor_ver,
|
||||
port->ver_info.hw_step_ver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RO(ver_info);
|
||||
|
||||
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
|
||||
static int __init msm_geni_console_setup(struct console *co, char *options)
|
||||
{
|
||||
|
|
@ -2039,7 +2223,8 @@ static int __init msm_geni_console_setup(struct console *co, char *options)
|
|||
int flow = 'n';
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(co->index >= GENI_UART_NR_PORTS || co->index < 0))
|
||||
/* Max 1 port supported as of now */
|
||||
if (unlikely(co->index >= GENI_UART_CONS_PORTS || co->index < 0))
|
||||
return -ENXIO;
|
||||
|
||||
dev_port = get_port_from_line(co->index, true);
|
||||
|
|
@ -2133,8 +2318,9 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev,
|
|||
goto exit_geni_serial_earlyconsetup;
|
||||
}
|
||||
|
||||
if (hw_version_info(uport->membase) >= QUP_VER)
|
||||
if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING))
|
||||
clk_div *= 2;
|
||||
|
||||
s_clk_cfg |= SER_CLK_EN;
|
||||
s_clk_cfg |= (clk_div << CLK_DIV_SHFT);
|
||||
|
||||
|
|
@ -2143,7 +2329,6 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev,
|
|||
* it else we could end up in data loss scenarios.
|
||||
*/
|
||||
msm_geni_serial_poll_cancel_tx(uport);
|
||||
msm_geni_serial_abort_rx(uport);
|
||||
|
||||
se_get_packing_config(8, 1, false, &cfg0, &cfg1);
|
||||
geni_se_init(uport->membase, (DEF_FIFO_DEPTH_WORDS >> 1),
|
||||
|
|
@ -2155,14 +2340,8 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev,
|
|||
SE_UART_TX_TRANS_CFG);
|
||||
geni_write_reg_nolog(tx_parity_cfg, uport->membase,
|
||||
SE_UART_TX_PARITY_CFG);
|
||||
geni_write_reg_nolog(rx_trans_cfg, uport->membase,
|
||||
SE_UART_RX_TRANS_CFG);
|
||||
geni_write_reg_nolog(rx_parity_cfg, uport->membase,
|
||||
SE_UART_RX_PARITY_CFG);
|
||||
geni_write_reg_nolog(bits_per_char, uport->membase,
|
||||
SE_UART_TX_WORD_LEN);
|
||||
geni_write_reg_nolog(bits_per_char, uport->membase,
|
||||
SE_UART_RX_WORD_LEN);
|
||||
geni_write_reg_nolog(stop_bit, uport->membase, SE_UART_TX_STOP_BIT_LEN);
|
||||
geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG);
|
||||
geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG);
|
||||
|
|
@ -2204,7 +2383,7 @@ static struct uart_driver msm_geni_console_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
.driver_name = "msm_geni_console",
|
||||
.dev_name = "ttyMSM",
|
||||
.nr = GENI_UART_NR_PORTS,
|
||||
.nr = GENI_UART_CONS_PORTS,
|
||||
.cons = &cons_ops,
|
||||
};
|
||||
#else
|
||||
|
|
@ -2221,14 +2400,13 @@ static void console_unregister(struct uart_driver *drv)
|
|||
static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
|
||||
{
|
||||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
char name[30];
|
||||
|
||||
msm_port->dbg = debugfs_create_dir(dev_name(uport->dev), NULL);
|
||||
if (IS_ERR_OR_NULL(msm_port->dbg))
|
||||
dev_err(uport->dev, "Failed to create dbg dir\n");
|
||||
|
||||
if (!console) {
|
||||
char name[30];
|
||||
|
||||
memset(name, 0, sizeof(name));
|
||||
if (!msm_port->ipc_log_rx) {
|
||||
scnprintf(name, sizeof(name), "%s%s",
|
||||
|
|
@ -2265,6 +2443,16 @@ static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
|
|||
if (!msm_port->ipc_log_misc)
|
||||
dev_info(uport->dev, "Err in Misc IPC Log\n");
|
||||
}
|
||||
} else {
|
||||
memset(name, 0, sizeof(name));
|
||||
if (!msm_port->console_log) {
|
||||
scnprintf(name, sizeof(name), "%s%s",
|
||||
dev_name(uport->dev), "_console");
|
||||
msm_port->console_log = ipc_log_context_create(
|
||||
IPC_LOG_MISC_PAGES, name, 0);
|
||||
if (!msm_port->console_log)
|
||||
dev_info(uport->dev, "Err in Misc IPC Log\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2329,6 +2517,43 @@ static const struct of_device_id msm_geni_device_tbl[] = {
|
|||
{},
|
||||
};
|
||||
|
||||
static int msm_geni_serial_get_ver_info(struct uart_port *uport)
|
||||
{
|
||||
int hw_ver, ret = 0;
|
||||
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
|
||||
|
||||
se_geni_clks_on(&msm_port->serial_rsc);
|
||||
/* Basic HW and FW info */
|
||||
if (unlikely(get_se_proto(uport->membase) != UART)) {
|
||||
dev_err(uport->dev, "%s: Invalid FW %d loaded.\n",
|
||||
__func__, get_se_proto(uport->membase));
|
||||
ret = -ENXIO;
|
||||
goto exit_ver_info;
|
||||
}
|
||||
|
||||
msm_port->ver_info.m_fw_ver = get_se_m_fw(uport->membase);
|
||||
msm_port->ver_info.s_fw_ver = get_se_s_fw(uport->membase);
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: FW Ver:0x%x%x\n",
|
||||
__func__,
|
||||
msm_port->ver_info.m_fw_ver, msm_port->ver_info.s_fw_ver);
|
||||
|
||||
hw_ver = geni_se_qupv3_hw_version(msm_port->wrapper_dev,
|
||||
&msm_port->ver_info.hw_major_ver,
|
||||
&msm_port->ver_info.hw_minor_ver,
|
||||
&msm_port->ver_info.hw_step_ver);
|
||||
if (hw_ver)
|
||||
dev_err(uport->dev, "%s:Err getting HW version %d\n",
|
||||
__func__, hw_ver);
|
||||
else
|
||||
IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: HW Ver:%x.%x.%x\n",
|
||||
__func__, msm_port->ver_info.hw_major_ver,
|
||||
msm_port->ver_info.hw_minor_ver,
|
||||
msm_port->ver_info.hw_step_ver);
|
||||
exit_ver_info:
|
||||
se_geni_clks_off(&msm_port->serial_rsc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_geni_serial_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
@ -2354,19 +2579,21 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
if (drv->cons)
|
||||
if (drv->cons) {
|
||||
line = of_alias_get_id(pdev->dev.of_node, "serial");
|
||||
else
|
||||
if (line < 0)
|
||||
line = 0;
|
||||
} else {
|
||||
line = of_alias_get_id(pdev->dev.of_node, "hsuart");
|
||||
if (line < 0)
|
||||
line = uart_line_id++;
|
||||
else
|
||||
uart_line_id++;
|
||||
}
|
||||
} else {
|
||||
line = pdev->id;
|
||||
}
|
||||
|
||||
if (line < 0)
|
||||
line = atomic_inc_return(&uart_line_id) - 1;
|
||||
|
||||
if ((line < 0) || (line >= GENI_UART_NR_PORTS))
|
||||
return -ENXIO;
|
||||
is_console = (drv->cons ? true : false);
|
||||
dev_port = get_port_from_line(line, is_console);
|
||||
if (IS_ERR_OR_NULL(dev_port)) {
|
||||
|
|
@ -2475,13 +2702,22 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
|
|||
}
|
||||
dev_port->serial_rsc.geni_gpio_active =
|
||||
pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
|
||||
PINCTRL_DEFAULT);
|
||||
if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
|
||||
dev_err(&pdev->dev, "No default config specified!\n");
|
||||
ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
|
||||
goto exit_geni_serial_probe;
|
||||
}
|
||||
PINCTRL_ACTIVE);
|
||||
|
||||
if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
|
||||
/*
|
||||
* Backward compatible : In case few chips doesn't have ACTIVE
|
||||
* state defined.
|
||||
*/
|
||||
dev_port->serial_rsc.geni_gpio_active =
|
||||
pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
|
||||
PINCTRL_DEFAULT);
|
||||
if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_active)) {
|
||||
dev_err(&pdev->dev, "No default config specified!\n");
|
||||
ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
|
||||
goto exit_geni_serial_probe;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* For clients who setup an Inband wakeup, leave the GPIO pins
|
||||
* always connected to the core, else move the pins to their
|
||||
|
|
@ -2542,8 +2778,12 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
|
|||
line, uport->fifosize, is_console);
|
||||
device_create_file(uport->dev, &dev_attr_loopback);
|
||||
device_create_file(uport->dev, &dev_attr_xfer_mode);
|
||||
device_create_file(uport->dev, &dev_attr_ver_info);
|
||||
msm_geni_serial_debug_init(uport, is_console);
|
||||
dev_port->port_setup = false;
|
||||
ret = msm_geni_serial_get_ver_info(uport);
|
||||
if (ret)
|
||||
goto exit_geni_serial_probe;
|
||||
return uart_add_one_port(drv, uport);
|
||||
|
||||
exit_geni_serial_probe:
|
||||
|
|
|
|||
Loading…
Reference in a new issue