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