* [patch v4 1/5] AST2500 DMA UART driver
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <1564147640-30753-1-git-send-email-open.sudheer@gmail.com>
From: sudheer veliseti <sudheer.open@gmail.com>
UART driver for Aspeed's bmc chip AST2500
Design approch:
AST2500 has dedicated Uart DMA controller which has 12 sets of Tx and RX channels
connected to UART controller directly.
Since the DMA controller have dedicated buffers and registers,
there would be little benifit in adding DMA framework overhead.
So the software for DMA controller is included within the UART driver itself.
implementation details:
'struct ast_uart_port' is populated and registered with uart_core.
code is organised into two layers UART-layer and DMA-Layer,both of them are
in the same file.UART-layer requests Rx and Tx dma channels
and registers callbacks with DMA controller software Layer
Interrupt service routine for DMA controller is the crucial one for Handling all
the tx and rx data. ISRs installed for individual uarts are just dummy,and are helpful
only to report any spurious interrupts in hardware.
Signed-off-by: sudheer veliseti <sudheer.open@gmail.com>
---
Changes from v3->v4:
- per port uart structures are registerd directly with uart core
Instead of registering through 8250 Frame work,
ast_uart_port is registered using uart_add_one_port
-SDMA_RX_FIX macro replaced with CONFIG_AST_UART_DMA_RX_INTERRUPT
-ast_uart_sdma_isr : DMA interrupt handler code is improvised
-replaced pr_debug with ftrace wherever appropriate
-dev_err is used in all error return cases
-uart driver structure ast25xx_uart_reg is modified
-driver name changed to ast2500-uart-dma-drv
-rx_timer initialisation and callback fn modified
Changes from v2->v3:
-custom debug replaced by in kerenl dynamic debug: pr_debug
-change-logs added
.../tty/serial/8250/8250_ast2500_uart_dma.c | 1901 +++++++++++++++++
1 file changed, 1901 insertions(+)
create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c
diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
new file mode 100644
index 000000000000..bc830d605372
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
@@ -0,0 +1,1901 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA UART Driver for ASPEED BMC chip: AST2500
+ *
+ * Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
+ * <open.sudheer@gmail.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include "8250.h"
+
+#define SERIAL8250_CONSOLE NULL
+#define TTY_AST_MAJOR 204
+#define TTY_AST_MINOR 68
+
+#define DMA_BUFF_SIZE 0x1000
+#define SDMA_RX_BUFF_SIZE 0x10000
+#define PASS_LIMIT 256
+#define UART_DMA_NR CONFIG_AST_NR_DMA_UARTS
+#define AST_UART_SDMA_CH 12
+
+/* enum ast_uart_chan_op
+ * operation codes passed to the DMA code by the user, and also used
+ * to inform the current channel owner of any changes to the system state
+ */
+enum ast_uart_chan_op {
+ AST_UART_DMAOP_TRIGGER,
+ AST_UART_DMAOP_STOP,
+ AST_UART_DMAOP_PAUSE,
+};
+
+/* ast_uart_dma_cbfn: buffer callback routinei type */
+typedef void (*ast_uart_dma_cbfn)(void *dev_id, u16 len);
+
+struct ast_sdma_info {
+ u8 ch_no;
+ u8 direction;
+ u8 enable;
+ void *priv;
+ char *sdma_virt_addr;
+ dma_addr_t dma_phy_addr;
+ /* cdriver callbacks */
+ ast_uart_dma_cbfn callback_fn; /* buffer done callback */
+};
+
+struct ast_sdma_ch {
+ struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
+ struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
+};
+
+struct ast_sdma {
+ void __iomem *reg_base;
+ int dma_irq;
+ struct ast_sdma_ch *dma_ch;
+ struct regmap *map;
+};
+
+#define UART_TX_SDMA_EN 0x00
+#define UART_RX_SDMA_EN 0x04
+#define UART_SDMA_CONF 0x08 /* Misc, Buffer size */
+#define UART_SDMA_TIMER 0x0C
+#define UART_TX_SDMA_REST 0x20
+#define UART_RX_SDMA_REST 0x24
+#define UART_TX_SDMA_IER 0x30
+#define UART_TX_SDMA_ISR 0x34
+#define UART_RX_SDMA_IER 0x38
+#define UART_RX_SDMA_ISR 0x3C
+#define UART_TX_R_POINT(x) (0x40 + ((x) * 0x20))
+#define UART_TX_W_POINT(x) (0x44 + ((x) * 0x20))
+#define UART_TX_SDMA_ADDR(x) (0x48 + ((x) * 0x20))
+#define UART_RX_R_POINT(x) (0x50 + ((x) * 0x20))
+#define UART_RX_W_POINT(x) (0x54 + ((x) * 0x20))
+#define UART_RX_SDMA_ADDR(x) (0x58 + ((x) * 0x20))
+#define SDMA_CH_EN(x) BIT(x)
+
+#define SDMA_TX_BUFF_SIZE_MASK (0x3)
+#define SDMA_SET_TX_BUFF_SIZE(x)(x)
+#define SDMA_BUFF_SIZE_1KB (0x0)
+#define SDMA_BUFF_SIZE_4KB (0x1)
+#define SDMA_BUFF_SIZE_16KB (0x2)
+#define SDMA_BUFF_SIZE_64KB (0x3)
+#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
+#define SDMA_SET_RX_BUFF_SIZE(x)((x) << 2)
+#define SDMA_TIMEOUT_DIS BIT(4)
+
+#define UART_SDMA11_INT BIT(11)
+#define UART_SDMA10_INT BIT(10)
+#define UART_SDMA9_INT BIT(9)
+#define UART_SDMA8_INT BIT(8)
+#define UART_SDMA7_INT BIT(7)
+#define UART_SDMA6_INT BIT(6)
+#define UART_SDMA5_INT BIT(5)
+#define UART_SDMA4_INT BIT(4)
+#define UART_SDMA3_INT BIT(3)
+#define UART_SDMA2_INT BIT(2)
+#define UART_SDMA1_INT BIT(1)
+#define UART_SDMA0_INT BIT(0)
+
+/*
+ * Configuration:
+ * share_irqs - whether we pass IRQF_SHARED to request_irq().
+ * This option is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
+
+struct ast_uart_port {
+ struct uart_port port;
+ unsigned short capabilities; /* port capabilities */
+ unsigned short bugs; /* port bugs */
+ unsigned int tx_loadsz; /* transmit fifo load size */
+ unsigned char acr;
+ unsigned char ier;
+ unsigned char lcr;
+ unsigned char mcr;
+ unsigned char mcr_mask; /* mask of user bits */
+ unsigned char mcr_force; /* mask of forced bits */
+ struct circ_buf rx_dma_buf;
+ struct circ_buf tx_dma_buf;
+ unsigned char dma_channel;
+ dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
+ dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ struct tasklet_struct rx_tasklet;
+#else
+ struct timer_list rx_timer;
+ unsigned int workaround;
+#endif
+ struct tasklet_struct tx_tasklet;
+ spinlock_t lock;
+ int tx_done;
+ int tx_count;
+ struct platform_device *ast_uart_pdev;
+/*
+ * Some bits in registers are cleared on a read, so they must
+ * be saved whenever the register is read but the bits will not
+ * be immediately processed.
+ */
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+ unsigned char lsr_saved_flags;
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+ unsigned char msr_saved_flags;
+
+ /*
+ * We provide a per-port pm hook.
+ */
+ void (*pm)(struct uart_port *port, unsigned int state,
+ unsigned int old);
+};
+
+static struct ast_uart_port ast_uart_ports[UART_DMA_NR];
+
+#define GET_DEV(ast_uart_port_priv_ptr)\
+ (ast_uart_port_priv_ptr->ast_uart_pdev->dev)
+
+static inline struct ast_uart_port *
+to_ast_dma_uart_port(struct uart_port *uart) {
+ return container_of(uart, struct ast_uart_port, port);
+}
+
+struct irq_info {
+ spinlock_t lock;
+ struct ast_uart_port *up;
+};
+
+static struct irq_info ast_uart_irq[1];
+static DEFINE_MUTEX(ast_uart_mutex);
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+ [PORT_UNKNOWN] = {
+ .name = "unknown",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_8250] = {
+ .name = "8250",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16450] = {
+ .name = "16450",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16550] = {
+ .name = "16550",
+ .fifo_size = 1,
+ .tx_loadsz = 1,
+ },
+ [PORT_16550A] = {
+ .name = "16550A",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
+ | UART_FCR_DMA_SELECT,
+ .flags = UART_CAP_FIFO,
+ },
+};
+
+#define map_8250_reg_offset(up, offset) ((offset) << (up->port.regshift))
+
+
+
+// SDMA - software Layer : ast-uart-sdma.c
+static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
+ u32 val, u32 reg)
+{
+ trace_printk("uart dma-write : val: %x, reg: %x\n", val, reg);
+ writel(val, sdma->reg_base + reg);
+}
+
+static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
+{
+ return readl(sdma->reg_base + reg);
+}
+
+struct ast_sdma ast_uart_sdma;
+
+int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("ch = %d, tx buff = %x\n", ch, tx_buff);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+void ast_uart_set_sdma_time_out(u16 val)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
+}
+
+int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ local_irq_save(flags);
+ trace_printk("ch=%d op=%d\n", ch, op);
+ switch (op) {
+ case AST_UART_DMAOP_TRIGGER:
+ dma_ch->enable = 1;
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ ast_uart_set_sdma_time_out(0xffff);
+#endif
+ // set enable
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
+ UART_RX_SDMA_EN);
+ break;
+ case AST_UART_DMAOP_STOP:
+ // disable engine
+ dma_ch->enable = 0;
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN)
+ & ~(0x1 << ch), UART_RX_SDMA_EN);
+ // set reset
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
+ (0x1 << ch), UART_RX_SDMA_REST);
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
+ ~(0x1 << ch), UART_RX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
+ ast_uart_sdma_write(sdma,
+ dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
+ break;
+ case AST_UART_DMAOP_PAUSE:
+ // disable engine
+ dma_ch->enable = 0;
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+ ~(0x1 << ch), UART_RX_SDMA_EN);
+ break;
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+ trace_printk("ch=%d op=%d\n", ch, op);
+ local_irq_save(flags);
+ switch (op) {
+ case AST_UART_DMAOP_TRIGGER:
+ dma_ch->enable = 1;
+ // set enable
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
+ UART_TX_SDMA_EN);
+ break;
+ case AST_UART_DMAOP_STOP:
+ dma_ch->enable = 0;
+ // disable engine
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << ch), UART_TX_SDMA_EN);
+ // set reset
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_REST)
+ | (0x1 << ch), UART_TX_SDMA_REST);
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_REST)
+ & ~(0x1 << ch), UART_TX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
+ break;
+ case AST_UART_DMAOP_PAUSE:
+ dma_ch->enable = 0;
+ // disable engine
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << ch), UART_TX_SDMA_EN);
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+u32 ast_uart_get_tx_sdma_pt(u8 ch)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
+}
+
+int ast_uart_tx_sdma_update(u8 ch, u16 point)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("TX DMA CTRL [ch %d] point %d\n", ch, point);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
+ local_irq_restore(flags);
+ return 0;
+}
+
+int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn rtn, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+ trace_printk("TX DMA REQUEST ch = %d\n", ch);
+ local_irq_save(flags);
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ dma_ch->priv = id;
+ dma_ch->callback_fn = rtn;
+
+ // DMA IRQ En
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
+ UART_TX_SDMA_IER);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+int ast_uart_rx_sdma_update(u8 ch, u16 point)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ trace_printk("RX DMA CTRL [ch %d] point %x\n", ch, point);
+ local_irq_save(flags);
+ ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
+ local_irq_restore(flags);
+ return 0;
+}
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn rtn, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ trace_printk("RX DMA REQUEST ch = %d\n", ch);
+ local_irq_save(flags);
+
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return 0;
+ }
+ dma_ch->priv = id;
+ dma_ch->callback_fn = rtn;
+ // DMA IRQ En
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
+ UART_RX_SDMA_IER);
+
+ local_irq_restore(flags);
+
+ return dma_ch->sdma_virt_addr;
+}
+
+#else
+char *ast_uart_rx_sdma_request(u8 ch, void *id)
+{
+ unsigned long flags;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+ trace_printk("RX DMA REQUEST ch = %d\n", ch);
+
+ local_irq_save(flags);
+
+ if (dma_ch->enable) {
+ local_irq_restore(flags);
+ return ERR_PTR(-EBUSY);
+ }
+ dma_ch->priv = id;
+
+ local_irq_restore(flags);
+ return dma_ch->sdma_virt_addr;
+}
+#endif
+
+u16 ast_uart_get_rx_sdma_pt(u8 ch)
+{
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
+}
+
+static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
+{
+ u32 len;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+
+ if (sdma_ch->enable == 0) {
+ pr_debug("sdma Please check ch_no %x %s!!!!!\n",
+ sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
+ if (sdma_ch->direction) {
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+ & ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
+ } else {
+ ast_uart_sdma_write(sdma,
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+ ~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
+ ast_uart_rx_sdma_update(sdma_ch->ch_no,
+ ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
+ trace_printk("OFFSET : UART_RX_SDMA_EN = %x\n ",
+ ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
+ }
+ return;
+ }
+
+ if (sdma_ch->direction) {
+ len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
+ trace_printk("tx rp %x , wp %x\n",
+ ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
+ ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
+ );
+ } else {
+ trace_printk("rx rp %x , wp %x\n",
+ ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
+ ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
+ );
+ len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
+ }
+
+ trace_printk("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
+ sdma_ch->direction ? "tx" : "rx", len);
+
+ if (sdma_ch->callback_fn != NULL)
+ (sdma_ch->callback_fn)(sdma_ch->priv, len);
+}
+
+static irqreturn_t ast_uart_sdma_isr(int irq, void *dev_id)
+{
+ struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
+ int sdma_int_num = 0;
+ u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
+ u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
+
+ trace_printk("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
+ if ((tx_sts == 0) && (rx_sts == 0)) {
+ pr_err("SDMA IRQ ERROR !!!\n");
+ return IRQ_HANDLED;
+ }
+
+ while ((sdma_int_num = ffs(rx_sts))) {
+ ast_uart_sdma_write(sdma,
+ BIT(sdma_int_num-1), UART_RX_SDMA_ISR);
+ ast_sdma_bufffdone(
+ &(sdma->dma_ch->rx_dma_info[sdma_int_num-1]));
+ rx_sts = rx_sts & (~BIT(sdma_int_num-1));
+ }
+ while ((sdma_int_num = ffs(tx_sts))) {
+ ast_uart_sdma_write(sdma,
+ BIT(sdma_int_num-1), UART_TX_SDMA_ISR);
+ ast_sdma_bufffdone(
+ &(sdma->dma_ch->tx_dma_info[sdma_int_num-1]));
+ tx_sts = tx_sts & (~BIT(sdma_int_num-1));
+ }
+ return IRQ_HANDLED;
+}
+
+static int ast_uart_sdma_probe(void)
+{
+ int i;
+ struct device_node *node;
+ int ret;
+ struct ast_sdma *sdma = &ast_uart_sdma;
+ char *rx_dma_virt_addr;
+ dma_addr_t rx_dma_phy_addr;
+
+ sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
+ if (!sdma->dma_ch)
+ return -ENOMEM;
+
+ // sdma memory mapping
+ node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
+ if (!node)
+ return -ENODEV;
+
+ sdma->reg_base = of_iomap(node, 0);
+ if (IS_ERR(sdma->reg_base))
+ return PTR_ERR(sdma->map);
+ rx_dma_virt_addr = dma_alloc_coherent(NULL,
+ SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH,
+ &rx_dma_phy_addr, GFP_KERNEL);
+
+ if (!rx_dma_virt_addr) {
+ pr_err("rx_dma_virt_addr Err:dma alloc Failed\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < AST_UART_SDMA_CH; i++) {
+ // TX ------------------------
+ sdma->dma_ch->tx_dma_info[i].enable = 0;
+ sdma->dma_ch->tx_dma_info[i].ch_no = i;
+ sdma->dma_ch->tx_dma_info[i].direction = 1;
+ ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
+ // RX ------------------------
+ sdma->dma_ch->rx_dma_info[i].enable = 0;
+ sdma->dma_ch->rx_dma_info[i].ch_no = i;
+ sdma->dma_ch->rx_dma_info[i].direction = 0;
+ sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
+ rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
+ sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
+ rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
+ ast_uart_sdma_write(sdma,
+ sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
+ UART_RX_SDMA_ADDR(i));
+ ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
+ }
+
+ ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
+ ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
+ ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
+
+ ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
+ ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
+#else
+ ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
+#endif
+
+ // TX
+ ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
+ ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
+
+ // RX
+ ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
+ ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
+
+ sdma->dma_irq = of_irq_get(node, 0);
+ ret = request_irq(sdma->dma_irq, ast_uart_sdma_isr, 0,
+ "sdma-intr", sdma);
+ if (ret) {
+ pr_err("Unable to get UART SDMA IRQ %x\n", ret);
+ return -ENODEV;
+ }
+
+ ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
+ SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
+ UART_SDMA_CONF);
+ return 0;
+}
+// END of SDMA Layer
+
+
+// UART Driver Layer
+static unsigned int ast_serial_in(struct ast_uart_port *up, int offset)
+{
+ offset = map_8250_reg_offset(up, offset);
+ return readb(up->port.membase + offset);
+}
+
+static void ast_serial_out(struct ast_uart_port *up, int offset, int value)
+{
+ /* Save the offset before it's remapped */
+ offset = map_8250_reg_offset(up, offset);
+ writeb(value, up->port.membase + offset);
+}
+
+#define serial_inp(up, offset) ast_serial_in(up, offset)
+#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct ast_uart_port *up)
+{
+ return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct ast_uart_port *up, int value)
+{
+ serial_outp(up, UART_DLL, value & 0xff);
+ serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+
+static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(
+ (struct uart_port *)data);
+ struct circ_buf *xmit = &up->port.state->xmit;
+ u32 tx_pt;
+
+ spin_lock(&up->port.lock);
+
+ up->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ dma_sync_single_for_device(up->port.dev, up->dma_tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ tx_pt = ast_uart_get_tx_sdma_pt(up->dma_channel);
+
+ if (tx_pt > xmit->head) {
+ if ((tx_pt & 0xfffc) == 0)
+ ast_uart_tx_sdma_update(up->dma_channel, 0xffff);
+ else
+ ast_uart_tx_sdma_update(up->dma_channel, 0);
+ } else {
+ ast_uart_tx_sdma_update(up->dma_channel, xmit->head);
+ }
+ ast_uart_tx_sdma_update(up->dma_channel, xmit->head);
+ spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_tx_buffdone(void *dev_id, u16 len)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)dev_id;
+ struct circ_buf *xmit = &up->port.state->xmit;
+
+ trace_printk("line[%d] : tx len = % d\n", up->port.line, len);
+ spin_lock(&up->port.lock);
+ xmit->tail = len;
+ trace_printk(" line[%d], xmit->head = %d, xmit->tail = % d\n",
+ up->port.line, xmit->head, xmit->tail);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (xmit->head != xmit->tail)
+ tasklet_schedule(&up->tx_tasklet);
+
+ spin_unlock(&up->port.lock);
+}
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(
+ (struct uart_port *)data);
+ struct tty_port *port = &up->port.state->port;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+
+ int count;
+ int copy = 0;
+
+ trace_printk("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+ up->port.line, rx_ring->head, rx_ring->tail);
+
+ spin_lock(&up->port.lock);
+ if (rx_ring->head > rx_ring->tail) {
+ count = rx_ring->head - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else if (rx_ring->head < rx_ring->tail) {
+ count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else {
+ count = 0;
+ }
+
+ if (copy != count)
+ dev_err(&GET_DEV(up), "RxData copy to tty layer failed\n");
+ if (count) {
+ rx_ring->tail += count;
+ rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+ up->port.icount.rx += count;
+ tty_flip_buffer_push(port);
+ ast_uart_rx_sdma_update(up->dma_channel, rx_ring->tail);
+ }
+ spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_rx_buffdone(void *dev_id, u16 len)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)dev_id;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+
+ trace_printk("line[%d], head = %d,len:%d\n",
+ up->port.line, up->rx_dma_buf.head, len);
+
+ spin_lock(&up->port.lock);
+ rx_ring->head = len;
+ spin_unlock(&up->port.lock);
+ tasklet_schedule(&up->rx_tasklet);
+}
+
+#else
+static void ast_uart_rx_timer_func(struct timer_list *t)
+{
+ struct ast_uart_port *up = from_timer(up, t, rx_timer);
+ struct tty_port *port = &up->port.state->port;
+ struct circ_buf *rx_ring = &up->rx_dma_buf;
+ int count, copy = 0;
+
+ trace_printk("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+ up->port.line, rx_ring->head, rx_ring->tail);
+
+ rx_ring->head = ast_uart_get_rx_sdma_pt(up->dma_channel);
+
+ del_timer(&up->rx_timer);
+
+ if (rx_ring->head > rx_ring->tail) {
+ ast_uart_set_sdma_time_out(0xffff);
+ count = rx_ring->head - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else if (rx_ring->head < rx_ring->tail) {
+ ast_uart_set_sdma_time_out(0xffff);
+ count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+ copy = tty_insert_flip_string(port,
+ rx_ring->buf + rx_ring->tail, count);
+ } else {
+ count = 0;
+ trace_printk("@@--%s-- ch = 0x%x\n", __func__, ch);
+ }
+
+ if (copy != count)
+ dev_err(&GET_DEV(up), "RxData copy to tty layer failed\n");
+
+ if (count) {
+ rx_ring->tail += count;
+ rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+ }
+ if (count) {
+ trace_printk("\n count = % d\n", count);
+ up->port.icount.rx += count;
+ spin_lock(&up->port.lock);
+ tty_flip_buffer_push(port);
+ spin_unlock(&up->port.lock);
+ trace_printk("update rx_ring->tail % x\n", rx_ring->tail);
+ ast_uart_rx_sdma_update(up->dma_channel, rx_ring->tail);
+ up->workaround = 1;
+ } else {
+ if (up->workaround) {
+ up->workaround++;
+ if (up->workaround > 1)
+ ast_uart_set_sdma_time_out(0);
+ else
+ ast_uart_set_sdma_time_out(0xffff);
+ }
+ }
+ add_timer(&up->rx_timer);
+}
+#endif
+
+/*
+ * FIFO support.
+ */
+inline void ast25xx_uart_clear_fifos(struct ast_uart_port *p)
+{
+ serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_outp(p, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_outp(p, UART_FCR, 0);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.
+ */
+static void autoconfig(struct ast_uart_port *up)
+{
+ unsigned long flags;
+
+ trace_printk("line[%d]\n", up->port.line);
+ if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+ return;
+
+ trace_printk("ttyDMA%d : autoconf (0x%04lx, 0x%p) : ", up->port.line,
+ up->port.iobase, up->port.membase);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ up->capabilities = 0;
+ up->bugs = 0;
+
+ up->port.type = PORT_16550A;
+ up->capabilities |= UART_CAP_FIFO;
+
+ up->port.fifosize = uart_config[up->port.type].fifo_size;
+ up->capabilities = uart_config[up->port.type].flags;
+ up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+ if (up->port.type == PORT_UNKNOWN)
+ goto out;
+
+ /*
+ * Reset the UART.
+ */
+ ast25xx_uart_clear_fifos(up);
+ ast_serial_in(up, UART_RX);
+ serial_outp(up, UART_IER, 0);
+
+out:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ trace_printk("type=%s\n", uart_config[up->port.type].name);
+}
+
+static inline void __stop_tx(struct ast_uart_port *p)
+{
+ if (p->ier & UART_IER_THRI) {
+ p->ier &= ~UART_IER_THRI;
+ ast_serial_out(p, UART_IER, p->ier);
+ }
+}
+
+void ast25xx_uart_stop_tx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", up->port.line);
+ __stop_tx(up);
+}
+
+static void transmit_chars(struct ast_uart_port *up);
+
+static void ast25xx_uart_start_tx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ tasklet_schedule(&up->tx_tasklet);
+}
+
+static void ast25xx_uart_stop_rx(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void ast25xx_uart_enable_ms(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+ trace_printk("line[%d]\n", port->line);
+ up->ier |= UART_IER_MSI;
+ ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct ast_uart_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ int count;
+
+ if (up->port.x_char) {
+ serial_outp(up, UART_TX, up->port.x_char);
+ up->port.icount.tx++;
+ up->port.x_char = 0;
+ return;
+ }
+ if (uart_tx_stopped(&up->port)) {
+ ast25xx_uart_stop_tx(&up->port);
+ return;
+ }
+ if (uart_circ_empty(xmit)) {
+ __stop_tx(up);
+ return;
+ }
+
+ count = up->tx_loadsz;
+ do {
+ ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit))
+ __stop_tx(up);
+}
+
+static unsigned int check_modem_status(struct ast_uart_port *up)
+{
+ unsigned int status = ast_serial_in(up, UART_MSR);
+
+ trace_printk("line[%d]\n", up->port.line);
+ status |= up->msr_saved_flags;
+ up->msr_saved_flags = 0;
+ if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI
+ && up->port.state != NULL) {
+ if (status & UART_MSR_TERI)
+ up->port.icount.rng++;
+ if (status & UART_MSR_DDSR)
+ up->port.icount.dsr++;
+ if (status & UART_MSR_DDCD)
+ uart_handle_dcd_change(&up->port,
+ status & UART_MSR_DCD);
+ if (status & UART_MSR_DCTS)
+ uart_handle_cts_change(&up->port,
+ status & UART_MSR_CTS);
+
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+ }
+
+ return status;
+}
+
+/*
+ * handles the interrupt from one port.
+ */
+static inline void ast25xx_uart_handle_port(struct ast_uart_port *up)
+{
+ unsigned int status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ status = serial_inp(up, UART_LSR);
+
+ trace_printk("status = %x\n", status);
+
+ check_modem_status(up);
+ if (status & UART_LSR_THRE)
+ transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * serial driver's interrupt routine.
+ */
+static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
+{
+ struct irq_info *i = dev_id;
+ int pass_counter = 0, handled = 0, end = 0;
+
+ trace_printk("(%d)-", irq);
+ spin_lock(&i->lock);
+
+ do {
+ struct ast_uart_port *up;
+ unsigned int iir;
+
+ up = (struct ast_uart_port *)(i->up);
+
+ iir = ast_serial_in(up, UART_IIR);
+ if (!(iir & UART_IIR_NO_INT)) {
+ ast25xx_uart_handle_port(up);
+ handled = 1;
+
+ } else
+ end = 1;
+
+ if (pass_counter++ > PASS_LIMIT) {
+ pr_err("ast-uart-dma:too much work for irq%d\n", irq);
+ break;
+ }
+ } while (end);
+
+ spin_unlock(&i->lock);
+
+ trace_printk("-(%d)\n", irq);
+
+ return IRQ_RETVAL(handled);
+}
+
+static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+ unsigned int lsr;
+
+ trace_printk("line[%d]\n", up->port.line);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ lsr = ast_serial_in(up, UART_LSR);
+ up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ast25xx_uart_get_mctrl(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned int status;
+ unsigned int ret;
+
+ status = check_modem_status(up);
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned char mcr = 0;
+
+ mctrl = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+ ast_serial_out(up, UART_MCR, mcr);
+}
+
+static void ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (break_state == -1)
+ up->lcr |= UART_LCR_SBC;
+ else
+ up->lcr &= ~UART_LCR_SBC;
+ ast_serial_out(up, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ast25xx_uart_startup(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ // TX DMA
+ struct circ_buf *xmit = &up->port.state->xmit;
+ unsigned long flags;
+ unsigned char lsr, iir;
+ int retval;
+ int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+ trace_printk("line[%d]\n", port->line);
+ up->capabilities = uart_config[up->port.type].flags;
+ up->mcr = 0;
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in set_termios())
+ */
+ ast25xx_uart_clear_fifos(up);
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void)serial_inp(up, UART_LSR);
+ (void)serial_inp(up, UART_RX);
+ (void)serial_inp(up, UART_IIR);
+ (void)serial_inp(up, UART_MSR);
+
+ ast_uart_irq[0].up = up;
+ retval = request_irq(up->port.irq, ast_uart_interrupt, irq_flags,
+ "ast-uart-dma", ast_uart_irq);
+ if (retval)
+ return retval;
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl |= TIOCM_OUT2;
+
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+
+ /*
+ * Do a quick test to see if we receive an
+ * interrupt when we enable the TX irq.
+ */
+ serial_outp(up, UART_IER, UART_IER_THRI);
+ lsr = ast_serial_in(up, UART_LSR);
+ iir = ast_serial_in(up, UART_IIR);
+ serial_outp(up, UART_IER, 0);
+
+ if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+ if (!(up->bugs & UART_BUG_TXEN)) {
+ up->bugs |= UART_BUG_TXEN;
+ pr_debug("ttyDMA%d-enabling bad tx status\n",
+ port->line);
+ }
+ } else {
+ up->bugs &= ~UART_BUG_TXEN;
+ }
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /*
+ * Clear the interrupt registers again for luck, and clear the
+ * saved flags to avoid getting false values from polling
+ * routines or the previous session.
+ */
+ serial_inp(up, UART_LSR);
+ serial_inp(up, UART_RX);
+ serial_inp(up, UART_IIR);
+ serial_inp(up, UART_MSR);
+ up->lsr_saved_flags = 0;
+ up->msr_saved_flags = 0;
+
+ // RX DMA
+ up->rx_dma_buf.head = 0;
+ up->rx_dma_buf.tail = 0;
+ up->port.icount.rx = 0;
+
+ up->tx_done = 1;
+ up->tx_count = 0;
+
+ up->rx_dma_buf.head = 0;
+ up->rx_dma_buf.tail = 0;
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ up->workaround = 0;
+#endif
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_STOP);
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_TRIGGER);
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ add_timer(&up->rx_timer);
+#endif
+ up->tx_dma_buf.head = 0;
+ up->tx_dma_buf.tail = 0;
+ up->tx_dma_buf.buf = xmit->buf;
+
+ trace_printk("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
+ xmit->head = 0;
+ xmit->tail = 0;
+
+ up->dma_tx_addr = dma_map_single(port->dev, up->tx_dma_buf.buf,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ ast_uart_tx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_STOP);
+ ast_uart_tx_sdma_enqueue(up->dma_channel, up->dma_tx_addr);
+ ast_uart_tx_sdma_update(up->dma_channel, 0);
+ ast_uart_tx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_TRIGGER);
+
+ return 0;
+}
+
+static void ast25xx_uart_shutdown(struct uart_port *port)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned long flags;
+
+ trace_printk("line[%d]\n", port->line);
+
+ up->ier = 0;
+ serial_outp(up, UART_IER, 0);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl &= ~TIOCM_OUT2;
+
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /*
+ * Disable break condition and FIFOs
+ */
+ ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+ ast25xx_uart_clear_fifos(up);
+
+ (void)ast_serial_in(up, UART_RX);
+
+ ast_uart_rx_sdma_ctrl(up->dma_channel, AST_UART_DMAOP_PAUSE);
+#ifndef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ del_timer_sync(&up->rx_timer);
+#endif
+ free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
+ unsigned int baud)
+{
+ unsigned int quot;
+
+ quot = uart_get_divisor(port, baud);
+ return quot;
+}
+
+static void ast25xx_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct ast_uart_port *up = to_ast_dma_uart_port(port);
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+ unsigned int baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (termios->c_cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ quot = ast25xx_uart_get_divisor(port, baud);
+
+ if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+ if (baud < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = uart_config[up->port.type].fcr;
+ }
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ up->port.read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ up->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ up->port.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /*
+ * ignore all characters if CREAD is not set
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ up->port.ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * CTS flow control flag and modem status interrupts
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+
+ ast_serial_out(up, UART_IER, up->ier);
+
+ serial_outp(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+
+ serial_dl_write(up, quot);
+
+ /*
+ * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+ * is written without DLAB set, this mode will be disabled.
+ */
+
+ serial_outp(up, UART_LCR, cval); /* reset DLAB */
+ up->lcr = cval; /* Save LCR */
+ if (fcr & UART_FCR_ENABLE_FIFO) {
+ /* emulated UARTs (Lucent Venus 167x) need two steps */
+ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
+ serial_outp(up, UART_FCR, fcr); /* set fcr */
+ ast25xx_uart_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static void ast25xx_uart_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct ast_uart_port *p = (struct ast_uart_port *)port;
+
+ if (p->pm)
+ p->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.
+ */
+static int ast25xx_uart_request_std_resource(struct ast_uart_port *up)
+{
+ unsigned int size = 8 << up->port.regshift;
+ int ret = 0;
+
+ if (!up->port.mapbase)
+ return ret;
+
+ if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
+ ret = -EBUSY;
+ return ret;
+ }
+
+ if (up->port.flags & UPF_IOREMAP) {
+ up->port.membase = ioremap_nocache(up->port.mapbase, size);
+ if (!up->port.membase) {
+ release_mem_region(up->port.mapbase, size);
+ ret = -ENOMEM;
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static void ast25xx_uart_release_std_resource(struct ast_uart_port *up)
+{
+ unsigned int size = 8 << up->port.regshift;
+
+ if (!up->port.mapbase)
+ return;
+
+ if (up->port.flags & UPF_IOREMAP) {
+ iounmap(up->port.membase);
+ up->port.membase = NULL;
+ }
+ release_mem_region(up->port.mapbase, size);
+}
+
+static void ast25xx_uart_release_port(struct uart_port *port)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+
+ ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_request_port(struct uart_port *port)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+ int ret;
+
+ ret = ast25xx_uart_request_std_resource(up);
+ if (ret == 0)
+ ast25xx_uart_release_std_resource(up);
+
+ return ret;
+}
+
+static void ast25xx_uart_config_port(struct uart_port *port, int flags)
+{
+ struct ast_uart_port *up = (struct ast_uart_port *)port;
+ int ret;
+
+ /*
+ * Find the region that we can probe for. This in turn
+ * tells us whether we can probe for the type of port.
+ */
+ ret = ast25xx_uart_request_std_resource(up);
+ if (ret < 0)
+ return;
+
+ if (flags & UART_CONFIG_TYPE)
+ autoconfig(up);
+
+ if (up->port.type == PORT_UNKNOWN)
+ ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ return 0;
+}
+
+static const char *ast25xx_uart_type(struct uart_port *port)
+{
+ int type = port->type;
+
+ if (type >= ARRAY_SIZE(uart_config))
+ type = 0;
+ return uart_config[type].name;
+}
+
+static const struct uart_ops ast25xx_uart_pops = {
+ .tx_empty = ast25xx_uart_tx_empty,
+ .set_mctrl = ast25xx_uart_set_mctrl,
+ .get_mctrl = ast25xx_uart_get_mctrl,
+ .stop_tx = ast25xx_uart_stop_tx,
+ .start_tx = ast25xx_uart_start_tx,
+ .stop_rx = ast25xx_uart_stop_rx,
+ .enable_ms = ast25xx_uart_enable_ms,
+ .break_ctl = ast25xx_uart_break_ctl,
+ .startup = ast25xx_uart_startup,
+ .shutdown = ast25xx_uart_shutdown,
+ .set_termios = ast25xx_uart_set_termios,
+ .pm = ast25xx_uart_pm,
+ .type = ast25xx_uart_type,
+ .release_port = ast25xx_uart_release_port,
+ .request_port = ast25xx_uart_request_port,
+ .config_port = ast25xx_uart_config_port,
+ .verify_port = ast25xx_uart_verify_port,
+};
+
+static void __init ast25xx_uart_isa_init_ports(void)
+{
+ static int first = 1;
+ int i;
+
+ if (!first)
+ return;
+ first = 0;
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ up->port.line = i;
+ spin_lock_init(&up->port.lock);
+
+ /*
+ * ALPHA_KLUDGE_MCR needs to be killed.
+ */
+ up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+ up->mcr_force = ALPHA_KLUDGE_MCR;
+ up->port.ops = &ast25xx_uart_pops;
+ }
+}
+
+static void __init ast25xx_uart_register_ports(struct uart_driver *drv,
+ struct device *dev)
+{
+ int i;
+
+ ast25xx_uart_isa_init_ports();
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ up->port.dev = dev;
+ uart_add_one_port(drv, &up->port);
+ }
+}
+
+
+static struct ast_uart_port *
+ast25xx_uart_find_match_or_unused(struct uart_port *port)
+{
+ int i;
+
+ /*
+ * First, find a port entry which matches.
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (uart_match_port(&ast_uart_ports[i].port, port))
+ return &ast_uart_ports[i];
+
+ /*
+ * We didn't find a matching entry, so look for the first
+ * free entry. We look for one which hasn't been previously
+ * used (indicated by zero iobase).
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (ast_uart_ports[i].port.type == PORT_UNKNOWN &&
+ ast_uart_ports[i].port.iobase == 0)
+ return &ast_uart_ports[i];
+
+ /*
+ * That also failed. Last resort is to find any entry which
+ * doesn't have a real port associated with it.
+ */
+ for (i = 0; i < nr_uarts; i++)
+ if (ast_uart_ports[i].port.type == PORT_UNKNOWN)
+ return &ast_uart_ports[i];
+
+ return NULL;
+}
+
+/*
+ * This "device" covers _all_ ISA 8250-compatible serial devices listed
+ * in the table in include/asm/serial.h
+ */
+static struct platform_device *ast25xx_uart_platform_obj_dev;
+
+static struct uart_driver ast25xx_uart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "ast-uart-dma",
+ .dev_name = "ttyDMA",
+ .major = TTY_AST_MAJOR,
+ .minor = TTY_AST_MINOR,
+ .nr = UART_DMA_NR,
+ .cons = SERIAL8250_CONSOLE,
+};
+
+
+/*
+ *Configure the serial port specified by the request. If the
+ *port exists and is in use, it is hung up and unregistered
+ *first.
+ *
+ *The port is then probed and if necessary the IRQ is autodetected
+ *If this fails an error is returned.
+ *
+ *On success the port is ready to use and the line number is returned.
+ */
+int ast_uart_register_port(struct uart_port *port)
+{
+ struct ast_uart_port *up;
+ int ret = -ENOSPC;
+
+ if (port->uartclk == 0)
+ return -EINVAL;
+
+ mutex_lock(&ast_uart_mutex);
+
+ up = ast25xx_uart_find_match_or_unused(port);
+ if (up) {
+ uart_remove_one_port(&ast25xx_uart_reg, &up->port);
+ up->port.iobase = port->iobase;
+ up->port.membase = port->membase;
+ up->port.irq = port->irq;
+ up->port.uartclk = port->uartclk;
+ up->port.fifosize = port->fifosize;
+ up->port.regshift = port->regshift;
+ up->port.iotype = port->iotype;
+ up->port.flags = port->flags | UPF_BOOT_AUTOCONF;
+ up->port.mapbase = port->mapbase;
+ up->port.private_data = port->private_data;
+ if (port->dev)
+ up->port.dev = port->dev;
+
+ ret = uart_add_one_port(&ast25xx_uart_reg, &up->port);
+ if (ret == 0)
+ ret = up->port.line;
+
+ spin_lock_init(&up->lock);
+
+ tasklet_init(&up->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+ (unsigned long)up);
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ tasklet_init(&up->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+ (unsigned long)up);
+#else
+ up->rx_timer.expires = jiffies + (HZ);
+ up->rx_timer.function = ast_uart_rx_timer_func;
+ timer_setup(&up->rx_timer, ast_uart_rx_timer_func, 0);
+#endif
+ }
+
+ mutex_unlock(&ast_uart_mutex);
+ return ret;
+}
+
+void ast_uart_unregister_port(int line)
+{
+ struct ast_uart_port *up = &ast_uart_ports[line];
+
+ mutex_lock(&ast_uart_mutex);
+ uart_remove_one_port(&ast25xx_uart_reg, &up->port);
+ if (ast25xx_uart_platform_obj_dev) {
+ up->port.flags &= ~UPF_BOOT_AUTOCONF;
+ up->port.type = PORT_UNKNOWN;
+ up->port.dev = &ast25xx_uart_platform_obj_dev->dev;
+ uart_add_one_port(&ast25xx_uart_reg, &up->port);
+ } else {
+ up->port.dev = NULL;
+ }
+ mutex_unlock(&ast_uart_mutex);
+}
+
+/*
+ * Register a set of serial devices attached to a platform device. The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+static int port_index;
+struct clk *clk;
+
+static int ast25xx_uart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct uart_port port;
+ struct ast_uart_port *up = NULL;
+
+ int ret = 0;
+ u32 read, dma_channel = 0;
+ struct resource *res;
+
+ if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+ trace_printk("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+
+ up = &ast_uart_ports[port_index];
+ memset(&port, 0, sizeof(struct uart_port));
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "IRQ resource not found");
+ return -ENODEV;
+ }
+ port.irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Register base not found");
+ return -ENODEV;
+ }
+ port.mapbase = res->start;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "missing controller clock");
+ return PTR_ERR(clk);
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed enabling DMA UART Clk: %d\n", ret);
+ return ret;
+ }
+ port.uartclk = clk_get_rate(clk);
+
+ ret = of_property_read_u32(np, "reg-shift", &read);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read reg-shift from DT");
+ return -ret;
+ }
+ port.regshift = read;
+ ret = of_property_read_u32(np, "dma-channel", &read);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read dma-channel from DT");
+ return -ret;
+ }
+ dma_channel = read;
+ up->dma_channel = dma_channel;
+ port.iotype = UPIO_MEM;
+ port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+ port.dev = &pdev->dev;
+ //port.private_data = up->dma_channel;
+ if (share_irqs)
+ port.flags |= UPF_SHARE_IRQ;
+
+ ret = ast_uart_register_port(&port);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "unable to registr port at index%d (IO%lx MEM%llx IRQ%d):%d\n",
+ port_index, port.iobase, (unsigned long long)port.mapbase,
+ port.irq, ret);
+ return ret;
+ }
+
+#ifdef CONFIG_AST_UART_DMA_RX_INTERRUPT
+ up->rx_dma_buf.buf =
+ ast_uart_rx_sdma_request(up->dma_channel, ast_uart_rx_buffdone,
+ up);
+ if (up->rx_dma_buf.buf < 0) {
+ trace_printk("Error : failed to get rx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+
+#else
+ up->rx_dma_buf.buf = ast_uart_rx_sdma_request(
+ up->dma_channel, up);
+ if (up->rx_dma_buf.buf < 0) {
+ trace_printk("Error : failed to get rx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+#endif
+ ret = ast_uart_tx_sdma_request(up->dma_channel,
+ ast_uart_tx_buffdone, up);
+ if (ret < 0) {
+ pr_debug("Error : failed to get tx dma channel[%d]\n",
+ up->dma_channel);
+ goto out_ast_uart_unregister_port;
+ }
+
+ up->ast_uart_pdev = pdev;
+ port_index++;
+ return 0;
+
+out_ast_uart_unregister_port:
+ up = &ast_uart_ports[port_index];
+
+ if (up->port.dev == &pdev->dev)
+ ast_uart_unregister_port(up->port.line);
+ return ret;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ast25xx_uart_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < nr_uarts; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.dev == &pdev->dev)
+ ast_uart_unregister_port(i);
+ }
+ return 0;
+}
+
+static int ast25xx_uart_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ int i;
+
+ for (i = 0; i < UART_DMA_NR; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &pdev->dev)
+ uart_suspend_port(&ast25xx_uart_reg, &up->port);
+ }
+
+ return 0;
+}
+
+static int ast25xx_uart_resume(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < UART_DMA_NR; i++) {
+ struct ast_uart_port *up = &ast_uart_ports[i];
+
+ if (up->port.type != PORT_UNKNOWN && up->port.dev == &pdev->dev)
+ serial8250_resume_port(i);
+ }
+
+ return 0;
+}
+static const struct of_device_id ast_serial_dt_ids[] = {
+ { .compatible = "aspeed,ast-sdma-uart", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver ast25xx_uart_ast_dma_driver = {
+ .probe = ast25xx_uart_probe,
+ .remove = ast25xx_uart_remove,
+ .suspend = ast25xx_uart_suspend,
+ .resume = ast25xx_uart_resume,
+ .driver = {
+ .name = "ast2500-uart-dma-drv",
+ .of_match_table = of_match_ptr(ast_serial_dt_ids),
+ },
+};
+
+static int __init ast_uart_init(void)
+{
+ int ret;
+
+ if (nr_uarts > UART_DMA_NR)
+ nr_uarts = UART_DMA_NR;
+ ret = ast_uart_sdma_probe();
+ if (ret) {
+ pr_err("ast_uart_sdma_probe Failed ret = %d\n", ret);
+ goto out;
+ }
+ pr_debug("UART driver with DMA%d ports IRQ sharing %sabled\n",
+ nr_uarts, share_irqs ? "en" : "dis");
+
+ spin_lock_init(&ast_uart_irq[0].lock);
+
+ ret = uart_register_driver(&ast25xx_uart_reg);
+ if (ret)
+ goto out;
+
+ ast25xx_uart_platform_obj_dev =
+ platform_device_alloc("ast-uart-dma", PLAT8250_DEV_LEGACY);
+ if (!ast25xx_uart_platform_obj_dev) {
+ ret = -ENOMEM;
+ goto unreg_uart_drv;
+ }
+
+ ret = platform_device_add(ast25xx_uart_platform_obj_dev);
+ if (ret)
+ goto put_dev;
+
+ ast25xx_uart_register_ports(&ast25xx_uart_reg,
+ &ast25xx_uart_platform_obj_dev->dev);
+
+ ret = platform_driver_register(&ast25xx_uart_ast_dma_driver);
+ if (ret == 0)
+ goto out;
+
+ platform_device_del(ast25xx_uart_platform_obj_dev);
+put_dev:
+ platform_device_put(ast25xx_uart_platform_obj_dev);
+unreg_uart_drv:
+ uart_unregister_driver(&ast25xx_uart_reg);
+out:
+ return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+ struct platform_device *isa_dev = ast25xx_uart_platform_obj_dev;
+
+ ast25xx_uart_platform_obj_dev = NULL;
+ platform_driver_unregister(&ast25xx_uart_ast_dma_driver);
+ platform_device_unregister(isa_dev);
+ uart_unregister_driver(&ast25xx_uart_reg);
+}
+
+module_init(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Aspeed's AST2500 DMA UART driver");
--
2.17.1
^ permalink raw reply related
* [patch v4 0/5] *** AST2500 DMA UART driver ***
From: sudheer.v @ 2019-07-26 13:27 UTC (permalink / raw)
To: linux-aspeed
From: sudheer veliseti <sudheer.open@gmail.com>
Hi,
In this patchset v4,all the comments provided for patchset v3
are addressed.Also there is considerable
change in the code related to how the per port
uart structure is registerd with uart core.
so the patch [patch v4 1/5] considerably differs from [patch v3 1/5]
Thanks and Regards
Sudheer.V
changes in v4:
- since TX uart interrupt is being invoked by 8250 framework
instead of expected TX DMA interrupt,registration of uart port is changed.
per port uart structure registration is done directly now
instead of going throgh 8250 framework.
so there is considerable change in code against v3 patch
changes in v3:
- custom debugs replaced with pr_debug in 8250_ast2500_uart_dma.c
- change logs added in patches
sudheer veliseti (5):
AST2500 DMA UART driver
build configuration for AST2500 DMA UART driver
DT nodes for AST2500 DMA UART driver
defconfig and MAINTAINERS updated for AST2500 DMA UART driver
Documentation: DT bindings AST2500 DMA UART driver
.../bindings/serial/ast2500-dma-uart.txt | 43 +
MAINTAINERS | 12 +
arch/arm/boot/dts/aspeed-ast2500-evb.dts | 21 +
arch/arm/boot/dts/aspeed-g5.dtsi | 71 +-
arch/arm/configs/aspeed_g5_defconfig | 1 +
.../tty/serial/8250/8250_ast2500_uart_dma.c | 1901 +++++++++++++++++
drivers/tty/serial/8250/Kconfig | 45 +-
drivers/tty/serial/8250/Makefile | 1 +
8 files changed, 2090 insertions(+), 5 deletions(-)
create mode 100644 Documentation/devicetree/bindings/serial/ast2500-dma-uart.txt
create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c
--
2.17.1
^ permalink raw reply
* [PATCH 04/17] ARM: dts: vesnin: Add unit address for memory node
From: Alexander A. Filippov @ 2019-07-26 7:56 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-5-andrew@aj.id.au>
On Fri, Jul 26, 2019 at 03:09:46PM +0930, Andrew Jeffery wrote:
> Fixes the following warnings:
>
> arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dt.yaml: /: memory: False schema does not allow {'reg': [[1073741824, 536870912]]}
> arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dt.yaml: memory: 'device_type' is a required property
>
> Cc: Alexander Filippov <a.filippov@yadro.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
> arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts b/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
> index 0b9e29c3212e..81d9dcb752a0 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
> @@ -14,7 +14,7 @@
> bootargs = "console=ttyS4,115200 earlyprintk";
> };
>
> - memory {
> + memory at 40000000 {
> reg = <0x40000000 0x20000000>;
> };
>
> --
> 2.20.1
>
Tested-by: Alexander Filippov <a.filippov@yadro.com>
^ permalink raw reply
* [PATCH v2 2/2] mmc: Add support for the ASPEED SD controller
From: Andrew Jeffery @ 2019-07-26 6:47 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <8a7bfe52-83ca-7601-7d75-e5615da7b5de@intel.com>
On Fri, 26 Jul 2019, at 15:27, Adrian Hunter wrote:
> On 26/07/19 3:52 AM, Andrew Jeffery wrote:
> > On Thu, 25 Jul 2019, at 22:49, Adrian Hunter wrote:
> >> On 12/07/19 6:32 AM, Andrew Jeffery wrote:
> >>> +static int aspeed_sdhci_probe(struct platform_device *pdev)
> >>> +{
> >>> + struct sdhci_pltfm_host *pltfm_host;
> >>> + struct aspeed_sdhci *dev;
> >>> + struct sdhci_host *host;
> >>> + struct resource *res;
> >>> + int slot;
> >>> + int ret;
> >>> +
> >>> + host = sdhci_pltfm_init(pdev, &aspeed_sdc_pdata, sizeof(*dev));
> >>> + if (IS_ERR(host))
> >>> + return PTR_ERR(host);
> >>> +
> >>> + pltfm_host = sdhci_priv(host);
> >>> + dev = sdhci_pltfm_priv(pltfm_host);
> >>> + dev->parent = dev_get_drvdata(pdev->dev.parent);
> >>> +
> >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>> + slot = aspeed_sdhci_calculate_slot(dev, res);
> >>> + if (slot < 0)
> >>> + return slot;
> >>> + dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
> >>> + dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
> >>
> >> That implies that you only support 2 slots which begs the question why
> >> you don't validate slot.
> >
> > I'm not sure what you mean here, but I'll dig into it.
>
> I just meant, if you only support 2 slots:
>
> if (slot > 1)
> return -EINVAL;
>
Oh, sure.
^ permalink raw reply
* [PATCH v2 2/2] mmc: Add support for the ASPEED SD controller
From: Adrian Hunter @ 2019-07-26 5:56 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <7cd30f3d-43fd-42da-9301-091eb2625c65@www.fastmail.com>
On 26/07/19 3:52 AM, Andrew Jeffery wrote:
> On Thu, 25 Jul 2019, at 22:49, Adrian Hunter wrote:
>> On 12/07/19 6:32 AM, Andrew Jeffery wrote:
>>> +static int aspeed_sdhci_probe(struct platform_device *pdev)
>>> +{
>>> + struct sdhci_pltfm_host *pltfm_host;
>>> + struct aspeed_sdhci *dev;
>>> + struct sdhci_host *host;
>>> + struct resource *res;
>>> + int slot;
>>> + int ret;
>>> +
>>> + host = sdhci_pltfm_init(pdev, &aspeed_sdc_pdata, sizeof(*dev));
>>> + if (IS_ERR(host))
>>> + return PTR_ERR(host);
>>> +
>>> + pltfm_host = sdhci_priv(host);
>>> + dev = sdhci_pltfm_priv(pltfm_host);
>>> + dev->parent = dev_get_drvdata(pdev->dev.parent);
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + slot = aspeed_sdhci_calculate_slot(dev, res);
>>> + if (slot < 0)
>>> + return slot;
>>> + dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
>>> + dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
>>
>> That implies that you only support 2 slots which begs the question why
>> you don't validate slot.
>
> I'm not sure what you mean here, but I'll dig into it.
I just meant, if you only support 2 slots:
if (slot > 1)
return -EINVAL;
^ permalink raw reply
* [RFC PATCH 17/17] ARM: dts: aspeed-g5: Sort LPC child nodes by unit address
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Lets try to maintain some sort of sanity.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g5.dtsi | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 50ba58dc5093..99d2995a43db 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -423,17 +423,18 @@
status = "disabled";
};
- lhc: lhc at 20 {
- compatible = "aspeed,ast2500-lhc";
- reg = <0x20 0x24 0x48 0x8>;
- };
-
lpc_reset: reset-controller at 18 {
compatible = "aspeed,ast2500-lpc-reset";
reg = <0x18 0x4>;
#reset-cells = <1>;
};
+ lhc: lhc at 20 {
+ compatible = "aspeed,ast2500-lhc";
+ reg = <0x20 0x24 0x48 0x8>;
+ };
+
+
ibt: ibt at c0 {
compatible = "aspeed,ast2500-ibt-bmc";
reg = <0xc0 0x18>;
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 16/17] ARM: dts: aspeed-g5: Change KCS nodes to v2 binding
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Fixes the following warnings:
arch/arm/boot/dts/aspeed-g5.dtsi:376.19-381.8: Warning (unit_address_vs_reg): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs1 at 0: node has a unit name, but no reg property
arch/arm/boot/dts/aspeed-g5.dtsi:382.19-387.8: Warning (unit_address_vs_reg): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs2 at 0: node has a unit name, but no reg property
arch/arm/boot/dts/aspeed-g5.dtsi:388.19-393.8: Warning (unit_address_vs_reg): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs3 at 0: node has a unit name, but no reg property
arch/arm/boot/dts/aspeed-g5.dtsi:405.19-410.8: Warning (unit_address_vs_reg): /ahb/apb/lpc at 1e789000/lpc-host at 80/kcs4 at 0: node has a unit name, but no reg property
arch/arm/boot/dts/aspeed-g5.dtsi:376.19-381.8: Warning (unique_unit_address): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs1 at 0: duplicate unit-address (also used in node /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs2 at 0)
arch/arm/boot/dts/aspeed-g5.dtsi:376.19-381.8: Warning (unique_unit_address): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs1 at 0: duplicate unit-address (also used in node /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs3 at 0)
arch/arm/boot/dts/aspeed-g5.dtsi:382.19-387.8: Warning (unique_unit_address): /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs2 at 0: duplicate unit-address (also used in node /ahb/apb/lpc at 1e789000/lpc-bmc at 0/kcs3 at 0)
arch/arm/boot/dts/aspeed-g5.dtsi:405.19-410.8: Warning (unique_unit_address): /ahb/apb/lpc at 1e789000/lpc-host at 80/kcs4 at 0: duplicate unit-address (also used in node /ahb/apb/lpc at 1e789000/lpc-host at 80/lpc-ctrl at 0)
Cc: Haiyue Wang <haiyue.wang@linux.intel.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g5.dtsi | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 92c659c50b4c..50ba58dc5093 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -373,22 +373,22 @@
#size-cells = <1>;
ranges = <0x0 0x0 0x80>;
- kcs1: kcs1 at 0 {
- compatible = "aspeed,ast2500-kcs-bmc";
+ kcs1: kcs at 24 {
+ compatible = "aspeed,ast2500-kcs-bmc-v2";
+ reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
interrupts = <8>;
- kcs_chan = <1>;
status = "disabled";
};
- kcs2: kcs2 at 0 {
- compatible = "aspeed,ast2500-kcs-bmc";
+ kcs2: kcs at 28 {
+ compatible = "aspeed,ast2500-kcs-bmc-v2";
+ reg = <0x28 0x1>, <0x34 0x1>, <0x40 0x1>;
interrupts = <8>;
- kcs_chan = <2>;
status = "disabled";
};
- kcs3: kcs3 at 0 {
- compatible = "aspeed,ast2500-kcs-bmc";
+ kcs3: kcs at 2c {
+ compatible = "aspeed,ast2500-kcs-bmc-v2";
+ reg = <0x2c 0x1>, <0x38 0x1>, <0x44 0x1>;
interrupts = <8>;
- kcs_chan = <3>;
status = "disabled";
};
};
@@ -402,10 +402,10 @@
#size-cells = <1>;
ranges = <0x0 0x80 0x1e0>;
- kcs4: kcs4 at 0 {
- compatible = "aspeed,ast2500-kcs-bmc";
+ kcs4: kcs at 94 {
+ compatible = "aspeed,ast2500-kcs-bmc-v2";
+ reg = <0x94 0x1>, <0x98 0x1>, <0x9c 0x1>;
interrupts = <8>;
- kcs_chan = <4>;
status = "disabled";
};
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 15/17] ipmi: kcs: aspeed: Implement v2 bindings
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The v2 bindings allow us to extract the resources from the devicetree.
The table in the driver is retained to derive the channel index, which
removes the need for kcs_chan property from the v1 bindings. The v2
bindings allow us to reduce the number of warnings generated by the
existing devicetree nodes.
Cc: Haiyue Wang <haiyue.wang@linux.intel.com>
Cc: Corey Minyard <minyard@acm.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: openipmi-developer at lists.sourceforge.net
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
drivers/char/ipmi/kcs_bmc_aspeed.c | 156 +++++++++++++++++++++++------
1 file changed, 127 insertions(+), 29 deletions(-)
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index e3dd09022589..509e0d3c6eb1 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -12,6 +12,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regmap.h>
@@ -233,38 +234,133 @@ static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
};
+static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
+{
+ struct aspeed_kcs_bmc *priv;
+ struct device_node *np;
+ struct kcs_bmc *kcs;
+ u32 channel;
+ u32 slave;
+ int rc;
+
+ np = pdev->dev.of_node;
+
+ rc = of_property_read_u32(np, "kcs_chan", &channel);
+ if ((rc != 0) || (channel == 0 || channel > KCS_CHANNEL_MAX)) {
+ dev_err(&pdev->dev, "no valid 'kcs_chan' configured\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
+ if (!kcs)
+ return ERR_PTR(-ENOMEM);
+
+ priv = kcs_bmc_priv(kcs);
+ priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(priv->map)) {
+ dev_err(&pdev->dev, "Couldn't get regmap\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ rc = of_property_read_u32(np, "kcs_addr", &slave);
+ if (rc) {
+ dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ kcs->ioreg = ast_kcs_bmc_ioregs[channel - 1];
+ aspeed_kcs_set_address(kcs, slave);
+
+ return 0;
+}
+
+static int aspeed_kcs_calculate_channel(const struct kcs_ioreg *regs)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
+ if (!memcmp(&ast_kcs_bmc_ioregs[i], regs, sizeof(*regs)))
+ return i + 1;
+ }
+
+ return -EINVAL;
+}
+
+static struct kcs_bmc *aspeed_kcs_probe_of_v2(struct platform_device *pdev)
+{
+ struct aspeed_kcs_bmc *priv;
+ struct device_node *np;
+ struct kcs_ioreg ioreg;
+ struct kcs_bmc *kcs;
+ const __be32 *reg;
+ int channel;
+ u32 slave;
+ int rc;
+
+ np = pdev->dev.of_node;
+
+ /* Don't translate addresses, we want offsets for the regmaps */
+ reg = of_get_address(np, 0, NULL, NULL);
+ if (!reg)
+ return ERR_PTR(-EINVAL);
+ ioreg.idr = be32_to_cpup(reg);
+
+ reg = of_get_address(np, 1, NULL, NULL);
+ if (!reg)
+ return ERR_PTR(-EINVAL);
+ ioreg.odr = be32_to_cpup(reg);
+
+ reg = of_get_address(np, 2, NULL, NULL);
+ if (!reg)
+ return ERR_PTR(-EINVAL);
+ ioreg.str = be32_to_cpup(reg);
+
+ channel = aspeed_kcs_calculate_channel(&ioreg);
+ if (channel < 0)
+ return ERR_PTR(channel);
+
+ kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
+ if (!kcs)
+ return ERR_PTR(-ENOMEM);
+
+ kcs->ioreg = ioreg;
+
+ priv = kcs_bmc_priv(kcs);
+ priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(priv->map)) {
+ dev_err(&pdev->dev, "Couldn't get regmap\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ rc = of_property_read_u32(np, "slave-reg", &slave);
+ if (rc)
+ return ERR_PTR(rc);
+
+ aspeed_kcs_set_address(kcs, slave);
+
+ return kcs;
+}
+
static int aspeed_kcs_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct aspeed_kcs_bmc *priv;
struct kcs_bmc *kcs_bmc;
- u32 chan, addr;
+ struct device_node *np;
int rc;
- rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan);
- if ((rc != 0) || (chan == 0 || chan > KCS_CHANNEL_MAX)) {
- dev_err(dev, "no valid 'kcs_chan' configured\n");
- return -ENODEV;
- }
+ np = pdev->dev.of_node;
+ if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
+ of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
+ kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
+ else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
+ of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
+ kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
+ else
+ return -EINVAL;
+
+ if (IS_ERR(kcs_bmc))
+ return PTR_ERR(kcs_bmc);
- rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr);
- if (rc) {
- dev_err(dev, "no valid 'kcs_addr' configured\n");
- return -ENODEV;
- }
-
- kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
- if (!kcs_bmc)
- return -ENOMEM;
-
- priv = kcs_bmc_priv(kcs_bmc);
- priv->map = syscon_node_to_regmap(dev->parent->of_node);
- if (IS_ERR(priv->map)) {
- dev_err(dev, "Couldn't get regmap\n");
- return -ENODEV;
- }
-
- kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1];
kcs_bmc->io_inputb = aspeed_kcs_inb;
kcs_bmc->io_outputb = aspeed_kcs_outb;
@@ -274,7 +370,6 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
dev_set_drvdata(dev, kcs_bmc);
- aspeed_kcs_set_address(kcs_bmc, addr);
aspeed_kcs_enable_channel(kcs_bmc, true);
rc = misc_register(&kcs_bmc->miscdev);
@@ -283,9 +378,10 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
return rc;
}
- pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n",
- chan, addr,
- kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
+ dev_dbg(&pdev->dev,
+ "Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
+ kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
+ kcs_bmc->ioreg.str);
return 0;
}
@@ -302,6 +398,8 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
static const struct of_device_id ast_kcs_bmc_match[] = {
{ .compatible = "aspeed,ast2400-kcs-bmc" },
{ .compatible = "aspeed,ast2500-kcs-bmc" },
+ { .compatible = "aspeed,ast2400-kcs-bmc-v2" },
+ { .compatible = "aspeed,ast2500-kcs-bmc-v2" },
{ }
};
MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 14/17] ipmi: kcs: Finish configuring ASPEED KCS device before enable
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The currently interrupts are configured after the channel was enabled.
Cc: Haiyue Wang <haiyue.wang@linux.intel.com>
Cc: Corey Minyard <minyard@acm.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: openipmi-developer at lists.sourceforge.net
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
drivers/char/ipmi/kcs_bmc_aspeed.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
index 3c955946e647..e3dd09022589 100644
--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -268,13 +268,14 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
kcs_bmc->io_inputb = aspeed_kcs_inb;
kcs_bmc->io_outputb = aspeed_kcs_outb;
+ rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
+ if (rc)
+ return rc;
+
dev_set_drvdata(dev, kcs_bmc);
aspeed_kcs_set_address(kcs_bmc, addr);
aspeed_kcs_enable_channel(kcs_bmc, true);
- rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
- if (rc)
- return rc;
rc = misc_register(&kcs_bmc->miscdev);
if (rc) {
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 13/17] dt-bindings: ipmi: aspeed: Introduce a v2 binding for KCS
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The v2 binding utilises reg and renames some of the v1 properties.
Cc: Haiyue Wang <haiyue.wang@linux.intel.com>
Cc: Corey Minyard <minyard@acm.org>
Cc: openipmi-developer at lists.sourceforge.net
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
.../bindings/ipmi/aspeed-kcs-bmc.txt | 20 +++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
index d98a9bf45d6c..76b180ebbde4 100644
--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
@@ -1,9 +1,10 @@
-* Aspeed KCS (Keyboard Controller Style) IPMI interface
+# Aspeed KCS (Keyboard Controller Style) IPMI interface
The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs
(Baseboard Management Controllers) and the KCS interface can be
used to perform in-band IPMI communication with their host.
+## v1
Required properties:
- compatible : should be one of
"aspeed,ast2400-kcs-bmc"
@@ -12,14 +13,21 @@ Required properties:
- kcs_chan : The LPC channel number in the controller
- kcs_addr : The host CPU IO map address
+## v2
+Required properties:
+- compatible : should be one of
+ "aspeed,ast2400-kcs-bmc-v2"
+ "aspeed,ast2500-kcs-bmc-v2"
+- reg : The address and size of the IDR, ODR and STR registers
+- interrupts : interrupt generated by the controller
+- slave-reg : The host CPU IO map address
Example:
- kcs3: kcs3 at 0 {
- compatible = "aspeed,ast2500-kcs-bmc";
- reg = <0x0 0x80>;
+ kcs3: kcs at 24 {
+ compatible = "aspeed,ast2500-kcs-bmc-v2";
+ reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
interrupts = <8>;
- kcs_chan = <3>;
- kcs_addr = <0xCA2>;
+ slave-reg = <0xca2>;
status = "okay";
};
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 12/17] ARM: dts: aspeed: Add reg hints to syscon children
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Fixes the following warnings:
arch/arm/boot/dts/aspeed-g5.dtsi:209.28-226.6: Warning (avoid_unnecessary_addr_size): /ahb/apb/syscon at 1e6e2000: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property
arch/arm/boot/dts/aspeed-g4.dtsi:156.28-172.6: Warning (avoid_unnecessary_addr_size): /ahb/apb/syscon at 1e6e2000: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property
Cc: Patrick Venture <venture@google.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g4.dtsi | 15 +++++++++------
arch/arm/boot/dts/aspeed-g5.dtsi | 17 ++++++++++-------
2 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index ed78020f6269..1515b56a1487 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -157,18 +157,21 @@
compatible = "aspeed,ast2400-scu", "syscon", "simple-mfd";
reg = <0x1e6e2000 0x1a8>;
#address-cells = <1>;
- #size-cells = <0>;
+ #size-cells = <1>;
+ ranges = <0 0x1e6e2000 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
- pinctrl: pinctrl {
- compatible = "aspeed,g4-pinctrl";
- };
-
- p2a: p2a-control {
+ p2a: p2a-control at 2c {
+ reg = <0x2c 0x4>;
compatible = "aspeed,ast2400-p2a-ctrl";
status = "disabled";
};
+
+ pinctrl: pinctrl at 80 {
+ reg = <0x80 0x18>, <0xa0 0x10>;
+ compatible = "aspeed,g4-pinctrl";
+ };
};
rng: hwrng at 1e6e2078 {
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index a8a593dd2240..92c659c50b4c 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -210,19 +210,22 @@
compatible = "aspeed,ast2500-scu", "syscon", "simple-mfd";
reg = <0x1e6e2000 0x1a8>;
#address-cells = <1>;
- #size-cells = <0>;
+ #size-cells = <1>;
+ ranges = <0 0x1e6e2000 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
- pinctrl: pinctrl {
- compatible = "aspeed,g5-pinctrl";
- aspeed,external-nodes = <&gfx>, <&lhc>;
- };
-
- p2a: p2a-control {
+ p2a: p2a-control at 2c {
compatible = "aspeed,ast2500-p2a-ctrl";
+ reg = <0x2c 0x4>;
status = "disabled";
};
+
+ pinctrl: pinctrl at 80 {
+ compatible = "aspeed,g5-pinctrl";
+ reg = <0x80 0x18>, <0xa0 0x10>;
+ aspeed,external-nodes = <&gfx>, <&lhc>;
+ };
};
rng: hwrng at 1e6e2078 {
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 11/17] dt-bindings: misc: Document reg for aspeed, p2a-ctrl nodes
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The P2A controller node sits under a syscon device, and can assume
offsets from the base of the syscon based on the compatible. However,
for devicetree correctness allow a reg property to be specified, which
an associated driver may choose to use to discover associated resources.
Cc: Patrick Venture <venture@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
index 854bd67ffec6..091d1c5ec58f 100644
--- a/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
+++ b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
@@ -18,6 +18,7 @@ Required properties:
Optional properties:
===================
+- reg: A hint for the memory regions associated with the P2A controller
- memory-region: A phandle to a reserved_memory region to be used for the PCI
to AHB mapping
--
2.20.1
^ permalink raw reply related
* [RFC PATCH 10/17] dt-bindings: pinctrl: aspeed: Add reg property as a hint
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The pinctrl node sits under a syscon device, and can assume offsets from
the base of the syscon based on the compatible. However, for devicetree
correctness allow a reg property to be specified, which an associated
driver may choose to use to discover associated resources.
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: linux-gpio at vger.kernel.org
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
.../devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml | 3 +++
.../devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml | 3 +++
2 files changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml
index 125599a2dc5e..629a55c167ae 100644
--- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml
@@ -25,6 +25,9 @@ properties:
enum:
- aspeed,ast2400-pinctrl
- aspeed,g4-pinctrl
+ reg:
+ description: |
+ A hint for the memory regions associated with the pin-controller
patternProperties:
'^.*$':
diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml
index 3e6d85318577..2e8971e23a02 100644
--- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml
@@ -25,6 +25,9 @@ properties:
enum:
- aspeed,ast2500-pinctrl
- aspeed,g5-pinctrl
+ reg:
+ description: |
+ A hint for the memory regions associated with the pin-controller
aspeed,external-nodes:
minItems: 2
maxItems: 2
--
2.20.1
^ permalink raw reply related
* [PATCH 09/17] ARM: dts: ibm-power9-dual: Add a unit address for OCC nodes
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
These temporarily have a unit address until userspace is fixed up as
noted in comments elsewhere in the dtsi.
Fixes the following warning:
arch/arm/boot/dts/ibm-power9-dual.dtsi:89.18-91.6: Warning (unit_address_vs_reg): /gpio-fsi/cfam at 0,0/sbefifo at 2400/occ: node has a reg or ranges property, but no unit name
arch/arm/boot/dts/ibm-power9-dual.dtsi:190.18-192.6: Warning (unit_address_vs_reg): /gpio-fsi/cfam at 0,0/hub at 3400/cfam at 1,0/sbefifo at 2400/occ: node has a reg or ranges property, but no unit name
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/ibm-power9-dual.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/ibm-power9-dual.dtsi b/arch/arm/boot/dts/ibm-power9-dual.dtsi
index 2abc42eda7b0..a0fa65b44b0f 100644
--- a/arch/arm/boot/dts/ibm-power9-dual.dtsi
+++ b/arch/arm/boot/dts/ibm-power9-dual.dtsi
@@ -86,7 +86,7 @@
#address-cells = <1>;
#size-cells = <0>;
- fsi_occ0: occ {
+ fsi_occ0: occ at 1 {
compatible = "ibm,p9-occ";
};
};
@@ -187,7 +187,7 @@
#address-cells = <1>;
#size-cells = <0>;
- fsi_occ1: occ {
+ fsi_occ1: occ at 2 {
compatible = "ibm,p9-occ";
};
};
--
2.20.1
^ permalink raw reply related
* [PATCH 08/17] ARM: dts: aspeed: Cleanup lpc-ctrl and snoop regs
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Fix the following warning:
arch/arm/boot/dts/aspeed-g5.dtsi:409.27-414.8: Warning (unique_unit_address): /ahb/apb/lpc at 1e789000/lpc-host at 80/lpc-ctrl at 0: duplicate unit-address (also used in node /ahb/apb/lpc at 1e789000/lpc-host at 80/lpc-snoop at 0)
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g4.dtsi | 6 +++---
arch/arm/boot/dts/aspeed-g5.dtsi | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index dd4b0b15afcf..ed78020f6269 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -296,14 +296,14 @@
lpc_ctrl: lpc-ctrl at 0 {
compatible = "aspeed,ast2400-lpc-ctrl";
- reg = <0x0 0x80>;
+ reg = <0x0 0x10>;
clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
status = "disabled";
};
- lpc_snoop: lpc-snoop at 0 {
+ lpc_snoop: lpc-snoop at 10 {
compatible = "aspeed,ast2400-lpc-snoop";
- reg = <0x0 0x80>;
+ reg = <0x10 0x8>;
interrupts = <8>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 3b4af88f9b80..a8a593dd2240 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -408,14 +408,14 @@
lpc_ctrl: lpc-ctrl at 0 {
compatible = "aspeed,ast2500-lpc-ctrl";
- reg = <0x0 0x80>;
+ reg = <0x0 0x10>;
clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
status = "disabled";
};
- lpc_snoop: lpc-snoop at 0 {
+ lpc_snoop: lpc-snoop at 10 {
compatible = "aspeed,ast2500-lpc-snoop";
- reg = <0x0 0x80>;
+ reg = <0x10 0x8>;
interrupts = <8>;
status = "disabled";
};
--
2.20.1
^ permalink raw reply related
* [PATCH 07/17] ARM: dts: witherspoon: Cleanup gpio-keys-polled properties
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
dtbs_check gave the following warning:
Warning (avoid_unnecessary_addr_size): /gpio-keys-polled: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property
Cc: Joel Stanley <joel@jms.id.au>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index 08facb2120b2..06319c6771ca 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -77,8 +77,6 @@
gpio-keys-polled {
compatible = "gpio-keys-polled";
- #address-cells = <1>;
- #size-cells = <0>;
poll-interval = <1000>;
fan0-presence {
--
2.20.1
^ permalink raw reply related
* [PATCH 06/17] ARM: dts: swift: Cleanup gpio-keys-polled properties
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
dtbs_check gave the following warning:
Warning (avoid_unnecessary_addr_size): /gpio-keys-polled: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property
Cc: Adriana Kobylak <anoo@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
index 2077e8d0e096..9f934509ca1b 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
@@ -82,8 +82,6 @@
gpio-keys-polled {
compatible = "gpio-keys-polled";
- #address-cells = <1>;
- #size-cells = <0>;
poll-interval = <1000>;
scm0-presence {
--
2.20.1
^ permalink raw reply related
* [PATCH 05/17] ARM: dts: fp5280g2: Cleanup gpio-keys-polled properties
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
dtbs_check gave the following warning:
Warning (avoid_unnecessary_addr_size): /gpio-keys-polled: unnecessary #address-cells/#size-cells without "ranges" or child "reg" property
Cc: John Wang <wangzqbj@inspur.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
index 2dd664bff928..aa5efd1dfa70 100644
--- a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
@@ -94,8 +94,6 @@
gpio-keys-polled {
compatible = "gpio-keys-polled";
- #address-cells = <1>;
- #size-cells = <0>;
poll-interval = <1000>;
fan0-presence {
--
2.20.1
^ permalink raw reply related
* [PATCH 04/17] ARM: dts: vesnin: Add unit address for memory node
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Fixes the following warnings:
arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dt.yaml: /: memory: False schema does not allow {'reg': [[1073741824, 536870912]]}
arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dt.yaml: memory: 'device_type' is a required property
Cc: Alexander Filippov <a.filippov@yadro.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts b/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
index 0b9e29c3212e..81d9dcb752a0 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts
@@ -14,7 +14,7 @@
bootargs = "console=ttyS4,115200 earlyprintk";
};
- memory {
+ memory at 40000000 {
reg = <0x40000000 0x20000000>;
};
--
2.20.1
^ permalink raw reply related
* [PATCH 03/17] ARM: dts: aspeed-g5: Fix aspeed, external-nodes description
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The existing approach lead to an error from the dtbs_check:
pinctrl: aspeed,external-nodes: [[8, 9]] is too short
Cc: Adriana Kobylak <anoo@us.ibm.com>
Cc: Brian Yang <yang.brianc.w@inventec.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: John Wang <wangzqbj@inspur.com>
Cc: Ken Chen <chen.kenyy@inventec.com>
Cc: Tao Ren <taoren@fb.com>
Cc: Xo Wang <xow@google.com>
Cc: Yuan Yao <yao.yuan@linaro.org>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-bmc-arm-centriq2400-rep.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-arm-stardragon4800-rep2.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-facebook-yamp.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-intel-s2600wf.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 4 ----
arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts | 2 --
arch/arm/boot/dts/aspeed-g5.dtsi | 3 +--
12 files changed, 1 insertion(+), 44 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-arm-centriq2400-rep.dts b/arch/arm/boot/dts/aspeed-bmc-arm-centriq2400-rep.dts
index c2ece0b91885..de9612e49c69 100644
--- a/arch/arm/boot/dts/aspeed-bmc-arm-centriq2400-rep.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-arm-centriq2400-rep.dts
@@ -211,10 +211,6 @@
status = "okay";
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&gpio {
pin_gpio_c7 {
gpio-hog;
diff --git a/arch/arm/boot/dts/aspeed-bmc-arm-stardragon4800-rep2.dts b/arch/arm/boot/dts/aspeed-bmc-arm-stardragon4800-rep2.dts
index 521afbea2c5b..d122a8efcc66 100644
--- a/arch/arm/boot/dts/aspeed-bmc-arm-stardragon4800-rep2.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-arm-stardragon4800-rep2.dts
@@ -197,10 +197,6 @@
status = "okay";
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&gpio {
pin_gpio_c7 {
gpio-hog;
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
index d519d307aa2a..dd2be50112a4 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts
@@ -64,10 +64,6 @@
};
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
/*
* Update reset type to "system" (full chip) to fix warm reboot hang issue
* when reset type is set to default ("soc", gated by reset mask registers).
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-yamp.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-yamp.dts
index 4e09a9cf32b7..d4d2d0b48f83 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-yamp.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-yamp.dts
@@ -29,10 +29,6 @@
};
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
/*
* Update reset type to "system" (full chip) to fix warm reboot hang issue
* when reset type is set to default ("soc", gated by reset mask registers).
diff --git a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
index 628195b66d46..2dd664bff928 100644
--- a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts
@@ -745,10 +745,6 @@
memory-region = <&gfx_memory>;
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&gpio {
pin_gpio_b7 {
gpio-hog;
diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-s2600wf.dts b/arch/arm/boot/dts/aspeed-bmc-intel-s2600wf.dts
index 22dade6393d0..4e9c03d5e5a3 100644
--- a/arch/arm/boot/dts/aspeed-bmc-intel-s2600wf.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-intel-s2600wf.dts
@@ -115,10 +115,6 @@
status = "okay";
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&pwm_tacho {
status = "okay";
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts b/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
index de95112e2a04..fe7965366f4a 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
@@ -260,10 +260,6 @@
status = "okay";
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&gpio {
pin_gpio_b0 {
gpio-hog;
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
index 9628ecb879cf..2fa4f361ac6a 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
@@ -255,10 +255,6 @@
memory-region = <&gfx_memory>;
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&pwm_tacho {
status = "okay";
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
index caac895c60b4..2077e8d0e096 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
@@ -937,10 +937,6 @@
memory-region = <&gfx_memory>;
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&wdt1 {
aspeed,reset-type = "none";
aspeed,external-signal;
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index 31ea34e14c79..08facb2120b2 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -618,10 +618,6 @@
memory-region = <&gfx_memory>;
};
-&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-};
-
&wdt1 {
aspeed,reset-type = "none";
aspeed,external-signal;
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
index 30624378316d..c99113e69e43 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
@@ -461,8 +461,6 @@
};
&pinctrl {
- aspeed,external-nodes = <&gfx &lhc>;
-
pinctrl_gpioh_unbiased: gpioi_unbiased {
pins = "A8", "C7", "B7", "A7", "D7", "B6", "A6", "E7";
bias-disable;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 6e5b0c493f16..3b4af88f9b80 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -216,8 +216,7 @@
pinctrl: pinctrl {
compatible = "aspeed,g5-pinctrl";
- aspeed,external-nodes = <&gfx &lhc>;
-
+ aspeed,external-nodes = <&gfx>, <&lhc>;
};
p2a: p2a-control {
--
2.20.1
^ permalink raw reply related
* [PATCH 02/17] ARM: dts: aspeed-g5: Use recommended generic node name for SDMC
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
The EDAC is a sub-function of the SDRAM Memory Controller. Rename the
node to the appropriate generic node name.
Cc: Stefan M Schaeckeler <sschaeck@cisco.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g5.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 7723afc7c249..6e5b0c493f16 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -199,7 +199,7 @@
#size-cells = <1>;
ranges;
- edac: sdram at 1e6e0000 {
+ edac: memory-controller at 1e6e0000 {
compatible = "aspeed,ast2500-sdram-edac";
reg = <0x1e6e0000 0x174>;
interrupts = <0>;
--
2.20.1
^ permalink raw reply related
* [PATCH 01/17] ARM: dts: aspeed-g5: Move EDAC node to APB
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <20190726053959.2003-1-andrew@aj.id.au>
Previously the register interface was not attached to any internal bus,
which is not correct - it lives on the APB.
Cc: Stefan M Schaeckeler <sschaeck@cisco.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
arch/arm/boot/dts/aspeed-g5.dtsi | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 5b1ca265c2ce..7723afc7c249 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -47,13 +47,6 @@
reg = <0x80000000 0>;
};
- edac: sdram at 1e6e0000 {
- compatible = "aspeed,ast2500-sdram-edac";
- reg = <0x1e6e0000 0x174>;
- interrupts = <0>;
- status = "disabled";
- };
-
ahb {
compatible = "simple-bus";
#address-cells = <1>;
@@ -206,6 +199,13 @@
#size-cells = <1>;
ranges;
+ edac: sdram at 1e6e0000 {
+ compatible = "aspeed,ast2500-sdram-edac";
+ reg = <0x1e6e0000 0x174>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
syscon: syscon at 1e6e2000 {
compatible = "aspeed,ast2500-scu", "syscon", "simple-mfd";
reg = <0x1e6e2000 0x1a8>;
--
2.20.1
^ permalink raw reply related
* [RFC-ish PATCH 00/17] Clean up ASPEED devicetree warnings
From: Andrew Jeffery @ 2019-07-26 5:39 UTC (permalink / raw)
To: linux-aspeed
Hello,
The aim of this series is to minimise/eliminate all the warnings from the
ASPEED devicetrees. It mostly achieves its goal, as outlined below.
Using `aspeed_g5_defconfig` we started with the follow warning count:
$ make dtbs 2>&1 >/dev/null | wc -l
218
and after the full series is applied we have:
$ make dtbs 2>&1 >/dev/null | wc -l
2
for a 100x reduction.
Getting there though isn't without some potential controversy, which I've saved
for the last half of the series. The following patches I think are in pretty
good shape:
ARM: dts: aspeed-g5: Move EDAC node to APB
ARM: dts: aspeed-g5: Use recommended generic node name for SDMC
ARM: dts: aspeed-g5: Fix aspeed,external-nodes description
ARM: dts: vesnin: Add unit address for memory node
ARM: dts: fp5280g2: Cleanup gpio-keys-polled properties
ARM: dts: swift: Cleanup gpio-keys-polled properties
ARM: dts: witherspoon: Cleanup gpio-keys-polled properties
ARM: dts: aspeed: Cleanup lpc-ctrl and snoop regs
ARM: dts: ibm-power9-dual: Add a unit address for OCC nodes
With these patches applied we get to:
$ make dtbs 2>&1 >/dev/null | wc -l
144
So they make a dent, but fail to clean up the bulk of the issues. From here
I've mixed in some binding and driver changes with subsequent updates to the
devicetrees:
dt-bindings: pinctrl: aspeed: Add reg property as a hint
dt-bindings: misc: Document reg for aspeed,p2a-ctrl nodes
ARM: dts: aspeed: Add reg hints to syscon children
dt-bindings: ipmi: aspeed: Introduce a v2 binding for KCS
ipmi: kcs: Finish configuring ASPEED KCS device before enable
ipmi: kcs: aspeed: Implement v2 bindings
ARM: dts: aspeed-g5: Change KCS nodes to v2 binding
ARM: dts: aspeed-g5: Sort LPC child nodes by unit address
By `dt-bindings: ipmi: aspeed: Introduce a v2 binding for KCS` the warnings are
reduced to:
$ make dtbs 2>&1 >/dev/null | wc -l
125
The bang-for-buck is in fixing up the KCS bindings which removes all-but-two of
the remaining warnings (which we can't feasibly remove), but doing so forces
code changes (which I'd avoided up until this point).
Reflecting broadly on the fixes, I think I've made a mistake way back by using
syscon/simple-mfds to expose the innards of the SCU and LPC controllers in the
devicetree. This series cleans up what's currently there, but I have half a
mind to rev the SCU and LPC bindings to not use simple-mfd and instead have a
driver implementation that uses `platform_device_register_full()` or similar to
deal with the mess.
Rob - I'm looking for your thoughts here and on the series, I've never felt
entirely comfortable with what I cooked up. Your advice would be appreciated.
Anyway, everyone, please let me know your thoughts on the bits relevant to you.
If we can agree on a way forward I'll split up the series for subsequent
submissions so it isn't such a spam-fest.
Cheers,
Andrew
Andrew Jeffery (17):
ARM: dts: aspeed-g5: Move EDAC node to APB
ARM: dts: aspeed-g5: Use recommended generic node name for SDMC
ARM: dts: aspeed-g5: Fix aspeed,external-nodes description
ARM: dts: vesnin: Add unit address for memory node
ARM: dts: fp5280g2: Cleanup gpio-keys-polled properties
ARM: dts: swift: Cleanup gpio-keys-polled properties
ARM: dts: witherspoon: Cleanup gpio-keys-polled properties
ARM: dts: aspeed: Cleanup lpc-ctrl and snoop regs
ARM: dts: ibm-power9-dual: Add a unit address for OCC nodes
dt-bindings: pinctrl: aspeed: Add reg property as a hint
dt-bindings: misc: Document reg for aspeed,p2a-ctrl nodes
ARM: dts: aspeed: Add reg hints to syscon children
dt-bindings: ipmi: aspeed: Introduce a v2 binding for KCS
ipmi: kcs: Finish configuring ASPEED KCS device before enable
ipmi: kcs: aspeed: Implement v2 bindings
ARM: dts: aspeed-g5: Change KCS nodes to v2 binding
ARM: dts: aspeed-g5: Sort LPC child nodes by unit address
.../bindings/ipmi/aspeed-kcs-bmc.txt | 20 ++-
.../bindings/misc/aspeed-p2a-ctrl.txt | 1 +
.../pinctrl/aspeed,ast2400-pinctrl.yaml | 3 +
.../pinctrl/aspeed,ast2500-pinctrl.yaml | 3 +
.../dts/aspeed-bmc-arm-centriq2400-rep.dts | 4 -
.../aspeed-bmc-arm-stardragon4800-rep2.dts | 4 -
arch/arm/boot/dts/aspeed-bmc-facebook-cmm.dts | 4 -
.../arm/boot/dts/aspeed-bmc-facebook-yamp.dts | 4 -
.../boot/dts/aspeed-bmc-inspur-fp5280g2.dts | 6 -
.../arm/boot/dts/aspeed-bmc-intel-s2600wf.dts | 4 -
arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts | 4 -
arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts | 4 -
arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 6 -
arch/arm/boot/dts/aspeed-bmc-opp-vesnin.dts | 2 +-
.../boot/dts/aspeed-bmc-opp-witherspoon.dts | 6 -
arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts | 2 -
arch/arm/boot/dts/aspeed-g4.dtsi | 21 ++-
arch/arm/boot/dts/aspeed-g5.dtsi | 73 ++++----
arch/arm/boot/dts/ibm-power9-dual.dtsi | 4 +-
drivers/char/ipmi/kcs_bmc_aspeed.c | 163 ++++++++++++++----
20 files changed, 205 insertions(+), 133 deletions(-)
--
2.20.1
^ permalink raw reply
* [PATCH v2 2/2] mmc: Add support for the ASPEED SD controller
From: Andrew Jeffery @ 2019-07-26 0:52 UTC (permalink / raw)
To: linux-aspeed
In-Reply-To: <d6f7fdf2-07ed-354a-ca29-f3175623679c@intel.com>
On Thu, 25 Jul 2019, at 22:49, Adrian Hunter wrote:
> On 12/07/19 6:32 AM, Andrew Jeffery wrote:
> > Add a minimal driver for ASPEED's SD controller, which exposes two
> > SDHCIs.
> >
> > The ASPEED design implements a common register set for the SDHCIs, and
> > moves some of the standard configuration elements out to this common
> > area (e.g. 8-bit mode, and card detect configuration which is not
> > currently supported).
> >
> > The SD controller has a dedicated hardware interrupt that is shared
> > between the slots. The common register set exposes information on which
> > slot triggered the interrupt; early revisions of the patch introduced an
> > irqchip for the register, but reality is it doesn't behave as an
> > irqchip, and the result fits awkwardly into the irqchip APIs. Instead
> > I've taken the simple approach of using the IRQ as a shared IRQ with
> > some minor performance impact for the second slot.
> >
> > Ryan was the original author of the patch - I've taken his work and
> > massaged it to drop the irqchip support and rework the devicetree
> > integration. The driver has been smoke tested under qemu against a
> > minimal SD controller model and lightly tested on an ast2500-evb.
> >
> > Signed-off-by: Ryan Chen <ryanchen.aspeed@gmail.com>
> > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>
> Looks fine. Few minor comments below.
>
> > ---
> > In v2:
> >
> > * Drop unnecesasry MODULE_DEVICE_TABLE()
> > * Rename sd-controller compatible
> > * Add IBM copyright
> > * Drop unnecesary data assignment in of match table entries
> > * Derive the slot from the SDHCI offset
> >
> > drivers/mmc/host/Kconfig | 12 ++
> > drivers/mmc/host/Makefile | 1 +
> > drivers/mmc/host/sdhci-of-aspeed.c | 326 +++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/mmc/host/sdhci-of-aspeed.c
> >
> > diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> > index 931770f17087..2bb5e1264b3d 100644
> > --- a/drivers/mmc/host/Kconfig
> > +++ b/drivers/mmc/host/Kconfig
> > @@ -154,6 +154,18 @@ config MMC_SDHCI_OF_ARASAN
> >
> > If unsure, say N.
> >
> > +config MMC_SDHCI_OF_ASPEED
> > + tristate "SDHCI OF support for the ASPEED SDHCI controller"
> > + depends on MMC_SDHCI_PLTFM
> > + depends on OF
> > + help
> > + This selects the ASPEED Secure Digital Host Controller Interface.
> > +
> > + If you have a controller with this interface, say Y or M here. You
> > + also need to enable an appropriate bus interface.
> > +
> > + If unsure, say N.
> > +
> > config MMC_SDHCI_OF_AT91
> > tristate "SDHCI OF support for the Atmel SDMMC controller"
> > depends on MMC_SDHCI_PLTFM
> > diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> > index 73578718f119..390ee162fe71 100644
> > --- a/drivers/mmc/host/Makefile
> > +++ b/drivers/mmc/host/Makefile
> > @@ -84,6 +84,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
> > obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
> > obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
> > obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
> > +obj-$(CONFIG_MMC_SDHCI_OF_ASPEED) += sdhci-of-aspeed.o
> > obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
> > obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
> > obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
> > diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
> > new file mode 100644
> > index 000000000000..9528e43c257d
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-of-aspeed.c
> > @@ -0,0 +1,326 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/* Copyright (C) 2019 ASPEED Technology Inc. */
> > +/* Copyright (C) 2019 IBM Corp. */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/io.h>
> > +#include <linux/mmc/host.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +
> > +#include "sdhci-pltfm.h"
> > +
> > +#define ASPEED_SDC_INFO 0x00
> > +#define ASPEED_SDC_S1MMC8 BIT(25)
> > +#define ASPEED_SDC_S0MMC8 BIT(24)
> > +
> > +struct aspeed_sdc {
> > + struct clk *clk;
> > + struct resource *res;
> > +
> > + spinlock_t lock;
> > + void __iomem *regs;
> > +};
> > +
> > +struct aspeed_sdhci {
> > + struct aspeed_sdc *parent;
> > + u32 width_mask;
> > +};
> > +
> > +static void aspeed_sdc_bus_width(struct aspeed_sdc *sdc,
> > + struct aspeed_sdhci *sdhci, bool bus8)
>
> The function name threw me at first. I suggest:
>
> static void aspeed_sdhci_set_clr_8_bit_mode(struct aspeed_sdhci *aspeed_sdhci,
> bool bus8)
I wasn't real happy with the name I picked, so was hoping to some degree to be
bike-shedded on it. I'll fix it up.
> {
> struct aspeed_sdc *aspeed_sdc = aspeed_sdhci->parent;
>
> > +{
> > + u32 info;
> > +
> > + /* Set/clear 8 bit mode */
> > + spin_lock(&sdc->lock);
> > + info = readl(sdc->regs + ASPEED_SDC_INFO);
> > + if (bus8)
> > + info |= sdhci->width_mask;
> > + else
> > + info &= ~sdhci->width_mask;
> > + writel(info, sdc->regs + ASPEED_SDC_INFO);
> > + spin_unlock(&sdc->lock);
> > +}
> > +
> > +static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> > +{
> > + unsigned long timeout;
> > + int div;
> > + u16 clk;
> > +
> > + if (clock == host->clock)
> > + return;
> > +
> > + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> > +
> > + if (clock == 0)
> > + goto out;
> > +
> > + for (div = 1; div < 256; div *= 2) {
> > + if ((host->max_clk / div) <= clock)
> > + break;
> > + }
> > + div >>= 1;
> > +
> > + clk = div << SDHCI_DIVIDER_SHIFT;
>
> Could call sdhci_enable_clk() here.
I'll look into it.
>
> > + clk |= SDHCI_CLOCK_INT_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > + /* Wait max 20 ms */
> > + timeout = 20;
> > + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
> > + & SDHCI_CLOCK_INT_STABLE)) {
> > + if (timeout == 0) {
> > + pr_err("%s: Internal clock never stabilised.\n",
> > + mmc_hostname(host->mmc));
> > + return;
> > + }
> > + timeout--;
> > + mdelay(1);
> > + }
> > +
> > + clk |= SDHCI_CLOCK_CARD_EN;
> > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> > +
> > +out:
> > + host->clock = clock;
> > +}
> > +
> > +static void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width)
> > +{
> > + struct sdhci_pltfm_host *pltfm_priv;
> > + struct aspeed_sdhci *aspeed_sdhci;
> > + struct aspeed_sdc *aspeed_sdc;
> > + u8 ctrl;
> > +
> > + pltfm_priv = sdhci_priv(host);
> > + aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv);
> > + aspeed_sdc = aspeed_sdhci->parent;
> > +
> > + /* Set/clear 8-bit mode */
> > + aspeed_sdc_bus_width(aspeed_sdc, aspeed_sdhci,
> > + width == MMC_BUS_WIDTH_8);
> > +
> > + /* Set/clear 1 or 4 bit mode */
> > + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> > + if (width == MMC_BUS_WIDTH_4)
> > + ctrl |= SDHCI_CTRL_4BITBUS;
> > + else
> > + ctrl &= ~SDHCI_CTRL_4BITBUS;
> > + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> > +}
> > +
> > +static const struct sdhci_ops aspeed_sdhci_ops = {
> > + .set_clock = aspeed_sdhci_set_clock,
> > + .get_max_clock = sdhci_pltfm_clk_get_max_clock,
> > + .set_bus_width = aspeed_sdhci_set_bus_width,
> > + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
> > + .reset = sdhci_reset,
> > + .set_uhs_signaling = sdhci_set_uhs_signaling,
> > +};
> > +
> > +static const struct sdhci_pltfm_data aspeed_sdc_pdata = {
>
> Up to you, but this is for the aspeed_sdhci driver, so I would
> have expected it to be called aspeed_sdhci_pdata
Uh, yeah. Good catch.
>
> > + .ops = &aspeed_sdhci_ops,
> > + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
> > + .quirks2 = SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
>
> You don't use sdhci_set_clock() or sdhci_calc_clk(), so it doesn't
> look like SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN is needed.
I did look at that and was a bit suspicious about it, good to have my suspicion
confirmed.
>
> > +};
> > +
> > +static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
> > + struct resource *res)
> > +{
> > + resource_size_t delta;
> > +
> > + if (!res || resource_type(res) != IORESOURCE_MEM)
> > + return -EINVAL;
> > +
> > + if (res->start < dev->parent->res->start)
> > + return -EINVAL;
> > +
> > + delta = res->start - dev->parent->res->start;
> > + if (delta & (0x100 - 1))
> > + return -EINVAL;
> > +
> > + return (delta / 0x100) - 1;
> > +}
> > +
> > +static int aspeed_sdhci_probe(struct platform_device *pdev)
> > +{
> > + struct sdhci_pltfm_host *pltfm_host;
> > + struct aspeed_sdhci *dev;
> > + struct sdhci_host *host;
> > + struct resource *res;
> > + int slot;
> > + int ret;
> > +
> > + host = sdhci_pltfm_init(pdev, &aspeed_sdc_pdata, sizeof(*dev));
> > + if (IS_ERR(host))
> > + return PTR_ERR(host);
> > +
> > + pltfm_host = sdhci_priv(host);
> > + dev = sdhci_pltfm_priv(pltfm_host);
> > + dev->parent = dev_get_drvdata(pdev->dev.parent);
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + slot = aspeed_sdhci_calculate_slot(dev, res);
> > + if (slot < 0)
> > + return slot;
> > + dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
> > + dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
>
> That implies that you only support 2 slots which begs the question why
> you don't validate slot.
I'm not sure what you mean here, but I'll dig into it.
>
> > +
> > + sdhci_get_of_property(pdev);
> > +
> > + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
> > + if (IS_ERR(pltfm_host->clk))
> > + return PTR_ERR(pltfm_host->clk);
> > +
> > + ret = clk_prepare_enable(pltfm_host->clk);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
> > + goto err_pltfm_free;
> > + }
> > +
> > + ret = mmc_of_parse(host->mmc);
> > + if (ret)
> > + goto err_sdhci_add;
> > +
> > + ret = sdhci_add_host(host);
> > + if (ret)
> > + goto err_sdhci_add;
> > +
> > + return 0;
> > +
> > +err_sdhci_add:
> > + clk_disable_unprepare(pltfm_host->clk);
> > +err_pltfm_free:
> > + sdhci_pltfm_free(pdev);
> > + return ret;
> > +}
> > +
> > +static int aspeed_sdhci_remove(struct platform_device *pdev)
> > +{
> > + struct sdhci_pltfm_host *pltfm_host;
> > + struct sdhci_host *host;
> > + int dead;
> > +
> > + host = platform_get_drvdata(pdev);
> > + pltfm_host = sdhci_priv(host);
> > +
> > + dead = readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff;
>
> 'dead' only makes sense for PCI. Just set it to zero.
Ack.
Thanks for the feedback, I'll send a v3.
Andrew
>
> > +
> > + sdhci_remove_host(host, dead);
> > +
> > + clk_disable_unprepare(pltfm_host->clk);
> > +
> > + sdhci_pltfm_free(pdev);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id aspeed_sdhci_of_match[] = {
> > + { .compatible = "aspeed,ast2400-sdhci", },
> > + { .compatible = "aspeed,ast2500-sdhci", },
> > + { }
> > +};
> > +
> > +static struct platform_driver aspeed_sdhci_driver = {
> > + .driver = {
> > + .name = "sdhci-aspeed",
> > + .of_match_table = aspeed_sdhci_of_match,
> > + },
> > + .probe = aspeed_sdhci_probe,
> > + .remove = aspeed_sdhci_remove,
> > +};
> > +
> > +module_platform_driver(aspeed_sdhci_driver);
> > +
> > +static int aspeed_sdc_probe(struct platform_device *pdev)
> > +
> > +{
> > + struct device_node *parent, *child;
> > + struct aspeed_sdc *sdc;
> > + int ret;
> > +
> > + sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
> > + if (!sdc)
> > + return -ENOMEM;
> > +
> > + spin_lock_init(&sdc->lock);
> > +
> > + sdc->clk = devm_clk_get(&pdev->dev, NULL);
> > + if (IS_ERR(sdc->clk))
> > + return PTR_ERR(sdc->clk);
> > +
> > + ret = clk_prepare_enable(sdc->clk);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Unable to enable SDCLK\n");
> > + return ret;
> > + }
> > +
> > + sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res);
> > + if (IS_ERR(sdc->regs)) {
> > + ret = PTR_ERR(sdc->regs);
> > + goto err_clk;
> > + }
> > +
> > + dev_set_drvdata(&pdev->dev, sdc);
> > +
> > + parent = pdev->dev.of_node;
> > + for_each_available_child_of_node(parent, child) {
> > + struct platform_device *cpdev;
> > +
> > + cpdev = of_platform_device_create(child, NULL, &pdev->dev);
> > + if (IS_ERR(cpdev)) {
> > + of_node_put(child);
> > + ret = PTR_ERR(pdev);
> > + goto err_clk;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err_clk:
> > + clk_disable_unprepare(sdc->clk);
> > + return ret;
> > +}
> > +
> > +static int aspeed_sdc_remove(struct platform_device *pdev)
> > +{
> > + struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
> > +
> > + clk_disable_unprepare(sdc->clk);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id aspeed_sdc_of_match[] = {
> > + { .compatible = "aspeed,ast2400-sd-controller", },
> > + { .compatible = "aspeed,ast2500-sd-controller", },
> > + { }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
> > +
> > +static struct platform_driver aspeed_sdc_driver = {
> > + .driver = {
> > + .name = "sd-controller-aspeed",
> > + .pm = &sdhci_pltfm_pmops,
> > + .of_match_table = aspeed_sdc_of_match,
> > + },
> > + .probe = aspeed_sdc_probe,
> > + .remove = aspeed_sdc_remove,
> > +};
> > +
> > +module_platform_driver(aspeed_sdc_driver);
> > +
> > +MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
> > +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
> > +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> > +MODULE_LICENSE("GPL v2");
> >
>
>
^ permalink raw reply
* [PATCH] ARM: dts: aspeed: tiogapass: Move battery sensor
From: Vijay Khemka @ 2019-07-25 17:45 UTC (permalink / raw)
To: linux-aspeed
Moved adc7 hwmon battery sensor to correct label to be read
by single applications for all adc sensors.
Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
---
arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
index d0c823e8fce5..682f729ea25e 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
@@ -46,13 +46,9 @@
iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>,
- <&adc 4>, <&adc 5>, <&adc 6>;
+ <&adc 4>, <&adc 5>, <&adc 6>, <&adc 7>;
};
- iio-hwmon-battery {
- compatible = "iio-hwmon";
- io-channels = <&adc 7>;
- };
};
&fmc {
--
2.17.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox