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/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/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 */ 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/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 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/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 */ 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;