#define pr_fmt(fmt) "[hui][stm32] " fmt #include #include #include #include #include "spi.h" enum { STM32_SOF = 0x5A, STM32_ACK = 0x79, STM32_NACK = 0x1F, }; static int spi_write_locked(struct spi_device *spi, const u8 *buf, size_t len) { int err; struct spi_message *m; struct spi_transfer *t; m = spi_message_alloc(len, GFP_KERNEL); if (!m) return -ENOMEM; list_for_each_entry(t, &m->transfers, transfer_list) { t->tx_buf = buf++; t->len = 1; t->delay_usecs = 16; } err = spi_sync_locked(spi, m); spi_message_free(m); return err; } static inline int spi_read_locked(struct spi_device *spi, u8 *buf, size_t len) { int err; struct spi_message *m; struct spi_transfer *t; m = spi_message_alloc(len, GFP_KERNEL); if (!m) return -ENOMEM; list_for_each_entry(t, &m->transfers, transfer_list) { t->rx_buf = buf++; t->len = 1; t->delay_usecs = 16; } err = spi_sync_locked(spi, m); spi_message_free(m); return err; } static inline int stm32_begin(struct stm32 *stm32) { spi_bus_lock(stm32->dev->master); gpiod_set_value(stm32->cs_gpio, 1); usleep_range(100, 200); return 0; } static inline int stm32_end(struct stm32 *stm32) { gpiod_set_value(stm32->cs_gpio, 0); spi_bus_unlock(stm32->dev->master); return 0; } static int stm32_wait_for_ack(struct spi_device *dev) { int err, i; u8 c; c = 0; err = spi_write_locked(dev, &c, 1); if (err) return err; for (i = 0; i < 10; i++) { err = spi_read_locked(dev, &c, 1); if (c == STM32_ACK) { // Send back ack return spi_write_locked(dev, &c, 1); return 0; } else if (c == STM32_NACK) { return -EPROTO; } } return -ETIMEDOUT; } static int stm32_send_hdr(struct spi_device *dev, u8 cmd) { int err; u8 buf[] = { STM32_SOF, cmd, ~cmd, }; err = spi_write_locked(dev, buf, sizeof(buf)); if (err) return err; return stm32_wait_for_ack(dev); } int stm32_write(struct stm32 *stm32, int cmd, const void *_buf, unsigned len) { const char *buf = _buf; struct spi_device *dev = stm32->dev; int err, i; u8 checksum = 0; for (i = 0; i < len; i++) checksum ^= buf[i]; stm32_begin(stm32); err = stm32_send_hdr(dev, cmd); if (err) goto out_err; err = spi_write_locked(dev, buf, len); if (err) goto out_err; err = spi_write_locked(dev, &checksum, 1); if (err) goto out_err; err = stm32_wait_for_ack(dev); if (err) goto out_err; out_err: stm32_end(stm32); return err; } int stm32_read(struct stm32 *stm32, int cmd, void *_buf, unsigned len) { char *buf = _buf; struct spi_device *dev = stm32->dev; int err, i; u8 checksum_expected, checksum_actual = 0; // Reading of size zero makes no sense, if you want to trigger // effects use stm32_write if (len == 0) return -EINVAL; stm32_begin(stm32); err = stm32_send_hdr(dev, cmd); if (err) goto out_err_locked; err = spi_read_locked(dev, buf, len); if (err) goto out_err_locked; err = spi_read_locked(dev, &checksum_expected, 1); if (err) goto out_err_locked; stm32_end(stm32); for (i = 0; i < len; i++) checksum_actual ^= buf[i]; if (checksum_expected != checksum_actual) { pr_err("Checksum does not match 0x%02X != 0x%02X\n", checksum_expected, checksum_actual); print_hex_dump(KERN_ERR, "[hui][stm32] rx: ", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); return -EIO; } return 0; out_err_locked: stm32_end(stm32); return err; }