From cea0d9c0ffc06359b01310c0dd728b4527c0013d Mon Sep 17 00:00:00 2001 From: T Michael Turney Date: Wed, 27 Nov 2019 19:28:23 -0800 Subject: sc7180: Add QUPv3 FW load & config UART driver requires firmware loading Developer/Reviewer, be aware of this patch from Napali: https://review.coreboot.org/c/coreboot/+/25372/78 https://review.coreboot.org/c/coreboot/+/27483/58 Change-Id: I4d91dd10488931247f81a87b0bdcc598f4bceb31 Signed-off-by: Roja Rani Yarubandi Reviewed-on: https://review.coreboot.org/c/coreboot/+/35499 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- src/soc/qualcomm/sc7180/qcom_qup_se.c | 258 ++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 src/soc/qualcomm/sc7180/qcom_qup_se.c (limited to 'src/soc/qualcomm/sc7180/qcom_qup_se.c') diff --git a/src/soc/qualcomm/sc7180/qcom_qup_se.c b/src/soc/qualcomm/sc7180/qcom_qup_se.c new file mode 100644 index 0000000000..b6540a88cf --- /dev/null +++ b/src/soc/qualcomm/sc7180/qcom_qup_se.c @@ -0,0 +1,258 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2018-2019 Qualcomm Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +struct qup qup[12] = { + [0] = { .regs = (void *)QUP_SERIAL0_BASE, + .pin = { GPIO(34), GPIO(35), GPIO(36), GPIO(37) }, + .func = { GPIO34_FUNC_QUP0_L0, GPIO35_FUNC_QUP0_L1, + GPIO36_FUNC_QUP0_L2, GPIO37_FUNC_QUP0_L3 } + }, + [1] = { .regs = (void *)QUP_SERIAL1_BASE, + .pin = { GPIO(0), GPIO(1), GPIO(2), GPIO(3), + GPIO(12), GPIO(94) }, + .func = { GPIO0_FUNC_QUP0_L0, GPIO1_FUNC_QUP0_L1, + GPIO2_FUNC_QUP0_L2, GPIO3_FUNC_QUP0_L3, + GPIO12_FUNC_QUP0_L4, GPIO94_FUNC_QUP0_L5 } + }, + [2] = { .regs = (void *)QUP_SERIAL2_BASE, + .pin = { GPIO(15), GPIO(16) }, + .func = { GPIO15_FUNC_QUP0_L0, GPIO16_FUNC_QUP0_L1 } + }, + [3] = { .regs = (void *)QUP_SERIAL3_BASE, + .pin = { GPIO(38), GPIO(39), GPIO(40), GPIO(41) }, + .func = { GPIO38_FUNC_QUP0_L0, GPIO39_FUNC_QUP0_L1, + GPIO40_FUNC_QUP0_L2, GPIO41_FUNC_QUP0_L3 } + }, + [4] = { .regs = (void *)QUP_SERIAL4_BASE, + .pin = { GPIO(115), GPIO(116) }, + .func = { GPIO115_FUNC_QUP0_L0, GPIO116_FUNC_QUP0_L1 } + }, + [5] = { .regs = (void *)QUP_SERIAL5_BASE, + .pin = { GPIO(25), GPIO(26), GPIO(27), GPIO(28) }, + .func = { GPIO25_FUNC_QUP0_L0, GPIO26_FUNC_QUP0_L1, + GPIO27_FUNC_QUP0_L2, GPIO28_FUNC_QUP0_L3 } + }, + [6] = { .regs = (void *)QUP_SERIAL6_BASE, + .pin = { GPIO(59), GPIO(60), GPIO(61), GPIO(62), + GPIO(68), GPIO(72) }, + .func = { GPIO59_FUNC_QUP1_L0, GPIO60_FUNC_QUP1_L1, + GPIO61_FUNC_QUP1_L2, GPIO62_FUNC_QUP1_L3, + GPIO68_FUNC_QUP1_L4, GPIO72_FUNC_QUP1_L5 } + }, + [7] = { .regs = (void *)QUP_SERIAL7_BASE, + .pin = { GPIO(6), GPIO(7) }, + .func = { GPIO6_FUNC_QUP1_L0, GPIO7_FUNC_QUP1_L1 } + }, + [8] = { .regs = (void *)QUP_SERIAL8_BASE, + .pin = { GPIO(42), GPIO(43), GPIO(44), GPIO(45) }, + .func = { GPIO42_FUNC_QUP1_L0, GPIO43_FUNC_QUP1_L1, + GPIO44_FUNC_QUP1_L2, GPIO45_FUNC_QUP1_L3 } + }, + [9] = { .regs = (void *)QUP_SERIAL9_BASE, + .pin = { GPIO(46), GPIO(47) }, + .func = { GPIO46_FUNC_QUP1_L0, GPIO47_FUNC_QUP1_L1 } + }, + [10] = { .regs = (void *)QUP_SERIAL10_BASE, + .pin = { GPIO(86), GPIO(87), GPIO(88), GPIO(89), + GPIO(90), GPIO(91) }, + .func = { GPIO86_FUNC_QUP1_L0, GPIO87_FUNC_QUP1_L1, + GPIO88_FUNC_QUP1_L2, GPIO89_FUNC_QUP1_L3, + GPIO90_FUNC_QUP1_L4, GPIO91_FUNC_QUP1_L5 } + }, + [11] = { .regs = (void *)QUP_SERIAL11_BASE, + .pin = { GPIO(53), GPIO(54), GPIO(55), GPIO(56) }, + .func = { GPIO53_FUNC_QUP1_L0, GPIO54_FUNC_QUP1_L1, + GPIO55_FUNC_QUP1_L2, GPIO56_FUNC_QUP1_L3 } + }, +}; + +u32 qup_wait_for_m_irq(unsigned int bus) +{ + struct stopwatch sw; + unsigned int m_irq = 0; + struct qup_regs *regs = qup[bus].regs; + + stopwatch_init_usecs_expire(&sw, 25); + while (!stopwatch_expired(&sw)) { + m_irq = read32(®s->geni_m_irq_status); + if (m_irq) + break; + } + return m_irq; +} + +u32 qup_wait_for_s_irq(unsigned int bus) +{ + struct stopwatch sw; + unsigned int s_irq = 0; + struct qup_regs *regs = qup[bus].regs; + + stopwatch_init_usecs_expire(&sw, 25); + while (!stopwatch_expired(&sw)) { + s_irq = read32(®s->geni_s_irq_status); + if (s_irq) + break; + } + return s_irq; +} + +static int handle_tx(unsigned int bus, const u8 *dout, + unsigned int tx_rem_bytes) +{ + int max_bytes = 0; + struct qup_regs *regs = qup[bus].regs; + + max_bytes = (FIFO_DEPTH - TX_WATERMARK) * BYTES_PER_FIFO_WORD; + max_bytes = MIN(tx_rem_bytes, max_bytes); + + buffer_to_fifo32((void *)dout, max_bytes, ®s->geni_tx_fifon, + 0, BYTES_PER_FIFO_WORD); + + if (tx_rem_bytes == max_bytes) + write32(®s->geni_tx_watermark_reg, 0); + return max_bytes; +} + +static int handle_rx(unsigned int bus, u8 *din, unsigned int rx_rem_bytes) +{ + struct qup_regs *regs = qup[bus].regs; + u32 rx_fifo_status = read32(®s->geni_rx_fifo_status); + int rx_bytes = 0; + + rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * BYTES_PER_FIFO_WORD; + rx_bytes = MIN(rx_rem_bytes, rx_bytes); + + buffer_from_fifo32(din, rx_bytes, ®s->geni_rx_fifon, + 0, BYTES_PER_FIFO_WORD); + return rx_bytes; +} + +void qup_m_cancel_and_abort(unsigned int bus) +{ + struct qup_regs *regs = qup[bus].regs; + struct stopwatch sw; + unsigned int m_irq; + + write32(®s->geni_tx_watermark_reg, 0); + write32(®s->geni_m_cmd_ctrl_reg, M_GENI_CMD_CANCEL); + + stopwatch_init_msecs_expire(&sw, 100); + do { + m_irq = qup_wait_for_m_irq(bus); + if (m_irq & M_CMD_CANCEL_EN) { + write32(®s->geni_m_irq_clear, m_irq); + break; + } + write32(®s->geni_m_irq_clear, m_irq); + } while (!stopwatch_expired(&sw)); + + if (!(m_irq & M_CMD_CANCEL_EN)) { + printk(BIOS_INFO, "%s:Cancel failed, Abort the operation\n", + __func__); + + write32(®s->geni_m_cmd_ctrl_reg, M_GENI_CMD_ABORT); + stopwatch_init_msecs_expire(&sw, 100); + do { + m_irq = qup_wait_for_m_irq(bus); + if (m_irq & M_CMD_ABORT_EN) { + write32(®s->geni_m_irq_clear, m_irq); + break; + } + write32(®s->geni_m_irq_clear, m_irq); + } while (!stopwatch_expired(&sw)); + + if (!(m_irq & M_CMD_ABORT_EN)) + printk(BIOS_INFO, "%s:Abort failed\n", __func__); + } +} + +void qup_s_cancel_and_abort(unsigned int bus) +{ + struct qup_regs *regs = qup[bus].regs; + struct stopwatch sw; + unsigned int s_irq; + u32 rx_fifo_status; + u8 buf[64]; /* FIFO size */ + + write32(®s->geni_tx_watermark_reg, 0); + write32(®s->geni_s_cmd_ctrl_reg, S_GENI_CMD_CANCEL); + + stopwatch_init_msecs_expire(&sw, 100); + do { + s_irq = qup_wait_for_s_irq(bus); + rx_fifo_status = read32(®s->geni_rx_fifo_status); + if (rx_fifo_status & RX_LAST) + handle_rx(bus, buf, 64); /* Read whatever data available in FIFO */ + if (s_irq & S_CMD_CANCEL_EN) { + write32(®s->geni_s_irq_clear, s_irq); + break; + } + write32(®s->geni_s_irq_clear, s_irq); + } while (!stopwatch_expired(&sw)); + + if (!(s_irq & S_CMD_CANCEL_EN)) { + printk(BIOS_INFO, "%s:Cancel failed, Abort the operation\n", + __func__); + + write32(®s->geni_s_cmd_ctrl_reg, S_GENI_CMD_ABORT); + stopwatch_init_msecs_expire(&sw, 100); + do { + s_irq = qup_wait_for_s_irq(bus); + if (s_irq & S_CMD_ABORT_EN) { + write32(®s->geni_s_irq_clear, s_irq); + break; + } + write32(®s->geni_s_irq_clear, s_irq); + } while (!stopwatch_expired(&sw)); + + if (!(s_irq & S_CMD_ABORT_EN)) + printk(BIOS_INFO, "%s:Abort failed\n", __func__); + } +} + +int qup_handle_transfer(unsigned int bus, const void *dout, void *din, int size) +{ + unsigned int m_irq; + struct stopwatch sw; + unsigned int rx_rem_bytes = din ? size : 0; + unsigned int tx_rem_bytes = dout ? size : 0; + struct qup_regs *regs = qup[bus].regs; + + stopwatch_init_msecs_expire(&sw, 1000); + do { + m_irq = qup_wait_for_m_irq(bus); + if ((m_irq & M_RX_FIFO_WATERMARK_EN) || + (m_irq & M_RX_FIFO_LAST_EN)) + rx_rem_bytes -= handle_rx(bus, din + size + - rx_rem_bytes, rx_rem_bytes); + if (m_irq & M_TX_FIFO_WATERMARK_EN) + tx_rem_bytes -= handle_tx(bus, dout + size + - tx_rem_bytes, tx_rem_bytes); + if (m_irq & M_CMD_DONE_EN) { + write32(®s->geni_m_irq_clear, m_irq); + break; + } + write32(®s->geni_m_irq_clear, m_irq); + } while (!stopwatch_expired(&sw)); + + if (!(m_irq & M_CMD_DONE_EN) || tx_rem_bytes || rx_rem_bytes) { + printk(BIOS_INFO, "%s:Error: Transfer failed\n", __func__); + qup_m_cancel_and_abort(bus); + return -1; + } + return 0; +} -- cgit v1.2.3