Skip to content

Commit

Permalink
Add --spidev_atomic.
Browse files Browse the repository at this point in the history
Some systems have special-purpose spidev drivers that can only enforce
atomicity of the mailbox request if the entire SPI transaction sequence
(write followed by read) is given as a single atomic `ioctl`.  Add the
--spidev_atomic flag to enable this special-case behavior.
  • Loading branch information
cjevans-google committed May 13, 2024
1 parent cfa8c3a commit ceea0b0
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 36 deletions.
4 changes: 4 additions & 0 deletions examples/htool.c
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,10 @@ static const struct htool_param GLOBAL_FLAGS[] = {
{HTOOL_FLAG_VALUE, .name = "spidev_path", .default_value = "",
.desc = "The full SPIDEV path of the RoT; for example "
"'/dev/spidev0.0'."},
{HTOOL_FLAG_BOOL, .name = "spidev_atomic", .default_value = "false",
.desc = "If true, force spidev to send the request and receive the "
"corresponding response with a single atomic ioctl. This is "
"required on some systems for correctness."},
{HTOOL_FLAG_VALUE, .name = "mtddev_path", .default_value = "",
.desc = "The full MTD path of the RoT mailbox; for example "
"'/dev/mtd0'. If unspecified, will attempt to detect "
Expand Down
13 changes: 9 additions & 4 deletions examples/htool_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ struct libhoth_device* htool_libhoth_spi_device(void) {
int rv;
const char* spidev_path_str;
uint32_t mailbox_location;
bool atomic;
rv = htool_get_param_string(htool_global_flags(), "spidev_path",
&spidev_path_str) ||
htool_get_param_u32(htool_global_flags(), "mailbox_location",
&mailbox_location);
&mailbox_location) ||
htool_get_param_bool(htool_global_flags(), "spidev_atomic", &atomic);
if (rv) {
return NULL;
}
Expand All @@ -47,13 +49,16 @@ struct libhoth_device* htool_libhoth_spi_device(void) {
return NULL;
}

struct libhoth_spi_device_init_options opts = {.path = spidev_path_str,
.mailbox = mailbox_location};
struct libhoth_spi_device_init_options opts = {
.path = spidev_path_str,
.mailbox = mailbox_location,
.atomic = atomic,
};
rv = libhoth_spi_open(&opts, &result);
if (rv) {
// TODO: Convert error-code to a string
fprintf(stderr, "libhoth_spi_open error: %d\n", rv);
return NULL;
}
return result;
}
}
181 changes: 149 additions & 32 deletions libhoth_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct libhoth_spi_device {
int fd;
unsigned int mailbox_address;
unsigned int address_mode_4b;

void* buffered_request;
size_t buffered_request_size;
};

int libhoth_spi_send_request(struct libhoth_device* dev, const void* request,
Expand All @@ -40,8 +43,32 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response,
size_t max_response_size, size_t* actual_size,
int timeout_ms);

int libhoth_spi_buffer_request(struct libhoth_device* dev, const void* request,
size_t request_size);

int libhoth_spi_send_and_receive_response(struct libhoth_device* dev,
void* response,
size_t max_response_size,
size_t* actual_size, int timeout_ms);

int libhoth_spi_close(struct libhoth_device* dev);

static int spi_nor_address(uint8_t* buf, uint32_t address,
unsigned int address_mode_4b) {
if (address_mode_4b) {
buf[0] = (address >> 24) & 0xFF;
buf[1] = (address >> 16) & 0xFF;
buf[2] = (address >> 8) & 0xFF;
buf[3] = address & 0xFF;
return 4;
} else {
buf[0] = (address >> 16) & 0xFF;
buf[1] = (address >> 8) & 0xFF;
buf[2] = address & 0xFF;
return 3;
}
}

static int spi_nor_write(int fd, unsigned int address_mode_4b,
unsigned int address, const void* data,
size_t data_len) {
Expand All @@ -61,21 +88,11 @@ static int spi_nor_write(int fd, unsigned int address_mode_4b,

// Page Program OPCODE + Mailbox Address
rq_buf[0] = 0x02;
if (address_mode_4b) {
rq_buf[1] = (address >> 24) & 0xFF;
rq_buf[2] = (address >> 16) & 0xFF;
rq_buf[3] = (address >> 8) & 0xFF;
rq_buf[4] = address & 0xFF;

xfer[1].len = 5;
} else {
rq_buf[1] = (address >> 16) & 0xFF;
rq_buf[2] = (address >> 8) & 0xFF;
rq_buf[3] = address & 0xFF;

xfer[1].len = 4;
}
xfer[1].tx_buf = (unsigned long)rq_buf;
int address_len = spi_nor_address(&rq_buf[1], address, address_mode_4b);
xfer[1] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)rq_buf,
.len = 1 + address_len,
};

