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