#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sequoia.c,v 1.18 2025/11/29 22:08:06 andvar Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/device.h>
#include <sys/syslog.h>
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <dev/isa/isareg.h>
#include <dev/ofisa/ofisavar.h>
#include <machine/isa_machdep.h>
#include <shark/shark/sequoia.h>
#include <shark/shark/shark_fiq.h>
#include <arm/cpufunc.h>
#include <dev/ofw/openfirm.h>
#define SEQUOIA_1GPIO PMC_GPCR_REG
#define SEQUOIA_2GPIO SEQ2_OGPIOCR_REG
#define SCR_DETECT_DIR GPIOCR2_M_GPIOBDIR0
#define SCR_DETECT GPIOCR2_M_GPIOBDATA0
#define SCR_POWER_DIR GPCR_M_GPIODIR0
#define SCR_POWER GPCR_M_GPIODATA0
#define SCR_RESET_DIR GPCR_M_GPIODIR1
#define SCR_RESET GPCR_M_GPIODATA1
#define SCR_CLOCK_DIR OGPIOCR_M_GPIODIR6
#define SCR_CLOCK OGPIOCR_M_GPIODATA6
#define SCR_DATA_IN_DIR GPCR_M_GPIODIR2
#define SCR_DATA_IN GPCR_M_GPIODATA2
#define SCR_DATA_OUT_DIR OGPIOCR_M_GPIODIR5
#define SCR_DATA_OUT OGPIOCR_M_GPIODATA5
#define SCR_BUGA_DIR OGPIOCR_M_GPIODIR4
#define SCR_BUGA OGPIOCR_M_GPIODATA4
#define SCR_BUGB_DIR OGPIOCR_M_GPIODIR7
#define SCR_BUGB OGPIOCR_M_GPIODATA7
#define LED_BILED_YELLOW_BIT FOMPCR_M_PCON5
#define LED_BILED_GREEN_BIT FOMPCR_M_PCON6
#define LED_DEBUG_YELLOW_BIT FOMPCR_M_PCON7
#define LED_DEBUG_GREEN_BIT FOMPCR_M_PCON8
#define LED_BILED_NONE 0
#define LED_BILED_GREEN 1
#define LED_BILED_YELLOW 2
#define LED_BILED_RED 3
#define LED_TIMEOUT hz / 20
#define LED_NET_ACTIVE (1000000/hz) * LED_TIMEOUT
static bus_space_handle_t sequoia_ioh;
static struct timeval ledLastActive;
static int ledColor;
static int ledBlockCount;
int sequoia_index_cache = -1;
static callout_t led_timo_ch;
static u_int sequoiaLock_savedints;
static bool sequoiaLock_held;
static void ledSetBiled(int color);
static void ledTimeout(void *arg);
void sequoiaInit(void)
{
u_int16_t seqReg;
if (bus_space_map(&isa_io_bs_tag, SEQUOIA_BASE, SEQUOIA_NPORTS, 0, &sequoia_ioh))
{
panic("SequoiaInit: io mapping failed");
}
sequoiaLock();
sequoiaRead(SEQR_SEQPSR1_REG,&seqReg);
SET(seqReg,SEQPSR1_M_TAGDEN);
sequoiaWrite(SEQR_SEQPSR1_REG,seqReg);
sequoiaRead(SEQR_SEQPSR3_REG,&seqReg);
CLR(seqReg,SEQPSR3_M_PC5PINEN);
CLR(seqReg,SEQPSR3_M_PC6PINEN);
CLR(seqReg,SEQPSR3_M_PC7PINEN);
CLR(seqReg,SEQPSR3_M_PC8PINEN);
sequoiaWrite(SEQR_SEQPSR3_REG,seqReg);
sequoiaRead (PMC_FOMPCR_REG, &seqReg);
CLR(seqReg,LED_BILED_YELLOW_BIT);
CLR(seqReg,LED_BILED_GREEN_BIT);
SET(seqReg,LED_DEBUG_YELLOW_BIT);
CLR(seqReg,LED_DEBUG_GREEN_BIT);
sequoiaWrite(PMC_FOMPCR_REG, seqReg);
sequoiaRead(SEQUOIA_1GPIO,&seqReg);
SET(seqReg,SCR_POWER_DIR);
SET(seqReg,SCR_RESET_DIR);
CLR(seqReg,SCR_DATA_IN_DIR);
CLR(seqReg,SCR_POWER);
SET(seqReg,SCR_RESET);
sequoiaWrite(SEQUOIA_1GPIO,seqReg);
sequoiaRead(SEQ2_SEQ2PSR_REG,&seqReg);
SET(seqReg,SEQ2PSR_M_DPBUSEN);
CLR(seqReg,SEQ2PSR_M_GPIOPINEN);
sequoiaWrite(SEQ2_SEQ2PSR_REG,seqReg);
sequoiaRead(SEQUOIA_2GPIO,&seqReg);
SET(seqReg,SCR_BUGA_DIR);
SET(seqReg,SCR_DATA_OUT_DIR);
SET(seqReg,SCR_CLOCK_DIR);
SET(seqReg,SCR_BUGB_DIR);
CLR(seqReg,SCR_BUGA);
CLR(seqReg,SCR_BUGB);
CLR(seqReg,SCR_CLOCK);
sequoiaWrite(SEQUOIA_2GPIO,seqReg);
sequoiaRead(SEQR_SEQPSR2_REG,&seqReg);
SET(seqReg,SEQPSR2_M_DIRTYPINEN);
SET(seqReg,SEQPSR2_M_GPIOB0PINEN);
sequoiaWrite(SEQR_SEQPSR2_REG,seqReg);
sequoiaRead(PMC_GPIOCR2_REG,&seqReg);
CLR(seqReg,SCR_DETECT_DIR);
sequoiaWrite(PMC_GPIOCR2_REG,seqReg);
sequoiaRead(PMC_SCCR_REG, &seqReg);
sequoiaWrite(PMC_SCCR_REG, seqReg | SCCR_M_PCSTGDIS);
sequoiaUnlock();
ledColor = LED_BILED_GREEN;
ledLastActive.tv_usec = 0;
ledLastActive.tv_sec = 0;
ledBlockCount = 0;
callout_init(&led_timo_ch, 0);
callout_reset(&led_timo_ch, LED_TIMEOUT, ledTimeout, NULL);
}
void
sequoiaLock(void)
{
sequoiaLock_savedints = disable_interrupts(I32_bit | F32_bit);
KASSERT(!sequoiaLock_held);
sequoiaLock_held = true;
}
void
sequoiaUnlock(void)
{
KASSERT(sequoiaLock_held);
sequoiaLock_held = false;
restore_interrupts(sequoiaLock_savedints);
}
bool
sequoiaIsLocked(void)
{
return sequoiaLock_held;
}
void consXTvOn(void)
{
u_int16_t savedPSR3;
u_int16_t savedFMPCR;
sequoiaLock();
sequoiaRead (SEQR_SEQPSR3_REG, &savedPSR3);
sequoiaWrite(SEQR_SEQPSR3_REG, (savedPSR3 | SEQPSR3_M_PC3PINEN));
sequoiaRead (PMC_FOMPCR_REG, &savedFMPCR);
sequoiaWrite(PMC_FOMPCR_REG, (savedFMPCR | FOMPCR_M_PCON3));
sequoiaUnlock();
}
void consXTvOff(void)
{
u_int16_t savedPSR3;
u_int16_t savedFMPCR;
sequoiaLock();
sequoiaRead (SEQR_SEQPSR3_REG, &savedPSR3);
sequoiaWrite(SEQR_SEQPSR3_REG, (savedPSR3 & ~SEQPSR3_M_PC3PINEN));
sequoiaRead (PMC_FOMPCR_REG, &savedFMPCR);
sequoiaWrite(PMC_FOMPCR_REG, (savedFMPCR & ~FOMPCR_M_PCON3));
sequoiaUnlock();
}
int scrGetDetect (void)
{
int r;
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(PMC_GPIOCR2_REG,&seqReg);
sequoiaUnlock();
if (ISSET(seqReg,SCR_DETECT))
{
r = 0;
} else
{
r = 1;
}
return r;
}
void scrSetPower (int value)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_1GPIO,&seqReg);
if (value)
{
SET(seqReg,SCR_POWER);
} else
{
CLR(seqReg,SCR_POWER);
}
sequoiaWrite(SEQUOIA_1GPIO,seqReg);
sequoiaUnlock();
}
void scrSetClock (int value)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_2GPIO,&seqReg);
if (value)
{
SET(seqReg,SCR_CLOCK);
} else
{
CLR(seqReg,SCR_CLOCK);
}
sequoiaWrite(SEQUOIA_2GPIO,seqReg);
sequoiaUnlock();
}
void scrSetReset (int value)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_1GPIO,&seqReg);
if (value)
{
SET(seqReg,SCR_RESET);
} else
{
CLR(seqReg,SCR_RESET);
}
sequoiaWrite(SEQUOIA_1GPIO,seqReg);
sequoiaUnlock();
}
void scrSetDataHighZ (void)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_2GPIO,&seqReg);
CLR(seqReg,SCR_DATA_OUT);
sequoiaWrite(SEQUOIA_2GPIO,seqReg);
sequoiaUnlock();
}
void scrSetData (int value)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_2GPIO,&seqReg);
if (value )
{
CLR(seqReg,SCR_DATA_OUT);
} else
{
SET(seqReg,SCR_DATA_OUT);
}
sequoiaWrite(SEQUOIA_2GPIO,seqReg);
sequoiaUnlock();
}
int scrGetData (void)
{
int r;
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_1GPIO,&seqReg);
sequoiaUnlock();
if (ISSET(seqReg,SCR_DATA_IN))
{
r = 1;
} else
{
r = 0;
}
return r;
}
void ledNetActive (void)
{
getmicrotime(&ledLastActive);
}
void ledNetBlock (void)
{
ledBlockCount++;
}
void ledNetUnblock (void)
{
ledBlockCount--;
}
void ledPanic (void)
{
ledSetBiled(LED_BILED_RED);
}
static void ledTimeout(void *arg)
{
int timeSpan;
struct timeval now;
getmicrotime(&now);
if(now.tv_sec == ledLastActive.tv_sec)
{
timeSpan = now.tv_usec - ledLastActive.tv_usec;
}
else if (now.tv_sec - 10 < ledLastActive.tv_sec)
{
timeSpan = (1000000 + now.tv_usec) - ledLastActive.tv_usec;
}
else
{
timeSpan = LED_NET_ACTIVE * 2;
}
if(ledBlockCount)
{
if(ledColor == LED_BILED_YELLOW)
{
ledSetBiled(LED_BILED_NONE);
}
else
{
ledSetBiled(LED_BILED_YELLOW);
}
}
else if (timeSpan < LED_NET_ACTIVE)
{
if(ledColor == LED_BILED_GREEN)
{
ledSetBiled(LED_BILED_NONE);
}
else
{
ledSetBiled(LED_BILED_GREEN);
}
}
else
{
if(ledColor != LED_BILED_GREEN)
{
ledSetBiled(LED_BILED_GREEN);
}
}
callout_reset(&led_timo_ch, LED_TIMEOUT, ledTimeout, NULL);
}
static void ledSetBiled(int color)
{
u_int16_t seqReg;
sequoiaLock();
ledColor = color;
sequoiaRead (PMC_FOMPCR_REG, &seqReg);
switch(color)
{
case LED_BILED_NONE:
SET(seqReg,LED_BILED_YELLOW_BIT);
SET(seqReg,LED_BILED_GREEN_BIT);
break;
case LED_BILED_YELLOW:
CLR(seqReg,LED_BILED_YELLOW_BIT);
SET(seqReg,LED_BILED_GREEN_BIT);
break;
case LED_BILED_GREEN:
SET(seqReg,LED_BILED_YELLOW_BIT);
CLR(seqReg,LED_BILED_GREEN_BIT);
break;
case LED_BILED_RED:
CLR(seqReg,LED_BILED_YELLOW_BIT);
CLR(seqReg,LED_BILED_GREEN_BIT);
break;
default:
panic("invalid color %x",color);
break;
}
sequoiaWrite(PMC_FOMPCR_REG, seqReg);
sequoiaUnlock();
}
int hwGetRev(void)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SR_POR_REG,&seqReg);
sequoiaUnlock();
seqReg = seqReg >> POR_V_MISCCF0;
seqReg = seqReg & 0x7;
return seqReg;
}
void sequoiaWrite(int reg,u_int16_t seqReg)
{
KASSERT(sequoiaLock_held);
if (reg == PMC_FOMPCR_REG) {
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_INDEX_OFFSET,reg);
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_DATA_OFFSET,
seqReg | (FOMPCR_M_PCON4));
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_INDEX_OFFSET,
PMC_SLPMPCR_REG);
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_DATA_OFFSET,
seqReg & ~(SLPMPCR_M_PCSLP4));
sequoia_index_cache = PMC_SLPMPCR_REG;
} else {
if(sequoia_index_cache != reg)
{
sequoia_index_cache = reg;
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_INDEX_OFFSET,reg);
}
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_DATA_OFFSET,seqReg);
}
}
void sequoiaRead (int reg,u_int16_t * seqReg_ptr)
{
KASSERT(sequoiaLock_held);
if(sequoia_index_cache != reg)
{
sequoia_index_cache = reg;
bus_space_write_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_INDEX_OFFSET,reg);
}
*seqReg_ptr = bus_space_read_2(&isa_io_bs_tag,sequoia_ioh,SEQUOIA_DATA_OFFSET);
}
void ledSetDebug(int command)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead (PMC_FOMPCR_REG, &seqReg);
switch (command)
{
case LED_DEBUG_STATE_0:
CLR(seqReg,LED_DEBUG_YELLOW_BIT);
CLR(seqReg,LED_DEBUG_GREEN_BIT);
break;
case LED_DEBUG_STATE_1:
SET(seqReg,LED_DEBUG_YELLOW_BIT);
CLR(seqReg,LED_DEBUG_GREEN_BIT);
break;
case LED_DEBUG_STATE_2:
CLR(seqReg,LED_DEBUG_YELLOW_BIT);
SET(seqReg,LED_DEBUG_GREEN_BIT);
break;
case LED_DEBUG_STATE_3:
SET(seqReg,LED_DEBUG_YELLOW_BIT);
SET(seqReg,LED_DEBUG_GREEN_BIT);
break;
case LED_DEBUG_YELLOW_ON:
SET(seqReg,LED_DEBUG_YELLOW_BIT);
break;
case LED_DEBUG_YELLOW_OFF:
CLR(seqReg,LED_DEBUG_YELLOW_BIT);
break;
case LED_DEBUG_GREEN_ON:
SET(seqReg,LED_DEBUG_GREEN_BIT);
break;
case LED_DEBUG_GREEN_OFF:
CLR(seqReg,LED_DEBUG_GREEN_BIT);
break;
default:
panic("ledSetDebug: invalid command %d",command);
break;
}
sequoiaWrite(PMC_FOMPCR_REG, seqReg);
sequoiaUnlock();
}
int testPin=0;
void scrToggleTestPin (void)
{
u_int16_t seqReg;
sequoiaLock();
sequoiaRead(SEQUOIA_2GPIO,&seqReg);
if (testPin)
{
testPin = 0;
CLR(seqReg,SCR_BUGA);
}
else
{
SET(seqReg,SCR_BUGA);
testPin = 1;
}
sequoiaWrite(SEQUOIA_2GPIO,seqReg);
sequoiaUnlock();
}
static const struct device_compatible_entry compat_data[] = {
{ .compat = "sequoia" },
DEVICE_COMPAT_EOL
};
static int
sequoia_cfprint(void *aux, const char *pnp)
{
struct ofbus_attach_args *oba = aux;
if (pnp != NULL) {
aprint_normal("%s at %s", oba->oba_ofname, pnp);
}
return UNCONF;
}
static int
sequoia_match(device_t parent, cfdata_t cf, void *aux)
{
struct ofbus_attach_args *oba = aux;
return of_compatible_match(oba->oba_phandle, compat_data) ? 100 : 0;
}
static void
sequoia_attach(device_t parent, device_t self, void *aux)
{
const struct ofbus_attach_args *oba = aux;
struct ofbus_attach_args noba = *oba;
aprint_naive("\n");
aprint_normal("\n");
noba.oba_busname = "sequoia";
strlcpy(noba.oba_ofname, "dec,dnard-i2c", sizeof(noba.oba_ofname));
config_found(self, &noba, sequoia_cfprint,
CFARGS(.iattr = "sequoia"));
config_found(self, aux, ofisaprint,
CFARGS(.iattr = "ofisa_subclass"));
}
CFATTACH_DECL_NEW(sequoia, 0,
sequoia_match, sequoia_attach, NULL, NULL);