mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
pi3: implement sdhost controller driver so we can use wifi always
This commit is contained in:
parent
019a935f6b
commit
9b68b42610
2 changed files with 260 additions and 1 deletions
|
@ -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
258
sys/src/9/bcm64/sdhost.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue