xref: /plan9-contrib/sys/src/9/bcm/spi.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
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