1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2002
4  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
5  *
6  * Influenced by code from:
7  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
8  */
9 
10 #include <common.h>
11 #include <spi.h>
12 
13 #include <malloc.h>
14 
15 /*-----------------------------------------------------------------------
16  * Definitions
17  */
18 
19 #ifdef DEBUG_SPI
20 #define PRINTD(fmt,args...)	printf (fmt ,##args)
21 #else
22 #define PRINTD(fmt,args...)
23 #endif
24 
25 struct soft_spi_slave {
26 	struct spi_slave slave;
27 	unsigned int mode;
28 };
29 
30 static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)
31 {
32 	return container_of(slave, struct soft_spi_slave, slave);
33 }
34 
35 /*=====================================================================*/
36 /*                         Public Functions                            */
37 /*=====================================================================*/
38 
39 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
40 		unsigned int max_hz, unsigned int mode)
41 {
42 	struct soft_spi_slave *ss;
43 
44 	if (!spi_cs_is_valid(bus, cs))
45 		return NULL;
46 
47 	ss = spi_alloc_slave(struct soft_spi_slave, bus, cs);
48 	if (!ss)
49 		return NULL;
50 
51 	ss->mode = mode;
52 
53 	/* TODO: Use max_hz to limit the SCK rate */
54 
55 	return &ss->slave;
56 }
57 
58 void spi_free_slave(struct spi_slave *slave)
59 {
60 	struct soft_spi_slave *ss = to_soft_spi(slave);
61 
62 	free(ss);
63 }
64 
65 int spi_claim_bus(struct spi_slave *slave)
66 {
67 #ifdef CONFIG_SYS_IMMR
68 	volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
69 #endif
70 	struct soft_spi_slave *ss = to_soft_spi(slave);
71 
72 	/*
73 	 * Make sure the SPI clock is in idle state as defined for
74 	 * this slave.
75 	 */
76 	if (ss->mode & SPI_CPOL)
77 		SPI_SCL(1);
78 	else
79 		SPI_SCL(0);
80 
81 	return 0;
82 }
83 
84 void spi_release_bus(struct spi_slave *slave)
85 {
86 	/* Nothing to do */
87 }
88 
89 /*-----------------------------------------------------------------------
90  * SPI transfer
91  *
92  * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
93  * "bitlen" bits in the SPI MISO port.  That's just the way SPI works.
94  *
95  * The source of the outgoing bits is the "dout" parameter and the
96  * destination of the input bits is the "din" parameter.  Note that "dout"
97  * and "din" can point to the same memory location, in which case the
98  * input data overwrites the output data (since both are buffered by
99  * temporary variables, this is OK).
100  */
101 int  spi_xfer(struct spi_slave *slave, unsigned int bitlen,
102 		const void *dout, void *din, unsigned long flags)
103 {
104 #ifdef CONFIG_SYS_IMMR
105 	volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
106 #endif
107 	struct soft_spi_slave *ss = to_soft_spi(slave);
108 	uchar		tmpdin  = 0;
109 	uchar		tmpdout = 0;
110 	const u8	*txd = dout;
111 	u8		*rxd = din;
112 	int		cpol = ss->mode & SPI_CPOL;
113 	int		cpha = ss->mode & SPI_CPHA;
114 	unsigned int	j;
115 
116 	PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
117 		slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen);
118 
119 	if (flags & SPI_XFER_BEGIN)
120 		spi_cs_activate(slave);
121 
122 	for(j = 0; j < bitlen; j++) {
123 		/*
124 		 * Check if it is time to work on a new byte.
125 		 */
126 		if ((j % 8) == 0) {
127 			if (txd)
128 				tmpdout = *txd++;
129 			else
130 				tmpdout = 0;
131 			if(j != 0) {
132 				if (rxd)
133 					*rxd++ = tmpdin;
134 			}
135 			tmpdin  = 0;
136 		}
137 
138 		if (!cpha)
139 			SPI_SCL(!cpol);
140 		SPI_SDA(tmpdout & 0x80);
141 		SPI_DELAY;
142 		if (cpha)
143 			SPI_SCL(!cpol);
144 		else
145 			SPI_SCL(cpol);
146 		tmpdin	<<= 1;
147 		tmpdin	|= SPI_READ;
148 		tmpdout	<<= 1;
149 		SPI_DELAY;
150 		if (cpha)
151 			SPI_SCL(cpol);
152 	}
153 	/*
154 	 * If the number of bits isn't a multiple of 8, shift the last
155 	 * bits over to left-justify them.  Then store the last byte
156 	 * read in.
157 	 */
158 	if (rxd) {
159 		if ((bitlen % 8) != 0)
160 			tmpdin <<= 8 - (bitlen % 8);
161 		*rxd++ = tmpdin;
162 	}
163 
164 	if (flags & SPI_XFER_END)
165 		spi_cs_deactivate(slave);
166 
167 	return(0);
168 }
169