qemu/hw/char/omap_uart.c
<<
>>
Prefs
   1/*
   2 * TI OMAP processors UART emulation.
   3 *
   4 * Copyright (C) 2006-2008 Andrzej Zaborowski  <balrog@zabor.org>
   5 * Copyright (C) 2007-2009 Nokia Corporation
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 or
  10 * (at your option) version 3 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along
  18 * with this program; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20#include "qemu/osdep.h"
  21#include "chardev/char.h"
  22#include "hw/arm/omap.h"
  23#include "hw/char/serial.h"
  24#include "exec/address-spaces.h"
  25
  26/* UARTs */
  27struct omap_uart_s {
  28    MemoryRegion iomem;
  29    hwaddr base;
  30    SerialMM *serial; /* TODO */
  31    struct omap_target_agent_s *ta;
  32    omap_clk fclk;
  33    qemu_irq irq;
  34
  35    uint8_t eblr;
  36    uint8_t syscontrol;
  37    uint8_t wkup;
  38    uint8_t cfps;
  39    uint8_t mdr[2];
  40    uint8_t scr;
  41    uint8_t clksel;
  42};
  43
  44void omap_uart_reset(struct omap_uart_s *s)
  45{
  46    s->eblr = 0x00;
  47    s->syscontrol = 0;
  48    s->wkup = 0x3f;
  49    s->cfps = 0x69;
  50    s->clksel = 0;
  51}
  52
  53struct omap_uart_s *omap_uart_init(hwaddr base,
  54                qemu_irq irq, omap_clk fclk, omap_clk iclk,
  55                qemu_irq txdma, qemu_irq rxdma,
  56                const char *label, Chardev *chr)
  57{
  58    struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
  59
  60    s->base = base;
  61    s->fclk = fclk;
  62    s->irq = irq;
  63    s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
  64                               omap_clk_getrate(fclk)/16,
  65                               chr ?: qemu_chr_new(label, "null", NULL),
  66                               DEVICE_NATIVE_ENDIAN);
  67    return s;
  68}
  69
  70static uint64_t omap_uart_read(void *opaque, hwaddr addr, unsigned size)
  71{
  72    struct omap_uart_s *s = opaque;
  73
  74    if (size == 4) {
  75        return omap_badwidth_read8(opaque, addr);
  76    }
  77
  78    switch (addr) {
  79    case 0x20:  /* MDR1 */
  80        return s->mdr[0];
  81    case 0x24:  /* MDR2 */
  82        return s->mdr[1];
  83    case 0x40:  /* SCR */
  84        return s->scr;
  85    case 0x44:  /* SSR */
  86        return 0x0;
  87    case 0x48:  /* EBLR (OMAP2) */
  88        return s->eblr;
  89    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
  90        return s->clksel;
  91    case 0x50:  /* MVR */
  92        return 0x30;
  93    case 0x54:  /* SYSC (OMAP2) */
  94        return s->syscontrol;
  95    case 0x58:  /* SYSS (OMAP2) */
  96        return 1;
  97    case 0x5c:  /* WER (OMAP2) */
  98        return s->wkup;
  99    case 0x60:  /* CFPS (OMAP2) */
 100        return s->cfps;
 101    }
 102
 103    OMAP_BAD_REG(addr);
 104    return 0;
 105}
 106
 107static void omap_uart_write(void *opaque, hwaddr addr,
 108                            uint64_t value, unsigned size)
 109{
 110    struct omap_uart_s *s = opaque;
 111
 112    if (size == 4) {
 113        omap_badwidth_write8(opaque, addr, value);
 114        return;
 115    }
 116
 117    switch (addr) {
 118    case 0x20:  /* MDR1 */
 119        s->mdr[0] = value & 0x7f;
 120        break;
 121    case 0x24:  /* MDR2 */
 122        s->mdr[1] = value & 0xff;
 123        break;
 124    case 0x40:  /* SCR */
 125        s->scr = value & 0xff;
 126        break;
 127    case 0x48:  /* EBLR (OMAP2) */
 128        s->eblr = value & 0xff;
 129        break;
 130    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
 131        s->clksel = value & 1;
 132        break;
 133    case 0x44:  /* SSR */
 134    case 0x50:  /* MVR */
 135    case 0x58:  /* SYSS (OMAP2) */
 136        OMAP_RO_REG(addr);
 137        break;
 138    case 0x54:  /* SYSC (OMAP2) */
 139        s->syscontrol = value & 0x1d;
 140        if (value & 2)
 141            omap_uart_reset(s);
 142        break;
 143    case 0x5c:  /* WER (OMAP2) */
 144        s->wkup = value & 0x7f;
 145        break;
 146    case 0x60:  /* CFPS (OMAP2) */
 147        s->cfps = value & 0xff;
 148        break;
 149    default:
 150        OMAP_BAD_REG(addr);
 151    }
 152}
 153
 154static const MemoryRegionOps omap_uart_ops = {
 155    .read = omap_uart_read,
 156    .write = omap_uart_write,
 157    .endianness = DEVICE_NATIVE_ENDIAN,
 158};
 159
 160struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
 161                struct omap_target_agent_s *ta,
 162                qemu_irq irq, omap_clk fclk, omap_clk iclk,
 163                qemu_irq txdma, qemu_irq rxdma,
 164                const char *label, Chardev *chr)
 165{
 166    hwaddr base = omap_l4_attach(ta, 0, NULL);
 167    struct omap_uart_s *s = omap_uart_init(base, irq,
 168                    fclk, iclk, txdma, rxdma, label, chr);
 169
 170    memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100);
 171
 172    s->ta = ta;
 173
 174    memory_region_add_subregion(sysmem, base + 0x20, &s->iomem);
 175
 176    return s;
 177}
 178
 179void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
 180{
 181    /* TODO: Should reuse or destroy current s->serial */
 182    s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
 183                               omap_clk_getrate(s->fclk) / 16,
 184                               chr ?: qemu_chr_new("null", "null", NULL),
 185                               DEVICE_NATIVE_ENDIAN);
 186}
 187