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
10mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI");
11
12static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
13
14void DSSPIReset(struct DS* ds) {
15 memset(&ds->memory.spiBus, 0, sizeof(ds->memory.spiBus));
16 ds->memory.spiBus.tscEvent.name = "DS SPI TSC";
17 ds->memory.spiBus.tscEvent.context = ds;
18 ds->memory.spiBus.tscEvent.callback = _tscEvent;
19 ds->memory.spiBus.tscEvent.priority = 0x60;
20}
21
22DSSPICNT DSSPIWriteControl(struct DS* ds, uint16_t control) {
23 // TODO
24 if (!ds->memory.spiBus.holdEnabled) {
25 ds->memory.spiBus.tscControlByte = 0;
26 }
27 ds->memory.spiBus.holdEnabled = DSSPICNTIsCSHold(control);
28 return control;
29}
30
31void DSSPIWrite(struct DS* ds, uint8_t datum) {
32 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
33 if (!DSSPICNTIsEnable(control)) {
34 return;
35 }
36 uint32_t baud = 19 - DSSPICNTGetBaud(control);
37 baud = DS_ARM7TDMI_FREQUENCY >> baud;
38 switch (DSSPICNTGetChipSelect(control)) {
39 case DS_SPI_DEV_TSC:
40 control = DSSPICNTFillBusy(control);
41 mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent);
42 mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent, baud);
43 break;
44 case DS_SPI_DEV_POWERMAN:
45 case DS_SPI_DEV_FIRMWARE:
46 default:
47 mLOG(DS_SPI, STUB, "Unimplemented data write: %04X:%02X", control, datum);
48 break;
49 }
50 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
51}
52
53static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
54 UNUSED(timing);
55 UNUSED(cyclesLate);
56 struct DS* ds = context;
57 uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
58 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
59 uint8_t newValue = 0;
60
61 // TODO: /PENIRQ
62 if (ds->memory.spiBus.tscOffset > 0) {
63 // TODO: Make generic?
64 if (ds->memory.spiBus.tscOffset < 12) {
65 newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
66 ds->memory.spiBus.tscOffset = 12;
67 } else {
68 newValue = 0;
69 }
70 } else if (ds->memory.spiBus.tscControlByte) {
71 switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
72 case DS_TSC_CHANNEL_TS_X:
73 mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
74 ds->memory.spiBus.tscRegister = 0;
75 break;
76 case DS_TSC_CHANNEL_TS_Y:
77 mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
78 ds->memory.spiBus.tscRegister = 0xFFF;
79 break;
80 case DS_TSC_CHANNEL_TEMP_0:
81 case DS_TSC_CHANNEL_BATTERY_V:
82 case DS_TSC_CHANNEL_TS_Z1:
83 case DS_TSC_CHANNEL_TS_Z2:
84 case DS_TSC_CHANNEL_MIC:
85 case DS_TSC_CHANNEL_TEMP_1:
86 ds->memory.spiBus.tscRegister = 0;
87 mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
88 }
89 newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
90 ds->memory.spiBus.tscOffset = 7;
91 }
92
93 if (DSTSCControlByteIsControl(oldValue)) {
94 ds->memory.spiBus.tscControlByte = oldValue;
95 ds->memory.spiBus.tscOffset = 0;
96 }
97
98 control = DSSPICNTClearBusy(control);
99 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
100 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
101 if (DSSPICNTIsDoIRQ(control)) {
102 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
103 }
104}