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(cyclesLate);
55 struct DS* ds = context;
56 uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];
57 DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
58 uint8_t newValue = 0;
59
60 // TODO: /PENIRQ
61 if (ds->memory.spiBus.tscOffset > 0) {
62 // TODO: Make generic?
63 if (ds->memory.spiBus.tscOffset < 12) {
64 newValue = (ds->memory.spiBus.tscRegister & 0x1F) << 3;
65 ds->memory.spiBus.tscOffset = 12;
66 } else {
67 newValue = 0;
68 }
69 } else if (ds->memory.spiBus.tscControlByte) {
70 switch (DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte)) {
71 case DS_TSC_CHANNEL_TS_X:
72 mLOG(DS_SPI, STUB, "Unimplemented TSC channel X");
73 ds->memory.spiBus.tscRegister = 0;
74 break;
75 case DS_TSC_CHANNEL_TS_Y:
76 mLOG(DS_SPI, STUB, "Unimplemented TSC channel Y");
77 ds->memory.spiBus.tscRegister = 0xFFF;
78 break;
79 case DS_TSC_CHANNEL_TEMP_0:
80 case DS_TSC_CHANNEL_BATTERY_V:
81 case DS_TSC_CHANNEL_TS_Z1:
82 case DS_TSC_CHANNEL_TS_Z2:
83 case DS_TSC_CHANNEL_MIC:
84 case DS_TSC_CHANNEL_TEMP_1:
85 ds->memory.spiBus.tscRegister = 0;
86 mLOG(DS_SPI, STUB, "Unimplemented TSC channel %i", DSTSCControlByteGetChannel(ds->memory.spiBus.tscControlByte));
87 }
88 newValue = (ds->memory.spiBus.tscRegister >> 5) & 0x7F;
89 ds->memory.spiBus.tscOffset = 7;
90 }
91
92 if (DSTSCControlByteIsControl(oldValue)) {
93 ds->memory.spiBus.tscControlByte = oldValue;
94 ds->memory.spiBus.tscOffset = 0;
95 }
96
97 control = DSSPICNTClearBusy(control);
98 ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
99 ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
100 if (DSSPICNTIsDoIRQ(control)) {
101 DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
102 }
103}