|
@@ -62,6 +62,7 @@
|
|
|
#define MXC_CSPICTRL_MAXBITS 0x1f
|
|
|
|
|
|
#define MXC_CSPIPERIOD_32KHZ (1 << 15)
|
|
|
+#define MAX_SPI_BYTES 4
|
|
|
|
|
|
static unsigned long spi_bases[] = {
|
|
|
0x43fa4000,
|
|
@@ -95,6 +96,7 @@ static unsigned long spi_bases[] = {
|
|
|
#define MXC_CSPICTRL_RXOVF (1 << 6)
|
|
|
|
|
|
#define MXC_CSPIPERIOD_32KHZ (1 << 15)
|
|
|
+#define MAX_SPI_BYTES 32
|
|
|
|
|
|
/* Bit position inside CTRL register to be associated with SS */
|
|
|
#define MXC_CSPICTRL_CHAN 18
|
|
@@ -252,13 +254,15 @@ static s32 spi_cfg(struct mxc_spi_slave *mxcs, unsigned int cs,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen,
|
|
|
- unsigned long flags)
|
|
|
+int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen,
|
|
|
+ const u8 *dout, u8 *din, unsigned long flags)
|
|
|
{
|
|
|
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
|
|
|
+ int nbytes = (bitlen + 7) / 8;
|
|
|
+ u32 data, cnt, i;
|
|
|
|
|
|
- if (flags & SPI_XFER_BEGIN)
|
|
|
- spi_cs_activate(slave);
|
|
|
+ debug("%s: bitlen %d dout 0x%x din 0x%x\n",
|
|
|
+ __func__, bitlen, (u32)dout, (u32)din);
|
|
|
|
|
|
mxcs->ctrl_reg = (mxcs->ctrl_reg &
|
|
|
~MXC_CSPICTRL_BITCOUNT(MXC_CSPICTRL_MAXBITS)) |
|
|
@@ -273,8 +277,46 @@ static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen,
|
|
|
reg_write(mxcs->base + MXC_CSPISTAT,
|
|
|
MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
|
|
|
|
|
|
- debug("Sending SPI 0x%x\n", data);
|
|
|
- reg_write(mxcs->base + MXC_CSPITXDATA, data);
|
|
|
+ /*
|
|
|
+ * The SPI controller works only with words,
|
|
|
+ * check if less than a word is sent.
|
|
|
+ * Access to the FIFO is only 32 bit
|
|
|
+ */
|
|
|
+ if (bitlen % 32) {
|
|
|
+ data = 0;
|
|
|
+ cnt = (bitlen % 32) / 8;
|
|
|
+ if (dout) {
|
|
|
+ for (i = 0; i < cnt; i++) {
|
|
|
+ data = (data << 8) | (*dout++ & 0xFF);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ debug("Sending SPI 0x%x\n", data);
|
|
|
+
|
|
|
+ reg_write(mxcs->base + MXC_CSPITXDATA, data);
|
|
|
+ nbytes -= cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ data = 0;
|
|
|
+
|
|
|
+ while (nbytes > 0) {
|
|
|
+ data = 0;
|
|
|
+ if (dout) {
|
|
|
+ /* Buffer is not 32-bit aligned */
|
|
|
+ if ((unsigned long)dout & 0x03) {
|
|
|
+ data = 0;
|
|
|
+ for (i = 0; i < 4; i++, data <<= 8) {
|
|
|
+ data = (data << 8) | (*dout++ & 0xFF);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ data = *(u32 *)dout;
|
|
|
+ data = cpu_to_be32(data);
|
|
|
+ }
|
|
|
+ dout += 4;
|
|
|
+ }
|
|
|
+ debug("Sending SPI 0x%x\n", data);
|
|
|
+ reg_write(mxcs->base + MXC_CSPITXDATA, data);
|
|
|
+ nbytes -= 4;
|
|
|
+ }
|
|
|
|
|
|
/* FIFO is written, now starts the transfer setting the XCH bit */
|
|
|
reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg |
|
|
@@ -288,49 +330,78 @@ static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen,
|
|
|
reg_write(mxcs->base + MXC_CSPISTAT,
|
|
|
MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
|
|
|
|
|
|
- data = reg_read(mxcs->base + MXC_CSPIRXDATA);
|
|
|
- debug("SPI Rx: 0x%x\n", data);
|
|
|
+ nbytes = (bitlen + 7) / 8;
|
|
|
|
|
|
- if (flags & SPI_XFER_END)
|
|
|
- spi_cs_deactivate(slave);
|
|
|
+ cnt = nbytes % 32;
|
|
|
|
|
|
- return data;
|
|
|
+ if (bitlen % 32) {
|
|
|
+ data = reg_read(mxcs->base + MXC_CSPIRXDATA);
|
|
|
+ cnt = (bitlen % 32) / 8;
|
|
|
+ debug("SPI Rx unaligned: 0x%x\n", data);
|
|
|
+ if (din) {
|
|
|
+ for (i = 0; i < cnt; i++, data >>= 8) {
|
|
|
+ *din++ = data & 0xFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nbytes -= cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (nbytes > 0) {
|
|
|
+ u32 tmp;
|
|
|
+ tmp = reg_read(mxcs->base + MXC_CSPIRXDATA);
|
|
|
+ data = cpu_to_be32(tmp);
|
|
|
+ debug("SPI Rx: 0x%x 0x%x\n", tmp, data);
|
|
|
+ cnt = min(nbytes, sizeof(data));
|
|
|
+ if (din) {
|
|
|
+ memcpy(din, &data, cnt);
|
|
|
+ din += cnt;
|
|
|
+ }
|
|
|
+ nbytes -= cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
+
|
|
|
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
|
|
void *din, unsigned long flags)
|
|
|
{
|
|
|
- int n_blks = (bitlen + 31) / 32;
|
|
|
- u32 *out_l, *in_l;
|
|
|
- int i;
|
|
|
+ int n_bytes = (bitlen + 7) / 8;
|
|
|
+ int n_bits;
|
|
|
+ int ret;
|
|
|
+ u32 blk_size;
|
|
|
+ u8 *p_outbuf = (u8 *)dout;
|
|
|
+ u8 *p_inbuf = (u8 *)din;
|
|
|
|
|
|
- if ((int)dout & 3 || (int)din & 3) {
|
|
|
- printf("Error: unaligned buffers in: %p, out: %p\n", din, dout);
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ if (!slave)
|
|
|
+ return -1;
|
|
|
|
|
|
- /* This driver is currently partly broken, alert the user */
|
|
|
- if (bitlen > 16 && (bitlen % 32)) {
|
|
|
- printf("Error: SPI transfer with bitlen=%d is broken.\n",
|
|
|
- bitlen);
|
|
|
- return 1;
|
|
|
+ if (flags & SPI_XFER_BEGIN)
|
|
|
+ spi_cs_activate(slave);
|
|
|
+
|
|
|
+ while (n_bytes > 0) {
|
|
|
+
|
|
|
+ if (n_bytes < MAX_SPI_BYTES)
|
|
|
+ blk_size = n_bytes;
|
|
|
+ else
|
|
|
+ blk_size = MAX_SPI_BYTES;
|
|
|
+
|
|
|
+ n_bits = blk_size * 8;
|
|
|
+
|
|
|
+ ret = spi_xchg_single(slave, n_bits, p_outbuf, p_inbuf, 0);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if (dout)
|
|
|
+ p_outbuf += blk_size;
|
|
|
+ if (din)
|
|
|
+ p_inbuf += blk_size;
|
|
|
+ n_bytes -= blk_size;
|
|
|
}
|
|
|
|
|
|
- for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout;
|
|
|
- i < n_blks;
|
|
|
- i++, in_l++, out_l++, bitlen -= 32) {
|
|
|
- u32 data = spi_xchg_single(slave, *out_l, bitlen, flags);
|
|
|
-
|
|
|
- /* Check if we're only transfering 8 or 16 bits */
|
|
|
- if (!i) {
|
|
|
- if (bitlen < 9)
|
|
|
- *(u8 *)din = data;
|
|
|
- else if (bitlen < 17)
|
|
|
- *(u16 *)din = data;
|
|
|
- else
|
|
|
- *in_l = data;
|
|
|
- }
|
|
|
+ if (flags & SPI_XFER_END) {
|
|
|
+ spi_cs_deactivate(slave);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -378,8 +449,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
|
|
return NULL;
|
|
|
|
|
|
mxcs = malloc(sizeof(struct mxc_spi_slave));
|
|
|
- if (!mxcs)
|
|
|
+ if (!mxcs) {
|
|
|
+ puts("mxc_spi: SPI Slave not allocated !\n");
|
|
|
return NULL;
|
|
|
+ }
|
|
|
|
|
|
ret = decode_cs(mxcs, cs);
|
|
|
if (ret < 0) {
|