drivers/spi: reduce confusion in the API

Julius brought up confusion about the current spi api in [1]. In order
alleviate the confusion stemming from supporting x86 spi flash
controllers:

- Remove spi_xfer_two_vectors() which was fusing transactions to
  accomodate the limitations of the spi controllers themselves.
- Add spi_flash_vector_helper() for the x86 spi flash controllers to
  utilize in validating driver/controller current assumptions.
- Remove the xfer() callback in the x86 spi flash drivers which
  will trigger an error as these controllers can't support the api.

[1] https://mail.coreboot.org/pipermail/coreboot/2018-April/086561.html

Change-Id: Id88adc6ad5234c29a739d43521c5f344bb7d3217
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/25745
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
Aaron Durbin 2018-04-19 21:15:25 -06:00
parent 6c2b10e989
commit 851dde8255
15 changed files with 147 additions and 95 deletions

View File

@ -146,56 +146,3 @@ int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
return 0; return 0;
} }
static int spi_xfer_combine_two_vectors(const struct spi_slave *slave,
struct spi_op *v1, struct spi_op *v2)
{
struct spi_op op = {
.dout = v1->dout, .bytesout = v1->bytesout,
.din = v2->din, .bytesin = v2->bytesin,
};
int ret;
/*
* Combine two vectors only if:
* v1 has non-NULL dout and NULL din and
* v2 has non-NULL din and NULL dout and
*
* In all other cases, do not combine the two vectors.
*/
if ((!v1->dout || v1->din) || (v2->dout || !v2->din))
return -1;
ret = spi_xfer_single_op(slave, &op);
v1->status = v2->status = op.status;
return ret;
}
/*
* Helper function to allow chipsets to combine two vectors if possible. This
* function can only handle upto 2 vectors.
*
* Two vectors are combined if first vector has a non-NULL dout and NULL din and
* second vector has a non-NULL din and NULL dout. Otherwise, each vector is
* operated upon one at a time.
*
* Returns 0 on success and non-zero on failure.
*/
int spi_xfer_two_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
int ret;
assert (count <= 2);
if (count == 2) {
ret = spi_xfer_combine_two_vectors(slave, &vectors[0],
&vectors[1]);
if (!ret || (vectors[0].status != SPI_OP_NOT_EXECUTED))
return ret;
}
return spi_xfer_vector_default(slave, vectors, count);
}

View File

@ -519,3 +519,51 @@ int spi_flash_ctrlr_protect_region(const struct spi_flash *flash,
return -1; return -1;
} }
int spi_flash_vector_helper(const struct spi_slave *slave,
struct spi_op vectors[], size_t count,
int (*func)(const struct spi_slave *slave, const void *dout,
size_t bytesout, void *din, size_t bytesin))
{
int ret;
void *din;
size_t bytes_in;
if (count < 1 || count > 2)
return -1;
/* SPI flash commands always have a command first... */
if (!vectors[0].dout || !vectors[0].bytesout)
return -1;
/* And not read any data during the command. */
if (vectors[0].din || vectors[0].bytesin)
return -1;
if (count == 2) {
/* If response bytes requested ensure the buffer is valid. */
if (vectors[1].bytesin && !vectors[1].din)
return -1;
/* No sends can accompany a receive. */
if (vectors[1].dout || vectors[1].bytesout)
return -1;
din = vectors[1].din;
bytes_in = vectors[1].bytesin;
} else {
din = NULL;
bytes_in = 0;
}
ret = func(slave, vectors[0].dout, vectors[0].bytesout, din, bytes_in);
if (ret) {
vectors[0].status = SPI_OP_FAILURE;
if (count == 2)
vectors[1].status = SPI_OP_FAILURE;
} else {
vectors[0].status = SPI_OP_SUCCESS;
if (count == 2)
vectors[1].status = SPI_OP_SUCCESS;
}
return ret;
}

View File

@ -109,7 +109,10 @@ enum {
}; };
/*----------------------------------------------------------------------- /*-----------------------------------------------------------------------
* Representation of a SPI controller. * Representation of a SPI controller. Note the xfer() and xfer_vector()
* callbacks are meant to process full duplex transactions. If the
* controller cannot handle these transactions then return an error when
* din and dout are both set. See spi_xfer() below for more details.
* *
* claim_bus: Claim SPI bus and prepare for communication. * claim_bus: Claim SPI bus and prepare for communication.
* release_bus: Release SPI bus. * release_bus: Release SPI bus.
@ -232,6 +235,10 @@ void spi_release_bus(const struct spi_slave *slave);
* din: Pointer to a string of bytes that will be filled in. * din: Pointer to a string of bytes that will be filled in.
* bytesin: How many bytes to read. * bytesin: How many bytes to read.
* *
* Note that din and dout are transferred simulataneously in a full duplex
* transaction. The number of clocks within one transaction is calculated
* as: MAX(bytesout*8, bytesin*8).
*
* Returns: 0 on success, not 0 on failure * Returns: 0 on success, not 0 on failure
*/ */
int spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytesout, int spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytesout,
@ -281,23 +288,4 @@ static inline int spi_w8r8(const struct spi_slave *slave, unsigned char byte)
return ret < 0 ? ret : din[1]; return ret < 0 ? ret : din[1];
} }
/*
* Helper function to allow chipsets to combine two vectors if possible. It can
* only handle upto 2 vectors.
*
* This function is provided to support command-response kind of transactions
* expected by users like flash. Some special SPI flash controllers can handle
* such command-response operations in a single transaction. For these special
* controllers, separate command and response vectors can be combined into a
* single operation.
*
* Two vectors are combined if first vector has a non-NULL dout and NULL din and
* second vector has a non-NULL din and NULL dout. Otherwise, each vector is
* operated upon one at a time.
*
* Returns 0 on success and non-zero on failure.
*/
int spi_xfer_two_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count);
#endif /* _SPI_GENERIC_H_ */ #endif /* _SPI_GENERIC_H_ */

