xref: /qemu/hw/char/omap_uart.c (revision 490a9d9b36bfa22a6b46b27e6e9e84e1bac44c68)
102d74341Scmchao /*
202d74341Scmchao  * TI OMAP processors UART emulation.
302d74341Scmchao  *
402d74341Scmchao  * Copyright (C) 2006-2008 Andrzej Zaborowski  <balrog@zabor.org>
502d74341Scmchao  * Copyright (C) 2007-2009 Nokia Corporation
602d74341Scmchao  *
702d74341Scmchao  * This program is free software; you can redistribute it and/or
802d74341Scmchao  * modify it under the terms of the GNU General Public License as
902d74341Scmchao  * published by the Free Software Foundation; either version 2 or
1002d74341Scmchao  * (at your option) version 3 of the License.
1102d74341Scmchao  *
1202d74341Scmchao  * This program is distributed in the hope that it will be useful,
1302d74341Scmchao  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1402d74341Scmchao  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1502d74341Scmchao  * GNU General Public License for more details.
1602d74341Scmchao  *
1702d74341Scmchao  * You should have received a copy of the GNU General Public License along
1802d74341Scmchao  * with this program; if not, see <http://www.gnu.org/licenses/>.
1902d74341Scmchao  */
2017b7f2dbSPeter Maydell #include "qemu/osdep.h"
218228e353SMarc-André Lureau #include "chardev/char.h"
220d09e41aSPaolo Bonzini #include "hw/arm/omap.h"
230d09e41aSPaolo Bonzini #include "hw/char/serial.h"
24022c62cbSPaolo Bonzini #include "exec/address-spaces.h"
2502d74341Scmchao 
2602d74341Scmchao /* UARTs */
2702d74341Scmchao struct omap_uart_s {
28aee39503SAvi Kivity     MemoryRegion iomem;
29a8170e5eSAvi Kivity     hwaddr base;
30*490a9d9bSMarc-André Lureau     SerialMM *serial; /* TODO */
3102d74341Scmchao     struct omap_target_agent_s *ta;
3202d74341Scmchao     omap_clk fclk;
3302d74341Scmchao     qemu_irq irq;
3402d74341Scmchao 
3502d74341Scmchao     uint8_t eblr;
3602d74341Scmchao     uint8_t syscontrol;
3702d74341Scmchao     uint8_t wkup;
3802d74341Scmchao     uint8_t cfps;
3902d74341Scmchao     uint8_t mdr[2];
4002d74341Scmchao     uint8_t scr;
4102d74341Scmchao     uint8_t clksel;
4202d74341Scmchao };
4302d74341Scmchao 
4402d74341Scmchao void omap_uart_reset(struct omap_uart_s *s)
4502d74341Scmchao {
4602d74341Scmchao     s->eblr = 0x00;
4702d74341Scmchao     s->syscontrol = 0;
4802d74341Scmchao     s->wkup = 0x3f;
4902d74341Scmchao     s->cfps = 0x69;
5002d74341Scmchao     s->clksel = 0;
5102d74341Scmchao }
5202d74341Scmchao 
53a8170e5eSAvi Kivity struct omap_uart_s *omap_uart_init(hwaddr base,
5402d74341Scmchao                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
556a8aabd3SStefan Weil                 qemu_irq txdma, qemu_irq rxdma,
560ec7b3e7SMarc-André Lureau                 const char *label, Chardev *chr)
5702d74341Scmchao {
58b45c03f5SMarkus Armbruster     struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
5902d74341Scmchao 
6002d74341Scmchao     s->base = base;
6102d74341Scmchao     s->fclk = fclk;
6202d74341Scmchao     s->irq = irq;
6339186d8aSRichard Henderson     s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
6439186d8aSRichard Henderson                                omap_clk_getrate(fclk)/16,
654ad6f6cbSPaolo Bonzini                                chr ?: qemu_chr_new(label, "null", NULL),
66fb50cfe4SRichard Henderson                                DEVICE_NATIVE_ENDIAN);
6702d74341Scmchao     return s;
6802d74341Scmchao }
6902d74341Scmchao 
70a8170e5eSAvi Kivity static uint64_t omap_uart_read(void *opaque, hwaddr addr,
71aee39503SAvi Kivity                                unsigned size)
7202d74341Scmchao {
7302d74341Scmchao     struct omap_uart_s *s = (struct omap_uart_s *) opaque;
7402d74341Scmchao 
75aee39503SAvi Kivity     if (size == 4) {
76aee39503SAvi Kivity         return omap_badwidth_read8(opaque, addr);
77aee39503SAvi Kivity     }
78aee39503SAvi Kivity 
7902d74341Scmchao     switch (addr) {
8002d74341Scmchao     case 0x20:	/* MDR1 */
8102d74341Scmchao         return s->mdr[0];
8202d74341Scmchao     case 0x24:	/* MDR2 */
8302d74341Scmchao         return s->mdr[1];
8402d74341Scmchao     case 0x40:	/* SCR */
8502d74341Scmchao         return s->scr;
8602d74341Scmchao     case 0x44:	/* SSR */
8702d74341Scmchao         return 0x0;
8802d74341Scmchao     case 0x48:	/* EBLR (OMAP2) */
8902d74341Scmchao         return s->eblr;
9002d74341Scmchao     case 0x4C:	/* OSC_12M_SEL (OMAP1) */
9102d74341Scmchao         return s->clksel;
9202d74341Scmchao     case 0x50:	/* MVR */
9302d74341Scmchao         return 0x30;
9402d74341Scmchao     case 0x54:	/* SYSC (OMAP2) */
9502d74341Scmchao         return s->syscontrol;
9602d74341Scmchao     case 0x58:	/* SYSS (OMAP2) */
9702d74341Scmchao         return 1;
9802d74341Scmchao     case 0x5c:	/* WER (OMAP2) */
9902d74341Scmchao         return s->wkup;
10002d74341Scmchao     case 0x60:	/* CFPS (OMAP2) */
10102d74341Scmchao         return s->cfps;
10202d74341Scmchao     }
10302d74341Scmchao 
10402d74341Scmchao     OMAP_BAD_REG(addr);
10502d74341Scmchao     return 0;
10602d74341Scmchao }
10702d74341Scmchao 
108a8170e5eSAvi Kivity static void omap_uart_write(void *opaque, hwaddr addr,
109aee39503SAvi Kivity                             uint64_t value, unsigned size)
11002d74341Scmchao {
11102d74341Scmchao     struct omap_uart_s *s = (struct omap_uart_s *) opaque;
11202d74341Scmchao 
113aee39503SAvi Kivity     if (size == 4) {
11477a8257eSStefan Weil         omap_badwidth_write8(opaque, addr, value);
11577a8257eSStefan Weil         return;
116aee39503SAvi Kivity     }
117aee39503SAvi Kivity 
11802d74341Scmchao     switch (addr) {
11902d74341Scmchao     case 0x20:	/* MDR1 */
12002d74341Scmchao         s->mdr[0] = value & 0x7f;
12102d74341Scmchao         break;
12202d74341Scmchao     case 0x24:	/* MDR2 */
12302d74341Scmchao         s->mdr[1] = value & 0xff;
12402d74341Scmchao         break;
12502d74341Scmchao     case 0x40:	/* SCR */
12602d74341Scmchao         s->scr = value & 0xff;
12702d74341Scmchao         break;
12802d74341Scmchao     case 0x48:	/* EBLR (OMAP2) */
12902d74341Scmchao         s->eblr = value & 0xff;
13002d74341Scmchao         break;
13102d74341Scmchao     case 0x4C:	/* OSC_12M_SEL (OMAP1) */
13202d74341Scmchao         s->clksel = value & 1;
13302d74341Scmchao         break;
13402d74341Scmchao     case 0x44:	/* SSR */
13502d74341Scmchao     case 0x50:	/* MVR */
13602d74341Scmchao     case 0x58:	/* SYSS (OMAP2) */
13702d74341Scmchao         OMAP_RO_REG(addr);
13802d74341Scmchao         break;
13902d74341Scmchao     case 0x54:	/* SYSC (OMAP2) */
14002d74341Scmchao         s->syscontrol = value & 0x1d;
14102d74341Scmchao         if (value & 2)
14202d74341Scmchao             omap_uart_reset(s);
14302d74341Scmchao         break;
14402d74341Scmchao     case 0x5c:	/* WER (OMAP2) */
14502d74341Scmchao         s->wkup = value & 0x7f;
14602d74341Scmchao         break;
14702d74341Scmchao     case 0x60:	/* CFPS (OMAP2) */
14802d74341Scmchao         s->cfps = value & 0xff;
14902d74341Scmchao         break;
15002d74341Scmchao     default:
15102d74341Scmchao         OMAP_BAD_REG(addr);
15202d74341Scmchao     }
15302d74341Scmchao }
15402d74341Scmchao 
155aee39503SAvi Kivity static const MemoryRegionOps omap_uart_ops = {
156aee39503SAvi Kivity     .read = omap_uart_read,
157aee39503SAvi Kivity     .write = omap_uart_write,
158aee39503SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
15902d74341Scmchao };
16002d74341Scmchao 
161aee39503SAvi Kivity struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
162aee39503SAvi Kivity                 struct omap_target_agent_s *ta,
16302d74341Scmchao                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
1646a8aabd3SStefan Weil                 qemu_irq txdma, qemu_irq rxdma,
1650ec7b3e7SMarc-André Lureau                 const char *label, Chardev *chr)
16602d74341Scmchao {
167a8170e5eSAvi Kivity     hwaddr base = omap_l4_attach(ta, 0, NULL);
16802d74341Scmchao     struct omap_uart_s *s = omap_uart_init(base, irq,
1696a8aabd3SStefan Weil                     fclk, iclk, txdma, rxdma, label, chr);
170aee39503SAvi Kivity 
1712c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100);
17202d74341Scmchao 
17302d74341Scmchao     s->ta = ta;
17402d74341Scmchao 
175aee39503SAvi Kivity     memory_region_add_subregion(sysmem, base + 0x20, &s->iomem);
17602d74341Scmchao 
17702d74341Scmchao     return s;
17802d74341Scmchao }
17902d74341Scmchao 
1800ec7b3e7SMarc-André Lureau void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
18102d74341Scmchao {
18202d74341Scmchao     /* TODO: Should reuse or destroy current s->serial */
18339186d8aSRichard Henderson     s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
18402d74341Scmchao                                omap_clk_getrate(s->fclk) / 16,
1854ad6f6cbSPaolo Bonzini                                chr ?: qemu_chr_new("null", "null", NULL),
186fb50cfe4SRichard Henderson                                DEVICE_NATIVE_ENDIAN);
18702d74341Scmchao }
188