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,
  71                               unsigned size)
  72{
  73    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
  74
  75    if (size == 4) {
  76        return omap_badwidth_read8(opaque, addr);
  77    }
  78
  79    switch (addr) {
  80    case 0x20:  /* MDR1 */
  81        return s->mdr[0];
  82    case 0x24:  /* MDR2 */
  83        return s->mdr[1];
  84    case 0x40:  /* SCR */
  85        return s->scr;
  86    case 0x44:  /* SSR */
  87        return 0x0;
  88    case 0x48:  /* EBLR (OMAP2) */
  89        return s->eblr;
  90    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
  91        return s->clksel;
  92    case 0x50:  /* MVR */
  93        return 0x30;
  94    case 0x54:  /* SYSC (OMAP2) */
  95        return s->syscontrol;
  96    case 0x58:  /* SYSS (OMAP2) */
  97        return 1;
  98    case 0x5c:  /* WER (OMAP2) */
  99        return s->wkup;
 100    case 0x60:  /* CFPS (OMAP2) */
 101        return s->cfps;
 102    }
 103
 104    OMAP_BAD_REG(addr);
 105    return 0;
 106}
 107
 108static void omap_uart_write(void *opaque, hwaddr addr,
 109                            uint64_t value, unsigned size)
 110{
 111    struct omap_uart_s *s = (struct omap_uart_s *) opaque;
 112
 113    if (size == 4) {
 114        omap_badwidth_write8(opaque, addr, value);
 115        return;
 116    }
 117
 118    switch (addr) {
 119    case 0x20:  /* MDR1 */
 120        s->mdr[0] = value & 0x7f;
 121        break;
 122    case 0x24:  /* MDR2 */
 123        s->mdr[1] = value & 0xff;
 124        break;
 125    case 0x40:  /* SCR */
 126        s->scr = value & 0xff;
 127        break;
 128    case 0x48:  /* EBLR (OMAP2) */
 129        s->eblr = value & 0xff;
 130        break;
 131    case 0x4C:  /* OSC_12M_SEL (OMAP1) */
 132        s->clksel = value & 1;
 133        break;
 134    case 0x44:  /* SSR */
 135    case 0x50:  /* MVR */
 136    case 0x58:  /* SYSS (OMAP2) */
 137        OMAP_RO_REG(addr);
 138        break;
 139    case 0x54:  /* SYSC (OMAP2) */
 140        s->syscontrol = value & 0x1d;
 141        if (value & 2)
 142            omap_uart_reset(s);
 143        break;
 144    case 0x5c:  /* WER (OMAP2) */
 145        s->wkup = value & 0x7f;
 146        break;
 147    case 0x60:  /* CFPS (OMAP2) */
 148        s->cfps = value & 0xff;
 149        break;
 150    default:
 151        OMAP_BAD_REG(addr);
 152    }
 153}
 154
 155static const MemoryRegionOps omap_uart_ops = {
 156    .read = omap_uart_read,
 157    .write = omap_uart_write,
 158    .endianness = DEVICE_NATIVE_ENDIAN,
 159};
 160
 161struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
 162                struct omap_target_agent_s *ta,
 163                qemu_irq irq, omap_clk fclk, omap_clk iclk,
 164                qemu_irq txdma, qemu_irq rxdma,
 165                const char *label, Chardev *chr)
 166{
 167    hwaddr base = omap_l4_attach(ta, 0, NULL);
 168    struct omap_uart_s *s = omap_uart_init(base, irq,
 169                    fclk, iclk, txdma, rxdma, label, chr);
 170
 171    memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100);
 172
 173    s->ta = ta;
 174
 175    memory_region_add_subregion(sysmem, base + 0x20, &s->iomem);
 176
 177    return s;
 178}
 179
 180void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
 181{
 182    /* TODO: Should reuse or destroy current s->serial */
 183    s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
 184                               omap_clk_getrate(s->fclk) / 16,
 185                               chr ?: qemu_chr_new("null", "null", NULL),
 186                               DEVICE_NATIVE_ENDIAN);
 187}
 188