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