imx8: add work in progress i.MX8MQ kernel for the mntreform2 laptop
This is a work in progress port to the mntreform2 laptop. Working so far: - mmu (same as raspberry pi 3b+) - arm generic timer - gicv3 - uart1 - enet With access to the uart, one can netboot this kernel in u-boot using the following commands: > dhcp > bootm
This commit is contained in:
parent
9126ee3eea
commit
fff070f2cb
19 changed files with 4740 additions and 0 deletions
212
sys/src/9/imx8/cache.v8.s
Normal file
212
sys/src/9/imx8/cache.v8.s
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include "sysreg.h"
|
||||
|
||||
#undef SYSREG
|
||||
#define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
|
||||
|
||||
/*
|
||||
* instruction cache operations
|
||||
*/
|
||||
TEXT cacheiinvse(SB), 1, $-4
|
||||
MOVWU len+8(FP), R2
|
||||
ADD R0, R2
|
||||
|
||||
MRS DAIF, R11
|
||||
MSR $0x2, DAIFSet
|
||||
MOVWU $1, R10
|
||||
MSR R10, CSSELR_EL1
|
||||
ISB $SY
|
||||
MRS CCSIDR_EL1, R4
|
||||
|
||||
ANDW $7, R4
|
||||
ADDW $4, R4 // log2(linelen)
|
||||
LSL R4, R10
|
||||
LSR R4, R0
|
||||
LSL R4, R0
|
||||
|
||||
_iinvse:
|
||||
IC R0, 3,7,5,1 // IVAU
|
||||
ADD R10, R0
|
||||
CMP R0, R2
|
||||
BGT _iinvse
|
||||
DSB $NSH
|
||||
ISB $SY
|
||||
MSR R11, DAIF
|
||||
RETURN
|
||||
|
||||
TEXT cacheiinv(SB), 1, $-4
|
||||
IC R0, 0,7,5,0 // IALLU
|
||||
DSB $NSH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
TEXT cacheuwbinv(SB), 1, $0
|
||||
BL cachedwbinv(SB)
|
||||
BL cacheiinv(SB)
|
||||
RETURN
|
||||
|
||||
/*
|
||||
* data cache operations
|
||||
*/
|
||||
TEXT cachedwbse(SB), 1, $-4
|
||||
MOV LR, R29
|
||||
BL cachedva<>(SB)
|
||||
TEXT dccvac(SB), 1, $-4
|
||||
DC R0, 3,7,10,1 // CVAC
|
||||
RETURN
|
||||
|
||||
TEXT cacheduwbse(SB), 1, $-4
|
||||
MOV LR, R29
|
||||
BL cachedva<>(SB)
|
||||
TEXT dccvau(SB), 1, $-4
|
||||
DC R0, 3,7,11,1 // CVAU
|
||||
RETURN
|
||||
|
||||
TEXT cachedinvse(SB), 1, $-4
|
||||
MOV LR, R29
|
||||
BL cachedva<>(SB)
|
||||
TEXT dcivac(SB), 1, $-4
|
||||
DC R0, 0,7,6,1 // IVAC
|
||||
RETURN
|
||||
|
||||
TEXT cachedwbinvse(SB), 1, $-4
|
||||
MOV LR, R29
|
||||
BL cachedva<>(SB)
|
||||
TEXT dccivac(SB), 1, $-4
|
||||
DC R0, 3,7,14,1 // CIVAC
|
||||
RETURN
|
||||
|
||||
TEXT cachedva<>(SB), 1, $-4
|
||||
MOV LR, R1
|
||||
MOVWU len+8(FP), R2
|
||||
ADD R0, R2
|
||||
|
||||
MRS DAIF, R11
|
||||
MSR $0x2, DAIFSet
|
||||
MOVWU $0, R10
|
||||
MSR R10, CSSELR_EL1
|
||||
ISB $SY
|
||||
MRS CCSIDR_EL1, R4
|
||||
|
||||
ANDW $7, R4
|
||||
ADDW $4, R4 // log2(linelen)
|
||||
MOVWU $1, R10
|
||||
LSL R4, R10
|
||||
LSR R4, R0
|
||||
LSL R4, R0
|
||||
|
||||
DSB $SY
|
||||
ISB $SY
|
||||
_cachedva:
|
||||
BL (R1)
|
||||
ADD R10, R0
|
||||
CMP R0, R2
|
||||
BGT _cachedva
|
||||
DSB $SY
|
||||
ISB $SY
|
||||
MSR R11, DAIF
|
||||
RET R29
|
||||
|
||||
/*
|
||||
* l1 cache operations
|
||||
*/
|
||||
TEXT cachedwb(SB), 1, $-4
|
||||
MOVWU $0, R0
|
||||
_cachedwb:
|
||||
MOV LR, R29
|
||||
BL cachedsw<>(SB)
|
||||
TEXT dccsw(SB), 1, $-4
|
||||
DC R0, 0,7,10,2 // CSW
|
||||
RETURN
|
||||
|
||||
TEXT cachedinv(SB), 1, $-4
|
||||
MOVWU $0, R0
|
||||
_cachedinv:
|
||||
MOV LR, R29
|
||||
BL cachedsw<>(SB)
|
||||
TEXT dcisw(SB), 1, $-4
|
||||
DC R0, 0,7,6,2 // ISW
|
||||
RETURN
|
||||
|
||||
TEXT cachedwbinv(SB), 1, $-4
|
||||
MOVWU $0, R0
|
||||
_cachedwbinv:
|
||||
MOV LR, R29
|
||||
BL cachedsw<>(SB)
|
||||
TEXT dccisw(SB), 1, $-4
|
||||
DC R0, 0,7,14,2 // CISW
|
||||
RETURN
|
||||
|
||||
/*
|
||||
* l2 cache operations
|
||||
*/
|
||||
TEXT l2cacheuwb(SB), 1, $-4
|
||||
MOVWU $1, R0
|
||||
B _cachedwb
|
||||
TEXT l2cacheuinv(SB), 1, $-4
|
||||
MOVWU $1, R0
|
||||
B _cachedinv
|
||||
TEXT l2cacheuwbinv(SB), 1, $-4
|
||||
MOVWU $1, R0
|
||||
B _cachedwbinv
|
||||
|
||||
TEXT cachesize(SB), 1, $-4
|
||||
MRS DAIF, R11
|
||||
MSR $0x2, DAIFSet
|
||||
MSR R0, CSSELR_EL1
|
||||
ISB $SY
|
||||
MRS CCSIDR_EL1, R0
|
||||
MSR R11, DAIF
|
||||
RETURN
|
||||
|
||||
TEXT cachedsw<>(SB), 1, $-4
|
||||
MOV LR, R1
|
||||
|
||||
MRS DAIF, R11
|
||||
MSR $0x2, DAIFSet
|
||||
ADDW R0, R0, R8
|
||||
MSR R8, CSSELR_EL1
|
||||
ISB $SY
|
||||
MRS CCSIDR_EL1, R4
|
||||
|
||||
LSR $3, R4, R7
|
||||
ANDW $1023, R7 // lastway
|
||||
ADDW $1, R7, R5 // #ways
|
||||
|
||||
LSR $13, R4, R2
|
||||
ANDW $32767, R2 // lastset
|
||||
ADDW $1, R2 // #sets
|
||||
|
||||
ANDW $7, R4
|
||||
ADDW $4, R4 // log2(linelen)
|
||||
|
||||
MOVWU $32, R3 // wayshift = 32 - log2(#ways)
|
||||
_countlog2ways:
|
||||
CBZ R7, _loop // lastway == 0?
|
||||
LSR $1, R7 // lastway >>= 1
|
||||
SUB $1, R3 // wayshift--
|
||||
B _countlog2ways
|
||||
_loop:
|
||||
DSB $SY
|
||||
ISB $SY
|
||||
_nextway:
|
||||
MOVWU $0, R6 // set
|
||||
_nextset:
|
||||
LSL R3, R7, R0 // way<<wayshift
|
||||
LSL R4, R6, R9 // set<<log2(linelen)
|
||||
ORRW R8, R0 // level
|
||||
ORRW R9, R0 // setway
|
||||
|
||||
BL (R1) // op(setway)
|
||||
|
||||
ADDW $1, R6 // set++
|
||||
CMPW R2, R6
|
||||
BLT _nextset
|
||||
|
||||
ADDW $1, R7 // way++
|
||||
CMPW R5, R7
|
||||
BLT _nextway
|
||||
|
||||
DSB $SY
|
||||
ISB $SY
|
||||
MSR R11, DAIF
|
||||
RET R29
|
114
sys/src/9/imx8/clock.c
Normal file
114
sys/src/9/imx8/clock.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "ureg.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
static uvlong freq;
|
||||
|
||||
enum {
|
||||
Enable = 1<<0,
|
||||
Imask = 1<<1,
|
||||
Istatus = 1<<2,
|
||||
};
|
||||
|
||||
void
|
||||
clockshutdown(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
localclockintr(Ureg *ureg, void *)
|
||||
{
|
||||
timerintr(ureg, 0);
|
||||
}
|
||||
|
||||
void
|
||||
clockinit(void)
|
||||
{
|
||||
syswr(PMCR_EL0, 1<<6 | 7);
|
||||
syswr(PMCNTENSET, 1<<31);
|
||||
syswr(PMUSERENR_EL0, 1<<2);
|
||||
syswr(CNTKCTL_EL1, 1<<1);
|
||||
|
||||
syswr(CNTP_TVAL_EL0, ~0UL);
|
||||
syswr(CNTP_CTL_EL0, Enable);
|
||||
|
||||
if(m->machno == 0){
|
||||
freq = sysrd(CNTFRQ_EL0);
|
||||
print("timer frequency %lld Hz\n", freq);
|
||||
}
|
||||
|
||||
intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock");
|
||||
}
|
||||
|
||||
void
|
||||
timerset(uvlong next)
|
||||
{
|
||||
uvlong now;
|
||||
long period;
|
||||
|
||||
now = fastticks(nil);
|
||||
period = next - now;
|
||||
syswr(CNTP_TVAL_EL0, period);
|
||||
}
|
||||
|
||||
uvlong
|
||||
fastticks(uvlong *hz)
|
||||
{
|
||||
if(hz)
|
||||
*hz = freq;
|
||||
return sysrd(CNTPCT_EL0);
|
||||
}
|
||||
|
||||
ulong
|
||||
perfticks(void)
|
||||
{
|
||||
return fastticks(nil);
|
||||
}
|
||||
|
||||
ulong
|
||||
µs(void)
|
||||
{
|
||||
uvlong hz;
|
||||
uvlong t = fastticks(&hz);
|
||||
return (t * 1000000ULL) / hz;
|
||||
}
|
||||
|
||||
void
|
||||
microdelay(int n)
|
||||
{
|
||||
ulong now;
|
||||
|
||||
now = µs();
|
||||
while(µs() - now < n);
|
||||
}
|
||||
|
||||
void
|
||||
delay(int n)
|
||||
{
|
||||
while(--n >= 0)
|
||||
microdelay(1000);
|
||||
}
|
||||
|
||||
void
|
||||
synccycles(void)
|
||||
{
|
||||
static Ref r1, r2;
|
||||
int s;
|
||||
|
||||
s = splhi();
|
||||
r2.ref = 0;
|
||||
incref(&r1);
|
||||
while(r1.ref != conf.nmach)
|
||||
;
|
||||
// syswr(PMCR_EL0, 1<<6 | 7);
|
||||
incref(&r2);
|
||||
while(r2.ref != conf.nmach)
|
||||
;
|
||||
r1.ref = 0;
|
||||
splx(s);
|
||||
}
|
218
sys/src/9/imx8/dat.h
Normal file
218
sys/src/9/imx8/dat.h
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Time.
|
||||
*
|
||||
* HZ should divide 1000 evenly, ideally.
|
||||
* 100, 125, 200, 250 and 333 are okay.
|
||||
*/
|
||||
#define HZ 100 /* clock frequency */
|
||||
#define MS2HZ (1000/HZ) /* millisec per clock tick */
|
||||
#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
|
||||
|
||||
enum {
|
||||
Mhz = 1000 * 1000,
|
||||
};
|
||||
|
||||
typedef struct Conf Conf;
|
||||
typedef struct Confmem Confmem;
|
||||
typedef struct FPsave FPsave;
|
||||
typedef struct PFPU PFPU;
|
||||
typedef struct ISAConf ISAConf;
|
||||
typedef struct Label Label;
|
||||
typedef struct Lock Lock;
|
||||
typedef struct Memcache Memcache;
|
||||
typedef struct MMMU MMMU;
|
||||
typedef struct Mach Mach;
|
||||
typedef struct Page Page;
|
||||
typedef struct PhysUart PhysUart;
|
||||
typedef struct Pcidev Pcidev;
|
||||
typedef struct PMMU PMMU;
|
||||
typedef struct Proc Proc;
|
||||
typedef u64int PTE;
|
||||
typedef struct Soc Soc;
|
||||
typedef struct Uart Uart;
|
||||
typedef struct Ureg Ureg;
|
||||
typedef uvlong Tval;
|
||||
typedef void KMap;
|
||||
|
||||
#pragma incomplete Pcidev
|
||||
#pragma incomplete Ureg
|
||||
|
||||
#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */
|
||||
|
||||
/*
|
||||
* parameters for sysproc.c
|
||||
*/
|
||||
#define AOUT_MAGIC (R_MAGIC)
|
||||
|
||||
struct Lock
|
||||
{
|
||||
ulong key;
|
||||
u32int sr;
|
||||
uintptr pc;
|
||||
Proc* p;
|
||||
Mach* m;
|
||||
int isilock;
|
||||
};
|
||||
|
||||
struct Label
|
||||
{
|
||||
uintptr sp;
|
||||
uintptr pc;
|
||||
};
|
||||
|
||||
struct FPsave
|
||||
{
|
||||
uvlong regs[32][2];
|
||||
|
||||
ulong control;
|
||||
ulong status;
|
||||
};
|
||||
|
||||
struct PFPU
|
||||
{
|
||||
FPsave fpsave[1];
|
||||
|
||||
int fpstate;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FPinit,
|
||||
FPactive,
|
||||
FPinactive,
|
||||
|
||||
/* bits or'd with the state */
|
||||
FPillegal= 0x100,
|
||||
};
|
||||
|
||||
struct Confmem
|
||||
{
|
||||
uintptr base;
|
||||
ulong npage;
|
||||
uintptr limit;
|
||||
uintptr kbase;
|
||||
uintptr klimit;
|
||||
};
|
||||
|
||||
struct Conf
|
||||
{
|
||||
ulong nmach; /* processors */
|
||||
ulong nproc; /* processes */
|
||||
Confmem mem[3]; /* physical memory */
|
||||
ulong npage; /* total physical pages of memory */
|
||||
ulong upages; /* user page pool */
|
||||
ulong copymode; /* 0 is copy on write, 1 is copy on reference */
|
||||
ulong ialloc; /* max interrupt time allocation in bytes */
|
||||
ulong pipeqsize; /* size in bytes of pipe queues */
|
||||
ulong nimage; /* number of page cache image headers */
|
||||
ulong nswap; /* number of swap pages */
|
||||
int nswppo; /* max # of pageouts per segment pass */
|
||||
ulong hz; /* processor cycle freq */
|
||||
ulong mhz;
|
||||
int monitor; /* flag */
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU stuff in Mach.
|
||||
*/
|
||||
struct MMMU
|
||||
{
|
||||
PTE* mmutop; /* first level user page table */
|
||||
};
|
||||
|
||||
/*
|
||||
* MMU stuff in proc
|
||||
*/
|
||||
#define NCOLOR 1 /* 1 level cache, don't worry about VCE's */
|
||||
|
||||
struct PMMU
|
||||
{
|
||||
union {
|
||||
Page *mmufree; /* mmuhead[0] is freelist head */
|
||||
Page *mmuhead[PTLEVELS];
|
||||
};
|
||||
Page *mmutail[PTLEVELS];
|
||||
int asid;
|
||||
uintptr tpidr;
|
||||
};
|
||||
|
||||
#include "../port/portdat.h"
|
||||
|
||||
struct Mach
|
||||
{
|
||||
int machno; /* physical id of processor */
|
||||
uintptr splpc; /* pc of last caller to splhi */
|
||||
Proc* proc; /* current process on this processor */
|
||||
/* end of offsets known to asm */
|
||||
|
||||
MMMU;
|
||||
|
||||
PMach;
|
||||
|
||||
int cputype;
|
||||
ulong delayloop;
|
||||
int cpumhz;
|
||||
uvlong cpuhz; /* speed of cpu */
|
||||
|
||||
int stack[1];
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
char machs[MAXMACH]; /* active CPUs */
|
||||
int exiting; /* shutdown */
|
||||
}active;
|
||||
|
||||
#define MACHP(n) ((Mach*)MACHADDR(n))
|
||||
|
||||
extern register Mach* m; /* R27 */
|
||||
extern register Proc* up; /* R26 */
|
||||
extern int normalprint;
|
||||
|
||||
/*
|
||||
* a parsed plan9.ini line
|
||||
*/
|
||||
#define NISAOPT 8
|
||||
|
||||
struct ISAConf {
|
||||
char *type;
|
||||
uvlong port;
|
||||
int irq;
|
||||
ulong dma;
|
||||
ulong mem;
|
||||
ulong size;
|
||||
ulong freq;
|
||||
|
||||
int nopt;
|
||||
char *opt[NISAOPT];
|
||||
};
|
||||
|
||||
/*
|
||||
* Horrid. But the alternative is 'defined'.
|
||||
*/
|
||||
#ifdef _DBGC_
|
||||
#define DBGFLG (dbgflg[_DBGC_])
|
||||
#else
|
||||
#define DBGFLG (0)
|
||||
#endif /* _DBGC_ */
|
||||
|
||||
int vflag;
|
||||
extern char dbgflg[256];
|
||||
|
||||
#define dbgprint print /* for now */
|
||||
|
||||
/*
|
||||
* hardware info about a device
|
||||
*/
|
||||
typedef struct {
|
||||
ulong port;
|
||||
int size;
|
||||
} Devport;
|
||||
|
||||
struct DevConf
|
||||
{
|
||||
ulong intnum; /* interrupt number */
|
||||
char *type; /* card type, malloced */
|
||||
int nports; /* Number of ports */
|
||||
Devport *ports; /* The ports themselves */
|
||||
};
|
706
sys/src/9/imx8/etherimx.c
Normal file
706
sys/src/9/imx8/etherimx.c
Normal file
|
@ -0,0 +1,706 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/netif.h"
|
||||
#include "../port/etherif.h"
|
||||
#include "../port/ethermii.h"
|
||||
|
||||
enum {
|
||||
Moduleclk = 125000000, /* 125Mhz */
|
||||
Maxtu = 1518,
|
||||
|
||||
R_BUF_SIZE = ((Maxtu+BLOCKALIGN-1)&~BLOCKALIGN),
|
||||
};
|
||||
|
||||
enum {
|
||||
ENET_EIR = 0x004/4, /* Interrupt Event Register */
|
||||
ENET_EIMR = 0x008/4, /* Interrupt Mask Register */
|
||||
INT_BABR =1<<30, /* Babbling Receive Error */
|
||||
INT_BABT =1<<31, /* Babbling Transmit Error */
|
||||
INT_GRA =1<<28, /* Graceful Stop Complete */
|
||||
INT_TXF =1<<27, /* Transmit Frame Interrupt */
|
||||
INT_TXB =1<<26, /* Transmit Buffer Interrupt */
|
||||
INT_RXF =1<<25, /* Receive Frame Interrupt */
|
||||
INT_RXB =1<<24, /* Receive Buffer Interrupt */
|
||||
INT_MII =1<<23, /* MII Interrupt */
|
||||
INT_EBERR =1<<22, /* Ethernet Bus Error */
|
||||
INT_LC =1<<21, /* Late Collision */
|
||||
INT_RL =1<<20, /* Collision Retry Limit */
|
||||
INT_UN =1<<19, /* Transmit FIFO Underrun */
|
||||
INT_PLR =1<<18, /* Payload Receive Error */
|
||||
INT_WAKEUP =1<<17, /* Node Wakeup Request Indication */
|
||||
INT_TS_AVAIL =1<<16, /* Transmit Timestamp Available */
|
||||
INT_TS_TIMER =1<<15, /* Timestamp Timer */
|
||||
INT_RXFLUSH_2 =1<<14, /* RX DMA Ring 2 flush indication */
|
||||
INT_RXFLUSH_1 =1<<13, /* RX DMA Ring 1 flush indication */
|
||||
INT_RXFLUSH_0 =1<<12, /* RX DMA Ring 0 flush indication */
|
||||
INT_TXF2 =1<<7, /* Transmit frame interrupt, class 2 */
|
||||
INT_TXB2 =1<<6, /* Transmit buffer interrupt, class 2 */
|
||||
INT_RXF2 =1<<5, /* Receive frame interrupt, class 2 */
|
||||
INT_RXB2 =1<<4, /* Receive buffer interrupt, class 2 */
|
||||
INT_TXF1 =1<<3, /* Transmit frame interrupt, class 1 */
|
||||
INT_TXB1 =1<<2, /* Transmit buffer interrupt, class 1 */
|
||||
INT_RXF1 =1<<1, /* Receive frame interrupt, class 1 */
|
||||
INT_RXB1 =1<<0, /* Receive buffer interrupt, class 1 */
|
||||
|
||||
ENET_RDAR = 0x010/4, /* Receive Descriptor Active Register */
|
||||
RDAR_ACTIVE =1<<24, /* Descriptor Active */
|
||||
ENET_TDAR = 0x014/4, /* Transmit Descriptor Active Register */
|
||||
TDAR_ACTIVE =1<<24, /* Descriptor Active */
|
||||
|
||||
ENET_ECR = 0x024/4, /* Ethernet Control Register */
|
||||
ECR_RESERVED =7<<28,
|
||||
ECR_SVLANDBL =1<<11, /* S-VLAN double tag */
|
||||
ECR_VLANUSE2ND =1<<10, /* VLAN use second tag */
|
||||
ECR_SVLANEN =1<<9, /* S-VLAN enable */
|
||||
ECR_DBSWP =1<<8, /* Descriptor Byte Swapping Enable */
|
||||
ECR_DBGEN =1<<6, /* Debug Enable */
|
||||
ECR_SPEED_100M =0<<5,
|
||||
ECR_SPEED_1000M =1<<5,
|
||||
ECR_EN1588 =1<<4, /* Enables enhanced functionality of the MAC */
|
||||
ECR_SLEEP =1<<3, /* Sleep Mode Enable */
|
||||
ECR_MAGICEN =1<<2, /* Magic Packet Detection Enable */
|
||||
ECR_ETHEREN =1<<1, /* Ethernet Enable */
|
||||
ECR_RESET =1<<0, /* Ethernet MAC Reset */
|
||||
|
||||
ENET_MMFR = 0x040/4, /* MII Management Frame Register */
|
||||
MMFR_ST =1<<30,
|
||||
MMFR_RD =2<<28,
|
||||
MMFR_WR =1<<28,
|
||||
MMFR_PA_SHIFT =23,
|
||||
MMFR_TA =2<<16,
|
||||
MMFR_RA_SHIFT =18,
|
||||
|
||||
ENET_MSCR = 0x044/4, /* MII Speed Control Register */
|
||||
MSCR_SPEED_SHIFT=1, /* MII speed = module_clock/((SPEED+1)*2) */
|
||||
MSCR_DIS_PRE =1<<7, /* disable preamble */
|
||||
MSCR_HOLD_SHIFT =8, /* hold cycles in module_clock */
|
||||
|
||||
ENET_MIBC = 0x064/4, /* MIB Control Register */
|
||||
ENET_RCR = 0x084/4, /* Receive Control Register */
|
||||
RCR_GRS =1<<31, /* Gracefull Receive Stopped */
|
||||
RCR_NLC =1<<30, /* Payload Length Check Disable */
|
||||
RCR_MAX_FL_SHIFT=16, /* Maximum Frame Length */
|
||||
RCR_CFEN =1<<15, /* MAC Control Frame Enable */
|
||||
RCR_CRCFWD =1<<14, /* Forward Received CRC */
|
||||
RCR_PAUFWD =1<<13, /* Forward Pause Frames */
|
||||
RCR_PADEN =1<<12, /* Enable Frame Padding Remove */
|
||||
RCR_RMII_10T =1<<9, /* Enables 10-Mbit/s mode of the RMII/RGMII */
|
||||
RCR_RMII_MODE =1<<8, /* RMII Mode Enable */
|
||||
RCR_RGMII_EN =1<<6, /* RGMII Mode Enable */
|
||||
RCR_FCE =1<<5, /* Flow Control Enable */
|
||||
RCR_REJ =1<<4, /* Broadcast Frame Reject */
|
||||
RCR_PROM =1<<3, /* Promiscuous Mode */
|
||||
RCR_MII_MODE =1<<2, /* Media Independent Interface Mode (must always be set) */
|
||||
RCR_DRT =1<<1, /* Disable Receive On Timeout */
|
||||
RCR_LOOP =1<<0, /* Internal Loopback */
|
||||
|
||||
ENET_TCR = 0x0C4/4, /* Transmit Control Register */
|
||||
TCR_CRCFWD =1<<9, /* Foward Frame From Application With CRC */
|
||||
TCR_ADDINS =1<<8, /* Set MAC Address on Transmit */
|
||||
TCR_RFC_PAUSE =1<<4, /* Receive Frame Control Pause */
|
||||
TCR_TFC_PAUSE =1<<3, /* Transmit Frame Control Pause */
|
||||
TCR_FDEN =1<<2, /* Full-Duplex Enable */
|
||||
TCR_GTS =1<<0, /* Graceful Transmit Stop */
|
||||
|
||||
ENET_PALR = 0x0E4/4, /* Physical Address Lower Register */
|
||||
ENET_PAUR = 0x0E8/4, /* Physical Address Upper Register */
|
||||
|
||||
ENET_OPD = 0x0EC/4, /* Opcode/Pause Duration Register */
|
||||
|
||||
ENET_TXIC0 = 0x0F0/4, /* Transmit Interrupt Coalescing Register */
|
||||
ENET_TXIC1 = 0x0F4/4, /* Transmit Interrupt Coalescing Register */
|
||||
ENET_TXIC2 = 0x0F8/4, /* Transmit Interrupt Coalescing Register */
|
||||
ENET_RXIC0 = 0x100/4, /* Receive Interrupt Coalescing Register */
|
||||
ENET_RXIC1 = 0x104/4, /* Receive Interrupt Coalescing Register */
|
||||
ENET_RXIC2 = 0x108/4, /* Receive Interrupt Coalescing Register */
|
||||
IC_EN = 1<<31,
|
||||
IC_CS = 1<<30,
|
||||
IC_FT_SHIFT = 20,
|
||||
IC_TT_SHIFT = 0,
|
||||
|
||||
ENET_IAUR = 0x118/4, /* Descriptor Individual Upper Address Register */
|
||||
ENET_IALR = 0x11C/4, /* Descriptor Individual Lower Address Register */
|
||||
ENET_GAUR = 0x120/4, /* Descriptor Group Upper Address Register */
|
||||
ENET_GALR = 0x124/4, /* Descriptor Group Lower Address Register */
|
||||
ENET_TFWR = 0x144/4, /* Transmit FIFO Watermark Register */
|
||||
TFWR_STRFWD = 1<<8,
|
||||
|
||||
ENET_RDSR1 = 0x160/4, /* Receive Descriptor Ring 1 Start Register */
|
||||
ENET_TDSR1 = 0x164/4, /* Transmit Buffer Descriptor Ring 1 Start Register */
|
||||
ENET_MRBR1 = 0x168/4, /* Maximum Receive Buffer Size Register Ring 1 */
|
||||
|
||||
ENET_RDSR2 = 0x16C/4, /* Receive Descriptor Ring 2 Start Register */
|
||||
ENET_TDSR2 = 0x170/4, /* Transmit Buffer Descriptor Ring 2 Start Register */
|
||||
ENET_MRBR2 = 0x174/4, /* Maximum Receive Buffer Size Register Ring 2 */
|
||||
|
||||
ENET_RDSR = 0x180/4, /* Receive Descriptor Ring 0 Start Register */
|
||||
ENET_TDSR = 0x184/4, /* Transmit Buffer Descriptor Ring 0 Start Register */
|
||||
ENET_MRBR = 0x188/4, /* Maximum Receive Buffer Size Register Ring 0 */
|
||||
|
||||
ENET_RSFL = 0x190/4, /* Receive FIFO Section Full Threshold */
|
||||
ENET_RSEM = 0x194/4, /* Receive FIFO Section Empty Threshold */
|
||||
ENET_RAEM = 0x198/4, /* Receive FIFO Almost Empty Threshold */
|
||||
ENET_RAFL = 0x19C/4, /* Receive FIFO Almost Full Threshold */
|
||||
|
||||
ENET_TSEM = 0x1A0/4, /* Transmit FIFO Section Empty Threshold */
|
||||
ENET_TAEM = 0x1A4/4, /* Transmit FIFO Almost Empty Threshold */
|
||||
ENET_TAFL = 0x1A8/4, /* Transmit FIFO Almost Full Threshold */
|
||||
|
||||
ENET_TIPG = 0x1AC/4, /* Transmit Inter-Packet Gap */
|
||||
ENET_FTRL = 0x1B0/4, /* Frame Truncation Length */
|
||||
ENET_TACC = 0x1C0/4, /* Transmit Accelerator Function Configuration */
|
||||
ENET_RACC = 0x1C4/4, /* Receive Accelerator Function Configuration */
|
||||
|
||||
ENET_RCMR1 = 0x1C8/4, /* Receive Classification Match Register */
|
||||
ENET_RCMR2 = 0x1CC/4, /* Receive Classification Match Register */
|
||||
|
||||
ENET_DMA1CFG = 0x1D8/4, /* DMA Class Based Configuration */
|
||||
ENET_DMA2CFG = 0x1DC/4, /* DMA Class Based Configuration */
|
||||
|
||||
ENET_RDAR1 = 0x1E0/4, /* Receive Descriptor Active Register - Ring 1 */
|
||||
ENET_TDAR1 = 0x1E4/4, /* Transmit Descriptor Active Register - Ring 1 */
|
||||
ENET_RDAR2 = 0x1E8/4, /* Receive Descriptor Active Register - Ring 2 */
|
||||
ENET_TDAR2 = 0x1EC/4, /* Transmit Descriptor Active Register - Ring 2 */
|
||||
|
||||
ENET_QOS = 0x1F0/4, /* QOS Scheme */
|
||||
};
|
||||
|
||||
enum {
|
||||
/* transmit descriptor status bits */
|
||||
TD_R = 1<<(15+16), /* Ready */
|
||||
TD_OWN = 1<<(14+16), /* Ownership */
|
||||
TD_W = 1<<(13+16), /* Wrap */
|
||||
TD_L = 1<<(11+16), /* Last in a frame */
|
||||
|
||||
TD_TC = 1<<(10+16), /* Transmit CRC */
|
||||
TD_ERR = TD_TC,
|
||||
|
||||
TD_LEN = 0xFFFF,
|
||||
|
||||
/* receive desctriptor status bits */
|
||||
RD_E = 1<<(15+16), /* Empty */
|
||||
RD_W = 1<<(13+16), /* Wrap */
|
||||
RD_L = 1<<(11+16), /* Last in a frame */
|
||||
|
||||
RD_M = 1<<(8+16), /* Miss */
|
||||
RD_BC = 1<<(7+16), /* broadcast */
|
||||
RD_MC = 1<<(6+16), /* multicast */
|
||||
|
||||
RD_LG = 1<<(5+16), /* length violation */
|
||||
RD_NO = 1<<(4+16), /* non octet aligned frame */
|
||||
RD_CR = 1<<(2+16), /* crc error */
|
||||
RD_OV = 1<<(1+16), /* overrun */
|
||||
RD_TR = 1<<(0+16), /* truncated */
|
||||
RD_ERR = RD_LG | RD_NO | RD_CR | RD_OV | RD_TR,
|
||||
|
||||
RD_LEN = 0xFFFF,
|
||||
};
|
||||
|
||||
typedef struct Descr Descr;
|
||||
struct Descr
|
||||
{
|
||||
u32int status;
|
||||
u32int addr;
|
||||
};
|
||||
|
||||
typedef struct Ctlr Ctlr;
|
||||
struct Ctlr
|
||||
{
|
||||
u32int *regs;
|
||||
u32int intmask;
|
||||
|
||||
struct {
|
||||
Block *b[256];
|
||||
Descr *d;
|
||||
Rendez;
|
||||
} rx[1];
|
||||
|
||||
struct {
|
||||
Block *b[256];
|
||||
Descr *d;
|
||||
Rendez;
|
||||
} tx[1];
|
||||
|
||||
struct {
|
||||
Rendez;
|
||||
} free[1];
|
||||
|
||||
struct {
|
||||
Mii;
|
||||
Rendez;
|
||||
} mii[1];
|
||||
|
||||
int attached;
|
||||
QLock;
|
||||
};
|
||||
|
||||
#define rr(c, r) ((c)->regs[r])
|
||||
#define wr(c, r, v) ((c)->regs[r] = (v))
|
||||
|
||||
static int
|
||||
mdiodone(void *arg)
|
||||
{
|
||||
Ctlr *ctlr = arg;
|
||||
return rr(ctlr, ENET_EIR) & INT_MII;
|
||||
}
|
||||
static int
|
||||
mdiowait(Ctlr *ctlr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 200; i++){
|
||||
tsleep(ctlr->mii, mdiodone, ctlr, 5);
|
||||
if(mdiodone(ctlr))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int
|
||||
mdiow(Mii* mii, int phy, int addr, int data)
|
||||
{
|
||||
Ctlr *ctlr = mii->ctlr;
|
||||
|
||||
data &= 0xFFFF;
|
||||
wr(ctlr, ENET_EIR, INT_MII);
|
||||
wr(ctlr, ENET_MMFR, MMFR_WR | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT | data);
|
||||
if(mdiowait(ctlr) < 0) return -1;
|
||||
return data;
|
||||
}
|
||||
static int
|
||||
mdior(Mii* mii, int phy, int addr)
|
||||
{
|
||||
Ctlr *ctlr = mii->ctlr;
|
||||
|
||||
wr(ctlr, ENET_EIR, INT_MII);
|
||||
wr(ctlr, ENET_MMFR, MMFR_RD | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT);
|
||||
if(mdiowait(ctlr) < 0) return -1;
|
||||
return rr(ctlr, ENET_MMFR) & 0xFFFF;
|
||||
}
|
||||
|
||||
static void
|
||||
interrupt(Ureg*, void *arg)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
u32int e;
|
||||
|
||||
e = rr(ctlr, ENET_EIR);
|
||||
wr(ctlr, ENET_EIR, e);
|
||||
|
||||
if(e & INT_RXF) wakeup(ctlr->rx);
|
||||
if(e & INT_TXF) wakeup(ctlr->tx);
|
||||
if(e & INT_MII) wakeup(ctlr->mii);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown(Ether *edev)
|
||||
{
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
coherence();
|
||||
|
||||
wr(ctlr, ENET_ECR, ECR_RESERVED | ECR_RESET);
|
||||
while(rr(ctlr, ENET_ECR) & ECR_RESET) delay(1);
|
||||
|
||||
/* mask and clear interrupt events */
|
||||
wr(ctlr, ENET_EIMR, 0);
|
||||
wr(ctlr, ENET_EIR, ~0);
|
||||
}
|
||||
|
||||
static int
|
||||
tdfree(void *arg)
|
||||
{
|
||||
Descr *d = arg;
|
||||
return (d->status & (TD_OWN|TD_R)) == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
txproc(void *arg)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
Block *b;
|
||||
Descr *d;
|
||||
uint i = 0;
|
||||
|
||||
while(waserror())
|
||||
;
|
||||
|
||||
for(;;){
|
||||
if((b = qbread(edev->oq, 100000)) == nil)
|
||||
break;
|
||||
|
||||
d = &ctlr->tx->d[i];
|
||||
while(!tdfree(d))
|
||||
sleep(ctlr->free, tdfree, d);
|
||||
|
||||
ctlr->tx->b[i] = b;
|
||||
|
||||
dmaflush(1, b->rp, BLEN(b));
|
||||
d->addr = PADDR(b->rp);
|
||||
coherence();
|
||||
if(i == nelem(ctlr->tx->b)-1){
|
||||
d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC | TD_W;
|
||||
i = 0;
|
||||
} else {
|
||||
d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC;
|
||||
i++;
|
||||
}
|
||||
wr(ctlr, ENET_TDAR, TDAR_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tddone(void *arg)
|
||||
{
|
||||
Descr *d = arg;
|
||||
return (d->status & (TD_OWN|TD_R)) == TD_OWN;
|
||||
}
|
||||
|
||||
static void
|
||||
frproc(void *arg)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
Block *b;
|
||||
Descr *d;
|
||||
uint i = 0;
|
||||
|
||||
while(waserror())
|
||||
;
|
||||
|
||||
for(;;){
|
||||
d = &ctlr->tx->d[i];
|
||||
while(!tddone(d))
|
||||
sleep(ctlr->tx, tddone, d);
|
||||
|
||||
b = ctlr->tx->b[i];
|
||||
ctlr->tx->b[i] = nil;
|
||||
coherence();
|
||||
|
||||
if(i == nelem(ctlr->tx->b)-1){
|
||||
d->status = TD_W;
|
||||
i = 0;
|
||||
} else {
|
||||
d->status = 0;
|
||||
i++;
|
||||
}
|
||||
|
||||
wakeup(ctlr->free);
|
||||
freeb(b);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rdfull(void *arg)
|
||||
{
|
||||
Descr *d = arg;
|
||||
return (d->status & RD_E) == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rxproc(void *arg)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
Block *b;
|
||||
Descr *d;
|
||||
uint s, i = 0;
|
||||
|
||||
while(waserror())
|
||||
;
|
||||
|
||||
for(;;){
|
||||
d = &ctlr->rx->d[i];
|
||||
s = d->status;
|
||||
if(s & RD_E){
|
||||
sleep(ctlr->rx, rdfull, d);
|
||||
continue;
|
||||
}
|
||||
if(((s^RD_L) & (RD_L|RD_ERR)) == 0){
|
||||
b = ctlr->rx->b[i];
|
||||
b->wp = b->rp + (s & RD_LEN);
|
||||
dmaflush(0, b->rp, BLEN(b));
|
||||
etheriq(edev, b);
|
||||
|
||||
/* replenish */
|
||||
b = allocb(R_BUF_SIZE);
|
||||
ctlr->rx->b[i] = b;
|
||||
dmaflush(1, b->rp, R_BUF_SIZE);
|
||||
d->addr = PADDR(b->rp);
|
||||
coherence();
|
||||
}
|
||||
if(i == nelem(ctlr->rx->b)-1) {
|
||||
d->status = RD_E | RD_W;
|
||||
i = 0;
|
||||
} else {
|
||||
d->status = RD_E;
|
||||
i++;
|
||||
}
|
||||
wr(ctlr, ENET_RDAR, RDAR_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
linkproc(void *arg)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
MiiPhy *phy;
|
||||
int link = -1;
|
||||
|
||||
while(waserror())
|
||||
;
|
||||
|
||||
miiane(ctlr->mii, ~0, AnaAP|AnaP, ~0);
|
||||
|
||||
for(;;){
|
||||
miistatus(ctlr->mii);
|
||||
phy = ctlr->mii->curphy;
|
||||
if(phy->link == link){
|
||||
tsleep(ctlr->mii, return0, nil, 5000);
|
||||
continue;
|
||||
}
|
||||
link = phy->link;
|
||||
if(link){
|
||||
u32int ecr = rr(ctlr, ENET_ECR) & ~ECR_SPEED_1000M;
|
||||
u32int rcr = rr(ctlr, ENET_RCR) & ~(RCR_RMII_10T|RCR_FCE);
|
||||
u32int tcr = rr(ctlr, ENET_TCR) & ~(TCR_RFC_PAUSE|TCR_TFC_PAUSE|TCR_FDEN);
|
||||
|
||||
switch(phy->speed){
|
||||
case 1000:
|
||||
ecr |= ECR_SPEED_1000M;
|
||||
rcr |= RCR_FCE;
|
||||
|
||||
/* receive fifo thresholds */
|
||||
wr(ctlr, ENET_RSFL, 16);
|
||||
wr(ctlr, ENET_RSEM, 132);
|
||||
wr(ctlr, ENET_RAEM, 8);
|
||||
wr(ctlr, ENET_RAFL, 8);
|
||||
|
||||
/* opcode/pause duration */
|
||||
wr(ctlr, ENET_OPD, 0xFFF0);
|
||||
break;
|
||||
case 100:
|
||||
ecr |= ECR_SPEED_100M;
|
||||
break;
|
||||
case 10:
|
||||
rcr |= RCR_RMII_10T;
|
||||
break;
|
||||
}
|
||||
if(phy->fd)
|
||||
tcr |= TCR_FDEN;
|
||||
if(phy->rfc)
|
||||
tcr |= TCR_RFC_PAUSE;
|
||||
if(phy->tfc)
|
||||
tcr |= TCR_TFC_PAUSE;
|
||||
|
||||
wr(ctlr, ENET_ECR, ecr);
|
||||
wr(ctlr, ENET_RCR, rcr);
|
||||
wr(ctlr, ENET_TCR, tcr);
|
||||
|
||||
edev->mbps = phy->speed;
|
||||
|
||||
wr(ctlr, ENET_RDAR, RDAR_ACTIVE);
|
||||
}
|
||||
edev->link = link;
|
||||
print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
attach(Ether *edev)
|
||||
{
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
Descr *d;
|
||||
int i;
|
||||
|
||||
eqlock(ctlr);
|
||||
if(ctlr->attached){
|
||||
qunlock(ctlr);
|
||||
return;
|
||||
}
|
||||
if(waserror()){
|
||||
qunlock(ctlr);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
/* RGMII mode, max frame length */
|
||||
wr(ctlr, ENET_RCR, RCR_MII_MODE | RCR_RGMII_EN | Maxtu<<RCR_MAX_FL_SHIFT);
|
||||
|
||||
/* set MII clock to 2.5Mhz, 10ns hold time */
|
||||
wr(ctlr, ENET_MSCR, ((Moduleclk/(2*2500000))-1)<<MSCR_SPEED_SHIFT | ((Moduleclk/10000000)-1)<<MSCR_HOLD_SHIFT);
|
||||
|
||||
ctlr->intmask |= INT_MII;
|
||||
wr(ctlr, ENET_EIMR, ctlr->intmask);
|
||||
mii(ctlr->mii, ~0);
|
||||
|
||||
if(ctlr->mii->curphy == nil)
|
||||
error("no phy");
|
||||
|
||||
print("#l%d: phy%d id %.8ux oui %x\n",
|
||||
edev->ctlrno, ctlr->mii->curphy->phyno,
|
||||
ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
|
||||
|
||||
/* clear mac filter hash table */
|
||||
wr(ctlr, ENET_IALR, 0);
|
||||
wr(ctlr, ENET_IAUR, 0);
|
||||
wr(ctlr, ENET_GALR, 0);
|
||||
wr(ctlr, ENET_GAUR, 0);
|
||||
|
||||
/* set MAC address */
|
||||
wr(ctlr, ENET_PALR, (u32int)edev->ea[0]<<24 | (u32int)edev->ea[1]<<16 | (u32int)edev->ea[2]<<8 | edev->ea[3]<<0);
|
||||
wr(ctlr, ENET_PAUR, (u32int)edev->ea[4]<<24 | (u32int)edev->ea[5]<<16);
|
||||
|
||||
if(ctlr->rx->d == nil)
|
||||
ctlr->rx->d = ucalloc(sizeof(Descr) * nelem(ctlr->rx->b));
|
||||
for(i=0; i<nelem(ctlr->rx->b); i++){
|
||||
Block *b = allocb(R_BUF_SIZE);
|
||||
ctlr->rx->b[i] = b;
|
||||
d = &ctlr->rx->d[i];
|
||||
dmaflush(1, b->rp, R_BUF_SIZE);
|
||||
d->addr = PADDR(b->rp);
|
||||
d->status = RD_E;
|
||||
}
|
||||
ctlr->rx->d[nelem(ctlr->rx->b)-1].status = RD_E | RD_W;
|
||||
wr(ctlr, ENET_MRBR, R_BUF_SIZE);
|
||||
coherence();
|
||||
wr(ctlr, ENET_RDSR, PADDR(ctlr->rx->d));
|
||||
|
||||
if(ctlr->tx->d == nil)
|
||||
ctlr->tx->d = ucalloc(sizeof(Descr) * nelem(ctlr->tx->b));
|
||||
for(i=0; i<nelem(ctlr->tx->b); i++){
|
||||
ctlr->tx->b[i] = nil;
|
||||
d = &ctlr->tx->d[i];
|
||||
d->addr = 0;
|
||||
d->status = 0;
|
||||
}
|
||||
ctlr->tx->d[nelem(ctlr->tx->b)-1].status = TD_W;
|
||||
coherence();
|
||||
wr(ctlr, ENET_TDSR, PADDR(ctlr->tx->d));
|
||||
|
||||
/* store and forward tx fifo */
|
||||
wr(ctlr, ENET_TFWR, TFWR_STRFWD);
|
||||
|
||||
/* interrupt coalescing: 200 pkts, 1000 µs */
|
||||
wr(ctlr, ENET_RXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT);
|
||||
wr(ctlr, ENET_TXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT);
|
||||
|
||||
ctlr->intmask |= INT_TXF | INT_RXF;
|
||||
wr(ctlr, ENET_EIMR, ctlr->intmask);
|
||||
|
||||
/* enable ethernet */
|
||||
wr(ctlr, ENET_ECR, rr(ctlr, ENET_ECR) | ECR_ETHEREN | ECR_DBSWP);
|
||||
|
||||
ctlr->attached = 1;
|
||||
|
||||
kproc("ether-rx", rxproc, edev);
|
||||
kproc("ether-tx", txproc, edev);
|
||||
kproc("ether-fr", frproc, edev);
|
||||
|
||||
kproc("ether-link", linkproc, edev);
|
||||
|
||||
qunlock(ctlr);
|
||||
poperror();
|
||||
}
|
||||
|
||||
static void
|
||||
prom(void *arg, int on)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
|
||||
if(on)
|
||||
wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) | RCR_PROM);
|
||||
else
|
||||
wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) & ~RCR_PROM);
|
||||
}
|
||||
|
||||
static void
|
||||
multi(void *arg, uchar*, int)
|
||||
{
|
||||
Ether *edev = arg;
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
Netaddr *a;
|
||||
u64int hash;
|
||||
|
||||
hash = 0;
|
||||
for(a = edev->maddr; a != nil; a = a->next)
|
||||
hash |= 1ULL << ((ethercrc(a->addr, edev->alen) >> (32 - 6)) & 0x3F);
|
||||
|
||||
wr(ctlr, ENET_GALR, hash & 0xFFFFFFFF);
|
||||
wr(ctlr, ENET_GAUR, hash >> 32);
|
||||
}
|
||||
|
||||
static long
|
||||
ctl(Ether*, void*, long len)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
reset(Ether *edev)
|
||||
{
|
||||
Ctlr *ctlr = edev->ctlr;
|
||||
u32int paddr1, paddr2;
|
||||
|
||||
/* steal mac address from uboot */
|
||||
paddr1 = rr(ctlr, ENET_PALR);
|
||||
paddr2 = rr(ctlr, ENET_PAUR);
|
||||
edev->ea[0] = paddr1>>24;
|
||||
edev->ea[1] = paddr1>>16;
|
||||
edev->ea[2] = paddr1>>8;
|
||||
edev->ea[3] = paddr1>>0;
|
||||
edev->ea[4] = paddr2>>24;
|
||||
edev->ea[5] = paddr2>>16;
|
||||
|
||||
shutdown(edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pnp(Ether *edev)
|
||||
{
|
||||
static Ctlr ctlr[1];
|
||||
|
||||
if(ctlr->regs != nil)
|
||||
return -1;
|
||||
|
||||
ctlr->regs = (u32int*)(VIRTIO + 0xbe0000);
|
||||
|
||||
ctlr->mii->ctlr = ctlr;
|
||||
ctlr->mii->mir = mdior;
|
||||
ctlr->mii->miw = mdiow;
|
||||
|
||||
edev->port = (uintptr)ctlr->regs - KZERO;
|
||||
edev->irq = IRQenet1;
|
||||
edev->ctlr = ctlr;
|
||||
edev->attach = attach;
|
||||
edev->shutdown = shutdown;
|
||||
edev->promiscuous = prom;
|
||||
edev->multicast = multi;
|
||||
edev->ctl = ctl;
|
||||
edev->arg = edev;
|
||||
edev->mbps = 1000;
|
||||
edev->maxmtu = Maxtu;
|
||||
|
||||
if(reset(edev) < 0)
|
||||
return -1;
|
||||
|
||||
intrenable(edev->irq+0, interrupt, edev, BUSUNKNOWN, edev->name);
|
||||
intrenable(edev->irq+1, interrupt, edev, BUSUNKNOWN, edev->name);
|
||||
intrenable(edev->irq+2, interrupt, edev, BUSUNKNOWN, edev->name);
|
||||
intrenable(edev->irq+3, interrupt, edev, BUSUNKNOWN, edev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
etherimxlink(void)
|
||||
{
|
||||
addethercard("imx", pnp);
|
||||
}
|
139
sys/src/9/imx8/fns.h
Normal file
139
sys/src/9/imx8/fns.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
#include "../port/portfns.h"
|
||||
|
||||
/* l.s */
|
||||
extern void sev(void);
|
||||
extern int tas(void *);
|
||||
extern int cmpswap(long*, long, long);
|
||||
extern void coherence(void);
|
||||
extern void idlehands(void);
|
||||
extern uvlong vcycles(void);
|
||||
#define cycles(ip) *(ip) = vcycles()
|
||||
extern int splfhi(void);
|
||||
extern void splflo(void);
|
||||
extern void touser(uintptr sp);
|
||||
extern void forkret(void);
|
||||
extern void noteret(void);
|
||||
extern void returnto(void*);
|
||||
extern void fpsaveregs(void*);
|
||||
extern void fploadregs(void*);
|
||||
|
||||
extern void setttbr(uintptr pa);
|
||||
extern uintptr getfar(void);
|
||||
|
||||
extern void flushasidva(uintptr asidva);
|
||||
extern void tlbivae1is(uintptr asidva);
|
||||
|
||||
extern void flushasidvall(uintptr asidva);
|
||||
extern void tlbivale1is(uintptr asidva);
|
||||
|
||||
extern void flushasid(uintptr asid);
|
||||
extern void tlbiaside1is(uintptr asid);
|
||||
|
||||
extern void flushtlb(void);
|
||||
extern void tlbivmalle1(void);
|
||||
|
||||
extern void flushlocaltlb(void);
|
||||
extern void tlbivmalle1(void);
|
||||
|
||||
/* cache */
|
||||
extern ulong cachesize(int level);
|
||||
|
||||
extern void cacheiinvse(void*, int);
|
||||
extern void cacheuwbinv(void);
|
||||
extern void cacheiinv(void);
|
||||
|
||||
extern void cachedwbse(void*, int);
|
||||
extern void cacheduwbse(void*, int);
|
||||
extern void cachedinvse(void*, int);
|
||||
extern void cachedwbinvse(void*, int);
|
||||
|
||||
extern void cachedwb(void);
|
||||
extern void cachedinv(void);
|
||||
extern void cachedwbinv(void);
|
||||
|
||||
extern void l2cacheuwb(void);
|
||||
extern void l2cacheuinv(void);
|
||||
extern void l2cacheuwbinv(void);
|
||||
|
||||
/* mmu */
|
||||
#define getpgcolor(a) 0
|
||||
extern uintptr paddr(void*);
|
||||
#define PADDR(a) paddr((void*)(a))
|
||||
extern uintptr cankaddr(uintptr);
|
||||
extern void* kaddr(uintptr);
|
||||
#define KADDR(a) kaddr(a)
|
||||
extern void kmapinval(void);
|
||||
#define VA(k) ((uintptr)(k))
|
||||
extern KMap *kmap(Page*);
|
||||
extern void kunmap(KMap*);
|
||||
extern uintptr mmukmap(uintptr, uintptr, usize);
|
||||
extern void* vmap(uvlong, vlong);
|
||||
extern void vunmap(void*, vlong);
|
||||
|
||||
extern void mmu0init(uintptr*);
|
||||
extern void mmu0clear(uintptr*);
|
||||
extern void mmuidmap(uintptr*);
|
||||
extern void mmu1init(void);
|
||||
extern void meminit(void);
|
||||
|
||||
extern void putasid(Proc*);
|
||||
|
||||
extern void* ucalloc(usize);
|
||||
|
||||
/* clock */
|
||||
extern void clockinit(void);
|
||||
extern void synccycles(void);
|
||||
extern void armtimerset(int);
|
||||
extern void clockshutdown(void);
|
||||
|
||||
/* fpu */
|
||||
extern void fpuinit(void);
|
||||
extern void fpoff(void);
|
||||
extern void fpinit(void);
|
||||
extern void fpclear(void);
|
||||
extern void fpsave(FPsave*);
|
||||
extern void fprestore(FPsave*);
|
||||
extern void mathtrap(Ureg*);
|
||||
|
||||
/* trap */
|
||||
extern void trapinit(void);
|
||||
extern int userureg(Ureg*);
|
||||
extern void evenaddr(uintptr);
|
||||
extern void setkernur(Ureg*, Proc*);
|
||||
extern void procfork(Proc*);
|
||||
extern void procsetup(Proc*);
|
||||
extern void procsave(Proc*);
|
||||
extern void procrestore(Proc *);
|
||||
extern void trap(Ureg*);
|
||||
extern void syscall(Ureg*);
|
||||
extern void noted(Ureg*, ulong);
|
||||
extern void faultarm64(Ureg*);
|
||||
extern void dumpstack(void);
|
||||
extern void dumpregs(Ureg*);
|
||||
|
||||
/* irq */
|
||||
extern void intrinit(void);
|
||||
extern void intrcpushutdown(void);
|
||||
extern void intrsoff(void);
|
||||
extern void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
|
||||
extern void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
|
||||
extern int irq(Ureg*);
|
||||
extern void fiq(Ureg*);
|
||||
|
||||
/* sysreg */
|
||||
extern uvlong sysrd(ulong);
|
||||
extern void syswr(ulong, uvlong);
|
||||
|
||||
/* uartimx */
|
||||
extern void uartconsinit(void);
|
||||
|
||||
/* dma */
|
||||
extern void dmaflush(int, void*, ulong);
|
||||
|
||||
/* main */
|
||||
extern char *getconf(char *name);
|
||||
extern void setconfenv(void);
|
||||
extern void writeconf(void);
|
||||
|
||||
extern int isaconfig(char*, int, ISAConf*);
|
||||
extern void links(void);
|
92
sys/src/9/imx8/fpu.c
Normal file
92
sys/src/9/imx8/fpu.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
#include "ureg.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
/* libc */
|
||||
extern ulong getfcr(void);
|
||||
extern void setfcr(ulong fcr);
|
||||
extern ulong getfsr(void);
|
||||
extern void setfsr(ulong fsr);
|
||||
|
||||
void
|
||||
fpuinit(void)
|
||||
{
|
||||
fpoff();
|
||||
}
|
||||
|
||||
void
|
||||
fpon(void)
|
||||
{
|
||||
syswr(CPACR_EL1, 3<<20);
|
||||
}
|
||||
|
||||
void
|
||||
fpoff(void)
|
||||
{
|
||||
syswr(CPACR_EL1, 0<<20);
|
||||
}
|
||||
|
||||
void
|
||||
fpinit(void)
|
||||
{
|
||||
fpon();
|
||||
setfcr(0);
|
||||
setfsr(0);
|
||||
}
|
||||
|
||||
void
|
||||
fpclear(void)
|
||||
{
|
||||
fpoff();
|
||||
}
|
||||
|
||||
void
|
||||
fpsave(FPsave *p)
|
||||
{
|
||||
p->control = getfcr();
|
||||
p->status = getfsr();
|
||||
fpsaveregs(p->regs);
|
||||
fpoff();
|
||||
}
|
||||
|
||||
void
|
||||
fprestore(FPsave *p)
|
||||
{
|
||||
fpon();
|
||||
setfcr(p->control);
|
||||
setfsr(p->status);
|
||||
fploadregs(p->regs);
|
||||
}
|
||||
|
||||
void
|
||||
mathtrap(Ureg*)
|
||||
{
|
||||
int s;
|
||||
|
||||
if((up->fpstate & FPillegal) != 0){
|
||||
postnote(up, 1, "sys: floating point in note handler", NDebug);
|
||||
return;
|
||||
}
|
||||
switch(up->fpstate){
|
||||
case FPinit:
|
||||
s = splhi();
|
||||
fpinit();
|
||||
up->fpstate = FPactive;
|
||||
splx(s);
|
||||
break;
|
||||
case FPinactive:
|
||||
s = splhi();
|
||||
fprestore(up->fpsave);
|
||||
up->fpstate = FPactive;
|
||||
splx(s);
|
||||
break;
|
||||
case FPactive:
|
||||
postnote(up, 1, "sys: floating point error", NDebug);
|
||||
break;
|
||||
}
|
||||
}
|
316
sys/src/9/imx8/gic.c
Normal file
316
sys/src/9/imx8/gic.c
Normal file
|
@ -0,0 +1,316 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
#include "../port/pci.h"
|
||||
#include "ureg.h"
|
||||
#include "sysreg.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
enum {
|
||||
GICD_CTLR = 0x000/4, /* RW, Distributor Control Register */
|
||||
GICD_TYPER = 0x004/4, /* RO, Interrupt Controller Type */
|
||||
GICD_IIDR = 0x008/4, /* RO, Distributor Implementer Identification Register */
|
||||
|
||||
GICD_IGROUPR0 = 0x080/4, /* RW, Interrupt Group Registers (0x80-0xBC) */
|
||||
|
||||
GICD_ISENABLER0 = 0x100/4, /* RW, Interrupt Set-Enable Registers (0x100-0x13C) */
|
||||
GICD_ICENABLER0 = 0x180/4, /* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */
|
||||
|
||||
GICD_ISPENDR0 = 0x200/4, /* RW, Interrupt Set-Pending Registers (0x200-0x23C) */
|
||||
GICD_ICPENDR0 = 0x280/4, /* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */
|
||||
|
||||
GICD_ISACTIVER0 = 0x300/4, /* RW, Interrupt Set-Active Registers (0x300-0x33C) */
|
||||
GICD_ICACTIVER0 = 0x380/4, /* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */
|
||||
|
||||
GICD_IPRIORITYR0= 0x400/4, /* RW, Interrupt Priority Registers (0x400-0x5FC) */
|
||||
GICD_TARGETSR0 = 0x800/4, /* RW, Interrupt Target Registers (0x800-0x9FC) */
|
||||
GICD_ICFGR0 = 0xC00/4, /* RW, Interrupt Configuration Registers (0xC00-0xC7C) */
|
||||
|
||||
GICD_ISR0 = 0xD00/4,
|
||||
GICD_PPISR = GICD_ISR0, /* RO, Private Peripheral Interrupt Status Register */
|
||||
GICD_SPISR0 = GICD_ISR0+1, /* RO, Shared Peripheral Interrupt Status Register */
|
||||
GICD_SGIR = 0xF00/4, /* WO, Software Generated Interrupt Register */
|
||||
|
||||
GICD_CPENDSGIR0 = 0xF10/4, /* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */
|
||||
GICD_SPENDSGIR0 = 0xF20/4, /* RW, SGI Set-Pending Registers (0xF20-0xF2C) */
|
||||
|
||||
GICD_PIDR4 = 0xFD0/4, /* RO, Perpheral ID Registers */
|
||||
GICD_PIDR5 = 0xFD4/4,
|
||||
GICD_PIDR6 = 0xFD8/4,
|
||||
GICD_PIDR7 = 0xFDC/4,
|
||||
GICD_PIDR0 = 0xFE0/4,
|
||||
GICD_PIDR1 = 0xFE4/4,
|
||||
GICD_PIDR2 = 0xFE8/4,
|
||||
GICD_PIDR3 = 0xFEC/4,
|
||||
|
||||
GICD_CIDR0 = 0xFF0/4, /* RO, Component ID Registers */
|
||||
GICD_CIDR1 = 0xFF4/4,
|
||||
GICD_CIDR2 = 0xFF8/4,
|
||||
GICD_CIDR3 = 0xFFC/4,
|
||||
|
||||
RD_base = 0x00000,
|
||||
GICR_CTLR = (RD_base+0x000)/4,
|
||||
GICR_IIDR = (RD_base+0x004)/4,
|
||||
GICR_TYPER = (RD_base+0x008)/4,
|
||||
GICR_STATUSR = (RD_base+0x010)/4,
|
||||
GICR_WAKER = (RD_base+0x014)/4,
|
||||
GICR_SETLPIR = (RD_base+0x040)/4,
|
||||
GICR_CLRLPIR = (RD_base+0x048)/4,
|
||||
GICR_PROPBASER = (RD_base+0x070)/4,
|
||||
GICR_PENDBASER = (RD_base+0x078)/4,
|
||||
GICR_INVLPIR = (RD_base+0x0A0)/4,
|
||||
GICR_INVALLR = (RD_base+0x0B0)/4,
|
||||
GICR_SYNCR = (RD_base+0x0C0)/4,
|
||||
|
||||
SGI_base = 0x10000,
|
||||
GICR_IGROUPR0 = (SGI_base+0x080)/4,
|
||||
GICR_ISENABLER0 = (SGI_base+0x100)/4,
|
||||
GICR_ICENABLER0 = (SGI_base+0x180)/4,
|
||||
GICR_ISPENDR0 = (SGI_base+0x200)/4,
|
||||
GICR_ICPENDR0 = (SGI_base+0x280)/4,
|
||||
GICR_ISACTIVER0 = (SGI_base+0x300)/4,
|
||||
GICR_ICACTIVER0 = (SGI_base+0x380)/4,
|
||||
GICR_IPRIORITYR0= (SGI_base+0x400)/4,
|
||||
GICR_ICFGR0 = (SGI_base+0xC00)/4,
|
||||
GICR_ICFGR1 = (SGI_base+0xC04)/4,
|
||||
GICR_IGRPMODR0 = (SGI_base+0xD00)/4,
|
||||
GICR_NSACR = (SGI_base+0xE00)/4,
|
||||
};
|
||||
|
||||
typedef struct Vctl Vctl;
|
||||
struct Vctl {
|
||||
Vctl *next;
|
||||
void (*f)(Ureg*, void*);
|
||||
void *a;
|
||||
int irq;
|
||||
u32int intid;
|
||||
};
|
||||
|
||||
static Lock vctllock;
|
||||
static Vctl *vctl[MAXMACH][32], *vfiq;
|
||||
static u32int *dregs = (u32int*)(VIRTIO + 0x8800000);
|
||||
|
||||
static u32int*
|
||||
getrregs(int machno)
|
||||
{
|
||||
u32int *rregs = (u32int*)(VIRTIO + 0x8880000);
|
||||
|
||||
for(;;){
|
||||
if((rregs[GICR_TYPER] & 0xFFFF00) == (machno << 8))
|
||||
return rregs;
|
||||
if(rregs[GICR_TYPER] & (1<<4))
|
||||
break;
|
||||
rregs += (0x20000/4);
|
||||
}
|
||||
panic("getrregs: no re-distributor for cpu %d\n", machno);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
intrcpushutdown(void)
|
||||
{
|
||||
/* disable cpu interface */
|
||||
syswr(ICC_IGRPEN0_EL1, 0);
|
||||
syswr(ICC_IGRPEN1_EL1, 0);
|
||||
coherence();
|
||||
}
|
||||
|
||||
void
|
||||
intrsoff(void)
|
||||
{
|
||||
/* disable distributor */
|
||||
dregs[GICD_CTLR] = 0;
|
||||
coherence();
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
intrinit(void)
|
||||
{
|
||||
u32int *rregs;
|
||||
int i, n;
|
||||
|
||||
if(m->machno == 0){
|
||||
intrsoff();
|
||||
|
||||
/* clear all interrupts */
|
||||
n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5;
|
||||
print("nirq %d\n", n);
|
||||
for(i = 32; i < n; i += 32){
|
||||
dregs[GICD_IGROUPR0 + (i/32)] = -1;
|
||||
|
||||
dregs[GICD_ISENABLER0 + (i/32)] = -1;
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
print("%d: distributor stuck disabled: %.8ux\n", i, ~dregs[GICD_ISENABLER0 + (i/32)]);
|
||||
dregs[GICD_ICENABLER0 + (i/32)] = -1;
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
print("%d: distributor stuck enabled: %.8ux\n", i, dregs[GICD_ISENABLER0 + (i/32)]);
|
||||
dregs[GICD_ICACTIVER0 + (i/32)] = -1;
|
||||
}
|
||||
for(i = 0; i < n; i += 4){
|
||||
dregs[GICD_IPRIORITYR0 + (i/4)] = 0;
|
||||
dregs[GICD_TARGETSR0 + (i/4)] = 0;
|
||||
}
|
||||
for(i = 32; i < n; i += 16){
|
||||
dregs[GICD_ICFGR0 + (i/16)] = 0;
|
||||
}
|
||||
coherence();
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
dregs[GICD_CTLR] = (1<<0) | (1<<1) | (1<<4);
|
||||
}
|
||||
|
||||
rregs = getrregs(m->machno);
|
||||
n = 32;
|
||||
for(i = 0; i < n; i += 32){
|
||||
rregs[GICR_IGROUPR0 + (i/32)] = -1;
|
||||
|
||||
rregs[GICR_ISENABLER0 + (i/32)] = -1;
|
||||
while(rregs[GICR_CTLR]&(1<<3))
|
||||
;
|
||||
print("%d: re-distributor stuck disabled: %.8ux\n", i, ~rregs[GICR_ISENABLER0 + (i/32)]);
|
||||
rregs[GICR_ICENABLER0 + (i/32)] = -1;
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
print("%d: re-distributor stuck enabled: %.8ux\n", i, rregs[GICR_ISENABLER0 + (i/32)]);
|
||||
rregs[GICR_ICACTIVER0 + (i/32)] = -1;
|
||||
}
|
||||
for(i = 0; i < n; i += 4){
|
||||
rregs[GICR_IPRIORITYR0 + (i/4)] = 0;
|
||||
}
|
||||
coherence();
|
||||
while(rregs[GICR_CTLR]&(1<<3))
|
||||
;
|
||||
|
||||
coherence();
|
||||
|
||||
/* enable cpu interface */
|
||||
syswr(ICC_CTLR_EL1, 0);
|
||||
syswr(ICC_BPR1_EL1, 7);
|
||||
syswr(ICC_PMR_EL1, 0xFF);
|
||||
|
||||
coherence();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* called by trap to handle irq interrupts.
|
||||
* returns true iff a clock interrupt, thus maybe reschedule.
|
||||
*/
|
||||
int
|
||||
irq(Ureg* ureg)
|
||||
{
|
||||
Vctl *v;
|
||||
int clockintr;
|
||||
u32int intid;
|
||||
|
||||
m->intr++;
|
||||
intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
|
||||
// iprint("i<%d>", intid);
|
||||
if((intid & ~3) == 1020)
|
||||
return 0; // spurious
|
||||
clockintr = 0;
|
||||
for(v = vctl[m->machno][intid%32]; v != nil; v = v->next)
|
||||
if(v->intid == intid){
|
||||
coherence();
|
||||
v->f(ureg, v->a);
|
||||
coherence();
|
||||
if(v->irq == IRQcntpns)
|
||||
clockintr = 1;
|
||||
}
|
||||
coherence();
|
||||
syswr(ICC_EOIR1_EL1, intid);
|
||||
return clockintr;
|
||||
}
|
||||
|
||||
/*
|
||||
* called direct from lexception.s to handle fiq interrupt.
|
||||
*/
|
||||
void
|
||||
fiq(Ureg *ureg)
|
||||
{
|
||||
Vctl *v;
|
||||
u32int intid;
|
||||
|
||||
m->intr++;
|
||||
intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
|
||||
// iprint("f<%d>", intid);
|
||||
if((intid & ~3) == 1020)
|
||||
return; // spurious
|
||||
v = vfiq;
|
||||
if(v != nil && v->intid == intid && m->machno == 0){
|
||||
coherence();
|
||||
v->f(ureg, v->a);
|
||||
coherence();
|
||||
}
|
||||
syswr(ICC_EOIR1_EL1, intid);
|
||||
}
|
||||
|
||||
void
|
||||
intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char *)
|
||||
{
|
||||
Vctl *v;
|
||||
u32int intid;
|
||||
int cpu, prio;
|
||||
|
||||
if(tbdf != BUSUNKNOWN)
|
||||
return;
|
||||
|
||||
prio = 0x80;
|
||||
intid = irq;
|
||||
if((v = xalloc(sizeof(Vctl))) == nil)
|
||||
panic("irqenable: no mem");
|
||||
v->irq = irq;
|
||||
v->intid = intid;
|
||||
v->f = f;
|
||||
v->a = a;
|
||||
|
||||
lock(&vctllock);
|
||||
if(intid < SPI)
|
||||
cpu = m->machno;
|
||||
else
|
||||
cpu = 0;
|
||||
if(irq == IRQfiq){
|
||||
vfiq = v;
|
||||
prio = 0;
|
||||
}else{
|
||||
v->next = vctl[cpu][intid%32];
|
||||
vctl[cpu][intid%32] = v;
|
||||
}
|
||||
syswr(ICC_IGRPEN1_EL1, sysrd(ICC_IGRPEN1_EL1)|1);
|
||||
coherence();
|
||||
|
||||
syswr(ICC_EOIR1_EL1, intid);
|
||||
coherence();
|
||||
|
||||
/* setup */
|
||||
if(intid < 32){
|
||||
u32int *rregs = getrregs(cpu);
|
||||
rregs[GICR_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
|
||||
coherence();
|
||||
rregs[GICR_ISENABLER0] = 1 << (intid%32);
|
||||
coherence();
|
||||
while(rregs[GICR_CTLR]&(1<<3))
|
||||
;
|
||||
} else {
|
||||
dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
|
||||
dregs[GICD_TARGETSR0 + (intid/4)] |= (1<<cpu) << ((intid%4) << 3);
|
||||
coherence();
|
||||
dregs[GICD_ISENABLER0 + (intid/32)] = 1 << (intid%32);
|
||||
coherence();
|
||||
while(dregs[GICD_CTLR]&(1<<31))
|
||||
;
|
||||
}
|
||||
unlock(&vctllock);
|
||||
}
|
||||
|
||||
void
|
||||
intrdisable(int, void (*)(Ureg*, void*), void *, int, char*)
|
||||
{
|
||||
}
|
4
sys/src/9/imx8/init9.s
Normal file
4
sys/src/9/imx8/init9.s
Normal file
|
@ -0,0 +1,4 @@
|
|||
TEXT main(SB), 1, $8
|
||||
MOV $setSB(SB), R28 /* load the SB */
|
||||
MOV $boot(SB), R0
|
||||
B startboot(SB)
|
31
sys/src/9/imx8/io.h
Normal file
31
sys/src/9/imx8/io.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
enum {
|
||||
IRQfiq = -1,
|
||||
|
||||
PPI = 16,
|
||||
SPI = 32,
|
||||
|
||||
IRQcntps = PPI+13,
|
||||
IRQcntpns = PPI+14,
|
||||
|
||||
IRQuart1 = SPI+26,
|
||||
IRQuart2 = SPI+27,
|
||||
IRQuart3 = SPI+28,
|
||||
IRQuart4 = SPI+29,
|
||||
|
||||
IRQi2c1 = SPI+35,
|
||||
IRQi2c2 = SPI+36,
|
||||
IRQi2c3 = SPI+37,
|
||||
IRQi2c4 = SPI+38,
|
||||
|
||||
IRQrdc = SPI+39,
|
||||
|
||||
IRQusb1 = SPI+40,
|
||||
IRQusb2 = SPI+41,
|
||||
|
||||
IRQsctr0 = SPI+47,
|
||||
IRQsctr1 = SPI+48,
|
||||
|
||||
IRQenet1 = SPI+118,
|
||||
};
|
||||
|
||||
#define BUSUNKNOWN (-1)
|
681
sys/src/9/imx8/l.s
Normal file
681
sys/src/9/imx8/l.s
Normal file
|
@ -0,0 +1,681 @@
|
|||
#include "mem.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
#undef SYSREG
|
||||
#define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
|
||||
|
||||
TEXT _start(SB), 1, $-4
|
||||
MOV R0, R26 /* save */
|
||||
|
||||
MOV $setSB-KZERO(SB), R28
|
||||
BL svcmode<>(SB)
|
||||
|
||||
/* use dedicated stack pointer per exception level */
|
||||
MOVWU $1, R1
|
||||
MSR R1, SPSel
|
||||
|
||||
BL mmudisable<>(SB)
|
||||
|
||||
/* invalidate local caches */
|
||||
BL cachedwbinv(SB)
|
||||
BL cacheiinv(SB)
|
||||
|
||||
MOV $(MACHADDR(0)-KZERO), R27
|
||||
MRS MPIDR_EL1, R1
|
||||
ANDW $(MAXMACH-1), R1
|
||||
MOVWU $MACHSIZE, R2
|
||||
MULW R1, R2, R2
|
||||
SUB R2, R27
|
||||
|
||||
ADD $(MACHSIZE-16), R27, R2
|
||||
MOV R2, SP
|
||||
|
||||
CBNZ R1, _startup
|
||||
|
||||
/* clear page table and machs */
|
||||
MOV $(L1-KZERO), R1
|
||||
MOV $(MACHADDR(-1)-KZERO), R2
|
||||
_zerol1:
|
||||
MOV ZR, (R1)8!
|
||||
CMP R1, R2
|
||||
BNE _zerol1
|
||||
|
||||
/* clear BSS */
|
||||
MOV $edata-KZERO(SB), R1
|
||||
MOV $end-KZERO(SB), R2
|
||||
_zerobss:
|
||||
MOV ZR, (R1)8!
|
||||
CMP R1, R2
|
||||
BNE _zerobss
|
||||
|
||||
/* setup page tables */
|
||||
MOV $(L1-KZERO), R0
|
||||
BL mmu0init(SB)
|
||||
|
||||
SEVL
|
||||
_startup:
|
||||
WFE
|
||||
BL mmuenable<>(SB)
|
||||
|
||||
MOV R26, R0
|
||||
MOV $0, R26
|
||||
ORR $KZERO, R27
|
||||
MSR R27, TPIDR_EL1
|
||||
MOV $setSB(SB), R28
|
||||
|
||||
BL main(SB)
|
||||
|
||||
TEXT stop<>(SB), 1, $-4
|
||||
_stop:
|
||||
WFE
|
||||
B _stop
|
||||
|
||||
TEXT aaa<>(SB), 1, $-4
|
||||
xxx:
|
||||
MOV $(0x860040+VIRTIO), R1
|
||||
MOVW $'A', R2
|
||||
MOVW R2, (R1)
|
||||
B xxx
|
||||
|
||||
TEXT sev(SB), 1, $-4
|
||||
SEV
|
||||
WFE
|
||||
RETURN
|
||||
|
||||
TEXT svcmode<>(SB), 1, $-4
|
||||
MSR $0xF, DAIFSet
|
||||
MRS CurrentEL, R0
|
||||
ANDW $(3<<2), R0
|
||||
CMPW $(1<<2), R0
|
||||
BEQ el1
|
||||
CMPW $(2<<2), R0
|
||||
BEQ el2
|
||||
B stop<>(SB)
|
||||
el2:
|
||||
MOV $0, R0
|
||||
MSR R0, MDCR_EL2
|
||||
ISB $SY
|
||||
|
||||
/* HCR = RW, HCD, SWIO, BSU, FB */
|
||||
MOVWU $(1<<31 | 1<<29 | 1<<2 | 0<<10 | 0<<9), R0
|
||||
MSR R0, HCR_EL2
|
||||
ISB $SY
|
||||
|
||||
/* SCTLR = RES1 */
|
||||
MOVWU $(3<<4 | 1<<11 | 1<<16 | 1<<18 | 3<<22 | 3<<28), R0
|
||||
ISB $SY
|
||||
MSR R0, SCTLR_EL2
|
||||
ISB $SY
|
||||
|
||||
/* set VMID to zero */
|
||||
MOV $0, R0
|
||||
MSR R0, VTTBR_EL2
|
||||
ISB $SY
|
||||
|
||||
MOVWU $(0xF<<6 | 4), R0
|
||||
MSR R0, SPSR_EL2
|
||||
MSR LR, ELR_EL2
|
||||
ERET
|
||||
el1:
|
||||
RETURN
|
||||
|
||||
TEXT mmudisable<>(SB), 1, $-4
|
||||
#define SCTLRCLR \
|
||||
/* RES0 */ ( 3<<30 \
|
||||
/* RES0 */ | 1<<27 \
|
||||
/* UCI */ | 1<<26 \
|
||||
/* EE */ | 1<<25 \
|
||||
/* RES0 */ | 1<<21 \
|
||||
/* E0E */ | 1<<24 \
|
||||
/* WXN */ | 1<<19 \
|
||||
/* nTWE */ | 1<<18 \
|
||||
/* RES0 */ | 1<<17 \
|
||||
/* nTWI */ | 1<<16 \
|
||||
/* UCT */ | 1<<15 \
|
||||
/* DZE */ | 1<<14 \
|
||||
/* RES0 */ | 1<<13 \
|
||||
/* RES0 */ | 1<<10 \
|
||||
/* UMA */ | 1<<9 \
|
||||
/* SA0 */ | 1<<4 \
|
||||
/* SA */ | 1<<3 \
|
||||
/* A */ | 1<<1 )
|
||||
#define SCTLRSET \
|
||||
/* RES1 */ ( 3<<28 \
|
||||
/* RES1 */ | 3<<22 \
|
||||
/* RES1 */ | 1<<20 \
|
||||
/* RES1 */ | 1<<11 )
|
||||
#define SCTLRMMU \
|
||||
/* I */ ( 1<<12 \
|
||||
/* C */ | 1<<2 \
|
||||
/* M */ | 1<<0 )
|
||||
|
||||
/* initialise SCTLR, MMU and caches off */
|
||||
ISB $SY
|
||||
MRS SCTLR_EL1, R0
|
||||
BIC $(SCTLRCLR | SCTLRMMU), R0
|
||||
ORR $SCTLRSET, R0
|
||||
ISB $SY
|
||||
MSR R0, SCTLR_EL1
|
||||
ISB $SY
|
||||
|
||||
B flushlocaltlb(SB)
|
||||
|
||||
TEXT mmuenable<>(SB), 1, $-4
|
||||
/* return to virtual */
|
||||
ORR $KZERO, LR
|
||||
MOV LR, -16(RSP)!
|
||||
|
||||
BL flushlocaltlb(SB)
|
||||
|
||||
/* memory attributes */
|
||||
#define MAIRINIT \
|
||||
( 0xFF << MA_MEM_WB*8 \
|
||||
| 0x33 << MA_MEM_WT*8 \
|
||||
| 0x44 << MA_MEM_UC*8 \
|
||||
| 0x00 << MA_DEV_nGnRnE*8 \
|
||||
| 0x04 << MA_DEV_nGnRE*8 \
|
||||
| 0x08 << MA_DEV_nGRE*8 \
|
||||
| 0x0C << MA_DEV_GRE*8 )
|
||||
MOV $MAIRINIT, R1
|
||||
MSR R1, MAIR_EL1
|
||||
ISB $SY
|
||||
|
||||
/* translation control */
|
||||
#define TCRINIT \
|
||||
/* TBI1 */ ( 0<<38 \
|
||||
/* TBI0 */ | 0<<37 \
|
||||
/* AS */ | 0<<36 \
|
||||
/* TG1 */ | (((3<<16|1<<14|2<<12)>>PGSHIFT)&3)<<30 \
|
||||
/* SH1 */ | SHARE_INNER<<28 \
|
||||
/* ORGN1 */ | CACHE_WB<<26 \
|
||||
/* IRGN1 */ | CACHE_WB<<24 \
|
||||
/* EPD1 */ | 0<<23 \
|
||||
/* A1 */ | 0<<22 \
|
||||
/* T1SZ */ | (64-EVASHIFT)<<16 \
|
||||
/* TG0 */ | (((1<<16|2<<14|0<<12)>>PGSHIFT)&3)<<14 \
|
||||
/* SH0 */ | SHARE_INNER<<12 \
|
||||
/* ORGN0 */ | CACHE_WB<<10 \
|
||||
/* IRGN0 */ | CACHE_WB<<8 \
|
||||
/* EPD0 */ | 0<<7 \
|
||||
/* T0SZ */ | (64-EVASHIFT)<<0 )
|
||||
MOV $TCRINIT, R1
|
||||
MRS ID_AA64MMFR0_EL1, R2
|
||||
ANDW $0x7, R2 // PARange
|
||||
ADD R2<<32, R1 // IPS
|
||||
MSR R1, TCR_EL1
|
||||
ISB $SY
|
||||
|
||||
/* load the page tables */
|
||||
MOV $(L1TOP-KZERO), R0
|
||||
ISB $SY
|
||||
MSR R0, TTBR0_EL1
|
||||
MSR R0, TTBR1_EL1
|
||||
ISB $SY
|
||||
|
||||
/* enable MMU and caches */
|
||||
MRS SCTLR_EL1, R1
|
||||
ORR $SCTLRMMU, R1
|
||||
ISB $SY
|
||||
MSR R1, SCTLR_EL1
|
||||
ISB $SY
|
||||
|
||||
MOV RSP, R1
|
||||
ORR $KZERO, R1
|
||||
MOV R1, RSP
|
||||
MOV (RSP)16!, LR
|
||||
B cacheiinv(SB)
|
||||
|
||||
TEXT touser(SB), 1, $-4
|
||||
MSR $0x3, DAIFSet // interrupts off
|
||||
MOVWU $0x10028, R1 // entry
|
||||
MOVWU $0, R2 // psr
|
||||
MSR R0, SP_EL0 // sp
|
||||
MSR R1, ELR_EL1
|
||||
MSR R2, SPSR_EL1
|
||||
ERET
|
||||
|
||||
TEXT cas(SB), 1, $-4
|
||||
TEXT cmpswap(SB), 1, $-4
|
||||
MOVWU ov+8(FP), R1
|
||||
MOVWU nv+16(FP), R2
|
||||
_cas1:
|
||||
LDXRW (R0), R3
|
||||
CMP R3, R1
|
||||
BNE _cas0
|
||||
STXRW R2, (R0), R4
|
||||
CBNZ R4, _cas1
|
||||
MOVW $1, R0
|
||||
DMB $ISH
|
||||
RETURN
|
||||
_cas0:
|
||||
CLREX
|
||||
MOVW $0, R0
|
||||
RETURN
|
||||
|
||||
TEXT tas(SB), 1, $-4
|
||||
TEXT _tas(SB), 1, $-4
|
||||
MOVW $0xdeaddead, R2
|
||||
_tas1:
|
||||
LDXRW (R0), R1
|
||||
STXRW R2, (R0), R3
|
||||
CBNZ R3, _tas1
|
||||
MOVW R1, R0
|
||||
|
||||
TEXT coherence(SB), 1, $-4
|
||||
DMB $ISH
|
||||
RETURN
|
||||
|
||||
TEXT islo(SB), 1, $-4
|
||||
MRS DAIF, R0
|
||||
AND $(0x2<<6), R0
|
||||
EOR $(0x2<<6), R0
|
||||
RETURN
|
||||
|
||||
TEXT splhi(SB), 1, $-4
|
||||
MRS DAIF, R0
|
||||
MSR $0x2, DAIFSet
|
||||
RETURN
|
||||
|
||||
TEXT splfhi(SB), 1, $-4
|
||||
MRS DAIF, R0
|
||||
MSR $0x3, DAIFSet
|
||||
RETURN
|
||||
|
||||
TEXT spllo(SB), 1, $-4
|
||||
MSR $0x3, DAIFClr
|
||||
RETURN
|
||||
|
||||
TEXT splflo(SB), 1, $-4
|
||||
MSR $0x1, DAIFClr
|
||||
RETURN
|
||||
|
||||
TEXT splx(SB), 1, $-4
|
||||
MSR R0, DAIF
|
||||
RETURN
|
||||
|
||||
TEXT idlehands(SB), 1, $-4
|
||||
DMB $ISH
|
||||
MOV $nrdy(SB), R1
|
||||
LDXRW (R1), R0
|
||||
CBZ R0, _goodnight
|
||||
CLREX
|
||||
SEVL
|
||||
_goodnight:
|
||||
WFE
|
||||
RETURN
|
||||
|
||||
TEXT vcycles(SB), 1, $-4
|
||||
MRS CNTVCT_EL0, R0
|
||||
RETURN
|
||||
|
||||
TEXT lcycles(SB), 1, $-4
|
||||
MRS PMCCNTR_EL0, R0
|
||||
RETURN
|
||||
|
||||
TEXT setlabel(SB), 1, $-4
|
||||
MOV LR, 8(R0)
|
||||
MOV SP, R1
|
||||
MOV R1, 0(R0)
|
||||
MOVW $0, R0
|
||||
RETURN
|
||||
|
||||
TEXT gotolabel(SB), 1, $-4
|
||||
MOV 8(R0), LR /* link */
|
||||
MOV 0(R0), R1 /* sp */
|
||||
MOV R1, SP
|
||||
MOVW $1, R0
|
||||
RETURN
|
||||
|
||||
TEXT returnto(SB), 1, $-4
|
||||
MOV R0, 0(SP)
|
||||
RETURN
|
||||
|
||||
TEXT getfar(SB), 1, $-4
|
||||
MRS FAR_EL1, R0
|
||||
RETURN
|
||||
|
||||
TEXT setttbr(SB), 1, $-4
|
||||
DSB $ISHST
|
||||
MSR R0, TTBR0_EL1
|
||||
DSB $ISH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
/*
|
||||
* TLB maintenance operations.
|
||||
* these broadcast to all cpu's in the cluser
|
||||
* (inner sharable domain).
|
||||
*/
|
||||
TEXT flushasidva(SB), 1, $-4
|
||||
TEXT tlbivae1is(SB), 1, $-4
|
||||
DSB $ISHST
|
||||
TLBI R0, 0,8,3,1 /* VAE1IS */
|
||||
DSB $ISH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
TEXT flushasidvall(SB), 1, $-4
|
||||
TEXT tlbivale1is(SB), 1, $-4
|
||||
DSB $ISHST
|
||||
TLBI R0, 0,8,3,5 /* VALE1IS */
|
||||
DSB $ISH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
TEXT flushasid(SB), 1, $-4
|
||||
TEXT tlbiaside1is(SB), 1, $-4
|
||||
DSB $ISHST
|
||||
TLBI R0, 0,8,3,2 /* ASIDE1IS */
|
||||
DSB $ISH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
TEXT flushtlb(SB), 1, $-4
|
||||
TEXT tlbivmalle1is(SB), 1, $-4
|
||||
DSB $ISHST
|
||||
TLBI R0, 0,8,3,0 /* VMALLE1IS */
|
||||
DSB $ISH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
/*
|
||||
* flush the tlb of this cpu. no broadcast.
|
||||
*/
|
||||
TEXT flushlocaltlb(SB), 1, $-4
|
||||
TEXT tlbivmalle1(SB), 1, $-4
|
||||
DSB $NSHST
|
||||
TLBI R0, 0,8,7,0 /* VMALLE1 */
|
||||
DSB $NSH
|
||||
ISB $SY
|
||||
RETURN
|
||||
|
||||
TEXT fpsaveregs(SB), 1, $-4
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV { V0, V1, V2, V3 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV { V4, V5, V6, V7 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV { V8, V9, V10,V11 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV { V12,V13,V14,V15 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV { V16,V17,V18,V19 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV { V20,V21,V22,V23 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV { V24,V25,V26,V27 }, (R0)64! */
|
||||
WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV { V28,V29,V30,V31 }, (R0)64! */
|
||||
RETURN
|
||||
|
||||
TEXT fploadregs(SB), 1, $-4
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV (R0)64!, { V0, V1, V2, V3 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV (R0)64!, { V4, V5, V6, V7 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV (R0)64!, { V8, V9, V10,V11 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV (R0)64!, { V12,V13,V14,V15 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV (R0)64!, { V16,V17,V18,V19 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV (R0)64!, { V20,V21,V22,V23 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV (R0)64!, { V24,V25,V26,V27 } */
|
||||
WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV (R0)64!, { V28,V29,V30,V31 } */
|
||||
RETURN
|
||||
|
||||
// syscall or trap from EL0
|
||||
TEXT vsys0(SB), 1, $-4
|
||||
LSRW $26, R0, R17 // ec
|
||||
CMPW $0x15, R17 // SVC trap?
|
||||
BNE _itsatrap // nope.
|
||||
|
||||
MOVP R26, R27, 224(RSP)
|
||||
MOVP R28, R29, 240(RSP)
|
||||
|
||||
MRS SP_EL0, R1
|
||||
MRS ELR_EL1, R2
|
||||
MRS SPSR_EL1, R3
|
||||
|
||||
MOV R0, 288(RSP) // type
|
||||
MOV R1, 264(RSP) // sp
|
||||
MOV R2, 272(RSP) // pc
|
||||
MOV R3, 280(RSP) // psr
|
||||
|
||||
MOV $setSB(SB), R28
|
||||
MRS TPIDR_EL1, R27
|
||||
MOV 16(R27), R26
|
||||
|
||||
ADD $16, RSP, R0 // ureg
|
||||
BL syscall(SB)
|
||||
|
||||
TEXT forkret(SB), 1, $-4
|
||||
MSR $0x3, DAIFSet // interrupts off
|
||||
|
||||
ADD $16, RSP, R0 // ureg
|
||||
|
||||
MOV 16(RSP), R0 // ret
|
||||
MOV 264(RSP), R1 // sp
|
||||
MOV 272(RSP), R2 // pc
|
||||
MOV 280(RSP), R3 // psr
|
||||
|
||||
MSR R1, SP_EL0
|
||||
MSR R2, ELR_EL1
|
||||
MSR R3, SPSR_EL1
|
||||
|
||||
MOVP 224(RSP), R26, R27
|
||||
MOVP 240(RSP), R28, R29
|
||||
|
||||
MOV 256(RSP), R30 // link
|
||||
|
||||
ADD $TRAPFRAMESIZE, RSP
|
||||
ERET
|
||||
|
||||
TEXT itsatrap<>(SB), 1, $-4
|
||||
_itsatrap:
|
||||
MOVP R1, R2, 24(RSP)
|
||||
MOVP R3, R4, 40(RSP)
|
||||
MOVP R5, R6, 56(RSP)
|
||||
MOVP R7, R8, 72(RSP)
|
||||
MOVP R9, R10, 88(RSP)
|
||||
MOVP R11, R12, 104(RSP)
|
||||
MOVP R13, R14, 120(RSP)
|
||||
MOVP R15, R16, 136(RSP)
|
||||
|
||||
MOVP R18, R19, 160(RSP)
|
||||
MOVP R20, R21, 176(RSP)
|
||||
MOVP R22, R23, 192(RSP)
|
||||
MOVP R24, R25, 208(RSP)
|
||||
|
||||
// trap/irq/fiq/serr from EL0
|
||||
TEXT vtrap0(SB), 1, $-4
|
||||
MOVP R26, R27, 224(RSP)
|
||||
MOVP R28, R29, 240(RSP)
|
||||
|
||||
MRS SP_EL0, R1
|
||||
MRS ELR_EL1, R2
|
||||
MRS SPSR_EL1, R3
|
||||
|
||||
MOV R0, 288(RSP) // type
|
||||
MOV R1, 264(RSP) // sp
|
||||
MOV R2, 272(RSP) // pc
|
||||
MOV R3, 280(RSP) // psr
|
||||
|
||||
MOV $setSB(SB), R28
|
||||
MRS TPIDR_EL1, R27
|
||||
MOV 16(R27), R26
|
||||
|
||||
ADD $16, RSP, R0 // ureg
|
||||
BL trap(SB)
|
||||
|
||||
TEXT noteret(SB), 1, $-4
|
||||
MSR $0x3, DAIFSet // interrupts off
|
||||
|
||||
ADD $16, RSP, R0 // ureg
|
||||
|
||||
MOV 264(RSP), R1 // sp
|
||||
MOV 272(RSP), R2 // pc
|
||||
MOV 280(RSP), R3 // psr
|
||||
|
||||
MSR R1, SP_EL0
|
||||
MSR R2, ELR_EL1
|
||||
MSR R3, SPSR_EL1
|
||||
|
||||
MOVP 224(RSP), R26, R27
|
||||
MOVP 240(RSP), R28, R29
|
||||
|
||||
_intrreturn:
|
||||
MOVP 16(RSP), R0, R1
|
||||
MOVP 32(RSP), R2, R3
|
||||
MOVP 48(RSP), R4, R5
|
||||
MOVP 64(RSP), R6, R7
|
||||
MOVP 80(RSP), R8, R9
|
||||
MOVP 96(RSP), R10, R11
|
||||
MOVP 112(RSP), R12, R13
|
||||
MOVP 128(RSP), R14, R15
|
||||
MOVP 144(RSP), R16, R17
|
||||
MOVP 160(RSP), R18, R19
|
||||
MOVP 176(RSP), R20, R21
|
||||
MOVP 192(RSP), R22, R23
|
||||
MOVP 208(RSP), R24, R25
|
||||
|
||||
MOV 256(RSP), R30 // link
|
||||
|
||||
ADD $TRAPFRAMESIZE, RSP
|
||||
ERET
|
||||
|
||||
// irq/fiq/trap/serr from EL1
|
||||
TEXT vtrap1(SB), 1, $-4
|
||||
MOV R29, 248(RSP) // special
|
||||
|
||||
ADD $TRAPFRAMESIZE, RSP, R1
|
||||
MRS ELR_EL1, R2
|
||||
MRS SPSR_EL1, R3
|
||||
|
||||
MOV R0, 288(RSP) // type
|
||||
MOV R1, 264(RSP) // sp
|
||||
MOV R2, 272(RSP) // pc
|
||||
MOV R3, 280(RSP) // psr
|
||||
|
||||
ADD $16, RSP, R0 // ureg
|
||||
BL trap(SB)
|
||||
|
||||
MSR $0x3, DAIFSet // interrupts off
|
||||
|
||||
MOV 272(RSP), R2 // pc
|
||||
MOV 280(RSP), R3 // psr
|
||||
|
||||
MSR R2, ELR_EL1
|
||||
MSR R3, SPSR_EL1
|
||||
|
||||
MOV 248(RSP), R29 // special
|
||||
B _intrreturn
|
||||
|
||||
// vector tables
|
||||
TEXT vsys(SB), 1, $-4
|
||||
SUB $TRAPFRAMESIZE, RSP
|
||||
|
||||
MOV R0, 16(RSP)
|
||||
MOV R30, 256(RSP) // link
|
||||
|
||||
MOV R17, 152(RSP) // temp
|
||||
|
||||
MRS ESR_EL1, R0 // type
|
||||
|
||||
_vsyspatch:
|
||||
B _vsyspatch // branch to vsys0() patched in
|
||||
|
||||
TEXT vtrap(SB), 1, $-4
|
||||
SUB $TRAPFRAMESIZE, RSP
|
||||
|
||||
MOVP R0, R1, 16(RSP)
|
||||
MOVP R2, R3, 32(RSP)
|
||||
MOVP R4, R5, 48(RSP)
|
||||
MOVP R6, R7, 64(RSP)
|
||||
MOVP R8, R9, 80(RSP)
|
||||
MOVP R10, R11, 96(RSP)
|
||||
MOVP R12, R13, 112(RSP)
|
||||
MOVP R14, R15, 128(RSP)
|
||||
MOVP R16, R17, 144(RSP)
|
||||
MOVP R18, R19, 160(RSP)
|
||||
MOVP R20, R21, 176(RSP)
|
||||
MOVP R22, R23, 192(RSP)
|
||||
MOVP R24, R25, 208(RSP)
|
||||
|
||||
MOV R30, 256(RSP) // link
|
||||
|
||||
MRS ESR_EL1, R0 // type
|
||||
|
||||
_vtrappatch:
|
||||
B _vtrappatch // branch to vtrapX() patched in
|
||||
|
||||
TEXT virq(SB), 1, $-4
|
||||
SUB $TRAPFRAMESIZE, RSP
|
||||
|
||||
MOVP R0, R1, 16(RSP)
|
||||
MOVP R2, R3, 32(RSP)
|
||||
MOVP R4, R5, 48(RSP)
|
||||
MOVP R6, R7, 64(RSP)
|
||||
MOVP R8, R9, 80(RSP)
|
||||
MOVP R10, R11, 96(RSP)
|
||||
MOVP R12, R13, 112(RSP)
|
||||
MOVP R14, R15, 128(RSP)
|
||||
MOVP R16, R17, 144(RSP)
|
||||
MOVP R18, R19, 160(RSP)
|
||||
MOVP R20, R21, 176(RSP)
|
||||
MOVP R22, R23, 192(RSP)
|
||||
MOVP R24, R25, 208(RSP)
|
||||
|
||||
MOV R30, 256(RSP) // link
|
||||
|
||||
MOV $(1<<32), R0 // type irq
|
||||
|
||||
_virqpatch:
|
||||
B _virqpatch // branch to vtrapX() patched in
|
||||
|
||||
TEXT vfiq(SB), 1, $-4
|
||||
SUB $TRAPFRAMESIZE, RSP
|
||||
|
||||
MOVP R0, R1, 16(RSP)
|
||||
MOVP R2, R3, 32(RSP)
|
||||
MOVP R4, R5, 48(RSP)
|
||||
MOVP R6, R7, 64(RSP)
|
||||
MOVP R8, R9, 80(RSP)
|
||||
MOVP R10, R11, 96(RSP)
|
||||
MOVP R12, R13, 112(RSP)
|
||||
MOVP R14, R15, 128(RSP)
|
||||
MOVP R16, R17, 144(RSP)
|
||||
MOVP R18, R19, 160(RSP)
|
||||
MOVP R20, R21, 176(RSP)
|
||||
MOVP R22, R23, 192(RSP)
|
||||
MOVP R24, R25, 208(RSP)
|
||||
|
||||
MOV R30, 256(RSP) // link
|
||||
MOV $(2<<32), R0 // type fiq
|
||||
|
||||
_vfiqpatch:
|
||||
B _vfiqpatch // branch to vtrapX() patched in
|
||||
|
||||
TEXT vserr(SB), 1, $-4
|
||||
SUB $TRAPFRAMESIZE, RSP
|
||||
|
||||
MOVP R0, R1, 16(RSP)
|
||||
MOVP R2, R3, 32(RSP)
|
||||
MOVP R4, R5, 48(RSP)
|
||||
MOVP R6, R7, 64(RSP)
|
||||
MOVP R8, R9, 80(RSP)
|
||||
MOVP R10, R11, 96(RSP)
|
||||
MOVP R12, R13, 112(RSP)
|
||||
MOVP R14, R15, 128(RSP)
|
||||
MOVP R16, R17, 144(RSP)
|
||||
MOVP R18, R19, 160(RSP)
|
||||
MOVP R20, R21, 176(RSP)
|
||||
MOVP R22, R23, 192(RSP)
|
||||
MOVP R24, R25, 208(RSP)
|
||||
|
||||
MOV R30, 256(RSP) // link
|
||||
|
||||
MRS ESR_EL1, R0
|
||||
ORR $(3<<32), R0 // type
|
||||
_vserrpatch:
|
||||
B _vserrpatch // branch to vtrapX() patched in
|
||||
|
||||
/* fault-proof memcpy */
|
||||
TEXT peek(SB), 1, $-4
|
||||
MOV R0, R1
|
||||
MOV dst+8(FP), R2
|
||||
MOVWU len+16(FP), R0
|
||||
TEXT _peekinst(SB), 1, $-4
|
||||
_peekloop:
|
||||
MOVBU (R1)1!, R3
|
||||
MOVBU R3, (R2)1!
|
||||
SUBS $1, R0
|
||||
BNE _peekloop
|
||||
RETURN
|
218
sys/src/9/imx8/main.c
Normal file
218
sys/src/9/imx8/main.c
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "u.h"
|
||||
#include "tos.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
#include "pool.h"
|
||||
#include "io.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
Conf conf;
|
||||
|
||||
/*
|
||||
* starting place for first process
|
||||
*/
|
||||
void
|
||||
init0(void)
|
||||
{
|
||||
char **sp;
|
||||
|
||||
chandevinit();
|
||||
|
||||
if(!waserror()){
|
||||
ksetenv("cputype", "arm64", 0);
|
||||
if(cpuserver)
|
||||
ksetenv("service", "cpu", 0);
|
||||
else
|
||||
ksetenv("service", "terminal", 0);
|
||||
ksetenv("console", "0", 0);
|
||||
poperror();
|
||||
}
|
||||
kproc("alarm", alarmkproc, 0);
|
||||
sp = (char**)(USTKTOP-sizeof(Tos) - 8 - sizeof(sp[0])*4);
|
||||
sp[3] = sp[2] = sp[1] = nil;
|
||||
strcpy(sp[1] = (char*)&sp[4], "boot");
|
||||
sp[0] = (void*)&sp[1];
|
||||
touser((uintptr)sp);
|
||||
}
|
||||
|
||||
void
|
||||
confinit(void)
|
||||
{
|
||||
int userpcnt;
|
||||
ulong kpages;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
conf.nmach = 1;
|
||||
|
||||
if(p = getconf("service")){
|
||||
if(strcmp(p, "cpu") == 0)
|
||||
cpuserver = 1;
|
||||
else if(strcmp(p,"terminal") == 0)
|
||||
cpuserver = 0;
|
||||
}
|
||||
|
||||
if(p = getconf("*kernelpercent"))
|
||||
userpcnt = 100 - strtol(p, 0, 0);
|
||||
else
|
||||
userpcnt = 0;
|
||||
|
||||
if(userpcnt < 10)
|
||||
userpcnt = 60 + cpuserver*10;
|
||||
|
||||
conf.npage = 0;
|
||||
for(i = 0; i < nelem(conf.mem); i++)
|
||||
conf.npage += conf.mem[i].npage;
|
||||
|
||||
kpages = conf.npage - (conf.npage*userpcnt)/100;
|
||||
if(kpages > ((uintptr)-VDRAM)/BY2PG)
|
||||
kpages = ((uintptr)-VDRAM)/BY2PG;
|
||||
|
||||
conf.upages = conf.npage - kpages;
|
||||
conf.ialloc = (kpages/2)*BY2PG;
|
||||
|
||||
/* set up other configuration parameters */
|
||||
conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
|
||||
if(cpuserver)
|
||||
conf.nproc *= 3;
|
||||
if(conf.nproc > 2000)
|
||||
conf.nproc = 2000;
|
||||
conf.nswap = conf.npage*3;
|
||||
conf.nswppo = 4096;
|
||||
conf.nimage = 200;
|
||||
|
||||
conf.copymode = conf.nmach > 1;
|
||||
|
||||
/*
|
||||
* Guess how much is taken by the large permanent
|
||||
* datastructures. Mntcache and Mntrpc are not accounted for.
|
||||
*/
|
||||
kpages = conf.npage - conf.upages;
|
||||
kpages *= BY2PG;
|
||||
kpages -= conf.upages*sizeof(Page)
|
||||
+ conf.nproc*sizeof(Proc)
|
||||
+ conf.nimage*sizeof(Image)
|
||||
+ conf.nswap
|
||||
+ conf.nswppo*sizeof(Page*);
|
||||
mainmem->maxsize = kpages;
|
||||
if(!cpuserver)
|
||||
/*
|
||||
* give terminals lots of image memory, too; the dynamic
|
||||
* allocation will balance the load properly, hopefully.
|
||||
* be careful with 32-bit overflow.
|
||||
*/
|
||||
imagmem->maxsize = kpages;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
machinit(void)
|
||||
{
|
||||
m->ticks = 1;
|
||||
m->perf.period = 1;
|
||||
active.machs[m->machno] = 1;
|
||||
}
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
machinit();
|
||||
if(m->machno){
|
||||
trapinit();
|
||||
fpuinit();
|
||||
intrinit();
|
||||
clockinit();
|
||||
// cpuidprint();
|
||||
synccycles();
|
||||
timersinit();
|
||||
flushtlb();
|
||||
mmu1init();
|
||||
m->ticks = MACHP(0)->ticks;
|
||||
schedinit();
|
||||
return;
|
||||
}
|
||||
quotefmtinstall();
|
||||
meminit();
|
||||
confinit();
|
||||
xinit();
|
||||
uartconsinit();
|
||||
printinit();
|
||||
print("\nPlan 9\n");
|
||||
trapinit();
|
||||
fpuinit();
|
||||
intrinit();
|
||||
clockinit();
|
||||
timersinit();
|
||||
pageinit();
|
||||
procinit0();
|
||||
initseg();
|
||||
links();
|
||||
chandevreset();
|
||||
userinit();
|
||||
// mpinit();
|
||||
mmu0clear((uintptr*)L1);
|
||||
flushtlb();
|
||||
mmu1init();
|
||||
schedinit();
|
||||
}
|
||||
|
||||
void
|
||||
exit(int)
|
||||
{
|
||||
cpushutdown();
|
||||
splfhi();
|
||||
for(;;);
|
||||
}
|
||||
|
||||
int
|
||||
isaconfig(char *, int, ISAConf *)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char*
|
||||
getconf(char *)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
writeconf(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
reboot(void *, void *, ulong)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
dmaflush(int clean, void *p, ulong len)
|
||||
{
|
||||
uintptr s = (uintptr)p;
|
||||
uintptr e = (uintptr)p + len;
|
||||
|
||||
if(clean){
|
||||
s &= ~(BLOCKALIGN-1);
|
||||
e += BLOCKALIGN-1;
|
||||
e &= ~(BLOCKALIGN-1);
|
||||
cachedwbse((void*)s, e - s);
|
||||
return;
|
||||
}
|
||||
if(s & BLOCKALIGN-1){
|
||||
s &= ~(BLOCKALIGN-1);
|
||||
cachedwbinvse((void*)s, BLOCKALIGN);
|
||||
s += BLOCKALIGN;
|
||||
}
|
||||
if(e & BLOCKALIGN-1){
|
||||
e &= ~(BLOCKALIGN-1);
|
||||
if(e < s)
|
||||
return;
|
||||
cachedwbinvse((void*)e, BLOCKALIGN);
|
||||
}
|
||||
if(s < e)
|
||||
cachedinvse((void*)s, e - s);
|
||||
}
|
138
sys/src/9/imx8/mem.h
Normal file
138
sys/src/9/imx8/mem.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Memory and machine-specific definitions. Used in C and assembler.
|
||||
*/
|
||||
#define KiB 1024u /* Kibi 0x0000000000000400 */
|
||||
#define MiB 1048576u /* Mebi 0x0000000000100000 */
|
||||
#define GiB 1073741824u /* Gibi 000000000040000000 */
|
||||
|
||||
/*
|
||||
* Sizes:
|
||||
* L0 L1 L2 L3
|
||||
* 4K 2M 1G 512G
|
||||
* 16K 32M 64G 128T
|
||||
* 64K 512M 4T -
|
||||
*/
|
||||
#define PGSHIFT 16 /* log(BY2PG) */
|
||||
#define BY2PG (1ULL<<PGSHIFT) /* bytes per page */
|
||||
#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
|
||||
#define PGROUND(s) ROUND(s, BY2PG)
|
||||
|
||||
/* effective virtual address space */
|
||||
#define EVASHIFT 34
|
||||
#define EVAMASK ((1ULL<<EVASHIFT)-1)
|
||||
|
||||
#define PTSHIFT (PGSHIFT-3)
|
||||
#define PTLEVELS (((EVASHIFT-PGSHIFT)+PTSHIFT-1)/PTSHIFT)
|
||||
#define PTLX(v, l) ((((v) & EVAMASK) >> (PGSHIFT + (l)*PTSHIFT)) & ((1 << PTSHIFT)-1))
|
||||
#define PGLSZ(l) (1ULL << (PGSHIFT + (l)*PTSHIFT))
|
||||
|
||||
#define PTL1X(v, l) (L1TABLEX(v, l) | PTLX(v, l))
|
||||
#define L1TABLEX(v, l) (L1TABLE(v, l) << PTSHIFT)
|
||||
#define L1TABLES ((-KSEG0+PGLSZ(2)-1)/PGLSZ(2))
|
||||
#define L1TABLE(v, l) (L1TABLES - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1)
|
||||
#define L1TOPSIZE (1ULL << (EVASHIFT - PTLEVELS*PTSHIFT))
|
||||
|
||||
#define MAXMACH 4 /* max # cpus system can run */
|
||||
#define MACHSIZE (8*KiB)
|
||||
|
||||
#define KSTACK (8*KiB)
|
||||
#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */
|
||||
#define TRAPFRAMESIZE (38*8)
|
||||
|
||||
/* reserved dram for ucalloc() at the end of KZERO (physical) */
|
||||
#define UCRAMBASE (-KZERO - UCRAMSIZE)
|
||||
#define UCRAMSIZE (1*MiB)
|
||||
|
||||
#define VDRAM (0xFFFFFFFFC0000000ULL) /* 0x40000000 - 0x80000000 */
|
||||
#define KTZERO (VDRAM + 0x100000) /* kernel text start */
|
||||
|
||||
#define ARMLOCAL (0xFFFFFFFFB1000000ULL) /* 0x31000000 */
|
||||
#define VIRTIO (0xFFFFFFFFB0000000ULL) /* 0x30000000 */
|
||||
|
||||
#define KZERO (0xFFFFFFFF80000000ULL) /* 0x00000000 - kernel address space */
|
||||
|
||||
#define VMAP (0xFFFFFFFF00000000ULL) /* 0x00000000 - 0x40000000 */
|
||||
|
||||
#define KMAPEND (0xFFFFFFFF00000000ULL) /* 0x140000000 */
|
||||
#define KMAP (0xFFFFFFFE00000000ULL) /* 0x40000000 */
|
||||
|
||||
#define KSEG0 (0xFFFFFFFE00000000ULL)
|
||||
|
||||
#define L1 (L1TOP-L1SIZE)
|
||||
#define L1SIZE ((L1TABLES+PTLEVELS-2)*BY2PG)
|
||||
#define L1TOP ((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG)
|
||||
|
||||
#define MACHADDR(n) (KTZERO-((n)+1)*MACHSIZE)
|
||||
|
||||
#define UZERO 0ULL /* user segment */
|
||||
#define UTZERO (UZERO+0x10000) /* user text start */
|
||||
#define USTKTOP ((EVAMASK>>1)-0xFFFF) /* user segment end +1 */
|
||||
#define USTKSIZE (16*1024*1024) /* user stack size */
|
||||
|
||||
#define BLOCKALIGN 64 /* only used in allocb.c */
|
||||
|
||||
/*
|
||||
* Sizes
|
||||
*/
|
||||
#define BI2BY 8 /* bits per byte */
|
||||
#define BY2SE 4
|
||||
#define BY2WD 8
|
||||
#define BY2V 8 /* only used in xalloc.c */
|
||||
|
||||
#define PTEMAPMEM (1024*1024)
|
||||
#define PTEPERTAB (PTEMAPMEM/BY2PG)
|
||||
#define SEGMAPSIZE 8192
|
||||
#define SSEGMAPSIZE 16
|
||||
#define PPN(x) ((x)&~(BY2PG-1))
|
||||
|
||||
#define SHARE_NONE 0
|
||||
#define SHARE_OUTER 2
|
||||
#define SHARE_INNER 3
|
||||
|
||||
#define CACHE_UC 0
|
||||
#define CACHE_WB 1
|
||||
#define CACHE_WT 2
|
||||
#define CACHE_WB_NA 3
|
||||
|
||||
#define MA_MEM_WB 0
|
||||
#define MA_MEM_WT 1
|
||||
#define MA_MEM_UC 2
|
||||
#define MA_DEV_nGnRnE 3
|
||||
#define MA_DEV_nGnRE 4
|
||||
#define MA_DEV_nGRE 5
|
||||
#define MA_DEV_GRE 6
|
||||
|
||||
#define PTEVALID 1
|
||||
#define PTEBLOCK 0
|
||||
#define PTETABLE 2
|
||||
#define PTEPAGE 2
|
||||
|
||||
#define PTEMA(x) ((x)<<2)
|
||||
#define PTEAP(x) ((x)<<6)
|
||||
#define PTESH(x) ((x)<<8)
|
||||
|
||||
#define PTEAF (1<<10)
|
||||
#define PTENG (1<<11)
|
||||
#define PTEPXN (1ULL<<53)
|
||||
#define PTEUXN (1ULL<<54)
|
||||
|
||||
#define PTEKERNEL PTEAP(0)
|
||||
#define PTEUSER PTEAP(1)
|
||||
#define PTEWRITE PTEAP(0)
|
||||
#define PTERONLY PTEAP(2)
|
||||
#define PTENOEXEC (PTEPXN|PTEUXN)
|
||||
|
||||
#define PTECACHED PTEMA(MA_MEM_WB)
|
||||
#define PTEWT PTEMA(MA_MEM_WT)
|
||||
#define PTEUNCACHED PTEMA(MA_MEM_UC)
|
||||
#define PTEDEVICE PTEMA(MA_DEV_nGnRE)
|
||||
|
||||
/*
|
||||
* Physical machine information from here on.
|
||||
* PHYS addresses as seen from the arm cpu.
|
||||
* BUS addresses as seen from peripherals
|
||||
*/
|
||||
#define PHYSDRAM 0
|
||||
|
||||
#define MIN(a, b) ((a) < (b)? (a): (b))
|
||||
#define MAX(a, b) ((a) > (b)? (a): (b))
|
94
sys/src/9/imx8/mkfile
Normal file
94
sys/src/9/imx8/mkfile
Normal file
|
@ -0,0 +1,94 @@
|
|||
CONF=reform
|
||||
CONFLIST=reform
|
||||
|
||||
kzero=0xffffffff80000000
|
||||
loadaddr=0xffffffffc0100000
|
||||
|
||||
objtype=arm64
|
||||
</$objtype/mkfile
|
||||
p=9
|
||||
|
||||
DEVS=`{rc ../port/mkdevlist $CONF}
|
||||
|
||||
PORT=\
|
||||
alarm.$O\
|
||||
alloc.$O\
|
||||
allocb.$O\
|
||||
auth.$O\
|
||||
cache.$O\
|
||||
chan.$O\
|
||||
dev.$O\
|
||||
edf.$O\
|
||||
fault.$O\
|
||||
mul64fract.$O\
|
||||
page.$O\
|
||||
parse.$O\
|
||||
pgrp.$O\
|
||||
portclock.$O\
|
||||
print.$O\
|
||||
proc.$O\
|
||||
qio.$O\
|
||||
qlock.$O\
|
||||
rdb.$O\
|
||||
rebootcmd.$O\
|
||||
segment.$O\
|
||||
syscallfmt.$O\
|
||||
sysfile.$O\
|
||||
sysproc.$O\
|
||||
taslock.$O\
|
||||
tod.$O\
|
||||
xalloc.$O\
|
||||
userinit.$O\
|
||||
|
||||
OBJ=\
|
||||
l.$O\
|
||||
cache.v8.$O\
|
||||
clock.$O\
|
||||
fpu.$O\
|
||||
main.$O\
|
||||
mmu.$O\
|
||||
sysreg.$O\
|
||||
random.$O\
|
||||
trap.$O\
|
||||
$CONF.root.$O\
|
||||
$CONF.rootc.$O\
|
||||
$DEVS\
|
||||
$PORT\
|
||||
|
||||
# HFILES=
|
||||
|
||||
LIB=\
|
||||
# /$objtype/lib/libmemlayer.a\
|
||||
# /$objtype/lib/libmemdraw.a\
|
||||
# /$objtype/lib/libdraw.a\
|
||||
/$objtype/lib/libip.a\
|
||||
/$objtype/lib/libsec.a\
|
||||
# /$objtype/lib/libmp.a\
|
||||
/$objtype/lib/libc.a\
|
||||
# /$objtype/lib/libdtracy.a\
|
||||
|
||||
9:V: $p$CONF $p$CONF.u
|
||||
|
||||
$p$CONF.u:D: $p$CONF
|
||||
aux/aout2uimage -Z$kzero $p$CONF
|
||||
|
||||
$p$CONF:D: $OBJ $CONF.$O $LIB
|
||||
$LD -o $target -T$loadaddr -l $prereq
|
||||
size $target
|
||||
|
||||
$OBJ: $HFILES
|
||||
|
||||
install:V: /$objtype/$p$CONF
|
||||
|
||||
/$objtype/$p$CONF:D: $p$CONF s$p$CONF
|
||||
cp -x $p$CONF $p$CONF.u /$objtype/
|
||||
|
||||
<../boot/bootmkfile
|
||||
<../port/portmkfile
|
||||
<|../port/mkbootrules $CONF
|
||||
|
||||
initcode.out: init9.$O initcode.$O /$objtype/lib/libc.a
|
||||
$LD -l -R1 -s -o $target $prereq
|
||||
|
||||
$CONF.clean:
|
||||
rm -rf $p$CONF $p$CONF.u errstr.h $CONF.c boot$CONF.c
|
492
sys/src/9/imx8/mmu.c
Normal file
492
sys/src/9/imx8/mmu.c
Normal file
|
@ -0,0 +1,492 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
void
|
||||
mmu0init(uintptr *l1)
|
||||
{
|
||||
uintptr va, pa, pe, attr;
|
||||
|
||||
/* VDRAM */
|
||||
attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTESH(SHARE_INNER);
|
||||
pe = -KZERO;
|
||||
for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
|
||||
l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
|
||||
l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | attr;
|
||||
}
|
||||
|
||||
attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE;
|
||||
pe = VDRAM - KZERO;
|
||||
for(pa = VIRTIO - KZERO, va = VIRTIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
|
||||
if(((pa|va) & PGLSZ(1)-1) != 0){
|
||||
l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
|
||||
for(; pa < pe && ((va|pa) & PGLSZ(1)-1) != 0; pa += PGLSZ(0), va += PGLSZ(0)){
|
||||
assert(l1[PTLX(va, 0)] == 0);
|
||||
l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
|
||||
}
|
||||
|
||||
if(PTLEVELS > 2)
|
||||
for(va = KSEG0; va != 0; va += PGLSZ(2))
|
||||
l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
|
||||
if(PTLEVELS > 3)
|
||||
for(va = KSEG0; va != 0; va += PGLSZ(3))
|
||||
l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
|
||||
}
|
||||
|
||||
void
|
||||
mmu0clear(uintptr *l1)
|
||||
{
|
||||
uintptr va, pa, pe;
|
||||
|
||||
pe = -VDRAM;
|
||||
for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
|
||||
if(PTL1X(pa, 1) != PTL1X(va, 1))
|
||||
l1[PTL1X(pa, 1)] = 0;
|
||||
|
||||
if(PTLEVELS > 2)
|
||||
for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
|
||||
if(PTL1X(pa, 2) != PTL1X(va, 2))
|
||||
l1[PTL1X(pa, 2)] = 0;
|
||||
if(PTLEVELS > 3)
|
||||
for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
|
||||
if(PTL1X(pa, 3) != PTL1X(va, 3))
|
||||
l1[PTL1X(pa, 3)] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mmu1init(void)
|
||||
{
|
||||
m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0);
|
||||
if(m->mmutop == nil)
|
||||
panic("mmu1init: no memory for mmutop");
|
||||
memset(m->mmutop, 0, L1TOPSIZE);
|
||||
mmuswitch(nil);
|
||||
}
|
||||
|
||||
/* KZERO maps the first 1GB of ram */
|
||||
uintptr
|
||||
paddr(void *va)
|
||||
{
|
||||
if((uintptr)va >= KZERO)
|
||||
return (uintptr)va-KZERO;
|
||||
panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr
|
||||
cankaddr(uintptr pa)
|
||||
{
|
||||
if(pa < (uintptr)-KZERO)
|
||||
return -KZERO - pa;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void*
|
||||
kaddr(uintptr pa)
|
||||
{
|
||||
if(pa < (uintptr)-KZERO)
|
||||
return (void*)(pa + KZERO);
|
||||
panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void*
|
||||
kmapaddr(uintptr pa)
|
||||
{
|
||||
if(pa < (uintptr)-KZERO)
|
||||
return (void*)(pa + KZERO);
|
||||
if(pa < (VDRAM - KZERO) || pa >= (VDRAM - KZERO) + (KMAPEND - KMAP))
|
||||
panic("kmapaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
|
||||
return (void*)(pa + KMAP - (VDRAM - KZERO));
|
||||
}
|
||||
|
||||
KMap*
|
||||
kmap(Page *p)
|
||||
{
|
||||
return kmapaddr(p->pa);
|
||||
}
|
||||
|
||||
void
|
||||
kunmap(KMap*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
kmapinval(void)
|
||||
{
|
||||
}
|
||||
|
||||
#define INITMAP (ROUND((uintptr)end + BY2PG, PGLSZ(1))-KZERO)
|
||||
|
||||
static void*
|
||||
rampage(void)
|
||||
{
|
||||
uintptr pa;
|
||||
|
||||
if(conf.npage)
|
||||
return mallocalign(BY2PG, BY2PG, 0, 0);
|
||||
|
||||
pa = conf.mem[0].base;
|
||||
assert((pa % BY2PG) == 0);
|
||||
assert(pa < INITMAP);
|
||||
conf.mem[0].base += BY2PG;
|
||||
return KADDR(pa);
|
||||
}
|
||||
|
||||
static void
|
||||
l1map(uintptr va, uintptr pa, uintptr pe, uintptr attr)
|
||||
{
|
||||
uintptr *l1, *l0;
|
||||
|
||||
assert(pa < pe);
|
||||
|
||||
va &= -BY2PG;
|
||||
pa &= -BY2PG;
|
||||
pe = PGROUND(pe);
|
||||
|
||||
attr |= PTEKERNEL | PTEAF;
|
||||
|
||||
l1 = (uintptr*)L1;
|
||||
|
||||
while(pa < pe){
|
||||
if(l1[PTL1X(va, 1)] == 0 && (pe-pa) >= PGLSZ(1) && ((va|pa) & PGLSZ(1)-1) == 0){
|
||||
l1[PTL1X(va, 1)] = PTEVALID | PTEBLOCK | pa | attr;
|
||||
va += PGLSZ(1);
|
||||
pa += PGLSZ(1);
|
||||
continue;
|
||||
}
|
||||
if(l1[PTL1X(va, 1)] & PTEVALID) {
|
||||
assert((l1[PTL1X(va, 1)] & PTETABLE) == PTETABLE);
|
||||
l0 = KADDR(l1[PTL1X(va, 1)] & -PGLSZ(0));
|
||||
} else {
|
||||
l0 = rampage();
|
||||
memset(l0, 0, BY2PG);
|
||||
l1[PTL1X(va, 1)] = PTEVALID | PTETABLE | PADDR(l0);
|
||||
}
|
||||
assert(l0[PTLX(va, 0)] == 0);
|
||||
l0[PTLX(va, 0)] = PTEVALID | PTEPAGE | pa | attr;
|
||||
va += BY2PG;
|
||||
pa += BY2PG;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kmapram(uintptr base, uintptr limit)
|
||||
{
|
||||
if(base < (uintptr)-KZERO && limit > (uintptr)-KZERO){
|
||||
kmapram(base, (uintptr)-KZERO);
|
||||
kmapram((uintptr)-KZERO, limit);
|
||||
return;
|
||||
}
|
||||
if(base < INITMAP)
|
||||
base = INITMAP;
|
||||
if(base >= limit || limit <= INITMAP)
|
||||
return;
|
||||
|
||||
l1map((uintptr)kmapaddr(base), base, limit,
|
||||
PTEWRITE | PTEPXN | PTEUXN | PTESH(SHARE_INNER));
|
||||
}
|
||||
|
||||
void
|
||||
meminit(void)
|
||||
{
|
||||
uintptr va, pa;
|
||||
|
||||
/*
|
||||
* now we know the real memory regions, unmap
|
||||
* everything above INITMAP and map again with
|
||||
* the proper sizes.
|
||||
*/
|
||||
coherence();
|
||||
for(va = INITMAP+KZERO; va != 0; va += PGLSZ(1)){
|
||||
pa = va-KZERO;
|
||||
((uintptr*)L1)[PTL1X(pa, 1)] = 0;
|
||||
((uintptr*)L1)[PTL1X(va, 1)] = 0;
|
||||
}
|
||||
flushtlb();
|
||||
|
||||
/* DDR Memory (All modules) */
|
||||
conf.mem[0].base = PGROUND((uintptr)end - KZERO);
|
||||
|
||||
/* exclude uncached dram for ucalloc() */
|
||||
conf.mem[0].limit = UCRAMBASE;
|
||||
conf.mem[1].base = UCRAMBASE+UCRAMSIZE;
|
||||
|
||||
conf.mem[1].limit = 0x100000000ULL;
|
||||
|
||||
/* DDR Memory (Quad-A53 only) */
|
||||
conf.mem[2].base = 0x100000000ULL;
|
||||
conf.mem[2].limit = 0x140000000ULL;
|
||||
|
||||
kmapram(conf.mem[0].base, conf.mem[0].limit);
|
||||
kmapram(conf.mem[1].base, conf.mem[1].limit);
|
||||
kmapram(conf.mem[2].base, conf.mem[2].limit);
|
||||
|
||||
conf.mem[0].npage = (conf.mem[0].limit - conf.mem[0].base)/BY2PG;
|
||||
conf.mem[1].npage = (conf.mem[1].limit - conf.mem[1].base)/BY2PG;
|
||||
conf.mem[2].npage = (conf.mem[2].limit - conf.mem[2].base)/BY2PG;
|
||||
}
|
||||
|
||||
uintptr
|
||||
mmukmap(uintptr va, uintptr pa, usize size)
|
||||
{
|
||||
uintptr attr, off;
|
||||
|
||||
if(va == 0)
|
||||
return 0;
|
||||
|
||||
off = pa & BY2PG-1;
|
||||
|
||||
attr = va & PTEMA(7);
|
||||
attr |= PTEWRITE | PTEUXN | PTEPXN | PTESH(SHARE_OUTER);
|
||||
|
||||
va &= -BY2PG;
|
||||
pa &= -BY2PG;
|
||||
|
||||
l1map(va, pa, pa + off + size, attr);
|
||||
flushtlb();
|
||||
|
||||
return va + off;
|
||||
}
|
||||
|
||||
void*
|
||||
vmap(uvlong pa, vlong size)
|
||||
{
|
||||
static uintptr base = VMAP;
|
||||
uvlong pe = pa + size;
|
||||
uintptr va;
|
||||
|
||||
va = base;
|
||||
base += PGROUND(pe) - (pa & -BY2PG);
|
||||
|
||||
return (void*)mmukmap(va | PTEDEVICE, pa, size);
|
||||
}
|
||||
|
||||
void
|
||||
vunmap(void *, vlong)
|
||||
{
|
||||
}
|
||||
|
||||
static uintptr*
|
||||
mmuwalk(uintptr va, int level)
|
||||
{
|
||||
uintptr *table, pte;
|
||||
Page *pg;
|
||||
int i, x;
|
||||
|
||||
x = PTLX(va, PTLEVELS-1);
|
||||
table = m->mmutop;
|
||||
for(i = PTLEVELS-2; i >= level; i--){
|
||||
pte = table[x];
|
||||
if(pte & PTEVALID) {
|
||||
if(pte & (0xFFFFULL<<48))
|
||||
iprint("strange pte %#p va %#p\n", pte, va);
|
||||
pte &= ~(0xFFFFULL<<48 | BY2PG-1);
|
||||
} else {
|
||||
pg = up->mmufree;
|
||||
if(pg == nil)
|
||||
return nil;
|
||||
up->mmufree = pg->next;
|
||||
pg->va = va & -PGLSZ(i+1);
|
||||
if((pg->next = up->mmuhead[i+1]) == nil)
|
||||
up->mmutail[i+1] = pg;
|
||||
up->mmuhead[i+1] = pg;
|
||||
pte = pg->pa;
|
||||
memset(kmapaddr(pte), 0, BY2PG);
|
||||
coherence();
|
||||
table[x] = pte | PTEVALID | PTETABLE;
|
||||
}
|
||||
table = kmapaddr(pte);
|
||||
x = PTLX(va, (uintptr)i);
|
||||
}
|
||||
return &table[x];
|
||||
}
|
||||
|
||||
static Proc *asidlist[256];
|
||||
|
||||
static int
|
||||
allocasid(Proc *p)
|
||||
{
|
||||
static Lock lk;
|
||||
Proc *x;
|
||||
int a;
|
||||
|
||||
lock(&lk);
|
||||
a = p->asid;
|
||||
if(a < 0)
|
||||
a = -a;
|
||||
if(a == 0)
|
||||
a = p->pid;
|
||||
for(;; a++){
|
||||
a %= nelem(asidlist);
|
||||
if(a == 0)
|
||||
continue; // reserved
|
||||
x = asidlist[a];
|
||||
if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
|
||||
break;
|
||||
}
|
||||
p->asid = a;
|
||||
asidlist[a] = p;
|
||||
unlock(&lk);
|
||||
|
||||
return x != p;
|
||||
}
|
||||
|
||||
static void
|
||||
freeasid(Proc *p)
|
||||
{
|
||||
int a;
|
||||
|
||||
a = p->asid;
|
||||
if(a < 0)
|
||||
a = -a;
|
||||
if(a > 0 && asidlist[a] == p)
|
||||
asidlist[a] = nil;
|
||||
p->asid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
putasid(Proc *p)
|
||||
{
|
||||
/*
|
||||
* Prevent the following scenario:
|
||||
* pX sleeps on cpuA, leaving its page tables in mmutop
|
||||
* pX wakes up on cpuB, and exits, freeing its page tables
|
||||
* pY on cpuB allocates a freed page table page and overwrites with data
|
||||
* cpuA takes an interrupt, and is now running with bad page tables
|
||||
* In theory this shouldn't hurt because only user address space tables
|
||||
* are affected, and mmuswitch will clear mmutop before a user process is
|
||||
* dispatched. But empirically it correlates with weird problems, eg
|
||||
* resetting of the core clock at 0x4000001C which confuses local timers.
|
||||
*/
|
||||
if(conf.nmach > 1)
|
||||
mmuswitch(nil);
|
||||
|
||||
if(p->asid > 0)
|
||||
p->asid = -p->asid;
|
||||
}
|
||||
|
||||
void
|
||||
putmmu(uintptr va, uintptr pa, Page *pg)
|
||||
{
|
||||
uintptr *pte, old;
|
||||
int s;
|
||||
|
||||
s = splhi();
|
||||
while((pte = mmuwalk(va, 0)) == nil){
|
||||
spllo();
|
||||
up->mmufree = newpage(0, nil, 0);
|
||||
splhi();
|
||||
}
|
||||
old = *pte;
|
||||
*pte = 0;
|
||||
if((old & PTEVALID) != 0)
|
||||
flushasidvall((uvlong)up->asid<<48 | va>>12);
|
||||
else
|
||||
flushasidva((uvlong)up->asid<<48 | va>>12);
|
||||
*pte = pa | PTEPAGE | PTEUSER | PTEPXN | PTENG | PTEAF |
|
||||
(((pa & PTEMA(7)) == PTECACHED)? PTESH(SHARE_INNER): PTESH(SHARE_OUTER));
|
||||
if(needtxtflush(pg)){
|
||||
cachedwbinvse(kmap(pg), BY2PG);
|
||||
cacheiinvse((void*)va, BY2PG);
|
||||
donetxtflush(pg);
|
||||
}
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static void
|
||||
mmufree(Proc *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
freeasid(p);
|
||||
|
||||
for(i=1; i<PTLEVELS; i++){
|
||||
if(p->mmuhead[i] == nil)
|
||||
break;
|
||||
p->mmutail[i]->next = p->mmufree;
|
||||
p->mmufree = p->mmuhead[i];
|
||||
p->mmuhead[i] = p->mmutail[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mmuswitch(Proc *p)
|
||||
{
|
||||
uintptr va;
|
||||
Page *t;
|
||||
|
||||
for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
|
||||
m->mmutop[PTLX(va, PTLEVELS-1)] = 0;
|
||||
|
||||
if(p == nil){
|
||||
setttbr(PADDR(m->mmutop));
|
||||
return;
|
||||
}
|
||||
|
||||
if(p->newtlb){
|
||||
mmufree(p);
|
||||
p->newtlb = 0;
|
||||
}
|
||||
|
||||
if(allocasid(p))
|
||||
flushasid((uvlong)p->asid<<48);
|
||||
|
||||
setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop));
|
||||
|
||||
for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){
|
||||
va = t->va;
|
||||
m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID | PTETABLE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mmurelease(Proc *p)
|
||||
{
|
||||
mmuswitch(nil);
|
||||
mmufree(p);
|
||||
freepages(p->mmufree, nil, 0);
|
||||
p->mmufree = nil;
|
||||
}
|
||||
|
||||
void
|
||||
flushmmu(void)
|
||||
{
|
||||
int x;
|
||||
|
||||
x = splhi();
|
||||
up->newtlb = 1;
|
||||
mmuswitch(up);
|
||||
splx(x);
|
||||
}
|
||||
|
||||
void
|
||||
checkmmu(uintptr, uintptr)
|
||||
{
|
||||
}
|
||||
|
||||
void*
|
||||
ucalloc(usize size)
|
||||
{
|
||||
static uintptr top = UCRAMBASE + UCRAMSIZE;
|
||||
static Lock lk;
|
||||
uintptr va;
|
||||
|
||||
size = PGROUND(size);
|
||||
|
||||
lock(&lk);
|
||||
top -= size;
|
||||
if(top < UCRAMBASE)
|
||||
panic("ucalloc: %p needs %zd bytes\n", getcallerpc(&size), size);
|
||||
va = KZERO + top;
|
||||
unlock(&lk);
|
||||
|
||||
return (void*)mmukmap(va | PTEUNCACHED, PADDR(va), size);
|
||||
}
|
39
sys/src/9/imx8/reform
Normal file
39
sys/src/9/imx8/reform
Normal file
|
@ -0,0 +1,39 @@
|
|||
dev
|
||||
root
|
||||
cons
|
||||
swap
|
||||
env
|
||||
pipe
|
||||
proc
|
||||
mnt
|
||||
srv
|
||||
shr
|
||||
dup
|
||||
tls
|
||||
cap
|
||||
fs
|
||||
ether netif
|
||||
ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
|
||||
uart
|
||||
link
|
||||
etherimx ethermii
|
||||
ethermedium
|
||||
loopbackmedium
|
||||
ip
|
||||
tcp
|
||||
udp
|
||||
il
|
||||
ipifc
|
||||
icmp
|
||||
icmp6
|
||||
ipmux
|
||||
misc
|
||||
gic
|
||||
uartimx
|
||||
port
|
||||
int cpuserver = 0;
|
||||
bootdir
|
||||
/$objtype/bin/paqfs
|
||||
/$objtype/bin/auth/factotum
|
||||
bootfs.paq
|
||||
boot
|
58
sys/src/9/imx8/sysreg.c
Normal file
58
sys/src/9/imx8/sysreg.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* ARMv8 system registers
|
||||
* mainly to cope with arm hard-wiring register numbers into instructions.
|
||||
*
|
||||
* these routines must be callable from KZERO.
|
||||
*
|
||||
* on a multiprocessor, process switching to another cpu is assumed
|
||||
* to be inhibited by the caller as these registers are local to the cpu.
|
||||
*/
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
static void*
|
||||
mkinstr(ulong wd)
|
||||
{
|
||||
static ulong ib[256], *ep[MAXMACH+1];
|
||||
static Lock lk;
|
||||
ulong *ip, *ie;
|
||||
|
||||
ie = ep[m->machno];
|
||||
for(ip = ib; ip < ie; ip += 2)
|
||||
if(*ip == wd)
|
||||
return ip;
|
||||
|
||||
ilock(&lk);
|
||||
ie = ep[MAXMACH];
|
||||
for(; ip < ie; ip += 2)
|
||||
if(*ip == wd)
|
||||
goto Found;
|
||||
if(ip >= &ib[nelem(ib)])
|
||||
panic("mkinstr: out of instrucuction buffer");
|
||||
ip[0] = wd;
|
||||
ip[1] = 0xd65f03c0; // RETURN
|
||||
ep[MAXMACH] = ie = ip + 2;
|
||||
cachedwbinvse(ip, 2*sizeof(*ip));
|
||||
Found:
|
||||
iunlock(&lk);
|
||||
cacheiinv();
|
||||
ep[m->machno] = ie;
|
||||
return ip;
|
||||
}
|
||||
|
||||
uvlong
|
||||
sysrd(ulong spr)
|
||||
{
|
||||
uvlong (*fp)(void) = mkinstr(0xd5380000UL | spr);
|
||||
return fp();
|
||||
}
|
||||
|
||||
void
|
||||
syswr(ulong spr, uvlong val)
|
||||
{
|
||||
void (*fp)(uvlong) = mkinstr(0xd5180000UL | spr);
|
||||
fp(val);
|
||||
}
|
89
sys/src/9/imx8/sysreg.h
Normal file
89
sys/src/9/imx8/sysreg.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#define MIDR_EL1 SYSREG(3,0,0,0,0)
|
||||
#define MPIDR_EL1 SYSREG(3,0,0,0,5)
|
||||
#define ID_AA64AFR0_EL1 SYSREG(3,0,0,5,4)
|
||||
#define ID_AA64AFR1_EL1 SYSREG(3,0,0,5,5)
|
||||
#define ID_AA64DFR0_EL1 SYSREG(3,0,0,5,0)
|
||||
#define ID_AA64DFR1_EL1 SYSREG(3,0,0,5,1)
|
||||
#define ID_AA64ISAR0_EL1 SYSREG(3,0,0,6,0)
|
||||
#define ID_AA64ISAR1_EL1 SYSREG(3,0,0,6,1)
|
||||
#define ID_AA64MMFR0_EL1 SYSREG(3,0,0,7,0)
|
||||
#define ID_AA64MMFR1_EL1 SYSREG(3,0,0,7,1)
|
||||
#define ID_AA64PFR0_EL1 SYSREG(3,0,0,4,0)
|
||||
#define ID_AA64PFR1_EL1 SYSREG(3,0,0,4,1)
|
||||
#define SCTLR_EL1 SYSREG(3,0,1,0,0)
|
||||
#define CPACR_EL1 SYSREG(3,0,1,0,2)
|
||||
#define MAIR_EL1 SYSREG(3,0,10,2,0)
|
||||
#define TCR_EL1 SYSREG(3,0,2,0,2)
|
||||
#define TTBR0_EL1 SYSREG(3,0,2,0,0)
|
||||
#define TTBR1_EL1 SYSREG(3,0,2,0,1)
|
||||
#define ESR_EL1 SYSREG(3,0,5,2,0)
|
||||
#define FAR_EL1 SYSREG(3,0,6,0,0)
|
||||
#define VBAR_EL1 SYSREG(3,0,12,0,0)
|
||||
#define VTTBR_EL2 SYSREG(3,4,2,1,0)
|
||||
#define SP_EL0 SYSREG(3,0,4,1,0)
|
||||
#define SP_EL1 SYSREG(3,4,4,1,0)
|
||||
#define SP_EL2 SYSREG(3,6,4,1,0)
|
||||
#define SCTLR_EL2 SYSREG(3,4,1,0,0)
|
||||
#define HCR_EL2 SYSREG(3,4,1,1,0)
|
||||
#define MDCR_EL2 SYSREG(3,4,1,1,1)
|
||||
#define PMCR_EL0 SYSREG(3,3,9,12,0)
|
||||
#define PMCNTENSET SYSREG(3,3,9,12,1)
|
||||
#define PMCCNTR_EL0 SYSREG(3,3,9,13,0)
|
||||
#define PMUSERENR_EL0 SYSREG(3,3,9,14,0)
|
||||
|
||||
#define CNTPCT_EL0 SYSREG(3,3,14,0,1)
|
||||
#define CNTVCT_EL0 SYSREG(3,3,14,0,2)
|
||||
#define CNTKCTL_EL1 SYSREG(3,0,14,1,0)
|
||||
#define CNTFRQ_EL0 SYSREG(3,3,14,0,0)
|
||||
#define CNTP_TVAL_EL0 SYSREG(3,3,14,2,0)
|
||||
#define CNTP_CTL_EL0 SYSREG(3,3,14,2,1)
|
||||
#define CNTP_CVAL_EL0 SYSREG(3,3,14,2,2)
|
||||
|
||||
#define TPIDR_EL0 SYSREG(3,3,13,0,2)
|
||||
#define TPIDR_EL1 SYSREG(3,0,13,0,4)
|
||||
|
||||
#define CCSIDR_EL1 SYSREG(3,1,0,0,0)
|
||||
#define CSSELR_EL1 SYSREG(3,2,0,0,0)
|
||||
|
||||
#define ACTLR_EL2 SYSREG(3,4,1,0,1)
|
||||
#define CPUACTLR_EL1 SYSREG(3,1,15,2,0)
|
||||
#define CPUECTLR_EL1 SYSREG(3,1,15,2,1)
|
||||
#define CBAR_EL1 SYSREG(3,1,15,3,0)
|
||||
|
||||
#define ICC_AP0R_EL1(m) SYSREG(3,0,12,8,4|(m))
|
||||
#define ICC_AP1R_EL1(m) SYSREG(3,0,12,9,0|(m))
|
||||
#define ICC_ASGI1R_EL1 SYSREG(3,0,12,11,6)
|
||||
#define ICC_BPR0_EL1 SYSREG(3,0,12,8,3)
|
||||
#define ICC_BPR1_EL1 SYSREG(3,0,12,12,3)
|
||||
#define ICC_CTLR_EL1 SYSREG(3,0,12,12,4)
|
||||
#define ICC_DIR_EL1 SYSREG(3,0,12,11,1)
|
||||
#define ICC_EOIR0_EL1 SYSREG(3,0,12,8,1)
|
||||
#define ICC_EOIR1_EL1 SYSREG(3,0,12,12,1)
|
||||
#define ICC_HPPIR0_EL1 SYSREG(3,0,12,8,2)
|
||||
#define ICC_HPPIR1_EL1 SYSREG(3,0,12,12,2)
|
||||
#define ICC_IAR0_EL1 SYSREG(3,0,12,8,0)
|
||||
#define ICC_IAR1_EL1 SYSREG(3,0,12,12,0)
|
||||
#define ICC_IGRPEN0_EL1 SYSREG(3,0,12,12,6)
|
||||
#define ICC_IGRPEN1_EL1 SYSREG(3,0,12,12,7)
|
||||
#define ICC_NMIAR1_EL1 SYSREG(3,0,12,9,5)
|
||||
#define ICC_PMR_EL1 SYSREG(3,0,4,6,0)
|
||||
#define ICC_RPR_EL1 SYSREG(3,0,12,11,3)
|
||||
#define ICC_SGI0R_EL1 SYSREG(3,0,12,11,7)
|
||||
#define ICC_SGI1R_EL1 SYSREG(3,0,12,11,5)
|
||||
#define ICC_SRE_EL1 SYSREG(3,0,12,12,5)
|
||||
|
||||
/* l.s redefines this for the assembler */
|
||||
#define SYSREG(op0,op1,Cn,Cm,op2) ((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)
|
||||
|
||||
#define OSHLD (0<<2 | 1)
|
||||
#define OSHST (0<<2 | 2)
|
||||
#define OSH (0<<2 | 3)
|
||||
#define NSHLD (1<<2 | 1)
|
||||
#define NSHST (1<<2 | 2)
|
||||
#define NSH (1<<2 | 3)
|
||||
#define ISHLD (2<<2 | 1)
|
||||
#define ISHST (2<<2 | 2)
|
||||
#define ISH (2<<2 | 3)
|
||||
#define LD (3<<2 | 1)
|
||||
#define ST (3<<2 | 2)
|
||||
#define SY (3<<2 | 3)
|
716
sys/src/9/imx8/trap.c
Normal file
716
sys/src/9/imx8/trap.c
Normal file
|
@ -0,0 +1,716 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
#include "../port/systab.h"
|
||||
|
||||
#include <tos.h>
|
||||
#include "ureg.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
int (*buserror)(Ureg*);
|
||||
|
||||
/* SPSR bits user can modify */
|
||||
#define USPSRMASK (0xFULL<<28)
|
||||
|
||||
static void
|
||||
setupvector(u32int *v, void (*t)(void), void (*f)(void))
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 0x80/4; i++){
|
||||
v[i] = ((u32int*)t)[i];
|
||||
if(v[i] == 0x14000000){
|
||||
v[i] |= ((u32int*)f - &v[i]) & 0x3ffffff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic("bug in vector code");
|
||||
}
|
||||
|
||||
void
|
||||
trapinit(void)
|
||||
{
|
||||
extern void vsys(void);
|
||||
extern void vtrap(void);
|
||||
extern void virq(void);
|
||||
extern void vfiq(void);
|
||||
extern void vserr(void);
|
||||
|
||||
extern void vsys0(void);
|
||||
extern void vtrap0(void);
|
||||
extern void vtrap1(void);
|
||||
|
||||
static u32int *v;
|
||||
|
||||
intrcpushutdown();
|
||||
if(v == nil){
|
||||
/* disable everything */
|
||||
intrsoff();
|
||||
|
||||
v = mallocalign(0x80*4*4, 1<<11, 0, 0);
|
||||
if(v == nil)
|
||||
panic("no memory for vector table");
|
||||
|
||||
setupvector(&v[0x000/4], vtrap, vtrap0);
|
||||
setupvector(&v[0x080/4], virq, vtrap0);
|
||||
setupvector(&v[0x100/4], vfiq, vtrap0);
|
||||
setupvector(&v[0x180/4], vserr, vtrap0);
|
||||
|
||||
setupvector(&v[0x200/4], vtrap, vtrap1);
|
||||
setupvector(&v[0x280/4], virq, vtrap1);
|
||||
setupvector(&v[0x300/4], vfiq, vtrap1);
|
||||
setupvector(&v[0x380/4], vserr, vtrap1);
|
||||
|
||||
setupvector(&v[0x400/4], vsys, vsys0);
|
||||
setupvector(&v[0x480/4], virq, vtrap0);
|
||||
setupvector(&v[0x500/4], vfiq, vtrap0);
|
||||
setupvector(&v[0x580/4], vserr, vtrap0);
|
||||
|
||||
setupvector(&v[0x600/4], vtrap, vtrap0);
|
||||
setupvector(&v[0x680/4], virq, vtrap0);
|
||||
setupvector(&v[0x700/4], vfiq, vtrap0);
|
||||
setupvector(&v[0x780/4], vserr, vtrap0);
|
||||
|
||||
cacheduwbse(v, 0x80*4*4);
|
||||
}
|
||||
cacheiinvse(v, 0x80*4*4);
|
||||
syswr(VBAR_EL1, (uintptr)v);
|
||||
splx(0x3<<6); // unmask serr and debug
|
||||
}
|
||||
|
||||
static char *traps[64] = {
|
||||
[0x00] "sys: trap: unknown",
|
||||
[0x01] "sys: trap: WFI or WFE instruction execution",
|
||||
[0x0E] "sys: trap: illegal execution state",
|
||||
[0x18] "sys: trap: illegal MSR/MRS access",
|
||||
[0x22] "sys: trap: misaligned pc",
|
||||
[0x26] "sys: trap: stack pointer misaligned",
|
||||
[0x30] "sys: trap: breakpoint",
|
||||
[0x32] "sys: trap: software step",
|
||||
[0x34] "sys: trap: watchpoint",
|
||||
[0x3C] "sys: trap: BRK instruction",
|
||||
};
|
||||
|
||||
void
|
||||
trap(Ureg *ureg)
|
||||
{
|
||||
u32int type, intr;
|
||||
int user;
|
||||
|
||||
intr = ureg->type >> 32;
|
||||
if(intr == 2){
|
||||
fiq(ureg);
|
||||
return;
|
||||
}
|
||||
splflo();
|
||||
user = kenter(ureg);
|
||||
type = (u32int)ureg->type >> 26;
|
||||
switch(type){
|
||||
case 0x20: // instruction abort from lower level
|
||||
case 0x21: // instruction abort from same level
|
||||
case 0x24: // data abort from lower level
|
||||
case 0x25: // data abort from same level
|
||||
faultarm64(ureg);
|
||||
break;
|
||||
case 0x07: // SIMD/FP
|
||||
case 0x2C: // FPU exception (A64 only)
|
||||
mathtrap(ureg);
|
||||
break;
|
||||
case 0x00: // unknown
|
||||
if(intr == 1){
|
||||
if(irq(ureg) && up != nil && up->delaysched)
|
||||
sched();
|
||||
break;
|
||||
}
|
||||
if(intr == 3){
|
||||
case 0x2F: // SError interrupt
|
||||
if(buserror != nil && (*buserror)(ureg))
|
||||
break;
|
||||
dumpregs(ureg);
|
||||
panic("SError interrupt");
|
||||
break;
|
||||
}
|
||||
/* wet floor */
|
||||
case 0x01: // WFI or WFE instruction execution
|
||||
case 0x03: // MCR or MRC access to CP15 (A32 only)
|
||||
case 0x04: // MCRR or MRC access to CP15 (A32 only)
|
||||
case 0x05: // MCR or MRC access to CP14 (A32 only)
|
||||
case 0x06: // LDC or STD access to CP14 (A32 only)
|
||||
case 0x08: // MCR or MRC to CP10 (A32 only)
|
||||
case 0x0C: // MRC access to CP14 (A32 only)
|
||||
case 0x0E: // Illegal Execution State
|
||||
case 0x11: // SVC instruction execution (A32 only)
|
||||
case 0x12: // HVC instruction execution (A32 only)
|
||||
case 0x13: // SMC instruction execution (A32 only)
|
||||
case 0x15: // SVC instruction execution (A64 only)
|
||||
case 0x16: // HVC instruction execution (A64 only)
|
||||
case 0x17: // SMC instruction execution (A64 only)
|
||||
case 0x18: // MSR/MRS (A64)
|
||||
case 0x22: // misaligned pc
|
||||
case 0x26: // stack pointer misaligned
|
||||
case 0x28: // FPU exception (A32 only)
|
||||
case 0x30: // breakpoint from lower level
|
||||
case 0x31: // breakpoint from same level
|
||||
case 0x32: // software step from lower level
|
||||
case 0x33: // software step from same level
|
||||
case 0x34: // watchpoint execution from lower level
|
||||
case 0x35: // watchpoint exception from same level
|
||||
case 0x38: // breapoint (A32 only)
|
||||
case 0x3A: // vector catch exception (A32 only)
|
||||
case 0x3C: // BRK instruction (A64 only)
|
||||
default:
|
||||
if(!userureg(ureg)){
|
||||
dumpregs(ureg);
|
||||
panic("unhandled trap");
|
||||
}
|
||||
if(traps[type] == nil) type = 0; // unknown
|
||||
postnote(up, 1, traps[type], NDebug);
|
||||
break;
|
||||
}
|
||||
splhi();
|
||||
if(user){
|
||||
if(up->procctl || up->nnote)
|
||||
notify(ureg);
|
||||
kexit(ureg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
syscall(Ureg *ureg)
|
||||
{
|
||||
vlong startns, stopns;
|
||||
uintptr sp, ret;
|
||||
ulong scallnr;
|
||||
int i, s;
|
||||
char *e;
|
||||
|
||||
if(!kenter(ureg))
|
||||
panic("syscall from kernel");
|
||||
|
||||
m->syscall++;
|
||||
up->insyscall = 1;
|
||||
up->pc = ureg->pc;
|
||||
|
||||
sp = ureg->sp;
|
||||
up->scallnr = scallnr = ureg->r0;
|
||||
|
||||
spllo();
|
||||
|
||||
up->nerrlab = 0;
|
||||
startns = 0;
|
||||
ret = -1;
|
||||
if(!waserror()){
|
||||
if(sp < USTKTOP - BY2PG || sp > USTKTOP - sizeof(Sargs) - BY2WD){
|
||||
validaddr(sp, sizeof(Sargs)+BY2WD, 0);
|
||||
evenaddr(sp);
|
||||
}
|
||||
up->s = *((Sargs*) (sp + BY2WD));
|
||||
|
||||
if(up->procctl == Proc_tracesyscall){
|
||||
syscallfmt(scallnr, ureg->pc, (va_list) up->s.args);
|
||||
s = splhi();
|
||||
up->procctl = Proc_stopme;
|
||||
procctl();
|
||||
splx(s);
|
||||
startns = todget(nil);
|
||||
}
|
||||
|
||||
if(scallnr >= nsyscall || systab[scallnr] == nil){
|
||||
pprint("bad sys call number %lud pc %#p", scallnr, ureg->pc);
|
||||
postnote(up, 1, "sys: bad sys call", NDebug);
|
||||
error(Ebadarg);
|
||||
}
|
||||
up->psstate = sysctab[scallnr];
|
||||
ret = systab[scallnr]((va_list)up->s.args);
|
||||
poperror();
|
||||
}else{
|
||||
e = up->syserrstr;
|
||||
up->syserrstr = up->errstr;
|
||||
up->errstr = e;
|
||||
}
|
||||
if(up->nerrlab){
|
||||
print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
|
||||
for(i = 0; i < NERR; i++)
|
||||
print("sp=%#p pc=%#p\n", up->errlab[i].sp, up->errlab[i].pc);
|
||||
panic("error stack");
|
||||
}
|
||||
ureg->r0 = ret;
|
||||
if(up->procctl == Proc_tracesyscall){
|
||||
stopns = todget(nil);
|
||||
sysretfmt(scallnr, (va_list) up->s.args, ret, startns, stopns);
|
||||
s = splhi();
|
||||
up->procctl = Proc_stopme;
|
||||
procctl();
|
||||
splx(s);
|
||||
}
|
||||
|
||||
up->insyscall = 0;
|
||||
up->psstate = 0;
|
||||
if(scallnr == NOTED){
|
||||
noted(ureg, *((ulong*) up->s.args));
|
||||
/*
|
||||
* normally, syscall() returns to forkret()
|
||||
* not restoring general registers when going
|
||||
* to userspace. to completely restore the
|
||||
* interrupted context, we have to return thru
|
||||
* noteret(). we override return pc to jump to
|
||||
* to it when returning form syscall()
|
||||
*/
|
||||
returnto(noteret);
|
||||
}
|
||||
|
||||
if(scallnr != RFORK && (up->procctl || up->nnote)){
|
||||
splhi();
|
||||
notify(ureg);
|
||||
}
|
||||
if(up->delaysched)
|
||||
sched();
|
||||
kexit(ureg);
|
||||
}
|
||||
|
||||
int
|
||||
notify(Ureg *ureg)
|
||||
{
|
||||
int l;
|
||||
uintptr s, sp;
|
||||
Note *n;
|
||||
|
||||
if(up->procctl)
|
||||
procctl();
|
||||
if(up->nnote == 0)
|
||||
return 0;
|
||||
if(up->fpstate == FPactive){
|
||||
fpsave(up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
}
|
||||
up->fpstate |= FPillegal;
|
||||
|
||||
s = spllo();
|
||||
qlock(&up->debug);
|
||||
up->notepending = 0;
|
||||
n = &up->note[0];
|
||||
if(strncmp(n->msg, "sys:", 4) == 0){
|
||||
l = strlen(n->msg);
|
||||
if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */
|
||||
l = ERRMAX-23;
|
||||
sprint(n->msg+l, " pc=%#p", ureg->pc);
|
||||
}
|
||||
|
||||
if(n->flag!=NUser && (up->notified || up->notify==0)){
|
||||
qunlock(&up->debug);
|
||||
if(n->flag == NDebug)
|
||||
pprint("suicide: %s\n", n->msg);
|
||||
pexit(n->msg, n->flag!=NDebug);
|
||||
}
|
||||
|
||||
if(up->notified){
|
||||
qunlock(&up->debug);
|
||||
splhi();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!up->notify){
|
||||
qunlock(&up->debug);
|
||||
pexit(n->msg, n->flag!=NDebug);
|
||||
}
|
||||
sp = ureg->sp;
|
||||
sp -= 256; /* debugging: preserve context causing problem */
|
||||
sp -= sizeof(Ureg);
|
||||
sp = STACKALIGN(sp);
|
||||
|
||||
if(!okaddr((uintptr)up->notify, 1, 0)
|
||||
|| !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
|
||||
|| ((uintptr) up->notify & 3) != 0
|
||||
|| (sp & 7) != 0){
|
||||
qunlock(&up->debug);
|
||||
pprint("suicide: bad address in notify: handler=%#p sp=%#p\n",
|
||||
up->notify, sp);
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
|
||||
memmove((Ureg*)sp, ureg, sizeof(Ureg));
|
||||
*(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
|
||||
up->ureg = (void*)sp;
|
||||
sp -= BY2WD+ERRMAX;
|
||||
memmove((char*)sp, up->note[0].msg, ERRMAX);
|
||||
sp -= 3*BY2WD;
|
||||
*(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD;
|
||||
*(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg;
|
||||
ureg->r0 = (uintptr) up->ureg;
|
||||
ureg->sp = sp;
|
||||
ureg->pc = (uintptr) up->notify;
|
||||
ureg->link = 0;
|
||||
up->notified = 1;
|
||||
up->nnote--;
|
||||
memmove(&up->lastnote, &up->note[0], sizeof(Note));
|
||||
memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
|
||||
|
||||
qunlock(&up->debug);
|
||||
splx(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
noted(Ureg *ureg, ulong arg0)
|
||||
{
|
||||
Ureg *nureg;
|
||||
uintptr oureg, sp;
|
||||
|
||||
qlock(&up->debug);
|
||||
if(arg0 != NRSTR && !up->notified){
|
||||
qunlock(&up->debug);
|
||||
pprint("call to noted() when not notified\n");
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
up->notified = 0;
|
||||
|
||||
nureg = up->ureg;
|
||||
up->fpstate &= ~FPillegal;
|
||||
|
||||
oureg = (uintptr) nureg;
|
||||
if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 7) != 0){
|
||||
qunlock(&up->debug);
|
||||
pprint("bad ureg in noted or call to noted when not notified\n");
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
|
||||
nureg->psr = (nureg->psr & USPSRMASK) | (ureg->psr & ~USPSRMASK);
|
||||
memmove(ureg, nureg, sizeof(Ureg));
|
||||
|
||||
switch(arg0){
|
||||
case NCONT: case NRSTR:
|
||||
if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
|
||||
(nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){
|
||||
qunlock(&up->debug);
|
||||
pprint("suicide: trap in noted\n");
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
up->ureg = (Ureg *) (*(uintptr*) (oureg - BY2WD));
|
||||
qunlock(&up->debug);
|
||||
break;
|
||||
|
||||
case NSAVE:
|
||||
if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
|
||||
(nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){
|
||||
qunlock(&up->debug);
|
||||
pprint("suicide: trap in noted\n");
|
||||
pexit("Suicide", 0);
|
||||
}
|
||||
qunlock(&up->debug);
|
||||
sp = oureg - 4 * BY2WD - ERRMAX;
|
||||
splhi();
|
||||
ureg->sp = sp;
|
||||
ureg->r0 = (uintptr) oureg;
|
||||
((uintptr *) sp)[1] = oureg;
|
||||
((uintptr *) sp)[0] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
up->lastnote.flag = NDebug;
|
||||
|
||||
case NDFLT:
|
||||
qunlock(&up->debug);
|
||||
if(up->lastnote.flag == NDebug)
|
||||
pprint("suicide: %s\n", up->lastnote.msg);
|
||||
pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
faultarm64(Ureg *ureg)
|
||||
{
|
||||
extern void checkpages(void);
|
||||
char buf[ERRMAX];
|
||||
int read, insyscall;
|
||||
uintptr addr;
|
||||
|
||||
insyscall = up->insyscall;
|
||||
up->insyscall = 1;
|
||||
|
||||
if(!userureg(ureg)){
|
||||
extern void _peekinst(void);
|
||||
|
||||
if(ureg->pc == (uintptr)_peekinst){
|
||||
ureg->pc = ureg->link;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(waserror()){
|
||||
if(up->nerrlab == 0){
|
||||
pprint("suicide: sys: %s\n", up->errstr);
|
||||
pexit(up->errstr, 1);
|
||||
}
|
||||
up->insyscall = insyscall;
|
||||
nexterror();
|
||||
}
|
||||
}
|
||||
|
||||
addr = getfar();
|
||||
read = (ureg->type & (1<<6)) == 0;
|
||||
|
||||
switch((u32int)ureg->type & 0x3F){
|
||||
case 4: case 5: case 6: case 7: // Tanslation fault.
|
||||
case 8: case 9: case 10: case 11: // Access flag fault.
|
||||
case 12: case 13: case 14: case 15: // Permission fault.
|
||||
case 48: // tlb conflict fault.
|
||||
if(fault(addr, ureg->pc, read) == 0)
|
||||
break;
|
||||
|
||||
/* wet floor */
|
||||
case 0: case 1: case 2: case 3: // Address size fault.
|
||||
case 16: // synchronous external abort.
|
||||
case 24: // synchronous parity error on a memory access.
|
||||
case 20: case 21: case 22: case 23: // synchronous external abort on a table walk.
|
||||
case 28: case 29: case 30: case 31: // synchronous parity error on table walk.
|
||||
case 33: // alignment fault.
|
||||
case 52: // implementation defined, lockdown abort.
|
||||
case 53: // implementation defined, unsuppoted exclusive.
|
||||
case 61: // first level domain fault
|
||||
case 62: // second level domain fault
|
||||
default:
|
||||
if(!userureg(ureg)){
|
||||
dumpregs(ureg);
|
||||
panic("fault: %s addr=%#p", read ? "read" : "write", addr);
|
||||
}
|
||||
checkpages();
|
||||
sprint(buf, "sys: trap: fault %s addr=%#p", read ? "read" : "write", addr);
|
||||
postnote(up, 1, buf, NDebug);
|
||||
}
|
||||
|
||||
if(!userureg(ureg))
|
||||
poperror();
|
||||
|
||||
out:
|
||||
up->insyscall = insyscall;
|
||||
}
|
||||
|
||||
int
|
||||
userureg(Ureg* ureg)
|
||||
{
|
||||
return (ureg->psr & 15) == 0;
|
||||
}
|
||||
|
||||
uintptr
|
||||
userpc(void)
|
||||
{
|
||||
Ureg *ur = up->dbgreg;
|
||||
return ur->pc;
|
||||
}
|
||||
|
||||
uintptr
|
||||
dbgpc(Proc *)
|
||||
{
|
||||
Ureg *ur = up->dbgreg;
|
||||
if(ur == nil)
|
||||
return 0;
|
||||
return ur->pc;
|
||||
}
|
||||
|
||||
void
|
||||
procfork(Proc *p)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = splhi();
|
||||
switch(up->fpstate & ~FPillegal){
|
||||
case FPactive:
|
||||
fpsave(up->fpsave);
|
||||
up->fpstate = FPinactive;
|
||||
case FPinactive:
|
||||
memmove(p->fpsave, up->fpsave, sizeof(FPsave));
|
||||
p->fpstate = FPinactive;
|
||||
}
|
||||
splx(s);
|
||||
|
||||
p->tpidr = up->tpidr;
|
||||
}
|
||||
|
||||
void
|
||||
procsetup(Proc *p)
|
||||
{
|
||||
p->fpstate = FPinit;
|
||||
fpoff();
|
||||
|
||||
p->tpidr = 0;
|
||||
syswr(TPIDR_EL0, p->tpidr);
|
||||
}
|
||||
|
||||
void
|
||||
procsave(Proc *p)
|
||||
{
|
||||
if(p->fpstate == FPactive){
|
||||
if(p->state == Moribund)
|
||||
fpclear();
|
||||
else
|
||||
fpsave(p->fpsave);
|
||||
p->fpstate = FPinactive;
|
||||
}
|
||||
|
||||
if(p->kp == 0)
|
||||
p->tpidr = sysrd(TPIDR_EL0);
|
||||
|
||||
putasid(p); // release asid
|
||||
}
|
||||
|
||||
void
|
||||
procrestore(Proc *p)
|
||||
{
|
||||
if(p->kp == 0)
|
||||
syswr(TPIDR_EL0, p->tpidr);
|
||||
}
|
||||
|
||||
void
|
||||
kprocchild(Proc *p, void (*entry)(void))
|
||||
{
|
||||
p->sched.pc = (uintptr) entry;
|
||||
p->sched.sp = (uintptr) p->kstack + KSTACK - 16;
|
||||
*(void**)p->sched.sp = kprocchild; /* fake */
|
||||
}
|
||||
|
||||
void
|
||||
forkchild(Proc *p, Ureg *ureg)
|
||||
{
|
||||
Ureg *cureg;
|
||||
|
||||
p->sched.pc = (uintptr) forkret;
|
||||
p->sched.sp = (uintptr) p->kstack + KSTACK - TRAPFRAMESIZE;
|
||||
|
||||
cureg = (Ureg*) (p->sched.sp + 16);
|
||||
memmove(cureg, ureg, sizeof(Ureg));
|
||||
cureg->r0 = 0;
|
||||
}
|
||||
|
||||
uintptr
|
||||
execregs(uintptr entry, ulong ssize, ulong nargs)
|
||||
{
|
||||
uintptr *sp;
|
||||
Ureg *ureg;
|
||||
|
||||
sp = (uintptr*)(USTKTOP - ssize);
|
||||
*--sp = nargs;
|
||||
|
||||
ureg = up->dbgreg;
|
||||
ureg->sp = (uintptr)sp;
|
||||
ureg->pc = entry;
|
||||
ureg->link = 0;
|
||||
return USTKTOP-sizeof(Tos);
|
||||
}
|
||||
|
||||
void
|
||||
evenaddr(uintptr addr)
|
||||
{
|
||||
if(addr & 3){
|
||||
postnote(up, 1, "sys: odd address", NDebug);
|
||||
error(Ebadarg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
callwithureg(void (*f) (Ureg *))
|
||||
{
|
||||
Ureg u;
|
||||
|
||||
u.pc = getcallerpc(&f);
|
||||
u.sp = (uintptr) &f;
|
||||
f(&u);
|
||||
}
|
||||
|
||||
void
|
||||
setkernur(Ureg *ureg, Proc *p)
|
||||
{
|
||||
ureg->pc = p->sched.pc;
|
||||
ureg->sp = p->sched.sp;
|
||||
ureg->link = (uintptr)sched;
|
||||
}
|
||||
|
||||
void
|
||||
setupwatchpts(Proc*, Watchpt*, int)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
setregisters(Ureg* ureg, char* pureg, char* uva, int n)
|
||||
{
|
||||
ulong v;
|
||||
|
||||
v = ureg->psr;
|
||||
memmove(pureg, uva, n);
|
||||
ureg->psr = (ureg->psr & USPSRMASK) | (v & ~USPSRMASK);
|
||||
}
|
||||
|
||||
static void
|
||||
dumpstackwithureg(Ureg *ureg)
|
||||
{
|
||||
uintptr v, estack, sp;
|
||||
char *s;
|
||||
int i;
|
||||
|
||||
if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
|
||||
iprint("dumpstack disabled\n");
|
||||
return;
|
||||
}
|
||||
iprint("ktrace /kernel/path %#p %#p %#p # pc, sp, link\n",
|
||||
ureg->pc, ureg->sp, ureg->link);
|
||||
delay(2000);
|
||||
|
||||
sp = ureg->sp;
|
||||
if(sp < KZERO || (sp & 7) != 0)
|
||||
sp = (uintptr)&ureg;
|
||||
|
||||
estack = (uintptr)m+MACHSIZE;
|
||||
if(up != nil && sp <= (uintptr)up->kstack+KSTACK)
|
||||
estack = (uintptr)up->kstack+KSTACK;
|
||||
|
||||
if(sp > estack){
|
||||
if(up != nil)
|
||||
iprint("&up->kstack %#p sp %#p\n", up->kstack, sp);
|
||||
else
|
||||
iprint("&m %#p sp %#p\n", m, sp);
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for(; sp < estack; sp += sizeof(uintptr)){
|
||||
v = *(uintptr*)sp;
|
||||
if(KTZERO < v && v < (uintptr)etext && (v & 3) == 0){
|
||||
iprint("%#8.8lux=%#8.8lux ", (ulong)sp, (ulong)v);
|
||||
i++;
|
||||
}
|
||||
if(i == 4){
|
||||
i = 0;
|
||||
iprint("\n");
|
||||
}
|
||||
}
|
||||
if(i)
|
||||
iprint("\n");
|
||||
}
|
||||
|
||||
void
|
||||
dumpstack(void)
|
||||
{
|
||||
callwithureg(dumpstackwithureg);
|
||||
}
|
||||
|
||||
void
|
||||
dumpregs(Ureg *ureg)
|
||||
{
|
||||
u64int *r;
|
||||
int i, x;
|
||||
|
||||
x = splhi();
|
||||
if(up != nil)
|
||||
iprint("cpu%d: dumpregs ureg %#p process %lud: %s\n", m->machno, ureg,
|
||||
up->pid, up->text);
|
||||
else
|
||||
iprint("cpu%d: dumpregs ureg %#p\n", m->machno, ureg);
|
||||
r = &ureg->r0;
|
||||
for(i = 0; i < 30; i += 3)
|
||||
iprint("R%d %.16llux R%d %.16llux R%d %.16llux\n", i, r[i], i+1, r[i+1], i+2, r[i+2]);
|
||||
iprint("PC %#p SP %#p LR %#p PSR %llux TYPE %llux\n",
|
||||
ureg->pc, ureg->sp, ureg->link,
|
||||
ureg->psr, ureg->type);
|
||||
splx(x);
|
||||
}
|
383
sys/src/9/imx8/uartimx.c
Normal file
383
sys/src/9/imx8/uartimx.c
Normal file
|
@ -0,0 +1,383 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "../port/error.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "io.h"
|
||||
|
||||
enum {
|
||||
URXD = 0x00/4, /* UART Receiver Register */
|
||||
RX_CHARRDY = 1<<15,
|
||||
RX_ERR = 1<<14,
|
||||
RX_OVRRUN = 1<<13,
|
||||
RX_FRMERR = 1<<12,
|
||||
RX_BRK = 1<<11,
|
||||
RX_PRERR = 1<<10,
|
||||
RX_DATA = 0xFF,
|
||||
|
||||
UTXD = 0x40/4, /* UART Transmitter Register */
|
||||
TX_DATA = 0xFF,
|
||||
|
||||
UCR1 = 0x80/4, /* UART Control Register 1 */
|
||||
CR1_ADEN = 1<<15, /* Automatic Baud Rate Detection Interrupt Enable */
|
||||
CR1_ADNR = 1<<14, /* Automatic Detection of Baud Rate */
|
||||
CR1_TRDYEN = 1<<13, /* Transmitter Ready Interrupt Enable */
|
||||
CR1_IDEN = 1<<12, /* Idle Condition Detected Interrupt Enable */
|
||||
|
||||
CR1_ICD_SHIFT = 10, /* Idle Condition Detect Mask */
|
||||
CR1_ICD_MASK = 3<<CR1_ICD_SHIFT,
|
||||
|
||||
CR1_RRDYEN = 1<<9, /* Receiver Ready Interrupt Enable */
|
||||
CR1_RXDMAEN = 1<<8, /* Receive Ready DMA Enable */
|
||||
CR1_IREN = 1<<7, /* Infrared Interface Enable */
|
||||
CR1_TXMPTYEN = 1<<6, /* Transmitter Empty Interrupt Enable */
|
||||
CR1_RTSDEN = 1<<5, /* RTS Delta Interrupt Enable */
|
||||
CR1_SNDBRK = 1<<4, /* Send BREAK */
|
||||
CR1_TXDMAEN = 1<<3, /* Transmitter Ready DMA Enable */
|
||||
CR1_ATDMAEN = 1<<2, /* Aging DMA Timer Enable */
|
||||
CR1_DOZE = 1<<1, /* DOZE */
|
||||
CR1_UARTEN = 1<<0, /* Uart Enable */
|
||||
|
||||
UCR2 = 0x84/4, /* UART Control Register 2 */
|
||||
CR2_ESCI = 1<<15, /* Escape Sequence Interrupt Enable */
|
||||
CR2_IRTS = 1<<14, /* Ignore RTS Pin */
|
||||
CR2_CTSC = 1<<13, /* CTS Pin Control */
|
||||
CR2_CTS = 1<<12, /* Clear to Send */
|
||||
CR2_ESCEN = 1<<11, /* Escape Enable */
|
||||
|
||||
CR2_RTEC_RAISING= 0<<9,
|
||||
CR2_RTEC_FALLING= 1<<9,
|
||||
CR2_RTEC_ANY = 2<<9,
|
||||
CR2_RTEC_MASK = 3<<9,
|
||||
|
||||
CR2_PREN = 1<<8, /* Parity Enable */
|
||||
CR2_PREVEN = 0<<7, /* Parity Even */
|
||||
CR2_PRODD = 1<<7, /* Parity Odd */
|
||||
CR2_STPB = 1<<6, /* Stop */
|
||||
CR2_WS8 = 1<<5, /* Word Size */
|
||||
CR2_WS7 = 0<<5,
|
||||
CR2_RTSEN = 1<<4, /* Request to Send Interrupt Enable */
|
||||
CR2_ATEN = 1<<3, /* Aging Timer Enable */
|
||||
CR2_TXEN = 1<<2, /* Transmitter Enable */
|
||||
CR2_RXEN = 1<<1, /* Receiver Enable */
|
||||
CR2_SRST = 1<<0, /* Software Reset */
|
||||
|
||||
UCR3 = 0x88/4, /* UART Control Register 3 */
|
||||
CR3_PARERREN = 1<<12, /* Parity Error Interrupt Enable */
|
||||
CR3_FRAERREN = 1<<11, /* Frame Error Interrupt Enable */
|
||||
CR3_ADNIMP = 1<<7, /* Autobaud Detection Not Improved */
|
||||
CR3_RXDSEN = 1<<6, /* Receive Status Interrupt Enable */
|
||||
CR3_AIRINTEN = 1<<5, /* Asynchronous IR WAKE Interrupt Enable */
|
||||
CR3_AWAKEN = 1<<4, /* Asynchronous WAKE Interrupt Enable */
|
||||
CR3_RXDMUXSEL = 1<<2, /* RXD Muxed Input Selected */
|
||||
CR3_INVT = 1<<1, /* Invert TXD output in RS-232/RS-485 mode */
|
||||
CR3_ACIEN = 1<<0, /* Autobaud Counter Interrupt Enable */
|
||||
|
||||
UCR4 = 0x8C/4, /* UART Control Register 4 */
|
||||
CR4_CTSTL_SHIFT = 10, /* CTS Trigger Level */
|
||||
CR4_CTSTL_MASK = 0x3F<<CR4_CTSTL_SHIFT,
|
||||
|
||||
CR4_INVR = 1<<9, /* Invert RXD Input in RS-232/RS-485 Mode */
|
||||
CR4_ENIRI = 1<<8, /* Serial Infrared Interrupt Enable */
|
||||
CR4_WKEN = 1<<7, /* WAKE Interrupt Enable */
|
||||
CR4_IDDMAEN = 1<<6, /* DMA IDLE Condition Detected Interrupt Enable */
|
||||
CR4_IRSC = 1<<5, /* IR Special Case */
|
||||
CR4_LPBYP = 1<<4, /* Low Power Bypass */
|
||||
CR4_TCEN = 1<<3, /* Transmit Complete Interrupt Enable */
|
||||
CR4_BKEN = 1<<2, /* BREAK Condition Detected Interrupt Enable */
|
||||
CR4_OREN = 1<<1, /* Receiver Overrun Interrupt Enable */
|
||||
CR4_DREN = 1<<0, /* Receive Data Interrupt Enable */
|
||||
|
||||
UFCR = 0x90/4, /* UART FIFO Control Register */
|
||||
FCR_TXTL_SHIFT = 10, /* Transmitter Trigger Level */
|
||||
FCR_TXTL_MASK = 0x3F<<FCR_TXTL_SHIFT,
|
||||
|
||||
FCR_RFDIV_SHIFT = 7, /* Reference Frequency Divider */
|
||||
FCR_RFDIV_MASK = 0x7<<FCR_RFDIV_SHIFT,
|
||||
|
||||
FCR_DCE = 0<<6, /* DCE/DTE mode select */
|
||||
FCR_DTE = 1<<6,
|
||||
|
||||
FCR_RXTL_SHIFT = 0, /* Receive Trigger Level */
|
||||
FCR_RXTL_MASK = 0x3F<<FCR_RXTL_SHIFT,
|
||||
|
||||
USR1 = 0x94/4, /* UART Status Register 1 */
|
||||
SR1_PARITYERR = 1<<15, /* Parity Error Interrupt Flag */
|
||||
SR1_RTSS = 1<<14, /* RTS_B Pin Status */
|
||||
SR1_TRDY = 1<<13, /* Transmitter Ready Interrupt / DMA Flag */
|
||||
SR1_RTSD = 1<<12, /* RTS Delta */
|
||||
SR1_ESCF = 1<<11, /* Escape Sequence Interrupt Flag */
|
||||
SR1_FRAMEERR = 1<<10, /* Frame Error Interrupt Flag */
|
||||
SR1_RRDY = 1<<9, /* Receiver Ready Interrupt / DMA Flag */
|
||||
SR1_AGTIM = 1<<8, /* Aging Timer Interrupt Flag */
|
||||
SR1_DTRD = 1<<7,
|
||||
SR1_RXDS = 1<<6, /* Receiver IDLE Interrupt Flag */
|
||||
SR1_AIRINT = 1<<5, /* Asynchronous IR WAKE Interrupt Flag */
|
||||
SR1_AWAKE = 1<<4, /* Asynchronous WAKE Interrupt Flag */
|
||||
SR1_SAD = 1<<3, /* RS-485 Slave Address Detected Interrupt Flag */
|
||||
|
||||
USR2 = 0x98/4, /* UART Status Register 2 */
|
||||
SR2_ADET = 1<<15, /* Automatic Baud Rate Detected Complete */
|
||||
SR2_TXFE = 1<<14, /* Transmit Buffer FIFO Empty */
|
||||
SR2_DTRF = 1<<13,
|
||||
SR2_IDLE = 1<<12, /* Idle Condition */
|
||||
SR2_ACST = 1<<11, /* Autobaud Counter Stopped */
|
||||
SR2_RIDELT = 1<<10,
|
||||
SR2_RIIN = 1<<9,
|
||||
SR2_IRINT = 1<<8, /* Serial Infrared Interrupt Flag */
|
||||
SR2_WAKE = 1<<7, /* Wake */
|
||||
SR2_DCDDELT = 1<<6,
|
||||
SR2_DCDIN = 1<<5,
|
||||
SR2_RTSF = 1<<4, /* RTS Edge Triggered Interrupt Flag */
|
||||
SR2_TXDC = 1<<3, /* Transmitter Complete */
|
||||
SR2_BRCD = 1<<2, /* BREAK Condition Detected */
|
||||
SR2_ORE = 1<<1, /* Overrun Error */
|
||||
SR2_RDR = 1<<0, /* Receive Data Ready */
|
||||
|
||||
UESC = 0x9C/4, /* UART Escape Character Register */
|
||||
UTIM = 0xA0/4, /* UART Escape Timer Register */
|
||||
UBIR = 0xA4/4, /* UART BRM Incremental Modulator Register */
|
||||
UBMR = 0xA8/4, /* UART BRM Modulator Register */
|
||||
UBRC = 0xAC/4, /* UART Baud Rate Count Register */
|
||||
ONEMS = 0xB0/4, /* UART One-Millisecond Register */
|
||||
UTS = 0xB5/4, /* UART Test Register */
|
||||
UMCR = 0xB8/4, /* UART RS-485 Mode Control Register */
|
||||
};
|
||||
|
||||
extern PhysUart imxphysuart;
|
||||
|
||||
static Uart uart1 = {
|
||||
.regs = (u32int*)(VIRTIO + 0x860000ULL),
|
||||
.name = "uart1",
|
||||
.baud = 115200,
|
||||
.freq = 25*Mhz,
|
||||
.phys = &imxphysuart,
|
||||
};
|
||||
|
||||
static Uart*
|
||||
pnp(void)
|
||||
{
|
||||
return &uart1;
|
||||
}
|
||||
|
||||
static void
|
||||
kick(Uart *u)
|
||||
{
|
||||
u32int *regs = (u32int*)u->regs;
|
||||
|
||||
if(u->blocked)
|
||||
return;
|
||||
while(regs[USR1] & SR1_TRDY){
|
||||
if(u->op >= u->oe && uartstageoutput(u) == 0)
|
||||
break;
|
||||
regs[UTXD] = *(u->op++) & TX_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config(Uart *u)
|
||||
{
|
||||
u32int cr2, *regs = u->regs;
|
||||
|
||||
/* enable uart */
|
||||
regs[UCR1] = CR1_UARTEN;
|
||||
|
||||
cr2 = CR2_SRST | CR2_IRTS | CR2_RXEN | CR2_TXEN;
|
||||
switch(u->parity){
|
||||
case 'e': cr2 |= CR2_PREN | CR2_PREVEN; break;
|
||||
case 'o': cr2 |= CR2_PREN | CR2_PRODD; break;
|
||||
}
|
||||
cr2 |= u->bits == 7 ? CR2_WS7 : CR2_WS8;
|
||||
if(u->stop == 2) cr2 |= CR2_STPB;
|
||||
regs[UCR2] = cr2;
|
||||
regs[UCR3] = 0x7<<8 | CR3_RXDMUXSEL;
|
||||
regs[UCR4] = 31<<CR4_CTSTL_SHIFT;
|
||||
|
||||
/* baud = clock / (16 * (ubmr+1)/(ubir+1)) */
|
||||
regs[UFCR] = (6 - 1)<<FCR_RFDIV_SHIFT | 32<<FCR_TXTL_SHIFT | 32<<FCR_RXTL_SHIFT;
|
||||
regs[UBIR] = ((16*u->baud) / 1600)-1;
|
||||
regs[UBMR] = (u->freq / 1600)-1;
|
||||
|
||||
regs[UCR1] = CR1_UARTEN | CR1_TRDYEN | CR1_RRDYEN;
|
||||
}
|
||||
|
||||
static int
|
||||
bits(Uart *u, int n)
|
||||
{
|
||||
switch(n){
|
||||
case 8:
|
||||
break;
|
||||
case 7:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
u->bits = n;
|
||||
config(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
stop(Uart *u, int n)
|
||||
{
|
||||
switch(n){
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
u->stop = n;
|
||||
config(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parity(Uart *u, int n)
|
||||
{
|
||||
switch(n){
|
||||
case 'n':
|
||||
break;
|
||||
case 'e':
|
||||
break;
|
||||
case 'o':
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
u->parity = n;
|
||||
config(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
baud(Uart *u, int n)
|
||||
{
|
||||
if(u->freq == 0 || n <= 0)
|
||||
return -1;
|
||||
u->baud = n;
|
||||
config(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rts(Uart*, int)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
dobreak(Uart*, int)
|
||||
{
|
||||
}
|
||||
|
||||
static long
|
||||
status(Uart *uart, void *buf, long n, long offset)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = malloc(READSTR);
|
||||
if(p == nil)
|
||||
error(Enomem);
|
||||
snprint(p, READSTR,
|
||||
"b%d\n"
|
||||
"dev(%d) type(%d) framing(%d) overruns(%d) "
|
||||
"berr(%d) serr(%d)\n",
|
||||
|
||||
uart->baud,
|
||||
uart->dev,
|
||||
uart->type,
|
||||
uart->ferr,
|
||||
uart->oerr,
|
||||
uart->berr,
|
||||
uart->serr
|
||||
);
|
||||
n = readstr(offset, buf, n, p);
|
||||
free(p);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
interrupt(Ureg*, void *arg)
|
||||
{
|
||||
Uart *uart = arg;
|
||||
u32int v, *regs = (u32int*)uart->regs;
|
||||
|
||||
while((v = regs[URXD]) & RX_CHARRDY)
|
||||
uartrecv(uart, v & RX_DATA);
|
||||
|
||||
uartkick(uart);
|
||||
}
|
||||
|
||||
static void
|
||||
disable(Uart *u)
|
||||
{
|
||||
u32int *regs = u->regs;
|
||||
regs[UCR1] = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
enable(Uart *u, int ie)
|
||||
{
|
||||
disable(u);
|
||||
if(ie) intrenable(IRQuart1, interrupt, u, BUSUNKNOWN, u->name);
|
||||
config(u);
|
||||
}
|
||||
|
||||
static void
|
||||
donothing(Uart*, int)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
putc(Uart *u, int c)
|
||||
{
|
||||
u32int *regs = u->regs;
|
||||
|
||||
while((regs[USR1] & SR1_TRDY) == 0)
|
||||
;
|
||||
regs[UTXD] = c & TX_DATA;
|
||||
}
|
||||
|
||||
static int
|
||||
getc(Uart *u)
|
||||
{
|
||||
u32int c, *regs = (u32int*)u->regs;
|
||||
|
||||
do
|
||||
c = regs[URXD];
|
||||
while((c & RX_CHARRDY) == 0);
|
||||
return c & RX_DATA;
|
||||
}
|
||||
|
||||
void
|
||||
uartconsinit(void)
|
||||
{
|
||||
consuart = &uart1;
|
||||
consuart->console = 1;
|
||||
uartctl(consuart, "l8 pn s1");
|
||||
uartputs(kmesg.buf, kmesg.n);
|
||||
}
|
||||
|
||||
PhysUart imxphysuart = {
|
||||
.name = "imx",
|
||||
.pnp = pnp,
|
||||
.enable = enable,
|
||||
.disable = disable,
|
||||
.kick = kick,
|
||||
.dobreak = dobreak,
|
||||
.baud = baud,
|
||||
.bits = bits,
|
||||
.stop = stop,
|
||||
.parity = parity,
|
||||
.modemctl = donothing,
|
||||
.rts = rts,
|
||||
.dtr = donothing,
|
||||
.status = status,
|
||||
.fifo = donothing,
|
||||
.getc = getc,
|
||||
.putc = putc,
|
||||
};
|
||||
|
Loading…
Reference in a new issue