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", "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 if (ds->memory.spiBus.tscOffset > 0) {
68 // TODO: Make generic?
69 if (ds->memory.spiBus.tscOffset < 12) {
70 newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
71 ds->memory.spiBus.tscOffset = 12;
72 } else {
73 newValue = 0;
74 }
75 } else if (ds->memory.spiBus.tscControlByte) {
76 switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
77 // TODO: Calibrate from firmware
78 case DS_TSC_CHANNEL_TS_X:
79 if (*ds->touchSource) {
80 ds->memory.spiBus.tscRegister = (*ds->cursorSourceX * 0xDD0 / DS_VIDEO_HORIZONTAL_PIXELS) + 0x100;
81 } else {
82 ds->memory.spiBus.tscRegister = 0;
83 }
84 break;
85 case DS_TSC_CHANNEL_TS_Y:
86 if (*ds->touchSource) {
87 ds->memory.spiBus.tscRegister = (*ds->cursorSourceY * 0xE70 / DS_VIDEO_VERTICAL_PIXELS) + 0x0B0;
88 } else {
89 ds->memory.spiBus.tscRegister = 0xFFF;
90 }
91 break;
92 case DS_TSC_CHANNEL_TEMP_0:
93 if (*ds->touchSource) {
94 ds->memory.io7[DS7_REG_EXTKEYIN >> 1] &= ~0x040;
95 } else {
96 ds->memory.io7[DS7_REG_EXTKEYIN >> 1] |= 0x040;
97 }
98 break;
99 case DS_TSC_CHANNEL_BATTERY_V:
100 case DS_TSC_CHANNEL_TS_Z1:
101 case DS_TSC_CHANNEL_TS_Z2:
102 case DS_TSC_CHANNEL_MIC:
103 case DS_TSC_CHANNEL_TEMP_1:
104 ds->memory.spiBus.tscRegister = 0;
105 mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
106 }
107 newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
108 ds->memory.spiBus.tscOffset = 7;
109 }
110
111 if (DSTSCControlByteIsControl(oldValue)) {
112 ds->memory.spiBus.tscControlByte = oldValue;
113 ds->memory.spiBus.tscOffset = 0;
114 }
115
116 control = DSSPICNTClearBusy(control);
117 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
118 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
119 if (DSSPICNTIsDoIRQ(control)) {
120 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
121 }
122}
123
124static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
125 UNUSED(timing);
126 UNUSED(cyclesLate);
127 struct DS* ds = context;
128 uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
129 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
130 uint8_t newValue = 0;
131
132 if (!ds->memory.spiBus.firmCommand) {
133 ds->memory.spiBus.firmCommand = oldValue;
134 ds->memory.spiBus.firmAddress = 0;
135 ds->memory.spiBus.firmAddressingRemaining = 24;
136 } else if (ds->memory.spiBus.firmAddressingRemaining) {
137 ds->memory.spiBus.firmAddress <<= 8;
138 ds->memory.spiBus.firmAddress |= oldValue;
139 ds->memory.spiBus.firmAddressingRemaining -= 8;
140 ds->firmwareVf->seek(ds->firmwareVf, ds->memory.spiBus.firmAddress, SEEK_SET);
141 } else {
142 switch (ds->memory.spiBus.firmCommand) {
143 case 0x02: // WR
144 ds->firmwareVf->write(ds->firmwareVf, &oldValue, 1);
145 ++ds->memory.spiBus.firmAddress;
146 break;
147 case 0x03: // RD
148 ds->firmwareVf->read(ds->firmwareVf, &newValue, 1);
149 ++ds->memory.spiBus.firmAddress;
150 case 0x04: // WRDI
151 ds->memory.spiBus.firmStatusReg &= ~2;
152 break;
153 case 0x05: // RDSR
154 newValue = ds->memory.spiBus.firmStatusReg;
155 break;
156 case 0x06: // WREN
157 ds->memory.spiBus.firmStatusReg |= 2;
158 break;
159 default:
160 mLOG(DS_SPI, STUB, "Unimplemented Firmware write: %04X:%02X:%02X", control, ds->memory.spiBus.firmCommand, newValue);
161 break;
162 }
163 }
164
165 control = DSSPICNTClearBusy(control);
166 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
167 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
168 if (DSSPICNTIsDoIRQ(control)) {
169 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
170 }
171}