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/hw.h"
  23#include "hw/arm/omap.h"
  24#include "hw/char/serial.h"
  25#include "exec/address-spaces.h"
  26
  27/* UARTs */
  28struct omap_uart_s {
  29    MemoryRegion iomem;
  30    hwaddr base;
  31    SerialState *serial; /* TODO */
  32    struct omap_target_agent_s *ta;
  33    omap_clk fclk;
  34    qemu_irq irq;
  35
  36    uint8_t eblr;
  37    uint8_t syscontrol;
  38    uint8_t wkup;
  39    uint8_t cfps;
  40    uint8_t mdr[2];
  41    uint8_t scr;
  42    uint8_t clksel;
  43};
  44
  45void omap_uart_reset(struct omap_uart_s *s)
  46{
  47    s->eblr = 0x00;
  48    s->syscontrol = 0;
  49    s->wkup = 0x3f;
  50    s->cfps = 0x69;
  51    s->clksel = 0;
  52}
  53
  54struct omap_uart_s *omap_uart_init(hwaddr base,
  55                qemu_irq irq, omap_clk fclk, omap_clk iclk,
  56                qemu_irq txdma, qemu_irq rxdma,
  57                const char *label, Chardev *chr)
  58{
  59    struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
  60
  61    s->base = base;
  62    s->fclk = fclk;
  63    s->irq = irq;
  64    s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
  65                               omap_clk_getrate(fclk)/16,
  66                               chr ?: qemu_chr_new(label, "null"),
  67                               DEVICE_NATIVE_ENDIAN);
  68    return s;
  69}
  70
  71static uint64_t omap_uart_read(void *opaque, hwaddr addr,
  72                               unsigned size)
  73{
  74    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
  75
  76    if (size == 4) {
  77        return omap_badwidth_read8(opaque, addr);
  78    }
  79
  80    switch (addr) {
  81    case 0x20:  /* MDR1 */
  82        return s->mdr[0];
  83    case 0x24:  /* MDR2 */
  84        return s->mdr[1];
  85    case 0x40:  /* SCR */
  86        return s->scr;
  87    case 0x44:  /* SSR */
  88        return 0x0;
  89    case 0x48:  /* EBLR (OMAP2) */
  90        return s->eblr;
  91    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
  92        return s->clksel;
  93    case 0x50:  /* MVR */
  94        return 0x30;
  95    case 0x54:  /* SYSC (OMAP2) */
  96        return s->syscontrol;
  97    case 0x58:  /* SYSS (OMAP2) */
  98        return 1;
  99    case 0x5c:  /* WER (OMAP2) */
 100        return s->wkup;
 101    case 0x60:  /* CFPS (OMAP2) */
 102        return s->cfps;
 103    }
 104
 105    OMAP_BAD_REG(addr);
 106    return 0;
 107}
 108
 109static void omap_uart_write(void *opaque, hwaddr addr,
 110                            uint64_t value, unsigned size)
 111{
 112    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
 113
 114    if (size == 4) {
 115        omap_badwidth_write8(opaque, addr, value);
 116        return;
 117    }
 118
 119    switch (addr) {
 120    case 0x20:  /* MDR1 */
 121        s->mdr[0] = value & 0x7f;
 122        break;
 123    case 0x24:  /* MDR2 */
 124        s->mdr[1] = value & 0xff;
 125        break;
 126    case 0x40:  /* SCR */
 127        s->scr = value & 0xff;
 128        break;
 129    case 0x48:  /* EBLR (OMAP2) */
 130        s->eblr = value & 0xff;
 131        break;
 132    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
 133        s->clksel = value & 1;
 134        break;
 135    case 0x44:  /* SSR */
 136    case 0x50:  /* MVR */
 137    case 0x58:  /* SYSS (OMAP2) */
 138        OMAP_RO_REG(addr);
 139        break;
 140    case 0x54:  /* SYSC (OMAP2) */
 141        s->syscontrol = value & 0x1d;
 142        if (value & 2)
 143            omap_uart_reset(s);
 144        break;
 145    case 0x5c:  /* WER (OMAP2) */
 146        s->wkup = value & 0x7f;
 147        break;
 148    case 0x60:  /* CFPS (OMAP2) */
 149        s->cfps = value & 0xff;
 150        break;
 151    default:
 152        OMAP_BAD_REG(addr);
 153    }
 154}
 155
 156static const MemoryRegionOps omap_uart_ops = {
 157    .read = omap_uart_read,
 158    .write = omap_uart_write,
 159    .endianness = DEVICE_NATIVE_ENDIAN,
 160};
 161
 162struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
 163                struct omap_target_agent_s *ta,
 164                qemu_irq irq, omap_clk fclk, omap_clk iclk,
 165                qemu_irq txdma, qemu_irq rxdma,
 166                const char *label, Chardev *chr)
 167{
 168    hwaddr base = omap_l4_attach(ta, 0, NULL);
 169    struct omap_uart_s *s = omap_uart_init(base, irq,
 170                    fclk, iclk, txdma, rxdma, label, chr);
 171
 172    memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100);
 173
 174    s->ta = ta;
 175
 176    memory_region_add_subregion(sysmem, base + 0x20, &s->iomem);
 177
 178    return s;
 179}
 180
 181void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
 182{
 183    /* TODO: Should reuse or destroy current s->serial */
 184    s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
 185                               omap_clk_getrate(s->fclk) / 16,
 186                               chr ?: qemu_chr_new("null", "null"),
 187                               DEVICE_NATIVE_ENDIAN);
 188}
 189