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