// Write Data at mailbox address
xfer[2] = (struct spi_ioc_transfer){
Expand All @@ -100,21 +117,11 @@ static int spi_nor_read(int fd, unsigned int address_mode_4b,

// Read OPCODE and mailbox address
rd_request[0] = 0x03; // Read
if (address_mode_4b) {
rd_request[1] = (address >> 24) & 0xFF;
rd_request[2] = (address >> 16) & 0xFF;
rd_request[3] = (address >> 8) & 0xFF;
rd_request[4] = address & 0xFF;

xfer[0].len = 5;
} else {
rd_request[1] = (address >> 16) & 0xFF;
rd_request[2] = (address >> 8) & 0xFF;
rd_request[3] = address & 0xFF;

xfer[0].len = 4;
}
xfer[0].tx_buf = (unsigned long)rd_request;
int address_len = spi_nor_address(&rd_request[1], address, address_mode_4b);
xfer[0] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)rd_request,
.len = 1 + address_len,
};

// Read in data
xfer[1] = (struct spi_ioc_transfer){
Expand Down Expand Up @@ -197,8 +204,13 @@ int libhoth_spi_open(const struct libhoth_spi_device_init_options* options,
spi_dev->mailbox_address = options->mailbox;
spi_dev->address_mode_4b = 1;

dev->send = libhoth_spi_send_request;
dev->receive = libhoth_spi_receive_response;
if (options->atomic) {
dev->send = libhoth_spi_buffer_request;
dev->receive = libhoth_spi_send_and_receive_response;
} else {
dev->send = libhoth_spi_send_request;
dev->receive = libhoth_spi_receive_response;
}
dev->close = libhoth_spi_close;
dev->claim = libhoth_spi_claim;
dev->release = libhoth_spi_release;
Expand Down Expand Up @@ -284,6 +296,111 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response,
return LIBHOTH_OK;
}

int libhoth_spi_buffer_request(struct libhoth_device* dev, const void* request,
size_t request_size) {
if (dev == NULL) {
return LIBHOTH_ERR_INVALID_PARAMETER;
}

struct libhoth_spi_device* spi_dev =
(struct libhoth_spi_device*)dev->user_ctx;

if (spi_dev->buffered_request != NULL) {
return LIBHOTH_ERR_INTERFACE_BUSY;
}

spi_dev->buffered_request = malloc(request_size);
spi_dev->buffered_request_size = request_size;
memcpy(spi_dev->buffered_request, request, request_size);

return LIBHOTH_OK;
}

int libhoth_spi_send_and_receive_response(struct libhoth_device* dev,
void* response,
size_t max_response_size,
size_t* actual_size, int timeout_ms) {
if (dev == NULL) {
return LIBHOTH_ERR_INVALID_PARAMETER;
}

if (max_response_size < 8) {
return LIBHOTH_ERR_INVALID_PARAMETER;
}

struct libhoth_spi_device* spi_dev =
(struct libhoth_spi_device*)dev->user_ctx;

if (spi_dev->buffered_request == NULL) {
return LIBHOTH_ERR_INTERFACE_BUSY;
}

uint32_t address = spi_dev->mailbox_address;
int address_mode_4b = spi_dev->address_mode_4b;

struct spi_ioc_transfer xfer[5] = {};

// Write Enable Message
uint8_t wp_buf[1];
wp_buf[0] = 0x06;
xfer[0] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)wp_buf,
.len = 1,
.cs_change = 1,
};

// Page Program OPCODE + Mailbox Address
uint8_t pp_buf[5];
pp_buf[0] = 0x02;
int address_len = spi_nor_address(&pp_buf[1], address, address_mode_4b);
xfer[1] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)pp_buf,
.len = 1 + address_len,
};

// Write Data at mailbox address
xfer[2] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)spi_dev->buffered_request,
.len = spi_dev->buffered_request_size,
.cs_change = 1,
};

// Wait for status register is handled by the spidev driver.

// Read opcode + Mailbox Address
uint8_t rd_buf[5];
rd_buf[0] = 0x03; // Read
address_len = spi_nor_address(&rd_buf[1], address, address_mode_4b);
xfer[3] = (struct spi_ioc_transfer){
.tx_buf = (unsigned long)rd_buf,
.len = 1 + address_len,
};

// Read entire expected response buffer
xfer[4] = (struct spi_ioc_transfer){
.rx_buf = (unsigned long)response,
.len = max_response_size,
};

int rc = LIBHOTH_OK;
int status = ioctl(spi_dev->fd, SPI_IOC_MESSAGE(5), xfer);
if (status < 0) {
rc = LIBHOTH_ERR_FAIL;
} else {
if (actual_size) {
struct ec_host_response* host_response =
(struct ec_host_response*)response;
*actual_size = (size_t)host_response->data_len + 8;
}
}

free(spi_dev->buffered_request);
spi_dev->buffered_request = NULL;
spi_dev->buffered_request_size = 0;

return rc;
}

int libhoth_spi_close(struct libhoth_device* dev) {
if (dev == NULL) {
return LIBHOTH_ERR_INVALID_PARAMETER;
Expand Down
1 change: 1 addition & 0 deletions libhoth_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct libhoth_spi_device_init_options {
int bits;
int mode;
int speed;
int atomic;
};

// Note that the options struct only needs to to live for the duration of
Expand Down

0 comments on commit ceea0b0

Please sign in to comment.