1 /* 2 * TI OMAP processor's Multichannel SPI emulation. 3 * 4 * Copyright (C) 2007-2009 Nokia Corporation 5 * 6 * Original code for OMAP2 by Andrzej Zaborowski <andrew@openedhand.com> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 or 11 * (at your option) any later version of the License. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 */ 22 #include "hw/hw.h" 23 #include "hw/arm/omap.h" 24 25 /* Multichannel SPI */ 26 struct omap_mcspi_s { 27 MemoryRegion iomem; 28 qemu_irq irq; 29 int chnum; 30 31 uint32_t sysconfig; 32 uint32_t systest; 33 uint32_t irqst; 34 uint32_t irqen; 35 uint32_t wken; 36 uint32_t control; 37 38 struct omap_mcspi_ch_s { 39 qemu_irq txdrq; 40 qemu_irq rxdrq; 41 uint32_t (*txrx)(void *opaque, uint32_t, int); 42 void *opaque; 43 44 uint32_t tx; 45 uint32_t rx; 46 47 uint32_t config; 48 uint32_t status; 49 uint32_t control; 50 } ch[4]; 51 }; 52 53 static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) 54 { 55 qemu_set_irq(s->irq, s->irqst & s->irqen); 56 } 57 58 static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) 59 { 60 qemu_set_irq(ch->txdrq, 61 (ch->control & 1) && /* EN */ 62 (ch->config & (1 << 14)) && /* DMAW */ 63 (ch->status & (1 << 1)) && /* TXS */ 64 ((ch->config >> 12) & 3) != 1); /* TRM */ 65 qemu_set_irq(ch->rxdrq, 66 (ch->control & 1) && /* EN */ 67 (ch->config & (1 << 15)) && /* DMAW */ 68 (ch->status & (1 << 0)) && /* RXS */ 69 ((ch->config >> 12) & 3) != 2); /* TRM */ 70 } 71 72 static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) 73 { 74 struct omap_mcspi_ch_s *ch = s->ch + chnum; 75 76 if (!(ch->control & 1)) /* EN */ 77 return; 78 if ((ch->status & (1 << 0)) && /* RXS */ 79 ((ch->config >> 12) & 3) != 2 && /* TRM */ 80 !(ch->config & (1 << 19))) /* TURBO */ 81 goto intr_update; 82 if ((ch->status & (1 << 1)) && /* TXS */ 83 ((ch->config >> 12) & 3) != 1) /* TRM */ 84 goto intr_update; 85 86 if (!(s->control & 1) || /* SINGLE */ 87 (ch->config & (1 << 20))) { /* FORCE */ 88 if (ch->txrx) 89 ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ 90 1 + (0x1f & (ch->config >> 7))); 91 } 92 93 ch->tx = 0; 94 ch->status |= 1 << 2; /* EOT */ 95 ch->status |= 1 << 1; /* TXS */ 96 if (((ch->config >> 12) & 3) != 2) /* TRM */ 97 ch->status |= 1 << 0; /* RXS */ 98 99 intr_update: 100 if ((ch->status & (1 << 0)) && /* RXS */ 101 ((ch->config >> 12) & 3) != 2 && /* TRM */ 102 !(ch->config & (1 << 19))) /* TURBO */ 103 s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ 104 if ((ch->status & (1 << 1)) && /* TXS */ 105 ((ch->config >> 12) & 3) != 1) /* TRM */ 106 s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ 107 omap_mcspi_interrupt_update(s); 108 omap_mcspi_dmarequest_update(ch); 109 } 110 111 void omap_mcspi_reset(struct omap_mcspi_s *s) 112 { 113 int ch; 114 115 s->sysconfig = 0; 116 s->systest = 0; 117 s->irqst = 0; 118 s->irqen = 0; 119 s->wken = 0; 120 s->control = 4; 121 122 for (ch = 0; ch < 4; ch ++) { 123 s->ch[ch].config = 0x060000; 124 s->ch[ch].status = 2; /* TXS */ 125 s->ch[ch].control = 0; 126 127 omap_mcspi_dmarequest_update(s->ch + ch); 128 } 129 130 omap_mcspi_interrupt_update(s); 131 } 132 133 static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, 134 unsigned size) 135 { 136 struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; 137 int ch = 0; 138 uint32_t ret; 139 140 if (size != 4) { 141 return omap_badwidth_read32(opaque, addr); 142 } 143 144 switch (addr) { 145 case 0x00: /* MCSPI_REVISION */ 146 return 0x91; 147 148 case 0x10: /* MCSPI_SYSCONFIG */ 149 return s->sysconfig; 150 151 case 0x14: /* MCSPI_SYSSTATUS */ 152 return 1; /* RESETDONE */ 153 154 case 0x18: /* MCSPI_IRQSTATUS */ 155 return s->irqst; 156 157 case 0x1c: /* MCSPI_IRQENABLE */ 158 return s->irqen; 159 160 case 0x20: /* MCSPI_WAKEUPENABLE */ 161 return s->wken; 162 163 case 0x24: /* MCSPI_SYST */ 164 return s->systest; 165 166 case 0x28: /* MCSPI_MODULCTRL */ 167 return s->control; 168 169 case 0x68: ch ++; 170 /* fall through */ 171 case 0x54: ch ++; 172 /* fall through */ 173 case 0x40: ch ++; 174 /* fall through */ 175 case 0x2c: /* MCSPI_CHCONF */ 176 return s->ch[ch].config; 177 178 case 0x6c: ch ++; 179 /* fall through */ 180 case 0x58: ch ++; 181 /* fall through */ 182 case 0x44: ch ++; 183 /* fall through */ 184 case 0x30: /* MCSPI_CHSTAT */ 185 return s->ch[ch].status; 186 187 case 0x70: ch ++; 188 /* fall through */ 189 case 0x5c: ch ++; 190 /* fall through */ 191 case 0x48: ch ++; 192 /* fall through */ 193 case 0x34: /* MCSPI_CHCTRL */ 194 return s->ch[ch].control; 195 196 case 0x74: ch ++; 197 /* fall through */ 198 case 0x60: ch ++; 199 /* fall through */ 200 case 0x4c: ch ++; 201 /* fall through */ 202 case 0x38: /* MCSPI_TX */ 203 return s->ch[ch].tx; 204 205 case 0x78: ch ++; 206 /* fall through */ 207 case 0x64: ch ++; 208 /* fall through */ 209 case 0x50: ch ++; 210 /* fall through */ 211 case 0x3c: /* MCSPI_RX */ 212 s->ch[ch].status &= ~(1 << 0); /* RXS */ 213 ret = s->ch[ch].rx; 214 omap_mcspi_transfer_run(s, ch); 215 return ret; 216 } 217 218 OMAP_BAD_REG(addr); 219 return 0; 220 } 221 222 static void omap_mcspi_write(void *opaque, hwaddr addr, 223 uint64_t value, unsigned size) 224 { 225 struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; 226 int ch = 0; 227 228 if (size != 4) { 229 return omap_badwidth_write32(opaque, addr, value); 230 } 231 232 switch (addr) { 233 case 0x00: /* MCSPI_REVISION */ 234 case 0x14: /* MCSPI_SYSSTATUS */ 235 case 0x30: /* MCSPI_CHSTAT0 */ 236 case 0x3c: /* MCSPI_RX0 */ 237 case 0x44: /* MCSPI_CHSTAT1 */ 238 case 0x50: /* MCSPI_RX1 */ 239 case 0x58: /* MCSPI_CHSTAT2 */ 240 case 0x64: /* MCSPI_RX2 */ 241 case 0x6c: /* MCSPI_CHSTAT3 */ 242 case 0x78: /* MCSPI_RX3 */ 243 OMAP_RO_REG(addr); 244 return; 245 246 case 0x10: /* MCSPI_SYSCONFIG */ 247 if (value & (1 << 1)) /* SOFTRESET */ 248 omap_mcspi_reset(s); 249 s->sysconfig = value & 0x31d; 250 break; 251 252 case 0x18: /* MCSPI_IRQSTATUS */ 253 if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { 254 s->irqst &= ~value; 255 omap_mcspi_interrupt_update(s); 256 } 257 break; 258 259 case 0x1c: /* MCSPI_IRQENABLE */ 260 s->irqen = value & 0x1777f; 261 omap_mcspi_interrupt_update(s); 262 break; 263 264 case 0x20: /* MCSPI_WAKEUPENABLE */ 265 s->wken = value & 1; 266 break; 267 268 case 0x24: /* MCSPI_SYST */ 269 if (s->control & (1 << 3)) /* SYSTEM_TEST */ 270 if (value & (1 << 11)) { /* SSB */ 271 s->irqst |= 0x1777f; 272 omap_mcspi_interrupt_update(s); 273 } 274 s->systest = value & 0xfff; 275 break; 276 277 case 0x28: /* MCSPI_MODULCTRL */ 278 if (value & (1 << 3)) /* SYSTEM_TEST */ 279 if (s->systest & (1 << 11)) { /* SSB */ 280 s->irqst |= 0x1777f; 281 omap_mcspi_interrupt_update(s); 282 } 283 s->control = value & 0xf; 284 break; 285 286 case 0x68: ch ++; 287 /* fall through */ 288 case 0x54: ch ++; 289 /* fall through */ 290 case 0x40: ch ++; 291 /* fall through */ 292 case 0x2c: /* MCSPI_CHCONF */ 293 if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ 294 omap_mcspi_dmarequest_update(s->ch + ch); 295 if (((value >> 12) & 3) == 3) /* TRM */ 296 fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); 297 if (((value >> 7) & 0x1f) < 3) /* WL */ 298 fprintf(stderr, "%s: invalid WL value (%" PRIx64 ")\n", 299 __FUNCTION__, (value >> 7) & 0x1f); 300 s->ch[ch].config = value & 0x7fffff; 301 break; 302 303 case 0x70: ch ++; 304 /* fall through */ 305 case 0x5c: ch ++; 306 /* fall through */ 307 case 0x48: ch ++; 308 /* fall through */ 309 case 0x34: /* MCSPI_CHCTRL */ 310 if (value & ~s->ch[ch].control & 1) { /* EN */ 311 s->ch[ch].control |= 1; 312 omap_mcspi_transfer_run(s, ch); 313 } else 314 s->ch[ch].control = value & 1; 315 break; 316 317 case 0x74: ch ++; 318 /* fall through */ 319 case 0x60: ch ++; 320 /* fall through */ 321 case 0x4c: ch ++; 322 /* fall through */ 323 case 0x38: /* MCSPI_TX */ 324 s->ch[ch].tx = value; 325 s->ch[ch].status &= ~(1 << 1); /* TXS */ 326 omap_mcspi_transfer_run(s, ch); 327 break; 328 329 default: 330 OMAP_BAD_REG(addr); 331 return; 332 } 333 } 334 335 static const MemoryRegionOps omap_mcspi_ops = { 336 .read = omap_mcspi_read, 337 .write = omap_mcspi_write, 338 .endianness = DEVICE_NATIVE_ENDIAN, 339 }; 340 341 struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, 342 qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) 343 { 344 struct omap_mcspi_s *s = (struct omap_mcspi_s *) 345 g_malloc0(sizeof(struct omap_mcspi_s)); 346 struct omap_mcspi_ch_s *ch = s->ch; 347 348 s->irq = irq; 349 s->chnum = chnum; 350 while (chnum --) { 351 ch->txdrq = *drq ++; 352 ch->rxdrq = *drq ++; 353 ch ++; 354 } 355 omap_mcspi_reset(s); 356 357 memory_region_init_io(&s->iomem, &omap_mcspi_ops, s, "omap.mcspi", 358 omap_l4_region_size(ta, 0)); 359 omap_l4_attach(ta, 0, &s->iomem); 360 361 return s; 362 } 363 364 void omap_mcspi_attach(struct omap_mcspi_s *s, 365 uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, 366 int chipselect) 367 { 368 if (chipselect < 0 || chipselect >= s->chnum) 369 hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); 370 371 s->ch[chipselect].txrx = txrx; 372 s->ch[chipselect].opaque = opaque; 373 } 374