├── README.md ├── archA64.c ├── axp803.c ├── axp803.h ├── cache.v8.s ├── ccu.c ├── ccu.h ├── clock.c ├── dat.h ├── devarch.c ├── devrtc.c ├── ethersunxi.c ├── fns.h ├── fpu.c ├── gic.c ├── i2csunxi.c ├── init9.s ├── io.h ├── keyadc.c ├── l.s ├── main.c ├── mem.h ├── mkfile ├── mmu.c ├── notes ├── pine64 ├── pinephone ├── rebootcode.s ├── rsb.c ├── sysreg.c ├── sysreg.h ├── thermal.c ├── trap.c └── uarti8250.c /README.md: -------------------------------------------------------------------------------- 1 | # 9front-A64 2 | experimental 9 Front kernel for the Allwinner A64 3 | 4 | currently runs all 4 cores and the UART. 5 | reported to run on the PinePhone 6 | 7 | 8 | -------------------------------------------------------------------------------- /archA64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Allwinner A64 specific stuff looking for a home 3 | */ 4 | 5 | #include "u.h" 6 | #include "../port/lib.h" 7 | #include "mem.h" 8 | #include "dat.h" 9 | #include "fns.h" 10 | #include "../port/error.h" 11 | #include "io.h" 12 | 13 | 14 | /* R_PIO regs */ 15 | #define RPIO_CFG00 0x00 /* Port L Configure Register 0 */ 16 | #define S_RSB_SCK 0x2 17 | #define S_RSB_SDA 0x20 18 | #define RPIO_CFG10 0x04 /* Port L Configure Register 1 */ 19 | #define RPIO_CFG20 0x08 /* Port L Configure Register 2 */ 20 | #define RPIO_CFG30 0x0C /* Port L Configure Register 3 */ 21 | #define RPIO_DAT0 0x10 /* Port L Data Register */ 22 | #define RPIO_DRV00 0x14 /* Port L Multi-Driving Register 0 */ 23 | 24 | #define RPIO_DRV10 0x18 /* Port L Multi-Driving Register 1 */ 25 | #define RPIO_PUL00 0x1C /* Port L Pull Register 0 */ 26 | #define RPIO_PUL10 0x20 /* Port L Pull Register 1 */ 27 | 28 | 29 | /* 30 | * Information for the PRMC regs is 31 | * absent on the A64 datasheet. 32 | * This is taken from the A80 datasheet 33 | */ 34 | 35 | /* R_PRMC regs */ 36 | #define CPUS_RST_REG 0x0000 /* CPUS Reset Register */ 37 | #define C0_CPUX_RST_CTRL 0x0004 /* Cluster0 CPUX Reset Control Register */ 38 | #define CPUS_CLK_CFG_REG 0x0010 /* CPUS Clock Configuration Register */ 39 | #define APBS_CLK_DIV_REG 0x001C /* APBS Clock Divide Register */ 40 | #define APBS_CLK_GATING_REG 0x0028 /* APBS Clock Gating Register */ 41 | #define R_PIO_GATING 1<<0 42 | #define R_RSB_GATING 1<<3 43 | #define PLL_CTRL_REG1 0x0044 /* PLL Control Register 1 */ 44 | #define APBS_SOFT_RST_REG 0x00B0 /* APBS Software Reset Register */ 45 | #define R_RSB_RESET 1<<3 46 | 47 | 48 | 49 | static void 50 | prcmwr(int offset, u32int val) 51 | { 52 | *IO(u32int, (PRCM + offset)) = val; 53 | } 54 | 55 | 56 | static u32int 57 | prcmrd(int offset) 58 | { 59 | return *IO(u32int, (PRCM + offset)); 60 | } 61 | 62 | 63 | static void 64 | piowr(int offset, u32int val) 65 | { 66 | *IO(u32int, (PIO + offset)) = val; 67 | } 68 | 69 | 70 | static u32int 71 | piord(int offset) 72 | { 73 | return *IO(u32int, (PIO + offset)); 74 | } 75 | 76 | 77 | /* 78 | * This is needed to make sure RSB and access to the PMIC 79 | * works on the Pine64 Pinephone 80 | */ 81 | 82 | 83 | 84 | void 85 | arch_rsbsetup(void) 86 | { 87 | /* gate pass for R_PIO */ 88 | prcmwr(APBS_CLK_GATING_REG, prcmrd(APBS_CLK_GATING_REG) | R_PIO_GATING); 89 | 90 | /* sets PL0 and PL1 to use RSB */ 91 | piowr(RPIO_CFG00, piord(RPIO_CFG00) & ~0xff | (S_RSB_SCK | S_RSB_SDA)); 92 | /* sets PL0 and PL1 to "Level 2" */ 93 | piowr(RPIO_DRV00, piord(RPIO_DRV00) & ~0x0f | 0xa); 94 | /* sets PL0 and PL1 to pull-up */ 95 | piowr(RPIO_PUL00, piord(RPIO_PUL00) & ~0x0f | 0x5); 96 | 97 | /* asser then de-assert reset on RSB */ 98 | prcmwr(APBS_SOFT_RST_REG, prcmrd(APBS_SOFT_RST_REG) & ~R_RSB_RESET); 99 | prcmwr(APBS_SOFT_RST_REG, prcmrd(APBS_SOFT_RST_REG) | R_RSB_RESET); 100 | 101 | /* gate pass for R_RSB */ 102 | prcmwr(0x28, prcmrd(0x28) | R_RSB_GATING); 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /axp803.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The AXP803 is the PMIC (power manager) 3 | * used for Allwinner A64 SoC platforms 4 | */ 5 | 6 | 7 | #include "u.h" 8 | #include "../port/lib.h" 9 | #include "mem.h" 10 | #include "dat.h" 11 | #include "fns.h" 12 | #include "../port/error.h" 13 | #include "io.h" 14 | 15 | #include "axp803.h" 16 | 17 | 18 | #define PWR_REG_MASK 0xFF 19 | 20 | #define PWR_SOURCE 0x00 21 | #define PWR_MODE 0x01 22 | #define PWR_REASON 0x02 23 | #define PWR_ID_NUM 0x03 24 | 25 | 26 | static u8int 27 | pwrrd(u8int reg) 28 | { 29 | u32int buf; 30 | 31 | buf = rsb_read(PMICRTA, PMICADDR, reg, 1); 32 | return (u8int)(buf & PWR_REG_MASK); 33 | } 34 | 35 | 36 | static int 37 | pwrwr(u8int reg, u8int val) 38 | { 39 | return rsb_write(PMICRTA, PMICADDR, reg, val, 1); 40 | } 41 | 42 | 43 | u8int 44 | pmic_id(void) 45 | { 46 | u8int buf, msb, lsb; 47 | 48 | /* 49 | * Reg 03, IC type no. 50 | * bits 4 & 5 are uncertain 51 | * AXP803 should then return b01xx0001 52 | * shift bits 6 & 7 over to get b010001 53 | */ 54 | 55 | buf = pwrrd(PWR_ID_NUM); 56 | 57 | msb = (0xC0 & buf) >> 2; 58 | lsb = 0x0F & buf; 59 | 60 | return (msb | lsb); 61 | } 62 | 63 | 64 | int 65 | pmic_acin(void) 66 | { 67 | u8int buf; 68 | 69 | buf = pwrrd(PWR_SOURCE); 70 | 71 | return (buf & 0x80); 72 | } 73 | 74 | 75 | int 76 | pmic_vbat(void) 77 | { 78 | u8int buf; 79 | 80 | buf = pwrrd(PWR_SOURCE); 81 | 82 | return (buf & 0x08); 83 | } 84 | 85 | 86 | static Pmicregs* 87 | findpmicrail(char *name) 88 | { 89 | Pmicregs *pr; 90 | 91 | for(pr = pmicregs; pr->name != nil; pr++){ 92 | if(cistrcmp(name, pr->name) == 0) 93 | return pr; 94 | } 95 | 96 | return nil; 97 | } 98 | 99 | 100 | int 101 | getpmicstate(int rail) 102 | { 103 | u8int buf; 104 | Pmicregs *pr = pmicregs; 105 | 106 | pr += rail; 107 | 108 | buf = pwrrd(pr->onoffreg); 109 | 110 | buf &= 1<onoffbit; 111 | 112 | return (int)(buf? 1 : 0); 113 | } 114 | 115 | 116 | int 117 | setpmicstate(char *name, int state) 118 | { 119 | u8int buf; 120 | Pmicregs *pr = pmicregs; 121 | 122 | pr = findpmicrail(name); 123 | 124 | if(pr == nil) 125 | return -1; 126 | 127 | buf = pwrrd(pr->onoffreg); 128 | 129 | // iprint("setstate: %s = %d _ %08ub_", name, state, buf); 130 | 131 | if((buf & 1<onoffbit) == (state<onoffbit)) 132 | return 1; 133 | 134 | if(state) 135 | buf |= 1<onoffbit; 136 | else 137 | buf ^= 1<onoffbit; 138 | 139 | // iprint("%08ub\n", buf); 140 | 141 | if(pwrwr(pr->onoffreg, buf)) 142 | return 1; 143 | 144 | 145 | iprint("setpmicstate: pwrwr failed\n"); 146 | return 0; 147 | } 148 | 149 | 150 | /* 151 | * Some of the rails have split voltage steps. 152 | * Above a certain value, each step will be 153 | * twice as many mV. 154 | * ex. 10mV steps till 1200mV, and 20mv steps above 155 | */ 156 | 157 | int 158 | getpmicvolt(int rail) 159 | { 160 | u8int buf; 161 | int steps, mv; 162 | Pmicregs *pr = pmicregs; 163 | 164 | pr += rail; 165 | 166 | buf = pwrrd(pr->voltreg); 167 | buf &= pr->voltmask; 168 | 169 | steps = (int)buf; 170 | 171 | mv = (steps * pr->voltstep1) + pr->voltbase; 172 | 173 | if(mv > pr->voltsplit){ 174 | int mvl, mvh; 175 | mvl = (pr->voltsplit - pr->voltbase) / pr->voltstep1; 176 | mvh = steps - mvl; 177 | 178 | mv = (mvl * pr->voltstep1) + (mvh * pr->voltstep2); 179 | } 180 | 181 | return mv; 182 | } 183 | 184 | 185 | int 186 | setpmicvolt(char *name, int val) 187 | { 188 | u8int buf; 189 | Pmicregs *pr = pmicregs; 190 | 191 | pr = findpmicrail(name); 192 | 193 | if(pr == nil) 194 | return -1; 195 | 196 | if(val > pr->voltmax) 197 | val = pr->voltmax; 198 | 199 | if(val < pr->voltbase) 200 | val = pr->voltbase; 201 | 202 | if(val <= pr->voltsplit) 203 | buf = (u8int)((val - pr->voltbase) / pr->voltstep1); 204 | buf &= pr->voltmask; 205 | 206 | if(val > pr->voltsplit){ 207 | int mvl, mvh; 208 | mvl = (pr->voltsplit - pr->voltbase) / pr->voltstep1; 209 | mvh = (val - pr->voltsplit) / pr->voltstep2; 210 | buf = (u8int)(mvl + mvh); 211 | buf &= pr->voltmask; 212 | } 213 | 214 | // iprint("setvolt: %ud\n", buf); 215 | 216 | if(pwrwr(pr->voltreg, buf)) 217 | return 1; 218 | 219 | 220 | iprint("setpmicvolt: pwrwr failed\n"); 221 | return 0; 222 | } 223 | 224 | 225 | char* 226 | getpmicname(int rail) 227 | { 228 | Pmicregs *pr = pmicregs; 229 | 230 | pr += rail; 231 | 232 | return pr->name; 233 | } 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /axp803.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | typedef struct Pmicregs Pmicregs; 4 | 5 | 6 | struct Pmicregs{ 7 | char *name; 8 | u8int onoffreg; 9 | u8int onoffbit; 10 | u8int voltreg; 11 | u8int voltmask; 12 | int voltbase; /* ints in mV */ 13 | int voltstep1; 14 | int voltsplit; 15 | int voltstep2; 16 | int voltmax; 17 | }; 18 | 19 | 20 | static Pmicregs pmicregs[] = { 21 | {"DCDC6", 0x10, 5, 0x25, 0x7F, 600, 10, 1100, 20, 1520}, 22 | {"DCDC5", 0x10, 4, 0x24, 0x7F, 800, 10, 1200, 20, 1840}, 23 | {"DCDC4", 0x10, 3, 0x23, 0x7F, 500, 10, 1200, 20, 1300}, 24 | {"DCDC3", 0x10, 2, 0x22, 0x7F, 500, 10, 1200, 20, 1300}, 25 | {"DCDC2", 0x10, 1, 0x21, 0x7F, 500, 10, 1200, 20, 1300}, 26 | {"DCDC1", 0x10, 0, 0x20, 0x1F, 1600, 100, 3400, 0, 3400}, 27 | {"DC1SW", 0x12, 7, 0xFF, 0x00, 0, 0, 0, 0, 0}, // no volt register? 28 | {"DLDO4", 0x12, 6, 0x18, 0x1F, 700, 100, 3300, 0, 3300}, 29 | {"DLDO3", 0x12, 5, 0x17, 0x1F, 700, 100, 3300, 0, 3300}, 30 | {"DLDO2", 0x12, 4, 0x16, 0x1F, 700, 100, 3400, 200, 4200}, 31 | {"DLDO1", 0x12, 3, 0x15, 0x1F, 700, 100, 3300, 0, 3300}, 32 | {"ELDO3", 0x12, 2, 0x1B, 0x1F, 700, 50, 1900, 0, 1900}, 33 | {"ELDO2", 0x12, 1, 0x1A, 0x1F, 700, 50, 1900, 0, 1900}, 34 | {"ELDO1", 0x12, 0, 0x19, 0x1F, 700, 50, 1900, 0, 1900}, 35 | {"ALDO3", 0x13, 7, 0x2A, 0x1F, 700, 100, 3300, 0, 3300}, 36 | {"ALDO2", 0x13, 6, 0x29, 0x1F, 700, 100, 3300, 0, 3300}, 37 | {"ALDO1", 0x13, 5, 0X28, 0x1F, 700, 100, 3300, 0, 3300}, 38 | {"FLDO2", 0x13, 3, 0x1D, 0x07, 700, 50, 1450, 0, 1450}, 39 | {"FLDO1", 0x13, 2, 0x1C, 0x07, 700, 50, 1450, 0, 1450}, 40 | { nil }, 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /cache.v8.s: -------------------------------------------------------------------------------- 1 | #include "sysreg.h" 2 | 3 | #undef SYSREG 4 | #define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)) 5 | 6 | /* 7 | * instruction cache operations 8 | */ 9 | TEXT cacheiinvse(SB), 1, $-4 10 | MOVWU len+8(FP), R2 11 | ADD R0, R2 12 | 13 | MRS DAIF, R11 14 | MSR $0x2, DAIFSet 15 | MOVWU $1, R10 16 | MSR R10, CSSELR_EL1 17 | ISB $SY 18 | MRS CCSIDR_EL1, R4 19 | 20 | ANDW $7, R4 21 | ADDW $4, R4 // log2(linelen) 22 | LSL R4, R10 23 | LSR R4, R0 24 | LSL R4, R0 25 | 26 | _iinvse: 27 | IC R0, 3,7,5,1 // IVAU 28 | ADD R10, R0 29 | CMP R0, R2 30 | BGT _iinvse 31 | DSB $NSH 32 | ISB $SY 33 | MSR R11, DAIF 34 | RETURN 35 | 36 | TEXT cacheiinv(SB), 1, $-4 37 | IC R0, 0,7,5,0 // IALLU 38 | DSB $NSH 39 | ISB $SY 40 | RETURN 41 | 42 | TEXT cacheuwbinv(SB), 1, $0 43 | BL cachedwbinv(SB) 44 | BL cacheiinv(SB) 45 | RETURN 46 | 47 | /* 48 | * data cache operations 49 | */ 50 | TEXT cachedwbse(SB), 1, $-4 51 | MOV LR, R29 52 | BL cachedva<>(SB) 53 | TEXT dccvac(SB), 1, $-4 54 | DC R0, 3,7,10,1 // CVAC 55 | RETURN 56 | 57 | TEXT cacheduwbse(SB), 1, $-4 58 | MOV LR, R29 59 | BL cachedva<>(SB) 60 | TEXT dccvau(SB), 1, $-4 61 | DC R0, 3,7,11,1 // CVAU 62 | RETURN 63 | 64 | TEXT cachedinvse(SB), 1, $-4 65 | MOV LR, R29 66 | BL cachedva<>(SB) 67 | TEXT dcivac(SB), 1, $-4 68 | DC R0, 0,7,6,1 // IVAC 69 | RETURN 70 | 71 | TEXT cachedwbinvse(SB), 1, $-4 72 | MOV LR, R29 73 | BL cachedva<>(SB) 74 | TEXT dccivac(SB), 1, $-4 75 | DC R0, 3,7,14,1 // CIVAC 76 | RETURN 77 | 78 | TEXT cachedva<>(SB), 1, $-4 79 | MOV LR, R1 80 | MOVWU len+8(FP), R2 81 | ADD R0, R2 82 | 83 | MRS DAIF, R11 84 | MSR $0x2, DAIFSet 85 | MOVWU $0, R10 86 | MSR R10, CSSELR_EL1 87 | ISB $SY 88 | MRS CCSIDR_EL1, R4 89 | 90 | ANDW $7, R4 91 | ADDW $4, R4 // log2(linelen) 92 | MOVWU $1, R10 93 | LSL R4, R10 94 | LSR R4, R0 95 | LSL R4, R0 96 | 97 | DSB $SY 98 | ISB $SY 99 | _cachedva: 100 | BL (R1) 101 | ADD R10, R0 102 | CMP R0, R2 103 | BGT _cachedva 104 | DSB $SY 105 | ISB $SY 106 | MSR R11, DAIF 107 | RET R29 108 | 109 | /* 110 | * l1 cache operations 111 | */ 112 | TEXT cachedwb(SB), 1, $-4 113 | MOVWU $0, R0 114 | _cachedwb: 115 | MOV LR, R29 116 | BL cachedsw<>(SB) 117 | TEXT dccsw(SB), 1, $-4 118 | DC R0, 0,7,10,2 // CSW 119 | RETURN 120 | 121 | TEXT cachedinv(SB), 1, $-4 122 | MOVWU $0, R0 123 | _cachedinv: 124 | MOV LR, R29 125 | BL cachedsw<>(SB) 126 | TEXT dcisw(SB), 1, $-4 127 | DC R0, 0,7,6,2 // ISW 128 | RETURN 129 | 130 | TEXT cachedwbinv(SB), 1, $-4 131 | MOVWU $0, R0 132 | _cachedwbinv: 133 | MOV LR, R29 134 | BL cachedsw<>(SB) 135 | TEXT dccisw(SB), 1, $-4 136 | DC R0, 0,7,14,2 // CISW 137 | RETURN 138 | 139 | /* 140 | * l2 cache operations 141 | */ 142 | TEXT l2cacheuwb(SB), 1, $-4 143 | MOVWU $1, R0 144 | B _cachedwb 145 | TEXT l2cacheuinv(SB), 1, $-4 146 | MOVWU $1, R0 147 | B _cachedinv 148 | TEXT l2cacheuwbinv(SB), 1, $-4 149 | MOVWU $1, R0 150 | B _cachedwbinv 151 | 152 | TEXT cachesize(SB), 1, $-4 153 | MRS DAIF, R11 154 | MSR $0x2, DAIFSet 155 | MSR R0, CSSELR_EL1 156 | ISB $SY 157 | MRS CCSIDR_EL1, R0 158 | MSR R11, DAIF 159 | RETURN 160 | 161 | TEXT cachedsw<>(SB), 1, $-4 162 | MOV LR, R1 163 | 164 | MRS DAIF, R11 165 | MSR $0x2, DAIFSet 166 | ADDW R0, R0, R8 167 | MSR R8, CSSELR_EL1 168 | ISB $SY 169 | MRS CCSIDR_EL1, R4 170 | 171 | LSR $3, R4, R7 172 | ANDW $1023, R7 // lastway 173 | ADDW $1, R7, R5 // #ways 174 | 175 | LSR $13, R4, R2 176 | ANDW $32767, R2 // lastset 177 | ADDW $1, R2 // #sets 178 | 179 | ANDW $7, R4 180 | ADDW $4, R4 // log2(linelen) 181 | 182 | MOVWU $32, R3 // wayshift = 32 - log2(#ways) 183 | _countlog2ways: 184 | CBZ R7, _loop // lastway == 0? 185 | LSR $1, R7 // lastway >>= 1 186 | SUB $1, R3 // wayshift-- 187 | B _countlog2ways 188 | _loop: 189 | DSB $SY 190 | ISB $SY 191 | _nextway: 192 | MOVWU $0, R6 // set 193 | _nextset: 194 | LSL R3, R7, R0 // way<name != nil; g++){ 31 | if(cistrcmp(name, g->name) == 0) 32 | return g; 33 | } 34 | 35 | return nil; 36 | } 37 | 38 | 39 | static Reset* 40 | findreset(char *name) 41 | { 42 | Reset *r; 43 | 44 | for(r = resets; r->name != nil; r++){ 45 | if(cistrcmp(name, r->name) == 0) 46 | return r; 47 | } 48 | 49 | return nil; 50 | } 51 | 52 | 53 | char* 54 | listgates(int i) 55 | { 56 | Gate *g = gates; 57 | u32int v; 58 | char p[32]; 59 | int l, m; 60 | 61 | g += i; 62 | 63 | if(g->name == nil) 64 | return nil; 65 | 66 | v = ccurd(BUS_CLK_GATING_REG0 + g->bank); 67 | m = (v & g->mask ? 1 : 0); 68 | 69 | print("%s-\n", g->name); 70 | print("%d-\n", m); 71 | 72 | snprint(p, sizeof(p), "%s %d", g->name, m); 73 | 74 | return p; 75 | } 76 | 77 | 78 | char* 79 | getgatename(int i) 80 | { 81 | Gate *g = gates; 82 | 83 | g += i; 84 | 85 | return g->name; 86 | } 87 | 88 | 89 | int 90 | getgatestate(int i) 91 | { 92 | Gate *g = gates; 93 | int r, s; 94 | 95 | g += i; 96 | 97 | r = ccurd(BUS_CLK_GATING_REG0 + g->bank); 98 | s = (r & g->mask ? 1 : 0); 99 | 100 | return s; 101 | } 102 | 103 | 104 | char* 105 | getresetname(int i) 106 | { 107 | Gate *g = gates; 108 | 109 | g += i; 110 | 111 | return g->name; 112 | } 113 | 114 | 115 | int 116 | getresetstate(int i) 117 | { 118 | Gate *g = gates; 119 | int r, s; 120 | 121 | g += i; 122 | 123 | r = ccurd(BUS_SOFT_RST_REG0 + g->bank); 124 | s = (r & g->mask ? 1 : 0); 125 | 126 | return s; 127 | } 128 | 129 | 130 | int 131 | openthegate(char *name) 132 | { 133 | u32int buf; 134 | Reset *r; 135 | Gate *g; 136 | 137 | r = findreset(name); 138 | g = findgate(name); 139 | 140 | // iprint("opengate:%s = %s & %s\n", name, g->name, r->name); 141 | // iprint("opengate:%s = %08uX & %08uX\n", name, (BUS_CLK_GATING_REG0 + g->bank), (BUS_SOFT_RST_REG0 + r->bank)); 142 | 143 | if(g == nil || r == nil) 144 | return -1; 145 | 146 | /* hit the reset */ 147 | buf = ccurd(BUS_SOFT_RST_REG0 + r->bank); 148 | buf |= r->mask; 149 | ccuwr(BUS_SOFT_RST_REG0 + r->bank, buf); 150 | 151 | /* open the gate */ 152 | buf = ccurd(BUS_CLK_GATING_REG0 + g->bank); 153 | buf |= g->mask; 154 | ccuwr(BUS_CLK_GATING_REG0 + g->bank, buf); 155 | 156 | return 1; 157 | } 158 | 159 | 160 | 161 | void 162 | debuggates(void) 163 | { 164 | print("%ulb\n", ccurd(BUS_CLK_GATING_REG0)); 165 | print("%ulb\n", ccurd(BUS_CLK_GATING_REG1)); 166 | print("%ulb\n", ccurd(BUS_CLK_GATING_REG2)); 167 | print("%ulb\n", ccurd(BUS_CLK_GATING_REG3)); 168 | print("%ulb\n", ccurd(BUS_CLK_GATING_REG4)); 169 | } 170 | 171 | u32int 172 | getcpuclk_n(void) 173 | { 174 | u32int n; 175 | 176 | n = ccurd(PLL_CPUX_CTRL_REG); 177 | n = (n & 0x1F00) >> 8; 178 | return n; 179 | } 180 | 181 | u32int 182 | getcpuclk_k(void) 183 | { 184 | u32int k; 185 | 186 | k = ccurd(PLL_CPUX_CTRL_REG); 187 | k = (k & 0x30) >> 4; 188 | return k; 189 | } 190 | 191 | u32int 192 | getcpuclk_m(void) 193 | { 194 | u32int m; 195 | 196 | m = ccurd(PLL_CPUX_CTRL_REG); 197 | m = (m & 0x3); 198 | return m; 199 | } 200 | 201 | u32int 202 | getcpuclk_p(void) 203 | { 204 | u32int p; 205 | 206 | p = ccurd(PLL_CPUX_CTRL_REG); 207 | p = (p & 0x30000) >> 16; 208 | return p; 209 | } 210 | 211 | 212 | static Nkmp* 213 | findcpuclk(uint findrate) 214 | { 215 | Nkmp *nkmp; 216 | 217 | for(nkmp = cpu_nkmp; nkmp->rate != 0; nkmp++){ 218 | if(nkmp->rate == findrate) 219 | return nkmp; 220 | } 221 | 222 | return nil; 223 | } 224 | 225 | static Nkmp* 226 | findcpuclk_n(u32int findn) 227 | { 228 | Nkmp *nkmp; 229 | 230 | for(nkmp = cpu_nkmp; nkmp->rate != 0; nkmp++){ 231 | if(nkmp->n == findn) 232 | return nkmp; 233 | } 234 | 235 | return nil; 236 | } 237 | 238 | 239 | 240 | int 241 | setcpuclk_n(u32int setn) 242 | { 243 | Nkmp *nkmp; 244 | u32int reg; 245 | 246 | nkmp = findcpuclk_n(setn); 247 | 248 | if(nkmp == nil){ 249 | iprint("setcpuclk_n found nil\n"); 250 | return -1; 251 | } 252 | 253 | reg = ccurd(PLL_CPUX_CTRL_REG); 254 | reg &= 0xFFFC0000; 255 | reg |= (nkmp->n << 8) & 0x1F00; 256 | reg |= (nkmp->k << 4) & 0x30; 257 | reg |= (nkmp->m << 0) & 0x3; 258 | reg |= (nkmp->p << 16) & 0x30000; 259 | 260 | ccuwr(PLL_CPUX_CTRL_REG, reg); 261 | 262 | return 1; 263 | } 264 | 265 | 266 | int 267 | setcpuclk(uint setrate) 268 | { 269 | Nkmp *nkmp; 270 | u32int reg; 271 | 272 | nkmp = findcpuclk(setrate); 273 | 274 | if(nkmp == nil){ 275 | iprint("setcpuclk found nil\n"); 276 | return -1; 277 | } 278 | 279 | reg = ccurd(PLL_CPUX_CTRL_REG); 280 | reg &= 0xFFFC0000; 281 | reg |= (nkmp->n << 8) & 0x1F00; 282 | reg |= (nkmp->k << 4) & 0x30; 283 | reg |= (nkmp->m << 0) & 0x3; 284 | reg |= (nkmp->p << 16) & 0x30000; 285 | 286 | ccuwr(PLL_CPUX_CTRL_REG, reg); 287 | 288 | return 1; 289 | } 290 | 291 | /* need to do a general clock setting function */ 292 | void 293 | turnonths(void) 294 | { 295 | u32int buf; 296 | 297 | ccuwr(THS_CLK_REG, 0x80000000); 298 | 299 | // buf = ccurd(BUS_SOFT_RST_REG3); 300 | // buf |= 1<<8; 301 | // ccuwr(BUS_SOFT_RST_REG3, buf); 302 | 303 | // buf = ccurd(BUS_CLK_GATING_REG2); 304 | // buf |= 1<<8; 305 | // ccuwr(BUS_CLK_GATING_REG2, buf); 306 | 307 | if(openthegate("THS") != 1) 308 | iprint("THS open FAIL\n"); 309 | } 310 | -------------------------------------------------------------------------------- /ccu.h: -------------------------------------------------------------------------------- 1 | #define PLL_CPUX_CTRL_REG 0x0000 //PLL_CPUX Control Register 2 | #define PLL_AUDIO_CTRL_REG 0x0008 //PLL_AUDIO Control Register 3 | #define PLL_VIDEO0_CTRL_REG 0x0010 //PLL_VIDEO0 Control Register 4 | #define PLL_VE_CTRL_REG 0x0018 //PLL_VE Control Register 5 | #define PLL_DDR0_CTRL_REG 0x0020 //PLL_DDR0 Control Register 6 | #define PLL_PERIPH0_CTRL_REG 0x0028 //PLL_PERIPH0 Control Register 7 | #define PLL_PERIPH1_CTRL_REG 0x002C //PLL_PERIPH1 Control Register 8 | #define PLL_VIDEO1_CTRL_REG 0x0030 //PLL_VIDEO1 Control Register 9 | #define PLL_GPU_CTRL_REG 0x0038 //PLL_GPU Control Register 10 | #define PLL_MIPI_CTRL_REG 0x0040 //PLL_MIPI Control Register 11 | #define PLL_HSIC_CTRL_REG 0x0044 //PLL_HSIC Control Register 12 | #define PLL_DE_CTRL_REG 0x0048 //PLL_DE Control Register 13 | #define PLL_DDR1_CTRL_REG 0x004C //PLL_DDR1 Control Register 14 | #define CPU_AXI_CFG_REG 0x0050 //CPUX/AXI Configuration Register 15 | #define AHB1_APB1_CFG_REG 0x0054 //AHB1/APB1 Configuration Register 16 | #define APB2 _CFG_REG 0x0058 //APB2 Configuration Register 17 | #define AHB2_CFG_REG 0x005C //AHB2 Configuration Register 18 | #define BUS_CLK_GATING_REG0 0x0060 //Bus Clock Gating Register 0 19 | #define BUS_CLK_GATING_REG1 0x0064 //Bus Clock Gating Register 1 20 | #define BUS_CLK_GATING_REG2 0x0068 //Bus Clock Gating Register 2 21 | #define BUS_CLK_GATING_REG3 0x006C //Bus Clock Gating Register 3 22 | #define BUS_CLK_GATING_REG4 0x0070 //Bus Clock Gating Register 4 23 | #define THS_CLK_REG 0x0074 //THS Clock Register 24 | #define NAND_CLK_REG 0x0080 //NAND Clock Register 25 | #define SDMMC0_CLK_REG 0x0088 //SDMMC0 Clock Register 26 | #define SDMMC1_CLK_REG 0x008C //SDMMC1 Clock Register 27 | #define SDMMC2_CLK_REG 0x0090 //SDMMC2 Clock Register 28 | #define TS_CLK_REG 0x0098 //TS Clock Register 29 | #define CE_CLK_REG 0x009C //CE Clock Register 30 | #define SPI0_CLK_REG 0x00A0 //SPI0 Clock Register 31 | #define SPI1_CLK_REG 0x00A4 //SPI1 Clock Register 32 | #define I2SPCM0_CLK_REG 0x00B0 //I2S/PCM-0 Clock Register 33 | #define I2SPCM1_CLK_REG 0x00B4 //I2S/PCM-1 Clock Register 34 | #define I2SPCM2_CLK_REG 0x00B8 //I2S/PCM-2 Clock Register 35 | #define SPDIF_CLK_REG 0x00C0 //SPDIF Clock Register 36 | #define USBPHY_CFG_REG 0x00CC //USBPHY Configuration Register 37 | #define DRAM_CFG_REG 0x00F4 //DRAM Configuration Register 38 | #define PLL_DDR_CFG_REG 0x00F8 //PLL_DDR Configuration Register 39 | #define MBUS_RST_REG 0x00FC //MBUS Reset Register 40 | #define DRAM_CLK_GATING_REG 0x0100 //DRAM Clock Gating Register 41 | #define DE_CLK_REG 0x0104 //DE Clock Register 42 | #define TCON0_CLK_REG 0x0118 //TCON0 Clock Register 43 | #define TCON1_CLK_REG 0x011C //TCON1 Clock Register 44 | 45 | #define DEINTERLACE_CLK_REG 0x0124 //DEINTERLACE Clock Register 46 | #define CSI_MISC_CLK_REG 0x0130 //CSI_MISC Clock Register 47 | #define CSI_CLK_REG 0x0134 //CSI Clock Register 48 | #define VE_CLK_REG 0x013C //VE Clock Register 49 | 50 | #define AC_DIG_CLK_REG 0x0140 //AC Digital Clock Register 51 | #define AVS_CLK_REG 0x0144 //AVS Clock Register 52 | #define HDMI_CLK_REG 0x0150 //HDMI Clock Register 53 | #define HDMI_SLOW_CLK_REG 0x0154 //HDMI Slow Clock Register 54 | #define MBUS_CLK_REG 0x015C //MBUS Clock Register 55 | #define MIPI_DSI_CLK_REG 0x0168 //MIPI_DSI Clock Register 56 | #define GPU_CLK_REG 0x01A0 //GPU Clock Register 57 | 58 | #define PLL_STABLE_TIME_REG0 0x0200 //PLL Stable Time Register0 59 | #define PLL_STABLE_TIME_REG1 0x0204 //PLL Stable Time Register1 60 | #define PLL_PERIPH1_BIAS_REG 0x021C //PLL_PERIPH1 Bias Register 61 | #define PLL_CPUX_BIAS_REG 0x0220 //PLL_CPUX Bias Register 62 | #define PLL_AUDIO_BIAS_REG 0x0224 //PLL_AUDIO Bias Register 63 | #define PLL_VIDEO0_BIAS_REG 0x0228 //PLL_VIDEO0 Bias Register 64 | #define PLL_VE_BIAS_REG 0x022C //PLL_VE Bias Register 65 | #define PLL_DDR0_BIAS_REG 0x0230 //PLL_DDR0 Bias Register 66 | #define PLL_PERIPH0_BIAS_REG 0x0234 //PLL_PERIPH0 Bias Register 67 | #define PLL_VIDEO1_BIAS_REG 0x0238 //PLL_VIDEO1 Bias Register 68 | #define PLL_GPU_BIAS_REG 0x023C //PLL_GPU Bias Register 69 | #define PLL_MIPI_BIAS_REG 0x0240 //PLL_MIPI Bias Register 70 | #define PLL_HSIC_BIAS_REG 0x0244 //PLL_HSIC Bias Register 71 | #define PLL_DE_BIAS_REG 0x0248 //PLL_DE Bias Register 72 | #define PLL_DDR1_BIAS_REG 0x024C //PLL_DDR1 Bias Register 73 | #define PLL_CPUX_TUN_REG 0x0250 //PLL_CPUX Tuning Register 74 | #define PLL_DDR0_TUN_REG 0x0260 //PLL_DDR0 Tuning Register 75 | #define PLL_MIPI_TUN_REG 0x0270 //PLL_MIPI Tuning Register 76 | #define PLL_PERIPH1_PAT_CTRL_REG 0x027C //PLL_PERIPH1 Pattern Control Register 77 | #define PLL_CPUX_PAT_CTRL_REG 0x0280 //PLL_CPUX Pattern Control Register 78 | #define PLL_AUDIO_PAT_CTRL_REG 0x0284 //PLL_AUDIO Pattern Control Register 79 | #define PLL_VIDEO0_PAT_CTRL_REG 0x0288 //PLL_VIDEO0 Pattern Control Register 80 | #define PLL_VE_PAT_CTRL_REG 0x028C //PLL_VE Pattern Control Register 81 | #define PLL_DDR0_PAT_CTRL_REG 0x0290 //PLL_DDR0 Pattern Control Register 82 | #define PLL_VIDEO1_PAT_CTRL_REG 0x0298 //PLL_VIDEO1 Pattern Control Register 83 | #define PLL_GPU_PAT_CTRL_REG 0x029C //PLL_GPU Pattern Control Register 84 | #define PLL_MIPI_PAT_CTRL_REG 0x02A0 //PLL_MIPI Pattern Control Register 85 | #define PLL_HSIC_PAT_CTRL_REG 0x02A4 //PLL_HSIC Pattern Control Register 86 | #define PLL_DE_PAT_CTRL_REG 0x02A8 //PLL_DE Pattern Control Register 87 | #define PLL_DDR1_PAT_CTRL_REG0 0x02AC //PLL_DDR1 Pattern Control Register0 88 | #define PLL_DDR1_PAT_CTRL_REG1 0x02B0 //PLL_DDR1 Pattern Control Register1 89 | 90 | #define BUS_SOFT_RST_REG0 0x02C0 //Bus Software Reset Register 0 91 | #define BUS_SOFT_RST_REG1 0x02C4 //Bus Software Reset Register 1 92 | #define BUS_SOFT_RST_REG2 0x02C8 //Bus Software Reset Register 2 93 | #define BUS_SOFT_RST_REG3 0x02D0 //Bus Software Reset Register 3 94 | #define BUS_SOFT_RST_REG4 0x02D8 //Bus Software Reset Register 4 95 | 96 | #define CCM_SEC_SWITCH_REG 0x02F0 //CCM Security Switch Register 97 | #define PS_CTRL_REG 0x0300 //PS Control Register 98 | #define PS_CNT_REG 0x0304 //PS Counter Register 99 | #define PLL_LOCK_CTRL_REG 0x0320 //PLL Lock Control Register 100 | 101 | 102 | 103 | typedef struct Gate Gate; 104 | typedef struct Reset Reset; 105 | typedef struct Nkmp Nkmp; 106 | 107 | 108 | struct Gate{ 109 | char *name; 110 | uint bank; 111 | uint mask; 112 | }; 113 | 114 | 115 | struct Reset{ 116 | char *name; 117 | uint bank; 118 | uint mask; 119 | }; 120 | 121 | 122 | static Gate gates[] = { 123 | {"USBOHCI0", 0x0, 1<<29}, 124 | {"USB-OTG-OHCI", 0x0, 1<<28}, 125 | {"USBEHCI0", 0x0, 1<<25}, 126 | {"USB-OTG-EHCI", 0x0, 1<<24}, 127 | {"USB-OTG-Device", 0x0, 1<<23}, 128 | {"SPI1", 0x0, 1<<21}, 129 | {"SPI0", 0x0, 1<<20}, 130 | {"HSTMR", 0x0, 1<<19}, 131 | {"TS", 0x0, 1<<18}, 132 | {"EMAC", 0x0, 1<<17}, 133 | {"DRAM", 0x0, 1<<14}, 134 | {"NAND", 0x0, 1<<13}, 135 | {"SMHC2", 0x0, 1<<10}, 136 | {"SMHC1", 0x0, 1<<9}, 137 | {"SMHC0", 0x0, 1<<8}, 138 | {"DMA", 0x0, 1<<6}, 139 | {"CE", 0x0, 1<<5}, 140 | {"MIPIDSI", 0x0, 1<<1}, 141 | {"SPINLOCK", 0x4, 1<<22}, 142 | {"MSGBOX", 0x4, 1<<21}, 143 | {"GPU", 0x4, 1<<20}, 144 | {"DE", 0x4, 1<<12}, 145 | {"HDMI", 0x4, 1<<11}, 146 | {"CSI", 0x4, 1<<8}, 147 | {"DEINTERLACE", 0x4, 1<<5}, 148 | {"TCON1", 0x4, 1<<4}, 149 | {"TCON0", 0x4, 1<<3}, 150 | {"VE", 0x4, 1<<0}, 151 | {"I2SPCM2", 0x8, 1<<14}, 152 | {"I2SPCM1", 0x8, 1<<13}, 153 | {"I2SPCM0", 0x8, 1<<12}, 154 | {"THS", 0x8, 1<<8}, 155 | {"PIO", 0x8, 1<<5}, 156 | {"SPDIF", 0x8, 1<<1}, 157 | {"AC_DIG", 0x8, 1<<0}, 158 | {"UART4", 0xC, 1<<20}, 159 | {"UART3", 0xC, 1<<19}, 160 | {"UART2", 0xC, 1<<18}, 161 | {"UART1", 0xC, 1<<17}, 162 | {"UART0", 0xC, 1<<16}, 163 | {"SCR", 0xC, 1<<5}, 164 | {"TWI2", 0xC, 1<<2}, 165 | {"TWI1", 0xC, 1<<1}, 166 | {"TWI0", 0xC, 1<<0}, 167 | {"DBGSYS", 0x10, 1<<7}, 168 | { nil }, 169 | }; 170 | 171 | 172 | static Reset resets[] = { 173 | {"USBOHCI0", 0x0, 1<<29}, 174 | {"USB-OTG-OHCI", 0x0, 1<<28}, 175 | {"USBEHCI0", 0x0, 1<<25}, 176 | {"USB-OTG-EHCI", 0x0, 1<<24}, 177 | {"USB-OTG-Device", 0x0, 1<<23}, 178 | {"SPI1", 0x0, 1<<21}, 179 | {"SPI0", 0x0, 1<<20}, 180 | {"HSTMR", 0x0, 1<<19}, 181 | {"TS", 0x0, 1<<18}, 182 | {"EMAC", 0x0, 1<<17}, 183 | {"DRAM", 0x0, 1<<14}, 184 | {"NAND", 0x0, 1<<13}, 185 | {"SMHC2", 0x0, 1<<10}, 186 | {"SMHC1", 0x0, 1<<9}, 187 | {"SMHC0", 0x0, 1<<8}, 188 | {"DMA", 0x0, 1<<6}, 189 | {"CE", 0x0, 1<<5}, 190 | {"MIPIDSI", 0x0, 1<<1}, 191 | {"DBGSYS", 0x4, 1<<31}, 192 | {"SPINLOCK", 0x4, 1<<22}, 193 | {"MSGBOX", 0x4, 1<<21}, 194 | {"GPU", 0x4, 1<<20}, 195 | {"DE", 0x4, 1<<12}, 196 | {"HDMI", 0x4, 1<<11}, // listed as HDMI1 in datasheet 197 | {"HDMI0", 0x4, 1<<10}, 198 | {"CSI", 0x4, 1<<8}, 199 | {"DEINTERLACE", 0x4, 1<<5}, 200 | {"TCON1", 0x4, 1<<4}, 201 | {"TCON0", 0x4, 1<<3}, 202 | {"VE", 0x4, 1<<0}, 203 | {"LVDS", 0x8, 1<<0}, 204 | {"I2SPCM2", 0x10, 1<<14}, 205 | {"I2SPCM1", 0x10, 1<<13}, 206 | {"I2SPCM0", 0x10, 1<<12}, 207 | {"THS", 0x10, 1<<8}, 208 | {"OWA", 0x10, 1<<1}, 209 | {"AC_DIG", 0x10, 1<<0}, 210 | {"UART4", 0x18, 1<<20}, 211 | {"UART3", 0x18, 1<<19}, 212 | {"UART2", 0x18, 1<<18}, 213 | {"UART1", 0x18, 1<<17}, 214 | {"UART0", 0x18, 1<<16}, 215 | {"SCR", 0x18, 1<<5}, 216 | {"TWI2", 0x18, 1<<2}, 217 | {"TWI1", 0x18, 1<<1}, 218 | {"TWI0", 0x18, 1<<0}, 219 | { nil }, 220 | }; 221 | 222 | 223 | struct Nkmp { 224 | uint rate; 225 | u32int n; 226 | u32int k; 227 | u32int m; 228 | u32int p; 229 | }; 230 | 231 | 232 | static Nkmp bad_nkmp[] = { 233 | { 60000000, 9, 0, 0, 2 }, 234 | { 66000000, 10, 0, 0, 2 }, 235 | { 72000000, 11, 0, 0, 2 }, 236 | { 78000000, 12, 0, 0, 2 }, 237 | { 84000000, 13, 0, 0, 2 }, 238 | { 90000000, 14, 0, 0, 2 }, 239 | { 96000000, 15, 0, 0, 2 }, 240 | { 102000000, 16, 0, 0, 2 }, 241 | { 108000000, 17, 0, 0, 2 }, 242 | { 114000000, 18, 0, 0, 2 }, 243 | { 120000000, 9, 0, 0, 1 }, 244 | { 132000000, 10, 0, 0, 1 }, 245 | { 144000000, 11, 0, 0, 1 }, 246 | { 156000000, 12, 0, 0, 1 }, 247 | { 168000000, 13, 0, 0, 1 }, 248 | { 180000000, 14, 0, 0, 1 }, 249 | { 192000000, 15, 0, 0, 1 }, 250 | { 204000000, 16, 0, 0, 1 }, 251 | { 216000000, 17, 0, 0, 1 }, 252 | { 228000000, 18, 0, 0, 1 }, 253 | { 240000000, 9, 0, 0, 0 }, 254 | { 264000000, 10, 0, 0, 0 }, 255 | { 288000000, 11, 0, 0, 0 }, 256 | { 312000000, 12, 0, 0, 0 }, 257 | { 336000000, 13, 0, 0, 0 }, 258 | { 360000000, 14, 0, 0, 0 }, 259 | { 384000000, 15, 0, 0, 0 }, 260 | { 408000000, 16, 0, 0, 0 }, 261 | { 432000000, 17, 0, 0, 0 }, 262 | { 456000000, 18, 0, 0, 0 }, 263 | { 480000000, 19, 0, 0, 0 }, 264 | { 504000000, 20, 0, 0, 0 }, 265 | { 528000000, 21, 0, 0, 0 }, 266 | { 552000000, 22, 0, 0, 0 }, 267 | { 576000000, 23, 0, 0, 0 }, 268 | { 600000000, 24, 0, 0, 0 }, 269 | { 624000000, 25, 0, 0, 0 }, 270 | { 648000000, 26, 0, 0, 0 }, 271 | { 672000000, 27, 0, 0, 0 }, 272 | { 696000000, 28, 0, 0, 0 }, 273 | { 720000000, 29, 0, 0, 0 }, 274 | { 768000000, 15, 1, 0, 0 }, 275 | { 792000000, 10, 2, 0, 0 }, /* seem to be set by u-boot */ 276 | { 816000000, 16, 1, 0, 0 }, 277 | { 864000000, 17, 1, 0, 0 }, 278 | { 912000000, 18, 1, 0, 0 }, 279 | { 936000000, 12, 2, 0, 0 }, 280 | { 960000000, 19, 1, 0, 0 }, 281 | { 1008000000, 20, 1, 0, 0 }, 282 | { 1056000000, 21, 1, 0, 0 }, 283 | { 1080000000, 14, 2, 0, 0 }, 284 | { 1104000000, 22, 1, 0, 0 }, 285 | { 1152000000, 23, 1, 0, 0 }, /* seems to be the prefered speed */ 286 | // { 1152000000, 31, 2, 1, 0 }, 287 | { 1200000000, 24, 1, 0, 0 }, 288 | { 1224000000, 16, 2, 0, 0 }, 289 | { 1248000000, 25, 1, 0, 0 }, 290 | { 1296000000, 26, 1, 0, 0 }, 291 | { 1344000000, 27, 1, 0, 0 }, 292 | { 1368000000, 18, 2, 0, 0 }, 293 | { 1440000000, 19, 2, 0, 0 }, 294 | { 1512000000, 20, 2, 0, 0 }, 295 | { 1536000000, 15, 3, 0, 0 }, 296 | { 1584000000, 21, 2, 0, 0 }, 297 | { 1632000000, 16, 3, 0, 0 }, 298 | { 1656000000, 22, 2, 0, 0 }, 299 | { 1728000000, 23, 2, 0, 0 }, 300 | { 1800000000, 24, 2, 0, 0 }, 301 | { 1872000000, 25, 2, 0, 0 }, 302 | { 0 } 303 | }; 304 | 305 | static Nkmp cpu_nkmp[] = { 306 | { 36000000, 0, 2, 1, 0 }, 307 | { 72000000, 1, 2, 1, 0 }, 308 | { 108000000, 2, 2, 1, 0 }, 309 | { 144000000, 3, 2, 1, 0 }, 310 | { 180000000, 4, 2, 1, 0 }, 311 | { 216000000, 5, 2, 1, 0 }, 312 | { 252000000, 6, 2, 1, 0 }, 313 | { 288000000, 7, 2, 1, 0 }, 314 | { 324000000, 8, 2, 1, 0 }, 315 | { 360000000, 9, 2, 1, 0 }, 316 | { 396000000, 10, 2, 1, 0 }, 317 | { 432000000, 11, 2, 1, 0 }, 318 | { 468000000, 12, 2, 1, 0 }, 319 | { 504000000, 13, 2, 1, 0 }, 320 | { 540000000, 14, 2, 1, 0 }, 321 | { 576000000, 15, 2, 1, 0 }, 322 | { 612000000, 16, 2, 1, 0 }, 323 | { 648000000, 17, 2, 1, 0 }, 324 | { 684000000, 18, 2, 1, 0 }, 325 | { 720000000, 19, 2, 1, 0 }, 326 | { 756000000, 20, 2, 1, 0 }, 327 | { 792000000, 21, 2, 1, 0 }, 328 | { 828000000, 22, 2, 1, 0 }, 329 | { 864000000, 23, 2, 1, 0 }, 330 | { 900000000, 24, 2, 1, 0 }, 331 | { 936000000, 25, 2, 1, 0 }, 332 | { 972000000, 26, 2, 1, 0 }, 333 | { 1008000000, 27, 2, 1, 0 }, 334 | { 1044000000, 28, 2, 1, 0 }, 335 | { 1080000000, 29, 2, 1, 0 }, 336 | { 1116000000, 30, 2, 1, 0 }, 337 | { 1152000000, 31, 2, 1, 0 }, 338 | { 0 } 339 | }; -------------------------------------------------------------------------------- /clock.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #include "u.h" 5 | #include "../port/lib.h" 6 | #include "mem.h" 7 | #include "dat.h" 8 | #include "fns.h" 9 | #include "io.h" 10 | #include "ureg.h" 11 | #include "sysreg.h" 12 | 13 | static uvlong freq; 14 | 15 | enum { 16 | Enable = 1<<0, 17 | Imask = 1<<1, 18 | Istatus = 1<<2, 19 | }; 20 | 21 | void 22 | clockshutdown(void) 23 | { 24 | } 25 | 26 | static void 27 | localclockintr(Ureg *ureg, void *) 28 | { 29 | timerintr(ureg, 0); 30 | } 31 | 32 | void 33 | clockinit(void) 34 | { 35 | uvlong tstart, tend; 36 | ulong t0, t1; 37 | 38 | syswr(PMCR_EL0, 1<<6 | 7); 39 | syswr(PMCNTENSET, 1<<31); 40 | syswr(PMUSERENR_EL0, 1<<2); 41 | syswr(CNTKCTL_EL1, 1<<1); 42 | 43 | syswr(CNTP_TVAL_EL0, ~0UL); 44 | syswr(CNTP_CTL_EL0, Enable); 45 | 46 | if(m->machno == 0){ 47 | freq = sysrd(CNTFRQ_EL0); 48 | print("timer frequency %lld Hz\n", freq); 49 | 50 | /* TURBO! */ 51 | // setclkrate("ccm_arm_a53_clk_root", "osc_25m_ref_clk", 25*Mhz); 52 | // setclkrate("ccm_arm_a53_clk_root", "arm_pll_clk", 1600*Mhz); 53 | // setcpuclk(1152000000); 54 | // panic("setcpuclk failed"); 55 | 56 | } 57 | tstart = sysrd(CNTPCT_EL0); 58 | do{ 59 | t0 = lcycles(); 60 | }while(sysrd(CNTPCT_EL0) == tstart); 61 | tend = tstart + (freq/100); 62 | do{ 63 | t1 = lcycles(); 64 | }while(sysrd(CNTPCT_EL0) < tend); 65 | t1 -= t0; 66 | m->cpuhz = 100 * t1; 67 | m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz; 68 | 69 | /* 70 | * we are using virtual counter register CNTVCT_EL0 71 | * instead of the performance counter in userspace. 72 | */ 73 | m->cyclefreq = freq; 74 | 75 | intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock"); 76 | } 77 | 78 | void 79 | timerset(uvlong next) 80 | { 81 | uvlong now; 82 | long period; 83 | 84 | now = fastticks(nil); 85 | period = next - now; 86 | syswr(CNTP_TVAL_EL0, period); 87 | } 88 | 89 | uvlong 90 | fastticks(uvlong *hz) 91 | { 92 | if(hz) 93 | *hz = freq; 94 | return sysrd(CNTPCT_EL0); 95 | } 96 | 97 | ulong 98 | perfticks(void) 99 | { 100 | return fastticks(nil); 101 | } 102 | 103 | ulong 104 | µs(void) 105 | { 106 | uvlong hz; 107 | uvlong t = fastticks(&hz); 108 | return (t * 1000000ULL) / hz; 109 | } 110 | 111 | void 112 | microdelay(int n) 113 | { 114 | ulong now; 115 | 116 | now = µs(); 117 | while(µs() - now < n); 118 | } 119 | 120 | void 121 | delay(int n) 122 | { 123 | while(--n >= 0) 124 | microdelay(1000); 125 | } 126 | 127 | void 128 | synccycles(void) 129 | { 130 | static Ref r1, r2; 131 | int s; 132 | 133 | s = splhi(); 134 | r2.ref = 0; 135 | incref(&r1); 136 | while(r1.ref != conf.nmach) 137 | ; 138 | // syswr(PMCR_EL0, 1<<6 | 7); 139 | incref(&r2); 140 | while(r2.ref != conf.nmach) 141 | ; 142 | r1.ref = 0; 143 | splx(s); 144 | } 145 | -------------------------------------------------------------------------------- /dat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Time. 3 | * 4 | * HZ should divide 1000 evenly, ideally. 5 | * 100, 125, 200, 250 and 333 are okay. 6 | */ 7 | #define HZ 100 /* clock frequency */ 8 | #define MS2HZ (1000/HZ) /* millisec per clock tick */ 9 | #define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ 10 | 11 | enum { 12 | Mhz = 1000 * 1000, 13 | }; 14 | 15 | typedef struct Conf Conf; 16 | typedef struct Confmem Confmem; 17 | typedef struct FPsave FPsave; 18 | typedef struct PFPU PFPU; 19 | typedef struct ISAConf ISAConf; 20 | typedef struct Label Label; 21 | typedef struct Lock Lock; 22 | typedef struct Memcache Memcache; 23 | typedef struct MMMU MMMU; 24 | typedef struct Mach Mach; 25 | typedef struct Page Page; 26 | typedef struct PhysUart PhysUart; 27 | typedef struct Pcidev Pcidev; 28 | typedef struct PMMU PMMU; 29 | typedef struct Proc Proc; 30 | typedef u64int PTE; 31 | typedef struct Soc Soc; 32 | typedef struct Uart Uart; 33 | typedef struct Ureg Ureg; 34 | typedef uvlong Tval; 35 | typedef void KMap; 36 | 37 | #pragma incomplete Pcidev 38 | #pragma incomplete Ureg 39 | 40 | #define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */ 41 | 42 | /* 43 | * parameters for sysproc.c 44 | */ 45 | #define AOUT_MAGIC (R_MAGIC) 46 | 47 | struct Lock 48 | { 49 | ulong key; 50 | u32int sr; 51 | uintptr pc; 52 | Proc* p; 53 | Mach* m; 54 | int isilock; 55 | }; 56 | 57 | struct Label 58 | { 59 | uintptr sp; 60 | uintptr pc; 61 | }; 62 | 63 | struct FPsave 64 | { 65 | uvlong regs[32][2]; 66 | 67 | ulong control; 68 | ulong status; 69 | }; 70 | 71 | struct PFPU 72 | { 73 | FPsave fpsave[1]; 74 | 75 | int fpstate; 76 | }; 77 | 78 | enum 79 | { 80 | FPinit, 81 | FPactive, 82 | FPinactive, 83 | 84 | /* bits or'd with the state */ 85 | FPillegal= 0x100, 86 | }; 87 | 88 | struct Confmem 89 | { 90 | uintptr base; 91 | ulong npage; 92 | uintptr limit; 93 | uintptr kbase; 94 | uintptr klimit; 95 | }; 96 | 97 | struct Conf 98 | { 99 | ulong nmach; /* processors */ 100 | ulong nproc; /* processes */ 101 | Confmem mem[4]; /* physical memory */ 102 | ulong npage; /* total physical pages of memory */ 103 | ulong upages; /* user page pool */ 104 | ulong copymode; /* 0 is copy on write, 1 is copy on reference */ 105 | ulong ialloc; /* max interrupt time allocation in bytes */ 106 | ulong pipeqsize; /* size in bytes of pipe queues */ 107 | ulong nimage; /* number of page cache image headers */ 108 | ulong nswap; /* number of swap pages */ 109 | int nswppo; /* max # of pageouts per segment pass */ 110 | ulong hz; /* processor cycle freq */ 111 | ulong mhz; 112 | int monitor; /* flag */ 113 | }; 114 | 115 | /* 116 | * MMU stuff in Mach. 117 | */ 118 | struct MMMU 119 | { 120 | PTE* mmutop; /* first level user page table */ 121 | }; 122 | 123 | /* 124 | * MMU stuff in proc 125 | */ 126 | #define NCOLOR 1 /* 1 level cache, don't worry about VCE's */ 127 | 128 | struct PMMU 129 | { 130 | union { 131 | Page *mmufree; /* mmuhead[0] is freelist head */ 132 | Page *mmuhead[PTLEVELS]; 133 | }; 134 | Page *mmutail[PTLEVELS]; 135 | int asid; 136 | uintptr tpidr; 137 | }; 138 | 139 | #include "../port/portdat.h" 140 | 141 | struct Mach 142 | { 143 | int machno; /* physical id of processor */ 144 | uintptr splpc; /* pc of last caller to splhi */ 145 | Proc* proc; /* current process on this processor */ 146 | /* end of offsets known to asm */ 147 | 148 | MMMU; 149 | 150 | PMach; 151 | 152 | int cputype; 153 | ulong delayloop; 154 | int cpumhz; 155 | uvlong cpuhz; /* speed of cpu */ 156 | 157 | int stack[1]; 158 | }; 159 | 160 | struct 161 | { 162 | char machs[MAXMACH]; /* active CPUs */ 163 | int exiting; /* shutdown */ 164 | }active; 165 | 166 | #define MACHP(n) ((Mach*)MACHADDR(n)) 167 | 168 | extern register Mach* m; /* R27 */ 169 | extern register Proc* up; /* R26 */ 170 | extern int normalprint; 171 | 172 | /* 173 | * a parsed plan9.ini line 174 | */ 175 | #define NISAOPT 8 176 | 177 | struct ISAConf { 178 | char *type; 179 | uvlong port; 180 | int irq; 181 | ulong dma; 182 | ulong mem; 183 | ulong size; 184 | ulong freq; 185 | 186 | int nopt; 187 | char *opt[NISAOPT]; 188 | }; 189 | 190 | /* 191 | * Horrid. But the alternative is 'defined'. 192 | */ 193 | #ifdef _DBGC_ 194 | #define DBGFLG (dbgflg[_DBGC_]) 195 | #else 196 | #define DBGFLG (0) 197 | #endif /* _DBGC_ */ 198 | 199 | int vflag; 200 | extern char dbgflg[256]; 201 | 202 | #define dbgprint print /* for now */ 203 | 204 | /* 205 | * hardware info about a device 206 | */ 207 | typedef struct { 208 | ulong port; 209 | int size; 210 | } Devport; 211 | 212 | struct DevConf 213 | { 214 | ulong intnum; /* interrupt number */ 215 | char *type; /* card type, malloced */ 216 | int nports; /* Number of ports */ 217 | Devport *ports; /* The ports themselves */ 218 | }; 219 | 220 | struct Soc { /* SoC dependent configuration */ 221 | ulong dramsize; 222 | uintptr busdram; 223 | ulong iosize; 224 | uintptr physio; 225 | uintptr virtio; 226 | }; 227 | extern Soc soc; 228 | -------------------------------------------------------------------------------- /devarch.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "../port/lib.h" 3 | #include "mem.h" 4 | #include "dat.h" 5 | #include "fns.h" 6 | #include "../port/error.h" 7 | #include "io.h" 8 | 9 | enum { 10 | Qdir = 0, 11 | Qbase, 12 | 13 | Qmax = 16, 14 | }; 15 | 16 | typedef long Rdwrfn(Chan*, void*, long, vlong); 17 | 18 | static Rdwrfn *readfn[Qmax]; 19 | static Rdwrfn *writefn[Qmax]; 20 | 21 | static Dirtab archdir[Qmax] = { 22 | ".", { Qdir, 0, QTDIR }, 0, 0555, 23 | }; 24 | 25 | Lock archwlock; /* the lock is only for changing archdir */ 26 | QLock plock; 27 | int narchdir = Qbase; 28 | 29 | /* 30 | * Add a file to the #P listing. Once added, you can't delete it. 31 | * You can't add a file with the same name as one already there, 32 | * and you get a pointer to the Dirtab entry so you can do things 33 | * like change the Qid version. Changing the Qid path is disallowed. 34 | */ 35 | Dirtab* 36 | addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) 37 | { 38 | int i; 39 | Dirtab d; 40 | Dirtab *dp; 41 | 42 | memset(&d, 0, sizeof d); 43 | strcpy(d.name, name); 44 | d.perm = perm; 45 | 46 | lock(&archwlock); 47 | if(narchdir >= Qmax){ 48 | unlock(&archwlock); 49 | return nil; 50 | } 51 | 52 | for(i=0; iqid.path){ 103 | case Qdir: 104 | return devdirread(c, a, n, archdir, narchdir, devgen); 105 | 106 | default: 107 | if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) 108 | return fn(c, a, n, offset); 109 | error(Eperm); 110 | break; 111 | } 112 | 113 | return 0; 114 | } 115 | 116 | static long 117 | archwrite(Chan *c, void *a, long n, vlong offset) 118 | { 119 | Rdwrfn *fn; 120 | 121 | if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) 122 | return fn(c, a, n, offset); 123 | error(Eperm); 124 | 125 | return 0; 126 | } 127 | 128 | void archinit(void); 129 | 130 | Dev archdevtab = { 131 | 'P', 132 | "arch", 133 | 134 | devreset, 135 | archinit, 136 | devshutdown, 137 | archattach, 138 | archwalk, 139 | archstat, 140 | archopen, 141 | devcreate, 142 | archclose, 143 | archread, 144 | devbread, 145 | archwrite, 146 | devbwrite, 147 | devremove, 148 | devwstat, 149 | }; 150 | 151 | static long 152 | keyadcread(Chan*, void *a, long n, vlong offset) 153 | { 154 | char str[32]; 155 | uint v = getkeyadc(); 156 | snprint(str, sizeof str, "%ud\n", v); 157 | return readstr(offset, a, n, str); 158 | } 159 | 160 | static long 161 | keyadceventread(Chan*, void *a, long n, vlong offset) 162 | { 163 | char str[32]; 164 | int l; 165 | char *s = getkeyadc_event(); 166 | uint v = getkeyadc(); 167 | snprint(str, sizeof str, "%s %ud\n", s, v); 168 | l = readstr(offset, a, n, str); 169 | return n; 170 | } 171 | 172 | static long 173 | pllgatesread(Chan*, void *a, long n, vlong offset) 174 | { 175 | char *p, *g; 176 | 177 | int i, l, s; 178 | 179 | p = smalloc(READSTR); 180 | 181 | l = 0; 182 | qlock(&plock); 183 | for(i = 0; i < 45; i++){ 184 | g = getgatename(i); 185 | if(g == nil) 186 | continue; 187 | s = getgatestate(i); 188 | l += snprint(p+l, READSTR-l, "%s = %d\n", g, s); 189 | } 190 | 191 | n = readstr(offset, a, n, p); 192 | free(p); 193 | qunlock(&plock); 194 | return n; 195 | } 196 | 197 | static long 198 | pllresetread(Chan*, void *a, long n, vlong offset) 199 | { 200 | char *p, *g; 201 | 202 | int i, l, s; 203 | 204 | p = smalloc(READSTR); 205 | 206 | l = 0; 207 | qlock(&plock); 208 | for(i = 0; i < 45; i++){ 209 | g = getresetname(i); 210 | if(g == nil) 211 | continue; 212 | s = getresetstate(i); 213 | l += snprint(p+l, READSTR-l, "%s = %d\n", g, s); 214 | } 215 | 216 | n = readstr(offset, a, n, p); 217 | free(p); 218 | qunlock(&plock); 219 | return n; 220 | } 221 | 222 | 223 | static long 224 | cpuclkread(Chan*, void *a, long z, vlong offset) 225 | { 226 | char str[128]; 227 | 228 | u32int n, k, m, p, mhz; 229 | 230 | n = getcpuclk_n(); 231 | k = getcpuclk_k(); 232 | m = getcpuclk_m(); 233 | p = getcpuclk_p(); 234 | mhz = (24*(n+1)*(k+1))/((m+1)*(p+1)); 235 | 236 | snprint(str, sizeof str, "n=%d k=%d m=%d p=%d %dMHz\n", n, k, m, p, mhz); 237 | z = readstr(offset, a, n, str); 238 | return z; 239 | } 240 | 241 | static long 242 | cpuclkwrite(Chan*, void *a, long z, vlong offset) 243 | { 244 | Cmdbuf *cb; 245 | u32int v; 246 | 247 | cb = parsecmd(a, z); 248 | if(waserror()){ 249 | free(cb); 250 | nexterror(); 251 | } 252 | 253 | v = atoi(cb->f[0]); 254 | 255 | if(v < 0 || v > 31) 256 | error("must be an integer from 0 to 31"); 257 | 258 | if(setcpuclk_n((u32int)v) != 1) 259 | error("writing to clock failed"); 260 | 261 | free(cb); 262 | USED(offset); 263 | poperror(); 264 | return z; 265 | } 266 | 267 | static long 268 | cputempread(Chan*, void *a, long n, vlong offset) 269 | { 270 | char str[128]; 271 | int α, β, γ; 272 | α = gettemp0(); 273 | β = gettemp1(); 274 | γ = gettemp2(); 275 | 276 | snprint(str, sizeof(str), "%d\n%d\n%d\n",α ,β ,γ); 277 | return readstr(offset, a, n, str); 278 | } 279 | 280 | static long 281 | irqenread(Chan*, void *a, long n, vlong offset) 282 | { 283 | char *p; 284 | 285 | int i, l, s; 286 | 287 | p = smalloc(READSTR); 288 | 289 | l = 0; 290 | qlock(&plock); 291 | for(i = 0; i <= 160; i++){ 292 | if(isintrenable(i)) 293 | l += snprint(p+l, READSTR-l, "%d\n", i); 294 | } 295 | 296 | n = readstr(offset, a, n, p); 297 | free(p); 298 | qunlock(&plock); 299 | return n; 300 | } 301 | 302 | static long 303 | irqpendread(Chan*, void *a, long n, vlong offset) 304 | { 305 | char *p; 306 | 307 | int i, l, s; 308 | 309 | p = smalloc(READSTR); 310 | 311 | l = 0; 312 | qlock(&plock); 313 | for(i = 0; i <= 160; i++){ 314 | if(isintrpending(i)) 315 | l += snprint(p+l, READSTR-l, "%d\n", i); 316 | } 317 | 318 | n = readstr(offset, a, n, p); 319 | free(p); 320 | qunlock(&plock); 321 | return n; 322 | } 323 | 324 | static long 325 | irqactread(Chan*, void *a, long n, vlong offset) 326 | { 327 | char *p; 328 | 329 | int i, l, s; 330 | 331 | p = smalloc(READSTR); 332 | 333 | l = 0; 334 | qlock(&plock); 335 | for(i = 0; i <= 160; i++){ 336 | if(isintractive(i)) 337 | l += snprint(p+l, READSTR-l, "%d\n", i); 338 | } 339 | 340 | n = readstr(offset, a, n, p); 341 | free(p); 342 | qunlock(&plock); 343 | return n; 344 | } 345 | 346 | static long 347 | pmicread(Chan*, void *a, long n, vlong offset) 348 | { 349 | char *p, *name; 350 | int i, l, s; 351 | 352 | p = smalloc(READSTR); 353 | 354 | l = 0; 355 | qlock(&plock); 356 | 357 | l += snprint(p+l, READSTR-l, "PMIC ID = %ub\n", pmic_id()); 358 | l += snprint(p+l, READSTR-l, "ACIN = %s\n", pmic_acin() ? "on" : "off"); 359 | l += snprint(p+l, READSTR-l, "VBAT = %s\n", pmic_vbat() ? "on" : "off"); 360 | 361 | i = 0; 362 | while((name = getpmicname(i)) != nil){ 363 | l += snprint(p+l, READSTR-l, "%s\t%s\t%dmV\n", name, getpmicstate(i) ? "on" : "off", getpmicvolt(i)); 364 | i++; 365 | } 366 | 367 | n = readstr(offset, a, n, p); 368 | free(p); 369 | qunlock(&plock); 370 | return n; 371 | } 372 | 373 | 374 | static long 375 | pmicwrite(Chan*, void *a, long z, vlong offset) 376 | { 377 | Cmdbuf *cb; 378 | char *name, *cmd; 379 | int state = -1; 380 | int volt; 381 | 382 | cb = parsecmd(a, z); 383 | if(waserror()){ 384 | free(cb); 385 | nexterror(); 386 | } 387 | 388 | volt = atoi(cb->f[1]); 389 | 390 | name = cb->f[0]; 391 | 392 | cmd = cb->f[1]; 393 | 394 | if(volt >= 500){ 395 | if(setpmicvolt(name, volt) != 1) 396 | error("volt: no such rail"); 397 | }else if(volt == 0){ 398 | if(strcmp(cmd, "on") == 0) 399 | state = 1; 400 | 401 | if(strcmp(cmd, "off") == 0) 402 | state = 0; 403 | 404 | if(state > -1) 405 | if(setpmicstate(name, state) != 1) 406 | error("state: no such rail"); 407 | }else{ 408 | error("bad command"); 409 | } 410 | 411 | 412 | free(cb); 413 | USED(offset); 414 | poperror(); 415 | return z; 416 | } 417 | 418 | 419 | void 420 | archinit(void) 421 | { 422 | keyadcinit(); 423 | thermalinit(); 424 | rsbinit(); 425 | 426 | addarchfile("keyadc", 0444, keyadcread, nil); 427 | addarchfile("keyadc_event", 0444, keyadceventread, nil); 428 | addarchfile("pllgates", 0444, pllgatesread, nil); 429 | addarchfile("pllreset", 0444, pllresetread, nil); 430 | addarchfile("cpuclk", 0664, cpuclkread, cpuclkwrite); 431 | addarchfile("cputemp", 0444, cputempread, nil); 432 | addarchfile("irqen", 0444, irqenread, nil); 433 | addarchfile("irqpend", 0444, irqpendread, nil); 434 | addarchfile("irqact", 0444, irqactread, nil); 435 | addarchfile("pmic", 0664, pmicread, pmicwrite); 436 | } 437 | 438 | -------------------------------------------------------------------------------- /devrtc.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "../port/lib.h" 3 | #include "io.h" 4 | #include "mem.h" 5 | #include "dat.h" 6 | #include "fns.h" 7 | #include "../port/error.h" 8 | 9 | #define RTC_YYMMDD (0x10) 10 | #define RTC_HHMMSS (0x14) 11 | 12 | #define LOSC_CTRL_REG (0x00) 13 | #define LOSC_OUT_GATING_REG (0x60) 14 | 15 | #define INTOSC_CLK_PRESCAL_REG (0x8) 16 | 17 | #define BITS(n) ((1<> 0) & BITS(5); 66 | rtc.mon = (dreg >> 8) & BITS(4); 67 | rtc.year = (dreg >> 16) & BITS(6); 68 | 69 | rtc.sec = (treg >> 0) & BITS(6); 70 | rtc.min = (treg >> 8) & BITS(6); 71 | rtc.hour = (treg >> 16) & BITS(5); 72 | 73 | rtc.year += 2010; 74 | 75 | return rtc2sec(&rtc); 76 | } 77 | 78 | enum { 79 | Qdir = 0, 80 | Qrtc, 81 | }; 82 | 83 | Dirtab rtcdir[] = { 84 | ".", {Qdir, 0, QTDIR}, 0, 0555, 85 | "rtc", {Qrtc, 0}, 0, 0664, 86 | }; 87 | 88 | long 89 | rtctime(void) 90 | { 91 | int i; 92 | long t, ot; 93 | 94 | t = readtime(); 95 | for (i = 0; i < 100; i++) { 96 | ot = t; 97 | t = readtime(); 98 | if (ot == t) 99 | break; 100 | } 101 | if (i == 100) 102 | print("we are boofheads\n"); 103 | 104 | return t; 105 | } 106 | 107 | static long 108 | rtcread(Chan *c, void *buf, long n, vlong off) 109 | { 110 | ulong offset = off; 111 | 112 | if (c->qid.type & QTDIR) 113 | return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); 114 | 115 | switch ((ulong)c->qid.path) { 116 | case Qrtc: 117 | return readnum(offset, buf, n, rtctime(), 12); 118 | } 119 | error(Ebadarg); 120 | return 0; 121 | } 122 | 123 | static long 124 | rtcwrite(Chan *c, void *buf, long n, vlong off) 125 | { 126 | Rtc rtc; 127 | ulong secs; 128 | ulong num; 129 | uint leap = 0; 130 | char *cp, sbuf[32]; 131 | ulong offset = off; 132 | 133 | if (offset != 0) 134 | error(Ebadarg); 135 | 136 | switch((ulong)c->qid.path) { 137 | case Qrtc: 138 | if (n >= sizeof(sbuf)) 139 | error(Ebadarg); 140 | strncpy(sbuf, buf, n); 141 | sbuf[n] = '\0'; 142 | for (cp = sbuf; *cp != '\0'; cp++) 143 | if (*cp >= '0' && *cp <= '9') 144 | break; 145 | secs = strtoul(cp, 0, 0); 146 | 147 | sec2rtc(secs, &rtc); 148 | 149 | /* leap year */ 150 | if (yrsize(rtc.year) == ldmsize) 151 | leap = 1; 152 | 153 | rtc.year -= 2010; 154 | 155 | num = (leap << 22) 156 | | (((rtc.mday) & BITS(5)) << 0) 157 | | (((rtc.mon) & BITS(4)) << 8) 158 | | (((rtc.year) & BITS(6)) << 16); 159 | *IO(ulong, (RTC + RTC_YYMMDD)) = num; 160 | 161 | while (*IO(u32int, (RTC + LOSC_CTRL_REG)) & (1 << 7)) 162 | ; 163 | 164 | num = 0 165 | | ((rtc.sec & BITS(6)) << 0) 166 | | ((rtc.min & BITS(6)) << 8) 167 | | ((rtc.hour & BITS(5)) << 16) 168 | | ((rtc.wday & BITS(3)) << 29); 169 | *IO(u32int, (RTC + RTC_HHMMSS)) = num; 170 | 171 | while (*IO(u32int, (RTC + LOSC_CTRL_REG)) & (1 << 8)) 172 | ; 173 | 174 | return n; 175 | } 176 | 177 | error(Ebadarg); 178 | return 0; 179 | } 180 | 181 | static Chan* 182 | rtcattach(char *spec) 183 | { 184 | u32int ctrl = 0x16aa << 16; // magic KEY_FIELD 185 | ctrl |= 1 << 14; // LOSC_AUTO_SWT_EN 186 | *IO(u32int, (RTC + LOSC_CTRL_REG)) = ctrl; 187 | 188 | *IO(u32int, (RTC + LOSC_OUT_GATING_REG)) = 0x0; 189 | *IO(u32int, (RTC + INTOSC_CLK_PRESCAL_REG)) = 0xf; 190 | 191 | return devattach('r', spec); 192 | } 193 | 194 | static Walkqid* 195 | rtcwalk(Chan *c, Chan *nc, char **name, int nname) 196 | { 197 | return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); 198 | } 199 | 200 | static int 201 | rtcstat(Chan *c, uchar *dp, int n) 202 | { 203 | return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); 204 | } 205 | 206 | static Chan* 207 | rtcopen(Chan *c, int omode) 208 | { 209 | omode = openmode(omode); 210 | switch ((ulong)c->qid.path) { 211 | case Qrtc: 212 | if (strcmp(up->user, eve) != 0 && omode != OREAD) 213 | error(Eperm); 214 | break; 215 | } 216 | return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); 217 | } 218 | 219 | static void 220 | rtcclose(Chan*) 221 | { 222 | } 223 | 224 | Dev rtcdevtab = { 225 | 'r', 226 | "rtc", 227 | 228 | devreset, 229 | devinit, 230 | devshutdown, 231 | rtcattach, 232 | rtcwalk, 233 | rtcstat, 234 | rtcopen, 235 | devcreate, 236 | rtcclose, 237 | rtcread, 238 | devbread, 239 | rtcwrite, 240 | devbwrite, 241 | devremove, 242 | devwstat, 243 | }; 244 | 245 | static ulong 246 | rtc2sec(Rtc *rtc) 247 | { 248 | ulong secs; 249 | int i; 250 | int *d2m; 251 | 252 | secs = 0; 253 | 254 | /* 255 | * seconds per year 256 | */ 257 | for(i = 1970; i < rtc->year; i++){ 258 | d2m = yrsize(i); 259 | secs += d2m[0] * SEC2DAY; 260 | } 261 | 262 | /* 263 | * seconds per month 264 | */ 265 | d2m = yrsize(rtc->year); 266 | for(i = 1; i < rtc->mon; i++) 267 | secs += d2m[i] * SEC2DAY; 268 | 269 | secs += (rtc->mday-1) * SEC2DAY; 270 | secs += rtc->hour * SEC2HOUR; 271 | secs += rtc->min * SEC2MIN; 272 | secs += rtc->sec; 273 | 274 | return secs; 275 | } 276 | 277 | static void 278 | sec2rtc(ulong secs, Rtc *rtc) 279 | { 280 | int d; 281 | long hms, day; 282 | int *d2m; 283 | 284 | /* 285 | * break initial number into days 286 | */ 287 | hms = secs % SEC2DAY; 288 | day = secs / SEC2DAY; 289 | if(hms < 0) { 290 | hms += SEC2DAY; 291 | day -= 1; 292 | } 293 | 294 | /* 295 | * 19700101 was thursday 296 | */ 297 | rtc->wday = (day + 7340036L) % 7; 298 | 299 | /* 300 | * generate hours:minutes:seconds 301 | */ 302 | rtc->sec = hms % 60; 303 | d = hms / 60; 304 | rtc->min = d % 60; 305 | d /= 60; 306 | rtc->hour = d; 307 | 308 | /* 309 | * year number 310 | */ 311 | if(day >= 0) 312 | for(d = 1970; day >= *yrsize(d); d++) 313 | day -= *yrsize(d); 314 | else 315 | for (d = 1970; day < 0; d--) 316 | day += *yrsize(d-1); 317 | rtc->year = d; 318 | 319 | /* 320 | * generate month 321 | */ 322 | d2m = yrsize(rtc->year); 323 | for(d = 1; day >= d2m[d]; d++) 324 | day -= d2m[d]; 325 | rtc->mday = day + 1; 326 | rtc->mon = d; 327 | 328 | return; 329 | } 330 | 331 | -------------------------------------------------------------------------------- /ethersunxi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Ethernet for the Allwinner A64 (sun50i) 3 | */ 4 | 5 | #include "u.h" 6 | #include "../port/lib.h" 7 | #include "mem.h" 8 | #include "dat.h" 9 | #include "fns.h" 10 | #include "io.h" 11 | #include "../port/netif.h" 12 | #include "../port/etherif.h" 13 | #include "../port/ethermii.h" 14 | 15 | 16 | 17 | 18 | #define ETH_BASIC_CTL_0 0x00 /*Basic Control 0 Register*/ 19 | #define ETH_BASIC_CTL_1 0x04 /*Basic Control 1 Register*/ 20 | #define ETH_INT_STA 0x08 /*Interrupt Status Register*/ 21 | #define ETH_INT_EN 0x0C /*Interrupt Enable Register*/ 22 | #define ETH_TX_CTL_0 0x10 /*Transmit Control 0 Register*/ 23 | #define ETH_TX_CTL_1 0x14 /*Transmit Control 1 Register*/ 24 | #define ETH_TX_FLOW_CTL 0x1C /*Transmit Flow Control Register*/ 25 | #define ETH_TX_DMA_DESC_LIST 0x20 /*Transmit Descriptor List Address Register*/ 26 | #define ETH_RX_CTL_0 0x24 /*Receive Control 0 Register*/ 27 | #define ETH_RX_CTL_1 0x28 /*Receive Control 1 Register*/ 28 | #define ETH_RX_DMA_DESC_LIST 0x34 /*Receive Descriptor List Address Register*/ 29 | #define ETH_RX_FRM_FLT 0x38 /*Receive Frame Filter Register*/ 30 | #define ETH_RX_HASH_0 0x40 /*Hash Table 0 Register*/ 31 | #define ETH_RX_HASH_1 0x44 /*Hash Table 1 Register*/ 32 | #define ETH_MII_CMD 0x48 /*Management Interface Command Register*/ 33 | #define ETH_MII_DATA 0x4C /*Management Interface Data Register*/ 34 | #define ETH_ADDR_HIGH_0 0x50 /*MAC Address High Register 0*/ 35 | #define ETH_ADDR_LOW_0 0x54 /*MAC Address High Register 0*/ 36 | #define ETH_TX_DMA_STA 0xB0 /*Transmit DMA Status Register*/ 37 | #define ETH_TX_CUR_DESC 0xB4 /*Current Transmit Descriptor Register*/ 38 | #define ETH_TX_CUR_BUF 0xB8 /*Current Transmit Buffer Address Register*/ 39 | #define ETH_RX_DMA_STA 0xC0 /*Receive DMA Status Register*/ 40 | #define ETH_RX_CUR_DESC 0xC4 /*Current Receive Descriptor Register*/ 41 | #define ETH_RX_CUR_BUF 0xC8 /*Current Receive Buffer Addres*/ 42 | #define ETH_RGMII_STA 0xD0 /*RGMII Status Register*/ 43 | 44 | #define EMAC_CLK_REG 0x30 /*in System Control*/ 45 | 46 | /* Satatus Descriptor */ 47 | /* TX */ 48 | #define TX_DESC_CTL (1 << 31) 49 | #define TX_HEADER_ERR (1 << 16) 50 | #define TX_LENGTH_ERR (1 << 14) 51 | #define TX_PAYLOAD_ERR (1 << 12) 52 | #define TX_CRS_ERR (1 << 10) 53 | #define TX_COL_ERR_0 (1 << 9) 54 | #define TX_COL_ERR_1 (1 << 8) 55 | #define TX_COL_CNT (0xf << 3) 56 | #define TX_COL_CNT_SHIFT 3 57 | #define TX_DEFER_ERR (1 << 2) 58 | #define TX_UNDERFLOW_ERR (1 << 1) 59 | #define TX_DEFER (1 << 0) 60 | /* RX */ 61 | #define RX_DESC_CTL (1 << 31) 62 | #define RX_DAF_FAIL (1 << 30) 63 | #define RX_FRM_LEN (0x3fff << 16) 64 | #define RX_FRM_LEN_SHIFT 16 65 | #define RX_NO_ENOUGH_BUF_ERR (1 << 14) 66 | #define RX_SAF_FAIL (1 << 13) 67 | #define RX_OVERFLOW_ERR (1 << 11) 68 | #define RX_FIR_DESC (1 << 9) 69 | #define RX_LAST_DESC (1 << 8) 70 | #define RX_HEADER_ERR (1 << 7) 71 | #define RX_COL_ERR (1 << 6) 72 | #define RX_FRM_TYPE (1 << 5) 73 | #define RX_LENGTH_ERR (1 << 4) 74 | #define RX_PHY_ERR (1 << 3) 75 | #define RX_CRC_ERR (1 << 1) 76 | #define RX_PAYLOAD_ERR (1 << 0) 77 | 78 | /* Size Descriptor */ 79 | /* TX */ 80 | #define TX_INT_CTL (1 << 31) 81 | #define TX_LAST_DESC (1 << 30) 82 | #define TX_FIR_DESC (1 << 29) 83 | #define TX_CHECKSUM_CTL (0x3 << 27) 84 | #define TX_CHECKSUM_CTL_IP 1 85 | #define TX_CHECKSUM_CTL_NO_PSE 2 86 | #define TX_CHECKSUM_CTL_FULL 3 87 | #define TX_CHECKSUM_CTL_SHIFT 27 88 | #define TX_CRC_CTL (1 << 26) 89 | #define TX_BUF_SIZE (0x7ff << 0) 90 | #define TX_BUF_SIZE_SHIFT 0 91 | /* RX */ 92 | #define RX_INT_CTL (1 << 31) 93 | #define RX_CHAIN_DESC (1 << 24) 94 | #define RX_BUF_SIZE (0x7ff << 0) 95 | #define RX_BUF_SIZE_SHIFT 0 96 | 97 | 98 | /* ETH_BASIC_CTL_0 */ 99 | enum { 100 | CtlSpeedShift = 2, 101 | CtlSpeed10 = 2<<2, 102 | CtlSpeed100 = 3<<2, 103 | CtlSpeed1000 = 0<<2, 104 | CtlLoopback = 1<<1, 105 | CtlDuplex = 1<<0, 106 | }; 107 | 108 | /* ETH_BASIC_CTL_1 */ 109 | enum { 110 | CtlBurstLen = 0x3F<<24, 111 | CtlBurstShift = 24, 112 | CtlRxPriortiy = 1<<1, 113 | CtlSoftRst = 1<<0, 114 | }; 115 | 116 | /* ETH_INT_STA/EN */ 117 | enum { 118 | RGMIILinkSta = 1<<16, 119 | RXEarly = 1<<13, 120 | RXOverflow = 1<<12, 121 | RXTimeout = 1<<11, 122 | RXDMAStopped = 1<<10, 123 | RXBufUa = 1<<9, 124 | RXInt = 1<<8, 125 | TXEarly = 1<<5, 126 | TXUnderflow = 1<<4, 127 | TXTimeout = 1<<3, 128 | TXBufUa = 1<<2, 129 | TXDMAStopped = 1<<1, 130 | TXInt = 1<<0, 131 | }; 132 | 133 | /* ETH_TX_CTL_0 */ 134 | enum { 135 | TXEn = 1<<31, 136 | TXFrmLen = 1<<30, 137 | }; 138 | 139 | /* ETH_TX_CTL_1 */ 140 | enum { 141 | TXDMAStart = 1<<31, 142 | TXDMAEn = 1<<30, 143 | TX_TH = 0x7<<8, 144 | TXNextFrm = 1<<2, 145 | TX_MD = 1<<1, 146 | FlushTXFIFO = 1<<0, 147 | }; 148 | 149 | /* ETH_TX_FLOW_CTL */ 150 | enum { 151 | PauseTime = 0xFFFF<<4, 152 | PauseTimeShift = 4, 153 | TXFlowCtlEn = 1<<0, 154 | }; 155 | 156 | /* ETH_RX_CTL_0 */ 157 | enum { 158 | RXEn = 1<<31, 159 | RXFrmLen = 1<<30, 160 | JumboFrmEn = 1<<29, 161 | StripFCS = 1<<28, 162 | CheckCRC = 1<<27, 163 | RXPauseRrm = 1<<17, 164 | RXFlowCtlEn = 1<<16, 165 | }; 166 | 167 | /* ETH_RX_CTL_1 */ 168 | enum { 169 | RXDMAStart = 1<<31, 170 | RXDMAEn = 1<<30, 171 | RXFlowCtl = 1<<24, 172 | RXFlowDeact = 0x3<<22, 173 | RXFlowAct = 0x3<<20, 174 | RX_TH = 0x3<<4, 175 | RXErrFrm = 1<<3, 176 | RXRuntFrm = 1<<2, 177 | RX_MD = 1<<1, 178 | FlushRXFrm = 1<<0, 179 | }; 180 | 181 | /* ETH_RX_FRM_FLT */ 182 | enum { 183 | DisAddrFlt = 1<<31, 184 | DisBroadcast = 1<<17, 185 | RXMulticast = 1<<16, 186 | CtlFrmFlt = 0x3<<12, 187 | CtlFrmFltShift = 12, 188 | HashMulti = 1<<9, 189 | HashUni = 1<<8, 190 | SAFltEn = 1<<6, 191 | SAInvFlt = 1<<5, 192 | DAInvFlt = 1<<4, 193 | FltMD = 1<<1, 194 | RX_ALL = 1<<0, 195 | }; 196 | 197 | /* ETH_MII_CMD */ 198 | enum { 199 | DivRatio = 0x7<<20, 200 | DivRatio16 = 0, 201 | DivRatio32 = 1, 202 | DivRatio64 = 2, 203 | DivRatio128 = 3, 204 | DivRatioShift = 20, 205 | PhyAddr = 0x1F<<12, 206 | PhyAddrShift = 12, 207 | RegAddr = 0x1F<<4, 208 | RegAddrShift = 4, 209 | MiiWr = 1<<1, 210 | MiiBusy = 1<<0, 211 | }; 212 | 213 | /* EMAC_CLK_REG */ 214 | enum { 215 | ClkRmiiEn = 1<<13, 216 | ClkETXDC = 0x7<<10, 217 | ClkETXDCShift = 10, 218 | ClkERXDC = 0x1F<<5, 219 | ClkERXDCShift = 5, 220 | ClkPIT = 0x1<<2, 221 | ClkPITMII = 0<<2, 222 | ClkPITRGMII = 1<<2, 223 | ClkSrc = 0x3<<0, 224 | ClkSrcMII = 0<<0, 225 | ClkSrcExtRGMII = 1<<0, 226 | ClkSrcRGMII = 2<<0, 227 | }; 228 | 229 | /* TX desc status */ 230 | 231 | 232 | 233 | 234 | 235 | /* Debugging options */ 236 | enum{ 237 | Miidebug = 0, 238 | Ethdebug = 0, 239 | Attchbug = 1, 240 | }; 241 | 242 | 243 | enum{ 244 | Nrd = 256, /* Number rx descriptors */ 245 | Ntd = 256, /* Number tx descriptors */ 246 | Rbsz = 2048, /* block size */ 247 | RxBuf = 2044, /* rumors of problems at 2048 */ 248 | }; 249 | 250 | 251 | /* fine tuning for gigabit speed */ 252 | enum{ 253 | TxDelay = 0x3, 254 | RxDelay = 0x0, 255 | }; 256 | 257 | 258 | typedef struct Desc Desc; 259 | typedef struct Ctlr Ctlr; 260 | 261 | 262 | struct Desc 263 | { 264 | u32int status; 265 | u32int size; 266 | u32int addr; 267 | u32int next; 268 | }; 269 | 270 | 271 | struct Ctlr 272 | { 273 | int attached; 274 | QLock; 275 | Ether *edev; /* point back */ 276 | 277 | struct { 278 | Block *b[Nrd]; 279 | Desc *d; 280 | Rendez; 281 | Lock; 282 | } rx[1]; 283 | 284 | struct { 285 | Block *b[Ntd]; 286 | Desc *d; 287 | Rendez; 288 | Lock; 289 | } tx[1]; 290 | 291 | // Mii *mii; 292 | struct { 293 | Mii; 294 | int done; 295 | Rendez; 296 | } mii[1]; 297 | 298 | uint divratio; 299 | 300 | QLock statlock; 301 | int rxstat; 302 | int rxintr; 303 | int rxdmaerr; 304 | int txstat; 305 | int txintr; 306 | int txdmaerr; 307 | int nointr; 308 | int badrx; 309 | int anyintr; 310 | u32int rxring; 311 | u32int txring; 312 | }; 313 | 314 | 315 | static u32int 316 | sysconrd(int offset) 317 | { 318 | return *IO(u32int, (SYSCTL + offset)); 319 | } 320 | 321 | 322 | static void 323 | sysconwr(int offset, u32int val) 324 | { 325 | *IO(u32int, (SYSCTL + offset)) = val; 326 | } 327 | 328 | 329 | static u32int 330 | ethrd(int offset) 331 | { 332 | return *IO(u32int, (EMAC + offset)); 333 | } 334 | 335 | 336 | static void 337 | ethwr(int offset, u32int val) 338 | { 339 | *IO(u32int, (EMAC + offset)) = val; 340 | } 341 | 342 | 343 | static int 344 | miird(Mii *mii, int pa, int ra) 345 | { 346 | //int val = 0; 347 | int timeout; 348 | u32int buf; 349 | 350 | if(Miidebug) 351 | iprint("miird, phy_addr; %d phy_reg: %d", pa, ra); 352 | 353 | ethwr(ETH_MII_CMD, 354 | (DivRatio64 << DivRatioShift) | 355 | (pa << PhyAddrShift) | (ra << RegAddrShift) | MiiBusy); 356 | 357 | for(timeout = 0; timeout < 2000; timeout++){ 358 | if((ethrd(ETH_MII_CMD) & MiiBusy) == 0){ 359 | buf = ethrd(ETH_MII_DATA) & 0xFFFF; 360 | if(Miidebug) 361 | iprint(" - %ulX\n", buf); 362 | //return (ethrd(ETH_MII_DATA) & 0xFFFF); 363 | return buf; 364 | } 365 | microdelay(100); 366 | } 367 | 368 | /* read failed */ 369 | if(Miidebug) 370 | iprint("miird FAIL\n"); 371 | return -1; 372 | } 373 | 374 | 375 | static int 376 | miiwr(Mii *mii, int pa, int ra, int val) 377 | { 378 | int timeout; 379 | 380 | if(Miidebug) 381 | iprint("miiwr, phy_addr; %d phy_reg: %d val: 0x%04X\n", pa, ra, val); 382 | 383 | ethwr(ETH_MII_DATA, val); 384 | ethwr(ETH_MII_CMD, 385 | (DivRatio64 << DivRatioShift) | 386 | (pa << PhyAddrShift) | (ra << RegAddrShift) | 387 | MiiWr | MiiBusy); 388 | 389 | for(timeout = 0; timeout < 2000; timeout++){ 390 | if((ethrd(ETH_MII_CMD) & MiiBusy) == 0) 391 | return 0; 392 | microdelay(100); 393 | } 394 | 395 | /* write failed */ 396 | if(Miidebug) 397 | iprint("miiwr FAIL\n"); 398 | return -1; 399 | 400 | } 401 | 402 | 403 | static int 404 | rdfull(void *arg) 405 | { 406 | Desc *d = arg; 407 | return (d->status & RX_DESC_CTL) == 0; 408 | } 409 | 410 | 411 | static void 412 | rxproc(void *arg) 413 | { 414 | Ether *edev = arg; 415 | Ctlr *ctlr = edev->ctlr; 416 | Block *b; 417 | Desc *d; 418 | uint len, i; 419 | 420 | 421 | 422 | i = 0; 423 | 424 | while(waserror()) 425 | ; 426 | 427 | for(;;){ 428 | // i = (i + 1) % Nrd; 429 | ctlr->rxstat++; 430 | ctlr->rxring = PADDR(&ctlr->rx->d[i]); 431 | 432 | d = &ctlr->rx->d[i]; 433 | 434 | dmaflush(0, d, sizeof(Desc)); 435 | 436 | if((d->status & RX_DESC_CTL) != 0) 437 | sleep(ctlr->rx, rdfull, d); 438 | 439 | // ilock(ctlr->rx); /* helps with packet loss */ 440 | len = (d->status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; /* get length of packet */ 441 | b = ctlr->rx->b[i]; 442 | 443 | if(len > 0){ 444 | b->wp = b->rp + len; 445 | dmaflush(0, b->rp, BLEN(b)); /* move block to ram */ 446 | etheriq(edev, b); /* move block to ether input queue */ 447 | 448 | if(Ethdebug) 449 | iprint("rxproc: (%d) len=%d | ", i, len); 450 | 451 | } else { 452 | ctlr->badrx++; 453 | freeb(b); 454 | } 455 | 456 | /* replenish */ 457 | b = allocb(Rbsz); 458 | if(b == nil) 459 | panic("NO RX BLOCKS"); 460 | ctlr->rx->b[i] = b; 461 | dmaflush(1, b->rp, Rbsz); 462 | d->addr = PADDR(b->rp); /* point to fresh block */ 463 | d->status = RX_DESC_CTL; 464 | d->size = RxBuf; 465 | dmaflush(1, d, sizeof(Desc)); 466 | 467 | i = (i + 1) % Nrd; 468 | // iunlock(ctlr->rx); 469 | } 470 | } 471 | 472 | 473 | static int 474 | tdfree(void *arg) 475 | { 476 | Desc *d = arg; 477 | return (d->status & TX_DESC_CTL) == 0; 478 | } 479 | 480 | 481 | static void 482 | txproc(void *arg) 483 | { 484 | Ether *edev = arg; 485 | Ctlr *ctlr = edev->ctlr; 486 | Block *b; 487 | Desc *d; 488 | int i, len, Δlen; 489 | u32int buf; 490 | 491 | i = 0; 492 | 493 | while(waserror()) 494 | ; 495 | 496 | for(;;){ 497 | ctlr->txstat++; 498 | ctlr->txring = PADDR(&ctlr->tx->d[i]); 499 | if((b = qbread(edev->oq, 100000)) == nil) /* fetch packet from queue */ 500 | break; 501 | 502 | d = &ctlr->tx->d[i]; 503 | while(!tdfree(d)) 504 | sleep(ctlr->tx, tdfree, d); 505 | 506 | // ilock(ctlr->tx); /* helps with packet loss */ 507 | if(ctlr->tx->b[i] != nil) 508 | freeb(ctlr->tx->b[i]); 509 | 510 | ctlr->tx->b[i] = b; 511 | len = BLEN(b); 512 | 513 | if(Ethdebug) 514 | iprint("txproc: (%d) len=%d | ", i, len); 515 | 516 | dmaflush(1, b->rp, Rbsz); /* move packet to ram */ 517 | d->addr = PADDR(b->rp); 518 | d->size = (len & TX_BUF_SIZE) | TX_FIR_DESC | TX_LAST_DESC | TX_INT_CTL | 519 | (TX_CHECKSUM_CTL_IP << TX_CHECKSUM_CTL_SHIFT); 520 | d->status = TX_DESC_CTL; 521 | dmaflush(1, d, sizeof(Desc)); 522 | 523 | buf = ethrd(ETH_TX_CTL_1); 524 | if((buf & TXDMAStart) != 1) 525 | ethwr(ETH_TX_CTL_1, buf | TXDMAStart); 526 | 527 | i = (i + 1) % Ntd; 528 | // iunlock(ctlr->tx); 529 | } 530 | } 531 | 532 | 533 | static void 534 | linkproc(void *arg) 535 | { 536 | Ether *edev = arg; 537 | Ctlr *ctlr = edev->ctlr; 538 | MiiPhy *phy; 539 | int link = 0; 540 | u32int buf; 541 | 542 | while(waserror()) 543 | ; 544 | miiane(ctlr->mii, ~0, ~0, ~0); 545 | for(;;){ 546 | miistatus(ctlr->mii); 547 | phy = ctlr->mii->curphy; 548 | if(phy->link == link){ 549 | tsleep(ctlr->mii, return0, nil, 5000); 550 | continue; 551 | } 552 | link = phy->link; 553 | if(link){ 554 | /* Start Interface */ 555 | ethwr(ETH_BASIC_CTL_0, CtlSpeed100 | CtlDuplex); 556 | 557 | buf = ethrd(ETH_RX_CTL_0); 558 | buf |= RXFlowCtlEn; 559 | ethwr(ETH_RX_CTL_0, buf); 560 | 561 | buf = ethrd(ETH_TX_FLOW_CTL); 562 | buf &= ~(PauseTime | TXFlowCtlEn); 563 | buf |= TXFlowCtlEn; 564 | ethwr(ETH_TX_FLOW_CTL, buf); 565 | coherence(); 566 | 567 | edev->mbps = phy->speed; 568 | } 569 | edev->link = link; 570 | print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps); 571 | } 572 | } 573 | 574 | 575 | static void 576 | etherinterrupt(Ureg*, void *arg) 577 | { 578 | Ether *edev = arg; 579 | Ctlr *ctlr = edev->ctlr; 580 | u32int irq; 581 | int rxintΔ, txintΔ; 582 | 583 | ctlr->anyintr++; 584 | 585 | rxintΔ = ctlr->rxintr; 586 | txintΔ = ctlr->txintr; 587 | 588 | irq = ethrd(ETH_INT_STA); /* get interrupt requests */ 589 | ethwr(ETH_INT_STA, irq); /* writing back 1's clears irqs */ 590 | 591 | if(Ethdebug){ 592 | iprint("ether interrupt: %08uX |", irq); 593 | delay(10); 594 | } 595 | 596 | 597 | if(irq & (RXInt | RXBufUa)){ 598 | ctlr->rxintr++; 599 | wakeup(ctlr->rx); 600 | } 601 | 602 | if(irq & (TXInt | TXBufUa)){ 603 | ctlr->txintr++; 604 | wakeup(ctlr->tx); 605 | } 606 | 607 | if((rxintΔ == ctlr->rxintr) && (txintΔ == ctlr->txintr)){ 608 | ctlr->nointr++; 609 | iprint("etherinterrupt: spurious %X\n", irq); 610 | } 611 | } 612 | 613 | 614 | 615 | static void 616 | getmacaddr(Ether *edev) 617 | { 618 | u32int msb, lsb; 619 | 620 | lsb = ethrd(ETH_ADDR_HIGH_0); 621 | msb = ethrd(ETH_ADDR_LOW_0); 622 | 623 | edev->ea[0] = msb>>8; 624 | edev->ea[1] = msb>>0; 625 | edev->ea[2] = lsb>>24; 626 | edev->ea[3] = lsb>>16; 627 | edev->ea[4] = lsb>>8; 628 | edev->ea[5] = lsb>>0; 629 | 630 | if(Attchbug){ 631 | iprint("ether getmac: %04lX %08lX\n", (msb & 0xFFFF), lsb); 632 | delay(10); 633 | } 634 | } 635 | 636 | 637 | static void 638 | setmacaddr(Ether *edev) 639 | { 640 | u32int msb, lsb; 641 | 642 | msb = edev->ea[1] | (edev->ea[0] << 8); 643 | lsb = edev->ea[5] | (edev->ea[4] << 8) 644 | | (edev->ea[3] << 16) | (edev->ea[2] << 24); 645 | 646 | ethwr(ETH_ADDR_LOW_0, msb); 647 | ethwr(ETH_ADDR_HIGH_0, lsb); 648 | } 649 | 650 | 651 | static int 652 | initmii(Ctlr *ctlr) 653 | { 654 | Mii *mii; 655 | MiiPhy *miiphy; 656 | Ether *edev = ctlr->edev; 657 | int i, buf; 658 | int bit, oui, phyno, rmask; 659 | u32int id; 660 | 661 | ctlr->mii->ctlr = ctlr; 662 | ctlr->mii->mir = miird; 663 | ctlr->mii->miw = miiwr; 664 | 665 | mii = ctlr->mii; 666 | 667 | phyno = 0; 668 | 669 | id = mii->mir(mii, phyno, Phyidr1) << 16; 670 | id |= mii->mir(mii, phyno, Phyidr2); 671 | oui = (id & 0x3FFFFC00)>>10; 672 | 673 | if((miiphy = malloc(sizeof(MiiPhy))) == nil) 674 | return -1; 675 | 676 | miiphy->mii = mii; 677 | miiphy->id = id; 678 | miiphy->oui = oui; 679 | miiphy->phyno = phyno; 680 | 681 | miiphy->anar = ~0; 682 | miiphy->fc = ~0; 683 | miiphy->mscr = ~0; 684 | 685 | mii->phy[phyno] = miiphy; 686 | if(mii->curphy == nil) 687 | mii->curphy = miiphy; 688 | mii->mask |= bit; 689 | mii->nphy++; 690 | 691 | 692 | if(ctlr->mii->curphy == nil){ 693 | iprint("#l%d: init mii failure\n", edev->ctlrno); 694 | free(miiphy); 695 | // ctlr->mii = nil; 696 | return -1; 697 | } 698 | 699 | iprint("#l%d: phy%d id %.8ux oui %x\n", 700 | edev->ctlrno, ctlr->mii->curphy->phyno, 701 | ctlr->mii->curphy->id, ctlr->mii->curphy->oui); 702 | 703 | miireset(ctlr->mii); 704 | 705 | miiane(ctlr->mii, ~0, ~0, ~0); 706 | miistatus(ctlr->mii); 707 | print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps); 708 | 709 | 710 | return 0; 711 | } 712 | 713 | 714 | static void 715 | setupclocks(Ctlr *ctlr) 716 | { 717 | u32int reg; 718 | 719 | /* 720 | * MDIO needs to talk at 2.5MHz, but 721 | * the EMAC runs off the AHB which 722 | * should default to 300MHz, so I'm 723 | * hard coding the 128 ratio for mii 724 | */ 725 | ctlr->divratio = DivRatio128; 726 | 727 | reg = sysconrd(EMAC_CLK_REG); 728 | 729 | // just 100Mb for now 730 | 731 | print("syscon %ulX\n", reg); 732 | 733 | reg &= ~(ClkPIT | ClkSrc | ClkRmiiEn); 734 | reg |= ClkPITRGMII | ClkSrcRGMII | 735 | (TxDelay << ClkETXDCShift) | (RxDelay << ClkERXDCShift); 736 | // reg |= ClkSrcMII | ClkPITMII; 737 | // reg |= ClkRmiiEn | ClkSrcExtRGMII; 738 | 739 | sysconwr(EMAC_CLK_REG, reg); 740 | } 741 | 742 | 743 | static void 744 | attach(Ether *edev) 745 | { 746 | int i, reset; 747 | u32int buf; 748 | Ctlr *ctlr; 749 | Desc *d; 750 | 751 | ctlr = edev->ctlr; 752 | 753 | if(Attchbug){ 754 | iprint("ether attach called\n"); 755 | delay(10); 756 | } 757 | 758 | qlock(ctlr); 759 | if(ctlr->attached){ 760 | qunlock(ctlr); 761 | 762 | if(Attchbug){ 763 | iprint("ether attach already?\n"); 764 | delay(10); 765 | } 766 | 767 | return; 768 | } 769 | 770 | if(waserror()){ 771 | qunlock(ctlr); 772 | 773 | if(Attchbug){ 774 | iprint("ether attach waserror?\n"); 775 | delay(10); 776 | } 777 | 778 | // free(ctlr->rx->d); 779 | // free(ctlr->tx->d); 780 | nexterror(); 781 | } 782 | 783 | print("clocks-"); 784 | setupclocks(ctlr); 785 | 786 | /* Allocate Rx/Tx ring with uncached memmory */ 787 | // ctlr->tx->d = ucalloc(sizeof(Desc) * Ntd); 788 | // ctlr->rx->d = ucalloc(sizeof(Desc) * Nrd); 789 | 790 | ctlr->tx->d = xspanalloc(sizeof(Desc) * Ntd, 32, 0); 791 | ctlr->rx->d = xspanalloc(sizeof(Desc) * Nrd, 32, 0); 792 | 793 | 794 | 795 | print("rxblks-"); 796 | /* Allocate Rx blocks, initialize Rx ring. */ 797 | for(i = 0; i < Nrd; i++){ 798 | Block *b = allocb(Rbsz); 799 | if(b == nil) 800 | error("rxblock"); 801 | ctlr->rx->b[i] = b; 802 | dmaflush(1, b->rp, Rbsz); 803 | d = &ctlr->rx->d[i]; 804 | d->addr = PADDR(b->rp); 805 | d->next = PADDR(&ctlr->rx->d[NEXT(i, Nrd)]); 806 | d->status = RX_DESC_CTL; 807 | d->size = RxBuf; 808 | } 809 | 810 | dmaflush(1, d, sizeof(Desc)); 811 | 812 | d = nil; 813 | 814 | /* Initialize Tx ring */ 815 | for(i = 0; i < Ntd; i++){ 816 | ctlr->tx->b[i] = nil; 817 | d = &ctlr->tx->d[i]; 818 | d->next = PADDR(&ctlr->tx->d[NEXT(i, Ntd)]); 819 | d->status = 0; 820 | d->size = 0; 821 | } 822 | 823 | dmaflush(1, d, sizeof(Desc)); 824 | 825 | print("reset-"); 826 | /* do a soft reset on the emac */ 827 | ethwr(ETH_BASIC_CTL_1, CtlSoftRst); 828 | 829 | /* wait for reset bit to clear */ 830 | for(reset = 1000; reset > 0; reset--){ 831 | if((ethrd(ETH_BASIC_CTL_1) & CtlSoftRst) == 0) 832 | break; 833 | delay(10); 834 | } 835 | 836 | if(reset == 0) 837 | print("RESET FAILED\n"); 838 | 839 | setmacaddr(edev); 840 | 841 | /* DMA Burst, 8 is default */ 842 | ethwr(ETH_BASIC_CTL_1, 8 << CtlBurstShift); 843 | 844 | /* feed in the descriptor rings */ 845 | ethwr(ETH_TX_DMA_DESC_LIST, PADDR(ctlr->tx->d)); 846 | ethwr(ETH_RX_DMA_DESC_LIST, PADDR(ctlr->rx->d)); 847 | coherence(); 848 | 849 | /* disable all filters, rinning in prom mode */ 850 | ethwr(ETH_RX_FRM_FLT, DisAddrFlt); 851 | 852 | /* Enable Interrupts */ 853 | ethwr(ETH_INT_EN, RXInt | RXBufUa | TXInt | TXBufUa); 854 | 855 | /* Setup DMA */ 856 | ethwr(ETH_RX_CTL_1, RXDMAEn | RX_MD); 857 | // ethwr(ETH_TX_CTL_1, TXDMAEn | TXNextFrm | TX_MD); 858 | // ethwr(ETH_RX_CTL_1, RXDMAEn | RXErrFrm | RXRuntFrm); 859 | ethwr(ETH_TX_CTL_1, TXDMAEn | TX_MD); 860 | 861 | /* Enable RX/TX */ 862 | ethwr(ETH_RX_CTL_0, RXEn | CheckCRC); 863 | ethwr(ETH_TX_CTL_0, TXEn); 864 | coherence(); 865 | 866 | print("mii-"); 867 | if(initmii(ctlr) < 0) 868 | error("mii failed"); 869 | 870 | /* Start Interface */ 871 | ethwr(ETH_BASIC_CTL_0, CtlSpeed100 | CtlDuplex); 872 | 873 | buf = ethrd(ETH_RX_CTL_0); 874 | buf |= RXFlowCtlEn; 875 | ethwr(ETH_RX_CTL_0, buf); 876 | 877 | buf = ethrd(ETH_TX_FLOW_CTL); 878 | buf &= ~(PauseTime | TXFlowCtlEn); 879 | buf |= TXFlowCtlEn; 880 | ethwr(ETH_TX_FLOW_CTL, buf); 881 | coherence(); 882 | 883 | // edev->mbps = phy->speed; 884 | 885 | 886 | ctlr->attached = 1; 887 | 888 | print("kprocs-"); 889 | kproc("ether-rx", rxproc, edev); 890 | kproc("ether-tx", txproc, edev); 891 | // kproc("ether-fr", frproc, edev); 892 | 893 | // kproc("ether-link", linkproc, edev); 894 | 895 | qunlock(ctlr); 896 | poperror(); 897 | } 898 | 899 | 900 | static void 901 | shutdown(Ether *edev) 902 | { 903 | USED(edev); 904 | } 905 | 906 | 907 | /* promiscuous stub */ 908 | static void 909 | prom(void*, int) 910 | { 911 | } 912 | 913 | 914 | /* multicast stub */ 915 | static void 916 | multi(void*, uchar*, int) 917 | { 918 | } 919 | 920 | 921 | static long 922 | ifstat(Ether* edev, void* a, long n, ulong offset) 923 | { 924 | char* p; 925 | Ctlr* ctlr; 926 | int l; 927 | Desc *t, *r; 928 | 929 | ctlr = edev->ctlr; 930 | 931 | p = smalloc(READSTR); 932 | l = 0; 933 | qlock(ctlr); 934 | l += snprint(p+l, READSTR-l, "tx: %d\n", ctlr->txstat); 935 | l += snprint(p+l, READSTR-l, "rx: %d\n", ctlr->rxstat); 936 | l += snprint(p+l, READSTR-l, "allint: %d\n", ctlr->anyintr); 937 | l += snprint(p+l, READSTR-l, "txintr: %d\n", ctlr->txintr); 938 | l += snprint(p+l, READSTR-l, "rxintr: %d\n", ctlr->rxintr); 939 | l += snprint(p+l, READSTR-l, "nointr: %d\n", ctlr->nointr); 940 | l += snprint(p+l, READSTR-l, "bad rx: %d\n", ctlr->badrx); 941 | l += snprint(p+l, READSTR-l, "\n"); 942 | l += snprint(p+l, READSTR-l, "dma errs: tx: %d rx: %d\n", ctlr->txdmaerr, ctlr->rxdmaerr); 943 | l += snprint(p+l, READSTR-l, "\n"); 944 | l += snprint(p+l, READSTR-l, "tx base: %08uX\n", ethrd(ETH_TX_DMA_DESC_LIST)); 945 | l += snprint(p+l, READSTR-l, "tx curr: %08uX\n", ethrd(ETH_TX_CUR_DESC)); 946 | l += snprint(p+l, READSTR-l, "tx ring: %08uX\n", ctlr->txring); 947 | l += snprint(p+l, READSTR-l, "tx buff: %08uX\n", ethrd(ETH_TX_CUR_BUF)); 948 | l += snprint(p+l, READSTR-l, "tx stat: %ud\n", ethrd(ETH_TX_DMA_STA)); 949 | l += snprint(p+l, READSTR-l, "\n"); 950 | l += snprint(p+l, READSTR-l, "rx base: %08uX\n", ethrd(ETH_RX_DMA_DESC_LIST)); 951 | l += snprint(p+l, READSTR-l, "rx curr: %08uX\n", ethrd(ETH_RX_CUR_DESC)); 952 | l += snprint(p+l, READSTR-l, "rx ring: %08uX\n", ctlr->rxring); 953 | l += snprint(p+l, READSTR-l, "rx buff: %08uX\n", ethrd(ETH_RX_CUR_BUF)); 954 | l += snprint(p+l, READSTR-l, "rx stat: %ud\n", ethrd(ETH_RX_DMA_STA)); 955 | l += snprint(p+l, READSTR-l, "\n"); 956 | l += snprint(p+l, READSTR-l, "INT STATUS: %08uX\n", ethrd(ETH_INT_STA)); 957 | l += snprint(p+l, READSTR-l, "INT MASK: %08uX\n", ethrd(ETH_INT_EN)); 958 | snprint(p+l, READSTR-l, "\n"); 959 | 960 | n = readstr(offset, a, n, p); 961 | free(p); 962 | 963 | qunlock(ctlr); 964 | 965 | return n; 966 | } 967 | 968 | 969 | static int 970 | pnp(Ether *edev) 971 | { 972 | static Ctlr ctlr[1]; 973 | 974 | if(Attchbug) 975 | iprint("ether pnp called\n"); 976 | 977 | if(edev->ctlr != nil) 978 | return -1; 979 | 980 | /* only one controller */ 981 | if(edev->ctlrno != 0) 982 | return -1; 983 | 984 | ctlr->edev = edev; 985 | 986 | // ctlr->mii->ctlr = ctlr; 987 | // ctlr->mii->mir = miird; 988 | // ctlr->mii->miw = miiwr; 989 | 990 | edev->port = (uintptr)(VIRTIO|EMAC); 991 | edev->ctlr = ctlr; 992 | edev->irq = IRQemac; 993 | edev->mbps = 100; 994 | // edev->maxmtu = 1536; 995 | edev->arg = edev; 996 | 997 | edev->attach = attach; 998 | edev->shutdown = shutdown; 999 | edev->ifstat = ifstat; 1000 | // edev->ctl = ctl; 1001 | edev->promiscuous = prom; 1002 | edev->multicast = multi; 1003 | 1004 | getmacaddr(edev); 1005 | 1006 | intrenable(edev->irq, etherinterrupt, edev, BUSUNKNOWN, edev->name); 1007 | 1008 | if(Attchbug) 1009 | iprint("ether pnp done\n"); 1010 | 1011 | return 0; 1012 | } 1013 | 1014 | 1015 | 1016 | void 1017 | ethersunxilink(void) 1018 | { 1019 | addethercard("ethersunxi", pnp); 1020 | } 1021 | -------------------------------------------------------------------------------- /fns.h: -------------------------------------------------------------------------------- 1 | #include "../port/portfns.h" 2 | 3 | /* l.s */ 4 | extern void sev(void); 5 | extern int tas(void *); 6 | extern int cmpswap(long*, long, long); 7 | extern void coherence(void); 8 | extern void idlehands(void); 9 | extern uvlong vcycles(void); 10 | #define cycles(ip) *(ip) = vcycles() 11 | extern int splfhi(void); 12 | extern void splflo(void); 13 | extern void touser(uintptr sp); 14 | extern void forkret(void); 15 | extern void noteret(void); 16 | extern void returnto(void*); 17 | extern void fpsaveregs(void*); 18 | extern void fploadregs(void*); 19 | extern void smccall(Ureg*); 20 | 21 | extern void zoot(void); 22 | extern void voot(void); 23 | 24 | extern void setttbr(uintptr pa); 25 | extern uintptr getfar(void); 26 | 27 | extern void flushasidva(uintptr asidva); 28 | extern void tlbivae1is(uintptr asidva); 29 | 30 | extern void flushasidvall(uintptr asidva); 31 | extern void tlbivale1is(uintptr asidva); 32 | 33 | extern void flushasid(uintptr asid); 34 | extern void tlbiaside1is(uintptr asid); 35 | 36 | extern void flushtlb(void); 37 | extern void tlbivmalle1(void); 38 | 39 | extern void flushlocaltlb(void); 40 | extern void tlbivmalle1(void); 41 | 42 | /* cache */ 43 | extern ulong cachesize(int level); 44 | 45 | extern void cacheiinvse(void*, int); 46 | extern void cacheuwbinv(void); 47 | extern void cacheiinv(void); 48 | 49 | extern void cachedwbse(void*, int); 50 | extern void cacheduwbse(void*, int); 51 | extern void cachedinvse(void*, int); 52 | extern void cachedwbinvse(void*, int); 53 | 54 | extern void cachedwb(void); 55 | extern void cachedinv(void); 56 | extern void cachedwbinv(void); 57 | 58 | extern void l2cacheuwb(void); 59 | extern void l2cacheuinv(void); 60 | extern void l2cacheuwbinv(void); 61 | 62 | /* mmu */ 63 | #define getpgcolor(a) 0 64 | extern uintptr paddr(void*); 65 | #define PADDR(a) paddr((void*)(a)) 66 | extern uintptr cankaddr(uintptr); 67 | extern void* kaddr(uintptr); 68 | #define KADDR(a) kaddr(a) 69 | extern void kmapinval(void); 70 | #define VA(k) ((uintptr)(k)) 71 | extern KMap *kmap(Page*); 72 | extern void kunmap(KMap*); 73 | extern uintptr mmukmap(uintptr, uintptr, usize); 74 | extern void* vmap(uvlong, vlong); 75 | extern void vunmap(void*, vlong); 76 | 77 | extern void mmu0init(uintptr*); 78 | extern void mmu0clear(uintptr*); 79 | extern void mmuidmap(uintptr*); 80 | extern void mmu1init(void); 81 | extern void meminit(void); 82 | 83 | extern void putasid(Proc*); 84 | extern void* ucalloc(usize); 85 | 86 | 87 | /* clock */ 88 | extern void clockinit(void); 89 | extern void synccycles(void); 90 | extern void armtimerset(int); 91 | extern void clockshutdown(void); 92 | 93 | /* fpu */ 94 | extern void fpuinit(void); 95 | extern void fpoff(void); 96 | extern void fpinit(void); 97 | extern void fpclear(void); 98 | extern void fpsave(FPsave*); 99 | extern void fprestore(FPsave*); 100 | extern void mathtrap(Ureg*); 101 | 102 | /* trap */ 103 | extern void trapinit(void); 104 | extern int userureg(Ureg*); 105 | extern void evenaddr(uintptr); 106 | extern void setkernur(Ureg*, Proc*); 107 | extern void procfork(Proc*); 108 | extern void procsetup(Proc*); 109 | extern void procsave(Proc*); 110 | extern void procrestore(Proc *); 111 | extern void trap(Ureg*); 112 | extern void syscall(Ureg*); 113 | extern void noted(Ureg*, ulong); 114 | extern void faultarm64(Ureg*); 115 | extern void dumpstack(void); 116 | extern void dumpregs(Ureg*); 117 | 118 | /* irq */ 119 | extern void intrcpushutdown(void); 120 | extern void intrsoff(void); 121 | extern void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); 122 | extern void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); 123 | extern int irq(Ureg*); 124 | extern void fiq(Ureg*); 125 | extern void intrinit(void); 126 | 127 | extern int isintrenable(int); 128 | extern int isintrpending(int); 129 | extern int isintractive(int); 130 | 131 | 132 | /* sysreg */ 133 | extern uvlong sysrd(ulong); 134 | extern void syswr(ulong, uvlong); 135 | 136 | /* main */ 137 | extern char *getconf(char *name); 138 | extern void setconfenv(void); 139 | extern void writeconf(void); 140 | extern int isaconfig(char*, int, ISAConf*); 141 | extern void links(void); 142 | extern void dmaflush(int, void*, ulong); 143 | 144 | 145 | /* uart */ 146 | extern void _uartputs(char*, int); 147 | extern void uartconsinit(void); 148 | // extern int i8250console(void); 149 | 150 | 151 | /* devarch */ 152 | extern uint getkeyadc(void); 153 | extern char* getkeyadc_event(void); 154 | extern void keyadcinit(void); 155 | 156 | 157 | /* ccu */ 158 | extern char* listgates(int); 159 | extern void debuggates(void); 160 | extern char* getgatename(int); 161 | extern int getgatestate(int); 162 | extern char* getresetname(int); 163 | extern int getresetstate(int); 164 | extern u32int getcpuclk_n(void); 165 | extern u32int getcpuclk_k(void); 166 | extern u32int getcpuclk_m(void); 167 | extern u32int getcpuclk_p(void); 168 | extern int setcpuclk(uint); 169 | extern int setcpuclk_n(u32int); 170 | extern int openthegate(char*); 171 | 172 | extern void turnonths(void); //needs to go 173 | 174 | /* thermal */ 175 | extern void thermalinit(void); 176 | extern int gettemp0(void); 177 | extern int gettemp1(void); 178 | extern int gettemp2(void); 179 | 180 | /* rsb */ 181 | extern u32int rsb_read(u8int, u16int, u8int, uint); 182 | extern u32int rsb_write(u8int, u16int, u8int, u32int, uint); 183 | extern void rsbinit(void); 184 | 185 | /* axp803 */ 186 | extern u8int pmic_id(void); 187 | extern int pmic_acin(void); 188 | extern int pmic_vbat(void); 189 | extern char* getpmicname(int); 190 | extern int getpmicstate(int); 191 | extern int getpmicvolt(int); 192 | extern int setpmicstate(char*, int); 193 | extern int setpmicvolt(char*, int); 194 | 195 | /* archA64 */ 196 | extern void arch_rsbsetup(void); 197 | 198 | -------------------------------------------------------------------------------- /fpu.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "../port/lib.h" 3 | #include "mem.h" 4 | #include "dat.h" 5 | #include "fns.h" 6 | 7 | #include "ureg.h" 8 | #include "sysreg.h" 9 | 10 | /* libc */ 11 | extern ulong getfcr(void); 12 | extern void setfcr(ulong fcr); 13 | extern ulong getfsr(void); 14 | extern void setfsr(ulong fsr); 15 | 16 | void 17 | fpuinit(void) 18 | { 19 | fpoff(); 20 | } 21 | 22 | void 23 | fpon(void) 24 | { 25 | syswr(CPACR_EL1, 3<<20); 26 | } 27 | 28 | void 29 | fpoff(void) 30 | { 31 | syswr(CPACR_EL1, 0<<20); 32 | } 33 | 34 | void 35 | fpinit(void) 36 | { 37 | fpon(); 38 | setfcr(0); 39 | setfsr(0); 40 | } 41 | 42 | void 43 | fpclear(void) 44 | { 45 | fpoff(); 46 | } 47 | 48 | void 49 | fpsave(FPsave *p) 50 | { 51 | p->control = getfcr(); 52 | p->status = getfsr(); 53 | fpsaveregs(p->regs); 54 | fpoff(); 55 | } 56 | 57 | void 58 | fprestore(FPsave *p) 59 | { 60 | fpon(); 61 | setfcr(p->control); 62 | setfsr(p->status); 63 | fploadregs(p->regs); 64 | } 65 | 66 | void 67 | mathtrap(Ureg*) 68 | { 69 | int s; 70 | 71 | if((up->fpstate & FPillegal) != 0){ 72 | postnote(up, 1, "sys: floating point in note handler", NDebug); 73 | return; 74 | } 75 | switch(up->fpstate){ 76 | case FPinit: 77 | s = splhi(); 78 | fpinit(); 79 | up->fpstate = FPactive; 80 | splx(s); 81 | break; 82 | case FPinactive: 83 | s = splhi(); 84 | fprestore(up->fpsave); 85 | up->fpstate = FPactive; 86 | splx(s); 87 | break; 88 | case FPactive: 89 | postnote(up, 1, "sys: floating point error", NDebug); 90 | break; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /gic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * General Interrupt Controller 3 | * Arm GIC-400 4 | */ 5 | 6 | #include "u.h" 7 | #include "../port/lib.h" 8 | #include "mem.h" 9 | #include "dat.h" 10 | #include "fns.h" 11 | #include "io.h" 12 | #include "../port/pci.h" 13 | #include "ureg.h" 14 | #include "sysreg.h" 15 | #include "../port/error.h" 16 | 17 | enum { 18 | GICD_CTLR = 0x000/4, /* RW, Distributor Control Register */ 19 | GICD_TYPER = 0x004/4, /* RO, Interrupt Controller Type */ 20 | GICD_IIDR = 0x008/4, /* RO, Distributor Implementer Identification Register */ 21 | 22 | GICD_IGROUPR0 = 0x080/4, /* RW, Interrupt Group Registers (0x80-0xBC) */ 23 | 24 | GICD_ISENABLER0 = 0x100/4, /* RW, Interrupt Set-Enable Registers (0x100-0x13C) */ 25 | GICD_ICENABLER0 = 0x180/4, /* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */ 26 | 27 | GICD_ISPENDR0 = 0x200/4, /* RW, Interrupt Set-Pending Registers (0x200-0x23C) */ 28 | GICD_ICPENDR0 = 0x280/4, /* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */ 29 | 30 | GICD_ISACTIVER0 = 0x300/4, /* RW, Interrupt Set-Active Registers (0x300-0x33C) */ 31 | GICD_ICACTIVER0 = 0x380/4, /* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */ 32 | 33 | GICD_IPRIORITYR0= 0x400/4, /* RW, Interrupt Priority Registers (0x400-0x5FC) */ 34 | GICD_TARGETSR0 = 0x800/4, /* RW, Interrupt Target Registers (0x800-0x9FC) */ 35 | GICD_ICFGR0 = 0xC00/4, /* RW, Interrupt Configuration Registers (0xC00-0xC7C) */ 36 | 37 | GICD_ISR0 = 0xD00/4, 38 | GICD_PPISR = GICD_ISR0, /* RO, Private Peripheral Interrupt Status Register */ 39 | GICD_SPISR0 = GICD_ISR0+1, /* RO, Shared Peripheral Interrupt Status Register */ 40 | GICD_SGIR = 0xF00/4, /* WO, Software Generated Interrupt Register */ 41 | 42 | GICD_CPENDSGIR0 = 0xF10/4, /* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */ 43 | GICD_SPENDSGIR0 = 0xF20/4, /* RW, SGI Set-Pending Registers (0xF20-0xF2C) */ 44 | 45 | GICD_PIDR4 = 0xFD0/4, /* RO, Perpheral ID Registers */ 46 | GICD_PIDR5 = 0xFD4/4, 47 | GICD_PIDR6 = 0xFD8/4, 48 | GICD_PIDR7 = 0xFDC/4, 49 | GICD_PIDR0 = 0xFE0/4, 50 | GICD_PIDR1 = 0xFE4/4, 51 | GICD_PIDR2 = 0xFE8/4, 52 | GICD_PIDR3 = 0xFEC/4, 53 | 54 | GICD_CIDR0 = 0xFF0/4, /* RO, Component ID Registers */ 55 | GICD_CIDR1 = 0xFF4/4, 56 | GICD_CIDR2 = 0xFF8/4, 57 | GICD_CIDR3 = 0xFFC/4, 58 | 59 | GICC_CTLR = 0x000/4, /* RW, CPU Interace Control Register */ 60 | GICC_PMR = 0x004/4, /* RW, Interrupt Priority Mask Register */ 61 | GICC_BPR = 0x008/4, /* RW, Binary Point Register */ 62 | GICC_IAR = 0x00C/4, /* RO, Interrupt Acknowledge Register */ 63 | GICC_EOIR = 0x010/4, /* WO, End of Interrupt Register */ 64 | GICC_RPR = 0x014/4, /* RO, Running Priority Register */ 65 | GICC_HPPIR = 0x018/4, /* RO, Highest Priority Pending Interrupt Register */ 66 | GICC_ABPR = 0x01C/4, /* RW, Aliased Binary Point Register */ 67 | GICC_AIAR = 0x020/4, /* RO, Aliased Interrupt Acknowledge Register */ 68 | GICC_AEOIR = 0x024/4, /* WO, Aliased End of Interrupt Register */ 69 | GICC_AHPPIR = 0x028/4, /* RO, Aliased Highest Priority Pending Interrupt Register */ 70 | GICC_APR0 = 0x0D0/4, /* RW, Active Priority Register */ 71 | GICC_NSAPR0 = 0x0E0/4, /* RW, Non-Secure Active Priority Register */ 72 | GICC_IIDR = 0x0FC/4, /* RO, CPU Interface Identification Register */ 73 | GICC_DIR = 0x1000/4, /* WO, Deactivate Interrupt Register */ 74 | 75 | GICH_HCR = 0x000/4, /* RW, Hypervisor Control Register */ 76 | GICH_VTR = 0x004/4, /* RO, VGIC Type Register */ 77 | GICH_VMCR = 0x008/4, /* RW, Virtual Machine Control Register */ 78 | GICH_MISR = 0x010/4, /* RO, Maintenance Interrupt Status Register */ 79 | GICH_EISR0 = 0x020/4, /* RO, End of Interrupt Status Register */ 80 | GICH_ELSR0 = 0x030/4, /* RO, Empty List Register Status Register */ 81 | GICH_APR0 = 0x0F0/4, /* RW, Active Priority Register */ 82 | GICH_LR0 = 0x100/4, /* RW, List Registers (0x100-0x10C) */ 83 | 84 | GICV_CTLR = 0x000/4, /* RW, Virtual Machine Control Register */ 85 | GICV_PMR = 0x004/4, /* RW, VM Priority Mask Register */ 86 | GICV_BPR = 0x008/4, /* RW, VM Binary Point Register */ 87 | GICV_IAR = 0x00C/4, /* RO, VM Interrupt Acknowledge Register */ 88 | GICV_EOIR = 0x010/4, /* WO, VM End of Interrupt Register */ 89 | GICV_RPR = 0x014/4, /* RO, VM Running Priority Register */ 90 | GICV_HPPIR = 0x018/4, /* RO, VM Highest Piority Pending Interrupt Register */ 91 | GICV_ABPR = 0x01C/4, /* RW, VM Aliased Binary Point Register */ 92 | GICV_AIAR = 0x020/4, /* RO, VM Aliased Interrupt Acknowledge Register */ 93 | GICV_AEOIR = 0x024/4, /* WO, VM Aliased End of Interrupt Register */ 94 | GICV_AHPPIR = 0x028/4, /* RO, VM Aliaed Highest Piority Pending Interrupt Register */ 95 | GICV_APR0 = 0x0D0/4, /* RW, VM Active Priority Register */ 96 | GICV_IIDR = 0x0FC/4, /* RO, VM CPU Interface Identification Register */ 97 | GICV_DIR = 0x1000/4, /* WO, VM Deactivate Interrupt Register */ 98 | }; 99 | 100 | typedef struct Vctl Vctl; 101 | struct Vctl { 102 | Vctl *next; 103 | void (*f)(Ureg*, void*); 104 | void *a; 105 | int irq; 106 | u32int intid; 107 | }; 108 | 109 | static Lock vctllock; 110 | static Vctl *vctl[MAXMACH][32], *vfiq; 111 | static u32int *cregs, *dregs; 112 | 113 | void 114 | intrcpushutdown(void) 115 | { 116 | if(cregs == nil || dregs == nil){ 117 | // uintptr va, pa; 118 | 119 | // pa = sysrd(CBAR_EL1); 120 | // va = ARMLOCAL + (pa - soc.armlocal); 121 | dregs = (u32int*)((VIRTIO + GIC) + 0x1000); 122 | cregs = (u32int*)((VIRTIO + GIC) + 0x2000); 123 | } 124 | 125 | /* disable cpu interface */ 126 | cregs[GICC_CTLR] &= ~1; 127 | coherence(); 128 | } 129 | 130 | void 131 | intrsoff(void) 132 | { 133 | int i, n; 134 | 135 | intrcpushutdown(); 136 | 137 | /* disable distributor */ 138 | dregs[GICD_CTLR] &= ~1; 139 | coherence(); 140 | 141 | /* clear all interrupts */ 142 | n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5; 143 | for(i = 0; i < n; i += 32){ 144 | dregs[GICD_ISENABLER0 + (i/32)] = -1; 145 | coherence(); 146 | dregs[GICD_ICENABLER0 + (i/32)] = -1; 147 | coherence(); 148 | } 149 | for(i = 0; i < n; i += 4){ 150 | dregs[GICD_IPRIORITYR0 + (i/4)] = 0; 151 | dregs[GICD_TARGETSR0 + (i/4)] = 0; 152 | } 153 | for(i = 32; i < n; i += 16) 154 | dregs[GICD_ICFGR0 + (i/16)] = 0; 155 | coherence(); 156 | } 157 | 158 | /* 159 | * called by trap to handle irq interrupts. 160 | * returns true iff a clock interrupt, thus maybe reschedule. 161 | */ 162 | int 163 | irq(Ureg* ureg) 164 | { 165 | Vctl *v; 166 | int clockintr; 167 | u32int intid; 168 | 169 | m->intr++; 170 | intid = cregs[GICC_IAR] & 0xFFFFFF; 171 | if((intid & ~3) == 1020) 172 | return 0; // spurious 173 | clockintr = 0; 174 | for(v = vctl[m->machno][intid%32]; v != nil; v = v->next) 175 | if(v->intid == intid){ 176 | coherence(); 177 | v->f(ureg, v->a); 178 | coherence(); 179 | if(v->irq == IRQcntps || v->irq == IRQcntpns) 180 | clockintr = 1; 181 | } 182 | coherence(); 183 | cregs[GICC_EOIR] = intid; 184 | return clockintr; 185 | } 186 | 187 | /* 188 | * called direct from lexception.s to handle fiq interrupt. 189 | */ 190 | void 191 | fiq(Ureg *ureg) 192 | { 193 | Vctl *v; 194 | u32int intid; 195 | 196 | m->intr++; 197 | intid = cregs[GICC_IAR] & 0xFFFFFF; 198 | if((intid & ~3) == 1020) 199 | return; // spurious 200 | v = vfiq; 201 | if(v != nil && v->intid == intid && m->machno == 0){ 202 | coherence(); 203 | v->f(ureg, v->a); 204 | coherence(); 205 | } 206 | cregs[GICC_EOIR] = intid; 207 | } 208 | 209 | void 210 | intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char*) 211 | { 212 | Vctl *v; 213 | u32int intid; 214 | int cpu, prio; 215 | 216 | // if(BUSTYPE(tbdf) == BusPCI){ 217 | // pciintrenable(tbdf, f, a); 218 | // return; 219 | // } 220 | if(tbdf != BUSUNKNOWN) 221 | return; 222 | 223 | cpu = 0; 224 | prio = 0x80; 225 | intid = irq; 226 | // switch(irq){ 227 | // case IRQcntps: 228 | // intid = 16 + 13; 229 | // break; 230 | // case IRQcntpns: 231 | // intid = 16 + 14; 232 | // break; 233 | 234 | // case IRQmbox0: 235 | // case IRQmbox1: 236 | // case IRQmbox2: 237 | // case IRQmbox3: 238 | // case IRQlocaltmr: 239 | // print("irqenable: missing documentation for local irq %d\n", irq); 240 | // return; 241 | 242 | // default: 243 | // if(irq < IRQgic){ 244 | // if(irq < 64) 245 | // intid += IRQgic-64; 246 | // else if(irq >= IRQbasic) 247 | // intid += IRQgic-64-32-8-IRQbasic; 248 | // } 249 | // } 250 | if(intid < 32) 251 | cpu = m->machno; 252 | 253 | if((v = xalloc(sizeof(Vctl))) == nil) 254 | panic("irqenable: no mem"); 255 | v->irq = irq; 256 | v->intid = intid; 257 | v->f = f; 258 | v->a = a; 259 | 260 | lock(&vctllock); 261 | if(irq == IRQfiq){ 262 | vfiq = v; 263 | prio = 0; 264 | }else{ 265 | v->next = vctl[cpu][intid%32]; 266 | vctl[cpu][intid%32] = v; 267 | } 268 | 269 | /* enable cpu interface */ 270 | cregs[GICC_PMR] = 0xFF; 271 | coherence(); 272 | 273 | cregs[GICC_CTLR] |= 1; 274 | coherence(); 275 | 276 | cregs[GICC_EOIR] = intid; 277 | 278 | /* enable distributor */ 279 | dregs[GICD_CTLR] |= 1; 280 | coherence(); 281 | 282 | /* setup */ 283 | dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3); 284 | dregs[GICD_TARGETSR0 + (intid/4)] |= (1<(SB) 25 | 26 | //WAVE('2') 27 | 28 | /* use dedicated stack pointer per exception level */ 29 | MOVWU $1, R1 30 | MSR R1, SPSel 31 | 32 | BL mmudisable<>(SB) 33 | 34 | //WAVE('3') 35 | 36 | /* invalidate local caches */ 37 | BL cachedinv(SB) 38 | BL cachedwbinv(SB) 39 | BL l2cacheuwbinv(SB) 40 | BL cacheiinv(SB) 41 | 42 | MOV $(MACHADDR(0)-KZERO), R27 43 | MRS MPIDR_EL1, R1 44 | ANDW $(MAXMACH-1), R1 45 | MOVWU $MACHSIZE, R2 46 | MULW R1, R2, R2 47 | SUB R2, R27 48 | 49 | ADD $(MACHSIZE-16), R27, R2 50 | MOV R2, SP 51 | 52 | CBNZ R1, _startup 53 | 54 | //WAVE('4') 55 | 56 | /* clear page table and machs */ 57 | MOV $(L1-KZERO), R1 58 | MOV $(MACHADDR(-1)-KZERO), R2 59 | _zerol1: 60 | MOV ZR, (R1)8! 61 | CMP R1, R2 62 | BNE _zerol1 63 | 64 | //WAVE('5') 65 | 66 | /* clear BSS */ 67 | MOV $edata-KZERO(SB), R1 68 | MOV $end-KZERO(SB), R2 69 | _zerobss: 70 | MOV ZR, (R1)8! 71 | CMP R1, R2 72 | BNE _zerobss 73 | 74 | //WAVE('6') 75 | 76 | /* setup page tables */ 77 | MOV $(L1-KZERO), R0 78 | BL mmu0init(SB) 79 | 80 | 81 | 82 | SEVL 83 | _startup: 84 | WFE 85 | 86 | //WAVE('7') 87 | 88 | BL mmuenable<>(SB) 89 | 90 | //VWAVE('8') 91 | 92 | 93 | MOV R26, R0 94 | MOV $0, R26 95 | ORR $KZERO, R27 96 | MSR R27, TPIDR_EL1 97 | MOV $setSB(SB), R28 98 | 99 | //VWAVE('Z') 100 | 101 | BL main(SB) 102 | 103 | 104 | TEXT stop<>(SB), 1, $-4 105 | _stop: 106 | WFE 107 | B _stop 108 | 109 | TEXT sev(SB), 1, $-4 110 | SEV 111 | WFE 112 | RETURN 113 | 114 | TEXT svcmode<>(SB), 1, $-4 115 | MSR $0xF, DAIFSet 116 | MRS CurrentEL, R0 117 | ANDW $(3<<2), R0 118 | CMPW $(1<<2), R0 119 | BEQ el1 120 | CMPW $(2<<2), R0 121 | BEQ el2 122 | B stop<>(SB) 123 | el2: 124 | MOV $0, R0 125 | MSR R0, MDCR_EL2 126 | ISB $SY 127 | 128 | /* HCR = RW, HCD, SWIO, BSU, FB */ 129 | MOVWU $(1<<31 | 1<<29 | 1<<2 | 0<<10 | 0<<9), R0 130 | MSR R0, HCR_EL2 131 | ISB $SY 132 | 133 | /* SCTLR = RES1 */ 134 | MOVWU $(3<<4 | 1<<11 | 1<<16 | 1<<18 | 3<<22 | 3<<28), R0 135 | ISB $SY 136 | MSR R0, SCTLR_EL2 137 | ISB $SY 138 | 139 | /* set VMID to zero */ 140 | MOV $0, R0 141 | MSR R0, VTTBR_EL2 142 | ISB $SY 143 | 144 | MOVWU $(0xF<<6 | 4), R0 145 | MSR R0, SPSR_EL2 146 | MSR LR, ELR_EL2 147 | ERET 148 | el1: 149 | RETURN 150 | 151 | TEXT mmudisable<>(SB), 1, $-4 152 | #define SCTLRCLR \ 153 | /* RES0 */ ( 3<<30 \ 154 | /* RES0 */ | 1<<27 \ 155 | /* UCI */ | 1<<26 \ 156 | /* EE */ | 1<<25 \ 157 | /* RES0 */ | 1<<21 \ 158 | /* E0E */ | 1<<24 \ 159 | /* WXN */ | 1<<19 \ 160 | /* nTWE */ | 1<<18 \ 161 | /* RES0 */ | 1<<17 \ 162 | /* nTWI */ | 1<<16 \ 163 | /* UCT */ | 1<<15 \ 164 | /* DZE */ | 1<<14 \ 165 | /* RES0 */ | 1<<13 \ 166 | /* RES0 */ | 1<<10 \ 167 | /* UMA */ | 1<<9 \ 168 | /* SA0 */ | 1<<4 \ 169 | /* SA */ | 1<<3 \ 170 | /* A */ | 1<<1 ) 171 | #define SCTLRSET \ 172 | /* RES1 */ ( 3<<28 \ 173 | /* RES1 */ | 3<<22 \ 174 | /* RES1 */ | 1<<20 \ 175 | /* RES1 */ | 1<<11 ) 176 | #define SCTLRMMU \ 177 | /* I */ ( 1<<12 \ 178 | /* C */ | 1<<2 \ 179 | /* M */ | 1<<0 ) 180 | 181 | /* initialise SCTLR, MMU and caches off */ 182 | ISB $SY 183 | MRS SCTLR_EL1, R0 184 | BIC $(SCTLRCLR | SCTLRMMU), R0 185 | ORR $SCTLRSET, R0 186 | ISB $SY 187 | MSR R0, SCTLR_EL1 188 | ISB $SY 189 | 190 | B flushlocaltlb(SB) 191 | 192 | TEXT mmuenable<>(SB), 1, $-4 193 | /* return to virtual */ 194 | ORR $KZERO, LR 195 | MOV LR, -16(RSP)! 196 | 197 | BL flushlocaltlb(SB) 198 | 199 | /* memory attributes */ 200 | #define MAIRINIT \ 201 | ( 0xFF << MA_MEM_WB*8 \ 202 | | 0x33 << MA_MEM_WT*8 \ 203 | | 0x44 << MA_MEM_UC*8 \ 204 | | 0x00 << MA_DEV_nGnRnE*8 \ 205 | | 0x04 << MA_DEV_nGnRE*8 \ 206 | | 0x08 << MA_DEV_nGRE*8 \ 207 | | 0x0C << MA_DEV_GRE*8 ) 208 | MOV $MAIRINIT, R1 209 | MSR R1, MAIR_EL1 210 | ISB $SY 211 | 212 | /* translation control */ 213 | #define TCRINIT \ 214 | /* TBI1 */ ( 0<<38 \ 215 | /* TBI0 */ | 0<<37 \ 216 | /* AS */ | 0<<36 \ 217 | /* TG1 */ | (((3<<16|1<<14|2<<12)>>PGSHIFT)&3)<<30 \ 218 | /* SH1 */ | SHARE_INNER<<28 \ 219 | /* ORGN1 */ | CACHE_WB<<26 \ 220 | /* IRGN1 */ | CACHE_WB<<24 \ 221 | /* EPD1 */ | 0<<23 \ 222 | /* A1 */ | 0<<22 \ 223 | /* T1SZ */ | (64-EVASHIFT)<<16 \ 224 | /* TG0 */ | (((1<<16|2<<14|0<<12)>>PGSHIFT)&3)<<14 \ 225 | /* SH0 */ | SHARE_INNER<<12 \ 226 | /* ORGN0 */ | CACHE_WB<<10 \ 227 | /* IRGN0 */ | CACHE_WB<<8 \ 228 | /* EPD0 */ | 0<<7 \ 229 | /* T0SZ */ | (64-EVASHIFT)<<0 ) 230 | MOV $TCRINIT, R1 231 | MRS ID_AA64MMFR0_EL1, R2 232 | ANDW $0x7, R2 // PARange 233 | ADD R2<<32, R1 // IPS 234 | MSR R1, TCR_EL1 235 | ISB $SY 236 | 237 | /* load the page tables */ 238 | MOV $(L1TOP-KZERO), R0 239 | ISB $SY 240 | MSR R0, TTBR0_EL1 241 | MSR R0, TTBR1_EL1 242 | ISB $SY 243 | 244 | /* enable MMU and caches */ 245 | MRS SCTLR_EL1, R1 246 | ORR $SCTLRMMU, R1 247 | ISB $SY 248 | MSR R1, SCTLR_EL1 249 | ISB $SY 250 | 251 | MOV RSP, R1 252 | ORR $KZERO, R1 253 | MOV R1, RSP 254 | MOV (RSP)16!, LR 255 | B cacheiinv(SB) 256 | 257 | TEXT touser(SB), 1, $-4 258 | MSR $0x3, DAIFSet // interrupts off 259 | MOVWU $0x10028, R1 // entry 260 | MOVWU $0, R2 // psr 261 | MSR R0, SP_EL0 // sp 262 | MSR R1, ELR_EL1 263 | MSR R2, SPSR_EL1 264 | ERET 265 | 266 | TEXT cas(SB), 1, $-4 267 | TEXT cmpswap(SB), 1, $-4 268 | MOVWU ov+8(FP), R1 269 | MOVWU nv+16(FP), R2 270 | _cas1: 271 | LDXRW (R0), R3 272 | CMP R3, R1 273 | BNE _cas0 274 | STXRW R2, (R0), R4 275 | CBNZ R4, _cas1 276 | MOVW $1, R0 277 | DMB $ISH 278 | RETURN 279 | _cas0: 280 | CLREX 281 | MOVW $0, R0 282 | RETURN 283 | 284 | TEXT tas(SB), 1, $-4 285 | TEXT _tas(SB), 1, $-4 286 | MOVW $0xdeaddead, R2 287 | _tas1: 288 | LDXRW (R0), R1 289 | STXRW R2, (R0), R3 290 | CBNZ R3, _tas1 291 | MOVW R1, R0 292 | 293 | TEXT coherence(SB), 1, $-4 294 | DMB $ISH 295 | RETURN 296 | 297 | TEXT islo(SB), 1, $-4 298 | MRS DAIF, R0 299 | AND $(0x2<<6), R0 300 | EOR $(0x2<<6), R0 301 | RETURN 302 | 303 | TEXT splhi(SB), 1, $-4 304 | MRS DAIF, R0 305 | MSR $0x2, DAIFSet 306 | RETURN 307 | 308 | TEXT splfhi(SB), 1, $-4 309 | MRS DAIF, R0 310 | MSR $0x3, DAIFSet 311 | RETURN 312 | 313 | TEXT spllo(SB), 1, $-4 314 | MSR $0x3, DAIFClr 315 | RETURN 316 | 317 | TEXT splflo(SB), 1, $-4 318 | MSR $0x1, DAIFClr 319 | RETURN 320 | 321 | TEXT splx(SB), 1, $-4 322 | MSR R0, DAIF 323 | RETURN 324 | 325 | TEXT idlehands(SB), 1, $-4 326 | DMB $ISH 327 | MOV $nrdy(SB), R1 328 | LDXRW (R1), R0 329 | CBZ R0, _goodnight 330 | CLREX 331 | SEVL 332 | _goodnight: 333 | WFE 334 | RETURN 335 | 336 | TEXT vcycles(SB), 1, $-4 337 | MRS CNTVCT_EL0, R0 338 | RETURN 339 | 340 | TEXT lcycles(SB), 1, $-4 341 | MRS PMCCNTR_EL0, R0 342 | RETURN 343 | 344 | TEXT setlabel(SB), 1, $-4 345 | MOV LR, 8(R0) 346 | MOV SP, R1 347 | MOV R1, 0(R0) 348 | MOVW $0, R0 349 | RETURN 350 | 351 | TEXT gotolabel(SB), 1, $-4 352 | MOV 8(R0), LR /* link */ 353 | MOV 0(R0), R1 /* sp */ 354 | MOV R1, SP 355 | MOVW $1, R0 356 | RETURN 357 | 358 | TEXT returnto(SB), 1, $-4 359 | MOV R0, 0(SP) 360 | RETURN 361 | 362 | TEXT getfar(SB), 1, $-4 363 | MRS FAR_EL1, R0 364 | RETURN 365 | 366 | TEXT setttbr(SB), 1, $-4 367 | DSB $ISHST 368 | MSR R0, TTBR0_EL1 369 | DSB $ISH 370 | ISB $SY 371 | RETURN 372 | 373 | /* 374 | * TLB maintenance operations. 375 | * these broadcast to all cpu's in the cluser 376 | * (inner sharable domain). 377 | */ 378 | TEXT flushasidva(SB), 1, $-4 379 | TEXT tlbivae1is(SB), 1, $-4 380 | DSB $ISHST 381 | TLBI R0, 0,8,3,1 /* VAE1IS */ 382 | DSB $ISH 383 | ISB $SY 384 | RETURN 385 | 386 | TEXT flushasidvall(SB), 1, $-4 387 | TEXT tlbivale1is(SB), 1, $-4 388 | DSB $ISHST 389 | TLBI R0, 0,8,3,5 /* VALE1IS */ 390 | DSB $ISH 391 | ISB $SY 392 | RETURN 393 | 394 | TEXT flushasid(SB), 1, $-4 395 | TEXT tlbiaside1is(SB), 1, $-4 396 | DSB $ISHST 397 | TLBI R0, 0,8,3,2 /* ASIDE1IS */ 398 | DSB $ISH 399 | ISB $SY 400 | RETURN 401 | 402 | TEXT flushtlb(SB), 1, $-4 403 | TEXT tlbivmalle1is(SB), 1, $-4 404 | DSB $ISHST 405 | TLBI R0, 0,8,3,0 /* VMALLE1IS */ 406 | DSB $ISH 407 | ISB $SY 408 | RETURN 409 | 410 | /* 411 | * flush the tlb of this cpu. no broadcast. 412 | */ 413 | TEXT flushlocaltlb(SB), 1, $-4 414 | TEXT tlbivmalle1(SB), 1, $-4 415 | DSB $NSHST 416 | TLBI R0, 0,8,7,0 /* VMALLE1 */ 417 | DSB $NSH 418 | ISB $SY 419 | RETURN 420 | 421 | TEXT fpsaveregs(SB), 1, $-4 422 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV { V0, V1, V2, V3 }, (R0)64! */ 423 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV { V4, V5, V6, V7 }, (R0)64! */ 424 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV { V8, V9, V10,V11 }, (R0)64! */ 425 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV { V12,V13,V14,V15 }, (R0)64! */ 426 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV { V16,V17,V18,V19 }, (R0)64! */ 427 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV { V20,V21,V22,V23 }, (R0)64! */ 428 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV { V24,V25,V26,V27 }, (R0)64! */ 429 | WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV { V28,V29,V30,V31 }, (R0)64! */ 430 | RETURN 431 | 432 | TEXT fploadregs(SB), 1, $-4 433 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV (R0)64!, { V0, V1, V2, V3 } */ 434 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV (R0)64!, { V4, V5, V6, V7 } */ 435 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV (R0)64!, { V8, V9, V10,V11 } */ 436 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV (R0)64!, { V12,V13,V14,V15 } */ 437 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV (R0)64!, { V16,V17,V18,V19 } */ 438 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV (R0)64!, { V20,V21,V22,V23 } */ 439 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV (R0)64!, { V24,V25,V26,V27 } */ 440 | WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV (R0)64!, { V28,V29,V30,V31 } */ 441 | RETURN 442 | 443 | // syscall or trap from EL0 444 | TEXT vsys0(SB), 1, $-4 445 | LSRW $26, R0, R17 // ec 446 | CMPW $0x15, R17 // SVC trap? 447 | BNE _itsatrap // nope. 448 | 449 | MOVP R26, R27, 224(RSP) 450 | MOVP R28, R29, 240(RSP) 451 | 452 | MRS SP_EL0, R1 453 | MRS ELR_EL1, R2 454 | MRS SPSR_EL1, R3 455 | 456 | MOV R0, 288(RSP) // type 457 | MOV R1, 264(RSP) // sp 458 | MOV R2, 272(RSP) // pc 459 | MOV R3, 280(RSP) // psr 460 | 461 | MOV $setSB(SB), R28 462 | MRS TPIDR_EL1, R27 463 | MOV 16(R27), R26 464 | 465 | ADD $16, RSP, R0 // ureg 466 | BL syscall(SB) 467 | 468 | TEXT forkret(SB), 1, $-4 469 | MSR $0x3, DAIFSet // interrupts off 470 | 471 | ADD $16, RSP, R0 // ureg 472 | 473 | MOV 16(RSP), R0 // ret 474 | MOV 264(RSP), R1 // sp 475 | MOV 272(RSP), R2 // pc 476 | MOV 280(RSP), R3 // psr 477 | 478 | MSR R1, SP_EL0 479 | MSR R2, ELR_EL1 480 | MSR R3, SPSR_EL1 481 | 482 | MOVP 224(RSP), R26, R27 483 | MOVP 240(RSP), R28, R29 484 | 485 | MOV 256(RSP), R30 // link 486 | 487 | ADD $TRAPFRAMESIZE, RSP 488 | ERET 489 | 490 | TEXT itsatrap<>(SB), 1, $-4 491 | _itsatrap: 492 | MOVP R1, R2, 24(RSP) 493 | MOVP R3, R4, 40(RSP) 494 | MOVP R5, R6, 56(RSP) 495 | MOVP R7, R8, 72(RSP) 496 | MOVP R9, R10, 88(RSP) 497 | MOVP R11, R12, 104(RSP) 498 | MOVP R13, R14, 120(RSP) 499 | MOVP R15, R16, 136(RSP) 500 | 501 | MOVP R18, R19, 160(RSP) 502 | MOVP R20, R21, 176(RSP) 503 | MOVP R22, R23, 192(RSP) 504 | MOVP R24, R25, 208(RSP) 505 | 506 | // trap/irq/fiq/serr from EL0 507 | TEXT vtrap0(SB), 1, $-4 508 | MOVP R26, R27, 224(RSP) 509 | MOVP R28, R29, 240(RSP) 510 | 511 | MRS SP_EL0, R1 512 | MRS ELR_EL1, R2 513 | MRS SPSR_EL1, R3 514 | 515 | MOV R0, 288(RSP) // type 516 | MOV R1, 264(RSP) // sp 517 | MOV R2, 272(RSP) // pc 518 | MOV R3, 280(RSP) // psr 519 | 520 | MOV $setSB(SB), R28 521 | MRS TPIDR_EL1, R27 522 | MOV 16(R27), R26 523 | 524 | ADD $16, RSP, R0 // ureg 525 | BL trap(SB) 526 | 527 | TEXT noteret(SB), 1, $-4 528 | MSR $0x3, DAIFSet // interrupts off 529 | 530 | ADD $16, RSP, R0 // ureg 531 | 532 | MOV 264(RSP), R1 // sp 533 | MOV 272(RSP), R2 // pc 534 | MOV 280(RSP), R3 // psr 535 | 536 | MSR R1, SP_EL0 537 | MSR R2, ELR_EL1 538 | MSR R3, SPSR_EL1 539 | 540 | MOVP 224(RSP), R26, R27 541 | MOVP 240(RSP), R28, R29 542 | 543 | _intrreturn: 544 | MOVP 16(RSP), R0, R1 545 | MOVP 32(RSP), R2, R3 546 | MOVP 48(RSP), R4, R5 547 | MOVP 64(RSP), R6, R7 548 | MOVP 80(RSP), R8, R9 549 | MOVP 96(RSP), R10, R11 550 | MOVP 112(RSP), R12, R13 551 | MOVP 128(RSP), R14, R15 552 | MOVP 144(RSP), R16, R17 553 | MOVP 160(RSP), R18, R19 554 | MOVP 176(RSP), R20, R21 555 | MOVP 192(RSP), R22, R23 556 | MOVP 208(RSP), R24, R25 557 | 558 | MOV 256(RSP), R30 // link 559 | 560 | ADD $TRAPFRAMESIZE, RSP 561 | ERET 562 | 563 | // irq/fiq/trap/serr from EL1 564 | TEXT vtrap1(SB), 1, $-4 565 | MOV R29, 248(RSP) // special 566 | 567 | ADD $TRAPFRAMESIZE, RSP, R1 568 | MRS ELR_EL1, R2 569 | MRS SPSR_EL1, R3 570 | 571 | MOV R0, 288(RSP) // type 572 | MOV R1, 264(RSP) // sp 573 | MOV R2, 272(RSP) // pc 574 | MOV R3, 280(RSP) // psr 575 | 576 | ADD $16, RSP, R0 // ureg 577 | BL trap(SB) 578 | 579 | MSR $0x3, DAIFSet // interrupts off 580 | 581 | MOV 272(RSP), R2 // pc 582 | MOV 280(RSP), R3 // psr 583 | 584 | MSR R2, ELR_EL1 585 | MSR R3, SPSR_EL1 586 | 587 | MOV 248(RSP), R29 // special 588 | B _intrreturn 589 | 590 | // vector tables 591 | TEXT vsys(SB), 1, $-4 592 | SUB $TRAPFRAMESIZE, RSP 593 | 594 | MOV R0, 16(RSP) 595 | MOV R30, 256(RSP) // link 596 | 597 | MOV R17, 152(RSP) // temp 598 | 599 | MRS ESR_EL1, R0 // type 600 | 601 | _vsyspatch: 602 | B _vsyspatch // branch to vsys0() patched in 603 | 604 | TEXT vtrap(SB), 1, $-4 605 | SUB $TRAPFRAMESIZE, RSP 606 | 607 | MOVP R0, R1, 16(RSP) 608 | MOVP R2, R3, 32(RSP) 609 | MOVP R4, R5, 48(RSP) 610 | MOVP R6, R7, 64(RSP) 611 | MOVP R8, R9, 80(RSP) 612 | MOVP R10, R11, 96(RSP) 613 | MOVP R12, R13, 112(RSP) 614 | MOVP R14, R15, 128(RSP) 615 | MOVP R16, R17, 144(RSP) 616 | MOVP R18, R19, 160(RSP) 617 | MOVP R20, R21, 176(RSP) 618 | MOVP R22, R23, 192(RSP) 619 | MOVP R24, R25, 208(RSP) 620 | 621 | MOV R30, 256(RSP) // link 622 | 623 | MRS ESR_EL1, R0 // type 624 | 625 | _vtrappatch: 626 | B _vtrappatch // branch to vtrapX() patched in 627 | 628 | TEXT virq(SB), 1, $-4 629 | SUB $TRAPFRAMESIZE, RSP 630 | 631 | MOVP R0, R1, 16(RSP) 632 | MOVP R2, R3, 32(RSP) 633 | MOVP R4, R5, 48(RSP) 634 | MOVP R6, R7, 64(RSP) 635 | MOVP R8, R9, 80(RSP) 636 | MOVP R10, R11, 96(RSP) 637 | MOVP R12, R13, 112(RSP) 638 | MOVP R14, R15, 128(RSP) 639 | MOVP R16, R17, 144(RSP) 640 | MOVP R18, R19, 160(RSP) 641 | MOVP R20, R21, 176(RSP) 642 | MOVP R22, R23, 192(RSP) 643 | MOVP R24, R25, 208(RSP) 644 | 645 | MOV R30, 256(RSP) // link 646 | 647 | MOV $(1<<32), R0 // type irq 648 | 649 | _virqpatch: 650 | B _virqpatch // branch to vtrapX() patched in 651 | 652 | TEXT vfiq(SB), 1, $-4 653 | SUB $TRAPFRAMESIZE, RSP 654 | 655 | MOVP R0, R1, 16(RSP) 656 | MOVP R2, R3, 32(RSP) 657 | MOVP R4, R5, 48(RSP) 658 | MOVP R6, R7, 64(RSP) 659 | MOVP R8, R9, 80(RSP) 660 | MOVP R10, R11, 96(RSP) 661 | MOVP R12, R13, 112(RSP) 662 | MOVP R14, R15, 128(RSP) 663 | MOVP R16, R17, 144(RSP) 664 | MOVP R18, R19, 160(RSP) 665 | MOVP R20, R21, 176(RSP) 666 | MOVP R22, R23, 192(RSP) 667 | MOVP R24, R25, 208(RSP) 668 | 669 | MOV R30, 256(RSP) // link 670 | MOV $(2<<32), R0 // type fiq 671 | 672 | _vfiqpatch: 673 | B _vfiqpatch // branch to vtrapX() patched in 674 | 675 | TEXT vserr(SB), 1, $-4 676 | SUB $TRAPFRAMESIZE, RSP 677 | 678 | MOVP R0, R1, 16(RSP) 679 | MOVP R2, R3, 32(RSP) 680 | MOVP R4, R5, 48(RSP) 681 | MOVP R6, R7, 64(RSP) 682 | MOVP R8, R9, 80(RSP) 683 | MOVP R10, R11, 96(RSP) 684 | MOVP R12, R13, 112(RSP) 685 | MOVP R14, R15, 128(RSP) 686 | MOVP R16, R17, 144(RSP) 687 | MOVP R18, R19, 160(RSP) 688 | MOVP R20, R21, 176(RSP) 689 | MOVP R22, R23, 192(RSP) 690 | MOVP R24, R25, 208(RSP) 691 | 692 | MOV R30, 256(RSP) // link 693 | 694 | MRS ESR_EL1, R0 695 | ORR $(3<<32), R0 // type 696 | _vserrpatch: 697 | B _vserrpatch // branch to vtrapX() patched in 698 | 699 | /* fault-proof memcpy */ 700 | TEXT peek(SB), 1, $-4 701 | MOV R0, R1 702 | MOV dst+8(FP), R2 703 | MOVWU len+16(FP), R0 704 | TEXT _peekinst(SB), 1, $-4 705 | _peekloop: 706 | MOVBU (R1)1!, R3 707 | MOVBU R3, (R2)1! 708 | SUBS $1, R0 709 | BNE _peekloop 710 | RETURN 711 | 712 | TEXT smccall(SB), 1, $32 713 | /* save extern registers */ 714 | MOVP R26, R27, (RSP) 715 | 716 | /* R0 = Ureg */ 717 | MOV R0, R8 718 | 719 | /* save ureg pointer */ 720 | MOV R8, 16(RSP) 721 | 722 | MOVP 0(R8), R0, R1 723 | MOVP 16(R8), R2, R3 724 | MOVP 32(R8), R4, R5 725 | MOVP 48(R8), R6, R7 726 | 727 | SMC 728 | 729 | /* restore ureg pointer */ 730 | MOV 16(RSP), R8 731 | 732 | MOVP R0, R1, 0(R8) 733 | MOVP R2, R3, 16(R8) 734 | 735 | /* restore extern registers */ 736 | MOVP (RSP), R26, R27 737 | 738 | RETURN 739 | 740 | /* for debug waves */ 741 | TEXT zoot(SB), 1, $-4 742 | WAVE('W') 743 | RETURN 744 | 745 | /* for debug waves */ 746 | TEXT voot(SB), 1, $-4 747 | VWAVE('W') 748 | RETURN 749 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "tos.h" 3 | #include "../port/lib.h" 4 | #include "mem.h" 5 | #include "dat.h" 6 | #include "fns.h" 7 | #include "../port/error.h" 8 | #include "pool.h" 9 | #include "io.h" 10 | #include "sysreg.h" 11 | #include "ureg.h" 12 | 13 | #include "rebootcode.i" 14 | 15 | Conf conf; 16 | 17 | int normalprint; 18 | 19 | #define MAXCONF 64 20 | static char *confname[MAXCONF]; 21 | static char *confval[MAXCONF]; 22 | static int nconf = -1; 23 | 24 | void 25 | bootargsinit(void) 26 | { 27 | int i, j, n; 28 | char *cp, *line[MAXCONF], *p, *q; 29 | 30 | /* 31 | * parse configuration args from dos file plan9.ini 32 | */ 33 | cp = BOOTARGS; 34 | cp[BOOTARGSLEN-1] = 0; 35 | 36 | /* 37 | * Strip out '\r', change '\t' -> ' '. 38 | */ 39 | p = cp; 40 | for(q = cp; *q; q++){ 41 | if(*q == -1) 42 | break; 43 | if(*q == '\r') 44 | continue; 45 | if(*q == '\t') 46 | *q = ' '; 47 | *p++ = *q; 48 | } 49 | *p = 0; 50 | 51 | n = getfields(cp, line, MAXCONF, 1, "\n"); 52 | if(n <= 0){ 53 | /* empty plan9.ini, no configuration passed */ 54 | return; 55 | } 56 | 57 | nconf = 0; 58 | for(i = 0; i < n; i++){ 59 | if(*line[i] == '#') 60 | continue; 61 | cp = strchr(line[i], '='); 62 | if(cp == nil) 63 | continue; 64 | *cp++ = '\0'; 65 | for(j = 0; j < nconf; j++){ 66 | if(cistrcmp(confname[j], line[i]) == 0) 67 | break; 68 | } 69 | confname[j] = line[i]; 70 | confval[j] = cp; 71 | if(j == nconf) 72 | nconf++; 73 | } 74 | } 75 | 76 | char* 77 | getconf(char *name) 78 | { 79 | int i; 80 | 81 | for(i = 0; i < nconf; i++) 82 | if(cistrcmp(confname[i], name) == 0) 83 | return confval[i]; 84 | return nil; 85 | } 86 | 87 | void 88 | setconfenv(void) 89 | { 90 | int i; 91 | 92 | if(nconf < 0){ 93 | /* use defaults when there was no configuration */ 94 | ksetenv("console", "0", 0); 95 | return; 96 | } 97 | 98 | for(i = 0; i < nconf; i++){ 99 | if(confname[i][0] != '*') 100 | ksetenv(confname[i], confval[i], 0); 101 | ksetenv(confname[i], confval[i], 1); 102 | } 103 | } 104 | 105 | void 106 | writeconf(void) 107 | { 108 | char *p, *q; 109 | int n; 110 | 111 | p = getconfenv(); 112 | if(waserror()) { 113 | free(p); 114 | nexterror(); 115 | } 116 | 117 | /* convert to name=value\n format */ 118 | for(q=p; *q; q++) { 119 | q += strlen(q); 120 | *q = '='; 121 | q += strlen(q); 122 | *q = '\n'; 123 | } 124 | n = q - p + 1; 125 | if(n >= BOOTARGSLEN) 126 | error("kernel configuration too large"); 127 | memmove(BOOTARGS, p, n); 128 | memset(BOOTARGS+n, 0, BOOTARGSLEN-n); 129 | poperror(); 130 | free(p); 131 | } 132 | 133 | int 134 | isaconfig(char *, int, ISAConf *) 135 | { 136 | return 0; 137 | } 138 | 139 | /* 140 | * starting place for first process 141 | */ 142 | void 143 | init0(void) 144 | { 145 | char **sp; 146 | 147 | // i8250console(); 148 | normalprint = 1; 149 | 150 | chandevinit(); 151 | 152 | if(!waserror()){ 153 | ksetenv("cputype", "arm64", 0); 154 | if(cpuserver) 155 | ksetenv("service", "cpu", 0); 156 | else 157 | ksetenv("service", "terminal", 0); 158 | setconfenv(); 159 | poperror(); 160 | } 161 | kproc("alarm", alarmkproc, 0); 162 | 163 | sp = (char**)(USTKTOP-sizeof(Tos) - 8 - sizeof(sp[0])*4); 164 | sp[3] = sp[2] = sp[1] = nil; 165 | strcpy(sp[1] = (char*)&sp[4], "boot"); 166 | sp[0] = (void*)&sp[1]; 167 | touser((uintptr)sp); 168 | } 169 | 170 | void 171 | confinit(void) 172 | { 173 | int userpcnt; 174 | ulong kpages; 175 | char *p; 176 | int i; 177 | 178 | conf.nmach = MAXMACH; 179 | 180 | if(p = getconf("service")){ 181 | if(strcmp(p, "cpu") == 0) 182 | cpuserver = 1; 183 | else if(strcmp(p,"terminal") == 0) 184 | cpuserver = 0; 185 | } 186 | 187 | if(p = getconf("*kernelpercent")) 188 | userpcnt = 100 - strtol(p, 0, 0); 189 | else 190 | userpcnt = 0; 191 | 192 | if(userpcnt < 10) 193 | userpcnt = 60 + cpuserver*10; 194 | 195 | conf.npage = 0; 196 | for(i = 0; i < nelem(conf.mem); i++) 197 | conf.npage += conf.mem[i].npage; 198 | 199 | kpages = conf.npage - (conf.npage*userpcnt)/100; 200 | if(kpages > ((uintptr)-VDRAM)/BY2PG) 201 | kpages = ((uintptr)-VDRAM)/BY2PG; 202 | 203 | conf.upages = conf.npage - kpages; 204 | conf.ialloc = (kpages/2)*BY2PG; 205 | 206 | /* set up other configuration parameters */ 207 | conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; 208 | if(cpuserver) 209 | conf.nproc *= 3; 210 | if(conf.nproc > 4000) 211 | conf.nproc = 4000; 212 | conf.nswap = conf.npage*3; 213 | conf.nswppo = 4096; 214 | conf.nimage = 200; 215 | 216 | conf.copymode = conf.nmach > 1; 217 | 218 | /* 219 | * Guess how much is taken by the large permanent 220 | * datastructures. Mntcache and Mntrpc are not accounted for. 221 | */ 222 | kpages = conf.npage - conf.upages; 223 | kpages *= BY2PG; 224 | kpages -= conf.upages*sizeof(Page) 225 | + conf.nproc*sizeof(Proc*) 226 | + conf.nimage*sizeof(Image) 227 | + conf.nswap 228 | + conf.nswppo*sizeof(Page*); 229 | mainmem->maxsize = kpages; 230 | imagmem->maxsize = kpages; 231 | } 232 | 233 | void 234 | machinit(void) 235 | { 236 | m->ticks = 1; 237 | m->perf.period = 1; 238 | active.machs[m->machno] = 1; 239 | } 240 | 241 | void 242 | mpinit(void) 243 | { 244 | extern void _start(void); 245 | int i; 246 | 247 | splhi(); 248 | for(i = 1; i < conf.nmach; i++){ 249 | Ureg u = {0}; 250 | 251 | MACHP(i)->machno = i; 252 | cachedwbinvse(MACHP(i), MACHSIZE); 253 | 254 | u.r0 = 0x84000003; /* CPU_ON */ 255 | u.r1 = (sysrd(MPIDR_EL1) & ~0xFF) | i; 256 | u.r2 = PADDR(_start); 257 | u.r3 = i; 258 | smccall(&u); 259 | } 260 | synccycles(); 261 | spllo(); 262 | } 263 | 264 | void 265 | cpuidprint(void) 266 | { 267 | iprint("cpu%d: %dMHz ARM Cortex A53\n", m->machno, m->cpumhz); 268 | } 269 | 270 | void 271 | main(void) 272 | { 273 | machinit(); 274 | if(m->machno){ 275 | trapinit(); 276 | fpuinit(); 277 | // intrinit(); 278 | clockinit(); 279 | cpuidprint(); 280 | synccycles(); 281 | timersinit(); 282 | flushtlb(); 283 | mmu1init(); 284 | m->ticks = MACHP(0)->ticks; 285 | schedinit(); 286 | return; 287 | } 288 | uartconsinit(); 289 | quotefmtinstall(); 290 | bootargsinit(); 291 | meminit(); 292 | confinit(); 293 | iprint("foo\n"); 294 | xinit(); 295 | printinit(); 296 | print("\nPlan 9\n"); 297 | // normalprint = 1; 298 | trapinit(); 299 | fpuinit(); 300 | // intrinit(); 301 | clockinit(); 302 | cpuidprint(); 303 | timersinit(); 304 | pageinit(); 305 | procinit0(); 306 | initseg(); 307 | links(); 308 | chandevreset(); 309 | userinit(); 310 | mpinit(); 311 | mmu0clear((uintptr*)L1); 312 | flushtlb(); 313 | mmu1init(); 314 | schedinit(); 315 | } 316 | 317 | void 318 | exit(int) 319 | { 320 | Ureg u = { .r0 = 0x84000002 }; /* CPU_OFF */ 321 | 322 | cpushutdown(); 323 | splfhi(); 324 | 325 | if(m->machno == 0) 326 | u.r0 = 0x84000009; /* SYSTEM RESET */ 327 | smccall(&u); 328 | } 329 | 330 | static void 331 | rebootjump(void *entry, void *code, ulong size) 332 | { 333 | void (*f)(void*, void*, ulong); 334 | 335 | intrcpushutdown(); 336 | 337 | /* redo identity map */ 338 | mmuidmap((uintptr*)L1); 339 | 340 | /* setup reboot trampoline function */ 341 | f = (void*)REBOOTADDR; 342 | memmove(f, rebootcode, sizeof(rebootcode)); 343 | 344 | cachedwbinvse(f, sizeof(rebootcode)); 345 | cacheiinvse(f, sizeof(rebootcode)); 346 | 347 | (*f)(entry, code, size); 348 | 349 | for(;;); 350 | } 351 | 352 | void 353 | reboot(void*, void *code, ulong size) 354 | { 355 | writeconf(); 356 | while(m->machno != 0){ 357 | procwired(up, 0); 358 | sched(); 359 | } 360 | 361 | cpushutdown(); 362 | delay(2000); 363 | 364 | splfhi(); 365 | 366 | /* turn off buffered serial console */ 367 | serialoq = nil; 368 | 369 | /* shutdown devices */ 370 | chandevshutdown(); 371 | 372 | /* stop the clock */ 373 | clockshutdown(); 374 | intrsoff(); 375 | 376 | /* off we go - never to return */ 377 | rebootjump((void*)(KTZERO-KZERO), code, size); 378 | } 379 | 380 | void 381 | dmaflush(int clean, void *p, ulong len) 382 | { 383 | uintptr s = (uintptr)p; 384 | uintptr e = (uintptr)p + len; 385 | 386 | if(clean){ 387 | s &= ~(BLOCKALIGN-1); 388 | e += BLOCKALIGN-1; 389 | e &= ~(BLOCKALIGN-1); 390 | cachedwbse((void*)s, e - s); 391 | return; 392 | } 393 | if(s & BLOCKALIGN-1){ 394 | s &= ~(BLOCKALIGN-1); 395 | cachedwbinvse((void*)s, BLOCKALIGN); 396 | s += BLOCKALIGN; 397 | } 398 | if(e & BLOCKALIGN-1){ 399 | e &= ~(BLOCKALIGN-1); 400 | if(e < s) 401 | return; 402 | cachedwbinvse((void*)e, BLOCKALIGN); 403 | } 404 | if(s < e) 405 | cachedinvse((void*)s, e - s); 406 | } 407 | -------------------------------------------------------------------------------- /mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Memory and machine-specific definitions. Used in C and assembler. 3 | */ 4 | #define KiB 1024u /* Kibi 0x0000000000000400 */ 5 | #define MiB 1048576u /* Mebi 0x0000000000100000 */ 6 | #define GiB 1073741824u /* Gibi 000000000040000000 */ 7 | 8 | #define MIN(a, b) ((a) < (b)? (a): (b)) 9 | #define MAX(a, b) ((a) > (b)? (a): (b)) 10 | 11 | //#define FMASK(o, w) (((1<<(w))-1)<<(o)) 12 | 13 | #define UARTOUT 0x01C28000 14 | #define VUARTOUT (0xFFFFFF0001C28000ULL) 15 | //#define VUARTOUT 0x81C28000 16 | #define PHYSCONS (VIRTIO + UART0) 17 | 18 | /* 19 | * Sizes: 20 | * L0 L1 L2 L3 21 | * 4K 2M 1G 512G 22 | * 16K 32M 64G 128T 23 | * 64K 512M 4T - 24 | */ 25 | 26 | #define PGSHIFT 16 /* log(BY2PG) */ 27 | #define BY2PG (1ULL<> (PGSHIFT + (l)*PTSHIFT)) & ((1 << PTSHIFT)-1)) 38 | #define PGLSZ(l) (1ULL << (PGSHIFT + (l)*PTSHIFT)) 39 | 40 | #define PTL1X(v, l) (L1TABLEX(v, l) | PTLX(v, l)) 41 | #define L1TABLEX(v, l) (L1TABLE(v, l) << PTSHIFT) 42 | #define L1TABLES ((-KSEG0+PGLSZ(2)-1)/PGLSZ(2)) 43 | #define L1TABLE(v, l) (L1TABLES - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1) 44 | #define L1TOPSIZE (1ULL << (EVASHIFT - PTLEVELS*PTSHIFT)) 45 | 46 | #define MAXMACH 4 /* max # cpus system can run */ 47 | #define MACHSIZE (8*KiB) 48 | 49 | #define KSTACK (8*KiB) 50 | #define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */ 51 | #define TRAPFRAMESIZE (38*8) 52 | 53 | /* reserved dram for ucalloc() and fbmemalloc() at the end of KZERO (physical) */ 54 | #define UCRAMBASE (-KZERO - UCRAMSIZE) 55 | #define UCRAMSIZE (8*MiB) 56 | 57 | #define VDRAM (0xFFFFFFFFC0000000ULL) /* 0x40000000 - 0x80000000 */ 58 | #define KTZERO (VDRAM + 0x80000) /* 0x40100000 - kernel text start */ 59 | 60 | #define VIRTIO (0xFFFFFF0000000000ULL) /* 0x30000000 */ 61 | 62 | //#define PHYSIO 0x0 63 | //#define IOSIZE 0x10000000 64 | 65 | #define PHYSIO 0x01C00000 66 | #define IOSIZE 0x303C00 67 | 68 | #define PHYSDRAM 0x40000000 69 | #define DRAMSIZE 0x80000000 70 | 71 | #define KZERO (0xFFFFFFFF80000000ULL) /* 0x00000000 - kernel address space */ 72 | 73 | #define VMAP (0xFFFFFFFF00000000ULL) /* 0x00000000 - 0x40000000 */ 74 | 75 | #define KMAPEND (0xFFFFFFFF00000000ULL) /* 0x140000000 */ 76 | #define KMAP (0xFFFFFFFE00000000ULL) /* 0x40000000 */ 77 | 78 | #define KSEG0 (0xFFFFFFFE00000000ULL) 79 | 80 | #define L1 (L1TOP-L1SIZE) 81 | #define L1SIZE ((L1TABLES+PTLEVELS-2)*BY2PG) 82 | #define L1TOP ((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG) 83 | 84 | #define MACHADDR(n) (KTZERO-((n)+1)*MACHSIZE) 85 | 86 | #define CONFADDR (VDRAM + 0x10000) /* 0x40010000 */ 87 | 88 | #define BOOTARGS ((char*)CONFADDR) 89 | #define BOOTARGSLEN 0x10000 90 | 91 | #define REBOOTADDR (VDRAM-KZERO + 0x20000) /* 0x40020000 */ 92 | 93 | #define UZERO 0ULL /* user segment */ 94 | #define UTZERO (UZERO+0x10000) /* user text start */ 95 | #define USTKTOP ((EVAMASK>>1)-0xFFFF) /* user segment end +1 */ 96 | #define USTKSIZE (16*1024*1024) /* user stack size */ 97 | 98 | #define BLOCKALIGN 64 /* only used in allocb.c */ 99 | 100 | /* 101 | * Sizes 102 | */ 103 | #define BI2BY 8 /* bits per byte */ 104 | #define BY2SE 4 105 | #define BY2WD 8 106 | #define BY2V 8 /* only used in xalloc.c */ 107 | 108 | #define PTEMAPMEM (1024*1024) 109 | #define PTEPERTAB (PTEMAPMEM/BY2PG) 110 | #define SEGMAPSIZE 8192 111 | #define SSEGMAPSIZE 16 112 | #define PPN(x) ((x)&~(BY2PG-1)) 113 | 114 | #define SHARE_NONE 0 115 | #define SHARE_OUTER 2 116 | #define SHARE_INNER 3 117 | 118 | #define CACHE_UC 0 119 | #define CACHE_WB 1 120 | #define CACHE_WT 2 121 | #define CACHE_WB_NA 3 122 | 123 | #define MA_MEM_WB 0 124 | #define MA_MEM_WT 1 125 | #define MA_MEM_UC 2 126 | #define MA_DEV_nGnRnE 3 127 | #define MA_DEV_nGnRE 4 128 | #define MA_DEV_nGRE 5 129 | #define MA_DEV_GRE 6 130 | 131 | #define PTEVALID 1 132 | #define PTEBLOCK 0 133 | #define PTETABLE 2 134 | #define PTEPAGE 2 135 | 136 | #define PTEMA(x) ((x)<<2) 137 | #define PTEAP(x) ((x)<<6) 138 | #define PTESH(x) ((x)<<8) 139 | 140 | #define PTEAF (1<<10) 141 | #define PTENG (1<<11) 142 | #define PTEPXN (1ULL<<53) 143 | #define PTEUXN (1ULL<<54) 144 | 145 | #define PTEKERNEL PTEAP(0) 146 | #define PTEUSER PTEAP(1) 147 | #define PTEWRITE PTEAP(0) 148 | #define PTERONLY PTEAP(2) 149 | #define PTENOEXEC (PTEPXN|PTEUXN) 150 | 151 | #define PTECACHED PTEMA(MA_MEM_WB) 152 | #define PTEWT PTEMA(MA_MEM_WT) 153 | #define PTEUNCACHED PTEMA(MA_MEM_UC) 154 | #define PTEDEVICE PTEMA(MA_DEV_nGnRE) 155 | 156 | /* 157 | * Physical machine information from here on. 158 | * PHYS addresses as seen from the arm cpu. 159 | * BUS addresses as seen from peripherals 160 | */ 161 | 162 | 163 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | CONF=pine64 2 | CONFLIST=pine64 3 | 4 | loadaddr=0xFFFFFFFFC0080000 5 | kzero=0xffffffff80000000 6 | 7 | objtype=arm64 8 | DEBUG 78 | 79 | $OBJ: $HFILES 80 | 81 | install:V: /$objtype/$p$CONF 82 | 83 | /$objtype/$p$CONF:D: $p$CONF 84 | cp -x $p$CONF /$objtype/ 85 | 86 | <../boot/bootmkfile 87 | <../port/portmkfile 88 | <|../port/mkbootrules $CONF 89 | 90 | main.$O: rebootcode.i 91 | 92 | #mmu.$O: /$objtype/include/ureg.h 93 | #l.$O mmu.$O: mem.h 94 | #l.$O mmu.$O: sysreg.h 95 | 96 | initcode.out: init9.$O initcode.$O /$objtype/lib/libc.a 97 | $LD -l -R1 -s -o $target $prereq 98 | 99 | rebootcode.out: rebootcode.$O cache.v8.$O 100 | $LD -l -H6 -R1 -T0x40020000 -s -o $target $prereq 101 | 102 | $CONF.clean: 103 | rm -rf $p$CONF s$p$CONF $p$CONF.u errstr.h $CONF.c boot$CONF.c 104 | -------------------------------------------------------------------------------- /mmu.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "../port/lib.h" 3 | #include "mem.h" 4 | #include "dat.h" 5 | #include "fns.h" 6 | #include "sysreg.h" 7 | 8 | void 9 | mmu0init(uintptr *l1) 10 | { 11 | uintptr va, pa, pe, attr; 12 | 13 | /* VDRAM */ 14 | attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTESH(SHARE_INNER); 15 | pe = -KZERO; 16 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){ 17 | l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr; 18 | l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | attr; 19 | } 20 | 21 | attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE; 22 | pe = PHYSIO + IOSIZE; 23 | for(pa = PHYSIO, va = VIRTIO + PHYSIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){ 24 | if(((pa|va) & PGLSZ(1)-1) != 0){ 25 | l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE; 26 | for(; pa < pe && ((va|pa) & PGLSZ(1)-1) != 0; pa += PGLSZ(0), va += PGLSZ(0)){ 27 | assert(l1[PTLX(va, 0)] == 0); 28 | l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr; 29 | } 30 | break; 31 | } 32 | l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr; 33 | } 34 | 35 | if(PTLEVELS > 2) 36 | for(va = KSEG0; va != 0; va += PGLSZ(2)) 37 | l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE; 38 | if(PTLEVELS > 3) 39 | for(va = KSEG0; va != 0; va += PGLSZ(3)) 40 | l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE; 41 | } 42 | 43 | void 44 | mmu0clear(uintptr *l1) 45 | { 46 | uintptr va, pa, pe; 47 | 48 | pe = -VDRAM; 49 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)) 50 | if(PTL1X(pa, 1) != PTL1X(va, 1)) 51 | l1[PTL1X(pa, 1)] = 0; 52 | 53 | if(PTLEVELS > 2) 54 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(2), va += PGLSZ(2)) 55 | if(PTL1X(pa, 2) != PTL1X(va, 2)) 56 | l1[PTL1X(pa, 2)] = 0; 57 | if(PTLEVELS > 3) 58 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(3), va += PGLSZ(3)) 59 | if(PTL1X(pa, 3) != PTL1X(va, 3)) 60 | l1[PTL1X(pa, 3)] = 0; 61 | } 62 | 63 | void 64 | mmuidmap(uintptr *l1) 65 | { 66 | uintptr va, pa, pe; 67 | 68 | pe = -VDRAM; 69 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)) 70 | l1[PTL1X(pa, 1)] = l1[PTL1X(va, 1)]; 71 | if(PTLEVELS > 2) 72 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(2), va += PGLSZ(2)) 73 | l1[PTL1X(pa, 2)] = l1[PTL1X(va, 2)]; 74 | if(PTLEVELS > 3) 75 | for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(3), va += PGLSZ(3)) 76 | l1[PTL1X(pa, 3)] = l1[PTL1X(va, 3)]; 77 | setttbr(PADDR(&l1[L1TABLEX(0, PTLEVELS-1)])); 78 | flushtlb(); 79 | } 80 | 81 | void 82 | mmu1init(void) 83 | { 84 | m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0); 85 | if(m->mmutop == nil) 86 | panic("mmu1init: no memory for mmutop"); 87 | memset(m->mmutop, 0, L1TOPSIZE); 88 | mmuswitch(nil); 89 | } 90 | 91 | /* KZERO maps the first 1GB of ram */ 92 | uintptr 93 | paddr(void *va) 94 | { 95 | if((uintptr)va >= KZERO) 96 | return (uintptr)va-KZERO; 97 | panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va)); 98 | return 0; 99 | } 100 | 101 | uintptr 102 | cankaddr(uintptr pa) 103 | { 104 | if(pa < (uintptr)-KZERO) 105 | return -KZERO - pa; 106 | return 0; 107 | } 108 | 109 | void* 110 | kaddr(uintptr pa) 111 | { 112 | if(pa < (uintptr)-KZERO) 113 | return (void*)(pa + KZERO); 114 | panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa)); 115 | return nil; 116 | } 117 | 118 | static void* 119 | kmapaddr(uintptr pa) 120 | { 121 | if(pa < (uintptr)-KZERO) 122 | return (void*)(pa + KZERO); 123 | if(pa < (VDRAM - KZERO) || pa >= (VDRAM - KZERO) + (KMAPEND - KMAP)) 124 | panic("kmapaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa)); 125 | return (void*)(pa + KMAP - (VDRAM - KZERO)); 126 | } 127 | 128 | KMap* 129 | kmap(Page *p) 130 | { 131 | return kmapaddr(p->pa); 132 | } 133 | 134 | void 135 | kunmap(KMap*) 136 | { 137 | } 138 | 139 | void 140 | kmapinval(void) 141 | { 142 | } 143 | 144 | #define INITMAP (ROUND((uintptr)end + BY2PG, PGLSZ(1))-KZERO) 145 | 146 | static void* 147 | rampage(void) 148 | { 149 | uintptr pa; 150 | 151 | if(conf.npage) 152 | return mallocalign(BY2PG, BY2PG, 0, 0); 153 | 154 | pa = conf.mem[0].base; 155 | assert((pa % BY2PG) == 0); 156 | assert(pa < INITMAP); 157 | conf.mem[0].base += BY2PG; 158 | return KADDR(pa); 159 | } 160 | 161 | static void 162 | l1map(uintptr va, uintptr pa, uintptr pe, uintptr attr) 163 | { 164 | uintptr *l1, *l0; 165 | 166 | assert(pa < pe); 167 | 168 | va &= -BY2PG; 169 | pa &= -BY2PG; 170 | pe = PGROUND(pe); 171 | 172 | attr |= PTEKERNEL | PTEAF; 173 | 174 | l1 = (uintptr*)L1; 175 | 176 | while(pa < pe){ 177 | if(l1[PTL1X(va, 1)] == 0 && (pe-pa) >= PGLSZ(1) && ((va|pa) & PGLSZ(1)-1) == 0){ 178 | l1[PTL1X(va, 1)] = PTEVALID | PTEBLOCK | pa | attr; 179 | va += PGLSZ(1); 180 | pa += PGLSZ(1); 181 | continue; 182 | } 183 | if(l1[PTL1X(va, 1)] & PTEVALID) { 184 | assert((l1[PTL1X(va, 1)] & PTETABLE) == PTETABLE); 185 | l0 = KADDR(l1[PTL1X(va, 1)] & -PGLSZ(0)); 186 | } else { 187 | l0 = rampage(); 188 | memset(l0, 0, BY2PG); 189 | l1[PTL1X(va, 1)] = PTEVALID | PTETABLE | PADDR(l0); 190 | } 191 | assert(l0[PTLX(va, 0)] == 0); 192 | l0[PTLX(va, 0)] = PTEVALID | PTEPAGE | pa | attr; 193 | va += BY2PG; 194 | pa += BY2PG; 195 | } 196 | } 197 | 198 | static void 199 | kmapram(uintptr base, uintptr limit) 200 | { 201 | if(base < (uintptr)-KZERO && limit > (uintptr)-KZERO){ 202 | kmapram(base, (uintptr)-KZERO); 203 | kmapram((uintptr)-KZERO, limit); 204 | return; 205 | } 206 | if(base < INITMAP) 207 | base = INITMAP; 208 | if(base >= limit || limit <= INITMAP) 209 | return; 210 | 211 | l1map((uintptr)kmapaddr(base), base, limit, 212 | PTEWRITE | PTEPXN | PTEUXN | PTESH(SHARE_INNER)); 213 | } 214 | 215 | void 216 | meminit(void) 217 | { 218 | uintptr va, pa; 219 | int i; 220 | 221 | conf.mem[0].base = PHYSDRAM; 222 | conf.mem[0].limit = PHYSDRAM + DRAMSIZE; 223 | 224 | /* 225 | * now we know the real memory regions, unmap 226 | * everything above INITMAP and map again with 227 | * the proper sizes. 228 | */ 229 | coherence(); 230 | for(va = INITMAP+KZERO; va != 0; va += PGLSZ(1)){ 231 | pa = va-KZERO; 232 | ((uintptr*)L1)[PTL1X(pa, 1)] = 0; 233 | ((uintptr*)L1)[PTL1X(va, 1)] = 0; 234 | } 235 | flushtlb(); 236 | 237 | pa = PGROUND((uintptr)end)-KZERO; 238 | for(i=0; i= KMAPEND-KMAP) 240 | conf.mem[i].limit = KMAPEND-KMAP; 241 | 242 | if(conf.mem[i].limit <= conf.mem[i].base){ 243 | conf.mem[i].limit = conf.mem[i].base = 0; 244 | continue; 245 | } 246 | 247 | if(conf.mem[i].base < PHYSDRAM + DRAMSIZE 248 | && conf.mem[i].limit > PHYSDRAM + DRAMSIZE) 249 | conf.mem[i].limit = PHYSDRAM + DRAMSIZE; 250 | 251 | /* take kernel out of allocatable space */ 252 | if(pa > conf.mem[i].base && pa < conf.mem[i].limit) 253 | conf.mem[i].base = pa; 254 | 255 | kmapram(conf.mem[i].base, conf.mem[i].limit); 256 | } 257 | flushtlb(); 258 | 259 | /* rampage() is now done, count up the pages for each bank */ 260 | for(i=0; immutop; 315 | for(i = PTLEVELS-2; i >= level; i--){ 316 | pte = table[x]; 317 | if(pte & PTEVALID) { 318 | if(pte & (0xFFFFULL<<48)) 319 | iprint("strange pte %#p va %#p\n", pte, va); 320 | pte &= ~(0xFFFFULL<<48 | BY2PG-1); 321 | } else { 322 | pg = up->mmufree; 323 | if(pg == nil) 324 | return nil; 325 | up->mmufree = pg->next; 326 | pg->va = va & -PGLSZ(i+1); 327 | if((pg->next = up->mmuhead[i+1]) == nil) 328 | up->mmutail[i+1] = pg; 329 | up->mmuhead[i+1] = pg; 330 | pte = pg->pa; 331 | memset(kmapaddr(pte), 0, BY2PG); 332 | coherence(); 333 | table[x] = pte | PTEVALID | PTETABLE; 334 | } 335 | table = kmapaddr(pte); 336 | x = PTLX(va, (uintptr)i); 337 | } 338 | return &table[x]; 339 | } 340 | 341 | static Proc *asidlist[256]; 342 | 343 | static int 344 | allocasid(Proc *p) 345 | { 346 | static Lock lk; 347 | Proc *x; 348 | int a; 349 | 350 | lock(&lk); 351 | a = p->asid; 352 | if(a < 0) 353 | a = -a; 354 | if(a == 0) 355 | a = p->pid; 356 | for(;; a++){ 357 | a %= nelem(asidlist); 358 | if(a == 0) 359 | continue; // reserved 360 | x = asidlist[a]; 361 | if(x == p || x == nil || (x->asid < 0 && x->mach == nil)) 362 | break; 363 | } 364 | p->asid = a; 365 | asidlist[a] = p; 366 | unlock(&lk); 367 | 368 | return x != p; 369 | } 370 | 371 | static void 372 | freeasid(Proc *p) 373 | { 374 | int a; 375 | 376 | a = p->asid; 377 | if(a < 0) 378 | a = -a; 379 | if(a > 0 && asidlist[a] == p) 380 | asidlist[a] = nil; 381 | p->asid = 0; 382 | } 383 | 384 | void 385 | putasid(Proc *p) 386 | { 387 | /* 388 | * Prevent the following scenario: 389 | * pX sleeps on cpuA, leaving its page tables in mmutop 390 | * pX wakes up on cpuB, and exits, freeing its page tables 391 | * pY on cpuB allocates a freed page table page and overwrites with data 392 | * cpuA takes an interrupt, and is now running with bad page tables 393 | * In theory this shouldn't hurt because only user address space tables 394 | * are affected, and mmuswitch will clear mmutop before a user process is 395 | * dispatched. But empirically it correlates with weird problems, eg 396 | * resetting of the core clock at 0x4000001C which confuses local timers. 397 | */ 398 | if(conf.nmach > 1) 399 | mmuswitch(nil); 400 | 401 | if(p->asid > 0) 402 | p->asid = -p->asid; 403 | } 404 | 405 | void 406 | putmmu(uintptr va, uintptr pa, Page *pg) 407 | { 408 | uintptr *pte, old; 409 | int s; 410 | 411 | s = splhi(); 412 | while((pte = mmuwalk(va, 0)) == nil){ 413 | spllo(); 414 | up->mmufree = newpage(0, nil, 0); 415 | splhi(); 416 | } 417 | old = *pte; 418 | *pte = 0; 419 | if((old & PTEVALID) != 0) 420 | flushasidvall((uvlong)up->asid<<48 | va>>12); 421 | else 422 | flushasidva((uvlong)up->asid<<48 | va>>12); 423 | *pte = pa | PTEPAGE | PTEUSER | PTEPXN | PTENG | PTEAF | 424 | (((pa & PTEMA(7)) == PTECACHED)? PTESH(SHARE_INNER): PTESH(SHARE_OUTER)); 425 | if(needtxtflush(pg)){ 426 | cachedwbinvse(kmap(pg), BY2PG); 427 | cacheiinvse((void*)va, BY2PG); 428 | donetxtflush(pg); 429 | } 430 | splx(s); 431 | } 432 | 433 | static void 434 | mmufree(Proc *p) 435 | { 436 | int i; 437 | 438 | freeasid(p); 439 | 440 | for(i=1; immuhead[i] == nil) 442 | break; 443 | p->mmutail[i]->next = p->mmufree; 444 | p->mmufree = p->mmuhead[i]; 445 | p->mmuhead[i] = p->mmutail[i] = nil; 446 | } 447 | } 448 | 449 | void 450 | mmuswitch(Proc *p) 451 | { 452 | uintptr va; 453 | Page *t; 454 | 455 | /* 456 | static char lasttext[32]; 457 | if((p != nil) && !p->kp){ 458 | if(strncmp(lasttext, p->text, sizeof lasttext) != 0) 459 | iprint("[%s]", p->text); 460 | strncpy(lasttext, p->text, sizeof lasttext); 461 | } 462 | */ 463 | 464 | for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1)) 465 | m->mmutop[PTLX(va, PTLEVELS-1)] = 0; 466 | 467 | if(p == nil){ 468 | setttbr(PADDR(m->mmutop)); 469 | return; 470 | } 471 | 472 | if(p->newtlb){ 473 | mmufree(p); 474 | p->newtlb = 0; 475 | } 476 | 477 | if(allocasid(p)) 478 | flushasid((uvlong)p->asid<<48); 479 | 480 | setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop)); 481 | 482 | for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){ 483 | va = t->va; 484 | m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID | PTETABLE; 485 | } 486 | } 487 | 488 | void 489 | mmurelease(Proc *p) 490 | { 491 | mmuswitch(nil); 492 | mmufree(p); 493 | freepages(p->mmufree, nil, 0); 494 | p->mmufree = nil; 495 | } 496 | 497 | void 498 | flushmmu(void) 499 | { 500 | int x; 501 | 502 | x = splhi(); 503 | up->newtlb = 1; 504 | mmuswitch(up); 505 | splx(x); 506 | } 507 | 508 | void 509 | checkmmu(uintptr, uintptr) 510 | { 511 | } 512 | 513 | static void* 514 | ucramalloc(usize size, uintptr align, uint attr) 515 | { 516 | static uintptr top = UCRAMBASE + UCRAMSIZE; 517 | static Lock lk; 518 | uintptr va, pg; 519 | 520 | lock(&lk); 521 | top -= size; 522 | size += top & align-1; 523 | top &= -align; 524 | if(top < UCRAMBASE) 525 | panic("ucramalloc: need %zd bytes", size); 526 | va = KZERO + top; 527 | pg = va & -BY2PG; 528 | if(pg != ((va+size) & -BY2PG)) 529 | mmukmap(pg | attr, pg - KZERO, PGROUND(size)); 530 | unlock(&lk); 531 | 532 | return (void*)va; 533 | } 534 | 535 | void* 536 | ucalloc(usize size) 537 | { 538 | return ucramalloc(size, 8, PTEUNCACHED); 539 | } 540 | 541 | void* 542 | fbmemalloc(usize size) 543 | { 544 | return ucramalloc(PGROUND(size), BY2PG, PTEWT); 545 | } 546 | 547 | -------------------------------------------------------------------------------- /notes: -------------------------------------------------------------------------------- 1 | uart is a 16550 like 2 | 3 | CPU 4 | 5 | - Cortex-A53 (used in RPI 3) 6 | - 4 cores 7 | - 512 KB L2-cache 8 | 9 | gic-400 interrupt controller 10 | GIC 0x01C80000 11 | GICD_ + 0x1000 12 | GICC_ + 0x2000 13 | 14 | 15 | Memory mapping: a64man.pdf!73 16 | 0x01c00000 - 0x01f04000 17 | 18 | all "module registers" are mapped in memory 19 | SDRAM is the last "register" with 3GB 20 | 21 | 22 | bootloader: DRAM clock freq 528 23 | 24 | Booting: 25 | fatload mmc 0 0x60000000 9pinephone 26 | go 0x60000000 27 | 28 | 29 | MMAP: 30 | 31 | 0x5f80 0000 KZERO (KTZERO-0x800000) 32 | 0x6000 0000 KTZERO 33 | 34 | 35 | KZERO: 36 | 0x0000 0000 Machs 37 | 38 | ddr starts at 0x40000000 39 | 2gb goes to 0xC0000000 40 | 41 | 42 | 43 | delay chain 44 | phy-mode = "rgmii"; 45 | tx-delay = <0x3>; 46 | rx-delay = <0x0>; 47 | 48 | 49 | old mmu meminit 50 | I really would like something 51 | better for uncached ram 52 | pa = PGROUND((uintptr)end)-KZERO; 53 | for(i=0; i= KMAPEND-KMAP) 55 | conf.mem[i].limit = KMAPEND-KMAP; 56 | 57 | if(conf.mem[i].limit <= conf.mem[i].base){ 58 | conf.mem[i].limit = conf.mem[i].base = 0; 59 | continue; 60 | } 61 | 62 | if(conf.mem[i].base < PHYSDRAM + DRAMSIZE 63 | && conf.mem[i].limit > PHYSDRAM + DRAMSIZE) 64 | conf.mem[i].limit = PHYSDRAM + DRAMSIZE; 65 | 66 | /* take kernel out of allocatable space */ 67 | if(pa > conf.mem[i].base && pa < conf.mem[i].limit) 68 | conf.mem[i].base = pa; 69 | 70 | kmapram(conf.mem[i].base, conf.mem[i].limit); 71 | } 72 | 73 | new mmu meminit, 74 | seems to cause ilock issues 75 | conf.mem[0].base = PGROUND((uintptr)end - KZERO); 76 | 77 | /* exclude uncached dram for ucalloc() */ 78 | conf.mem[0].limit = UCRAMBASE; 79 | conf.mem[1].base = UCRAMBASE+UCRAMSIZE; 80 | 81 | conf.mem[1].limit = PHYSDRAM + DRAMSIZE; 82 | 83 | kmapram(conf.mem[0].base, conf.mem[0].limit); 84 | kmapram(conf.mem[1].base, conf.mem[1].limit); 85 | 86 | conf.mem[0].npage = (conf.mem[0].limit - conf.mem[0].base)/BY2PG; 87 | conf.mem[1].npage = (conf.mem[1].limit - conf.mem[1].base)/BY2PG; 88 | 89 | enum { 90 | Phyidr1 = 0x02, /* PHY Identifier #1 */ 91 | Phyidr2 = 0x03, /* PHY Identifier #2 */ 92 | }; 93 | 94 | 95 | mw.b 0x40010000 0x0 0x10000; load usb 0 0x40010000 plan9.ini; load usb 0 0x40080000 9pine64; go 0x40080000 96 | -------------------------------------------------------------------------------- /pine64: -------------------------------------------------------------------------------- 1 | dev 2 | root 3 | cons 4 | swap 5 | env 6 | pipe 7 | proc 8 | mnt 9 | srv 10 | shr 11 | dup 12 | arch 13 | tls 14 | cap 15 | fs 16 | ether netif 17 | bridge log 18 | ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno 19 | uart 20 | rtc 21 | 22 | link 23 | ethermedium 24 | loopbackmedium 25 | netdevmedium 26 | # ethersunxi ethermii 27 | 28 | ip 29 | tcp 30 | udp 31 | il 32 | ipifc 33 | icmp 34 | icmp6 35 | ipmux 36 | 37 | misc 38 | uarti8250 39 | keyadc 40 | ccu 41 | thermal 42 | rsb 43 | axp803 44 | 45 | port 46 | int cpuserver = 0; 47 | 48 | bootdir 49 | /$objtype/bin/paqfs 50 | /$objtype/bin/auth/factotum 51 | bootfs.paq 52 | boot 53 | # nvram 54 | -------------------------------------------------------------------------------- /pinephone: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adventuresin9/9front-A64/5401937e13a1683ec8d65e1ae66bd771f7ef5af8/pinephone -------------------------------------------------------------------------------- /rebootcode.s: -------------------------------------------------------------------------------- 1 | #include "mem.h" 2 | #include "sysreg.h" 3 | 4 | #undef SYSREG 5 | #define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)) 6 | 7 | TEXT _start(SB), 1, $-4 8 | MOV $setSB(SB), R28 9 | 10 | MOV R0, R27 11 | 12 | MOV code+8(FP), R1 13 | MOVWU size+16(FP), R2 14 | BIC $3, R2 15 | ADD R1, R2, R3 16 | 17 | _copy: 18 | MOVW (R1)4!, R4 19 | MOVW R4, (R0)4! 20 | CMP R1, R3 21 | BNE _copy 22 | 23 | BL cachedwbinv(SB) 24 | BL l2cacheuwbinv(SB) 25 | 26 | ISB $SY 27 | MRS SCTLR_EL1, R0 28 | BIC $(1<<0 | 1<<2 | 1<<12), R0 29 | ISB $SY 30 | MSR R0, SCTLR_EL1 31 | ISB $SY 32 | 33 | DSB $NSHST 34 | TLBI R0, 0,8,7,0 /* VMALLE1 */ 35 | DSB $NSH 36 | ISB $SY 37 | 38 | BL cachedwbinv(SB) 39 | BL cacheiinv(SB) 40 | 41 | MOVWU $0, R0 42 | MOVWU $0, R1 43 | MOVWU $0, R2 44 | MOVWU $0, R3 45 | 46 | MOV R27, LR 47 | 48 | RETURN 49 | -------------------------------------------------------------------------------- /rsb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Reduced Serial Bus(RSB™) 3 | * some special I²C bus from Allwinner 4 | * used to talk to the power controller 5 | * 6 | * cobbled together from documentation 7 | * for the Allwinner A80 and NetBSD 8 | */ 9 | 10 | #include "u.h" 11 | #include "../port/lib.h" 12 | #include "mem.h" 13 | #include "dat.h" 14 | #include "fns.h" 15 | #include "../port/error.h" 16 | #include "io.h" 17 | 18 | 19 | #define RSB_CTRL_REG 0x0000 20 | #define RSB_CTRL_START_TRANS 1<<7 21 | #define RSB_CTRL_ABORT_TRANS 1<<6 22 | #define RSB_CTRL_GLOBAL_INT_ENB 1<<1 23 | #define RSB_CTRL_SOFT_RESET 1<<0 24 | #define RSB_CCR_REG 0x0004 25 | #define RSB_CCR_SDA_ODLY 1<<8 //__BITS(10,8) 26 | #define RSB_CCR_CLK_DIV 1<<0 //__BITS(7,0) 27 | #define RSB_INTE_REG 0x0008 28 | #define RSB_INTE_LOAD_BSY_ENB 1<<2 29 | #define RSB_INTE_TRANS_ERR_ENB 1<<1 30 | #define RSB_INTE_TRANS_OVER_ENB 1<<0 31 | #define RSB_STAT_REG 0x000c 32 | #define RSB_STAT_TRANS_ERR_ID 1<<8 //__BITS(15,8) 33 | #define RSB_STAT_ERR_NACK 1<<16 34 | #define RSB_STAT_LOAD_BSY 1<<2 35 | #define RSB_STAT_TRANS_ERR 1<<1 36 | #define RSB_STAT_TRANS_OVER 1<<0 37 | #define RSB_STAT_MASK \ 38 | (RSB_STAT_LOAD_BSY | \ 39 | RSB_STAT_TRANS_ERR | \ 40 | RSB_STAT_TRANS_OVER) 41 | #define RSB_DADDR0_REG 0x0010 42 | #define RSB_DADDR1_REG 0x0014 43 | #define RSB_DLEN_REG 0x0018 44 | #define RSB_DLEN_READ_WRITE_FLAG 1<<4 45 | #define RSB_DLEN_ACCESS_LENGTH(v) (v)<<0 //__BITS(2,0) 46 | #define RSB_DATA0_REG 0x001c 47 | #define RSB_DATA1_REG 0x0020 48 | #define RSB_LCR_REG 0x0024 49 | #define RSB_LCR_SCL_STATE 1<<5 50 | #define RSB_LCR_SDA_STATE 1<<4 51 | #define RSB_LCR_SCL_CTL 1<<3 52 | #define RSB_LCR_SCL_CTL_EN 1<<2 53 | #define RSB_LCR_SDA_CTL 1<<1 54 | #define RSB_LCR_SDA_CTL_EN 1<<0 55 | #define RSB_PMCR_REG 0x0028 56 | #define RSB_PMCR_PMU_SEND 1<<31 57 | #define RSB_PMCR_PMU_DATA(d) ((d) & 0xFF)<<16 //__BITS(23,16) 58 | #define RSB_PMCR_PMU_REG(r) ((r) & 0xFF)<<8 //__BITS(15,8) 59 | #define RSB_PMCR_PMU_ADDR(a) ((a) & 0xFF)<<0 //__BITS(7,0) 60 | #define RSB_CMD_REG 0x002c 61 | #define RSB_CMD_IDX 1<<0 //__BITS(7,0) 62 | #define RSB_CMD_IDX_SRTA 0xe8 63 | #define RSB_CMD_IDX_RD8 0x8b 64 | #define RSB_CMD_IDX_RD16 0x9c 65 | #define RSB_CMD_IDX_RD32 0xa6 66 | #define RSB_CMD_IDX_WR8 0x4e 67 | #define RSB_CMD_IDX_WR16 0x59 68 | #define RSB_CMD_IDX_WR32 0x63 69 | #define RSB_DAR_REG 0x0030 70 | #define RSB_DAR_RTA(x) (x)<<16 //__BITS(23,16) 71 | #define RSB_DAR_DA(x) (x)<<0 //__BITS(15,0) 72 | 73 | 74 | 75 | 76 | static u32int 77 | rsbrd(u32int offset) 78 | { 79 | return *IO(u32int, (R_RSB + offset)); 80 | } 81 | 82 | 83 | static void 84 | rsbwr(int offset, u32int val) 85 | { 86 | *IO(u32int, (R_RSB + offset)) = val; 87 | } 88 | 89 | 90 | static int 91 | rsbidle(void) 92 | { 93 | u32int buf; 94 | int timeout = 1000; 95 | 96 | while(timeout > 0){ 97 | buf = rsbrd(RSB_CTRL_REG); 98 | if((buf & RSB_CTRL_START_TRANS) == 0) 99 | return 1; 100 | timeout--; 101 | delay(10); 102 | } 103 | 104 | /* not idle */ 105 | return -1; 106 | } 107 | 108 | 109 | static int 110 | rsbwait(void) 111 | { 112 | int timeout = 1000; 113 | 114 | while(timeout > 0){ 115 | if((rsbrd(RSB_PMCR_REG) & RSB_PMCR_PMU_SEND) == 0) 116 | return 1; 117 | timeout--; 118 | delay(10); 119 | } 120 | return 0; 121 | } 122 | 123 | 124 | /* 125 | * This uses the RSB Device Mode Control Register 126 | * to send a command to the PMIC, which on the 127 | * Allwinner A64 boards should be the AXP803. 128 | * pokepmic() send the value 0x7C to register 0x3E, 129 | * which tells the PMIC to use RSB rather than TWI 130 | */ 131 | 132 | static char* 133 | pokepmic(void) 134 | { 135 | u32int buf; 136 | 137 | buf = (RSB_PMCR_PMU_ADDR(0x0) | RSB_PMCR_PMU_REG(0x3E) | 138 | RSB_PMCR_PMU_DATA(0x7C) | RSB_PMCR_PMU_SEND); 139 | 140 | rsbwr(RSB_PMCR_REG, buf); 141 | 142 | if(!rsbwait()){ 143 | return("PMIC init FAILED"); 144 | } 145 | 146 | return nil; 147 | } 148 | 149 | 150 | static int 151 | rsbreset(void) 152 | { 153 | int timeout = 1000; 154 | 155 | rsbwr(RSB_CTRL_REG, RSB_CTRL_SOFT_RESET); 156 | 157 | while(timeout > 0){ 158 | if((rsbrd(RSB_CTRL_REG) & RSB_CTRL_SOFT_RESET) == 0) 159 | break; 160 | timeout--; 161 | delay(10); 162 | } 163 | 164 | if(timeout == 0){ 165 | return -1; 166 | } 167 | 168 | return 1; 169 | } 170 | 171 | 172 | static int 173 | rsb_xmit(void) 174 | { 175 | u32int buf; 176 | 177 | /* set interrupts */ 178 | rsbwr(RSB_INTE_REG, RSB_INTE_LOAD_BSY_ENB | RSB_INTE_TRANS_ERR_ENB | RSB_INTE_TRANS_OVER_ENB); 179 | 180 | /* start transfer with interrupts enabled */ 181 | // rsbwr(RSB_CTRL_REG, RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB); 182 | 183 | /* start transfer without interrupts enabled */ 184 | rsbwr(RSB_CTRL_REG, RSB_CTRL_START_TRANS); 185 | 186 | /* wait for the transmission to finish */ 187 | if(!rsbidle()){ 188 | iprint("RSB: read timeout\n"); 189 | return 0; 190 | } 191 | 192 | /* check for errors */ 193 | buf = rsbrd(RSB_STAT_REG); 194 | if(buf & RSB_STAT_TRANS_ERR){ 195 | if(buf & RSB_STAT_ERR_NACK) 196 | iprint("NACK! "); 197 | iprint("RSB: read transmit error\n"); 198 | return 0; 199 | } 200 | if(buf & RSB_STAT_LOAD_BSY){ 201 | iprint("RSB: read busy error\n"); 202 | return 0; 203 | } 204 | 205 | return 1; 206 | } 207 | 208 | 209 | 210 | u32int 211 | rsb_read(u8int rtaddr, u16int devaddr, u8int reg, uint bytes) 212 | { 213 | u32int cmd; 214 | 215 | if(!rsbreset()){ 216 | print("RSB: read reset FAIL\n"); 217 | return 0; 218 | } 219 | 220 | /* set command for READ and byte size */ 221 | switch(bytes){ 222 | case(1): 223 | cmd = RSB_CMD_IDX_RD8; 224 | break; 225 | case(2): 226 | cmd = RSB_CMD_IDX_RD16; 227 | break; 228 | case(4): 229 | cmd = RSB_CMD_IDX_RD32; 230 | break; 231 | default: 232 | print("RSB: invalid read size\n"); 233 | return 0; 234 | } 235 | 236 | /* Make sure the controller is idle */ 237 | if(!rsbidle()){ 238 | print("RSB: read not idle\n"); 239 | return 0; 240 | } 241 | 242 | /* set rtaddr and devaddr for the device to talk to */ 243 | // rsbwr(RSB_DAR_REG, (rtaddr << 16) | devaddr); 244 | rsbwr(RSB_DAR_REG, (rtaddr << 16)); 245 | rsbwr(RSB_CMD_REG, RSB_CMD_IDX_SRTA); 246 | 247 | /* set the register in the device to be read */ 248 | rsbwr(RSB_DADDR0_REG, reg); 249 | 250 | /* set the command for the action to perform */ 251 | rsbwr(RSB_CMD_REG, cmd); 252 | 253 | /* run transmit and error check */ 254 | if(rsb_xmit()) 255 | return rsbrd(RSB_DATA0_REG); 256 | 257 | return 0; 258 | } 259 | 260 | 261 | u32int 262 | rsb_write(u8int rtaddr, u16int devaddr, u8int reg, u32int val, uint bytes) 263 | { 264 | u32int cmd; 265 | 266 | if(!rsbreset()){ 267 | iprint("RSB: write reset FAIL\n"); 268 | return 0; 269 | } 270 | 271 | /* set command for WRITE and byte size */ 272 | switch(bytes){ 273 | case(1): 274 | cmd = RSB_CMD_IDX_WR8; 275 | break; 276 | case(2): 277 | cmd = RSB_CMD_IDX_WR16; 278 | break; 279 | case(4): 280 | cmd = RSB_CMD_IDX_WR32; 281 | break; 282 | default: 283 | print("RSB: invalid write size\n"); 284 | return 0; 285 | } 286 | 287 | /* Make sure the controller is idle */ 288 | if(!rsbidle()){ 289 | iprint("RSB: write not idle\n"); 290 | return 0; 291 | } 292 | 293 | /* set rtaddr and devaddr for the device to talk to */ 294 | // rsbwr(RSB_DAR_REG, (rtaddr << 16) | devaddr); 295 | rsbwr(RSB_DAR_REG, (rtaddr << 16)); 296 | rsbwr(RSB_CMD_REG, RSB_CMD_IDX_SRTA); 297 | 298 | /* set the register in the device to be writen to */ 299 | rsbwr(RSB_DADDR0_REG, reg); 300 | 301 | /* set data to write to device register */ 302 | rsbwr(RSB_DATA0_REG, val); 303 | 304 | /* set the command for the action to perform */ 305 | rsbwr(RSB_CMD_REG, cmd); 306 | 307 | /* run transmit and error check */ 308 | if(rsb_xmit()) 309 | return 1; 310 | 311 | iprint("rsb_write: failed\n"); 312 | return 0; 313 | } 314 | 315 | 316 | static void 317 | rsbinterrupt(Ureg*, void*) 318 | { 319 | u32int buf; 320 | 321 | buf = rsbrd(RSB_STAT_REG); 322 | 323 | 324 | /* clear held interrupts */ 325 | rsbwr(RSB_STAT_REG, buf & RSB_STAT_MASK); 326 | 327 | /* if just "Transfer Over Flag" then leave */ 328 | if(buf & RSB_STAT_TRANS_OVER && ((buf & ~RSB_STAT_TRANS_OVER) == 0)) 329 | return; 330 | 331 | iprint("RSBintr: "); 332 | 333 | if(buf & RSB_STAT_TRANS_ERR) 334 | iprint("Transfer Error, "); 335 | 336 | if(buf & RSB_STAT_ERR_NACK) 337 | iprint("No ACK, "); 338 | 339 | if(buf & 0xF00) 340 | iprint("in byte %uX, ", ((buf & 0xF00)>>8)); 341 | 342 | if(buf & RSB_STAT_LOAD_BSY) 343 | iprint("Busy "); 344 | 345 | if((buf & RSB_STAT_MASK) == 0) 346 | iprint("spurious interrupt"); 347 | 348 | iprint("\n"); 349 | } 350 | 351 | 352 | static char* 353 | rsbbusspeed(u32int speed) 354 | { 355 | u32int buf; 356 | 357 | if(speed == 0) 358 | return("no bus speed"); 359 | 360 | buf = SYSCLOCK / speed; 361 | if(buf < 2) 362 | return("speed too low"); 363 | 364 | buf = buf / 2 - 1; 365 | buf |= (1 << 8); 366 | 367 | rsbwr(RSB_CCR_REG, buf); 368 | 369 | return nil; 370 | } 371 | 372 | 373 | void 374 | rsbinit(void) 375 | { 376 | char *err; 377 | 378 | iprint("init: RSB\n"); 379 | intrenable(IRQrsb, rsbinterrupt, nil, BUSUNKNOWN, "RSB"); 380 | 381 | arch_rsbsetup(); 382 | 383 | rsbreset(); 384 | 385 | /* set bus speed to i2c */ 386 | if((err = rsbbusspeed(400000)) != nil) 387 | iprint(err); 388 | 389 | 390 | /* set device mode */ 391 | if((err = pokepmic()) != nil) 392 | iprint(err); 393 | 394 | /* set bus speed to rsb */ 395 | if((err = rsbbusspeed(3000000)) != nil) 396 | iprint(err); 397 | 398 | 399 | /* assign runtime address */ 400 | rsbwr(RSB_DAR_REG, (PMICRTA << 16) | PMICADDR); 401 | rsbwr(RSB_CMD_REG, RSB_CMD_IDX_SRTA); 402 | rsbwr(RSB_CTRL_REG, RSB_CTRL_START_TRANS); 403 | 404 | if(!rsbidle()) 405 | iprint("RSB: init timeout\n"); 406 | 407 | if(rsbrd(RSB_STAT_REG) != 0x01) 408 | iprint("set runtime error\n"); 409 | 410 | } 411 | 412 | -------------------------------------------------------------------------------- /sysreg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ARMv8 system registers 3 | * mainly to cope with arm hard-wiring register numbers into instructions. 4 | * 5 | * these routines must be callable from KZERO. 6 | * 7 | * on a multiprocessor, process switching to another cpu is assumed 8 | * to be inhibited by the caller as these registers are local to the cpu. 9 | */ 10 | #include "u.h" 11 | #include "../port/lib.h" 12 | #include "mem.h" 13 | #include "dat.h" 14 | #include "fns.h" 15 | 16 | static void* 17 | mkinstr(ulong wd) 18 | { 19 | static ulong ib[256], *ep[MAXMACH+1]; 20 | static Lock lk; 21 | ulong *ip, *ie; 22 | 23 | ie = ep[m->machno]; 24 | for(ip = ib; ip < ie; ip += 2) 25 | if(*ip == wd) 26 | return ip; 27 | 28 | ilock(&lk); 29 | ie = ep[MAXMACH]; 30 | for(; ip < ie; ip += 2) 31 | if(*ip == wd) 32 | goto Found; 33 | if(ip >= &ib[nelem(ib)]) 34 | panic("mkinstr: out of instrucuction buffer"); 35 | ip[0] = wd; 36 | ip[1] = 0xd65f03c0; // RETURN 37 | ep[MAXMACH] = ie = ip + 2; 38 | cachedwbinvse(ip, 2*sizeof(*ip)); 39 | Found: 40 | iunlock(&lk); 41 | cacheiinv(); 42 | ep[m->machno] = ie; 43 | return ip; 44 | } 45 | 46 | uvlong 47 | sysrd(ulong spr) 48 | { 49 | uvlong (*fp)(void) = mkinstr(0xd5380000UL | spr); 50 | return fp(); 51 | } 52 | 53 | void 54 | syswr(ulong spr, uvlong val) 55 | { 56 | void (*fp)(uvlong) = mkinstr(0xd5180000UL | spr); 57 | fp(val); 58 | } 59 | -------------------------------------------------------------------------------- /sysreg.h: -------------------------------------------------------------------------------- 1 | #define MIDR_EL1 SYSREG(3,0,0,0,0) 2 | #define MPIDR_EL1 SYSREG(3,0,0,0,5) 3 | #define ID_AA64AFR0_EL1 SYSREG(3,0,0,5,4) 4 | #define ID_AA64AFR1_EL1 SYSREG(3,0,0,5,5) 5 | #define ID_AA64DFR0_EL1 SYSREG(3,0,0,5,0) 6 | #define ID_AA64DFR1_EL1 SYSREG(3,0,0,5,1) 7 | #define ID_AA64ISAR0_EL1 SYSREG(3,0,0,6,0) 8 | #define ID_AA64ISAR1_EL1 SYSREG(3,0,0,6,1) 9 | #define ID_AA64MMFR0_EL1 SYSREG(3,0,0,7,0) 10 | #define ID_AA64MMFR1_EL1 SYSREG(3,0,0,7,1) 11 | #define ID_AA64PFR0_EL1 SYSREG(3,0,0,4,0) 12 | #define ID_AA64PFR1_EL1 SYSREG(3,0,0,4,1) 13 | #define SCTLR_EL1 SYSREG(3,0,1,0,0) 14 | #define CPACR_EL1 SYSREG(3,0,1,0,2) 15 | #define MAIR_EL1 SYSREG(3,0,10,2,0) 16 | #define TCR_EL1 SYSREG(3,0,2,0,2) 17 | #define TTBR0_EL1 SYSREG(3,0,2,0,0) 18 | #define TTBR1_EL1 SYSREG(3,0,2,0,1) 19 | #define ESR_EL1 SYSREG(3,0,5,2,0) 20 | #define FAR_EL1 SYSREG(3,0,6,0,0) 21 | #define VBAR_EL1 SYSREG(3,0,12,0,0) 22 | #define VTTBR_EL2 SYSREG(3,4,2,1,0) 23 | #define SP_EL0 SYSREG(3,0,4,1,0) 24 | #define SP_EL1 SYSREG(3,4,4,1,0) 25 | #define SP_EL2 SYSREG(3,6,4,1,0) 26 | #define SCTLR_EL2 SYSREG(3,4,1,0,0) 27 | #define HCR_EL2 SYSREG(3,4,1,1,0) 28 | #define MDCR_EL2 SYSREG(3,4,1,1,1) 29 | #define PMCR_EL0 SYSREG(3,3,9,12,0) 30 | #define PMCNTENSET SYSREG(3,3,9,12,1) 31 | #define PMCCNTR_EL0 SYSREG(3,3,9,13,0) 32 | #define PMUSERENR_EL0 SYSREG(3,3,9,14,0) 33 | 34 | #define CNTFRQ_EL0 SYSREG(3,3,14,0,0) 35 | #define CNTPCT_EL0 SYSREG(3,3,14,0,1) 36 | #define CNTVCT_EL0 SYSREG(3,3,14,0,2) 37 | #define CNTKCTL_EL1 SYSREG(3,0,14,1,0) 38 | #define CNTP_TVAL_EL0 SYSREG(3,3,14,2,0) 39 | #define CNTP_CTL_EL0 SYSREG(3,3,14,2,1) 40 | #define CNTP_CVAL_EL0 SYSREG(3,3,14,2,2) 41 | 42 | #define TPIDR_EL0 SYSREG(3,3,13,0,2) 43 | #define TPIDR_EL1 SYSREG(3,0,13,0,4) 44 | 45 | #define CCSIDR_EL1 SYSREG(3,1,0,0,0) 46 | #define CSSELR_EL1 SYSREG(3,2,0,0,0) 47 | 48 | #define ACTLR_EL2 SYSREG(3,4,1,0,1) 49 | #define CPUACTLR_EL1 SYSREG(3,1,15,2,0) 50 | #define CPUECTLR_EL1 SYSREG(3,1,15,2,1) 51 | #define CBAR_EL1 SYSREG(3,1,15,3,0) 52 | 53 | /* l.s redefines this for the assembler */ 54 | #define SYSREG(op0,op1,Cn,Cm,op2) ((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5) 55 | 56 | #define OSHLD (0<<2 | 1) 57 | #define OSHST (0<<2 | 2) 58 | #define OSH (0<<2 | 3) 59 | #define NSHLD (1<<2 | 1) 60 | #define NSHST (1<<2 | 2) 61 | #define NSH (1<<2 | 3) 62 | #define ISHLD (2<<2 | 1) 63 | #define ISHST (2<<2 | 2) 64 | #define ISH (2<<2 | 3) 65 | #define LD (3<<2 | 1) 66 | #define ST (3<<2 | 2) 67 | #define SY (3<<2 | 3) 68 | -------------------------------------------------------------------------------- /thermal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Thermal Sensor Controller 3 | * A64 manual, pg 264 4 | * Temp = -8.56 * Data + 2170 °C 5 | */ 6 | 7 | 8 | #include "u.h" 9 | #include "../port/lib.h" 10 | #include "mem.h" 11 | #include "dat.h" 12 | #include "fns.h" 13 | #include "../port/error.h" 14 | #include "io.h" 15 | 16 | 17 | #define THS_CTRL0 0x00 /*THS Control Register0*/ 18 | #define THS_CTRL1 0x04 /*THS Control Register1*/ 19 | #define ADC_CDAT 0x14 /*ADC calibration data Register*/ 20 | #define THS_CTRL2 0x40 /*THS Control Register2*/ 21 | #define THS_INT_CTRL 0x44 /*THS Interrupt Control Register*/ 22 | #define THS_STAT 0x48 /*THS Status Register*/ 23 | #define THS0_ALARM_CTRL 0x50 /*Alarm threshold Control Register0*/ 24 | #define THS1_ALARM_CTRL 0x54 /*Alarm threshold Control Register1*/ 25 | #define THS2_ALARM_CTRL 0x58 /*Alarm threshold Control Register2*/ 26 | #define THS0_SHUTDOWN_CTRL 0x60 /*Shutdown threshold Control Register0*/ 27 | #define THS1_SHUTDOWN_CTRL 0x64 /*Shutdown threshold Control Register1*/ 28 | #define THS2_SHUTDOWN_CTRL 0x68 /*Shutdown threshold Control Register2*/ 29 | #define THS_FILTER 0x70 /*Median filter Control Register*/ 30 | #define THS0_1_CDATA 0x74 /*Thermal Sensor 0 1 Calibration Data*/ 31 | #define THS2_CDATA 0x78 /*Thermal Sensor2 Calibration Data*/ 32 | #define THS0_DATA 0x80 /*THS0 Data Register*/ 33 | #define THS1_DATA 0x84 /*THS1 Data Register*/ 34 | #define THS2_DATA 0x88 /*THS2 Data Register*/ 35 | 36 | #define THS_DATA_MASK 0xFFF 37 | 38 | enum { 39 | /*THS_CTRL0*/ 40 | Acq0 = 0xFFFF<<0, 41 | Acq0shft = 0, 42 | /*THS_CTRL1*/ 43 | OpBias = 0x3<<20, 44 | OBshft = 20, 45 | ACaliEn = 1<<17, 46 | /*ADC_CDAT*/ 47 | AdcCdat = 0xFFF<<0, 48 | ADCshft = 0, 49 | /*THS_CTRL2*/ 50 | Acq1 = 0xFFFF<<16, 51 | Acq1shft = 16, 52 | Sens2En = 1<<2, 53 | Sens1En = 1<<1, 54 | Sens0En = 1<<0, 55 | /*THS_INT_CTRL & THS_STAT*/ 56 | Idata2 = 1<<10, 57 | Idata1 = 1<<9, 58 | Idata0 = 1<<8, 59 | Ishut2 = 1<<6, 60 | Ishut1 = 1<<5, 61 | Ishut0 = 1<<4, 62 | Ialrm2 = 1<<2, 63 | Ialrm1 = 1<<1, 64 | Ialrm0 = 1<<0, 65 | /*THS_INT_CTRL*/ 66 | ThemPer = 0xFFFFF<<12, 67 | TPshft = 12, 68 | /*THS_STAT*/ 69 | IAoff2 = 1<<14, 70 | IAoff1 = 1<<13, 71 | IAoff0 = 1<<12, 72 | /*THS_FILTER*/ 73 | FltrEn = 1<<2, 74 | FltrType = 0x3<<0, 75 | FTshft = 0<<0, 76 | Fltr2 = 0x0, 77 | Fltr4 = 0x1, 78 | Fltr8 = 0x2, 79 | Fltr16 = 0x3, 80 | }; 81 | 82 | 83 | enum { 84 | TPval = 0x18, 85 | AcqTime = 0x190, 86 | TmpBase = 2170000, 87 | TempMul = 1000, 88 | TempDiv = 8560, 89 | }; 90 | 91 | 92 | static u32int 93 | thrmrd(int offset) 94 | { 95 | return *IO(u32int, (THERMAL + offset)); 96 | } 97 | 98 | 99 | static void 100 | thrmwr(int offset, u32int val) 101 | { 102 | *IO(u32int, (THERMAL + offset)) = val; 103 | } 104 | 105 | 106 | static int 107 | tempformula(u32int val) 108 | { 109 | return ((TmpBase - (val * TempMul)) / TempDiv); 110 | } 111 | 112 | 113 | int 114 | gettemp0(void) 115 | { 116 | u32int val; 117 | 118 | val = thrmrd(THS0_DATA); 119 | val &= THS_DATA_MASK; 120 | 121 | return tempformula(val); 122 | } 123 | 124 | 125 | int 126 | gettemp1(void) 127 | { 128 | u32int val; 129 | 130 | val = thrmrd(THS1_DATA); 131 | val &= THS_DATA_MASK; 132 | 133 | return tempformula(val); 134 | } 135 | 136 | 137 | int 138 | gettemp2(void) 139 | { 140 | u32int val; 141 | 142 | val = thrmrd(THS2_DATA); 143 | val &= THS_DATA_MASK; 144 | 145 | return tempformula(val); 146 | } 147 | 148 | 149 | static void 150 | thrminterrupt(Ureg*, void*) 151 | { 152 | u32int buf; 153 | 154 | buf = thrmrd(THS_STAT); 155 | thrmwr(THS_STAT, buf); 156 | 157 | if(buf & Ialrm0) 158 | print("CPU IS HOT\n"); 159 | 160 | if(buf & Ishut0) 161 | print("CPU ON FIRE!!!\n"); 162 | } 163 | 164 | 165 | void 166 | thermalinit(void) 167 | { 168 | u32int buf; 169 | 170 | /* turn on THS clock */ 171 | turnonths(); 172 | 173 | /* start calibration */ 174 | thrmwr(THS_CTRL1, ACaliEn); 175 | thrmwr(THS_CTRL0, AcqTime << Acq0shft); 176 | thrmwr(THS_CTRL2, AcqTime << Acq1shft); 177 | 178 | /* enable filter, average of 4 */ 179 | thrmwr(THS_FILTER, FltrEn | Fltr4); 180 | 181 | /* clear interrupts */ 182 | buf = thrmrd(THS_STAT); 183 | thrmwr(THS_STAT, buf); 184 | 185 | /* set interrupts and THERMAL_PER value */ 186 | thrmwr(THS_INT_CTRL, Ialrm0 | Ishut0 | (TPval << TPshft)); 187 | 188 | /* start sensors 0, 1, 2 */ 189 | buf = thrmrd(THS_CTRL2); 190 | buf |= (Sens0En | Sens1En | Sens2En);; 191 | thrmwr(THS_CTRL2, buf); 192 | 193 | intrenable(IRQthermal, thrminterrupt, nil, BUSUNKNOWN, "thermal"); 194 | } -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | #include "u.h" 2 | #include "../port/lib.h" 3 | #include "mem.h" 4 | #include "dat.h" 5 | #include "fns.h" 6 | #include "../port/error.h" 7 | #include "../port/systab.h" 8 | 9 | #include 10 | #include "ureg.h" 11 | #include "sysreg.h" 12 | 13 | int (*buserror)(Ureg*); 14 | 15 | /* SPSR bits user can modify */ 16 | #define USPSRMASK (0xFULL<<28) 17 | 18 | static void 19 | setupvector(u32int *v, void (*t)(void), void (*f)(void)) 20 | { 21 | int i; 22 | 23 | for(i = 0; i < 0x80/4; i++){ 24 | v[i] = ((u32int*)t)[i]; 25 | if(v[i] == 0x14000000){ 26 | v[i] |= ((u32int*)f - &v[i]) & 0x3ffffff; 27 | return; 28 | } 29 | } 30 | panic("bug in vector code"); 31 | } 32 | 33 | void 34 | trapinit(void) 35 | { 36 | extern void vsys(void); 37 | extern void vtrap(void); 38 | extern void virq(void); 39 | extern void vfiq(void); 40 | extern void vserr(void); 41 | 42 | extern void vsys0(void); 43 | extern void vtrap0(void); 44 | extern void vtrap1(void); 45 | 46 | static u32int *v; 47 | 48 | intrcpushutdown(); 49 | 50 | if(v == nil){ 51 | /* disable everything */ 52 | intrsoff(); 53 | 54 | v = mallocalign(0x80*4*4, 1<<11, 0, 0); 55 | if(v == nil){ 56 | panic("no memory for vector table"); 57 | } 58 | 59 | setupvector(&v[0x000/4], vtrap, vtrap0); 60 | setupvector(&v[0x080/4], virq, vtrap0); 61 | setupvector(&v[0x100/4], vfiq, vtrap0); 62 | setupvector(&v[0x180/4], vserr, vtrap0); 63 | 64 | setupvector(&v[0x200/4], vtrap, vtrap1); 65 | setupvector(&v[0x280/4], virq, vtrap1); 66 | setupvector(&v[0x300/4], vfiq, vtrap1); 67 | setupvector(&v[0x380/4], vserr, vtrap1); 68 | 69 | setupvector(&v[0x400/4], vsys, vsys0); 70 | setupvector(&v[0x480/4], virq, vtrap0); 71 | setupvector(&v[0x500/4], vfiq, vtrap0); 72 | setupvector(&v[0x580/4], vserr, vtrap0); 73 | 74 | setupvector(&v[0x600/4], vtrap, vtrap0); 75 | setupvector(&v[0x680/4], virq, vtrap0); 76 | setupvector(&v[0x700/4], vfiq, vtrap0); 77 | setupvector(&v[0x780/4], vserr, vtrap0); 78 | 79 | cacheduwbse(v, 0x80*4*4); 80 | } 81 | cacheiinvse(v, 0x80*4*4); 82 | syswr(VBAR_EL1, (uintptr)v); 83 | splx(0x3<<6); // unmask serr and debug 84 | } 85 | 86 | static char *traps[64] = { 87 | [0x00] "sys: trap: unknown", 88 | [0x01] "sys: trap: WFI or WFE instruction execution", 89 | [0x0E] "sys: trap: illegal execution state", 90 | [0x18] "sys: trap: illegal MSR/MRS access", 91 | [0x22] "sys: trap: misaligned pc", 92 | [0x26] "sys: trap: stack pointer misaligned", 93 | [0x30] "sys: trap: breakpoint", 94 | [0x32] "sys: trap: software step", 95 | [0x34] "sys: trap: watchpoint", 96 | [0x3C] "sys: trap: BRK instruction", 97 | }; 98 | 99 | void 100 | trap(Ureg *ureg) 101 | { 102 | u32int type, intr; 103 | int user; 104 | 105 | intr = ureg->type >> 32; 106 | if(intr == 2){ 107 | fiq(ureg); 108 | return; 109 | } 110 | splflo(); 111 | user = kenter(ureg); 112 | type = (u32int)ureg->type >> 26; 113 | switch(type){ 114 | case 0x20: // instruction abort from lower level 115 | case 0x21: // instruction abort from same level 116 | case 0x24: // data abort from lower level 117 | case 0x25: // data abort from same level 118 | faultarm64(ureg); 119 | break; 120 | case 0x07: // SIMD/FP 121 | case 0x2C: // FPU exception (A64 only) 122 | mathtrap(ureg); 123 | break; 124 | case 0x00: // unknown 125 | if(intr == 1){ 126 | if(irq(ureg) && up != nil && up->delaysched) 127 | sched(); 128 | break; 129 | } 130 | if(intr == 3){ 131 | case 0x2F: // SError interrupt 132 | if(buserror != nil && (*buserror)(ureg)) 133 | break; 134 | dumpregs(ureg); 135 | panic("SError interrupt"); 136 | break; 137 | } 138 | /* wet floor */ 139 | case 0x01: // WFI or WFE instruction execution 140 | case 0x03: // MCR or MRC access to CP15 (A32 only) 141 | case 0x04: // MCRR or MRC access to CP15 (A32 only) 142 | case 0x05: // MCR or MRC access to CP14 (A32 only) 143 | case 0x06: // LDC or STD access to CP14 (A32 only) 144 | case 0x08: // MCR or MRC to CP10 (A32 only) 145 | case 0x0C: // MRC access to CP14 (A32 only) 146 | case 0x0E: // Illegal Execution State 147 | case 0x11: // SVC instruction execution (A32 only) 148 | case 0x12: // HVC instruction execution (A32 only) 149 | case 0x13: // SMC instruction execution (A32 only) 150 | case 0x15: // SVC instruction execution (A64 only) 151 | case 0x16: // HVC instruction execution (A64 only) 152 | case 0x17: // SMC instruction execution (A64 only) 153 | case 0x18: // MSR/MRS (A64) 154 | case 0x22: // misaligned pc 155 | case 0x26: // stack pointer misaligned 156 | case 0x28: // FPU exception (A32 only) 157 | case 0x30: // breakpoint from lower level 158 | case 0x31: // breakpoint from same level 159 | case 0x32: // software step from lower level 160 | case 0x33: // software step from same level 161 | case 0x34: // watchpoint execution from lower level 162 | case 0x35: // watchpoint exception from same level 163 | case 0x38: // breapoint (A32 only) 164 | case 0x3A: // vector catch exception (A32 only) 165 | case 0x3C: // BRK instruction (A64 only) 166 | default: 167 | if(!userureg(ureg)){ 168 | dumpregs(ureg); 169 | panic("unhandled trap"); 170 | } 171 | if(traps[type] == nil) type = 0; // unknown 172 | postnote(up, 1, traps[type], NDebug); 173 | break; 174 | } 175 | splhi(); 176 | if(user){ 177 | if(up->procctl || up->nnote) 178 | notify(ureg); 179 | kexit(ureg); 180 | } 181 | } 182 | 183 | void 184 | syscall(Ureg *ureg) 185 | { 186 | vlong startns, stopns; 187 | uintptr sp, ret; 188 | ulong scallnr; 189 | int i, s; 190 | char *e; 191 | 192 | if(!kenter(ureg)) 193 | panic("syscall from kernel"); 194 | 195 | m->syscall++; 196 | up->insyscall = 1; 197 | up->pc = ureg->pc; 198 | 199 | sp = ureg->sp; 200 | up->scallnr = scallnr = ureg->r0; 201 | 202 | spllo(); 203 | 204 | up->nerrlab = 0; 205 | startns = 0; 206 | ret = -1; 207 | if(!waserror()){ 208 | if(sp < USTKTOP - BY2PG || sp > USTKTOP - sizeof(Sargs) - BY2WD){ 209 | validaddr(sp, sizeof(Sargs)+BY2WD, 0); 210 | evenaddr(sp); 211 | } 212 | up->s = *((Sargs*) (sp + BY2WD)); 213 | 214 | if(up->procctl == Proc_tracesyscall){ 215 | syscallfmt(scallnr, ureg->pc, (va_list) up->s.args); 216 | s = splhi(); 217 | up->procctl = Proc_stopme; 218 | procctl(); 219 | splx(s); 220 | startns = todget(nil); 221 | } 222 | 223 | if(scallnr >= nsyscall || systab[scallnr] == nil){ 224 | pprint("bad sys call number %lud pc %#p", scallnr, ureg->pc); 225 | postnote(up, 1, "sys: bad sys call", NDebug); 226 | error(Ebadarg); 227 | } 228 | up->psstate = sysctab[scallnr]; 229 | //ad9 print("[%luX] %s: syscall %s\n", (ulong)&ureg, up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); 230 | 231 | ret = systab[scallnr]((va_list)up->s.args); 232 | poperror(); 233 | }else{ 234 | e = up->syserrstr; 235 | up->syserrstr = up->errstr; 236 | up->errstr = e; 237 | } 238 | if(up->nerrlab){ 239 | print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab); 240 | for(i = 0; i < NERR; i++) 241 | print("sp=%#p pc=%#p\n", up->errlab[i].sp, up->errlab[i].pc); 242 | panic("error stack"); 243 | } 244 | ureg->r0 = ret; 245 | if(up->procctl == Proc_tracesyscall){ 246 | stopns = todget(nil); 247 | sysretfmt(scallnr, (va_list) up->s.args, ret, startns, stopns); 248 | s = splhi(); 249 | up->procctl = Proc_stopme; 250 | procctl(); 251 | splx(s); 252 | } 253 | 254 | up->insyscall = 0; 255 | up->psstate = 0; 256 | if(scallnr == NOTED){ 257 | noted(ureg, *((ulong*) up->s.args)); 258 | /* 259 | * normally, syscall() returns to forkret() 260 | * not restoring general registers when going 261 | * to userspace. to completely restore the 262 | * interrupted context, we have to return thru 263 | * noteret(). we override return pc to jump to 264 | * to it when returning form syscall() 265 | */ 266 | returnto(noteret); 267 | } 268 | 269 | if(scallnr != RFORK && (up->procctl || up->nnote)){ 270 | splhi(); 271 | notify(ureg); 272 | } 273 | if(up->delaysched) 274 | sched(); 275 | kexit(ureg); 276 | } 277 | 278 | int 279 | notify(Ureg *ureg) 280 | { 281 | uintptr s, sp; 282 | char *msg; 283 | 284 | if(up->procctl) 285 | procctl(); 286 | if(up->nnote == 0) 287 | return 0; 288 | if(up->fpstate == FPactive){ 289 | fpsave(up->fpsave); 290 | up->fpstate = FPinactive; 291 | } 292 | up->fpstate |= FPillegal; 293 | 294 | s = spllo(); 295 | qlock(&up->debug); 296 | msg = popnote(ureg); 297 | if(msg == nil){ 298 | qunlock(&up->debug); 299 | splhi(); 300 | return 0; 301 | } 302 | 303 | sp = ureg->sp; 304 | sp -= 256; /* debugging: preserve context causing problem */ 305 | sp -= sizeof(Ureg); 306 | sp = STACKALIGN(sp); 307 | 308 | if(!okaddr((uintptr)up->notify, 1, 0) 309 | || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1) 310 | || ((uintptr) up->notify & 3) != 0 311 | || (sp & 7) != 0){ 312 | qunlock(&up->debug); 313 | pprint("suicide: bad address in notify: handler=%#p sp=%#p\n", 314 | up->notify, sp); 315 | pexit("Suicide", 0); 316 | } 317 | 318 | memmove((Ureg*)sp, ureg, sizeof(Ureg)); 319 | *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ 320 | up->ureg = (void*)sp; 321 | sp -= BY2WD+ERRMAX; 322 | memmove((char*)sp, msg, ERRMAX); 323 | sp -= 3*BY2WD; 324 | *(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD; 325 | *(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg; 326 | ureg->r0 = (uintptr) up->ureg; 327 | ureg->sp = sp; 328 | ureg->pc = (uintptr) up->notify; 329 | ureg->link = 0; 330 | qunlock(&up->debug); 331 | splx(s); 332 | return 1; 333 | } 334 | 335 | void 336 | noted(Ureg *ureg, ulong arg0) 337 | { 338 | Ureg *nureg; 339 | uintptr oureg, sp; 340 | 341 | qlock(&up->debug); 342 | if(arg0 != NRSTR && !up->notified){ 343 | qunlock(&up->debug); 344 | pprint("call to noted() when not notified\n"); 345 | pexit("Suicide", 0); 346 | } 347 | up->notified = 0; 348 | 349 | nureg = up->ureg; 350 | up->fpstate &= ~FPillegal; 351 | 352 | oureg = (uintptr) nureg; 353 | if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 7) != 0){ 354 | qunlock(&up->debug); 355 | pprint("bad ureg in noted or call to noted when not notified\n"); 356 | pexit("Suicide", 0); 357 | } 358 | 359 | nureg->psr = (nureg->psr & USPSRMASK) | (ureg->psr & ~USPSRMASK); 360 | memmove(ureg, nureg, sizeof(Ureg)); 361 | 362 | switch(arg0){ 363 | case NCONT: case NRSTR: 364 | if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) || 365 | (nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){ 366 | qunlock(&up->debug); 367 | pprint("suicide: trap in noted\n"); 368 | pexit("Suicide", 0); 369 | } 370 | up->ureg = (Ureg *) (*(uintptr*) (oureg - BY2WD)); 371 | qunlock(&up->debug); 372 | break; 373 | 374 | case NSAVE: 375 | if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) || 376 | (nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){ 377 | qunlock(&up->debug); 378 | pprint("suicide: trap in noted\n"); 379 | pexit("Suicide", 0); 380 | } 381 | qunlock(&up->debug); 382 | sp = oureg - 4 * BY2WD - ERRMAX; 383 | splhi(); 384 | ureg->sp = sp; 385 | ureg->r0 = (uintptr) oureg; 386 | ((uintptr *) sp)[1] = oureg; 387 | ((uintptr *) sp)[0] = 0; 388 | break; 389 | 390 | default: 391 | up->lastnote->flag = NDebug; 392 | 393 | case NDFLT: 394 | qunlock(&up->debug); 395 | if(up->lastnote->flag == NDebug) 396 | pprint("suicide: %s\n", up->lastnote->msg); 397 | pexit(up->lastnote->msg, up->lastnote->flag != NDebug); 398 | } 399 | } 400 | 401 | void 402 | faultarm64(Ureg *ureg) 403 | { 404 | extern void checkpages(void); 405 | char buf[ERRMAX]; 406 | int read, insyscall; 407 | uintptr addr; 408 | 409 | insyscall = up->insyscall; 410 | up->insyscall = 1; 411 | 412 | if(!userureg(ureg)){ 413 | extern void _peekinst(void); 414 | 415 | if(ureg->pc == (uintptr)_peekinst){ 416 | ureg->pc = ureg->link; 417 | goto out; 418 | } 419 | 420 | if(waserror()){ 421 | if(up->nerrlab == 0){ 422 | pprint("suicide: sys: %s\n", up->errstr); 423 | pexit(up->errstr, 1); 424 | } 425 | up->insyscall = insyscall; 426 | nexterror(); 427 | } 428 | } 429 | 430 | addr = getfar(); 431 | read = (ureg->type & (1<<6)) == 0; 432 | 433 | switch((u32int)ureg->type & 0x3F){ 434 | case 4: case 5: case 6: case 7: // Tanslation fault. 435 | case 8: case 9: case 10: case 11: // Access flag fault. 436 | case 12: case 13: case 14: case 15: // Permission fault. 437 | case 48: // tlb conflict fault. 438 | if(fault(addr, ureg->pc, read) == 0) 439 | break; 440 | 441 | /* wet floor */ 442 | case 0: case 1: case 2: case 3: // Address size fault. 443 | case 16: // synchronous external abort. 444 | case 24: // synchronous parity error on a memory access. 445 | case 20: case 21: case 22: case 23: // synchronous external abort on a table walk. 446 | case 28: case 29: case 30: case 31: // synchronous parity error on table walk. 447 | case 33: // alignment fault. 448 | case 52: // implementation defined, lockdown abort. 449 | case 53: // implementation defined, unsuppoted exclusive. 450 | case 61: // first level domain fault 451 | case 62: // second level domain fault 452 | default: 453 | if(!userureg(ureg)){ 454 | dumpregs(ureg); 455 | panic("fault: %s addr=%#p", read ? "read" : "write", addr); 456 | } 457 | checkpages(); 458 | sprint(buf, "sys: trap: fault %s addr=%#p", read ? "read" : "write", addr); 459 | postnote(up, 1, buf, NDebug); 460 | } 461 | 462 | if(!userureg(ureg)) 463 | poperror(); 464 | 465 | out: 466 | up->insyscall = insyscall; 467 | } 468 | 469 | int 470 | userureg(Ureg* ureg) 471 | { 472 | return (ureg->psr & 15) == 0; 473 | } 474 | 475 | uintptr 476 | userpc(void) 477 | { 478 | Ureg *ur = up->dbgreg; 479 | return ur->pc; 480 | } 481 | 482 | uintptr 483 | dbgpc(Proc *) 484 | { 485 | Ureg *ur = up->dbgreg; 486 | if(ur == nil) 487 | return 0; 488 | return ur->pc; 489 | } 490 | 491 | void 492 | procfork(Proc *p) 493 | { 494 | int s; 495 | 496 | s = splhi(); 497 | switch(up->fpstate & ~FPillegal){ 498 | case FPactive: 499 | fpsave(up->fpsave); 500 | up->fpstate = FPinactive; 501 | case FPinactive: 502 | memmove(p->fpsave, up->fpsave, sizeof(FPsave)); 503 | p->fpstate = FPinactive; 504 | } 505 | splx(s); 506 | 507 | p->tpidr = up->tpidr; 508 | } 509 | 510 | void 511 | procsetup(Proc *p) 512 | { 513 | p->fpstate = FPinit; 514 | fpoff(); 515 | 516 | p->tpidr = 0; 517 | syswr(TPIDR_EL0, p->tpidr); 518 | } 519 | 520 | void 521 | procsave(Proc *p) 522 | { 523 | if(p->fpstate == FPactive){ 524 | if(p->state == Moribund) 525 | fpclear(); 526 | else 527 | fpsave(p->fpsave); 528 | p->fpstate = FPinactive; 529 | } 530 | 531 | if(p->kp == 0) 532 | p->tpidr = sysrd(TPIDR_EL0); 533 | 534 | putasid(p); // release asid 535 | } 536 | 537 | void 538 | procrestore(Proc *p) 539 | { 540 | if(p->kp == 0) 541 | syswr(TPIDR_EL0, p->tpidr); 542 | } 543 | 544 | void 545 | kprocchild(Proc *p, void (*entry)(void)) 546 | { 547 | p->sched.pc = (uintptr) entry; 548 | p->sched.sp = (uintptr) p - 16; 549 | *(void**)p->sched.sp = kprocchild; /* fake */ 550 | } 551 | 552 | void 553 | forkchild(Proc *p, Ureg *ureg) 554 | { 555 | Ureg *cureg; 556 | 557 | p->sched.pc = (uintptr) forkret; 558 | p->sched.sp = (uintptr) p - TRAPFRAMESIZE; 559 | 560 | cureg = (Ureg*) (p->sched.sp + 16); 561 | memmove(cureg, ureg, sizeof(Ureg)); 562 | cureg->r0 = 0; 563 | } 564 | 565 | uintptr 566 | execregs(uintptr entry, ulong ssize, ulong nargs) 567 | { 568 | uintptr *sp; 569 | Ureg *ureg; 570 | 571 | sp = (uintptr*)(USTKTOP - ssize); 572 | *--sp = nargs; 573 | 574 | ureg = up->dbgreg; 575 | ureg->sp = (uintptr)sp; 576 | ureg->pc = entry; 577 | ureg->link = 0; 578 | return USTKTOP-sizeof(Tos); 579 | } 580 | 581 | void 582 | evenaddr(uintptr addr) 583 | { 584 | if(addr & 3){ 585 | postnote(up, 1, "sys: odd address", NDebug); 586 | error(Ebadarg); 587 | } 588 | } 589 | 590 | void 591 | callwithureg(void (*f) (Ureg *)) 592 | { 593 | Ureg u; 594 | 595 | u.pc = getcallerpc(&f); 596 | u.sp = (uintptr) &f; 597 | f(&u); 598 | } 599 | 600 | void 601 | setkernur(Ureg *ureg, Proc *p) 602 | { 603 | ureg->pc = p->sched.pc; 604 | ureg->sp = p->sched.sp; 605 | ureg->link = (uintptr)sched; 606 | } 607 | 608 | void 609 | setupwatchpts(Proc*, Watchpt*, int) 610 | { 611 | } 612 | 613 | void 614 | setregisters(Ureg* ureg, char* pureg, char* uva, int n) 615 | { 616 | ulong v; 617 | 618 | v = ureg->psr; 619 | memmove(pureg, uva, n); 620 | ureg->psr = (ureg->psr & USPSRMASK) | (v & ~USPSRMASK); 621 | } 622 | 623 | static void 624 | dumpstackwithureg(Ureg *ureg) 625 | { 626 | uintptr v, estack, sp; 627 | char *s; 628 | int i; 629 | 630 | dumpregs(ureg); 631 | 632 | if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){ 633 | iprint("dumpstack disabled\n"); 634 | return; 635 | } 636 | iprint("ktrace /kernel/path %#p %#p %#p # pc, sp, link\n", 637 | ureg->pc, ureg->sp, ureg->link); 638 | delay(2000); 639 | 640 | sp = ureg->sp; 641 | if(sp < KZERO || (sp & 7) != 0) 642 | sp = (uintptr)&ureg; 643 | 644 | estack = (uintptr)m+MACHSIZE; 645 | if(up != nil && sp <= (uintptr)up) 646 | estack = (uintptr)up; 647 | 648 | if(sp > estack){ 649 | if(up != nil) 650 | iprint("&up %#p sp %#p\n", up, sp); 651 | else 652 | iprint("&m %#p sp %#p\n", m, sp); 653 | return; 654 | } 655 | 656 | i = 0; 657 | for(; sp < estack; sp += sizeof(uintptr)){ 658 | v = *(uintptr*)sp; 659 | if(KTZERO < v && v < (uintptr)etext && (v & 3) == 0){ 660 | iprint("%#8.8lux=%#8.8lux ", (ulong)sp, (ulong)v); 661 | i++; 662 | } 663 | if(i == 4){ 664 | i = 0; 665 | iprint("\n"); 666 | } 667 | } 668 | if(i) 669 | iprint("\n"); 670 | } 671 | 672 | void 673 | dumpstack(void) 674 | { 675 | callwithureg(dumpstackwithureg); 676 | } 677 | 678 | void 679 | dumpregs(Ureg *ureg) 680 | { 681 | u64int *r; 682 | int i, x; 683 | 684 | x = splhi(); 685 | if(up != nil) 686 | iprint("cpu%d: dumpregs ureg %#p process %lud: %s\n", m->machno, ureg, 687 | up->pid, up->text); 688 | else 689 | iprint("cpu%d: dumpregs ureg %#p\n", m->machno, ureg); 690 | r = &ureg->r0; 691 | for(i = 0; i < 30; i += 3) 692 | iprint("R%d %.16llux R%d %.16llux R%d %.16llux\n", i, r[i], i+1, r[i+1], i+2, r[i+2]); 693 | iprint("PC %#p SP %#p LR %#p PSR %llux TYPE %llux\n", 694 | ureg->pc, ureg->sp, ureg->link, 695 | ureg->psr, ureg->type); 696 | splx(x); 697 | } 698 | -------------------------------------------------------------------------------- /uarti8250.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 8250-like UART 3 | */ 4 | 5 | #include "u.h" 6 | #include "../port/lib.h" 7 | #include "mem.h" 8 | #include "dat.h" 9 | #include "fns.h" 10 | #include "io.h" 11 | 12 | enum { /* registers */ 13 | Rbr = 0, /* Receiver Buffer (RO) */ 14 | Thr = 0, /* Transmitter Holding (WO) */ 15 | Ier = 1, /* Interrupt Enable */ 16 | Iir = 2, /* Interrupt Identification (RO) */ 17 | Fcr = 2, /* FIFO Control (WO) */ 18 | Lcr = 3, /* Line Control */ 19 | Mcr = 4, /* Modem Control */ 20 | Lsr = 5, /* Line Status */ 21 | Msr = 6, /* Modem Status */ 22 | Scr = 7, /* Scratch Pad */ 23 | // Mdr = 8, /* Mode Def'n (missing on mt7688)*/ 24 | Dll = 0, /* Divisor Latch LSB */ 25 | Dlm = 1, /* Divisor Latch MSB */ 26 | }; 27 | 28 | enum { /* Usr */ 29 | Busy = 0x01, 30 | }; 31 | 32 | enum { /* Ier */ 33 | Erda = 0x01, /* Enable Received Data Available */ 34 | Ethre = 0x02, /* Enable Thr Empty */ 35 | Erls = 0x04, /* Enable Receiver Line Status */ 36 | Ems = 0x08, /* Enable Modem Status */ 37 | }; 38 | 39 | enum { /* Iir */ 40 | Ims = 0x00, /* Ms interrupt */ 41 | Ip = 0x01, /* Interrupt Pending (not) */ 42 | Ithre = 0x02, /* Thr Empty */ 43 | Irda = 0x04, /* Received Data Available */ 44 | Irls = 0x06, /* Receiver Line Status */ 45 | Ictoi = 0x0C, /* Character Time-out Indication */ 46 | IirMASK = 0x3F, 47 | Ifena = 0xC0, /* FIFOs enabled */ 48 | }; 49 | 50 | enum { /* Fcr */ 51 | FIFOena = 0x01, /* FIFO enable */ 52 | FIFOrclr = 0x02, /* clear Rx FIFO */ 53 | FIFOtclr = 0x04, /* clear Tx FIFO */ 54 | FIFOdma = 0x08, 55 | FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ 56 | FIFO4 = 0x40, /* 4 bytes */ 57 | FIFO8 = 0x80, /* 8 bytes */ 58 | FIFO14 = 0xC0, /* 14 bytes */ 59 | }; 60 | 61 | enum { /* Lcr */ 62 | Wls5 = 0x00, /* Word Length Select 5 bits/byte */ 63 | Wls6 = 0x01, /* 6 bits/byte */ 64 | Wls7 = 0x02, /* 7 bits/byte */ 65 | Wls8 = 0x03, /* 8 bits/byte */ 66 | WlsMASK = 0x03, 67 | Stb = 0x04, /* 2 stop bits */ 68 | Pen = 0x08, /* Parity Enable */ 69 | Eps = 0x10, /* Even Parity Select */ 70 | Stp = 0x20, /* Stick Parity */ 71 | Brk = 0x40, /* Break */ 72 | Dlab = 0x80, /* Divisor Latch Access Bit */ 73 | }; 74 | 75 | enum { /* Mcr */ 76 | Dtr = 0x01, /* Data Terminal Ready */ 77 | Rts = 0x02, /* Ready To Send */ 78 | Out1 = 0x04, /* no longer in use */ 79 | Out2 = 0x08, 80 | Dm = 0x10, /* Diagnostic Mode loopback */ 81 | }; 82 | 83 | enum { /* Lsr */ 84 | Dr = 0x01, /* Data Ready */ 85 | Oe = 0x02, /* Overrun Error */ 86 | Pe = 0x04, /* Parity Error */ 87 | Fe = 0x08, /* Framing Error */ 88 | Bi = 0x10, /* Break Interrupt */ 89 | Thre = 0x20, /* Thr Empty */ 90 | Temt = 0x40, /* Transmitter Empty */ 91 | FIFOerr = 0x80, /* error in receiver FIFO */ 92 | }; 93 | 94 | enum { /* Msr */ 95 | Dcts = 0x01, /* Delta Cts */ 96 | Ddsr = 0x02, /* Delta Dsr */ 97 | Teri = 0x04, /* Trailing Edge of Ri */ 98 | Ddcd = 0x08, /* Delta Dcd */ 99 | Cts = 0x10, /* Clear To Send */ 100 | Dsr = 0x20, /* Data Set Ready */ 101 | Ri = 0x40, /* Ring Indicator */ 102 | Dcd = 0x80, /* Carrier Detect */ 103 | }; 104 | 105 | enum { /* Mdr */ 106 | Modemask = 7, 107 | Modeuart = 0, 108 | }; 109 | 110 | 111 | typedef struct Ctlr { 112 | u32int* io; 113 | int irq; 114 | int tbdf; 115 | int iena; 116 | int poll; 117 | 118 | uchar sticky[Scr+1]; 119 | 120 | Lock; 121 | int hasfifo; 122 | int checkfifo; 123 | int fena; 124 | } Ctlr; 125 | 126 | extern PhysUart i8250physuart; 127 | 128 | static Ctlr i8250ctlr[] = { 129 | { .io = (u32int*)PHYSCONS, 130 | .irq = IRQuart0, 131 | .tbdf = -1, 132 | .poll = 0, }, 133 | }; 134 | 135 | static Uart i8250uart[] = { 136 | { .regs = &i8250ctlr[0], /* not [2] */ 137 | .name = "uart0", 138 | .freq = 3686000, /* Not used, we use the global i8250freq */ 139 | .phys = &i8250physuart, 140 | .console = 1, 141 | .next = nil, }, 142 | }; 143 | 144 | #define csr8r(c, r) ((c)->io[r]) 145 | #define csr8w(c, r, v) ((c)->io[r] = (c)->sticky[r] | (v)) 146 | #define csr8o(c, r, v) ((c)->io[r] = (v)) 147 | 148 | static void emptyoutstage(Uart*, int); 149 | 150 | static long 151 | i8250status(Uart* uart, void* buf, long n, long offset) 152 | { 153 | char *p; 154 | Ctlr *ctlr; 155 | uchar ier, lcr, mcr, msr; 156 | 157 | ctlr = uart->regs; 158 | p = malloc(READSTR); 159 | mcr = ctlr->sticky[Mcr]; 160 | msr = csr8r(ctlr, Msr); 161 | ier = ctlr->sticky[Ier]; 162 | lcr = ctlr->sticky[Lcr]; 163 | snprint(p, READSTR, 164 | "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" 165 | "dev(%d) type(%d) framing(%d) overruns(%d) " 166 | "berr(%d) serr(%d)%s%s%s%s\n", 167 | 168 | uart->baud, 169 | uart->hup_dcd, 170 | (msr & Dsr) != 0, 171 | uart->hup_dsr, 172 | (lcr & WlsMASK) + 5, 173 | (ier & Ems) != 0, 174 | (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', 175 | (mcr & Rts) != 0, 176 | (lcr & Stb) ? 2: 1, 177 | ctlr->fena, 178 | 179 | uart->dev, 180 | uart->type, 181 | uart->ferr, 182 | uart->oerr, 183 | uart->berr, 184 | uart->serr, 185 | (msr & Cts) ? " cts": "", 186 | (msr & Dsr) ? " dsr": "", 187 | (msr & Dcd) ? " dcd": "", 188 | (msr & Ri) ? " ring": "" 189 | ); 190 | n = readstr(offset, buf, n, p); 191 | free(p); 192 | 193 | return n; 194 | } 195 | 196 | static void 197 | i8250fifo(Uart* uart, int level) 198 | { 199 | Ctlr *ctlr; 200 | 201 | ctlr = uart->regs; 202 | if(ctlr->hasfifo == 0) 203 | return; 204 | 205 | /* 206 | * Changing the FIFOena bit in Fcr flushes data 207 | * from both receive and transmit FIFOs; there's 208 | * no easy way to guarantee not losing data on 209 | * the receive side, but it's possible to wait until 210 | * the transmitter is really empty. 211 | */ 212 | ilock(ctlr); 213 | while(!(csr8r(ctlr, Lsr) & Temt)) 214 | ; 215 | 216 | /* 217 | * Set the trigger level, default is the max. 218 | * value. 219 | * Some UARTs require FIFOena to be set before 220 | * other bits can take effect, so set it twice. 221 | */ 222 | ctlr->fena = level; 223 | switch(level){ 224 | case 0: 225 | break; 226 | case 1: 227 | level = FIFO1|FIFOena; 228 | break; 229 | case 4: 230 | level = FIFO4|FIFOena; 231 | break; 232 | case 8: 233 | level = FIFO8|FIFOena; 234 | break; 235 | default: 236 | level = FIFO14|FIFOena; 237 | break; 238 | } 239 | csr8w(ctlr, Fcr, level); 240 | csr8w(ctlr, Fcr, level); 241 | iunlock(ctlr); 242 | } 243 | 244 | static void 245 | i8250dtr(Uart* uart, int on) 246 | { 247 | Ctlr *ctlr; 248 | 249 | /* 250 | * Toggle DTR. 251 | */ 252 | ctlr = uart->regs; 253 | if(on) 254 | ctlr->sticky[Mcr] |= Dtr; 255 | else 256 | ctlr->sticky[Mcr] &= ~Dtr; 257 | csr8w(ctlr, Mcr, 0); 258 | } 259 | 260 | static void 261 | i8250rts(Uart* uart, int on) 262 | { 263 | Ctlr *ctlr; 264 | 265 | /* 266 | * Toggle RTS. 267 | */ 268 | ctlr = uart->regs; 269 | if(on) 270 | ctlr->sticky[Mcr] |= Rts; 271 | else 272 | ctlr->sticky[Mcr] &= ~Rts; 273 | csr8w(ctlr, Mcr, 0); 274 | } 275 | 276 | static void 277 | i8250modemctl(Uart* uart, int on) 278 | { 279 | Ctlr *ctlr; 280 | 281 | ctlr = uart->regs; 282 | ilock(&uart->tlock); 283 | if(on){ 284 | ctlr->sticky[Ier] |= Ems; 285 | csr8w(ctlr, Ier, 0); 286 | uart->modem = 1; 287 | uart->cts = csr8r(ctlr, Msr) & Cts; 288 | } 289 | else{ 290 | ctlr->sticky[Ier] &= ~Ems; 291 | csr8w(ctlr, Ier, 0); 292 | uart->modem = 0; 293 | uart->cts = 1; 294 | } 295 | iunlock(&uart->tlock); 296 | 297 | /* modem needs fifo */ 298 | (*uart->phys->fifo)(uart, on); 299 | } 300 | 301 | static int 302 | i8250parity(Uart* uart, int parity) 303 | { 304 | int lcr; 305 | Ctlr *ctlr; 306 | 307 | ctlr = uart->regs; 308 | lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); 309 | 310 | switch(parity){ 311 | case 'e': 312 | lcr |= Eps|Pen; 313 | break; 314 | case 'o': 315 | lcr |= Pen; 316 | break; 317 | case 'n': 318 | break; 319 | default: 320 | return -1; 321 | } 322 | ctlr->sticky[Lcr] = lcr; 323 | csr8w(ctlr, Lcr, 0); 324 | 325 | uart->parity = parity; 326 | 327 | return 0; 328 | } 329 | 330 | static int 331 | i8250stop(Uart* uart, int stop) 332 | { 333 | int lcr; 334 | Ctlr *ctlr; 335 | 336 | ctlr = uart->regs; 337 | lcr = ctlr->sticky[Lcr] & ~Stb; 338 | 339 | switch(stop){ 340 | case 1: 341 | break; 342 | case 2: 343 | lcr |= Stb; 344 | break; 345 | default: 346 | return -1; 347 | } 348 | ctlr->sticky[Lcr] = lcr; 349 | csr8w(ctlr, Lcr, 0); 350 | 351 | uart->stop = stop; 352 | 353 | return 0; 354 | } 355 | 356 | static int 357 | i8250bits(Uart* uart, int bits) 358 | { 359 | int lcr; 360 | Ctlr *ctlr; 361 | 362 | ctlr = uart->regs; 363 | lcr = ctlr->sticky[Lcr] & ~WlsMASK; 364 | 365 | switch(bits){ 366 | case 5: 367 | lcr |= Wls5; 368 | break; 369 | case 6: 370 | lcr |= Wls6; 371 | break; 372 | case 7: 373 | lcr |= Wls7; 374 | break; 375 | case 8: 376 | lcr |= Wls8; 377 | break; 378 | default: 379 | return -1; 380 | } 381 | ctlr->sticky[Lcr] = lcr; 382 | csr8w(ctlr, Lcr, 0); 383 | 384 | uart->bits = bits; 385 | 386 | return 0; 387 | } 388 | 389 | static int 390 | i8250baud(Uart* uart, int baud) 391 | { 392 | #ifdef notdef /* don't change the speed */ 393 | ulong bgc; 394 | Ctlr *ctlr; 395 | extern int i8250freq; /* In the config file */ 396 | 397 | /* 398 | * Set the Baud rate by calculating and setting the Baud rate 399 | * Generator Constant. This will work with fairly non-standard 400 | * Baud rates. 401 | */ 402 | if(i8250freq == 0 || baud <= 0) 403 | return -1; 404 | bgc = (i8250freq+8*baud-1)/(16*baud); 405 | 406 | ctlr = uart->regs; 407 | while(csr8r(ctlr, Usr) & Busy) 408 | delay(1); 409 | csr8w(ctlr, Lcr, Dlab); /* begin kludge */ 410 | csr8o(ctlr, Dlm, bgc>>8); 411 | csr8o(ctlr, Dll, bgc); 412 | csr8w(ctlr, Lcr, 0); 413 | #endif 414 | uart->baud = baud; 415 | return 0; 416 | } 417 | 418 | static void 419 | i8250break(Uart* uart, int ms) 420 | { 421 | Ctlr *ctlr; 422 | 423 | if (up == nil) 424 | panic("i8250break: nil up"); 425 | /* 426 | * Send a break. 427 | */ 428 | if(ms <= 0) 429 | ms = 200; 430 | 431 | ctlr = uart->regs; 432 | csr8w(ctlr, Lcr, Brk); 433 | tsleep(&up->sleep, return0, 0, ms); 434 | csr8w(ctlr, Lcr, 0); 435 | } 436 | 437 | static void 438 | i8250kick(Uart* uart) 439 | { 440 | int i; 441 | Ctlr *ctlr; 442 | 443 | ctlr = uart->regs; 444 | 445 | if(!normalprint) { /* early */ 446 | if (uart->op < uart->oe) 447 | emptyoutstage(uart, uart->oe - uart->op); 448 | while ((i = uartstageoutput(uart)) > 0) 449 | emptyoutstage(uart, i); 450 | return; 451 | } 452 | 453 | 454 | /* 455 | * 128 here is an arbitrary limit to make sure 456 | * we don't stay in this loop too long. If the 457 | * chip's output queue is longer than 128, too 458 | * bad -- presotto 459 | */ 460 | for(i = 0; i < 128; i++){ 461 | if(!(csr8r(ctlr, Lsr) & Thre)) 462 | break; 463 | if(uart->op >= uart->oe && uartstageoutput(uart) == 0) 464 | break; 465 | csr8o(ctlr, Thr, *uart->op++); /* start tx */ 466 | } 467 | } 468 | 469 | static void 470 | i8250interrupt(Ureg*, void* arg) 471 | { 472 | Ctlr *ctlr; 473 | Uart *uart; 474 | int iir, lsr, old, r; 475 | 476 | uart = arg; 477 | ctlr = uart->regs; 478 | for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ 479 | switch(iir & IirMASK){ 480 | case Ims: /* Ms interrupt */ 481 | r = csr8r(ctlr, Msr); 482 | if(r & Dcts){ 483 | ilock(&uart->tlock); 484 | old = uart->cts; 485 | uart->cts = r & Cts; 486 | if(old == 0 && uart->cts) 487 | uart->ctsbackoff = 2; 488 | iunlock(&uart->tlock); 489 | } 490 | if(r & Ddsr){ 491 | old = r & Dsr; 492 | if(uart->hup_dsr && uart->dsr && !old) 493 | uart->dohup = 1; 494 | uart->dsr = old; 495 | } 496 | if(r & Ddcd){ 497 | old = r & Dcd; 498 | if(uart->hup_dcd && uart->dcd && !old) 499 | uart->dohup = 1; 500 | uart->dcd = old; 501 | } 502 | break; 503 | case Ithre: /* Thr Empty */ 504 | uartkick(uart); 505 | break; 506 | case Irda: /* Received Data Available */ 507 | case Irls: /* Receiver Line Status */ 508 | case Ictoi: /* Character Time-out Indication */ 509 | /* 510 | * Consume any received data. 511 | * If the received byte came in with a break, 512 | * parity or framing error, throw it away; 513 | * overrun is an indication that something has 514 | * already been tossed. 515 | */ 516 | while((lsr = csr8r(ctlr, Lsr)) & Dr){ 517 | if(lsr & (FIFOerr|Oe)) 518 | uart->oerr++; 519 | if(lsr & Pe) 520 | uart->perr++; 521 | if(lsr & Fe) 522 | uart->ferr++; 523 | r = csr8r(ctlr, Rbr); 524 | if(!(lsr & (Bi|Fe|Pe))) 525 | uartrecv(uart, r); 526 | } 527 | break; 528 | 529 | default: 530 | iprint("weird uart interrupt type %#2.2uX\n", iir); 531 | break; 532 | } 533 | coherence(); 534 | } 535 | } 536 | 537 | static void 538 | i8250disable(Uart* uart) 539 | { 540 | Ctlr *ctlr; 541 | 542 | /* 543 | * Turn off DTR and RTS, disable interrupts and fifos. 544 | */ 545 | (*uart->phys->dtr)(uart, 0); 546 | (*uart->phys->rts)(uart, 0); 547 | (*uart->phys->fifo)(uart, 0); 548 | 549 | ctlr = uart->regs; 550 | ctlr->sticky[Ier] = 0; 551 | csr8w(ctlr, Ier, 0); 552 | 553 | if(ctlr->iena != 0){ 554 | intrdisable(ctlr->irq, i8250interrupt, uart, BUSUNKNOWN, uart->name); 555 | ctlr->iena = 0; 556 | } 557 | } 558 | 559 | static void 560 | i8250enable(Uart* uart, int ie) 561 | { 562 | int mode; 563 | Ctlr *ctlr; 564 | 565 | ctlr = uart->regs; 566 | 567 | ctlr->sticky[Lcr] = Wls8; /* no parity */ 568 | csr8w(ctlr, Lcr, 0); 569 | 570 | /* 571 | * Check if there is a FIFO. 572 | * Changing the FIFOena bit in Fcr flushes data 573 | * from both receive and transmit FIFOs; there's 574 | * no easy way to guarantee not losing data on 575 | * the receive side, but it's possible to wait until 576 | * the transmitter is really empty. 577 | * Also, reading the Iir outwith i8250interrupt() 578 | * can be dangerous, but this should only happen 579 | * once, before interrupts are enabled. 580 | */ 581 | ilock(ctlr); 582 | if(!ctlr->checkfifo){ 583 | /* 584 | * Wait until the transmitter is really empty. 585 | */ 586 | while(!(csr8r(ctlr, Lsr) & Temt)) 587 | ; 588 | csr8w(ctlr, Fcr, FIFOena); 589 | if(csr8r(ctlr, Iir) & Ifena) 590 | ctlr->hasfifo = 1; 591 | csr8w(ctlr, Fcr, 0); 592 | ctlr->checkfifo = 1; 593 | } 594 | iunlock(ctlr); 595 | 596 | /* 597 | * Enable interrupts and turn on DTR and RTS. 598 | * Be careful if this is called to set up a polled serial line 599 | * early on not to try to enable interrupts as interrupt- 600 | * -enabling mechanisms might not be set up yet. 601 | */ 602 | if(ie){ 603 | if(ctlr->iena == 0 && !ctlr->poll){ 604 | intrenable(ctlr->irq, i8250interrupt, uart, BUSUNKNOWN, uart->name); 605 | ctlr->iena = 1; 606 | } 607 | ctlr->sticky[Ier] = Ethre|Erda; 608 | ctlr->sticky[Mcr] = 0; 609 | } 610 | else{ 611 | ctlr->sticky[Ier] = 0; 612 | ctlr->sticky[Mcr] = 0; 613 | } 614 | csr8w(ctlr, Ier, ctlr->sticky[Ier]); 615 | csr8w(ctlr, Mcr, 0); 616 | coherence(); 617 | 618 | (*uart->phys->dtr)(uart, 1); 619 | (*uart->phys->rts)(uart, 1); 620 | 621 | /* 622 | * During startup, the i8259 interrupt controller is reset. 623 | * This may result in a lost interrupt from the i8250 uart. 624 | * The i8250 thinks the interrupt is still outstanding and does not 625 | * generate any further interrupts. The workaround is to call the 626 | * interrupt handler to clear any pending interrupt events. 627 | * Note: this must be done after setting Ier. 628 | */ 629 | if(ie) 630 | i8250interrupt(nil, uart); 631 | } 632 | 633 | static Uart* 634 | i8250pnp(void) 635 | { 636 | return i8250uart; 637 | } 638 | 639 | static int 640 | i8250getc(Uart* uart) 641 | { 642 | Ctlr *ctlr; 643 | 644 | ctlr = uart->regs; 645 | while(!(csr8r(ctlr, Lsr) & Dr)) 646 | delay(1); 647 | return csr8r(ctlr, Rbr); 648 | } 649 | 650 | static void 651 | i8250putc(Uart* uart, int c) 652 | { 653 | int i, s; 654 | Ctlr *ctlr; 655 | 656 | if (!normalprint) { /* too early; use brute force */ 657 | int s = splhi(); 658 | 659 | while (!(((ulong *)PHYSCONS)[Lsr] & Thre)) 660 | ; 661 | ((ulong *)PHYSCONS)[Thr] = c; 662 | splx(s); 663 | return; 664 | } 665 | 666 | ctlr = uart->regs; 667 | // s = splhi(); 668 | for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++) 669 | delay(1); 670 | csr8o(ctlr, Thr, (uchar)c); 671 | for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++) 672 | delay(1); 673 | // splx(s); 674 | } 675 | 676 | void 677 | uartconsinit(void) 678 | { 679 | consuart = &i8250uart[0]; 680 | consuart->console = 1; 681 | uartctl(consuart, "l8 pn s1"); 682 | } 683 | 684 | PhysUart i8250physuart = { 685 | .name = "i8250", 686 | .pnp = i8250pnp, 687 | .enable = i8250enable, 688 | .disable = i8250disable, 689 | .kick = i8250kick, 690 | .dobreak = i8250break, 691 | .baud = i8250baud, 692 | .bits = i8250bits, 693 | .stop = i8250stop, 694 | .parity = i8250parity, 695 | .modemctl = i8250modemctl, 696 | .rts = i8250rts, 697 | .dtr = i8250dtr, 698 | .status = i8250status, 699 | .fifo = i8250fifo, 700 | .getc = i8250getc, 701 | .putc = i8250putc, 702 | }; 703 | 704 | /* for early printing */ 705 | 706 | void 707 | _uartputs(char* s, int n) 708 | { 709 | char *e; 710 | 711 | for(e = s+n; s < e; s++){ 712 | if(*s == '\n') 713 | i8250putc(&i8250uart[CONSOLE], '\r'); 714 | i8250putc(&i8250uart[CONSOLE], *s); 715 | } 716 | } 717 | 718 | 719 | static void 720 | emptyoutstage(Uart *uart, int n) 721 | { 722 | _uartputs((char *)uart->op, n); 723 | uart->op = uart->oe = uart->ostage; 724 | } 725 | --------------------------------------------------------------------------------