From d7be24780597b467383ead2d0281e1e14fbf50b9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Feb 2019 10:03:48 +0100 Subject: [PATCH 1/4] math: replace MIN() and MAX() macros with safer versions Using macro arguments multiple times within the macro definition body can lead to repeated expressioon evaluation. To avoid that replace MIN() and MAX() macros with safer versions. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/math/numbers.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/include/sof/math/numbers.h b/src/include/sof/math/numbers.h index d98b52831dbf..87000800ff81 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -35,8 +35,16 @@ #include -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + __a > __b ? __b : __a; \ +}) +#define MAX(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + __a < __b ? __b : __a; \ +}) int gcd(int a, int b); /* Calculate greatest common divisor for a and b */ From 9104d83bcf37f19394310cd5106d5de2e96abb67 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 17 Dec 2018 14:52:07 +0100 Subject: [PATCH 2/4] Add a driver for the DesignWare UART This patch adds a driver for the DesignWare UART IP, used on Sue Creek boards for both the boot-loader and for the SOF runtime proper. Signed-off-by: Guennadi Liakhovetski --- src/drivers/dw/uart-ll.c | 105 +++++++++++ src/drivers/dw/uart-priv.h | 111 ++++++++++++ src/drivers/dw/uart-write-word.c | 67 +++++++ src/drivers/dw/uart.c | 293 +++++++++++++++++++++++++++++++ src/include/sof/uart.h | 71 ++++++++ 5 files changed, 647 insertions(+) create mode 100644 src/drivers/dw/uart-ll.c create mode 100644 src/drivers/dw/uart-priv.h create mode 100644 src/drivers/dw/uart-write-word.c create mode 100644 src/drivers/dw/uart.c create mode 100644 src/include/sof/uart.h diff --git a/src/drivers/dw/uart-ll.c b/src/drivers/dw/uart-ll.c new file mode 100644 index 000000000000..3813fdccdfee --- /dev/null +++ b/src/drivers/dw/uart-ll.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "uart-priv.h" + +static void dw_uart_write_word(struct uart *uart, uint32_t word) +{ + struct dw_uart_device *dev = container_of(uart, struct dw_uart_device, + common); + + dw_uart_write_word_internal(dev, word); +} + +#define uart_write_reg dw_uart_write_common +#define uart_read_reg dw_uart_read_common + +static struct uart_ops dw_uart_ll_ops = { + .write_word = dw_uart_write_word, +}; + +static struct dw_uart_device trace_uart_dev = { + .common.ops = &dw_uart_ll_ops, + .retry = PLATFORM_UART_RETRY, +}; + +struct uart *dw_uart_init(const struct uart_platform_data *pdata) +{ + struct dw_uart_device *dev = &trace_uart_dev; + uint32_t divisor, tmp; + + dev->common.pdata = pdata; + + if (pdata->baud != 0) { + divisor = (PLATFORM_UART_CLK_FREQ / pdata->baud) >> 4; + + /* configure the baudrate */ + tmp = uart_read_reg(dev, SUE_UART_REG_LCR); + uart_write_reg(dev, SUE_UART_REG_LCR, LCR_DLAB_BIT); + uart_write_reg(dev, SUE_UART_REG_BRDL, + (uint8_t)(divisor & 0xFF)); + uart_write_reg(dev, SUE_UART_REG_BRDH, + (uint8_t)((divisor >> 8) & 0xFF)); + + /* restore to access baudrate divisor registers */ + uart_write_reg(dev, SUE_UART_REG_LCR, tmp); + } + + /* 8-bit data, 1 stop-bit, no parity, clear DLAB */ + uart_write_reg(dev, SUE_UART_REG_LCR, + LCR_DLS(3) | LCR_STOP(0) | LCR_PEN(0)); + + /* FIFO enalbe, mode0, Tx/Rx FIFO reset */ + uart_write_reg(dev, SUE_UART_REG_FCR, FCR_FIFO_RX_8 | FCR_FIFO_TX_0 | + FCR_FIFOE(1) | FCR_MODE(0) | FCR_RCVR_RST | FCR_XMIT_RST); + + /* reset port */ + uart_write_reg(dev, SUE_UART_REG_RBR, 0); + + /* disable all interrupts */ + uart_write_reg(dev, SUE_UART_REG_IER, 0); + + /* clear possibly pending interrupts */ + tmp = uart_read_reg(dev, SUE_UART_REG_LSR); + tmp = uart_read_reg(dev, SUE_UART_REG_IIR); + + return &dev->common; +} diff --git a/src/drivers/dw/uart-priv.h b/src/drivers/dw/uart-priv.h new file mode 100644 index 000000000000..d68672b56d32 --- /dev/null +++ b/src/drivers/dw/uart-priv.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017-2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCLUDE_DW_UART_PRIV_H__ +#define __INCLUDE_DW_UART_PRIV_H__ + +#include + +/* uart register list */ +#define SUE_UART_REG_THR 0 +#define SUE_UART_REG_RBR 0 +#define SUE_UART_REG_BRDL 0 +#define SUE_UART_REG_BRDH 4 +#define SUE_UART_REG_IER 4 +#define SUE_UART_REG_FCR 8 +#define SUE_UART_REG_IIR 8 +#define SUE_UART_REG_LCR 0xC +#define SUE_UART_REG_LSR 0x14 +#define SUE_UART_REG_USR 0x7C /* UART Status reg. */ +#define SUE_UART_REG_TFL 0x80 /* Transmit FIFO Level reg. */ +#define SUE_UART_REG_CPR 0xF4 /* Component Parameter reg. */ + +#define FCR_FIFO_RX_1 0x00 /* 1 byte in RCVR FIFO */ +#define FCR_FIFO_RX_4 0x40 /* RCVR FIFO 1/4 full */ +#define FCR_FIFO_RX_8 0x80 /* RCVR FIFO 1/2 full */ +#define FCR_FIFO_RX_14 0xC0 /* RCVR FIFO 2 bytes below full */ + +/* TX FIFO interrupt levels: trigger interrupt with this many bytes in FIFO */ +#define FCR_FIFO_TX_0 0x00 /* TX FIFO empty */ +#define FCR_FIFO_TX_2 0x10 /* 2 bytes in TX FIFO */ +#define FCR_FIFO_TX_4 0x20 /* TX FIFO 1/4 full */ +#define FCR_FIFO_TX_8 0x30 /* TX FIFO 1/2 full */ + +#define dw_uart_read_common(dev, reg)\ + io_reg_read((dev)->common.pdata->port + reg) +#define dw_uart_write_common(dev, reg, value)\ + io_reg_write((dev)->common.pdata->port + reg, value) + +/* ier register */ +#define IER_PTIME 0x80 +#define IER_ETBEI 0x2 + +/* iir register */ +#define IIR_THR_EMPTY 2 /* THR empty or TX FIFO below threshold */ +#define IIR_RX_AVAILABLE 4 +#define IIR_RX_STATUS 6 /* overrun, parity, framing, break */ + +/* lcr register */ +/* + * 0x0 -- 5bits + * 0x1 -- 6bits + * 0x2 -- 7bits + * 0x3 -- 8bits + */ +#define LCR_DLS(x) (x << 0) + +/* 0-1stop, 1-1.5stop */ +#define LCR_STOP(x) (x << 2) + +/* 0-parity disabled, 1-parity enabled */ +#define LCR_PEN(x) (x << 3) + +#define LCR_DLAB_BIT 0x80 + +/* fcr register */ +/* 0-fifo disabled; 1-enabled */ +#define FCR_FIFOE(x) (x << 0) +/* 0-mode0, 1-mode1 */ +#define FCR_MODE(x) (x << 3) +#define FCR_RCVR_RST 0x2 +#define FCR_XMIT_RST 0x4 + +/* lsr register */ +#define LSR_TEMT 0x40 /* transmitter empty */ + +/* usr register */ +#define USR_TFNF 0x2 /* transmitter FIFO not full */ + +struct dw_uart_device { + struct uart common; + uint32_t retry; +}; + +void dw_uart_write_word_internal(struct dw_uart_device *dev, uint32_t word); + +#endif diff --git a/src/drivers/dw/uart-write-word.c b/src/drivers/dw/uart-write-word.c new file mode 100644 index 000000000000..413d05966461 --- /dev/null +++ b/src/drivers/dw/uart-write-word.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "uart-priv.h" + +#define uart_write_reg dw_uart_write_common +#define uart_read_reg dw_uart_read_common + +void dw_uart_write_word_internal(struct dw_uart_device *dev, uint32_t word) +{ + uint8_t bytes[8]; + uint32_t outchar; + int i, j; + uint32_t retry; + + /* store 8 nibbles of a 32-bit word in an array */ + for (i = 28, j = 0; j < ARRAY_SIZE(bytes); i -= 4, j++) + bytes[j] = (word >> i) & 0xF; + + for (i = 0; i < ARRAY_SIZE(bytes) + 1; i++) { + if (i < ARRAY_SIZE(bytes)) + outchar = bytes[i] > 9 ? + bytes[i] - 10 + 'A' : bytes[i] + '0'; + else + /* add '\n' to jump to the new line after each output */ + outchar = '\n'; + + /* wait for transmitter to become ready to accept a character */ + retry = dev->retry; + while ((uart_read_reg(dev, SUE_UART_REG_LSR) & LSR_TEMT) == 0) + if (retry-- == 0) /* don't wait too long time */ + break; + + /* write to output reg */ + uart_write_reg(dev, SUE_UART_REG_THR, outchar); + } +} diff --git a/src/drivers/dw/uart.c b/src/drivers/dw/uart.c new file mode 100644 index 000000000000..056fa79f521a --- /dev/null +++ b/src/drivers/dw/uart.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2017-2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uart-priv.h" + +struct dw_uart_device_full { + struct dw_uart_device base; + completion_t complete; + uint8_t *dw_uart_ring; + bool dw_uart_ring_empty; + /* Write to head, read from tail */ + unsigned int dw_uart_ring_head; + unsigned int dw_uart_ring_tail; + /* Protect the ring */ + spinlock_t dw_uart_ring_lock; +}; + +/* Actually the FIFO size can be read out */ +#define DW_UART_FIFO_SIZE 64 +/* Using a ring buffer only makes sense when using a TX underrun IRQ */ +#define DW_UART_RING_SIZE 4096 + +#define uart_read_reg(d, r) dw_uart_read_common(&(d)->base, r) +#define uart_write_reg(d, r, x) dw_uart_write_common(&(d)->base, r, x) + +static void dw_uart_irq_handler(void *data) +{ + struct dw_uart_device_full *dev = data; + uint32_t iir = uart_read_reg(dev, SUE_UART_REG_IIR); + unsigned int count = DW_UART_FIFO_SIZE, low = dev->dw_uart_ring_tail, + high, i; + + /* Diisable all interrupts */ + uart_write_reg(dev, SUE_UART_REG_IER, 0); + + /* + * We're only interested in the "TX empty" interrupt and only if we have + * data to send + */ + if ((iir & 0xf) != IIR_THR_EMPTY || dev->dw_uart_ring_empty) + return; + + spin_lock(&dev->dw_uart_ring_lock); + + /* Calculate data offsets, taking wrapping into account */ + if (dev->dw_uart_ring_tail < dev->dw_uart_ring_head) + high = dev->dw_uart_ring_head; + else + high = DW_UART_RING_SIZE; + + if (high - low > count) + high = low + count; + else + count = high - low; + + for (i = 0; i < count; i++) { + uint32_t value = dev->dw_uart_ring[low + i]; + + uart_write_reg(dev, SUE_UART_REG_THR, value); + } + + dev->dw_uart_ring_tail += count; + if (dev->dw_uart_ring_tail == DW_UART_RING_SIZE) + dev->dw_uart_ring_tail = 0; + + if (dev->dw_uart_ring_tail == dev->dw_uart_ring_head) { + dev->dw_uart_ring_empty = true; + /* Sent the last data chunk, wake up the sender */ + wait_completed(&dev->complete); + return; + } + + /* more data in the ring buffer */ + + if (count == DW_UART_FIFO_SIZE) { + /* FIFO full, continue after the next IRQ */ + uart_write_reg(dev, SUE_UART_REG_IER, IER_PTIME | IER_ETBEI); + goto unlock; + } + + count = DW_UART_FIFO_SIZE - count; + + /* We wrapped, tail now == 0 */ + low = 0; + high = dev->dw_uart_ring_head; + + if (high > count) + high = count; + else + count = high; + + /* Send more data from the beginning of the ring buffer */ + for (i = 0; i < count; i++) { + uint32_t value = dev->dw_uart_ring[i]; + + uart_write_reg(dev, SUE_UART_REG_THR, value); + } + + dev->dw_uart_ring_tail += count; + if (dev->dw_uart_ring_tail == dev->dw_uart_ring_head) { + dev->dw_uart_ring_empty = true; + /* All sent, wake the sender */ + wait_completed(&dev->complete); + } else { + /* More to send, wait for the next IRQ */ + uart_write_reg(dev, SUE_UART_REG_IER, IER_PTIME | IER_ETBEI); + } + +unlock: + spin_unlock(&dev->dw_uart_ring_lock); +} + +static void dw_uart_write_word(struct uart *uart, uint32_t word) +{ + struct dw_uart_device_full * const dev = container_of(uart, + struct dw_uart_device_full, base.common); + uint32_t flags; + + spin_lock_irq(&dev->dw_uart_ring_lock, flags); + dw_uart_write_word_internal(&dev->base, word); + spin_unlock_irq(&dev->dw_uart_ring_lock, flags); +} + +static int dw_uart_wait(struct uart *uart) +{ + struct dw_uart_device_full * const dev = container_of(uart, + struct dw_uart_device_full, base.common); + + /* 100ms should be enough */ + dev->complete.timeout = 100000; + return wait_for_completion_timeout(&dev->complete); +} + +static int dw_uart_write_nowait(struct uart *uart, const char *data, + uint32_t size) +{ + struct dw_uart_device_full * const dev = container_of(uart, + struct dw_uart_device_full, base.common); + unsigned int tail_room, head_room, count; + uint32_t flags; + + if (!dev->dw_uart_ring) + /* No buffer, abort */ + return -ENOBUFS; + + spin_lock_irq(&dev->dw_uart_ring_lock, flags); + + if (size <= 0 || + (!dev->dw_uart_ring_empty && + dev->dw_uart_ring_tail == dev->dw_uart_ring_head)) { + /* No data or ring full */ + spin_unlock_irq(&dev->dw_uart_ring_lock, flags); + return size; + } + + if (dev->dw_uart_ring_tail <= dev->dw_uart_ring_head) { + head_room = DW_UART_RING_SIZE - dev->dw_uart_ring_head; + tail_room = dev->dw_uart_ring_tail; + } else { + head_room = dev->dw_uart_ring_tail - dev->dw_uart_ring_head; + tail_room = 0; + } + + count = MIN(head_room, size); + arch_memcpy(dev->dw_uart_ring + dev->dw_uart_ring_head, data, count); + + dev->dw_uart_ring_head += count; + if (dev->dw_uart_ring_head == DW_UART_RING_SIZE) + dev->dw_uart_ring_head = 0; + + size -= count; + + if (size && tail_room) { + count = MIN(tail_room, size); + arch_memcpy(dev->dw_uart_ring, data + head_room, count); + dev->dw_uart_ring_head = count; + size -= count; + } + + dev->dw_uart_ring_empty = false; + + wait_clear(&dev->complete); + + /* enable the TX empty interrupt */ + uart_write_reg(dev, SUE_UART_REG_IER, IER_PTIME | IER_ETBEI); + + spin_unlock_irq(&dev->dw_uart_ring_lock, flags); + + return size; +} + +/* Block until all the data is at least in the ring buffer */ +static int dw_uart_write(struct uart *uart, const char *data, uint32_t size) +{ + for (;;) { + int ret = dw_uart_write_nowait(uart, data, size); + if (ret <= 0) + return ret; + + data += size - ret; + size = ret; + + ret = dw_uart_wait(uart); + if (ret < 0) + /* + * The ring buffer is full and no TX-empty interrupt was + * received. Abort sending + */ + return ret; + } +} + +static struct uart_ops dw_uart_ops = { + .write = dw_uart_write, + .write_nowait = dw_uart_write_nowait, + .write_word = dw_uart_write_word, +}; + +struct uart *dw_uart_init(const struct uart_platform_data *pdata) +{ + struct dw_uart_device_full *dev; + unsigned int irq; + + dev = rmalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*dev)); + if (!dev) + return NULL; + + dev->base.common.pdata = pdata; + dev->base.common.ops = &dw_uart_ops; + dev->dw_uart_ring_empty = true; + + wait_init(&dev->complete); + spinlock_init(&dev->dw_uart_ring_lock); + + /* first call - allocate the ring */ + dev->dw_uart_ring = rballoc(RZONE_BUFFER, SOF_MEM_CAPS_RAM, + DW_UART_RING_SIZE); + if (dev->dw_uart_ring) { + /* TODO: handle the interrupt controller */ + irq = pdata->irq; + if (irq >= 0 && !interrupt_register(irq, IRQ_AUTO_UNMASK, + dw_uart_irq_handler, dev)) { + interrupt_enable(irq); + } else { + /* disable trace_event(), keep trace_point() */ + rfree(dev->dw_uart_ring); + dev->dw_uart_ring = NULL; + } + } + + return &dev->base.common; +} diff --git a/src/include/sof/uart.h b/src/include/sof/uart.h new file mode 100644 index 000000000000..ff2d11c1cc4c --- /dev/null +++ b/src/include/sof/uart.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017-2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INCLUDE_UART_H__ +#define __INCLUDE_UART_H__ + +#include +#include + +struct uart_platform_data { + uint32_t port; /* register base address */ + unsigned int baud; /* baud rate */ + int irq; /* interrupt number if >= 0 */ + const char *irq_dev; /* name of the interrupt controller */ +}; + +struct uart { + const struct uart_platform_data *pdata; + const struct uart_ops *ops; +}; + +struct uart_ops { + void (*write_word)(struct uart *dev, uint32_t word); + int (*write_nowait)(struct uart *dev, const char *data, uint32_t size); + int (*write)(struct uart *dev, const char *data, uint32_t size); +}; + +static inline void uart_write_word(struct uart *uart, uint32_t word) +{ + if (uart) + uart->ops->write_word(uart, word); +} + +static inline int uart_write(struct uart *uart, + const char *data, uint32_t size) +{ + return uart ? uart->ops->write(uart, data, size) : -ENODEV; +} + +static inline int uart_write_nowait(struct uart *uart, + const char *data, uint32_t size) +{ + return uart ? uart->ops->write_nowait(uart, data, size) : -ENODEV; +} + +#endif From 80c21964fcf507f9842182b9db44a246f1e6c14c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 21 Feb 2019 10:35:43 +0100 Subject: [PATCH 3/4] sue: fix a typo in the UART base address. On Sue Creek the UART is at 0x80800, not at 0x88000. Signed-off-by: Guennadi Liakhovetski --- src/platform/suecreek/include/platform/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/suecreek/include/platform/memory.h b/src/platform/suecreek/include/platform/memory.h index 4f473869688b..435e03a41117 100644 --- a/src/platform/suecreek/include/platform/memory.h +++ b/src/platform/suecreek/include/platform/memory.h @@ -114,7 +114,7 @@ #define DW_I2C_SIZE 0x400 /* DW UART controller */ -#define DW_UART_BASE 0x88000 +#define DW_UART_BASE 0x80800 #define DW_UART_SIZE 0x400 /* DW GPIO controller */ From 68b9795bed4f85a110a859b25355489e0632c7c2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 4 Feb 2019 16:54:48 +0100 Subject: [PATCH 4/4] sue: enable the DW UART driver for trace_point() The DW UART driver support both trace_point() and trace_event() style debugging. For trace_event() it uses an interrupt. However, on Sue Creek the UART interrupt cannot be used yet. Enable only the trace_point() part of it. Signed-off-by: Guennadi Liakhovetski --- Kconfig | 18 ++++++++++++++ src/arch/xtensa/boot_loader.c | 15 ++++++++++++ src/drivers/Kconfig | 11 +++++++++ src/drivers/dw/CMakeLists.txt | 5 ++++ src/include/sof/trace.h | 24 +++++++++++++++++-- src/lib/trace.c | 24 +++++++++++++++++-- src/platform/intel/cavs/platform.c | 14 +++++++++++ .../suecreek/include/platform/interrupt.h | 7 ++++++ .../suecreek/include/platform/platform.h | 20 ++++++++++++++++ 9 files changed, 134 insertions(+), 4 deletions(-) diff --git a/Kconfig b/Kconfig index 3bfa4e171959..517f3f4afaa7 100644 --- a/Kconfig +++ b/Kconfig @@ -58,4 +58,22 @@ config BUILD_VM_ROM help Select if you want to build VM ROM +choice + prompt "Tracing mechanism" + default TRACE_DMA + +config TRACE_DMA + bool "Use DMA to host RAM for tracing" + depends on !SUECREEK + help + This uses a DMA channel for tracing + +config TRACE_UART + bool "Use a UART for tracing" + depends on UART + help + Select this to use a UART for tracing + +endchoice + endmenu diff --git a/src/arch/xtensa/boot_loader.c b/src/arch/xtensa/boot_loader.c index b9fb02b9b21a..6d6ce1041dfe 100644 --- a/src/arch/xtensa/boot_loader.c +++ b/src/arch/xtensa/boot_loader.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -276,11 +277,25 @@ static int32_t lp_sram_init(void) } #endif +#if defined(CONFIG_TRACE_UART) + +struct uart *trace_uart; + +static const struct uart_platform_data trace_uart_pdata = { + .port = PLATFORM_TRACE_UART_BASE, + .baud = PLATFORM_TRACE_UART_BAUD, +}; +#endif + /* boot master core */ void boot_master_core(void) { int32_t result; +#if defined(CONFIG_TRACE_UART) + trace_use_uart(&trace_uart_pdata); +#endif + /* TODO: platform trace should write to HW IPC regs on CNL */ platform_trace_point(TRACE_BOOT_LDR_ENTRY); diff --git a/src/drivers/Kconfig b/src/drivers/Kconfig index ac5be39deda7..50d01751d7ed 100644 --- a/src/drivers/Kconfig +++ b/src/drivers/Kconfig @@ -96,6 +96,17 @@ endmenu # "Decimation factors" endif # CAVS_DMIC +config DW_UART + bool "DW UART driver" + default n + select UART + help + Enable the DesignWare UART driver + +config UART + bool + default n + endmenu # "Drivers" config CAVS_VERSION_1_5 diff --git a/src/drivers/dw/CMakeLists.txt b/src/drivers/dw/CMakeLists.txt index 63c788920b9b..db47bddc7642 100644 --- a/src/drivers/dw/CMakeLists.txt +++ b/src/drivers/dw/CMakeLists.txt @@ -3,3 +3,8 @@ add_local_sources(sof dma.c) if(CONFIG_SUECREEK) add_local_sources(sof gpio.c ssi-spi.c) endif() + +if(CONFIG_DW_UART) + add_local_sources(sof uart.c uart-write-word.c) + add_local_sources(bootloader uart-ll.c uart-write-word.c) +endif() diff --git a/src/include/sof/trace.h b/src/include/sof/trace.h index 708789d4058b..4ec95331e47e 100644 --- a/src/include/sof/trace.h +++ b/src/include/sof/trace.h @@ -378,7 +378,24 @@ _thrown_from_macro_BASE_LOG_in_trace_h format \ } #endif -#else + +#if defined(CONFIG_TRACE_UART) +#include + +extern struct uart *trace_uart; + +static inline void trace_use_uart(const struct uart_platform_data *pdata) +{ + trace_uart = PLATFORM_TRACE_UART_INIT(pdata); +} + +static inline void trace_write_word(uint32_t word) +{ + uart_write_word(trace_uart, word); +} +#endif + +#else // TRACE #define trace_event(...) #define trace_event_atomic(...) @@ -401,6 +418,9 @@ _thrown_from_macro_BASE_LOG_in_trace_h #define tracev_value(x) #define tracev_value_atomic(x) -#endif +#define trace_use_uart(...) +#define trace_write_word(...) + +#endif // TRACE #endif diff --git a/src/lib/trace.c b/src/lib/trace.c index c49cddb5e655..4d8425f395c8 100644 --- a/src/lib/trace.c +++ b/src/lib/trace.c @@ -109,6 +109,26 @@ static void mtrace_event(const char *data, uint32_t length) #define _TRACE_EVENT_NTH_PAYLOAD_IMPL(arg_count) \ META_SEQ_FROM_0_TO(arg_count, _TRACE_EVENT_NTH_IMPL_PAYLOAD_STEP) +#ifdef CONFIG_TRACE_UART +#include + +struct uart *trace_uart; + +static void hw_trace_event(const char *e, uint32_t length) +{ + uart_write(trace_uart, e, length); +} + +static void hw_trace_event_atomic(const char *e, uint32_t length) +{ + uart_write_nowait(trace_uart, e, length); +} + +#else +#define hw_trace_event dtrace_event +#define hw_trace_event_atomic dtrace_event_atomic +#endif + /* _trace_event function with poor people's version of constexpr if */ #define _TRACE_EVENT_NTH_IMPL(is_mbox, is_atomic, arg_count) \ _TRACE_EVENT_NTH(META_CONCAT( \ @@ -132,8 +152,8 @@ META_IF_ELSE(is_atomic)(_atomic)() \ \ _TRACE_EVENT_NTH_PAYLOAD_IMPL(arg_count) \ META_IF_ELSE(is_atomic) \ - (dtrace_event_atomic) \ - (dtrace_event) \ + (hw_trace_event_atomic) \ + (hw_trace_event) \ ((const char *)dt, MESSAGE_SIZE(arg_count)); \ /* send event by mail box too. */ \ META_IF_ELSE(is_mbox) \ diff --git a/src/platform/intel/cavs/platform.c b/src/platform/intel/cavs/platform.c index 6e8345e15ea0..ea1995e4263c 100644 --- a/src/platform/intel/cavs/platform.c +++ b/src/platform/intel/cavs/platform.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -309,6 +310,15 @@ static struct spi_platform_data spi = { }; #endif +#if defined(CONFIG_TRACE_UART) +static const struct uart_platform_data trace_uart_pdata = { + .port = PLATFORM_TRACE_UART_BASE, + .baud = PLATFORM_TRACE_UART_BAUD, + .irq = PLATFORM_TRACE_UART_IRQ, + .irq_dev = PLATFORM_TRACE_UART_IRQ_DEV, +}; +#endif + struct timer *platform_timer = &platform_generic_queue[PLATFORM_MASTER_CORE_ID].timer; @@ -489,6 +499,10 @@ int platform_init(struct sof *sof) shim_write16(SHIM_PWRCTL, SHIM_PWRCTL_TCPDSP0PG); #endif +#if defined(CONFIG_TRACE_UART) + trace_use_uart(&trace_uart_pdata); +#endif + /* init DMACs */ trace_point(TRACE_BOOT_PLATFORM_DMA); ret = dmac_init(); diff --git a/src/platform/suecreek/include/platform/interrupt.h b/src/platform/suecreek/include/platform/interrupt.h index eb42bf172917..e6172959c992 100644 --- a/src/platform/suecreek/include/platform/interrupt.h +++ b/src/platform/suecreek/include/platform/interrupt.h @@ -116,6 +116,13 @@ #define IRQ_EXT_HOST_DMA_OUT_LVL3(xcpu, channel) \ SOF_IRQ(IRQ_BIT_LVL3_HOST_STREAM_OUT(channel), 3, xcpu, IRQ_NUM_EXT_LEVEL3) +#if 0 +/* The UART is connected to bit 2 of the low 32 bits of the INTC */ +#define IRQ_DW_UART 2 +#else +#define IRQ_DW_UART -EINVAL +#endif + /* Priority 4 Peripheral IRQ mappings */ #define IRQ_EXT_LINK_DMA_IN_LVL4(xcpu, channel) \ SOF_IRQ(IRQ_BIT_LVL4_LINK_STREAM_IN(channel), 4, xcpu, IRQ_NUM_EXT_LEVEL4) diff --git a/src/platform/suecreek/include/platform/platform.h b/src/platform/suecreek/include/platform/platform.h index 66bf72c6dfa6..4cf7c77f77eb 100644 --- a/src/platform/suecreek/include/platform/platform.h +++ b/src/platform/suecreek/include/platform/platform.h @@ -36,10 +36,13 @@ #include #include +#include #include #include #include +#include + struct sof; /*! \def PLATFORM_DEFAULT_CLOCK @@ -162,6 +165,18 @@ struct sof; #define PLATFORM_SPI_GPIO_ID 0 #define PLATFORM_SPI_GPIO_IRQ 14 +#define PLATFORM_UART_CLK_FREQ 38400000 +/* A simple loop counter, checking the TX empty bit */ +#define PLATFORM_UART_RETRY 25000 + +struct uart *dw_uart_init(const struct uart_platform_data *pdata); + +#define PLATFORM_TRACE_UART_BASE DW_UART_BASE +#define PLATFORM_TRACE_UART_BAUD 115200 +#define PLATFORM_TRACE_UART_INIT dw_uart_init +#define PLATFORM_TRACE_UART_IRQ IRQ_DW_UART +#define PLATFORM_TRACE_UART_IRQ_DEV "dw-intc" + // TODO: need UART versions #if 0 /* Platform defined trace code */ @@ -181,7 +196,12 @@ static inline void platform_panic(uint32_t p) } /* Platform defined trace code */ +#ifdef CONFIG_TRACE_UART +#define platform_trace_point(__x) trace_write_word(__x) +#else #define platform_trace_point(__x) +#endif + #endif extern struct timer *platform_timer;