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