src/ds/spi.c (view raw)
1/* Copyright (c) 2013-2017 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/spi.h>
7
8#include <mgba/internal/ds/ds.h>
9#include <mgba-util/vfs.h>
10
11mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI");
12
13static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
14static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
15
16void DSSPIReset(struct DS* ds) {
17 memset(&ds->memory.spiBus, 0, sizeof(ds->memory.spiBus));
18 ds->memory.spiBus.event.name = "DS SPI Event";
19 ds->memory.spiBus.event.context = ds;
20 ds->memory.spiBus.event.callback = _tscEvent;
21 ds->memory.spiBus.event.priority = 0x60;
22}
23
24DSSPICNT DSSPIWriteControl(struct DS* ds, uint16_t control) {
25 // TODO
26 if (!ds->memory.spiBus.holdEnabled) {
27 ds->memory.spiBus.tscControlByte = 0;
28 ds->memory.spiBus.firmCommand = 0;
29 }
30 ds->memory.spiBus.holdEnabled = DSSPICNTIsCSHold(control);
31 return control;
32}
33
34void DSSPIWrite(struct DS* ds, uint8_t datum) {
35 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
36 if (!DSSPICNTIsEnable(control)) {
37 return;
38 }
39 uint32_t baud = 19 - DSSPICNTGetBaud(control);
40 baud = DS_ARM7TDMI_FREQUENCY >> baud;
41 switch (DSSPICNTGetChipSelect(control)) {
42 case DS_SPI_DEV_TSC:
43 ds->memory.spiBus.event.callback = _tscEvent;
44 break;
45 case DS_SPI_DEV_FIRMWARE:
46 ds->memory.spiBus.event.callback = _firmwareEvent;
47 break;
48 case DS_SPI_DEV_POWERMAN:
49 default:
50 mLOG(DS_SPI, STUB, "Unimplemented data write: %04X:%02X", control, datum);
51 break;
52 }
53 control = DSSPICNTFillBusy(control);
54 mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.event);
55 mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.event, baud);
56 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
57}
58
59static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
60 UNUSED(timing);
61 UNUSED(cyclesLate);
62 struct DS* ds = context;
63 uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
64 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
65 uint8_t newValue = 0;
66
67 // TODO: /PENIRQ
68 if (ds->memory.spiBus.tscOffset > 0) {
69 // TODO: Make generic?
70 if (ds->memory.spiBus.tscOffset < 12) {
71 newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
72 ds->memory.spiBus.tscOffset = 12;
73 } else {
74 newValue = 0;
75 }
76 } else if (ds->memory.spiBus.tscControlByte) {
77 switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
78 case DS_TSC_CHANNEL_TS_X:
79 mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
80 ds->memory.spiBus.tscRegister = 0;
81 break;
82 case DS_TSC_CHANNEL_TS_Y:
83 mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
84 ds->memory.spiBus.tscRegister = 0xFFF;
85 break;
86 case DS_TSC_CHANNEL_TEMP_0:
87 case DS_TSC_CHANNEL_BATTERY_V:
88 case DS_TSC_CHANNEL_TS_Z1:
89 case DS_TSC_CHANNEL_TS_Z2:
90 case DS_TSC_CHANNEL_MIC:
91 case DS_TSC_CHANNEL_TEMP_1:
92 ds->memory.spiBus.tscRegister = 0;
93 mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
94 }
95 newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
96 ds->memory.spiBus.tscOffset = 7;
97 }
98
99 if (DSTSCControlByteIsControl(oldValue)) {
100 ds->memory.spiBus.tscControlByte = oldValue;
101 ds->memory.spiBus.tscOffset = 0;
102 }
103
104 control = DSSPICNTClearBusy(control);
105 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
106 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
107 if (DSSPICNTIsDoIRQ(control)) {
108 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
109 }
110}
111
112static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
113 UNUSED(timing);
114 UNUSED(cyclesLate);
115 struct DS* ds = context;
116 uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
117 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
118 uint8_t newValue = 0;
119
120 if (!ds->memory.spiBus.firmCommand) {
121 ds->memory.spiBus.firmCommand = oldValue;
122 ds->memory.spiBus.firmAddress = 0;
123 ds->memory.spiBus.firmAddressingRemaining = 24;
124 } else if (ds->memory.spiBus.firmAddressingRemaining) {
125 ds->memory.spiBus.firmAddress <<= 8;
126 ds->memory.spiBus.firmAddress |= oldValue;
127 ds->memory.spiBus.firmAddressingRemaining -= 8;
128 ds->firmwareVf->seek(ds->firmwareVf, ds->memory.spiBus.firmAddress, SEEK_SET);
129 } else {
130 switch (ds->memory.spiBus.firmCommand) {
131 case 0x02: // WR
132 ds->firmwareVf->write(ds->firmwareVf, &oldValue, 1);
133 ++ds->memory.spiBus.firmAddress;
134 break;
135 case 0x03: // RD
136 ds->firmwareVf->read(ds->firmwareVf, &newValue, 1);
137 ++ds->memory.spiBus.firmAddress;
138 case 0x04: // WRDI
139 ds->memory.spiBus.firmStatusReg &= ~2;
140 break;
141 case 0x05: // RDSR
142 newValue = ds->memory.spiBus.firmStatusReg;
143 break;
144 case 0x06: // WREN
145 ds->memory.spiBus.firmStatusReg |= 2;
146 break;
147 default:
148 mLOG(DS_SPI, STUB, "Unimplemented Firmware write: %04X:%02X:%02X", control, ds->memory.spiBus.firmCommand, newValue);
149 break;
150 }
151 }
152
153 control = DSSPICNTClearBusy(control);
154 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
155 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
156 if (DSSPICNTIsDoIRQ(control)) {
157 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
158 }
159}