|
|
1.1 ! root 1: /* ! 2: * Arm PrimeCell PL011 UART ! 3: * ! 4: * Copyright (c) 2006 CodeSourcery. ! 5: * Written by Paul Brook ! 6: * ! 7: * This code is licenced under the GPL. ! 8: */ ! 9: ! 10: #include "vl.h" ! 11: ! 12: typedef struct { ! 13: uint32_t base; ! 14: uint32_t readbuff; ! 15: uint32_t flags; ! 16: uint32_t lcr; ! 17: uint32_t cr; ! 18: uint32_t dmacr; ! 19: uint32_t int_enabled; ! 20: uint32_t int_level; ! 21: uint32_t read_fifo[16]; ! 22: uint32_t ilpr; ! 23: uint32_t ibrd; ! 24: uint32_t fbrd; ! 25: uint32_t ifl; ! 26: int read_pos; ! 27: int read_count; ! 28: int read_trigger; ! 29: CharDriverState *chr; ! 30: void *pic; ! 31: int irq; ! 32: } pl011_state; ! 33: ! 34: #define PL011_INT_TX 0x20 ! 35: #define PL011_INT_RX 0x10 ! 36: ! 37: #define PL011_FLAG_TXFE 0x80 ! 38: #define PL011_FLAG_RXFF 0x40 ! 39: #define PL011_FLAG_TXFF 0x20 ! 40: #define PL011_FLAG_RXFE 0x10 ! 41: ! 42: static const unsigned char pl011_id[] = ! 43: { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; ! 44: ! 45: static void pl011_update(pl011_state *s) ! 46: { ! 47: uint32_t flags; ! 48: ! 49: flags = s->int_level & s->int_enabled; ! 50: pic_set_irq_new(s->pic, s->irq, flags != 0); ! 51: } ! 52: ! 53: static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) ! 54: { ! 55: pl011_state *s = (pl011_state *)opaque; ! 56: uint32_t c; ! 57: ! 58: offset -= s->base; ! 59: if (offset >= 0xfe0 && offset < 0x1000) { ! 60: return pl011_id[(offset - 0xfe0) >> 2]; ! 61: } ! 62: switch (offset >> 2) { ! 63: case 0: /* UARTDR */ ! 64: s->flags &= ~PL011_FLAG_RXFF; ! 65: c = s->read_fifo[s->read_pos]; ! 66: if (s->read_count > 0) { ! 67: s->read_count--; ! 68: if (++s->read_pos == 16) ! 69: s->read_pos = 0; ! 70: } ! 71: if (s->read_count == 0) { ! 72: s->flags |= PL011_FLAG_RXFE; ! 73: } ! 74: if (s->read_count == s->read_trigger - 1) ! 75: s->int_level &= ~ PL011_INT_RX; ! 76: pl011_update(s); ! 77: return c; ! 78: case 1: /* UARTCR */ ! 79: return 0; ! 80: case 6: /* UARTFR */ ! 81: return s->flags; ! 82: case 8: /* UARTILPR */ ! 83: return s->ilpr; ! 84: case 9: /* UARTIBRD */ ! 85: return s->ibrd; ! 86: case 10: /* UARTFBRD */ ! 87: return s->fbrd; ! 88: case 11: /* UARTLCR_H */ ! 89: return s->lcr; ! 90: case 12: /* UARTCR */ ! 91: return s->cr; ! 92: case 13: /* UARTIFLS */ ! 93: return s->ifl; ! 94: case 14: /* UARTIMSC */ ! 95: return s->int_enabled; ! 96: case 15: /* UARTRIS */ ! 97: return s->int_level; ! 98: case 16: /* UARTMIS */ ! 99: return s->int_level & s->int_enabled; ! 100: case 18: /* UARTDMACR */ ! 101: return s->dmacr; ! 102: default: ! 103: cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); ! 104: return 0; ! 105: } ! 106: } ! 107: ! 108: static void pl011_set_read_trigger(pl011_state *s) ! 109: { ! 110: #if 0 ! 111: /* The docs say the RX interrupt is triggered when the FIFO exceeds ! 112: the threshold. However linux only reads the FIFO in response to an ! 113: interrupt. Triggering the interrupt when the FIFO is non-empty seems ! 114: to make things work. */ ! 115: if (s->lcr & 0x10) ! 116: s->read_trigger = (s->ifl >> 1) & 0x1c; ! 117: else ! 118: #endif ! 119: s->read_trigger = 1; ! 120: } ! 121: ! 122: static void pl011_write(void *opaque, target_phys_addr_t offset, ! 123: uint32_t value) ! 124: { ! 125: pl011_state *s = (pl011_state *)opaque; ! 126: unsigned char ch; ! 127: ! 128: offset -= s->base; ! 129: switch (offset >> 2) { ! 130: case 0: /* UARTDR */ ! 131: /* ??? Check if transmitter is enabled. */ ! 132: ch = value; ! 133: if (s->chr) ! 134: qemu_chr_write(s->chr, &ch, 1); ! 135: s->int_level |= PL011_INT_TX; ! 136: pl011_update(s); ! 137: break; ! 138: case 1: /* UARTCR */ ! 139: s->cr = value; ! 140: break; ! 141: case 8: /* UARTUARTILPR */ ! 142: s->ilpr = value; ! 143: break; ! 144: case 9: /* UARTIBRD */ ! 145: s->ibrd = value; ! 146: break; ! 147: case 10: /* UARTFBRD */ ! 148: s->fbrd = value; ! 149: break; ! 150: case 11: /* UARTLCR_H */ ! 151: s->lcr = value; ! 152: pl011_set_read_trigger(s); ! 153: break; ! 154: case 12: /* UARTCR */ ! 155: /* ??? Need to implement the enable and loopback bits. */ ! 156: s->cr = value; ! 157: break; ! 158: case 13: /* UARTIFS */ ! 159: s->ifl = value; ! 160: pl011_set_read_trigger(s); ! 161: break; ! 162: case 14: /* UARTIMSC */ ! 163: s->int_enabled = value; ! 164: pl011_update(s); ! 165: break; ! 166: case 17: /* UARTICR */ ! 167: s->int_level &= ~value; ! 168: pl011_update(s); ! 169: break; ! 170: case 18: /* UARTDMACR */ ! 171: s->dmacr = value; ! 172: if (value & 3) ! 173: cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); ! 174: break; ! 175: default: ! 176: cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); ! 177: } ! 178: } ! 179: ! 180: static int pl011_can_recieve(void *opaque) ! 181: { ! 182: pl011_state *s = (pl011_state *)opaque; ! 183: ! 184: if (s->lcr & 0x10) ! 185: return s->read_count < 16; ! 186: else ! 187: return s->read_count < 1; ! 188: } ! 189: ! 190: static void pl011_recieve(void *opaque, const uint8_t *buf, int size) ! 191: { ! 192: pl011_state *s = (pl011_state *)opaque; ! 193: int slot; ! 194: ! 195: slot = s->read_pos + s->read_count; ! 196: if (slot >= 16) ! 197: slot -= 16; ! 198: s->read_fifo[slot] = *buf; ! 199: s->read_count++; ! 200: s->flags &= ~PL011_FLAG_RXFE; ! 201: if (s->cr & 0x10 || s->read_count == 16) { ! 202: s->flags |= PL011_FLAG_RXFF; ! 203: } ! 204: if (s->read_count == s->read_trigger) { ! 205: s->int_level |= PL011_INT_RX; ! 206: pl011_update(s); ! 207: } ! 208: } ! 209: ! 210: static void pl011_event(void *opaque, int event) ! 211: { ! 212: /* ??? Should probably implement break. */ ! 213: } ! 214: ! 215: static CPUReadMemoryFunc *pl011_readfn[] = { ! 216: pl011_read, ! 217: pl011_read, ! 218: pl011_read ! 219: }; ! 220: ! 221: static CPUWriteMemoryFunc *pl011_writefn[] = { ! 222: pl011_write, ! 223: pl011_write, ! 224: pl011_write ! 225: }; ! 226: ! 227: void pl011_init(uint32_t base, void *pic, int irq, ! 228: CharDriverState *chr) ! 229: { ! 230: int iomemtype; ! 231: pl011_state *s; ! 232: ! 233: s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); ! 234: iomemtype = cpu_register_io_memory(0, pl011_readfn, ! 235: pl011_writefn, s); ! 236: cpu_register_physical_memory(base, 0x00000fff, iomemtype); ! 237: s->base = base; ! 238: s->pic = pic; ! 239: s->irq = irq; ! 240: s->chr = chr; ! 241: s->read_trigger = 1; ! 242: s->ifl = 0x12; ! 243: s->cr = 0x300; ! 244: s->flags = 0x90; ! 245: if (chr){ ! 246: qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); ! 247: qemu_chr_add_event_handler(chr, pl011_event); ! 248: } ! 249: /* ??? Save/restore. */ ! 250: } ! 251:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.