xref: /qemu/hw/char/omap_uart.c (revision 0ec7b3e7f21caf6bfde404528928d600b661ea8d)
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"
21dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
2283c9f4caSPaolo Bonzini #include "hw/hw.h"
230d09e41aSPaolo Bonzini #include "hw/arm/omap.h"
240d09e41aSPaolo Bonzini #include "hw/char/serial.h"
25022c62cbSPaolo Bonzini #include "exec/address-spaces.h"
2602d74341Scmchao 
2702d74341Scmchao /* UARTs */
2802d74341Scmchao struct omap_uart_s {
29aee39503SAvi Kivity     MemoryRegion iomem;
30a8170e5eSAvi Kivity     hwaddr base;
3102d74341Scmchao     SerialState *serial; /* TODO */
3202d74341Scmchao     struct omap_target_agent_s *ta;
3302d74341Scmchao     omap_clk fclk;
3402d74341Scmchao     qemu_irq irq;
3502d74341Scmchao 
3602d74341Scmchao     uint8_t eblr;
3702d74341Scmchao     uint8_t syscontrol;
3802d74341Scmchao     uint8_t wkup;
3902d74341Scmchao     uint8_t cfps;
4002d74341Scmchao     uint8_t mdr[2];
4102d74341Scmchao     uint8_t scr;
4202d74341Scmchao     uint8_t clksel;
4302d74341Scmchao };
4402d74341Scmchao 
4502d74341Scmchao void omap_uart_reset(struct omap_uart_s *s)
4602d74341Scmchao {
4702d74341Scmchao     s->eblr = 0x00;
4802d74341Scmchao     s->syscontrol = 0;
4902d74341Scmchao     s->wkup = 0x3f;
5002d74341Scmchao     s->cfps = 0x69;
5102d74341Scmchao     s->clksel = 0;
5202d74341Scmchao }
5302d74341Scmchao 
54a8170e5eSAvi Kivity struct omap_uart_s *omap_uart_init(hwaddr base,
5502d74341Scmchao                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
566a8aabd3SStefan Weil                 qemu_irq txdma, qemu_irq rxdma,
57*0ec7b3e7SMarc-André Lureau                 const char *label, Chardev *chr)
5802d74341Scmchao {
59b45c03f5SMarkus Armbruster     struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
6002d74341Scmchao 
6102d74341Scmchao     s->base = base;
6202d74341Scmchao     s->fclk = fclk;
6302d74341Scmchao     s->irq = irq;
6439186d8aSRichard Henderson     s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
6539186d8aSRichard Henderson                                omap_clk_getrate(fclk)/16,
66b4948be9SMarc-André Lureau                                chr ?: qemu_chr_new(label, "null"),
67fb50cfe4SRichard Henderson                                DEVICE_NATIVE_ENDIAN);
6802d74341Scmchao     return s;
6902d74341Scmchao }
7002d74341Scmchao 
71a8170e5eSAvi Kivity static uint64_t omap_uart_read(void *opaque, hwaddr addr,
72aee39503SAvi Kivity                                unsigned size)
7302d74341Scmchao {
7402d74341Scmchao     struct omap_uart_s *s = (struct omap_uart_s *) opaque;
7502d74341Scmchao 
76aee39503SAvi Kivity     if (size == 4) {
77aee39503SAvi Kivity         return omap_badwidth_read8(opaque, addr);
78aee39503SAvi Kivity     }
79aee39503SAvi Kivity 
8002d74341Scmchao     switch (addr) {
8102d74341Scmchao     case 0x20:	/* MDR1 */
8202d74341Scmchao         return s->mdr[0];
8302d74341Scmchao     case 0x24:	/* MDR2 */
8402d74341Scmchao         return s->mdr[1];
8502d74341Scmchao     case 0x40:	/* SCR */
8602d74341Scmchao         return s->scr;
8702d74341Scmchao     case 0x44:	/* SSR */
8802d74341Scmchao         return 0x0;
8902d74341Scmchao     case 0x48:	/* EBLR (OMAP2) */
9002d74341Scmchao         return s->eblr;
9102d74341Scmchao     case 0x4C:	/* OSC_12M_SEL (OMAP1) */
9202d74341Scmchao         return s->clksel;
9302d74341Scmchao     case 0x50:	/* MVR */
9402d74341Scmchao         return 0x30;
9502d74341Scmchao     case 0x54:	/* SYSC (OMAP2) */
9602d74341Scmchao         return s->syscontrol;
9702d74341Scmchao     case 0x58:	/* SYSS (OMAP2) */
9802d74341Scmchao         return 1;
9902d74341Scmchao     case 0x5c:	/* WER (OMAP2) */
10002d74341Scmchao         return s->wkup;
10102d74341Scmchao     case 0x60:	/* CFPS (OMAP2) */
10202d74341Scmchao         return s->cfps;
10302d74341Scmchao     }
10402d74341Scmchao 
10502d74341Scmchao     OMAP_BAD_REG(addr);
10602d74341Scmchao     return 0;
10702d74341Scmchao }
10802d74341Scmchao 
109a8170e5eSAvi Kivity static void omap_uart_write(void *opaque, hwaddr addr,
110aee39503SAvi Kivity                             uint64_t value, unsigned size)
11102d74341Scmchao {
11202d74341Scmchao     struct omap_uart_s *s = (struct omap_uart_s *) opaque;
11302d74341Scmchao 
114aee39503SAvi Kivity     if (size == 4) {
11577a8257eSStefan Weil         omap_badwidth_write8(opaque, addr, value);
11677a8257eSStefan Weil         return;
117aee39503SAvi Kivity     }
118aee39503SAvi Kivity 
11902d74341Scmchao     switch (addr) {
12002d74341Scmchao     case 0x20:	/* MDR1 */
12102d74341Scmchao         s->mdr[0] = value & 0x7f;
12202d74341Scmchao         break;
12302d74341Scmchao     case 0x24:	/* MDR2 */
12402d74341Scmchao         s->mdr[1] = value & 0xff;
12502d74341Scmchao         break;
12602d74341Scmchao     case 0x40:	/* SCR */
12702d74341Scmchao         s->scr = value & 0xff;
12802d74341Scmchao         break;
12902d74341Scmchao     case 0x48:	/* EBLR (OMAP2) */
13002d74341Scmchao         s->eblr = value & 0xff;
13102d74341Scmchao         break;
13202d74341Scmchao     case 0x4C:	/* OSC_12M_SEL (OMAP1) */
13302d74341Scmchao         s->clksel = value & 1;
13402d74341Scmchao         break;
13502d74341Scmchao     case 0x44:	/* SSR */
13602d74341Scmchao     case 0x50:	/* MVR */
13702d74341Scmchao     case 0x58:	/* SYSS (OMAP2) */
13802d74341Scmchao         OMAP_RO_REG(addr);
13902d74341Scmchao         break;
14002d74341Scmchao     case 0x54:	/* SYSC (OMAP2) */
14102d74341Scmchao         s->syscontrol = value & 0x1d;
14202d74341Scmchao         if (value & 2)
14302d74341Scmchao             omap_uart_reset(s);
14402d74341Scmchao         break;
14502d74341Scmchao     case 0x5c:	/* WER (OMAP2) */
14602d74341Scmchao         s->wkup = value & 0x7f;
14702d74341Scmchao         break;
14802d74341Scmchao     case 0x60:	/* CFPS (OMAP2) */
14902d74341Scmchao         s->cfps = value & 0xff;
15002d74341Scmchao         break;
15102d74341Scmchao     default:
15202d74341Scmchao         OMAP_BAD_REG(addr);
15302d74341Scmchao     }
15402d74341Scmchao }
15502d74341Scmchao 
156aee39503SAvi Kivity static const MemoryRegionOps omap_uart_ops = {
157aee39503SAvi Kivity     .read = omap_uart_read,
158aee39503SAvi Kivity     .write = omap_uart_write,
159aee39503SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
16002d74341Scmchao };
16102d74341Scmchao 
162aee39503SAvi Kivity struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
163aee39503SAvi Kivity                 struct omap_target_agent_s *ta,
16402d74341Scmchao                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
1656a8aabd3SStefan Weil                 qemu_irq txdma, qemu_irq rxdma,
166*0ec7b3e7SMarc-André Lureau                 const char *label, Chardev *chr)
16702d74341Scmchao {
168a8170e5eSAvi Kivity     hwaddr base = omap_l4_attach(ta, 0, NULL);
16902d74341Scmchao     struct omap_uart_s *s = omap_uart_init(base, irq,
1706a8aabd3SStefan Weil                     fclk, iclk, txdma, rxdma, label, chr);
171aee39503SAvi Kivity 
1722c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100);
17302d74341Scmchao 
17402d74341Scmchao     s->ta = ta;
17502d74341Scmchao 
176aee39503SAvi Kivity     memory_region_add_subregion(sysmem, base + 0x20, &s->iomem);
17702d74341Scmchao 
17802d74341Scmchao     return s;
17902d74341Scmchao }
18002d74341Scmchao 
181*0ec7b3e7SMarc-André Lureau void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
18202d74341Scmchao {
18302d74341Scmchao     /* TODO: Should reuse or destroy current s->serial */
18439186d8aSRichard Henderson     s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
18502d74341Scmchao                                omap_clk_getrate(s->fclk) / 16,
186b4948be9SMarc-André Lureau                                chr ?: qemu_chr_new("null", "null"),
187fb50cfe4SRichard Henderson                                DEVICE_NATIVE_ENDIAN);
18802d74341Scmchao }
189