mirror of
https://github.com/akuker/RASCSI.git
synced 2026-10-25 12:02:59 +00:00
Move GPIO mappings into a single module and select a board profile at process startup instead of at compile time: Build every GPIO-facing utility for every supported profile. At the same time, we remove support for the historical AIBOM board type since it's the only one using positive logic, the other three variants share the same electrical behavior. Run piscsi, scsimon, scsiloop, scsidump, or sasidump with -C=STANDARD, FULLSPEC, or GAMERNIUM. FULLSPEC remains the default. To select a profile for the systemd service, set PISCSI_CONNECTION_TYPE and restart the service.
902 lines
23 KiB
C++
902 lines
23 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi
|
|
//
|
|
// Powered by XM6 TypeG Technology.
|
|
// Copyright (C) 2016-2020 GIMONS
|
|
//
|
|
// [ GPIO-SCSI bus ]
|
|
//
|
|
// Raspberry Pi 4:
|
|
// https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
|
|
// Raspberry Pi Zero:
|
|
// https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <spdlog/spdlog.h>
|
|
#include "hal/gpiobus_raspberry.h"
|
|
#include "hal/gpiobus.h"
|
|
#include "hal/systimer.h"
|
|
#include <fcntl.h>
|
|
#include <map>
|
|
#include <cstring>
|
|
#ifdef __linux__
|
|
#include <sys/epoll.h>
|
|
#endif
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// imported from bcm_host.c
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
uint32_t GPIOBUS_Raspberry::get_dt_ranges(const char *filename, uint32_t offset)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
uint32_t address = ~0;
|
|
if (FILE *fp = fopen(filename, "rb"); fp) {
|
|
fseek(fp, offset, SEEK_SET);
|
|
if (array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
|
|
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
|
|
}
|
|
fclose(fp);
|
|
}
|
|
return address;
|
|
}
|
|
|
|
uint32_t GPIOBUS_Raspberry::bcm_host_get_peripheral_address()
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
#ifdef __linux__
|
|
uint32_t address = get_dt_ranges("/proc/device-tree/soc/ranges", 4);
|
|
if (address == 0) {
|
|
address = get_dt_ranges("/proc/device-tree/soc/ranges", 8);
|
|
}
|
|
address = (address == (uint32_t)~0) ? 0x20000000 : address;
|
|
return address;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::Init(mode_e mode)
|
|
{
|
|
GPIOBUS::Init(mode);
|
|
MakeSignalTable();
|
|
|
|
#if !defined(__linux__) || defined(__x86_64__) || defined(__X86__)
|
|
(void)baseaddr;
|
|
level = new uint32_t();
|
|
return true;
|
|
#else
|
|
int i;
|
|
#ifdef USE_SEL_EVENT_ENABLE
|
|
epoll_event ev = {};
|
|
#endif
|
|
|
|
// Get the base address
|
|
baseaddr = (uint32_t)bcm_host_get_peripheral_address();
|
|
|
|
// Open /dev/mem
|
|
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
|
if (fd == -1) {
|
|
spdlog::error("Error: Unable to open /dev/mem. Are you running as root?");
|
|
return false;
|
|
}
|
|
|
|
// Map peripheral region memory
|
|
void *map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
|
|
if (map == MAP_FAILED) {
|
|
spdlog::error("Error: Unable to map memory: "+ string(strerror(errno)));
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
// Determine the type of raspberry pi from the base address
|
|
if (baseaddr == 0xfe000000) {
|
|
rpitype = 4;
|
|
} else if (baseaddr == 0x3f000000) {
|
|
rpitype = 2;
|
|
} else {
|
|
rpitype = 1;
|
|
}
|
|
|
|
// GPIO
|
|
gpio = (uint32_t *)map;
|
|
gpio += GPIO_OFFSET / sizeof(uint32_t);
|
|
level = &gpio[GPIO_LEV_0];
|
|
|
|
// PADS
|
|
pads = (uint32_t *)map;
|
|
pads += PADS_OFFSET / sizeof(uint32_t);
|
|
|
|
// System timer
|
|
SysTimer::Init();
|
|
|
|
// Interrupt controller
|
|
irpctl = (uint32_t *)map;
|
|
irpctl += IRPT_OFFSET / sizeof(uint32_t);
|
|
|
|
// Quad-A7 control
|
|
qa7regs = (uint32_t *)map;
|
|
qa7regs += QA7_OFFSET / sizeof(uint32_t);
|
|
|
|
// Map GIC memory
|
|
if (rpitype == 4) {
|
|
map = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE);
|
|
if (map == MAP_FAILED) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
gicd = (uint32_t *)map;
|
|
gicc = (uint32_t *)map;
|
|
gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t);
|
|
} else {
|
|
gicd = NULL;
|
|
gicc = NULL;
|
|
}
|
|
close(fd);
|
|
|
|
// Set Drive Strength to 16mA
|
|
DrvConfig(7);
|
|
|
|
// All supported profiles use SCSI logical signal control.
|
|
const int pullmode = GPIO_PULLNONE;
|
|
|
|
// Initialize all signals
|
|
for (i = 0; SignalTable[i] >= 0; i++) {
|
|
int j = SignalTable[i];
|
|
PinSetSignal(j, OFF);
|
|
PinConfig(j, GPIO_INPUT);
|
|
PullConfig(j, pullmode);
|
|
}
|
|
|
|
// Set control signals
|
|
PinSetSignal(PIN_ACT, OFF);
|
|
PinSetSignal(PIN_TAD, OFF);
|
|
PinSetSignal(PIN_IND, OFF);
|
|
PinSetSignal(PIN_DTD, OFF);
|
|
PinConfig(PIN_ACT, GPIO_OUTPUT);
|
|
PinConfig(PIN_TAD, GPIO_OUTPUT);
|
|
PinConfig(PIN_IND, GPIO_OUTPUT);
|
|
PinConfig(PIN_DTD, GPIO_OUTPUT);
|
|
|
|
// Set the ENABLE signal
|
|
// This is used to show that the application is running
|
|
PinSetSignal(PIN_ENB, ENB_OFF);
|
|
PinConfig(PIN_ENB, GPIO_OUTPUT);
|
|
|
|
// GPIO Function Select (GPFSEL) registers backup
|
|
gpfsel[0] = gpio[GPIO_FSEL_0];
|
|
gpfsel[1] = gpio[GPIO_FSEL_1];
|
|
gpfsel[2] = gpio[GPIO_FSEL_2];
|
|
gpfsel[3] = gpio[GPIO_FSEL_3];
|
|
|
|
// Initialize SEL signal interrupt
|
|
#ifdef USE_SEL_EVENT_ENABLE
|
|
// GPIO chip open
|
|
fd = open("/dev/gpiochip0", 0);
|
|
if (fd == -1) {
|
|
spdlog::error("Unable to open /dev/gpiochip0. If PiSCSI is running, please shut it down first.");
|
|
return false;
|
|
}
|
|
|
|
// Event request setting
|
|
strcpy(selevreq.consumer_label, "PiSCSI");
|
|
selevreq.lineoffset = PIN_SEL;
|
|
selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT;
|
|
selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE;
|
|
|
|
// Get event request
|
|
if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
|
|
spdlog::error("Unable to register event request. If PiSCSI is running, please shut it down first.");
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
// Close GPIO chip file handle
|
|
close(fd);
|
|
|
|
// epoll initialization
|
|
epfd = epoll_create(1);
|
|
ev.events = EPOLLIN | EPOLLPRI;
|
|
ev.data.fd = selevreq.fd;
|
|
epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev);
|
|
#else
|
|
// Edge detection setting
|
|
gpio[GPIO_AFEN_0] = 1 << PIN_SEL;
|
|
|
|
// Clear event - GPIO Pin Event Detect Status
|
|
gpio[GPIO_EDS_0] = 1 << PIN_SEL;
|
|
|
|
// Register interrupt handler
|
|
setIrqFuncAddress(IrqHandler);
|
|
|
|
// GPIO interrupt setting
|
|
if (rpitype == 4) {
|
|
// GIC Invalid
|
|
gicd[GICD_CTLR] = 0;
|
|
|
|
// Route all interupts to core 0
|
|
for (i = 0; i < 8; i++) {
|
|
gicd[GICD_ICENABLER0 + i] = 0xffffffff;
|
|
gicd[GICD_ICPENDR0 + i] = 0xffffffff;
|
|
gicd[GICD_ICACTIVER0 + i] = 0xffffffff;
|
|
}
|
|
for (i = 0; i < 64; i++) {
|
|
gicd[GICD_IPRIORITYR0 + i] = 0xa0a0a0a0;
|
|
gicd[GICD_ITARGETSR0 + i] = 0x01010101;
|
|
}
|
|
|
|
// Set all interrupts as level triggers
|
|
for (i = 0; i < 16; i++) {
|
|
gicd[GICD_ICFGR0 + i] = 0;
|
|
}
|
|
|
|
// GIC Invalid
|
|
gicd[GICD_CTLR] = 1;
|
|
|
|
// Enable CPU interface for core 0
|
|
gicc[GICC_PMR] = 0xf0;
|
|
gicc[GICC_CTLR] = 1;
|
|
|
|
// Enable interrupts
|
|
gicd[GICD_ISENABLER0 + (GIC_GPIO_IRQ / 32)] = 1 << (GIC_GPIO_IRQ % 32);
|
|
} else {
|
|
// Enable interrupts
|
|
irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32));
|
|
}
|
|
#endif // USE_SEL_EVENT_ENABLE
|
|
|
|
// Create work table
|
|
MakeTable();
|
|
|
|
// Finally, enable ENABLE
|
|
// Show the user that this app is running
|
|
SetControl(PIN_ENB, ENB_ON);
|
|
|
|
return true;
|
|
#endif // ifdef __x86_64__ || __X86__
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::Cleanup()
|
|
{
|
|
#if !defined(__linux__) || defined(__x86_64__) || defined(__X86__)
|
|
return;
|
|
#else
|
|
// Release SEL signal interrupt
|
|
#ifdef USE_SEL_EVENT_ENABLE
|
|
close(selevreq.fd);
|
|
#endif // USE_SEL_EVENT_ENABLE
|
|
|
|
// Set control signals
|
|
PinSetSignal(PIN_ENB, OFF);
|
|
PinSetSignal(PIN_ACT, OFF);
|
|
PinSetSignal(PIN_TAD, OFF);
|
|
PinSetSignal(PIN_IND, OFF);
|
|
PinSetSignal(PIN_DTD, OFF);
|
|
PinConfig(PIN_ACT, GPIO_INPUT);
|
|
PinConfig(PIN_TAD, GPIO_INPUT);
|
|
PinConfig(PIN_IND, GPIO_INPUT);
|
|
PinConfig(PIN_DTD, GPIO_INPUT);
|
|
|
|
// Initialize all signals
|
|
for (int i = 0; SignalTable[i] >= 0; i++) {
|
|
int pin = SignalTable[i];
|
|
PinSetSignal(pin, OFF);
|
|
PinConfig(pin, GPIO_INPUT);
|
|
PullConfig(pin, GPIO_PULLNONE);
|
|
}
|
|
|
|
// Set drive strength back to 8mA
|
|
DrvConfig(3);
|
|
#endif // ifdef __x86_64__ || __X86__
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::Reset()
|
|
{
|
|
#if !defined(__linux__) || defined(__x86_64__) || defined(__X86__)
|
|
return;
|
|
#else
|
|
int i;
|
|
int j;
|
|
|
|
// Turn off active signal
|
|
SetControl(PIN_ACT, ACT_OFF);
|
|
|
|
// Set all signals to off
|
|
for (i = 0;; i++) {
|
|
j = SignalTable[i];
|
|
if (j < 0) {
|
|
break;
|
|
}
|
|
|
|
SetSignal(j, OFF);
|
|
}
|
|
|
|
if (actmode == mode_e::TARGET) {
|
|
// Target mode
|
|
|
|
// Set target signal to input
|
|
SetControl(PIN_TAD, TAD_IN);
|
|
SetMode(PIN_BSY, IN);
|
|
SetMode(PIN_MSG, IN);
|
|
SetMode(PIN_CD, IN);
|
|
SetMode(PIN_REQ, IN);
|
|
SetMode(PIN_IO, IN);
|
|
|
|
// Set the initiator signal to input
|
|
SetControl(PIN_IND, IND_IN);
|
|
SetMode(PIN_SEL, IN);
|
|
SetMode(PIN_ATN, IN);
|
|
SetMode(PIN_ACK, IN);
|
|
SetMode(PIN_RST, IN);
|
|
|
|
// Set data bus signals to input
|
|
SetControl(PIN_DTD, DTD_IN);
|
|
SetMode(PIN_DT0, IN);
|
|
SetMode(PIN_DT1, IN);
|
|
SetMode(PIN_DT2, IN);
|
|
SetMode(PIN_DT3, IN);
|
|
SetMode(PIN_DT4, IN);
|
|
SetMode(PIN_DT5, IN);
|
|
SetMode(PIN_DT6, IN);
|
|
SetMode(PIN_DT7, IN);
|
|
SetMode(PIN_DP, IN);
|
|
} else {
|
|
// Initiator mode
|
|
|
|
// Set target signal to input
|
|
SetControl(PIN_TAD, TAD_IN);
|
|
SetMode(PIN_BSY, IN);
|
|
SetMode(PIN_MSG, IN);
|
|
SetMode(PIN_CD, IN);
|
|
SetMode(PIN_REQ, IN);
|
|
SetMode(PIN_IO, IN);
|
|
|
|
// Set the initiator signal to output
|
|
SetControl(PIN_IND, IND_OUT);
|
|
SetMode(PIN_SEL, OUT);
|
|
SetMode(PIN_ATN, OUT);
|
|
SetMode(PIN_ACK, OUT);
|
|
SetMode(PIN_RST, OUT);
|
|
|
|
// Set the data bus signals to output
|
|
SetControl(PIN_DTD, DTD_OUT);
|
|
SetMode(PIN_DT0, OUT);
|
|
SetMode(PIN_DT1, OUT);
|
|
SetMode(PIN_DT2, OUT);
|
|
SetMode(PIN_DT3, OUT);
|
|
SetMode(PIN_DT4, OUT);
|
|
SetMode(PIN_DT5, OUT);
|
|
SetMode(PIN_DT6, OUT);
|
|
SetMode(PIN_DT7, OUT);
|
|
SetMode(PIN_DP, OUT);
|
|
}
|
|
|
|
// Initialize all signals
|
|
signals = 0;
|
|
#endif // ifdef __x86_64__ || __X86__
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetENB(bool ast)
|
|
{
|
|
PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetBSY() const
|
|
{
|
|
return GetSignal(PIN_BSY);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetBSY(bool ast)
|
|
{
|
|
// Set BSY signal
|
|
SetSignal(PIN_BSY, ast);
|
|
|
|
if (ast) {
|
|
// Turn on ACTIVE signal
|
|
SetControl(PIN_ACT, ACT_ON);
|
|
|
|
// Set Target signal to output
|
|
SetControl(PIN_TAD, TAD_OUT);
|
|
|
|
SetMode(PIN_BSY, OUT);
|
|
SetMode(PIN_MSG, OUT);
|
|
SetMode(PIN_CD, OUT);
|
|
SetMode(PIN_REQ, OUT);
|
|
SetMode(PIN_IO, OUT);
|
|
} else {
|
|
// Turn off the ACTIVE signal
|
|
SetControl(PIN_ACT, ACT_OFF);
|
|
|
|
// Set the target signal to input
|
|
SetControl(PIN_TAD, TAD_IN);
|
|
|
|
SetMode(PIN_BSY, IN);
|
|
SetMode(PIN_MSG, IN);
|
|
SetMode(PIN_CD, IN);
|
|
SetMode(PIN_REQ, IN);
|
|
SetMode(PIN_IO, IN);
|
|
}
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetSEL() const
|
|
{
|
|
return GetSignal(PIN_SEL);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetSEL(bool ast)
|
|
{
|
|
if (actmode == mode_e::INITIATOR && ast) {
|
|
// Turn on ACTIVE signal
|
|
SetControl(PIN_ACT, ACT_ON);
|
|
}
|
|
|
|
// Set SEL signal
|
|
SetSignal(PIN_SEL, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetATN() const
|
|
{
|
|
return GetSignal(PIN_ATN);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetATN(bool ast)
|
|
{
|
|
SetSignal(PIN_ATN, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetACK() const
|
|
{
|
|
return GetSignal(PIN_ACK);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetACK(bool ast)
|
|
{
|
|
SetSignal(PIN_ACK, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetACT() const
|
|
{
|
|
return GetSignal(PIN_ACT);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetACT(bool ast)
|
|
{
|
|
SetSignal(PIN_ACT, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetRST() const
|
|
{
|
|
return GetSignal(PIN_RST);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetRST(bool ast)
|
|
{
|
|
SetSignal(PIN_RST, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetMSG() const
|
|
{
|
|
return GetSignal(PIN_MSG);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetMSG(bool ast)
|
|
{
|
|
SetSignal(PIN_MSG, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetCD() const
|
|
{
|
|
return GetSignal(PIN_CD);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetCD(bool ast)
|
|
{
|
|
SetSignal(PIN_CD, ast);
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetIO()
|
|
{
|
|
bool ast = GetSignal(PIN_IO);
|
|
|
|
if (actmode == mode_e::INITIATOR) {
|
|
// Change the data input/output direction by IO signal
|
|
if (ast) {
|
|
SetControl(PIN_DTD, DTD_IN);
|
|
SetMode(PIN_DT0, IN);
|
|
SetMode(PIN_DT1, IN);
|
|
SetMode(PIN_DT2, IN);
|
|
SetMode(PIN_DT3, IN);
|
|
SetMode(PIN_DT4, IN);
|
|
SetMode(PIN_DT5, IN);
|
|
SetMode(PIN_DT6, IN);
|
|
SetMode(PIN_DT7, IN);
|
|
SetMode(PIN_DP, IN);
|
|
} else {
|
|
SetControl(PIN_DTD, DTD_OUT);
|
|
SetMode(PIN_DT0, OUT);
|
|
SetMode(PIN_DT1, OUT);
|
|
SetMode(PIN_DT2, OUT);
|
|
SetMode(PIN_DT3, OUT);
|
|
SetMode(PIN_DT4, OUT);
|
|
SetMode(PIN_DT5, OUT);
|
|
SetMode(PIN_DT6, OUT);
|
|
SetMode(PIN_DT7, OUT);
|
|
SetMode(PIN_DP, OUT);
|
|
}
|
|
}
|
|
|
|
return ast;
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetIO(bool ast)
|
|
{
|
|
SetSignal(PIN_IO, ast);
|
|
|
|
if (actmode == mode_e::TARGET) {
|
|
// Change the data input/output direction by IO signal
|
|
if (ast) {
|
|
SetControl(PIN_DTD, DTD_OUT);
|
|
SetDAT(0);
|
|
SetMode(PIN_DT0, OUT);
|
|
SetMode(PIN_DT1, OUT);
|
|
SetMode(PIN_DT2, OUT);
|
|
SetMode(PIN_DT3, OUT);
|
|
SetMode(PIN_DT4, OUT);
|
|
SetMode(PIN_DT5, OUT);
|
|
SetMode(PIN_DT6, OUT);
|
|
SetMode(PIN_DT7, OUT);
|
|
SetMode(PIN_DP, OUT);
|
|
} else {
|
|
SetControl(PIN_DTD, DTD_IN);
|
|
SetMode(PIN_DT0, IN);
|
|
SetMode(PIN_DT1, IN);
|
|
SetMode(PIN_DT2, IN);
|
|
SetMode(PIN_DT3, IN);
|
|
SetMode(PIN_DT4, IN);
|
|
SetMode(PIN_DT5, IN);
|
|
SetMode(PIN_DT6, IN);
|
|
SetMode(PIN_DT7, IN);
|
|
SetMode(PIN_DP, IN);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GPIOBUS_Raspberry::GetREQ() const
|
|
{
|
|
return GetSignal(PIN_REQ);
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetREQ(bool ast)
|
|
{
|
|
SetSignal(PIN_REQ, ast);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Get data signals
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
uint8_t GPIOBUS_Raspberry::GetDAT()
|
|
{
|
|
uint32_t data = Acquire();
|
|
data = ((data >> (PIN_DT0 - 0)) & (1 << 0)) | ((data >> (PIN_DT1 - 1)) & (1 << 1)) |
|
|
((data >> (PIN_DT2 - 2)) & (1 << 2)) | ((data >> (PIN_DT3 - 3)) & (1 << 3)) |
|
|
((data >> (PIN_DT4 - 4)) & (1 << 4)) | ((data >> (PIN_DT5 - 5)) & (1 << 5)) |
|
|
((data >> (PIN_DT6 - 6)) & (1 << 6)) | ((data >> (PIN_DT7 - 7)) & (1 << 7));
|
|
|
|
return (uint8_t)data;
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::SetDAT(uint8_t dat)
|
|
{
|
|
// Write to ports
|
|
uint32_t fsel = gpfsel[0];
|
|
fsel &= tblDatMsk[0][dat];
|
|
fsel |= tblDatSet[0][dat];
|
|
gpfsel[0] = fsel;
|
|
gpio[GPIO_FSEL_0] = fsel;
|
|
|
|
fsel = gpfsel[1];
|
|
fsel &= tblDatMsk[1][dat];
|
|
fsel |= tblDatSet[1][dat];
|
|
gpfsel[1] = fsel;
|
|
gpio[GPIO_FSEL_1] = fsel;
|
|
|
|
fsel = gpfsel[2];
|
|
fsel &= tblDatMsk[2][dat];
|
|
fsel |= tblDatSet[2][dat];
|
|
gpfsel[2] = fsel;
|
|
gpio[GPIO_FSEL_2] = fsel;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Signal table
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::MakeSignalTable()
|
|
{
|
|
SignalTable = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP, PIN_SEL,
|
|
PIN_ATN, PIN_RST, PIN_ACK, PIN_BSY, PIN_MSG, PIN_CD, PIN_IO, PIN_REQ, -1};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Create work table
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::MakeTable(void)
|
|
{
|
|
const array<int, 9> pintbl = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP};
|
|
|
|
array<bool, 256> tblParity;
|
|
|
|
// Create parity table
|
|
for (uint32_t i = 0; i < 0x100; i++) {
|
|
uint32_t bits = i;
|
|
uint32_t parity = 0;
|
|
for (int j = 0; j < 8; j++) {
|
|
parity ^= bits & 1;
|
|
bits >>= 1;
|
|
}
|
|
parity = ~parity;
|
|
tblParity[i] = parity & 1;
|
|
}
|
|
|
|
// Mask and setting data generation
|
|
for (auto &tbl : tblDatMsk) {
|
|
tbl.fill(-1);
|
|
}
|
|
for (auto &tbl : tblDatSet) {
|
|
tbl.fill(0);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < 0x100; i++) {
|
|
// Bit string for inspection
|
|
uint32_t bits = i;
|
|
|
|
// Get parity
|
|
if (tblParity[i]) {
|
|
bits |= (1 << 8);
|
|
}
|
|
|
|
// Bit check
|
|
for (int j = 0; j < 9; j++) {
|
|
// Index and shift amount calculation
|
|
int index = pintbl[j] / 10;
|
|
int shift = (pintbl[j] % 10) * 3;
|
|
|
|
// Mask data
|
|
tblDatMsk[index][i] &= ~(0x7 << shift);
|
|
|
|
// Setting data
|
|
if (bits & 1) {
|
|
tblDatSet[index][i] |= (1 << shift);
|
|
}
|
|
|
|
bits >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Control signal setting
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::SetControl(int pin, bool ast)
|
|
{
|
|
PinSetSignal(pin, ast);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Input/output mode setting
|
|
//
|
|
// Set direction fo pin (IN / OUT)
|
|
// Used with: TAD, BSY, MSG, CD, REQ, O, SEL, IND, ATN, ACK, RST, DT*
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::SetMode(int pin, int mode)
|
|
{
|
|
if (mode == OUT) {
|
|
return;
|
|
}
|
|
|
|
int index = pin / 10;
|
|
int shift = (pin % 10) * 3;
|
|
uint32_t data = gpfsel[index];
|
|
data &= ~(0x7 << shift);
|
|
if (mode == OUT) {
|
|
data |= (1 << shift);
|
|
}
|
|
gpio[index] = data;
|
|
gpfsel[index] = data;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Get input signal value
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
bool GPIOBUS_Raspberry::GetSignal(int pin) const
|
|
{
|
|
return (signals >> pin) & 1;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Set output signal value
|
|
//
|
|
// Sets the output value. Used with:
|
|
// PIN_ENB, ACT, TAD, IND, DTD, BSY, SignalTable
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::SetSignal(int pin, bool ast)
|
|
{
|
|
int index = pin / 10;
|
|
int shift = (pin % 10) * 3;
|
|
uint32_t data = gpfsel[index];
|
|
if (ast) {
|
|
data |= (1 << shift);
|
|
} else {
|
|
data &= ~(0x7 << shift);
|
|
}
|
|
gpio[index] = data;
|
|
gpfsel[index] = data;
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::DisableIRQ()
|
|
{
|
|
#ifdef __linux__
|
|
if (rpitype == 4) {
|
|
// RPI4 is disabled by GICC
|
|
giccpmr = gicc[GICC_PMR];
|
|
gicc[GICC_PMR] = 0;
|
|
} else if (rpitype == 2) {
|
|
// RPI2,3 disable core timer IRQ
|
|
tintcore = sched_getcpu() + QA7_CORE0_TINTC;
|
|
tintctl = qa7regs[tintcore];
|
|
qa7regs[tintcore] = 0;
|
|
} else {
|
|
// Stop system timer interrupt with interrupt controller
|
|
irptenb = irpctl[IRPT_ENB_IRQ_1];
|
|
irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf;
|
|
}
|
|
#else
|
|
(void)0;
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_Raspberry::EnableIRQ()
|
|
{
|
|
if (rpitype == 4) {
|
|
// RPI4 enables interrupts via the GICC
|
|
gicc[GICC_PMR] = giccpmr;
|
|
} else if (rpitype == 2) {
|
|
// RPI2,3 re-enable core timer IRQ
|
|
qa7regs[tintcore] = tintctl;
|
|
} else {
|
|
// Restart the system timer interrupt with the interrupt controller
|
|
irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Pin direction setting (input/output)
|
|
//
|
|
// Used in Init() for ACT, TAD, IND, DTD, ENB to set direction (GPIO_OUTPUT vs GPIO_INPUT)
|
|
// Also used on SignalTable
|
|
// Only used in Init and Cleanup. Reset uses SetMode
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::PinConfig(int pin, int mode)
|
|
{
|
|
// Check for invalid pin
|
|
if (pin < 0) {
|
|
return;
|
|
}
|
|
|
|
int index = pin / 10;
|
|
uint32_t mask = ~(0x7 << ((pin % 10) * 3));
|
|
gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Pin pull-up/pull-down setting
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::PullConfig(int pin, int mode)
|
|
{
|
|
uint32_t pull;
|
|
|
|
// Check for invalid pin
|
|
if (pin < 0) {
|
|
return;
|
|
}
|
|
|
|
if (rpitype == 4) {
|
|
switch (mode) {
|
|
case GPIO_PULLNONE:
|
|
pull = 0;
|
|
break;
|
|
case GPIO_PULLUP:
|
|
pull = 1;
|
|
break;
|
|
case GPIO_PULLDOWN:
|
|
pull = 2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
pin &= 0x1f;
|
|
int shift = (pin & 0xf) << 1;
|
|
uint32_t bits = gpio[GPIO_PUPPDN0 + (pin >> 4)];
|
|
bits &= ~(3 << shift);
|
|
bits |= (pull << shift);
|
|
gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits;
|
|
} else {
|
|
pin &= 0x1f;
|
|
gpio[GPIO_PUD] = mode & 0x3;
|
|
SysTimer::SleepUsec(2);
|
|
gpio[GPIO_CLK_0] = 0x1 << pin;
|
|
SysTimer::SleepUsec(2);
|
|
gpio[GPIO_PUD] = 0;
|
|
gpio[GPIO_CLK_0] = 0;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Set output pin
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::PinSetSignal(int pin, bool ast)
|
|
{
|
|
// Check for invalid pin
|
|
if (pin < 0) {
|
|
return;
|
|
}
|
|
|
|
if (ast) {
|
|
gpio[GPIO_SET_0] = 0x1 << pin;
|
|
} else {
|
|
gpio[GPIO_CLR_0] = 0x1 << pin;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Set the signal drive strength
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_Raspberry::DrvConfig(uint32_t drive)
|
|
{
|
|
uint32_t data = pads[PAD_0_27];
|
|
pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Bus signal acquisition
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
uint32_t GPIOBUS_Raspberry::Acquire()
|
|
{
|
|
signals = *level;
|
|
|
|
// SCSI logical signal control is active-low internally.
|
|
signals = ~signals;
|
|
|
|
return signals;
|
|
}
|