warn = -pedantic -Wall
dbg = -g
inc = -Isrc -Isrc/libc
-gccopt = -marm -fpic -ffreestanding -nostdinc -ffast-math -fno-math-errno
+gccopt = -marm -fno-pic -ffreestanding -nostdinc -ffast-math -fno-math-errno
#arch = -mcpu=arm1176jzf-s
arch = -mcpu=cortex-a7
ASFLAGS = $(arch) $(dbg) $(inc)
LDFLAGS = -nostdlib -T rpikern.ld -print-gc-sections
-QEMU_FLAGS = -m 1024 -M raspi2 -serial stdio -d guest_errors
+QEMU_FLAGS = -vnc :0 -m 1024 -M raspi2 -serial stdio -d guest_errors
$(bin): $(elf)
Raspberry Pi2 notes
===================
-The following is not all confirmed.
+Accessing different peripherals migh return results out of order. See bcm2835
+spec section 1.3. Use memory barriers as follows:
+ - place write barrier before the first write to a peripheral
+ - place read barrier after the last read of a peripheral
+
+Multiple accesses to the same peripheral in a row do not require barriers.
+
+Interrupt handlers should have a read barrier before their first read, and end
+with a write barrier.
+
+Cache clean and invalidate operations are also needed before the GPU can see our
+command buffers... see:
+https://github.com/rsta2/uspi/blob/38eaff4f715643a9/lib/synchronize.c
Memory map
----------
static int cmdend;
rpi_init();
- init_serial(115200);
+ /*init_serial(115200); done in rpi_init now for early debugging */
con_init();
printf("Detected raspberry pi %d, I/O base: %x\n", rpi_model, rpi_iobase);
+ printf("Main RAM base: %x, size: %u bytes\n", rpi_mem_base, rpi_mem_size);
+ printf("Video RAM base: %x, size: %u bytes\n", rpi_vmem_base, rpi_vmem_size);
video_init();
printf("Going interactive\n");
for(;;) {
- int c = getchar();
-
- switch(c) {
- case '\r':
- case '\n':
- if(!lastnl) {
- ser_printstr("\r\n");
- cmdbuf[cmdend] = 0;
- cmdend = 0;
- cmdrun(cmdbuf);
- }
- lastnl = 1;
- break;
-
- case -1:
- lastnl = 0;
- printf("error!\n");
- break;
-
- default:
- lastnl = 0;
- ser_putchar(c);
- if(cmdend < sizeof cmdbuf) {
- cmdbuf[cmdend++] = c;
+ while(ser_pending()) {
+ int c = getchar();
+
+ switch(c) {
+ case '\r':
+ case '\n':
+ if(!lastnl) {
+ printf("\r\n");
+ cmdbuf[cmdend] = 0;
+ cmdend = 0;
+ cmdrun(cmdbuf);
+ }
+ lastnl = 1;
+ break;
+
+ case -1:
+ lastnl = 0;
+ printf("error!\n");
+ break;
+
+ default:
+ lastnl = 0;
+ putchar(c);
+ if(cmdend < sizeof cmdbuf) {
+ cmdbuf[cmdend++] = c;
+ }
}
}
+
+ /*video_update(1);*/
}
return 0;
static void cmdrun(char *cmd)
{
+ static int cur_x, cur_y;
char *ptr, *args;
while(*cmd && isspace(*cmd)) cmd++;
if(strcmp(cmd, "reboot") == 0) {
reboot();
+
+ } else if(strcmp(cmd, "down") == 0) {
+ printf("scroll down\n");
+ cur_y += 10;
+ video_scroll(cur_x, cur_y);
+
+ } else if(strcmp(cmd, "up") == 0) {
+ printf("scroll up\n");
+ cur_y -= 10;
+ video_scroll(cur_x, cur_y);
+
} else if(strcmp(cmd, "help") == 0) {
printf("help not implemented yet\n");
} else if(strcmp(cmd, "ver") == 0) {
+#include <stdio.h>
+#include <stdarg.h>
#include "rpi.h"
+#include "sysctl.h"
#include "asm.h"
+#include "serial.h"
+#include "debug.h"
#define IOREG(offs) (*(volatile uint32_t*)(rpi_iobase | offs))
int rpi_model;
uint32_t rpi_iobase;
-uint32_t rpi_memsize, rpi_vc_memsize;
+uint32_t rpi_mem_base, rpi_mem_size, rpi_vmem_base, rpi_vmem_size;
+
+
+/* needs to by 16-byte aligned, because the address we send over the mailbox
+ * interface, will have its 4 least significant bits masked off and taken over
+ * by the mailbox id
+ */
+static char propbuf[256] __attribute__((aligned(16)));
+static struct rpi_prop *wrprop, *rdprop;
+
void rpi_init(void)
{
+ struct rpi_prop *prop;
+
if((rpi_model = detect()) == -1) {
for(;;) halt_cpu();
}
- /* TODO */
+ init_serial(115200);
+
+ /* The model detected by detect is not accurate, get the correct board model
+ * through the mailbox property interface if possible.
+ * Also, detect the amount of CPU and GPU memory available.
+ */
+
+ rpi_prop(RPI_TAG_GETREV);
+ rpi_prop(RPI_TAG_GETRAM);
+ rpi_prop(RPI_TAG_GETVRAM);
+ if(rpi_prop_send() != -1) {
+ //hexdump(propbuf, sizeof propbuf);
+
+ while((prop = rpi_prop_next())) {
+ switch(prop->id) {
+ case RPI_TAG_GETREV:
+ printf("board revision: %x\n", prop->data[0]);
+ /* TODO: guess rpi model based on board revision */
+ break;
+
+ case RPI_TAG_GETRAM:
+ rpi_mem_base = prop->data[0];
+ rpi_mem_size = prop->data[1];
+ break;
+
+ case RPI_TAG_GETVRAM:
+ rpi_vmem_base = prop->data[0];
+ rpi_vmem_size = prop->data[1];
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
}
static int detect(void)
void rpi_reboot(void)
{
+ mem_barrier();
PM_WDOG_REG = PM_PASSWD | 1;
PM_RSTC_REG = PM_PASSWD | (PM_RSTC_REG & PMRSTC_WRCFG_CLEAR) | PMRSTC_WRCFG_FULL_RESET;
for(;;) halt_cpu();
void rpi_mbox_send(int chan, uint32_t msg)
{
+ mem_barrier();
while(MBOX_STATUS_REG & MBOX_STAT_FULL);
MBOX_WRITE_REG = (msg & 0xfffffff0) | chan;
+ mem_barrier();
}
uint32_t rpi_mbox_recv(int chan)
{
uint32_t msg;
+ mem_barrier();
do {
while(MBOX_STATUS_REG & MBOX_STAT_EMPTY);
msg = MBOX_READ_REG;
} while((msg & 0xf) != chan);
+ mem_barrier();
return msg & 0xfffffff0;
}
int rpi_mbox_pending(int chan)
{
+ mem_barrier();
return (MBOX_STATUS_REG & MBOX_STAT_EMPTY) == 0;
}
+
+
+static struct {
+ int id, req_len, resp_len;
+} taginfo[] = {
+ {RPI_TAG_GETMODEL, 0, 4},
+ {RPI_TAG_GETREV, 0, 4},
+ {RPI_TAG_GETRAM, 0, 8},
+ {RPI_TAG_GETVRAM, 0, 8},
+ {RPI_TAG_SETCLOCK, 4, 8},
+ {RPI_TAG_ALLOCFB, 4, 8},
+ {RPI_TAG_RELEASEFB, 0, 0},
+ {RPI_TAG_BLANKSCR, 4, 4},
+ {RPI_TAG_SETFBPHYS, 8, 8},
+ {RPI_TAG_SETFBVIRT, 8, 8},
+ {RPI_TAG_SETFBDEPTH, 4, 4},
+ {RPI_TAG_GETFBPITCH, 0, 4},
+ {RPI_TAG_SETFBOFFS, 8, 8},
+ {RPI_TAG_GETFBOFFS, 0, 8},
+ {0, 0, 0}
+};
+
+void rpi_prop(int id, ...)
+{
+ int i, req_len = 0, resp_len = 0;
+ va_list ap;
+
+ if(!wrprop) {
+ wrprop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+ }
+
+ for(i=0; taginfo[i].id; i++) {
+ if(taginfo[i].id == id) {
+ req_len = taginfo[i].req_len;
+ resp_len = taginfo[i].resp_len;
+ break;
+ }
+ }
+
+ wrprop->id = id;
+ wrprop->size = resp_len;
+ wrprop->res = 0;
+
+ va_start(ap, id);
+ for(i=0; i<resp_len >> 2; i++) {
+ if(i < req_len >> 2) {
+ wrprop->data[i] = va_arg(ap, uint32_t);
+ } else {
+ wrprop->data[i] = 0;
+ }
+ }
+ va_end(ap);
+
+ wrprop = RPI_PROP_NEXT(wrprop);
+}
+
+int rpi_prop_send(void)
+{
+ struct rpi_prop_header *hdr = (struct rpi_prop_header*)propbuf;
+ uint32_t addr = (uint32_t)propbuf;
+ uint32_t size;
+
+ /* terminate with null tag */
+ wrprop->id = 0;
+
+ size = (char*)wrprop - propbuf + 4;
+
+ hdr->size = (size + 15) & 0xfffffff0;
+ hdr->res = 0;
+
+ wrprop = rdprop = 0;
+
+ /* clean and invalidate cache, otherwise VC will see stale data */
+ sysctl_dcache_clean_inval(addr, hdr->size);
+
+ rpi_mbox_send(RPI_MBOX_PROP, addr);
+ while(rpi_mbox_recv(RPI_MBOX_PROP) != addr);
+
+ sysctl_dcache_clean_inval(addr, hdr->size);
+
+ if(hdr->res != 0x80000000) {
+ printf("Property request failed: %x\n", hdr->res);
+ return -1;
+ }
+ return 0;
+}
+
+struct rpi_prop *rpi_prop_next(void)
+{
+ struct rpi_prop *res;
+ if(!rdprop) {
+ rdprop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+ }
+
+ if(rdprop->id == 0) {
+ res = rdprop = 0;
+ } else {
+ res = rdprop;
+ rdprop->size = rdprop->res & 0x7fffffff;
+ rdprop = RPI_PROP_NEXT(rdprop);
+ }
+ return res;
+}
+
+struct rpi_prop *rpi_prop_find(int id)
+{
+ struct rpi_prop *prop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+
+ while(prop->id) {
+ prop->size = prop->res & 0x7fffffff;
+ if(prop->id == id) {
+ return prop;
+ }
+ prop = RPI_PROP_NEXT(prop);
+ }
+ return 0;
+}
#define RPI_MBOX_PROP 8
#define RPI_TAG_GETMODEL 0x010001
+#define RPI_TAG_GETREV 0x010002
#define RPI_TAG_GETRAM 0x010005
#define RPI_TAG_GETVRAM 0x010006
#define RPI_TAG_SETCLOCK 0x038002
#define RPI_TAG_SETFBPHYS 0x048003
#define RPI_TAG_SETFBVIRT 0x048004
#define RPI_TAG_SETFBDEPTH 0x048005
+#define RPI_TAG_GETFBPITCH 0x040008
+#define RPI_TAG_SETFBOFFS 0x048009
+#define RPI_TAG_GETFBOFFS 0x040009
struct rpi_prop_header {
uint32_t size;
extern int rpi_model;
extern uint32_t rpi_iobase;
-extern uint32_t rpi_memsize, rpi_vc_memsize;
+extern uint32_t rpi_mem_base, rpi_mem_size, rpi_vmem_base, rpi_vmem_size;
void rpi_init(void);
void rpi_reboot(void) __attribute__((noreturn));
uint32_t rpi_mbox_recv(int chan);
int rpi_mbox_pending(int chan);
+/* usage:
+ * rpi_prop(RPI_TAG_WHATEVER, foo, bar);
+ * rpi_prop(RPI_TAG_XYZZY, 42);
+ * if(rpi_prop_send() != -1) {
+ * struct rpi_prop *prop;
+ * while((prop = rpi_prop_next())) {
+ * ... process response tags ...
+ * }
+ * }
+ */
+void rpi_prop(int id, ...);
+int rpi_prop_send(void);
+struct rpi_prop *rpi_prop_next(void);
+/* find specific tag in response */
+struct rpi_prop *rpi_prop_find(int id);
+
#endif /* RPI_H_ */
{
uint32_t bdiv_fp6;
+ mem_barrier();
REG_CR = 0; /* disable UART */
/* disable pullups for GPIO 14 & 15 */
/* enable UART RX&TX */
REG_CR = CR_UARTEN | CR_TXEN | CR_RXEN;
+ mem_barrier();
}
void ser_putchar(int c)
{
if(c == '\n') ser_putchar('\r');
+ mem_barrier();
while(REG_FR & FR_TXFF);
REG_DR = c & 0xff;
+ mem_barrier();
}
int ser_getchar(void)
{
+ mem_barrier();
while(REG_FR & FR_RXFE);
return REG_DR & 0xff;
}
+int ser_pending(void)
+{
+ mem_barrier();
+ return (REG_FR & FR_RXFE) == 0;
+}
+
void ser_printstr(const char *s)
{
while(*s) {
void ser_putchar(int c);
int ser_getchar(void);
+int ser_pending(void);
+
void ser_printstr(const char *s);
#endif /* SERIAL_H_ */
exit: wfe
b exit
- .global dbgled
-dbgled:
- ldr r3, =0x3f200000 @ gpio base
- ldr r2, =0x9000 @ gpio 24 and 25 -> output
- str r2, [r3, #8] @ store to GPFSEL2
- ldr r2, =0x01000000 @ bit 24
- tst r0, #1
- strne r2, [r3, #0x1c] @ GPSET0
- streq r2, [r3, #0x28] @ GPCLR0
- lsl r2, #1
- tst r0, #2
- strne r2, [r3, #0x1c] @ GPSET0
- streq r2, [r3, #0x28] @ GPCLR0
- bx lr
-
@ vi:set filetype=armasm:
--- /dev/null
+ .text
+ .code 32
+
+@ wait for interrupt
+#define C7_CRM_WFI c0
+#define C7_OP2_WFI 4
+@ clean/inval operations
+#define C7_CRM_I_INVAL c5 @ invalidate instruction cache
+#define C7_CRM_D_INVAL c6 @ invalidate data cache
+#define C7_CRM_INVAL c7 @ invalidate both (reg = 0)
+#define C7_OP2_INVAL 0
+#define C7_CRM_D_CLEAN c10 @ clean data cache
+#define C7_CRM_D_CLEAN_INVAL c14 @ clean and invalidate data cache
+@ OP2 for cache clean/inval operations
+#define C7_OP2_ALL 0 @ reg = 0
+#define C7_OP2_ADDR 1
+#define C7_OP2_IDX 2
+@ data synchronization barrier
+#define C7_CRM_DSB c10
+#define C7_OP2_DSB 4
+@ data memory barrier
+#define C7_CRM_DMB c10
+#define C7_OP2_DMB 5
+
+@ vi:set filetype=armasm:
--- /dev/null
+#ifndef SYSCTL_H_
+#define SYSCTL_H_
+
+/* c7 reg, op1=0, mode c14 is clean&inval, op2=1 is cacheline MVA */
+#define sysctl_dcache_clean_inval(addr, len) \
+ do { \
+ register uint32_t a asm("r0") = addr; \
+ asm volatile( \
+ "\n0:\tmcr p15, 0, %0, c7, c14, 1" \
+ "\n\tadd %0, #64" \
+ "\n\tcmp %0, %1" \
+ "\n\tblo 0b" \
+ :: "r"(a), "r"(addr + len) \
+ : "memory"); \
+ } while(0)
+
+
+#endif /* SYSCTL_H_ */
#include "video.h"
#include "mem.h"
#include "debug.h"
-
-/* needs to by 16-byte aligned, because the address we send over the mailbox
- * interface, will have its 4 least significant bits masked off and taken over
- * by the mailbox id
- */
-static char propbuf[256] __attribute__((aligned(16)));
+#include "sysctl.h"
static void *fb_pixels;
-static int fb_width, fb_height, fb_depth, fb_size;
+static int scr_width, scr_height;
+static int fb_width, fb_height, fb_depth, fb_size, fb_pitch;
+static int fb_xoffs, fb_yoffs;
int video_init(void)
{
- int i;
- struct rpi_prop_header *hdr = (struct rpi_prop_header*)propbuf;
- struct rpi_prop *prop = (struct rpi_prop*)(hdr + 1);
-
- hdr->size = sizeof propbuf;
- hdr->res = 0;
-
- fb_width = 1024;
- fb_height = 600;
+ int i, j;
+ struct rpi_prop *prop;
+ uint32_t *fbptr;
+
+ scr_width = 1024;
+ scr_height = 576;
+ /*fb_width = 1920;
+ fb_height = 1024;*/
+ fb_width = scr_width;
+ fb_height = scr_height;
fb_depth = 32;
- prop->id = RPI_TAG_SETFBPHYS;
- prop->size = 8;
- prop->res = 0;
- prop->data[0] = fb_width;
- prop->data[1] = fb_height;
- prop = RPI_PROP_NEXT(prop);
-
- prop->id = RPI_TAG_SETFBVIRT;
- prop->size = 8;
- prop->res = 0;
- prop->data[0] = fb_width;
- prop->data[1] = fb_height;
- prop = RPI_PROP_NEXT(prop);
-
- prop->id = RPI_TAG_SETFBDEPTH;
- prop->size = 4;
- prop->res = 0;
- prop->data[0] = fb_depth;
- prop = RPI_PROP_NEXT(prop);
-
- prop->id = RPI_TAG_ALLOCFB;
- prop->size = 4;
- prop->res = 0;
- prop->data[0] = 4; /* alignment */
- prop = RPI_PROP_NEXT(prop);
-
- prop->id = 0;
- prop->size = 0;
- prop->res = 0;
- prop = RPI_PROP_NEXT(prop);
-
- printf("Requesting video mode: %dx%d (%d bpp)\n", fb_width, fb_height, fb_depth);
-
- rpi_mbox_send(RPI_MBOX_PROP, RPI_MEM_BUS_COHERENT(propbuf));
- while(rpi_mbox_recv(RPI_MBOX_PROP) != RPI_MEM_BUS_COHERENT(propbuf));
-
- hexdump(propbuf, sizeof propbuf);
-
- if(hdr->res != 0x80000000) {
- printf("Request failed, error: %x\n", hdr->res);
- return -1;
- }
+ printf("Requesting video mode: %dx%d %d bpp (fb:%dx%d)\n", scr_width, scr_height,
+ fb_depth, fb_width, fb_height);
+
+ rpi_prop(RPI_TAG_ALLOCFB, 16);
+ rpi_prop(RPI_TAG_SETFBVIRT, scr_width, scr_height);
+ rpi_prop(RPI_TAG_SETFBPHYS, fb_width, fb_height);
+ rpi_prop(RPI_TAG_SETFBDEPTH, fb_depth);
- prop = (struct rpi_prop*)(hdr + 1);
- while(prop->id) {
- prop->size = prop->res & 0x7fffffff;
+ rpi_prop_send();
+ while((prop = rpi_prop_next())) {
switch(prop->id) {
case RPI_TAG_SETFBPHYS:
- printf("setfbphys");
+ printf(" setfbphys");
+ fb_width = prop->data[0];
+ fb_height = prop->data[1];
break;
case RPI_TAG_SETFBVIRT:
- printf("setfbvirt");
- fb_width = prop->data[0];
- fb_height = prop->data[1];
+ printf(" setfbvirt");
+ scr_width = prop->data[0];
+ scr_height = prop->data[1];
break;
case RPI_TAG_SETFBDEPTH:
- printf("setfbdepth");
+ printf(" setfbdepth");
fb_depth = prop->data[0];
break;
case RPI_TAG_ALLOCFB:
- printf("allocfb");
- fb_pixels = (void*)prop->data[0];
+ printf(" allocfb");
+ fb_pixels = (void*)(prop->data[0] & 0x3fffffff);
fb_size = prop->data[1];
break;
default:
- printf("tag %x", prop->id);
+ printf(" tag %x", prop->id);
break;
}
printf(" %u", prop->data[i]);
}
putchar('\n');
- prop = RPI_PROP_NEXT(prop);
}
- printf("Got video mode: %dx%d (%d bpp) at %p (%d bytes)\n", fb_width, fb_height,
- fb_depth, fb_pixels, fb_size);
+ if(!fb_pixels) {
+ rpi_prop(RPI_TAG_ALLOCFB, 16);
+ if(rpi_prop_send() == -1 || !(prop = rpi_prop_find(RPI_TAG_ALLOCFB))) {
+ printf("Failed to allocate framebuffer\n");
+ return -1;
+ }
+ fb_pixels = (void*)(prop->data[0] & 0x3fffffff);
+ fb_size = prop->data[1];
+ }
+
+ rpi_prop(RPI_TAG_GETFBPITCH);
+ rpi_prop(RPI_TAG_GETFBOFFS);
+ rpi_prop_send();
+ /*
+ if(rpi_prop_send() == -1) {
+ printf("Failed to get pitch\n");
+ return -1;
+ }
+ */
+
+ while((prop = rpi_prop_next())) {
+ switch(prop->id) {
+ case RPI_TAG_GETFBPITCH:
+ fb_pitch = prop->data[0];
+ break;
+
+ case RPI_TAG_GETFBOFFS:
+ fb_xoffs = prop->data[0];
+ fb_yoffs = prop->data[1];
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ printf("Got video mode: %dx%d (%d bpp)\n", scr_width, scr_height, fb_depth);
+ printf("Framebuffer: %dx%d at %p (%d bytes)\n", fb_width, fb_height, fb_pixels, fb_size);
+ printf(" virtual offset: %d, %d\n", fb_xoffs, fb_yoffs);
+ printf(" scanline pitch: %d\n", fb_pitch);
+
+ fbptr = fb_pixels;
+ for(i=0; i<fb_height; i++) {
+ for(j=0; j<fb_width; j++) {
+ int r = i ^ j;
+ int g = (i ^ j) >> 1;
+ int b = (i ^ j) >> 2;
+ fbptr[j] = b | (g << 8) | (r << 16) | 0xff000000;
+ }
+ fbptr += fb_pitch >> 2;
+ }
- memset(fb_pixels, 0, fb_size);
+ sysctl_dcache_clean_inval((uint32_t)fb_pixels, fb_size);
return 0;
}
+
+int video_scroll(int x, int y)
+{
+ struct rpi_prop *prop;
+
+ rpi_prop(RPI_TAG_SETFBOFFS, x, y);
+ if(rpi_prop_send() == -1 || !(prop = rpi_prop_find(RPI_TAG_SETFBOFFS))) {
+ return -1;
+ }
+
+ fb_xoffs = prop->data[0];
+ fb_yoffs = prop->data[1];
+ return 0;
+}
+
+void video_update(int dt)
+{
+ static int dirx = 1, diry = 1;
+ int nx, ny;
+
+ nx = fb_xoffs + dirx * dt;
+ ny = fb_yoffs + diry * dt;
+
+ if(nx < 0) {
+ nx = 0;
+ dirx = -dirx;
+ } else if(nx + scr_width >= fb_width) {
+ nx = fb_width - scr_width;
+ dirx = -dirx;
+ }
+ if(ny < 0) {
+ ny = 0;
+ diry = -diry;
+ } else if(ny + scr_height >= fb_height) {
+ ny = fb_height - scr_height;
+ diry = -diry;
+ }
+
+ video_scroll(nx, ny);
+}
int video_init(void);
+int video_scroll(int x, int y);
+
+void video_update(int dt);
+
#endif /* VIDEO_H_ */