cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spi-bitbang-txrx.h (4963B)


      1/* SPDX-License-Identifier: GPL-2.0 */
      2/*
      3 * Mix this utility code with some glue code to get one of several types of
      4 * simple SPI master driver.  Two do polled word-at-a-time I/O:
      5 *
      6 *   -	GPIO/parport bitbangers.  Provide chipselect() and txrx_word[](),
      7 *	expanding the per-word routines from the inline templates below.
      8 *
      9 *   -	Drivers for controllers resembling bare shift registers.  Provide
     10 *	chipselect() and txrx_word[](), with custom setup()/cleanup() methods
     11 *	that use your controller's clock and chipselect registers.
     12 *
     13 * Some hardware works well with requests at spi_transfer scope:
     14 *
     15 *   -	Drivers leveraging smarter hardware, with fifos or DMA; or for half
     16 *	duplex (MicroWire) controllers.  Provide chipselect() and txrx_bufs(),
     17 *	and custom setup()/cleanup() methods.
     18 */
     19
     20/*
     21 * The code that knows what GPIO pins do what should have declared four
     22 * functions, ideally as inlines, before including this header:
     23 *
     24 *  void setsck(struct spi_device *, int is_on);
     25 *  void setmosi(struct spi_device *, int is_on);
     26 *  int getmiso(struct spi_device *);
     27 *  void spidelay(unsigned);
     28 *
     29 * setsck()'s is_on parameter is a zero/nonzero boolean.
     30 *
     31 * setmosi()'s is_on parameter is a zero/nonzero boolean.
     32 *
     33 * getmiso() is required to return 0 or 1 only. Any other value is invalid
     34 * and will result in improper operation.
     35 *
     36 * A non-inlined routine would call bitbang_txrx_*() routines.  The
     37 * main loop could easily compile down to a handful of instructions,
     38 * especially if the delay is a NOP (to run at peak speed).
     39 *
     40 * Since this is software, the timings may not be exactly what your board's
     41 * chips need ... there may be several reasons you'd need to tweak timings
     42 * in these routines, not just to make it faster or slower to match a
     43 * particular CPU clock rate.
     44 *
     45 * ToDo: Maybe the bitrev macros can be used to improve the code?
     46 */
     47
     48static inline u32
     49bitbang_txrx_be_cpha0(struct spi_device *spi,
     50		unsigned nsecs, unsigned cpol, unsigned flags,
     51		u32 word, u8 bits)
     52{
     53	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
     54
     55	u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
     56	/* clock starts at inactive polarity */
     57	for (word <<= (32 - bits); likely(bits); bits--) {
     58
     59		/* setup MSB (to slave) on trailing edge */
     60		if ((flags & SPI_MASTER_NO_TX) == 0) {
     61			if ((word & (1 << 31)) != oldbit) {
     62				setmosi(spi, word & (1 << 31));
     63				oldbit = word & (1 << 31);
     64			}
     65		}
     66		spidelay(nsecs);	/* T(setup) */
     67
     68		setsck(spi, !cpol);
     69		spidelay(nsecs);
     70
     71		/* sample MSB (from slave) on leading edge */
     72		word <<= 1;
     73		if ((flags & SPI_MASTER_NO_RX) == 0)
     74			word |= getmiso(spi);
     75		setsck(spi, cpol);
     76	}
     77	return word;
     78}
     79
     80static inline u32
     81bitbang_txrx_be_cpha1(struct spi_device *spi,
     82		unsigned nsecs, unsigned cpol, unsigned flags,
     83		u32 word, u8 bits)
     84{
     85	/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
     86
     87	u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
     88	/* clock starts at inactive polarity */
     89	for (word <<= (32 - bits); likely(bits); bits--) {
     90
     91		/* setup MSB (to slave) on leading edge */
     92		setsck(spi, !cpol);
     93		if ((flags & SPI_MASTER_NO_TX) == 0) {
     94			if ((word & (1 << 31)) != oldbit) {
     95				setmosi(spi, word & (1 << 31));
     96				oldbit = word & (1 << 31);
     97			}
     98		}
     99		spidelay(nsecs); /* T(setup) */
    100
    101		setsck(spi, cpol);
    102		spidelay(nsecs);
    103
    104		/* sample MSB (from slave) on trailing edge */
    105		word <<= 1;
    106		if ((flags & SPI_MASTER_NO_RX) == 0)
    107			word |= getmiso(spi);
    108	}
    109	return word;
    110}
    111
    112static inline u32
    113bitbang_txrx_le_cpha0(struct spi_device *spi,
    114		unsigned int nsecs, unsigned int cpol, unsigned int flags,
    115		u32 word, u8 bits)
    116{
    117	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
    118
    119	u32 oldbit = !(word & 1);
    120	/* clock starts at inactive polarity */
    121	for (; likely(bits); bits--) {
    122
    123		/* setup LSB (to slave) on trailing edge */
    124		if ((flags & SPI_MASTER_NO_TX) == 0) {
    125			if ((word & 1) != oldbit) {
    126				setmosi(spi, word & 1);
    127				oldbit = word & 1;
    128			}
    129		}
    130		spidelay(nsecs);	/* T(setup) */
    131
    132		setsck(spi, !cpol);
    133		spidelay(nsecs);
    134
    135		/* sample LSB (from slave) on leading edge */
    136		word >>= 1;
    137		if ((flags & SPI_MASTER_NO_RX) == 0)
    138			word |= getmiso(spi) << (bits - 1);
    139		setsck(spi, cpol);
    140	}
    141	return word;
    142}
    143
    144static inline u32
    145bitbang_txrx_le_cpha1(struct spi_device *spi,
    146		unsigned int nsecs, unsigned int cpol, unsigned int flags,
    147		u32 word, u8 bits)
    148{
    149	/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
    150
    151	u32 oldbit = !(word & 1);
    152	/* clock starts at inactive polarity */
    153	for (; likely(bits); bits--) {
    154
    155		/* setup LSB (to slave) on leading edge */
    156		setsck(spi, !cpol);
    157		if ((flags & SPI_MASTER_NO_TX) == 0) {
    158			if ((word & 1) != oldbit) {
    159				setmosi(spi, word & 1);
    160				oldbit = word & 1;
    161			}
    162		}
    163		spidelay(nsecs); /* T(setup) */
    164
    165		setsck(spi, cpol);
    166		spidelay(nsecs);
    167
    168		/* sample LSB (from slave) on trailing edge */
    169		word >>= 1;
    170		if ((flags & SPI_MASTER_NO_RX) == 0)
    171			word |= getmiso(spi) << (bits - 1);
    172	}
    173	return word;
    174}