1 /* 2 * Arm PrimeCell PL022 Synchronous Serial Port 3 * 4 * Copyright (c) 2007 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licensed under the GPL. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "hw/sysbus.h" 12 #include "hw/ssi/ssi.h" 13 14 //#define DEBUG_PL022 1 15 16 #ifdef DEBUG_PL022 17 #define DPRINTF(fmt, ...) \ 18 do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0) 19 #define BADF(fmt, ...) \ 20 do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) 21 #else 22 #define DPRINTF(fmt, ...) do {} while(0) 23 #define BADF(fmt, ...) \ 24 do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0) 25 #endif 26 27 #define PL022_CR1_LBM 0x01 28 #define PL022_CR1_SSE 0x02 29 #define PL022_CR1_MS 0x04 30 #define PL022_CR1_SDO 0x08 31 32 #define PL022_SR_TFE 0x01 33 #define PL022_SR_TNF 0x02 34 #define PL022_SR_RNE 0x04 35 #define PL022_SR_RFF 0x08 36 #define PL022_SR_BSY 0x10 37 38 #define PL022_INT_ROR 0x01 39 #define PL022_INT_RT 0x04 40 #define PL022_INT_RX 0x04 41 #define PL022_INT_TX 0x08 42 43 #define TYPE_PL022 "pl022" 44 #define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) 45 46 typedef struct PL022State { 47 SysBusDevice parent_obj; 48 49 MemoryRegion iomem; 50 uint32_t cr0; 51 uint32_t cr1; 52 uint32_t bitmask; 53 uint32_t sr; 54 uint32_t cpsr; 55 uint32_t is; 56 uint32_t im; 57 /* The FIFO head points to the next empty entry. */ 58 int tx_fifo_head; 59 int rx_fifo_head; 60 int tx_fifo_len; 61 int rx_fifo_len; 62 uint16_t tx_fifo[8]; 63 uint16_t rx_fifo[8]; 64 qemu_irq irq; 65 SSIBus *ssi; 66 } PL022State; 67 68 static const unsigned char pl022_id[8] = 69 { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 70 71 static void pl022_update(PL022State *s) 72 { 73 s->sr = 0; 74 if (s->tx_fifo_len == 0) 75 s->sr |= PL022_SR_TFE; 76 if (s->tx_fifo_len != 8) 77 s->sr |= PL022_SR_TNF; 78 if (s->rx_fifo_len != 0) 79 s->sr |= PL022_SR_RNE; 80 if (s->rx_fifo_len == 8) 81 s->sr |= PL022_SR_RFF; 82 if (s->tx_fifo_len) 83 s->sr |= PL022_SR_BSY; 84 s->is = 0; 85 if (s->rx_fifo_len >= 4) 86 s->is |= PL022_INT_RX; 87 if (s->tx_fifo_len <= 4) 88 s->is |= PL022_INT_TX; 89 90 qemu_set_irq(s->irq, (s->is & s->im) != 0); 91 } 92 93 static void pl022_xfer(PL022State *s) 94 { 95 int i; 96 int o; 97 int val; 98 99 if ((s->cr1 & PL022_CR1_SSE) == 0) { 100 pl022_update(s); 101 DPRINTF("Disabled\n"); 102 return; 103 } 104 105 DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len); 106 i = (s->tx_fifo_head - s->tx_fifo_len) & 7; 107 o = s->rx_fifo_head; 108 /* ??? We do not emulate the line speed. 109 This may break some applications. The are two problematic cases: 110 (a) A driver feeds data into the TX FIFO until it is full, 111 and only then drains the RX FIFO. On real hardware the CPU can 112 feed data fast enough that the RX fifo never gets chance to overflow. 113 (b) A driver transmits data, deliberately allowing the RX FIFO to 114 overflow because it ignores the RX data anyway. 115 116 We choose to support (a) by stalling the transmit engine if it would 117 cause the RX FIFO to overflow. In practice much transmit-only code 118 falls into (a) because it flushes the RX FIFO to determine when 119 the transfer has completed. */ 120 while (s->tx_fifo_len && s->rx_fifo_len < 8) { 121 DPRINTF("xfer\n"); 122 val = s->tx_fifo[i]; 123 if (s->cr1 & PL022_CR1_LBM) { 124 /* Loopback mode. */ 125 } else { 126 val = ssi_transfer(s->ssi, val); 127 } 128 s->rx_fifo[o] = val & s->bitmask; 129 i = (i + 1) & 7; 130 o = (o + 1) & 7; 131 s->tx_fifo_len--; 132 s->rx_fifo_len++; 133 } 134 s->rx_fifo_head = o; 135 pl022_update(s); 136 } 137 138 static uint64_t pl022_read(void *opaque, hwaddr offset, 139 unsigned size) 140 { 141 PL022State *s = (PL022State *)opaque; 142 int val; 143 144 if (offset >= 0xfe0 && offset < 0x1000) { 145 return pl022_id[(offset - 0xfe0) >> 2]; 146 } 147 switch (offset) { 148 case 0x00: /* CR0 */ 149 return s->cr0; 150 case 0x04: /* CR1 */ 151 return s->cr1; 152 case 0x08: /* DR */ 153 if (s->rx_fifo_len) { 154 val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7]; 155 DPRINTF("RX %02x\n", val); 156 s->rx_fifo_len--; 157 pl022_xfer(s); 158 } else { 159 val = 0; 160 } 161 return val; 162 case 0x0c: /* SR */ 163 return s->sr; 164 case 0x10: /* CPSR */ 165 return s->cpsr; 166 case 0x14: /* IMSC */ 167 return s->im; 168 case 0x18: /* RIS */ 169 return s->is; 170 case 0x1c: /* MIS */ 171 return s->im & s->is; 172 case 0x20: /* DMACR */ 173 /* Not implemented. */ 174 return 0; 175 default: 176 qemu_log_mask(LOG_GUEST_ERROR, 177 "pl022_read: Bad offset %x\n", (int)offset); 178 return 0; 179 } 180 } 181 182 static void pl022_write(void *opaque, hwaddr offset, 183 uint64_t value, unsigned size) 184 { 185 PL022State *s = (PL022State *)opaque; 186 187 switch (offset) { 188 case 0x00: /* CR0 */ 189 s->cr0 = value; 190 /* Clock rate and format are ignored. */ 191 s->bitmask = (1 << ((value & 15) + 1)) - 1; 192 break; 193 case 0x04: /* CR1 */ 194 s->cr1 = value; 195 if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE)) 196 == (PL022_CR1_MS | PL022_CR1_SSE)) { 197 BADF("SPI slave mode not implemented\n"); 198 } 199 pl022_xfer(s); 200 break; 201 case 0x08: /* DR */ 202 if (s->tx_fifo_len < 8) { 203 DPRINTF("TX %02x\n", (unsigned)value); 204 s->tx_fifo[s->tx_fifo_head] = value & s->bitmask; 205 s->tx_fifo_head = (s->tx_fifo_head + 1) & 7; 206 s->tx_fifo_len++; 207 pl022_xfer(s); 208 } 209 break; 210 case 0x10: /* CPSR */ 211 /* Prescaler. Ignored. */ 212 s->cpsr = value & 0xff; 213 break; 214 case 0x14: /* IMSC */ 215 s->im = value; 216 pl022_update(s); 217 break; 218 case 0x20: /* DMACR */ 219 if (value) { 220 qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n"); 221 } 222 break; 223 default: 224 qemu_log_mask(LOG_GUEST_ERROR, 225 "pl022_write: Bad offset %x\n", (int)offset); 226 } 227 } 228 229 static void pl022_reset(PL022State *s) 230 { 231 s->rx_fifo_len = 0; 232 s->tx_fifo_len = 0; 233 s->im = 0; 234 s->is = PL022_INT_TX; 235 s->sr = PL022_SR_TFE | PL022_SR_TNF; 236 } 237 238 static const MemoryRegionOps pl022_ops = { 239 .read = pl022_read, 240 .write = pl022_write, 241 .endianness = DEVICE_NATIVE_ENDIAN, 242 }; 243 244 static int pl022_post_load(void *opaque, int version_id) 245 { 246 PL022State *s = opaque; 247 248 if (s->tx_fifo_head < 0 || 249 s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) || 250 s->rx_fifo_head < 0 || 251 s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) { 252 return -1; 253 } 254 return 0; 255 } 256 257 static const VMStateDescription vmstate_pl022 = { 258 .name = "pl022_ssp", 259 .version_id = 1, 260 .minimum_version_id = 1, 261 .post_load = pl022_post_load, 262 .fields = (VMStateField[]) { 263 VMSTATE_UINT32(cr0, PL022State), 264 VMSTATE_UINT32(cr1, PL022State), 265 VMSTATE_UINT32(bitmask, PL022State), 266 VMSTATE_UINT32(sr, PL022State), 267 VMSTATE_UINT32(cpsr, PL022State), 268 VMSTATE_UINT32(is, PL022State), 269 VMSTATE_UINT32(im, PL022State), 270 VMSTATE_INT32(tx_fifo_head, PL022State), 271 VMSTATE_INT32(rx_fifo_head, PL022State), 272 VMSTATE_INT32(tx_fifo_len, PL022State), 273 VMSTATE_INT32(rx_fifo_len, PL022State), 274 VMSTATE_UINT16(tx_fifo[0], PL022State), 275 VMSTATE_UINT16(rx_fifo[0], PL022State), 276 VMSTATE_UINT16(tx_fifo[1], PL022State), 277 VMSTATE_UINT16(rx_fifo[1], PL022State), 278 VMSTATE_UINT16(tx_fifo[2], PL022State), 279 VMSTATE_UINT16(rx_fifo[2], PL022State), 280 VMSTATE_UINT16(tx_fifo[3], PL022State), 281 VMSTATE_UINT16(rx_fifo[3], PL022State), 282 VMSTATE_UINT16(tx_fifo[4], PL022State), 283 VMSTATE_UINT16(rx_fifo[4], PL022State), 284 VMSTATE_UINT16(tx_fifo[5], PL022State), 285 VMSTATE_UINT16(rx_fifo[5], PL022State), 286 VMSTATE_UINT16(tx_fifo[6], PL022State), 287 VMSTATE_UINT16(rx_fifo[6], PL022State), 288 VMSTATE_UINT16(tx_fifo[7], PL022State), 289 VMSTATE_UINT16(rx_fifo[7], PL022State), 290 VMSTATE_END_OF_LIST() 291 } 292 }; 293 294 static int pl022_init(SysBusDevice *sbd) 295 { 296 DeviceState *dev = DEVICE(sbd); 297 PL022State *s = PL022(dev); 298 299 memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000); 300 sysbus_init_mmio(sbd, &s->iomem); 301 sysbus_init_irq(sbd, &s->irq); 302 s->ssi = ssi_create_bus(dev, "ssi"); 303 pl022_reset(s); 304 vmstate_register(dev, -1, &vmstate_pl022, s); 305 return 0; 306 } 307 308 static void pl022_class_init(ObjectClass *klass, void *data) 309 { 310 SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); 311 312 sdc->init = pl022_init; 313 } 314 315 static const TypeInfo pl022_info = { 316 .name = TYPE_PL022, 317 .parent = TYPE_SYS_BUS_DEVICE, 318 .instance_size = sizeof(PL022State), 319 .class_init = pl022_class_init, 320 }; 321 322 static void pl022_register_types(void) 323 { 324 type_register_static(&pl022_info); 325 } 326 327 type_init(pl022_register_types) 328