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