diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2023-11-05 16:37:32 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2023-11-05 16:37:32 +0000 |
commit | 9b68b426103ca0564d522d9918082125836a9f23 (patch) | |
tree | d60a5cf971eabc5b97685dd7a9527371a4421fe1 | |
parent | 019a935f6b8961b8a8fab3916f1e529c36f261e2 (diff) |
pi3: implement sdhost controller driver so we can use wifi always
-rw-r--r-- | sys/src/9/bcm64/pi3 | 3 | ||||
-rw-r--r-- | sys/src/9/bcm64/sdhost.c | 258 |
2 files changed, 260 insertions, 1 deletions
diff --git a/sys/src/9/bcm64/pi3 b/sys/src/9/bcm64/pi3 index 79cd132bd..a5556ab56 100644 --- a/sys/src/9/bcm64/pi3 +++ b/sys/src/9/bcm64/pi3 @@ -28,11 +28,12 @@ dev link archbcm3 usbdwc -# ether4330 + ether4330 ethermedium loopbackmedium netdevmedium emmc + sdhost # i2cbcm devi2c i2cgpio devi2c gpio diff --git a/sys/src/9/bcm64/sdhost.c b/sys/src/9/bcm64/sdhost.c new file mode 100644 index 000000000..96fd54563 --- /dev/null +++ b/sys/src/9/bcm64/sdhost.c @@ -0,0 +1,258 @@ +/* + * bcm2835 sdhost controller + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/sd.h" + +#define SDHOSTREGS (VIRTIO+0x202000) + +enum { + HC_COMMAND = 0x00>>2, + HC_CMD_ENABLE = 0x8000, + HC_CMD_FAILED = 0x4000, + HC_CMD_BUSY = 0x0800, + HC_CMD_RESPONSE_NONE = 0x0400, + HC_CMD_RESPONSE_LONG = 0x0200, + HC_CMD_WRITE = 0x0080, + HC_CMD_READ = 0x0040, + HC_CMD_MASK = 0x003F, + + HC_ARGUMENT = 0x04>>2, + HC_TIMEOUTCOUNTER = 0x08>>2, + + HC_CLOCKDIVISOR = 0x0c>>2, + HC_CLKDIV_MAX = 0x07FF, + + HC_RESPONSE_0 = 0x10>>2, + HC_RESPONSE_1 = 0x14>>2, + HC_RESPONSE_2 = 0x18>>2, + HC_RESPONSE_3 = 0x1c>>2, + + HC_HOSTSTATUS = 0x20>>2, + HC_HSTST_HAVE_DATA = 0x0001, + HC_HSTST_ERROR_FIFO = 0x0008, + HC_HSTST_ERROR_CRC7 = 0x0010, + HC_HSTST_ERROR_CRC16 = 0x0020, + HC_HSTST_TIMEOUT_CMD = 0x0040, + HC_HSTST_TIMEOUT_DATA = 0x0080, + HC_HSTST_INT_BLOCK = 0x0200, + HC_HSTST_INT_BUSY = 0x0400, + HC_HSTST_RESET = 0xFFFF, + HC_HSTST_ERROR + = HC_HSTST_ERROR_FIFO + | HC_HSTST_ERROR_CRC7 + | HC_HSTST_ERROR_CRC16 + | HC_HSTST_TIMEOUT_CMD + | HC_HSTST_TIMEOUT_DATA, + + HC_POWER = 0x30>>2, + HC_DEBUG = 0x34>>2, + + HC_HOSTCONFIG = 0x38>>2, + HC_HSTCF_INTBUS_WIDE = 0x0002, + HC_HSTCF_EXTBUS_4BIT = 0x0004, + HC_HSTCF_SLOW_CARD = 0x0008, + HC_HSTCF_INT_DATA = 0x0010, + HC_HSTCF_INT_BLOCK = 0x0100, + HC_HSTCF_INT_BUSY = 0x0400, + + HC_BLOCKSIZE = 0x3C>>2, + HC_DATAPORT = 0x40>>2, + HC_BLOCKCOUNT = 0x50>>2, +}; + +static u32int +RD(int reg) +{ + u32int *r = (u32int*)SDHOSTREGS; + coherence(); + return r[reg]; +} + +static void +WR(int reg, u32int val) +{ + u32int *r = (u32int*)SDHOSTREGS; + + if(0)print("WR %2.2ux %ux\n", reg<<2, val); + coherence(); + r[reg] = val; +} + +static void +sdhostclk(uint freq) +{ + uint div, ext; + + ext = getclkrate(ClkCore); + div = freq / ext; + if(div < 2) + div = 2; + if((ext / div) > freq) + div++; + div -= 2; + if(div > HC_CLKDIV_MAX) + div = HC_CLKDIV_MAX; + freq = ext / (div+2); + WR(HC_CLOCKDIVISOR, div); + WR(HC_TIMEOUTCOUNTER, freq/2); /* 500ms timeout */ +} + +static void +sdhostbus(SDio*, int width, int speed) +{ + switch(width){ + case 1: + WR(HC_HOSTCONFIG, RD(HC_HOSTCONFIG) & ~HC_HSTCF_EXTBUS_4BIT); + break; + case 4: + WR(HC_HOSTCONFIG, RD(HC_HOSTCONFIG) | HC_HSTCF_EXTBUS_4BIT); + break; + } + if(speed) + sdhostclk(speed); +} + +static int +sdhostinit(SDio*) +{ + WR(HC_POWER, 0); + WR(HC_COMMAND, 0); + WR(HC_ARGUMENT, 0); + WR(HC_TIMEOUTCOUNTER, 0); + WR(HC_CLOCKDIVISOR, 0); + WR(HC_HOSTSTATUS, HC_HSTST_RESET); + WR(HC_HOSTCONFIG, 0); + WR(HC_BLOCKSIZE, 0); + WR(HC_BLOCKCOUNT, 0); + microdelay(20); + return 0; +} + +static int +sdhostinquiry(SDio *, char *inquiry, int inqlen) +{ + return snprint(inquiry, inqlen, "BCM SD Host Controller"); +} + +static void +sdhostenable(SDio *io) +{ + WR(HC_POWER, 1); + WR(HC_HOSTCONFIG, HC_HSTCF_INTBUS_WIDE|HC_HSTCF_SLOW_CARD); + + sdhostclk(25*Mhz); +} + +static void +sdhosterror(u32int i) +{ + snprint(up->genbuf, sizeof(up->genbuf), "sdhost error %#ux\n", i); + error(up->genbuf); +} + +static int +sdhostcmd(SDio*, SDiocmd *cmd, u32int arg, u32int *resp) +{ + u32int c, i; + ulong now; + + c = cmd->index & HC_CMD_MASK; + switch(cmd->resp){ + case 0: + c |= HC_CMD_RESPONSE_NONE; + break; + case 1: + if(cmd->busy) + c |= HC_CMD_BUSY; + default: + break; + case 2: + c |= HC_CMD_RESPONSE_LONG; + break; + } + if(cmd->data){ + if(cmd->data & 1) + c |= HC_CMD_READ; + else + c |= HC_CMD_WRITE; + } + + /* clear errors */ + WR(HC_HOSTSTATUS, RD(HC_HOSTSTATUS)); + + WR(HC_ARGUMENT, arg); + WR(HC_COMMAND, c | HC_CMD_ENABLE); + + now = MACHP(0)->ticks; + while((i = RD(HC_COMMAND)) & HC_CMD_ENABLE) + if(MACHP(0)->ticks - now > HZ) + break; + + if(i & HC_CMD_ENABLE) + error("command never completed"); + if(i & HC_CMD_FAILED) + error("command failed"); + + if((i = RD(HC_HOSTSTATUS)) & HC_HSTST_ERROR) + sdhosterror(i); + + if(c & HC_CMD_RESPONSE_NONE) { + resp[0] = 0; + } else if(c & HC_CMD_RESPONSE_LONG) { + resp[0] = RD(HC_RESPONSE_0); + resp[1] = RD(HC_RESPONSE_1); + resp[2] = RD(HC_RESPONSE_2); + resp[3] = RD(HC_RESPONSE_3); + } else { + resp[0] = RD(HC_RESPONSE_0); + } + return 0; +} + +static void +sdhostiosetup(SDio*, int write, void *buf, int bsize, int bcount) +{ + WR(HC_BLOCKSIZE, bsize); + WR(HC_BLOCKCOUNT, bcount); +} + +static void +sdhostio(SDio*, int write, uchar *buf, int len) +{ + u32int i, *r = (u32int*)SDHOSTREGS; + + if(write) + dmastart(DmaChanSdhost, DmaDevSdhost, DmaM2D, buf, r + HC_DATAPORT, len); + else + dmastart(DmaChanSdhost, DmaDevSdhost, DmaD2M, r + HC_DATAPORT, buf, len); + + if(dmawait(DmaChanSdhost) < 0) + error(Eio); + + if((i = RD(HC_HOSTSTATUS)) & HC_HSTST_ERROR) + sdhosterror(i); +} + +void +sdhostlink(void) +{ + static SDio io = { + "sdhost", + sdhostinit, + sdhostenable, + sdhostinquiry, + sdhostcmd, + sdhostiosetup, + sdhostio, + sdhostbus, + }; + addmmcio(&io); +} |