diff options
Diffstat (limited to 'drivers/spi/spi-amd.c')
-rw-r--r-- | drivers/spi/spi-amd.c | 183 |
1 files changed, 139 insertions, 44 deletions
diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 08df4f8d0531..e23121456c70 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -36,9 +36,17 @@ #define AMD_SPI_FIFO_SIZE 70 #define AMD_SPI_MEM_SIZE 200 -/* M_CMD OP codes for SPI */ -#define AMD_SPI_XFER_TX 1 -#define AMD_SPI_XFER_RX 2 +#define AMD_SPI_ENA_REG 0x20 +#define AMD_SPI_ALT_SPD_SHIFT 20 +#define AMD_SPI_ALT_SPD_MASK GENMASK(23, AMD_SPI_ALT_SPD_SHIFT) +#define AMD_SPI_SPI100_SHIFT 0 +#define AMD_SPI_SPI100_MASK GENMASK(AMD_SPI_SPI100_SHIFT, AMD_SPI_SPI100_SHIFT) +#define AMD_SPI_SPEED_REG 0x6C +#define AMD_SPI_SPD7_SHIFT 8 +#define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT) + +#define AMD_SPI_MAX_HZ 100000000 +#define AMD_SPI_MIN_HZ 800000 /** * enum amd_spi_versions - SPI controller versions @@ -50,14 +58,41 @@ enum amd_spi_versions { AMD_SPI_V2, }; +enum amd_spi_speed { + F_66_66MHz, + F_33_33MHz, + F_22_22MHz, + F_16_66MHz, + F_100MHz, + F_800KHz, + SPI_SPD7, + F_50MHz = 0x4, + F_4MHz = 0x32, + F_3_17MHz = 0x3F +}; + +/** + * struct amd_spi_freq - Matches device speed with values to write in regs + * @speed_hz: Device frequency + * @enable_val: Value to be written to "enable register" + * @spd7_val: Some frequencies requires to have a value written at SPISPEED register + */ +struct amd_spi_freq { + u32 speed_hz; + u32 enable_val; + u32 spd7_val; +}; + /** * struct amd_spi - SPI driver instance * @io_remap_addr: Start address of the SPI controller registers * @version: SPI controller hardware version + * @speed_hz: Device frequency */ struct amd_spi { void __iomem *io_remap_addr; enum amd_spi_versions version; + unsigned int speed_hz; }; static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) @@ -189,65 +224,125 @@ static int amd_spi_master_setup(struct spi_device *spi) return 0; } +static const struct amd_spi_freq amd_spi_freq[] = { + { AMD_SPI_MAX_HZ, F_100MHz, 0}, + { 66660000, F_66_66MHz, 0}, + { 50000000, SPI_SPD7, F_50MHz}, + { 33330000, F_33_33MHz, 0}, + { 22220000, F_22_22MHz, 0}, + { 16660000, F_16_66MHz, 0}, + { 4000000, SPI_SPD7, F_4MHz}, + { 3170000, SPI_SPD7, F_3_17MHz}, + { AMD_SPI_MIN_HZ, F_800KHz, 0}, +}; + +static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz) +{ + unsigned int i, spd7_val, alt_spd; + + if (speed_hz < AMD_SPI_MIN_HZ) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++) + if (speed_hz >= amd_spi_freq[i].speed_hz) + break; + + if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz) + return 0; + + amd_spi->speed_hz = amd_spi_freq[i].speed_hz; + + alt_spd = (amd_spi_freq[i].enable_val << AMD_SPI_ALT_SPD_SHIFT) + & AMD_SPI_ALT_SPD_MASK; + amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, alt_spd, + AMD_SPI_ALT_SPD_MASK); + + if (amd_spi->speed_hz == AMD_SPI_MAX_HZ) + amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, 1, + AMD_SPI_SPI100_MASK); + + if (amd_spi_freq[i].spd7_val) { + spd7_val = (amd_spi_freq[i].spd7_val << AMD_SPI_SPD7_SHIFT) + & AMD_SPI_SPD7_MASK; + amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val, + AMD_SPI_SPD7_MASK); + } + + return 0; +} + static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, struct spi_master *master, struct spi_message *message) { struct spi_transfer *xfer = NULL; - u8 cmd_opcode; + struct spi_device *spi = message->spi; + u8 cmd_opcode = 0, fifo_pos = AMD_SPI_FIFO_BASE; u8 *buf = NULL; - u32 m_cmd = 0; u32 i = 0; u32 tx_len = 0, rx_len = 0; list_for_each_entry(xfer, &message->transfers, transfer_list) { - if (xfer->rx_buf) - m_cmd = AMD_SPI_XFER_RX; - if (xfer->tx_buf) - m_cmd = AMD_SPI_XFER_TX; + if (xfer->speed_hz) + amd_set_spi_freq(amd_spi, xfer->speed_hz); + else + amd_set_spi_freq(amd_spi, spi->max_speed_hz); - if (m_cmd & AMD_SPI_XFER_TX) { + if (xfer->tx_buf) { buf = (u8 *)xfer->tx_buf; - tx_len = xfer->len - 1; - cmd_opcode = *(u8 *)xfer->tx_buf; - buf++; - amd_spi_set_opcode(amd_spi, cmd_opcode); + if (!tx_len) { + cmd_opcode = *(u8 *)xfer->tx_buf; + buf++; + xfer->len--; + } + tx_len += xfer->len; /* Write data into the FIFO. */ - for (i = 0; i < tx_len; i++) { - iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_FIFO_BASE + i)); - } + for (i = 0; i < xfer->len; i++) + amd_spi_writereg8(amd_spi, fifo_pos + i, buf[i]); - amd_spi_set_tx_count(amd_spi, tx_len); - amd_spi_clear_fifo_ptr(amd_spi); - /* Execute command */ - amd_spi_execute_opcode(amd_spi); - } - if (m_cmd & AMD_SPI_XFER_RX) { - /* - * Store no. of bytes to be received from - * FIFO - */ - rx_len = xfer->len; - buf = (u8 *)xfer->rx_buf; - amd_spi_set_rx_count(amd_spi, rx_len); - amd_spi_clear_fifo_ptr(amd_spi); - /* Execute command */ - amd_spi_execute_opcode(amd_spi); - amd_spi_busy_wait(amd_spi); - /* Read data from FIFO to receive buffer */ - for (i = 0; i < rx_len; i++) - buf[i] = amd_spi_readreg8(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i); + fifo_pos += xfer->len; } + + /* Store no. of bytes to be received from FIFO */ + if (xfer->rx_buf) + rx_len += xfer->len; + } + + if (!buf) { + message->status = -EINVAL; + goto fin_msg; + } + + amd_spi_set_opcode(amd_spi, cmd_opcode); + amd_spi_set_tx_count(amd_spi, tx_len); + amd_spi_set_rx_count(amd_spi, rx_len); + + /* Execute command */ + message->status = amd_spi_execute_opcode(amd_spi); + if (message->status) + goto fin_msg; + + if (rx_len) { + message->status = amd_spi_busy_wait(amd_spi); + if (message->status) + goto fin_msg; + + list_for_each_entry(xfer, &message->transfers, transfer_list) + if (xfer->rx_buf) { + buf = (u8 *)xfer->rx_buf; + /* Read data from FIFO to receive buffer */ + for (i = 0; i < xfer->len; i++) + buf[i] = amd_spi_readreg8(amd_spi, fifo_pos + i); + fifo_pos += xfer->len; + } } /* Update statistics */ message->actual_length = tx_len + rx_len + 1; - /* complete the transaction */ - message->status = 0; +fin_msg: switch (amd_spi->version) { case AMD_SPI_V1: break; @@ -260,7 +355,7 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, spi_finalize_current_message(master); - return 0; + return message->status; } static int amd_spi_master_transfer(struct spi_master *master, @@ -275,9 +370,7 @@ static int amd_spi_master_transfer(struct spi_master *master, * Extract spi_transfers from the spi message and * program the controller. */ - amd_spi_fifo_xfer(amd_spi, master, msg); - - return 0; + return amd_spi_fifo_xfer(amd_spi, master, msg); } static size_t amd_spi_max_transfer_size(struct spi_device *spi) @@ -312,6 +405,8 @@ static int amd_spi_probe(struct platform_device *pdev) master->num_chipselect = 4; master->mode_bits = 0; master->flags = SPI_MASTER_HALF_DUPLEX; + master->max_speed_hz = AMD_SPI_MAX_HZ; + master->min_speed_hz = AMD_SPI_MIN_HZ; master->setup = amd_spi_master_setup; master->transfer_one_message = amd_spi_master_transfer; master->max_transfer_size = amd_spi_max_transfer_size; |