pi3: implement sdhost controller driver so we can use wifi always

This commit is contained in:
cinap_lenrek 2023-11-05 16:37:32 +00:00
parent 019a935f6b
commit 9b68b42610
2 changed files with 260 additions and 1 deletions

View file

@ -28,11 +28,12 @@ dev
link
archbcm3
usbdwc
# ether4330
ether4330
ethermedium
loopbackmedium
netdevmedium
emmc
sdhost
# i2cbcm devi2c
i2cgpio devi2c gpio

258
sys/src/9/bcm64/sdhost.c Normal file
View file

@ -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);
}