/* * Copyright (c) 2016 Intel Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "qm_uart.h" #include "qm_scss.h" #include "qm_soc_regs.h" #define IIR_IID_NO_INTERRUPT_PENDING 0x01 #define IIR_IID_RECV_DATA_AVAIL 0x04 #define IIR_IID_CHAR_TIMEOUT 0x0C #define IER_ELSI 0x04 #define DIVISOR_LOW(baudrate) \ ((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / (16 * baudrate)) & 0xFF) #define DIVISOR_HIGH(baudrate) \ (((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / (16 * baudrate)) & 0xFF00) >> 8) /* Convenient macro to get the controller instance. */ #define GET_CONTROLLER_INSTANCE(dev) \ (((struct uart_qmsi_config_info *)dev->config->config_info)->instance) struct uart_qmsi_config_info { qm_uart_t instance; clk_periph_t clock_gate; uint32_t baud_divisor; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_config_func_t irq_config_func; #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; struct uart_qmsi_drv_data { uart_irq_callback_t user_cb; uint8_t iir_cache; }; static int uart_qmsi_init(struct device *dev); #ifdef CONFIG_UART_QMSI_0 #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void irq_config_func_0(struct device *dev); #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static struct uart_qmsi_config_info config_info_0 = { .instance = QM_UART_0, .clock_gate = CLK_PERIPH_UARTA_REGISTER | CLK_PERIPH_CLK, .baud_divisor = QM_UART_CFG_BAUD_DL_PACK( DIVISOR_HIGH(CONFIG_UART_QMSI_0_BAUDRATE), DIVISOR_LOW(CONFIG_UART_QMSI_0_BAUDRATE), 0), #ifdef CONFIG_UART_INTERRUPT_DRIVEN .irq_config_func = irq_config_func_0, #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; static struct uart_qmsi_drv_data drv_data_0; DEVICE_INIT(uart_0, CONFIG_UART_QMSI_0_NAME, uart_qmsi_init, &drv_data_0, &config_info_0, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); #endif /* CONFIG_UART_QMSI_0 */ #ifdef CONFIG_UART_QMSI_1 #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void irq_config_func_1(struct device *dev); #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ static struct uart_qmsi_config_info config_info_1 = { .instance = QM_UART_1, .clock_gate = CLK_PERIPH_UARTB_REGISTER | CLK_PERIPH_CLK, .baud_divisor = QM_UART_CFG_BAUD_DL_PACK( DIVISOR_HIGH(CONFIG_UART_QMSI_1_BAUDRATE), DIVISOR_LOW(CONFIG_UART_QMSI_1_BAUDRATE), 0), #ifdef CONFIG_UART_INTERRUPT_DRIVEN .irq_config_func = irq_config_func_1, #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; static struct uart_qmsi_drv_data drv_data_1; DEVICE_INIT(uart_1, CONFIG_UART_QMSI_1_NAME, uart_qmsi_init, &drv_data_1, &config_info_1, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); #endif /* CONFIG_UART_QMSI_1 */ static int uart_qmsi_poll_in(struct device *dev, unsigned char *data) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); qm_uart_status_t status = qm_uart_get_status(instance); /* In order to check if there is any data to read from UART * controller we should check if the QM_UART_RX_BUSY bit from * 'status' is not set. This bit is set only if there is any * pending character to read. */ if (!(status & QM_UART_RX_BUSY)) return -1; qm_uart_read(instance, data); return 0; } static unsigned char uart_qmsi_poll_out(struct device *dev, unsigned char data) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); qm_uart_write(instance, data); return data; } static int uart_qmsi_err_check(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); /* QMSI and Zephyr use the same bits to represent UART errors * so we don't need to translate each error bit from QMSI API * to Zephyr API. */ return qm_uart_get_status(instance) & QM_UART_LSR_ERROR_BITS; } #if CONFIG_UART_INTERRUPT_DRIVEN static bool is_tx_fifo_full(qm_uart_t instance) { return !!(QM_UART[instance].lsr & QM_UART_LSR_THRE); } static int uart_qmsi_fifo_fill(struct device *dev, const uint8_t *tx_data, int size) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); int i; for (i = 0; i < size && !is_tx_fifo_full(instance); i++) { QM_UART[instance].rbr_thr_dll = tx_data[i]; } return i; } static bool is_data_ready(qm_uart_t instance) { return QM_UART[instance].lsr & QM_UART_LSR_DR; } static int uart_qmsi_fifo_read(struct device *dev, uint8_t *rx_data, const int size) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); int i; for (i = 0; i < size && is_data_ready(instance); i++) { rx_data[i] = QM_UART[instance].rbr_thr_dll; } return i; } static void uart_qmsi_irq_tx_enable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh |= QM_UART_IER_ETBEI; } static void uart_qmsi_irq_tx_disable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh &= ~QM_UART_IER_ETBEI; } static int uart_qmsi_irq_tx_ready(struct device *dev) { struct uart_qmsi_drv_data *drv_data = dev->driver_data; uint32_t id = (drv_data->iir_cache & QM_UART_IIR_IID_MASK); return id == QM_UART_IIR_THR_EMPTY; } static int uart_qmsi_irq_tx_empty(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); const uint32_t mask = (QM_UART_LSR_TEMT | QM_UART_LSR_THRE); return (QM_UART[instance].lsr & mask) == mask; } static void uart_qmsi_irq_rx_enable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh |= QM_UART_IER_ERBFI; } static void uart_qmsi_irq_rx_disable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh &= ~QM_UART_IER_ERBFI; } static int uart_qmsi_irq_rx_ready(struct device *dev) { struct uart_qmsi_drv_data *drv_data = dev->driver_data; uint32_t id = (drv_data->iir_cache & QM_UART_IIR_IID_MASK); return (id == IIR_IID_RECV_DATA_AVAIL) || (id == IIR_IID_CHAR_TIMEOUT); } static void uart_qmsi_irq_err_enable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh |= IER_ELSI; } static void uart_qmsi_irq_err_disable(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); QM_UART[instance].ier_dlh &= ~IER_ELSI; } static int uart_qmsi_irq_is_pending(struct device *dev) { struct uart_qmsi_drv_data *drv_data = dev->driver_data; uint32_t id = (drv_data->iir_cache & QM_UART_IIR_IID_MASK); return !(id == IIR_IID_NO_INTERRUPT_PENDING); } static int uart_qmsi_irq_update(struct device *dev) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); struct uart_qmsi_drv_data *drv_data = dev->driver_data; drv_data->iir_cache = QM_UART[instance].iir_fcr; return 1; } static void uart_qmsi_irq_callback_set(struct device *dev, uart_irq_callback_t cb) { struct uart_qmsi_drv_data *drv_data = dev->driver_data; drv_data->user_cb = cb; } static void uart_qmsi_isr(void *arg) { struct device *dev = arg; struct uart_qmsi_drv_data *drv_data = dev->driver_data; if (drv_data->user_cb) drv_data->user_cb(dev); } #ifdef CONFIG_UART_QMSI_0 static void irq_config_func_0(struct device *dev) { IRQ_CONNECT(CONFIG_UART_QMSI_0_IRQ, CONFIG_UART_QMSI_0_IRQ_PRI, uart_qmsi_isr, DEVICE_GET(uart_0), (IOAPIC_LEVEL | IOAPIC_HIGH)); irq_enable(CONFIG_UART_QMSI_0_IRQ); QM_SCSS_INT->int_uart_0_mask &= ~BIT(0); } #endif /* CONFIG_UART_QMSI_0 */ #ifdef CONFIG_UART_QMSI_1 static void irq_config_func_1(struct device *dev) { IRQ_CONNECT(CONFIG_UART_QMSI_1_IRQ, CONFIG_UART_QMSI_1_IRQ_PRI, uart_qmsi_isr, DEVICE_GET(uart_1), (IOAPIC_LEVEL | IOAPIC_HIGH)); irq_enable(CONFIG_UART_QMSI_1_IRQ); QM_SCSS_INT->int_uart_1_mask &= ~BIT(0); } #endif /* CONFIG_UART_QMSI_1 */ #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ #ifdef CONFIG_UART_LINE_CTRL static int uart_qmsi_line_ctrl_set(struct device *dev, uint32_t ctrl, uint32_t val) { qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev); qm_uart_config_t cfg; switch (ctrl) { case LINE_CTRL_BAUD_RATE: qm_uart_get_config(instance, &cfg); cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(DIVISOR_HIGH(val), DIVISOR_LOW(val), 0); qm_uart_set_config(instance, &cfg); break; default: return -ENODEV; } return 0; } #endif /* CONFIG_UART_LINE_CTRL */ #ifdef CONFIG_UART_DRV_CMD static int uart_qmsi_drv_cmd(struct device *dev, uint32_t cmd, uint32_t p) { return -ENODEV; } #endif /* CONFIG_UART_DRV_CMD */ static struct uart_driver_api api = { .poll_in = uart_qmsi_poll_in, .poll_out = uart_qmsi_poll_out, .err_check = uart_qmsi_err_check, #ifdef CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_qmsi_fifo_fill, .fifo_read = uart_qmsi_fifo_read, .irq_tx_enable = uart_qmsi_irq_tx_enable, .irq_tx_disable = uart_qmsi_irq_tx_disable, .irq_tx_ready = uart_qmsi_irq_tx_ready, .irq_tx_empty = uart_qmsi_irq_tx_empty, .irq_rx_enable = uart_qmsi_irq_rx_enable, .irq_rx_disable = uart_qmsi_irq_rx_disable, .irq_rx_ready = uart_qmsi_irq_rx_ready, .irq_err_enable = uart_qmsi_irq_err_enable, .irq_err_disable = uart_qmsi_irq_err_disable, .irq_is_pending = uart_qmsi_irq_is_pending, .irq_update = uart_qmsi_irq_update, .irq_callback_set = uart_qmsi_irq_callback_set, #endif /* CONFIG_UART_INTERRUPT_DRIVE */ #ifdef CONFIG_UART_LINE_CTRL .line_ctrl_set = uart_qmsi_line_ctrl_set, #endif /* CONFIG_UART_LINE_CTR */ #ifdef CONFIG_UART_DRV_CMD .drv_cmd = uart_qmsi_drv_cmd, #endif /* CONFIG_UART_DRV_CMD */ }; static int uart_qmsi_init(struct device *dev) { struct uart_qmsi_config_info *config = dev->config->config_info; qm_uart_config_t cfg; cfg.line_control = QM_UART_LC_8N1; cfg.baud_divisor = config->baud_divisor; cfg.hw_fc = false; cfg.int_en = false; clk_periph_enable(config->clock_gate); qm_uart_set_config(config->instance, &cfg); #ifdef CONFIG_UART_INTERRUPT_DRIVEN config->irq_config_func(dev); #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ dev->driver_api = &api; return 0; }