| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| #include <assert.h> |
| #include <commonlib/helpers.h> |
| #include <spi-generic.h> |
| #include <stddef.h> |
| #include <string.h> |
| |
| int spi_claim_bus(const struct spi_slave *slave) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| if (ctrlr && ctrlr->claim_bus) |
| return ctrlr->claim_bus(slave); |
| return 0; |
| } |
| |
| void spi_release_bus(const struct spi_slave *slave) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| if (ctrlr && ctrlr->release_bus) |
| ctrlr->release_bus(slave); |
| } |
| |
| static int spi_xfer_single_op(const struct spi_slave *slave, |
| struct spi_op *op) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| int ret; |
| |
| if (!ctrlr || !ctrlr->xfer) |
| return -1; |
| |
| ret = ctrlr->xfer(slave, op->dout, op->bytesout, op->din, op->bytesin); |
| if (ret) |
| op->status = SPI_OP_FAILURE; |
| else |
| op->status = SPI_OP_SUCCESS; |
| |
| return ret; |
| } |
| |
| static int spi_xfer_vector_default(const struct spi_slave *slave, |
| struct spi_op vectors[], size_t count) |
| { |
| size_t i; |
| int ret; |
| |
| for (i = 0; i < count; i++) { |
| ret = spi_xfer_single_op(slave, &vectors[i]); |
| if (ret) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int spi_xfer_vector(const struct spi_slave *slave, |
| struct spi_op vectors[], size_t count) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| |
| if (ctrlr && ctrlr->xfer_vector) |
| return ctrlr->xfer_vector(slave, vectors, count); |
| |
| return spi_xfer_vector_default(slave, vectors, count); |
| } |
| |
| int spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytesout, |
| void *din, size_t bytesin) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| |
| if (ctrlr && ctrlr->xfer) |
| return ctrlr->xfer(slave, dout, bytesout, din, bytesin); |
| |
| return -1; |
| } |
| |
| unsigned int spi_crop_chunk(const struct spi_slave *slave, unsigned int cmd_len, |
| unsigned int buf_len) |
| { |
| const struct spi_ctrlr *ctrlr = slave->ctrlr; |
| unsigned int ctrlr_max; |
| bool deduct_cmd_len; |
| bool deduct_opcode_len; |
| |
| if (!ctrlr) |
| return 0; |
| |
| deduct_cmd_len = !!(ctrlr->flags & SPI_CNTRLR_DEDUCT_CMD_LEN); |
| deduct_opcode_len = !!(ctrlr->flags & SPI_CNTRLR_DEDUCT_OPCODE_LEN); |
| ctrlr_max = ctrlr->max_xfer_size; |
| |
| assert(ctrlr_max != 0); |
| |
| /* Assume opcode is always one byte and deduct it from the cmd_len |
| as the hardware has a separate register for the opcode. */ |
| if (deduct_opcode_len) |
| cmd_len--; |
| |
| /* Subtract command length from usable buffer size. If |
| deduct_opcode_len is set, only subtract the number command bytes |
| after the opcode. If the adjusted cmd_len is larger than ctrlr_max |
| return 0 to inidicate an error. */ |
| if (deduct_cmd_len) { |
| if (ctrlr_max >= cmd_len) { |
| ctrlr_max -= cmd_len; |
| } else { |
| ctrlr_max = 0; |
| printk(BIOS_WARNING, "%s: Command longer than buffer\n", __func__); |
| } |
| } |
| |
| return MIN(ctrlr_max, buf_len); |
| } |
| |
| void __weak spi_init(void) |
| { |
| /* Default weak implementation - do nothing. */ |
| } |
| |
| int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave) |
| { |
| size_t i; |
| |
| memset(slave, 0, sizeof(*slave)); |
| |
| for (i = 0; i < spi_ctrlr_bus_map_count; i++) { |
| if ((spi_ctrlr_bus_map[i].bus_start <= bus) && |
| (spi_ctrlr_bus_map[i].bus_end >= bus)) { |
| slave->ctrlr = spi_ctrlr_bus_map[i].ctrlr; |
| break; |
| } |
| } |
| |
| if (slave->ctrlr == NULL) { |
| printk(BIOS_ERR, "Can't find SPI bus %u\n", bus); |
| return -1; |
| } |
| |
| slave->bus = bus; |
| slave->cs = cs; |
| |
| if (slave->ctrlr->setup) |
| return slave->ctrlr->setup(slave); |
| |
| return 0; |
| } |