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
08a2cd30ba
commit
22934e123c
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