struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) { struct tegra_spi_channel *channel = to_tegra_spi(bus); if (!channel) return NULL; return &channel->slave; }
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave) { struct tegra_spi_channel *channel = to_tegra_spi(bus); if (!channel) return -1; slave->bus = channel->slave.bus; slave->cs = channel->slave.cs; slave->ctrlr = &spi_ctrlr; return 0; }
static void spi_ctrlr_release_bus(const struct spi_slave *slave) { struct tegra_spi_regs *regs = to_tegra_spi(slave->bus)->regs; u32 val; val = read32(®s->command1); if (val & (SPI_CMD1_CS_POL_INACTIVE0 << slave->cs)) val |= SPI_CMD1_CS_SW_VAL; else val &= ~SPI_CMD1_CS_SW_VAL; write32(®s->command1, val); }
static int spi_ctrlr_claim_bus(const struct spi_slave *slave) { struct tegra_spi_regs *regs = to_tegra_spi(slave->bus)->regs; u32 val; tegra_spi_init(slave->bus); val = read32(®s->command1); /* select appropriate chip-select line */ val &= ~(SPI_CMD1_CS_SEL_MASK << SPI_CMD1_CS_SEL_SHIFT); val |= (slave->cs << SPI_CMD1_CS_SEL_SHIFT); /* drive chip-select with the inverse of the "inactive" value */ if (val & (SPI_CMD1_CS_POL_INACTIVE0 << slave->cs)) val &= ~SPI_CMD1_CS_SW_VAL; else val |= SPI_CMD1_CS_SW_VAL; write32(®s->command1, val); return 0; }
static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout, size_t out_bytes, void *din, size_t in_bytes) { struct tegra_spi_channel *spi = to_tegra_spi(slave->bus); u8 *out_buf = (u8 *)dout; u8 *in_buf = (u8 *)din; size_t todo; int ret = 0; /* tegra bus numbers start at 1 */ ASSERT(slave->bus >= 1 && slave->bus <= ARRAY_SIZE(tegra_spi_channels)); while (out_bytes || in_bytes) { int x = 0; if (out_bytes == 0) todo = in_bytes; else if (in_bytes == 0) todo = out_bytes; else todo = MIN(out_bytes, in_bytes); if (out_bytes) { x = xfer_setup(spi, out_buf, todo, SPI_SEND); if (x < 0) { if (spi->xfer_mode == XFER_MODE_NONE) { spi->xfer_mode = XFER_MODE_PIO; continue; } else { ret = -1; break; } } } if (in_bytes) { x = xfer_setup(spi, in_buf, todo, SPI_RECEIVE); if (x < 0) { if (spi->xfer_mode == XFER_MODE_NONE) { spi->xfer_mode = XFER_MODE_PIO; continue; } else { ret = -1; break; } } } /* * Note: Some devices (such as Chrome EC) are sensitive to * delays, so be careful when adding debug prints not to * cause timeouts between transfers. */ xfer_start(spi); xfer_wait(spi); if (xfer_finish(spi)) { ret = -1; break; } /* Post-processing. */ if (out_bytes) { out_bytes -= x; out_buf += x; } if (in_bytes) { in_bytes -= x; in_buf += x; } } if (ret < 0) { printk(BIOS_ERR, "%s: Error detected\n", __func__); printk(BIOS_ERR, "Transaction size: %u, bytes remaining: " "%u out / %u in\n", todo, out_bytes, in_bytes); clear_fifo_status(spi); } return ret; }