1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier * bcm2835 spi controller
3*5c47fe09SDavid du Colombier */
4*5c47fe09SDavid du Colombier
5*5c47fe09SDavid du Colombier #include "u.h"
6*5c47fe09SDavid du Colombier #include "../port/lib.h"
7*5c47fe09SDavid du Colombier #include "../port/error.h"
8*5c47fe09SDavid du Colombier #include "mem.h"
9*5c47fe09SDavid du Colombier #include "dat.h"
10*5c47fe09SDavid du Colombier #include "fns.h"
11*5c47fe09SDavid du Colombier #include "io.h"
12*5c47fe09SDavid du Colombier
13*5c47fe09SDavid du Colombier #define SPIREGS (VIRTIO+0x204000)
14*5c47fe09SDavid du Colombier #define SPI0_CE1_N 7 /* P1 pin 26 */
15*5c47fe09SDavid du Colombier #define SPI0_CE0_N 8 /* P1 pin 24 */
16*5c47fe09SDavid du Colombier #define SPI0_MISO 9 /* P1 pin 21 */
17*5c47fe09SDavid du Colombier #define SPI0_MOSI 10 /* P1 pin 19 */
18*5c47fe09SDavid du Colombier #define SPI0_SCLK 11 /* P1 pin 23 */
19*5c47fe09SDavid du Colombier
20*5c47fe09SDavid du Colombier typedef struct Ctlr Ctlr;
21*5c47fe09SDavid du Colombier typedef struct Spiregs Spiregs;
22*5c47fe09SDavid du Colombier
23*5c47fe09SDavid du Colombier /*
24*5c47fe09SDavid du Colombier * Registers for main SPI controller
25*5c47fe09SDavid du Colombier */
26*5c47fe09SDavid du Colombier struct Spiregs {
27*5c47fe09SDavid du Colombier u32int cs; /* control and status */
28*5c47fe09SDavid du Colombier u32int data;
29*5c47fe09SDavid du Colombier u32int clkdiv;
30*5c47fe09SDavid du Colombier u32int dlen;
31*5c47fe09SDavid du Colombier u32int lossitoh;
32*5c47fe09SDavid du Colombier u32int dmactl;
33*5c47fe09SDavid du Colombier };
34*5c47fe09SDavid du Colombier
35*5c47fe09SDavid du Colombier /*
36*5c47fe09SDavid du Colombier * Per-controller info
37*5c47fe09SDavid du Colombier */
38*5c47fe09SDavid du Colombier struct Ctlr {
39*5c47fe09SDavid du Colombier Spiregs *regs;
40*5c47fe09SDavid du Colombier QLock lock;
41*5c47fe09SDavid du Colombier Lock reglock;
42*5c47fe09SDavid du Colombier Rendez r;
43*5c47fe09SDavid du Colombier };
44*5c47fe09SDavid du Colombier
45*5c47fe09SDavid du Colombier static Ctlr spi;
46*5c47fe09SDavid du Colombier
47*5c47fe09SDavid du Colombier enum {
48*5c47fe09SDavid du Colombier /* cs */
49*5c47fe09SDavid du Colombier Lossi32bit = 1<<25,
50*5c47fe09SDavid du Colombier Lossidma = 1<<24,
51*5c47fe09SDavid du Colombier Cspol2 = 1<<23,
52*5c47fe09SDavid du Colombier Cspol1 = 1<<22,
53*5c47fe09SDavid du Colombier Cspol0 = 1<<21,
54*5c47fe09SDavid du Colombier Rxf = 1<<20,
55*5c47fe09SDavid du Colombier Rxr = 1<<19,
56*5c47fe09SDavid du Colombier Txd = 1<<18,
57*5c47fe09SDavid du Colombier Rxd = 1<<17,
58*5c47fe09SDavid du Colombier Done = 1<<16,
59*5c47fe09SDavid du Colombier Lossi = 1<<13,
60*5c47fe09SDavid du Colombier Ren = 1<<12,
61*5c47fe09SDavid du Colombier Adcs = 1<<11, /* automatically deassert chip select (dma) */
62*5c47fe09SDavid du Colombier Intr = 1<<10,
63*5c47fe09SDavid du Colombier Intd = 1<<9,
64*5c47fe09SDavid du Colombier Dmaen = 1<<8,
65*5c47fe09SDavid du Colombier Ta = 1<<7,
66*5c47fe09SDavid du Colombier Cspol = 1<<6,
67*5c47fe09SDavid du Colombier Rxclear = 1<<5,
68*5c47fe09SDavid du Colombier Txclear = 1<<4,
69*5c47fe09SDavid du Colombier Cpol = 1<<3,
70*5c47fe09SDavid du Colombier Cpha = 1<<2,
71*5c47fe09SDavid du Colombier Csmask = 3<<0,
72*5c47fe09SDavid du Colombier Csshift = 0,
73*5c47fe09SDavid du Colombier
74*5c47fe09SDavid du Colombier /* dmactl */
75*5c47fe09SDavid du Colombier Rpanicshift = 24,
76*5c47fe09SDavid du Colombier Rdreqshift = 16,
77*5c47fe09SDavid du Colombier Tpanicshift = 8,
78*5c47fe09SDavid du Colombier Tdreqshift = 0,
79*5c47fe09SDavid du Colombier };
80*5c47fe09SDavid du Colombier
81*5c47fe09SDavid du Colombier static void
spiinit(void)82*5c47fe09SDavid du Colombier spiinit(void)
83*5c47fe09SDavid du Colombier {
84*5c47fe09SDavid du Colombier spi.regs = (Spiregs*)SPIREGS;
85*5c47fe09SDavid du Colombier spi.regs->clkdiv = 250; /* 1 MHz */
86*5c47fe09SDavid du Colombier gpiosel(SPI0_MISO, Alt0);
87*5c47fe09SDavid du Colombier gpiosel(SPI0_MOSI, Alt0);
88*5c47fe09SDavid du Colombier gpiosel(SPI0_SCLK, Alt0);
89*5c47fe09SDavid du Colombier gpiosel(SPI0_CE0_N, Alt0);
90*5c47fe09SDavid du Colombier gpiosel(SPI0_CE1_N, Alt0);
91*5c47fe09SDavid du Colombier }
92*5c47fe09SDavid du Colombier
93*5c47fe09SDavid du Colombier void
spimode(int mode)94*5c47fe09SDavid du Colombier spimode(int mode)
95*5c47fe09SDavid du Colombier {
96*5c47fe09SDavid du Colombier if(spi.regs == 0)
97*5c47fe09SDavid du Colombier spiinit();
98*5c47fe09SDavid du Colombier spi.regs->cs = (spi.regs->cs & ~(Cpha | Cpol)) | (mode << 2);
99*5c47fe09SDavid du Colombier }
100*5c47fe09SDavid du Colombier
101*5c47fe09SDavid du Colombier /*
102*5c47fe09SDavid du Colombier * According the Broadcom docs, the divisor has to
103*5c47fe09SDavid du Colombier * be a power of 2, but an errata says that should
104*5c47fe09SDavid du Colombier * be multiple of 2 and scope observations confirm
105*5c47fe09SDavid du Colombier * that restricting it to a power of 2 is unnecessary.
106*5c47fe09SDavid du Colombier */
107*5c47fe09SDavid du Colombier void
spiclock(uint mhz)108*5c47fe09SDavid du Colombier spiclock(uint mhz)
109*5c47fe09SDavid du Colombier {
110*5c47fe09SDavid du Colombier if(spi.regs == 0)
111*5c47fe09SDavid du Colombier spiinit();
112*5c47fe09SDavid du Colombier if(mhz == 0) {
113*5c47fe09SDavid du Colombier spi.regs->clkdiv = 32768; /* about 8 KHz */
114*5c47fe09SDavid du Colombier return;
115*5c47fe09SDavid du Colombier }
116*5c47fe09SDavid du Colombier spi.regs->clkdiv = 2 * ((125 + (mhz - 1)) / mhz);
117*5c47fe09SDavid du Colombier }
118*5c47fe09SDavid du Colombier
119*5c47fe09SDavid du Colombier void
spirw(uint cs,void * buf,int len)120*5c47fe09SDavid du Colombier spirw(uint cs, void *buf, int len)
121*5c47fe09SDavid du Colombier {
122*5c47fe09SDavid du Colombier Spiregs *r;
123*5c47fe09SDavid du Colombier
124*5c47fe09SDavid du Colombier assert(cs <= 2);
125*5c47fe09SDavid du Colombier assert(len < (1<<16));
126*5c47fe09SDavid du Colombier qlock(&spi.lock);
127*5c47fe09SDavid du Colombier if(waserror()){
128*5c47fe09SDavid du Colombier qunlock(&spi.lock);
129*5c47fe09SDavid du Colombier nexterror();
130*5c47fe09SDavid du Colombier }
131*5c47fe09SDavid du Colombier if(spi.regs == 0)
132*5c47fe09SDavid du Colombier spiinit();
133*5c47fe09SDavid du Colombier r = spi.regs;
134*5c47fe09SDavid du Colombier r->dlen = len;
135*5c47fe09SDavid du Colombier r->cs = (r->cs & (Cpha | Cpol)) | (cs << Csshift) | Rxclear | Txclear | Dmaen | Adcs | Ta;
136*5c47fe09SDavid du Colombier /*
137*5c47fe09SDavid du Colombier * Start write channel before read channel - cache wb before inv
138*5c47fe09SDavid du Colombier */
139*5c47fe09SDavid du Colombier dmastart(DmaChanSpiTx, DmaDevSpiTx, DmaM2D,
140*5c47fe09SDavid du Colombier buf, &r->data, len);
141*5c47fe09SDavid du Colombier dmastart(DmaChanSpiRx, DmaDevSpiRx, DmaD2M,
142*5c47fe09SDavid du Colombier &r->data, buf, len);
143*5c47fe09SDavid du Colombier if(dmawait(DmaChanSpiRx) < 0)
144*5c47fe09SDavid du Colombier error(Eio);
145*5c47fe09SDavid du Colombier cachedinvse(buf, len);
146*5c47fe09SDavid du Colombier r->cs &= (Cpha | Cpol);
147*5c47fe09SDavid du Colombier qunlock(&spi.lock);
148*5c47fe09SDavid du Colombier poperror();
149*5c47fe09SDavid du Colombier }
150