123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- * Copyright (c) 2006-2024 RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2018-05-05 Bernard The first version
- * 2022-08-24 GuEe-GUI add OFW support
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <cpuport.h>
- #include <ioremap.h>
- #include <drivers/serial_dm.h>
- #define PL011_OEIM RT_BIT(10) /* overrun error interrupt mask */
- #define PL011_BEIM RT_BIT(9) /* break error interrupt mask */
- #define PL011_PEIM RT_BIT(8) /* parity error interrupt mask */
- #define PL011_FEIM RT_BIT(7) /* framing error interrupt mask */
- #define PL011_RTIM RT_BIT(6) /* receive timeout interrupt mask */
- #define PL011_TXIM RT_BIT(5) /* transmit interrupt mask */
- #define PL011_RXIM RT_BIT(4) /* receive interrupt mask */
- #define PL011_DSRMIM RT_BIT(3) /* DSR interrupt mask */
- #define PL011_DCDMIM RT_BIT(2) /* DCD interrupt mask */
- #define PL011_CTSMIM RT_BIT(1) /* CTS interrupt mask */
- #define PL011_RIMIM RT_BIT(0) /* RI interrupt mask */
- #define PL011_DR 0x000
- #define PL011_FR 0x018
- #define PL011_IBRD 0x024
- #define PL011_FBRD 0x028
- #define PL011_LCR 0x02c
- #define PL011_CR 0x030
- #define PL011_IMSC 0x038
- #define PL011_RIS 0x03c
- #define PL011_DMACR 0x048
- #define PL011_LCRH_SPS (1 << 7)
- #define PL011_LCRH_WLEN_8 (3 << 5)
- #define PL011_LCRH_WLEN_7 (2 << 5)
- #define PL011_LCRH_WLEN_6 (1 << 5)
- #define PL011_LCRH_WLEN_5 (0 << 5)
- #define PL011_LCRH_FEN (1 << 4)
- #define PL011_LCRH_STP2 (1 << 3)
- #define PL011_LCRH_EPS (1 << 2)
- #define PL011_LCRH_PEN (1 << 1)
- #define PL011_LCRH_BRK (1 << 0)
- #define PL011_LCRH_WLEN(n) ((n - 5) << 5)
- #define PL011_CR_CTSEN RT_BIT(15)
- #define PL011_CR_RTSEN RT_BIT(14)
- #define PL011_CR_RTS RT_BIT(11)
- #define PL011_CR_DTR RT_BIT(10)
- #define PL011_CR_RXE RT_BIT(9)
- #define PL011_CR_TXE RT_BIT(8)
- #define PL011_CR_LBE RT_BIT(7)
- #define PL011_CR_SIRLP RT_BIT(2)
- #define PL011_CR_SIREN RT_BIT(1)
- #define PL011_CR_UARTEN RT_BIT(0)
- struct pl011
- {
- struct rt_serial_device parent;
- int irq;
- void *base;
- rt_ubase_t freq;
- struct rt_clk *clk;
- struct rt_clk *pclk;
- struct rt_spinlock spinlock;
- };
- #define raw_to_pl011(raw) rt_container_of(raw, struct pl011, parent)
- rt_inline rt_uint32_t pl011_read(struct pl011 *pl011, int offset)
- {
- return HWREG32(pl011->base + offset);
- }
- rt_inline void pl011_write(struct pl011 *pl011, int offset, rt_uint32_t value)
- {
- HWREG32(pl011->base + offset) = value;
- }
- static void pl011_isr(int irqno, void *param)
- {
- struct pl011 *pl011 = param;
- /* Check irq */
- if (pl011_read(pl011, PL011_RIS) & PL011_RXIM)
- {
- rt_base_t level = rt_spin_lock_irqsave(&pl011->spinlock);
- rt_hw_serial_isr(&pl011->parent, RT_SERIAL_EVENT_RX_IND);
- rt_spin_unlock_irqrestore(&pl011->spinlock, level);
- }
- }
- static rt_err_t pl011_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
- {
- rt_ubase_t quot;
- struct pl011 *pl011 = raw_to_pl011(serial);
- /* Clear UART setting */
- pl011_write(pl011, PL011_CR, 0);
- /* Disable FIFO */
- pl011_write(pl011, PL011_LCR, 0);
- if (cfg->baud_rate > pl011->freq / 16)
- {
- quot = RT_DIV_ROUND_CLOSEST(pl011->freq * 8, cfg->baud_rate);
- }
- else
- {
- quot = RT_DIV_ROUND_CLOSEST(pl011->freq * 4, cfg->baud_rate);
- }
- pl011_write(pl011, PL011_IBRD, quot >> 6);
- pl011_write(pl011, PL011_FBRD, quot & 0x3f);
- /* FIFO */
- pl011_write(pl011, PL011_LCR, PL011_LCRH_WLEN(cfg->data_bits));
- /* Art enable, TX/RX enable */
- pl011_write(pl011, PL011_CR, PL011_CR_UARTEN | PL011_CR_TXE | PL011_CR_RXE);
- return RT_EOK;
- }
- static rt_err_t pl011_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
- {
- struct pl011 *pl011 = raw_to_pl011(serial);
- switch (cmd)
- {
- case RT_DEVICE_CTRL_CLR_INT:
- /* Disable rx irq */
- pl011_write(pl011, PL011_IMSC, pl011_read(pl011, PL011_IMSC) & ~PL011_RXIM);
- rt_hw_interrupt_mask(pl011->irq);
- break;
- case RT_DEVICE_CTRL_SET_INT:
- /* Enable rx irq */
- pl011_write(pl011, PL011_IMSC, pl011_read(pl011, PL011_IMSC) | PL011_RXIM);
- rt_hw_interrupt_umask(pl011->irq);
- break;
- }
- return RT_EOK;
- }
- static int pl011_uart_putc(struct rt_serial_device *serial, char c)
- {
- struct pl011 *pl011 = raw_to_pl011(serial);
- while (pl011_read(pl011, PL011_FR) & PL011_TXIM)
- {
- rt_hw_cpu_relax();
- }
- pl011_write(pl011, PL011_DR, c);
- return 1;
- }
- static int pl011_uart_getc(struct rt_serial_device *serial)
- {
- int ch = -1;
- struct pl011 *pl011 = raw_to_pl011(serial);
- if (!(pl011_read(pl011, PL011_FR) & PL011_RXIM))
- {
- ch = pl011_read(pl011, PL011_DR);
- }
- return ch;
- }
- static const struct rt_uart_ops pl011_uart_ops =
- {
- .configure = pl011_uart_configure,
- .control = pl011_uart_control,
- .putc = pl011_uart_putc,
- .getc = pl011_uart_getc,
- };
- static void pl011_early_kick(struct rt_fdt_earlycon *con, int why)
- {
- struct pl011 *pl011 = raw_to_pl011(con->data);
- switch (why)
- {
- case FDT_EARLYCON_KICK_UPDATE:
- pl011->base = rt_ioremap((void *)con->mmio, con->size);
- break;
- case FDT_EARLYCON_KICK_COMPLETED:
- rt_iounmap(pl011->base);
- break;
- default:
- break;
- }
- }
- static rt_err_t pl011_early_setup(struct rt_fdt_earlycon *con, const char *options)
- {
- rt_err_t err = RT_EOK;
- static struct pl011 pl011 = { };
- if (options && !con->mmio)
- {
- char *arg;
- con->mmio = RT_NULL;
- /*
- * The pl011 serial port must already be setup and configured in early.
- * Options are not yet supported.
- * pl011,<addr>
- * pl011,mmio32,<addr>
- */
- serial_for_each_args(arg, options)
- {
- if (!rt_strcmp(arg, "pl011") || !rt_strcmp(arg, "mmio32"))
- {
- continue;
- }
- if (!con->mmio)
- {
- con->mmio = (rt_ubase_t)serial_base_from_args(arg);
- break;
- }
- }
- }
- if (!con->size)
- {
- con->size = 0x1000;
- }
- if (con->mmio)
- {
- pl011.base = rt_ioremap_early((void *)con->mmio, con->size);
- }
- if (pl011.base)
- {
- con->console_putc = (typeof(con->console_putc))&pl011_uart_putc;
- con->console_kick = pl011_early_kick;
- con->data = &pl011.parent;
- pl011.parent.config = (typeof(pl011.parent.config))RT_SERIAL_CONFIG_DEFAULT;
- }
- else
- {
- err = -RT_ERROR;
- }
- return err;
- }
- RT_FDT_EARLYCON_EXPORT(pl011, "pl011", "arm,pl011", pl011_early_setup);
- static void pl011_free(struct pl011 *pl011)
- {
- if (pl011->base)
- {
- rt_iounmap(pl011->base);
- }
- /* if (!rt_is_err_or_null(pl011->clk))*/
- /* {*/
- /* rt_clk_disable(pl011->clk);*/
- /* rt_clk_put(pl011->clk);*/
- /* }*/
- /* if (!rt_is_err_or_null(pl011->pclk))*/
- /* {*/
- /* rt_clk_disable_unprepare(pl011->pclk);*/
- /* rt_clk_put(pl011->pclk);*/
- /* }*/
- rt_free(pl011);
- }
- static rt_err_t pl011_probe(struct rt_platform_device *pdev)
- {
- rt_err_t err;
- const char *name;
- char isr_name[RT_NAME_MAX];
- struct rt_device *dev = &pdev->parent;
- struct pl011 *pl011 = rt_calloc(1, sizeof(*pl011));
- struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
- if (!pl011)
- {
- return -RT_ENOMEM;
- }
- pl011->base = rt_dm_dev_iomap(dev, 0);
- if (!pl011->base)
- {
- err = -RT_EIO;
- goto _fail;
- }
- pl011->irq = rt_dm_dev_get_irq(dev, 0);
- if (pl011->irq < 0)
- {
- err = pl011->irq;
- goto _fail;
- }
- /* pl011->clk = rt_clk_get_by_index(dev, 0);*/
- /* if (rt_is_err(pl011->clk))*/
- /* {*/
- /* err = rt_ptr_err(pl011->clk);*/
- /* goto _fail;*/
- /* }*/
- /* pl011->pclk = rt_clk_get_by_name(dev, "apb_pclk");*/
- /* if (rt_is_err(pl011->pclk))*/
- /* {*/
- /* err = rt_ptr_err(pl011->pclk);*/
- /* goto _fail;*/
- /* }*/
- /* if ((err = rt_clk_prepare_enable(pl011->pclk)))*/
- /* {*/
- /* goto _fail;*/
- /* }*/
- rt_dm_dev_bind_fwdata(&pl011->parent.parent, dev->ofw_node, &pl011->parent);
- /* rt_clk_enable(pl011->clk);*/
- /* pl011->freq = rt_clk_get_rate(pl011->clk);*/
- dev->user_data = pl011;
- pl011->parent.ops = &pl011_uart_ops;
- pl011->parent.config = config;
- rt_spin_lock_init(&pl011->spinlock);
- serial_dev_set_name(&pl011->parent);
- name = rt_dm_dev_get_name(&pl011->parent.parent);
- rt_hw_serial_register(&pl011->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, pl011);
- rt_snprintf(isr_name, RT_NAME_MAX, "%s-pl011", name);
- rt_hw_interrupt_install(pl011->irq, pl011_isr, pl011, isr_name);
- return RT_EOK;
- _fail:
- pl011_free(pl011);
- return err;
- }
- static rt_err_t pl011_remove(struct rt_platform_device *pdev)
- {
- struct rt_device *dev = &pdev->parent;
- struct pl011 *pl011 = dev->user_data;
- rt_dm_dev_unbind_fwdata(dev, RT_NULL);
- rt_hw_interrupt_mask(pl011->irq);
- rt_pic_detach_irq(pl011->irq, pl011);
- rt_device_unregister(&pl011->parent.parent);
- pl011_free(pl011);
- return RT_EOK;
- }
- static const struct rt_ofw_node_id pl011_ofw_ids[] =
- {
- { .type = "ttyAMA", .compatible = "arm,pl011" },
- { .type = "ttyAMA", .compatible = "arm,pl011-axi" },
- { /* sentinel */ }
- };
- static struct rt_platform_driver pl011_driver =
- {
- .name = "serial-pl011",
- .ids = pl011_ofw_ids,
- .probe = pl011_probe,
- .remove = pl011_remove,
- };
- static int pl011_drv_register(void)
- {
- rt_platform_driver_register(&pl011_driver);
- return 0;
- }
- INIT_DRIVER_EARLY_EXPORT(pl011_drv_register);
|