mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-21 23:29:39 +00:00
09ba19a24f
Co-authored-by: Tony Kuker <akuker@gmail.com>
1054 lines
31 KiB
C++
1054 lines
31 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi (And Banana Pi)
|
|
//
|
|
// Copyright (c) 2012-2015 Ben Croston
|
|
// Copyright (C) 2022 akuker
|
|
//
|
|
// Large portions of this functionality were derived from c_gpio.c, which
|
|
// is part of the RPI.GPIO library available here:
|
|
// https://github.com/BPI-SINOVOIP/RPi.GPIO/blob/master/source/c_gpio.c
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
// this software and associated documentation files (the "Software"), to deal in
|
|
// the Software without restriction, including without limitation the rights to
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
// of the Software, and to permit persons to whom the Software is furnished to do
|
|
// so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <iomanip>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include "hal/gpiobus.h"
|
|
#include "hal/gpiobus_bananam2p.h"
|
|
#include "hal/pi_defs/bpi-gpio.h"
|
|
#include "hal/sunxi_utils.h"
|
|
#include "hal/systimer.h"
|
|
#include "shared/log.h"
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / (sizeof(x[0])))
|
|
|
|
bool GPIOBUS_BananaM2p::Init(mode_e mode)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
GPIOBUS::Init(mode);
|
|
SysTimer::Init();
|
|
|
|
sbc_version = SBC_Version::GetSbcVersion();
|
|
|
|
for (auto const gpio_num : SignalTable) {
|
|
if (gpio_num == -1) {
|
|
break;
|
|
}
|
|
|
|
int gpio_bank = SunXI::GPIO_BANK(gpio_num);
|
|
|
|
if (std::find(gpio_banks.begin(), gpio_banks.end(), gpio_bank) != gpio_banks.end()) {
|
|
LOGTRACE("Duplicate bank: %d", gpio_bank)
|
|
|
|
} else {
|
|
LOGDEBUG("New bank: %d", gpio_bank)
|
|
gpio_banks.push_back(gpio_bank);
|
|
}
|
|
}
|
|
|
|
if (int result = sunxi_setup(); result != SETUP_OK) {
|
|
return false;
|
|
}
|
|
|
|
InitializeGpio();
|
|
|
|
MakeTable();
|
|
|
|
// SetupSelEvent needs to be called AFTER Initialize GPIO. This function
|
|
// reconfigures the SEL signal.
|
|
if (!SetupSelEvent()) {
|
|
LOGERROR("Failed to setup SELECT poll event")
|
|
return false;
|
|
}
|
|
LOGTRACE("SetupSelEvent OK!")
|
|
|
|
// Set drive strength to maximum
|
|
DrvConfig(3);
|
|
|
|
return true;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::InitializeGpio()
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
// Set pull up/pull down
|
|
#if SIGNAL_CONTROL_MODE == 0
|
|
int pullmode = GPIO_PULLNONE;
|
|
#elif SIGNAL_CONTROL_MODE == 1
|
|
int pullmode = GPIO_PULLUP;
|
|
#else
|
|
int pullmode = GPIO_PULLDOWN;
|
|
#endif
|
|
|
|
// Initialize all signals
|
|
for (int i = 0; SignalTable[i] >= 0; i++) {
|
|
int j = SignalTable[i];
|
|
PinConfig(j, GPIO_INPUT);
|
|
PullConfig(j, pullmode);
|
|
PinSetSignal(j, OFF);
|
|
}
|
|
|
|
// Set control signals
|
|
PinConfig(BPI_PIN_ACT, GPIO_OUTPUT);
|
|
PinConfig(BPI_PIN_TAD, GPIO_OUTPUT);
|
|
PinConfig(BPI_PIN_IND, GPIO_OUTPUT);
|
|
PinConfig(BPI_PIN_DTD, GPIO_OUTPUT);
|
|
PinSetSignal(BPI_PIN_ACT, OFF);
|
|
PinSetSignal(BPI_PIN_TAD, OFF);
|
|
PinSetSignal(BPI_PIN_IND, OFF);
|
|
PinSetSignal(BPI_PIN_DTD, OFF);
|
|
|
|
// Set the ENABLE signal
|
|
// This is used to show that the application is running
|
|
PinConfig(BPI_PIN_ENB, GPIO_OUTPUT);
|
|
PinSetSignal(BPI_PIN_ENB, ON);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::Cleanup()
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var--; // Need to do something to prevent Sonar from claiming this should be a const function
|
|
return;
|
|
#else
|
|
|
|
#ifdef USE_SEL_EVENT_ENABLE
|
|
// Release SEL signal interrupt
|
|
close(selevreq.fd);
|
|
#endif // USE_SEL_EVENT_ENABLE
|
|
|
|
// Set control signals
|
|
PinConfig(BPI_PIN_ACT, GPIO_INPUT);
|
|
PinConfig(BPI_PIN_TAD, GPIO_INPUT);
|
|
PinConfig(BPI_PIN_IND, GPIO_INPUT);
|
|
PinConfig(BPI_PIN_DTD, GPIO_INPUT);
|
|
PinSetSignal(BPI_PIN_ENB, OFF);
|
|
PinSetSignal(BPI_PIN_ACT, OFF);
|
|
PinSetSignal(BPI_PIN_TAD, OFF);
|
|
PinSetSignal(BPI_PIN_IND, OFF);
|
|
PinSetSignal(BPI_PIN_DTD, OFF);
|
|
|
|
// Initialize all signals
|
|
for (int i = 0; SignalTable[i] >= 0; i++) {
|
|
int pin = SignalTable[i];
|
|
PinConfig(pin, GPIO_INPUT);
|
|
PullConfig(pin, GPIO_PULLNONE);
|
|
PinSetSignal(pin, OFF);
|
|
}
|
|
|
|
// Set drive strength back to Default (Level 1)
|
|
DrvConfig(1);
|
|
|
|
munmap((void *)gpio_map, SunXI::BLOCK_SIZE);
|
|
munmap((void *)r_gpio_map, SunXI::BLOCK_SIZE);
|
|
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::Reset()
|
|
{
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var++;
|
|
return;
|
|
#else
|
|
int i;
|
|
int j;
|
|
|
|
// Turn off active signal
|
|
SetControl(BPI_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(BPI_PIN_TAD, TAD_IN);
|
|
SetMode(BPI_PIN_BSY, IN);
|
|
SetMode(BPI_PIN_MSG, IN);
|
|
SetMode(BPI_PIN_CD, IN);
|
|
SetMode(BPI_PIN_REQ, IN);
|
|
SetMode(BPI_PIN_IO, IN);
|
|
|
|
// Set the initiator signal to input
|
|
SetControl(BPI_PIN_IND, IND_IN);
|
|
SetMode(BPI_PIN_SEL, IN);
|
|
SetMode(BPI_PIN_ATN, IN);
|
|
SetMode(BPI_PIN_ACK, IN);
|
|
SetMode(BPI_PIN_RST, IN);
|
|
|
|
// Set data bus signals to input
|
|
SetControl(BPI_PIN_DTD, DTD_IN);
|
|
SetMode(BPI_PIN_DT0, IN);
|
|
SetMode(BPI_PIN_DT1, IN);
|
|
SetMode(BPI_PIN_DT2, IN);
|
|
SetMode(BPI_PIN_DT3, IN);
|
|
SetMode(BPI_PIN_DT4, IN);
|
|
SetMode(BPI_PIN_DT5, IN);
|
|
SetMode(BPI_PIN_DT6, IN);
|
|
SetMode(BPI_PIN_DT7, IN);
|
|
SetMode(BPI_PIN_DP, IN);
|
|
} else {
|
|
// Initiator mode
|
|
|
|
// Set target signal to input
|
|
SetControl(BPI_PIN_TAD, TAD_IN);
|
|
SetMode(BPI_PIN_BSY, IN);
|
|
SetMode(BPI_PIN_MSG, IN);
|
|
SetMode(BPI_PIN_CD, IN);
|
|
SetMode(BPI_PIN_REQ, IN);
|
|
SetMode(BPI_PIN_IO, IN);
|
|
|
|
// Set the initiator signal to output
|
|
SetControl(BPI_PIN_IND, IND_OUT);
|
|
SetMode(BPI_PIN_SEL, OUT);
|
|
SetMode(BPI_PIN_ATN, OUT);
|
|
SetMode(BPI_PIN_ACK, OUT);
|
|
SetMode(BPI_PIN_RST, OUT);
|
|
|
|
// Set the data bus signals to output
|
|
SetControl(BPI_PIN_DTD, DTD_OUT);
|
|
SetMode(BPI_PIN_DT0, OUT);
|
|
SetMode(BPI_PIN_DT1, OUT);
|
|
SetMode(BPI_PIN_DT2, OUT);
|
|
SetMode(BPI_PIN_DT3, OUT);
|
|
SetMode(BPI_PIN_DT4, OUT);
|
|
SetMode(BPI_PIN_DT5, OUT);
|
|
SetMode(BPI_PIN_DT6, OUT);
|
|
SetMode(BPI_PIN_DT7, OUT);
|
|
SetMode(BPI_PIN_DP, OUT);
|
|
}
|
|
|
|
// Initialize all signals
|
|
// TODO!! For now, just re-run Acquire
|
|
Acquire();
|
|
#endif // ifdef __x86_64__ || __X86__
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::SetupSelEvent()
|
|
{
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var += 2; // Need to do something to prevent Sonar from claiming this should be a const function
|
|
return false;
|
|
#else
|
|
GPIO_FUNCTION_TRACE
|
|
int gpio_pin = BPI_PIN_SEL;
|
|
|
|
// GPIO chip open
|
|
LOGTRACE("%s GPIO chip open [%d]", __PRETTY_FUNCTION__, gpio_pin)
|
|
std::string gpio_dev = "/dev/gpiochip0";
|
|
if (SunXI::GPIO_BANK(gpio_pin) >= 11) {
|
|
gpio_dev = "/dev/gpiochip1";
|
|
LOGWARN("gpiochip1 support isn't implemented yet....")
|
|
LOGWARN("THIS PROBABLY WONT WORK!")
|
|
}
|
|
|
|
int gpio_fd = open(gpio_dev.c_str(), 0);
|
|
if (gpio_fd == -1) {
|
|
LOGERROR("Unable to open /dev/gpiochip0. Is PiSCSI or RaSCSI already running?")
|
|
return false;
|
|
}
|
|
|
|
// Event request setting
|
|
LOGTRACE("%s Event request setting (pin sel: %d)", __PRETTY_FUNCTION__, gpio_pin)
|
|
strncpy(selevreq.consumer_label, "PiSCSI", ARRAY_SIZE(selevreq.consumer_label));
|
|
selevreq.lineoffset = gpio_pin;
|
|
selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT;
|
|
#if SIGNAL_CONTROL_MODE < 2
|
|
selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE;
|
|
LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_FALLING_EDGE", __PRETTY_FUNCTION__)
|
|
#else
|
|
selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE;
|
|
LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_RISING_EDGE", __PRETTY_FUNCTION__)
|
|
#endif // SIGNAL_CONTROL_MODE
|
|
|
|
// Get event request
|
|
if (ioctl(gpio_fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
|
|
LOGERROR("selevreq.fd = %d %08X", selevreq.fd, (unsigned int)selevreq.fd)
|
|
LOGERROR("Unable to register event request. Is PiSCSI or RaSCSI already running?")
|
|
LOGERROR("[%08X] %s", errno, strerror(errno))
|
|
close(gpio_fd);
|
|
return false;
|
|
}
|
|
|
|
// Close GPIO chip file handle
|
|
LOGTRACE("%s Close GPIO chip file handle", __PRETTY_FUNCTION__)
|
|
close(gpio_fd);
|
|
|
|
// epoll initialization
|
|
LOGTRACE("%s epoll initialization", __PRETTY_FUNCTION__)
|
|
epfd = epoll_create(1);
|
|
if (epfd == -1) {
|
|
LOGERROR("Unable to create the epoll event")
|
|
return false;
|
|
}
|
|
epoll_event ev = {};
|
|
memset(&ev, 0, sizeof(ev));
|
|
ev.events = EPOLLIN | EPOLLPRI;
|
|
ev.data.fd = selevreq.fd;
|
|
if (epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev) < 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetENB(bool ast)
|
|
{
|
|
PinSetSignal(BPI_PIN_ENB, ast ? ENB_ON : ENB_OFF);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetBSY() const
|
|
{
|
|
return GetSignal(BPI_PIN_BSY);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetBSY(bool ast)
|
|
{
|
|
if (actmode == mode_e::TARGET) {
|
|
if (ast) {
|
|
// Turn on ACTIVE signal
|
|
SetControl(BPI_PIN_ACT, ACT_ON);
|
|
|
|
// Set Target signal to output
|
|
SetControl(BPI_PIN_TAD, TAD_OUT);
|
|
|
|
SetMode(BPI_PIN_BSY, OUT);
|
|
SetMode(BPI_PIN_MSG, OUT);
|
|
SetMode(BPI_PIN_CD, OUT);
|
|
SetMode(BPI_PIN_REQ, OUT);
|
|
SetMode(BPI_PIN_IO, OUT);
|
|
|
|
// Set BSY signal
|
|
SetSignal(BPI_PIN_BSY, ast);
|
|
|
|
} else {
|
|
// Turn off the ACTIVE signal
|
|
SetControl(BPI_PIN_ACT, ACT_OFF);
|
|
|
|
// Set the target signal to input
|
|
SetControl(BPI_PIN_TAD, TAD_IN);
|
|
|
|
SetMode(BPI_PIN_BSY, IN);
|
|
SetMode(BPI_PIN_MSG, IN);
|
|
SetMode(BPI_PIN_CD, IN);
|
|
SetMode(BPI_PIN_REQ, IN);
|
|
SetMode(BPI_PIN_IO, IN);
|
|
}
|
|
} else {
|
|
// Set BSY signal
|
|
SetSignal(BPI_PIN_BSY, ast);
|
|
}
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetSEL() const
|
|
{
|
|
return GetSignal(BPI_PIN_SEL);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetSEL(bool ast)
|
|
{
|
|
if (actmode == mode_e::INITIATOR && ast) {
|
|
// Turn on ACTIVE signal
|
|
SetControl(BPI_PIN_ACT, ACT_ON);
|
|
}
|
|
|
|
// Set SEL signal
|
|
SetSignal(BPI_PIN_SEL, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetATN() const
|
|
{
|
|
return GetSignal(BPI_PIN_ATN);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetATN(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_ATN, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetACK() const
|
|
{
|
|
return GetSignal(BPI_PIN_ACK);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetACK(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_ACK, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetACT() const
|
|
{
|
|
return GetSignal(BPI_PIN_ACT);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetACT(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_ACT, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetRST() const
|
|
{
|
|
return GetSignal(BPI_PIN_RST);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetRST(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_RST, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetMSG() const
|
|
{
|
|
return GetSignal(BPI_PIN_MSG);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetMSG(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_MSG, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetCD() const
|
|
{
|
|
return GetSignal(BPI_PIN_CD);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetCD(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_CD, ast);
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetIO()
|
|
{
|
|
bool ast = GetSignal(BPI_PIN_IO);
|
|
|
|
if (actmode == mode_e::INITIATOR) {
|
|
// Change the data input/output direction by IO signal
|
|
if (ast) {
|
|
SetControl(BPI_PIN_DTD, DTD_IN);
|
|
SetMode(BPI_PIN_DT0, IN);
|
|
SetMode(BPI_PIN_DT1, IN);
|
|
SetMode(BPI_PIN_DT2, IN);
|
|
SetMode(BPI_PIN_DT3, IN);
|
|
SetMode(BPI_PIN_DT4, IN);
|
|
SetMode(BPI_PIN_DT5, IN);
|
|
SetMode(BPI_PIN_DT6, IN);
|
|
SetMode(BPI_PIN_DT7, IN);
|
|
SetMode(BPI_PIN_DP, IN);
|
|
} else {
|
|
SetControl(BPI_PIN_DTD, DTD_OUT);
|
|
SetMode(BPI_PIN_DT0, OUT);
|
|
SetMode(BPI_PIN_DT1, OUT);
|
|
SetMode(BPI_PIN_DT2, OUT);
|
|
SetMode(BPI_PIN_DT3, OUT);
|
|
SetMode(BPI_PIN_DT4, OUT);
|
|
SetMode(BPI_PIN_DT5, OUT);
|
|
SetMode(BPI_PIN_DT6, OUT);
|
|
SetMode(BPI_PIN_DT7, OUT);
|
|
SetMode(BPI_PIN_DP, OUT);
|
|
}
|
|
}
|
|
|
|
return ast;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetIO(bool ast)
|
|
{
|
|
if (actmode == mode_e::TARGET) {
|
|
// Change the data input/output direction by IO signal
|
|
if (ast) {
|
|
SetControl(BPI_PIN_DTD, DTD_OUT);
|
|
SetMode(BPI_PIN_DT0, OUT);
|
|
SetMode(BPI_PIN_DT1, OUT);
|
|
SetMode(BPI_PIN_DT2, OUT);
|
|
SetMode(BPI_PIN_DT3, OUT);
|
|
SetMode(BPI_PIN_DT4, OUT);
|
|
SetMode(BPI_PIN_DT5, OUT);
|
|
SetMode(BPI_PIN_DT6, OUT);
|
|
SetMode(BPI_PIN_DT7, OUT);
|
|
SetMode(BPI_PIN_DP, OUT);
|
|
|
|
SetDAT(0);
|
|
SetSignal(BPI_PIN_IO, ast);
|
|
|
|
} else {
|
|
SetControl(BPI_PIN_DTD, DTD_IN);
|
|
SetMode(BPI_PIN_DT0, IN);
|
|
SetMode(BPI_PIN_DT1, IN);
|
|
SetMode(BPI_PIN_DT2, IN);
|
|
SetMode(BPI_PIN_DT3, IN);
|
|
SetMode(BPI_PIN_DT4, IN);
|
|
SetMode(BPI_PIN_DT5, IN);
|
|
SetMode(BPI_PIN_DT6, IN);
|
|
SetMode(BPI_PIN_DT7, IN);
|
|
SetMode(BPI_PIN_DP, IN);
|
|
}
|
|
} else {
|
|
SetSignal(BPI_PIN_IO, ast);
|
|
}
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetREQ() const
|
|
{
|
|
return GetSignal(BPI_PIN_REQ);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetREQ(bool ast)
|
|
{
|
|
SetSignal(BPI_PIN_REQ, ast);
|
|
}
|
|
|
|
uint8_t GPIOBUS_BananaM2p::GetDAT()
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
Acquire();
|
|
uint32_t data =
|
|
((GetSignal(BPI_PIN_DT0) ? 0x01 : 0x00) << 0) | ((GetSignal(BPI_PIN_DT1) ? 0x01 : 0x00) << 1) |
|
|
((GetSignal(BPI_PIN_DT2) ? 0x01 : 0x00) << 2) | ((GetSignal(BPI_PIN_DT3) ? 0x01 : 0x00) << 3) |
|
|
((GetSignal(BPI_PIN_DT4) ? 0x01 : 0x00) << 4) | ((GetSignal(BPI_PIN_DT5) ? 0x01 : 0x00) << 5) |
|
|
((GetSignal(BPI_PIN_DT6) ? 0x01 : 0x00) << 6) |
|
|
((GetSignal(BPI_PIN_DT7) ? 0x01 : 0x00) << 7); // NOSONAR: GCC 10 doesn't support shift operations on std::byte
|
|
|
|
return (uint8_t)(data & 0xFF);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetDAT(uint8_t dat)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
array<uint32_t, 12> gpio_reg_values = {0};
|
|
|
|
for (size_t gpio_num = 0; gpio_num < pintbl.size(); gpio_num++) {
|
|
bool value;
|
|
if (gpio_num < 8) {
|
|
// data bits
|
|
value = !(dat & (1 << gpio_num)); // NOSONAR: GCC 10 doesn't support shift operations on std::byte
|
|
} else {
|
|
// parity bit
|
|
value = (__builtin_parity(dat) == 1);
|
|
}
|
|
|
|
if (value) {
|
|
uint32_t this_gpio = pintbl[gpio_num];
|
|
int bank = SunXI::GPIO_BANK(this_gpio);
|
|
int offset = SunXI::GPIO_NUM(this_gpio);
|
|
gpio_reg_values[bank] |= (1 << offset);
|
|
}
|
|
}
|
|
|
|
sunxi_set_all_gpios(gpio_data_masks, gpio_reg_values);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Signal table
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
const array<int, 19> GPIOBUS_BananaM2p::SignalTable = {BPI_PIN_DT0, BPI_PIN_DT1, BPI_PIN_DT2, BPI_PIN_DT3, BPI_PIN_DT4,
|
|
BPI_PIN_DT5, BPI_PIN_DT6, BPI_PIN_DT7, BPI_PIN_DP, BPI_PIN_SEL,
|
|
BPI_PIN_ATN, BPI_PIN_RST, BPI_PIN_ACK, BPI_PIN_BSY, BPI_PIN_MSG,
|
|
BPI_PIN_CD, BPI_PIN_IO, BPI_PIN_REQ, -1};
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Create work table
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
void GPIOBUS_BananaM2p::MakeTable(void)
|
|
{
|
|
for (auto this_gpio : pintbl) {
|
|
int bank = SunXI::GPIO_BANK(this_gpio);
|
|
int offset = (SunXI::GPIO_NUM(this_gpio));
|
|
|
|
gpio_data_masks[bank] |= (1 << offset);
|
|
}
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetDP() const
|
|
{
|
|
return GetSignal(BPI_PIN_DP);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetControl(int pin, bool ast)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
PinSetSignal(pin, ast);
|
|
}
|
|
|
|
// Set direction
|
|
int GPIOBUS_BananaM2p::GetMode(int pin)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(pin);
|
|
int index = SunXI::GPIO_CFG_INDEX(pin);
|
|
int offset = SunXI::GPIO_CFG_OFFSET(pin);
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
regval = pio->CFG[0 + index];
|
|
|
|
// Extract the CFG field
|
|
regval &= (0x7 << offset); // 0xf?
|
|
// Shift it down to the LSB
|
|
regval >>= offset;
|
|
return (int)regval;
|
|
}
|
|
|
|
// Set direction
|
|
void GPIOBUS_BananaM2p::SetMode(int pin, int mode)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int direction = mode;
|
|
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(pin); // gpio >> 5
|
|
int index = SunXI::GPIO_CFG_INDEX(pin); // (gpio & 0x1F) >> 3
|
|
int offset = SunXI::GPIO_CFG_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2
|
|
LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d) dir(%s)", __PRETTY_FUNCTION__, pin, bank, index, offset,
|
|
(GPIO_INPUT == direction) ? "IN"
|
|
: (GPIO_IRQ_IN == direction) ? "IRQ"
|
|
: "OUT")
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
regval = pio->CFG[0 + index];
|
|
|
|
// Clear the cfg field
|
|
regval &= ~(0x7 << offset); // 0xf?
|
|
if (GPIO_INPUT == direction) {
|
|
regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_input) << offset);
|
|
} else if (GPIO_OUTPUT == direction) {
|
|
regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_output) << offset);
|
|
} else if (GPIO_IRQ_IN == direction) {
|
|
regval |= (((uint32_t)SunXI::gpio_configure_values_e::gpio_interupt) << offset);
|
|
} else {
|
|
LOGERROR("line:%d gpio number error %d", __LINE__, pin)
|
|
}
|
|
pio->CFG[0 + index] = regval;
|
|
}
|
|
|
|
bool GPIOBUS_BananaM2p::GetSignal(int pin) const
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int gpio_num = pin;
|
|
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5
|
|
int num = SunXI::GPIO_NUM(gpio_num); // gpio & 0x1F
|
|
|
|
regval = (signals[bank] >> num) & 0x1;
|
|
return regval != 0;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::SetSignal(int pin, bool ast)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int gpio_num = pin;
|
|
|
|
#if SIGNAL_CONTROL_MODE == 0
|
|
// True : 0V
|
|
// False : Open collector output (disconnect from bus)
|
|
int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW;
|
|
#elif SIGNAL_CONTROL_MODE == 1
|
|
// True : 0V -> (CONVERT) -> 0V
|
|
// False : 3.3V -> (CONVERT) -> Open collector output
|
|
LOGWARN("%s:%d THIS LOGIC NEEDS TO BE VALIDATED/TESTED", __PRETTY_FUNCTION__, __LINE__)
|
|
int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW;
|
|
#elif SIGNAL_CONTROL_MODE == 2
|
|
// True : 3.3V -> (CONVERT) -> 0V
|
|
// False : 0V -> (CONVERT) -> Open collector output
|
|
LOGWARN("%s:%d THIS LOGIC NEEDS TO BE VALIDATED/TESTED", __PRETTY_FUNCTION__, __LINE__)
|
|
int sunxi_gpio_state = (ast == true) ? SunXI::LOW : SunXI::HIGH;
|
|
#endif // SIGNAL_CONTROL_MODE
|
|
|
|
LOGTRACE("gpio(%d) sunxi_state(%d)", gpio_num, sunxi_gpio_state)
|
|
|
|
int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5
|
|
int num = SunXI::GPIO_NUM(gpio_num); // gpio & 0x1F
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
LOGTRACE("bank > 11")
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
if (sunxi_gpio_state == SunXI::HIGH)
|
|
pio->DAT &= ~(1 << num);
|
|
else
|
|
pio->DAT |= (1 << num);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::DisableIRQ()
|
|
{
|
|
*tmr_ctrl = 0b00;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::EnableIRQ()
|
|
{
|
|
*tmr_ctrl = 0b11;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::PinConfig(int pin, int mode)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int gpio_num = pin;
|
|
int sunxi_direction = (mode == GPIO_INPUT) ? SunXI::INPUT : SunXI::OUTPUT;
|
|
|
|
sunxi_setup_gpio(gpio_num, sunxi_direction, -1);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::PullConfig(int pin, int mode)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function
|
|
(void)pin;
|
|
(void)mode;
|
|
return;
|
|
#else
|
|
|
|
// Note: this will throw an exception if an invalid pin is specified
|
|
int gpio_num = pin;
|
|
int pull_up_down_state = 0;
|
|
|
|
switch (mode) {
|
|
case GPIO_PULLNONE:
|
|
pull_up_down_state = SunXI::PUD_OFF;
|
|
break;
|
|
case GPIO_PULLUP:
|
|
pull_up_down_state = SunXI::PUD_UP;
|
|
break;
|
|
case GPIO_PULLDOWN:
|
|
pull_up_down_state = SunXI::PUD_DOWN;
|
|
break;
|
|
default:
|
|
LOGERROR("%s INVALID PIN MODE", __PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(gpio_num); // gpio >> 5
|
|
int index = SunXI::GPIO_PUL_INDEX(gpio_num); // (gpio & 0x1f) >> 4
|
|
int offset = SunXI::GPIO_PUL_OFFSET(gpio_num); // (gpio) & 0x0F) << 1
|
|
LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, gpio_num, bank, index, offset)
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
regval = *(&pio->PULL[0] + index);
|
|
regval &= ~(3 << offset);
|
|
regval |= pull_up_down_state << offset;
|
|
pio->PULL[0 + index] = regval;
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::PinSetSignal(int pin, bool ast)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int gpio_num = pin;
|
|
|
|
int sunxi_gpio_state = (ast == true) ? SunXI::HIGH : SunXI::LOW;
|
|
sunxi_output_gpio(gpio_num, sunxi_gpio_state);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::DrvConfig(uint32_t drive)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
for (auto pin : SignalTable) {
|
|
if (pin == -1) {
|
|
continue;
|
|
}
|
|
|
|
LOGTRACE("Configuring GPIO %d to drive strength %d", pin, drive)
|
|
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(pin); // gpio >> 5
|
|
int index = SunXI::GPIO_DRV_INDEX(pin); // (gpio & 0x1F) >> 3
|
|
int offset = SunXI::GPIO_DRV_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2
|
|
LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, pin, bank, index, offset)
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
// Get current register value
|
|
regval = pio->DRV[0 + index];
|
|
// Clear the DRV value for that gpio
|
|
regval &= ~(0x7 << offset); // 0xf?
|
|
// Set the new DRV strength
|
|
regval |= (drive & 0b11) << offset;
|
|
// Save back to the register
|
|
pio->DRV[0 + index] = regval;
|
|
}
|
|
|
|
// #endif // if __arm__
|
|
}
|
|
|
|
uint32_t GPIOBUS_BananaM2p::Acquire()
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
|
|
for (auto bank : gpio_banks) {
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
pio = &(r_pio_map->gpio_bank[bank - 11]);
|
|
}
|
|
|
|
uint32_t regval = pio->DAT;
|
|
|
|
#if SIGNAL_CONTROL_MODE < 2
|
|
// Invert if negative logic (internal processing is unified to positive logic)
|
|
regval = ~regval;
|
|
#endif // SIGNAL_CONTROL_MODE
|
|
signals[bank] = regval;
|
|
}
|
|
// TODO: This should do something someday....
|
|
return 0;
|
|
}
|
|
|
|
int GPIOBUS_BananaM2p::sunxi_setup(void)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function
|
|
return SunXI::SETUP_MMAP_FAIL;
|
|
#else
|
|
int mem_fd;
|
|
uint8_t *gpio_mem;
|
|
|
|
// mmap the GPIO memory registers
|
|
if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
|
|
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
|
|
LOGDEBUG("errno: [%08X] %s", errno, strerror(errno));
|
|
return SunXI::SETUP_DEVMEM_FAIL;
|
|
}
|
|
|
|
if ((gpio_mem = (uint8_t *)malloc(SunXI::BLOCK_SIZE + (SunXI::PAGE_SIZE - 1))) == NULL) {
|
|
LOGERROR("Error: Unable to allocate gpio memory. Are you running as root?")
|
|
LOGDEBUG("errno: [%08X] %s", errno, strerror(errno));
|
|
return SunXI::SETUP_DEVMEM_FAIL;
|
|
}
|
|
|
|
if ((uint32_t)gpio_mem % SunXI::PAGE_SIZE)
|
|
gpio_mem += SunXI::PAGE_SIZE - ((uint32_t)gpio_mem % SunXI::PAGE_SIZE);
|
|
|
|
gpio_map = (uint32_t *)mmap((caddr_t)gpio_mem, SunXI::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
|
|
mem_fd, SunXI::SUNXI_GPIO_BASE);
|
|
if ((void *)gpio_map == MAP_FAILED) {
|
|
LOGERROR("Error: Unable to map gpio memory. Are you running as root?")
|
|
LOGDEBUG("errno: [%08X] %s", errno, strerror(errno));
|
|
return SunXI::SETUP_MMAP_FAIL;
|
|
}
|
|
pio_map = (volatile SunXI::sunxi_gpio_reg *)(gpio_map + (SunXI::SUNXI_GPIO_REG_OFFSET >> 2));
|
|
LOGTRACE("gpio_mem[%p] gpio_map[%p] pio_map[%p]", gpio_mem, gpio_map, pio_map)
|
|
// R_PIO GPIO LMN
|
|
r_gpio_map = (uint32_t *)mmap((caddr_t)0, SunXI::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd,
|
|
SunXI::SUNXI_R_GPIO_BASE);
|
|
if ((void *)r_gpio_map == MAP_FAILED) {
|
|
LOGERROR("Error: Unable to map r_gpio memory. Are you running as root?")
|
|
LOGDEBUG("errno: [%08X] %s", errno, strerror(errno));
|
|
return SunXI::SETUP_MMAP_FAIL;
|
|
}
|
|
r_pio_map = (volatile SunXI::sunxi_gpio_reg *)(r_gpio_map + (SunXI::SUNXI_R_GPIO_REG_OFFSET >> 2));
|
|
LOGTRACE("r_gpio_map[%p] r_pio_map[%p]", r_gpio_map, r_pio_map)
|
|
|
|
tmr_ctrl = gpio_map + ((SunXI::TMR_REGISTER_BASE - SunXI::SUNXI_GPIO_BASE) >> 2);
|
|
// LOGINFO("tmr_ctrl offset: %08X value: %08X", (TMR_REGISTER_BASE - SUNXI_GPIO_BASE), *tmr_ctrl);
|
|
|
|
close(mem_fd);
|
|
return SETUP_OK;
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::sunxi_setup_gpio(int pin, int direction, int pud)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
#if defined(__x86_64__) || defined(__X86__) || defined(__aarch64__)
|
|
dummy_var++; // Need to do something to prevent Sonar from claiming this should be a const function
|
|
(void)pin;
|
|
(void)direction;
|
|
(void)pud;
|
|
return;
|
|
#else
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(pin); // gpio >> 5
|
|
int index = SunXI::GPIO_CFG_INDEX(pin); // (gpio & 0x1F) >> 3
|
|
int offset = SunXI::GPIO_CFG_OFFSET(pin); // ((gpio & 0x1F) & 0x7) << 2
|
|
LOGTRACE("%s gpio(%d) bank(%d) index(%d) offset(%d)", __PRETTY_FUNCTION__, pin, bank, index, offset)
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
if (pud != -1) {
|
|
set_pullupdn(pin, pud);
|
|
}
|
|
regval = *(&pio->CFG[0 + index]);
|
|
regval &= ~(0x7 << offset); // 0xf?
|
|
if (SunXI::INPUT == direction) {
|
|
pio->CFG[0 + index] = regval;
|
|
} else if (SunXI::OUTPUT == direction) {
|
|
regval |= (1 << offset);
|
|
pio->CFG[0 + index] = regval;
|
|
} else {
|
|
LOGERROR("line:%d gpio number error %d", __LINE__, pin)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::sunxi_output_gpio(int pin, int value)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
if (pin < 0) {
|
|
LOGWARN("Invalid GPIO Num")
|
|
return;
|
|
}
|
|
int bank = SunXI::GPIO_BANK(pin); // gpio >> 5
|
|
int num = SunXI::GPIO_NUM(pin); // gpio & 0x1F
|
|
|
|
LOGTRACE("%s gpio(%d) bank(%d) num(%d) value(%d)", __PRETTY_FUNCTION__, pin, bank, num, value)
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
if (value == 0)
|
|
pio->DAT &= ~(1 << num);
|
|
else
|
|
pio->DAT |= (1 << num);
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::sunxi_set_all_gpios(array<uint32_t, 12> &mask, array<uint32_t, 12> &value)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
for (size_t bank = 0; bank < mask.size(); bank++) {
|
|
if (mask[bank] == 0) {
|
|
continue;
|
|
}
|
|
|
|
volatile SunXI::sunxi_gpio_t *pio;
|
|
if (bank < 11) {
|
|
pio = &(pio_map->gpio_bank[bank]);
|
|
} else {
|
|
pio = &(r_pio_map->gpio_bank[bank - 11]);
|
|
}
|
|
|
|
uint32_t reg_val = pio->DAT;
|
|
reg_val &= ~mask[bank];
|
|
reg_val |= value[bank];
|
|
pio->DAT = reg_val;
|
|
}
|
|
}
|
|
|
|
int GPIOBUS_BananaM2p::sunxi_input_gpio(int pin) const
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
uint32_t regval = 0;
|
|
int bank = SunXI::GPIO_BANK(pin); // gpio >> 5
|
|
int num = SunXI::GPIO_NUM(pin); // gpio & 0x1F
|
|
|
|
LOGTRACE("%s gpio(%d) bank(%d) num(%d)", __PRETTY_FUNCTION__, pin, bank, num)
|
|
volatile SunXI::sunxi_gpio_t *pio = &(pio_map->gpio_bank[bank]);
|
|
/* DK, for PL and PM */
|
|
if (bank >= 11) {
|
|
bank -= 11;
|
|
pio = &(r_pio_map->gpio_bank[bank]);
|
|
}
|
|
|
|
regval = pio->DAT;
|
|
regval = regval >> num;
|
|
regval &= 1;
|
|
return regval;
|
|
}
|
|
|
|
void GPIOBUS_BananaM2p::set_pullupdn(int pin, int pud)
|
|
{
|
|
GPIO_FUNCTION_TRACE
|
|
int clk_offset = SunXI::PULLUPDNCLK_OFFSET + (pin / 32);
|
|
int shift = (pin % 32);
|
|
|
|
#ifdef BPI
|
|
if (bpi_found == 1) {
|
|
gpio = *(pinTobcm_BP + pin);
|
|
return sunxi_set_pullupdn(pin, pud);
|
|
}
|
|
#endif
|
|
if (pud == SunXI::PUD_DOWN)
|
|
*(gpio_map + SunXI::PULLUPDN_OFFSET) = (*(gpio_map + SunXI::PULLUPDN_OFFSET) & ~3) | SunXI::PUD_DOWN;
|
|
else if (pud == SunXI::PUD_UP)
|
|
*(gpio_map + SunXI::PULLUPDN_OFFSET) = (*(gpio_map + SunXI::PULLUPDN_OFFSET) & ~3) | SunXI::PUD_UP;
|
|
else // pud == PUD_OFF
|
|
*(gpio_map + SunXI::PULLUPDN_OFFSET) &= ~3;
|
|
|
|
SunXI::short_wait();
|
|
*(gpio_map + clk_offset) = 1 << shift;
|
|
SunXI::short_wait();
|
|
*(gpio_map + SunXI::PULLUPDN_OFFSET) &= ~3;
|
|
*(gpio_map + clk_offset) = 0;
|
|
}
|