162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Mix this utility code with some glue code to get one of several types of
462306a36Sopenharmony_ci * simple SPI master driver.  Two do polled word-at-a-time I/O:
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   -	GPIO/parport bitbangers.  Provide chipselect() and txrx_word[](),
762306a36Sopenharmony_ci *	expanding the per-word routines from the inline templates below.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *   -	Drivers for controllers resembling bare shift registers.  Provide
1062306a36Sopenharmony_ci *	chipselect() and txrx_word[](), with custom setup()/cleanup() methods
1162306a36Sopenharmony_ci *	that use your controller's clock and chipselect registers.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Some hardware works well with requests at spi_transfer scope:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *   -	Drivers leveraging smarter hardware, with fifos or DMA; or for half
1662306a36Sopenharmony_ci *	duplex (MicroWire) controllers.  Provide chipselect() and txrx_bufs(),
1762306a36Sopenharmony_ci *	and custom setup()/cleanup() methods.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * The code that knows what GPIO pins do what should have declared four
2262306a36Sopenharmony_ci * functions, ideally as inlines, before including this header:
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *  void setsck(struct spi_device *, int is_on);
2562306a36Sopenharmony_ci *  void setmosi(struct spi_device *, int is_on);
2662306a36Sopenharmony_ci *  int getmiso(struct spi_device *);
2762306a36Sopenharmony_ci *  void spidelay(unsigned);
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * setsck()'s is_on parameter is a zero/nonzero boolean.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * setmosi()'s is_on parameter is a zero/nonzero boolean.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * getmiso() is required to return 0 or 1 only. Any other value is invalid
3462306a36Sopenharmony_ci * and will result in improper operation.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * A non-inlined routine would call bitbang_txrx_*() routines.  The
3762306a36Sopenharmony_ci * main loop could easily compile down to a handful of instructions,
3862306a36Sopenharmony_ci * especially if the delay is a NOP (to run at peak speed).
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * Since this is software, the timings may not be exactly what your board's
4162306a36Sopenharmony_ci * chips need ... there may be several reasons you'd need to tweak timings
4262306a36Sopenharmony_ci * in these routines, not just to make it faster or slower to match a
4362306a36Sopenharmony_ci * particular CPU clock rate.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * ToDo: Maybe the bitrev macros can be used to improve the code?
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline u32
4962306a36Sopenharmony_cibitbang_txrx_be_cpha0(struct spi_device *spi,
5062306a36Sopenharmony_ci		unsigned nsecs, unsigned cpol, unsigned flags,
5162306a36Sopenharmony_ci		u32 word, u8 bits)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
5662306a36Sopenharmony_ci	/* clock starts at inactive polarity */
5762306a36Sopenharmony_ci	for (word <<= (32 - bits); likely(bits); bits--) {
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* setup MSB (to slave) on trailing edge */
6062306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_TX) == 0) {
6162306a36Sopenharmony_ci			if ((word & (1 << 31)) != oldbit) {
6262306a36Sopenharmony_ci				setmosi(spi, word & (1 << 31));
6362306a36Sopenharmony_ci				oldbit = word & (1 << 31);
6462306a36Sopenharmony_ci			}
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci		spidelay(nsecs);	/* T(setup) */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		setsck(spi, !cpol);
6962306a36Sopenharmony_ci		spidelay(nsecs);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		/* sample MSB (from slave) on leading edge */
7262306a36Sopenharmony_ci		word <<= 1;
7362306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_RX) == 0)
7462306a36Sopenharmony_ci			word |= getmiso(spi);
7562306a36Sopenharmony_ci		setsck(spi, cpol);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	return word;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline u32
8162306a36Sopenharmony_cibitbang_txrx_be_cpha1(struct spi_device *spi,
8262306a36Sopenharmony_ci		unsigned nsecs, unsigned cpol, unsigned flags,
8362306a36Sopenharmony_ci		u32 word, u8 bits)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
8862306a36Sopenharmony_ci	/* clock starts at inactive polarity */
8962306a36Sopenharmony_ci	for (word <<= (32 - bits); likely(bits); bits--) {
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		/* setup MSB (to slave) on leading edge */
9262306a36Sopenharmony_ci		setsck(spi, !cpol);
9362306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_TX) == 0) {
9462306a36Sopenharmony_ci			if ((word & (1 << 31)) != oldbit) {
9562306a36Sopenharmony_ci				setmosi(spi, word & (1 << 31));
9662306a36Sopenharmony_ci				oldbit = word & (1 << 31);
9762306a36Sopenharmony_ci			}
9862306a36Sopenharmony_ci		}
9962306a36Sopenharmony_ci		spidelay(nsecs); /* T(setup) */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		setsck(spi, cpol);
10262306a36Sopenharmony_ci		spidelay(nsecs);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		/* sample MSB (from slave) on trailing edge */
10562306a36Sopenharmony_ci		word <<= 1;
10662306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_RX) == 0)
10762306a36Sopenharmony_ci			word |= getmiso(spi);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	return word;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic inline u32
11362306a36Sopenharmony_cibitbang_txrx_le_cpha0(struct spi_device *spi,
11462306a36Sopenharmony_ci		unsigned int nsecs, unsigned int cpol, unsigned int flags,
11562306a36Sopenharmony_ci		u32 word, u8 bits)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	u8 rxbit = bits - 1;
12062306a36Sopenharmony_ci	u32 oldbit = !(word & 1);
12162306a36Sopenharmony_ci	/* clock starts at inactive polarity */
12262306a36Sopenharmony_ci	for (; likely(bits); bits--) {
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		/* setup LSB (to slave) on trailing edge */
12562306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_TX) == 0) {
12662306a36Sopenharmony_ci			if ((word & 1) != oldbit) {
12762306a36Sopenharmony_ci				setmosi(spi, word & 1);
12862306a36Sopenharmony_ci				oldbit = word & 1;
12962306a36Sopenharmony_ci			}
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci		spidelay(nsecs);	/* T(setup) */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		setsck(spi, !cpol);
13462306a36Sopenharmony_ci		spidelay(nsecs);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		/* sample LSB (from slave) on leading edge */
13762306a36Sopenharmony_ci		word >>= 1;
13862306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_RX) == 0)
13962306a36Sopenharmony_ci			word |= getmiso(spi) << rxbit;
14062306a36Sopenharmony_ci		setsck(spi, cpol);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	return word;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline u32
14662306a36Sopenharmony_cibitbang_txrx_le_cpha1(struct spi_device *spi,
14762306a36Sopenharmony_ci		unsigned int nsecs, unsigned int cpol, unsigned int flags,
14862306a36Sopenharmony_ci		u32 word, u8 bits)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	u8 rxbit = bits - 1;
15362306a36Sopenharmony_ci	u32 oldbit = !(word & 1);
15462306a36Sopenharmony_ci	/* clock starts at inactive polarity */
15562306a36Sopenharmony_ci	for (; likely(bits); bits--) {
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		/* setup LSB (to slave) on leading edge */
15862306a36Sopenharmony_ci		setsck(spi, !cpol);
15962306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_TX) == 0) {
16062306a36Sopenharmony_ci			if ((word & 1) != oldbit) {
16162306a36Sopenharmony_ci				setmosi(spi, word & 1);
16262306a36Sopenharmony_ci				oldbit = word & 1;
16362306a36Sopenharmony_ci			}
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		spidelay(nsecs); /* T(setup) */
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		setsck(spi, cpol);
16862306a36Sopenharmony_ci		spidelay(nsecs);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		/* sample LSB (from slave) on trailing edge */
17162306a36Sopenharmony_ci		word >>= 1;
17262306a36Sopenharmony_ci		if ((flags & SPI_CONTROLLER_NO_RX) == 0)
17362306a36Sopenharmony_ci			word |= getmiso(spi) << rxbit;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	return word;
17662306a36Sopenharmony_ci}
177