|
| 1 | +import { RP2040 } from '../rp2040'; |
| 2 | +import { FIFO } from '../utils/fifo'; |
| 3 | +import { BasePeripheral, Peripheral } from './peripheral'; |
| 4 | + |
| 5 | +const SSPCR0 = 0x000; // Control register 0, SSPCR0 on page 3-4 |
| 6 | +const SSPCR1 = 0x004; // Control register 1, SSPCR1 on page 3-5 |
| 7 | +const SSPDR = 0x008; // Data register, SSPDR on page 3-6 |
| 8 | +const SSPSR = 0x00c; // Status register, SSPSR on page 3-7 |
| 9 | +const SSPCPSR = 0x010; // Clock prescale register, SSPCPSR on page 3-8 |
| 10 | +const SSPIMSC = 0x014; // Interrupt mask set or clear register, SSPIMSC on page 3-9 |
| 11 | +const SSPRIS = 0x018; // Raw interrupt status register, SSPRIS on page 3-10 |
| 12 | +const SSPMIS = 0x01c; // Masked interrupt status register, SSPMIS on page 3-11 |
| 13 | +const SSPICR = 0x020; // Interrupt clear register, SSPICR on page 3-11 |
| 14 | +const SSPDMACR = 0x024; // DMA control register, SSPDMACR on page 3-12 |
| 15 | +const SSPPERIPHID0 = 0xfe0; // Peripheral identification registers, SSPPeriphID0-3 on page 3-13 |
| 16 | +const SSPPERIPHID1 = 0xfe4; // Peripheral identification registers, SSPPeriphID0-3 on page 3-13 |
| 17 | +const SSPPERIPHID2 = 0xfe8; // Peripheral identification registers, SSPPeriphID0-3 on page 3-13 |
| 18 | +const SSPPERIPHID3 = 0xfec; // Peripheral identification registers, SSPPeriphID0-3 on page 3-13 |
| 19 | +const SSPPCELLID0 = 0xff0; // PrimeCell identification registers, SSPPCellID0-3 on page 3-16 |
| 20 | +const SSPPCELLID1 = 0xff4; // PrimeCell identification registers, SSPPCellID0-3 on page 3-16 |
| 21 | +const SSPPCELLID2 = 0xff8; // PrimeCell identification registers, SSPPCellID0-3 on page 3-16 |
| 22 | +const SSPPCELLID3 = 0xffc; // PrimeCell identification registers, SSPPCellID0-3 on page 3-16 |
| 23 | + |
| 24 | +// SSPCR0 bits: |
| 25 | +const SCR_MASK = 0xff; |
| 26 | +const SCR_SHIFT = 8; |
| 27 | +const SPH = 1 << 7; |
| 28 | +const SPO = 1 << 6; |
| 29 | +const FRF_MASK = 0x3; |
| 30 | +const FRF_SHIFT = 4; |
| 31 | +const DSS_MASK = 0xf; |
| 32 | +const DSS_SHIFT = 0; |
| 33 | + |
| 34 | +// SSPCR1 bits: |
| 35 | +const SOD = 1 << 3; |
| 36 | +const MS = 1 << 2; |
| 37 | +const SSE = 1 << 1; |
| 38 | +const LBM = 1 << 0; |
| 39 | + |
| 40 | +// SSPSR bits: |
| 41 | +const BSY = 1 << 4; |
| 42 | +const RFF = 1 << 3; |
| 43 | +const RNE = 1 << 2; |
| 44 | +const TNF = 1 << 1; |
| 45 | +const TFE = 1 << 0; |
| 46 | + |
| 47 | +// SSPCPSR bits: |
| 48 | +const CPSDVSR_MASK = 0xfe; |
| 49 | +const CPSDVSR_SHIFT = 0; |
| 50 | + |
| 51 | +// SSPDMACR bits: |
| 52 | +const TXDMAE = 1 << 1; |
| 53 | +const RXDMAE = 1 << 0; |
| 54 | + |
| 55 | +// Interrupts: |
| 56 | +const SSPTXINTR = 1 << 3; |
| 57 | +const SSPRXINTR = 1 << 2; |
| 58 | +const SSPRTINTR = 1 << 1; |
| 59 | +const SSPRORINTR = 1 << 0; |
| 60 | + |
| 61 | +export class RPSPI extends BasePeripheral implements Peripheral { |
| 62 | + readonly rxFIFO = new FIFO(8); |
| 63 | + readonly txFIFO = new FIFO(8); |
| 64 | + |
| 65 | + // User provided callbacks |
| 66 | + onTransmit: (value: number) => void = () => this.completeTransmit(0); |
| 67 | + |
| 68 | + private busy = false; |
| 69 | + private control0 = 0; |
| 70 | + private control1 = 0; |
| 71 | + private dmaControl = 0; |
| 72 | + private clockDivisor = 0; |
| 73 | + private intRaw = 0; |
| 74 | + private intEnable = 0; |
| 75 | + |
| 76 | + get intStatus() { |
| 77 | + return this.intRaw & this.intEnable; |
| 78 | + } |
| 79 | + |
| 80 | + get enabled() { |
| 81 | + return !!(this.control1 & SSE); |
| 82 | + } |
| 83 | + |
| 84 | + /** Data size in bits: 4 to 16 bits */ |
| 85 | + get dataBits() { |
| 86 | + return ((this.control0 >> DSS_SHIFT) & DSS_MASK) + 1; |
| 87 | + } |
| 88 | + |
| 89 | + get masterMode() { |
| 90 | + return !(this.control0 & MS); |
| 91 | + } |
| 92 | + |
| 93 | + get spiMode() { |
| 94 | + const cpol = this.control0 & SPO; |
| 95 | + const cpha = this.control0 & SPH; |
| 96 | + return cpol ? (cpha ? 2 : 3) : cpha ? 1 : 0; |
| 97 | + } |
| 98 | + |
| 99 | + get clockFrequency() { |
| 100 | + if (!this.clockDivisor) { |
| 101 | + return 0; |
| 102 | + } |
| 103 | + |
| 104 | + const scr = (this.control0 >> SCR_SHIFT) & SCR_MASK; |
| 105 | + return this.rp2040.clkPeri / (this.clockDivisor * (1 + scr)); |
| 106 | + } |
| 107 | + |
| 108 | + constructor(rp2040: RP2040, name: string, readonly irq: number) { |
| 109 | + super(rp2040, name); |
| 110 | + } |
| 111 | + |
| 112 | + private doTX() { |
| 113 | + if (!this.busy && !this.txFIFO.empty) { |
| 114 | + const value = this.txFIFO.pull(); |
| 115 | + this.onTransmit(value); |
| 116 | + this.busy = true; |
| 117 | + this.fifosUpdated(); |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + completeTransmit(rxValue: number) { |
| 122 | + this.busy = false; |
| 123 | + if (!this.rxFIFO.full) { |
| 124 | + this.rxFIFO.push(rxValue); |
| 125 | + } else { |
| 126 | + this.intRaw |= SSPRORINTR; |
| 127 | + } |
| 128 | + this.fifosUpdated(); |
| 129 | + this.doTX(); |
| 130 | + } |
| 131 | + |
| 132 | + checkInterrupts() { |
| 133 | + this.rp2040.setInterrupt(this.irq, !!this.intStatus); |
| 134 | + } |
| 135 | + |
| 136 | + private fifosUpdated() { |
| 137 | + const prevStatus = this.intStatus; |
| 138 | + if (this.txFIFO.itemCount <= this.txFIFO.size / 2) { |
| 139 | + this.intRaw |= SSPTXINTR; |
| 140 | + } else { |
| 141 | + this.intRaw &= ~SSPTXINTR; |
| 142 | + } |
| 143 | + if (this.rxFIFO.itemCount >= this.rxFIFO.size / 2) { |
| 144 | + this.intRaw |= SSPRXINTR; |
| 145 | + } else { |
| 146 | + this.intRaw &= ~SSPRXINTR; |
| 147 | + } |
| 148 | + if (this.intStatus !== prevStatus) { |
| 149 | + this.checkInterrupts(); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + readUint32(offset: number) { |
| 154 | + switch (offset) { |
| 155 | + case SSPCR0: |
| 156 | + return this.control0; |
| 157 | + case SSPCR1: |
| 158 | + return this.control1; |
| 159 | + case SSPDR: |
| 160 | + if (!this.rxFIFO.empty) { |
| 161 | + const value = this.rxFIFO.pull(); |
| 162 | + this.fifosUpdated(); |
| 163 | + return value; |
| 164 | + } |
| 165 | + return 0; |
| 166 | + case SSPSR: |
| 167 | + return ( |
| 168 | + (this.busy || !this.txFIFO.empty ? BSY : 0) | |
| 169 | + (this.rxFIFO.full ? RFF : 0) | |
| 170 | + (!this.rxFIFO.empty ? RNE : 0) | |
| 171 | + (!this.txFIFO.full ? TNF : 0) | |
| 172 | + (this.txFIFO.empty ? TFE : 0) |
| 173 | + ); |
| 174 | + case SSPCPSR: |
| 175 | + return this.clockDivisor; |
| 176 | + case SSPIMSC: |
| 177 | + return this.intEnable; |
| 178 | + case SSPRIS: |
| 179 | + return this.intRaw; |
| 180 | + case SSPMIS: |
| 181 | + return this.intStatus; |
| 182 | + case SSPDMACR: |
| 183 | + return this.dmaControl; |
| 184 | + case SSPPERIPHID0: |
| 185 | + return 0x22; |
| 186 | + case SSPPERIPHID1: |
| 187 | + return 0x10; |
| 188 | + case SSPPERIPHID2: |
| 189 | + return 0x34; |
| 190 | + case SSPPERIPHID3: |
| 191 | + return 0x00; |
| 192 | + case SSPPCELLID0: |
| 193 | + return 0x0d; |
| 194 | + case SSPPCELLID1: |
| 195 | + return 0xf0; |
| 196 | + case SSPPCELLID2: |
| 197 | + return 0x05; |
| 198 | + case SSPPCELLID3: |
| 199 | + return 0xb1; |
| 200 | + } |
| 201 | + return super.readUint32(offset); |
| 202 | + } |
| 203 | + |
| 204 | + writeUint32(offset: number, value: number) { |
| 205 | + switch (offset) { |
| 206 | + case SSPCR0: |
| 207 | + this.control0 = value; |
| 208 | + return; |
| 209 | + case SSPCR1: |
| 210 | + this.control1 = value; |
| 211 | + return; |
| 212 | + case SSPDR: |
| 213 | + if (!this.txFIFO.full) { |
| 214 | + this.txFIFO.push(value); |
| 215 | + this.doTX(); |
| 216 | + this.fifosUpdated(); |
| 217 | + } |
| 218 | + return; |
| 219 | + case SSPCPSR: |
| 220 | + this.clockDivisor = value & CPSDVSR_MASK; |
| 221 | + return; |
| 222 | + case SSPIMSC: |
| 223 | + this.intEnable = value; |
| 224 | + this.checkInterrupts(); |
| 225 | + return; |
| 226 | + case SSPDMACR: |
| 227 | + this.dmaControl = value; |
| 228 | + return; |
| 229 | + case SSPICR: |
| 230 | + this.intRaw &= ~(value & (SSPRTINTR | SSPRORINTR)); |
| 231 | + this.checkInterrupts(); |
| 232 | + return; |
| 233 | + default: |
| 234 | + super.writeUint32(offset, value); |
| 235 | + } |
| 236 | + } |
| 237 | +} |
0 commit comments