View File

@ -125,4 +125,18 @@ const struct spi_flash *boot_device_spi_flash(void);
int spi_flash_ctrlr_protect_region(const struct spi_flash *flash, int spi_flash_ctrlr_protect_region(const struct spi_flash *flash,
const struct region *region); const struct region *region);
/*
* This function is provided to support spi flash command-response transactions.
* Only 2 vectors are supported and the 'func' is called with appropriate
* write and read buffers together. This can be used for chipsets that
* have specific spi flash controllers that don't conform to the normal
* spi xfer API because they are specialized controllers and not generic.
*
* Returns 0 on success and non-zero on failure.
*/
int spi_flash_vector_helper(const struct spi_slave *slave,
struct spi_op vectors[], size_t count,
int (*func)(const struct spi_slave *slave, const void *dout,
size_t bytesout, void *din, size_t bytesin));
#endif /* _SPI_FLASH_H_ */ #endif /* _SPI_FLASH_H_ */

View File

@ -181,9 +181,14 @@ int chipset_volatile_group_end(const struct spi_flash *flash)
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = SPI_FIFO_DEPTH, .max_xfer_size = SPI_FIFO_DEPTH,
.flags = SPI_CNTRLR_DEDUCT_CMD_LEN | SPI_CNTRLR_DEDUCT_OPCODE_LEN, .flags = SPI_CNTRLR_DEDUCT_CMD_LEN | SPI_CNTRLR_DEDUCT_OPCODE_LEN,
}; };

View File

@ -607,9 +607,14 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
}; };

View File

@ -591,9 +591,14 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
}; };

View File

@ -650,9 +650,14 @@ static int spi_flash_protect(const struct spi_flash *flash,
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
.flash_protect = spi_flash_protect, .flash_protect = spi_flash_protect,
}; };

View File

@ -588,9 +588,14 @@ spi_xfer_exit:
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
}; };

View File

@ -604,9 +604,14 @@ spi_xfer_exit:
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
}; };

View File

@ -160,9 +160,14 @@ int chipset_volatile_group_end(const struct spi_flash *flash)
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = AMD_SB_SPI_TX_LEN, .max_xfer_size = AMD_SB_SPI_TX_LEN,
.flags = SPI_CNTRLR_DEDUCT_CMD_LEN, .flags = SPI_CNTRLR_DEDUCT_CMD_LEN,
}; };

View File

@ -151,9 +151,14 @@ int chipset_volatile_group_end(const struct spi_flash *flash)
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = AMD_SB_SPI_TX_LEN, .max_xfer_size = AMD_SB_SPI_TX_LEN,
.flags = SPI_CNTRLR_DEDUCT_CMD_LEN, .flags = SPI_CNTRLR_DEDUCT_CMD_LEN,
}; };

View File

@ -108,9 +108,14 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = AMD_SB_SPI_TX_LEN, .max_xfer_size = AMD_SB_SPI_TX_LEN,
.flags = SPI_CNTRLR_DEDUCT_CMD_LEN, .flags = SPI_CNTRLR_DEDUCT_CMD_LEN,
}; };

View File

@ -953,9 +953,14 @@ static int spi_flash_programmer_probe(const struct spi_slave *spi,
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich9_spi_regs, fdata), .max_xfer_size = member_size(ich9_spi_regs, fdata),
.flash_probe = spi_flash_programmer_probe, .flash_probe = spi_flash_programmer_probe,
}; };

View File

@ -719,9 +719,14 @@ spi_xfer_exit:
return 0; return 0;
} }
static int xfer_vectors(const struct spi_slave *slave,
struct spi_op vectors[], size_t count)
{
return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
}
static const struct spi_ctrlr spi_ctrlr = { static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer, .xfer_vector = xfer_vectors,
.xfer_vector = spi_xfer_two_vectors,
.max_xfer_size = member_size(ich10_spi_regs, fdata), .max_xfer_size = member_size(ich10_spi_regs, fdata),
}; };