Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2019 Western Digital Corporation or its affiliates |
| 3 | * |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
Kumar Gala | 73c1f8d | 2020-03-25 11:38:11 -0500 | [diff] [blame] | 7 | #define DT_DRV_COMPAT opencores_spi_simple |
| 8 | |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 9 | #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL |
| 10 | #include <logging/log.h> |
| 11 | LOG_MODULE_REGISTER(spi_oc_simple); |
| 12 | |
Kumar Gala | cd0f8d5 | 2019-12-09 11:31:24 -0600 | [diff] [blame] | 13 | #include <sys/sys_io.h> |
Peter Bigot | 5ceb612 | 2020-01-25 05:35:02 -0600 | [diff] [blame] | 14 | #include <drivers/spi.h> |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 15 | |
| 16 | #include "spi_context.h" |
| 17 | #include "spi_oc_simple.h" |
| 18 | |
| 19 | /* Bit 5:4 == ESPR, Bit 1:0 == SPR */ |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 20 | uint8_t DIVIDERS[] = { 0x00, /* 2 */ |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 21 | 0x01, /* 4 */ |
| 22 | 0x10, /* 8 */ |
| 23 | 0x02, /* 16 */ |
| 24 | 0x03, /* 32 */ |
| 25 | 0x11, /* 64 */ |
| 26 | 0x12, /* 128 */ |
| 27 | 0x13, /* 256 */ |
| 28 | 0x20, /* 512 */ |
| 29 | 0x21, /* 1024 */ |
| 30 | 0x22, /* 2048 */ |
| 31 | 0x23 }; /* 4096 */ |
| 32 | |
| 33 | static int spi_oc_simple_configure(const struct spi_oc_simple_cfg *info, |
| 34 | struct spi_oc_simple_data *spi, |
| 35 | const struct spi_config *config) |
| 36 | { |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 37 | uint8_t spcr = 0U; |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 38 | int i; |
| 39 | |
| 40 | if (spi_context_configured(&spi->ctx, config)) { |
| 41 | /* Nothing to do */ |
| 42 | return 0; |
| 43 | } |
| 44 | |
| 45 | /* Simple SPI only supports master mode */ |
| 46 | if (spi_context_is_slave(&spi->ctx)) { |
| 47 | LOG_ERR("Slave mode not supported"); |
| 48 | return -ENOTSUP; |
| 49 | } |
| 50 | |
| 51 | if (config->operation & (SPI_MODE_LOOP | SPI_TRANSFER_LSB | |
| 52 | SPI_LINES_DUAL | SPI_LINES_QUAD)) { |
| 53 | LOG_ERR("Unsupported configuration"); |
| 54 | return -EINVAL; |
| 55 | } |
| 56 | |
| 57 | /* SPI mode */ |
| 58 | if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) { |
| 59 | spcr |= SPI_OC_SIMPLE_SPCR_CPOL; |
| 60 | } |
| 61 | |
| 62 | if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) { |
| 63 | spcr |= SPI_OC_SIMPLE_SPCR_CPHA; |
| 64 | } |
| 65 | |
| 66 | /* Set clock divider */ |
| 67 | for (i = 0; i < 12; i++) |
| 68 | if ((config->frequency << (i + 1)) > |
| 69 | CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) { |
| 70 | break; |
| 71 | } |
| 72 | |
| 73 | sys_write8((DIVIDERS[i] >> 4) & 0x3, SPI_OC_SIMPLE_SPER(info)); |
| 74 | spcr |= (DIVIDERS[i] & 0x3); |
| 75 | |
| 76 | /* Configure and Enable SPI controller */ |
| 77 | sys_write8(spcr | SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info)); |
| 78 | |
| 79 | spi->ctx.config = config; |
| 80 | |
| 81 | if (config->cs) { |
| 82 | spi_context_cs_configure(&spi->ctx); |
| 83 | } |
| 84 | |
| 85 | return 0; |
| 86 | } |
| 87 | |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 88 | int spi_oc_simple_transceive(const struct device *dev, |
| 89 | const struct spi_config *config, |
| 90 | const struct spi_buf_set *tx_bufs, |
| 91 | const struct spi_buf_set *rx_bufs) |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 92 | { |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 93 | const struct spi_oc_simple_cfg *info = dev->config; |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 94 | struct spi_oc_simple_data *spi = SPI_OC_SIMPLE_DATA(dev); |
| 95 | struct spi_context *ctx = &spi->ctx; |
| 96 | |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 97 | uint8_t rx_byte; |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 98 | size_t i; |
| 99 | size_t cur_xfer_len; |
| 100 | int rc; |
| 101 | |
| 102 | /* Lock the SPI Context */ |
Stefan Bigler | 596cad8 | 2020-10-19 08:52:29 +0200 | [diff] [blame] | 103 | spi_context_lock(ctx, false, NULL, config); |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 104 | |
| 105 | spi_oc_simple_configure(info, spi, config); |
| 106 | |
| 107 | /* Set chip select */ |
| 108 | if (config->cs) { |
| 109 | spi_context_cs_control(&spi->ctx, true); |
| 110 | } else { |
| 111 | sys_write8(1 << config->slave, SPI_OC_SIMPLE_SPSS(info)); |
| 112 | } |
| 113 | |
| 114 | spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1); |
| 115 | |
| 116 | while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) { |
| 117 | cur_xfer_len = spi_context_longest_current_buf(ctx); |
| 118 | |
| 119 | for (i = 0; i < cur_xfer_len; i++) { |
| 120 | |
| 121 | /* Write byte */ |
| 122 | if (spi_context_tx_buf_on(ctx)) { |
| 123 | sys_write8(*ctx->tx_buf, |
| 124 | SPI_OC_SIMPLE_SPDR(info)); |
| 125 | spi_context_update_tx(ctx, 1, 1); |
| 126 | } else { |
| 127 | sys_write8(0, SPI_OC_SIMPLE_SPDR(info)); |
| 128 | } |
| 129 | |
| 130 | /* Wait for rx FIFO empty flag to clear */ |
| 131 | while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) { |
| 132 | } |
| 133 | |
| 134 | /* Get received byte */ |
| 135 | rx_byte = sys_read8(SPI_OC_SIMPLE_SPDR(info)); |
| 136 | |
| 137 | /* Store received byte if rx buffer is on */ |
| 138 | if (spi_context_rx_on(ctx)) { |
| 139 | *ctx->rx_buf = rx_byte; |
| 140 | spi_context_update_rx(ctx, 1, 1); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /* Clear chip-select */ |
| 146 | if (config->cs) { |
| 147 | spi_context_cs_control(&spi->ctx, false); |
| 148 | } else { |
| 149 | sys_write8(0 << config->slave, SPI_OC_SIMPLE_SPSS(info)); |
| 150 | } |
| 151 | |
| 152 | spi_context_complete(ctx, 0); |
| 153 | rc = spi_context_wait_for_completion(ctx); |
| 154 | |
| 155 | spi_context_release(ctx, rc); |
| 156 | |
| 157 | return rc; |
| 158 | } |
| 159 | |
| 160 | #ifdef CONFIG_SPI_ASYNC |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 161 | static int spi_oc_simple_transceive_async(const struct device *dev, |
| 162 | const struct spi_config *config, |
| 163 | const struct spi_buf_set *tx_bufs, |
| 164 | const struct spi_buf_set *rx_bufs, |
| 165 | struct k_poll_signal *async) |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 166 | { |
| 167 | return -ENOTSUP; |
| 168 | } |
| 169 | #endif /* CONFIG_SPI_ASYNC */ |
| 170 | |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 171 | int spi_oc_simple_release(const struct device *dev, |
| 172 | const struct spi_config *config) |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 173 | { |
| 174 | spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx); |
| 175 | return 0; |
| 176 | } |
| 177 | |
| 178 | static struct spi_driver_api spi_oc_simple_api = { |
| 179 | .transceive = spi_oc_simple_transceive, |
| 180 | .release = spi_oc_simple_release, |
| 181 | #ifdef CONFIG_SPI_ASYNC |
| 182 | .transceive_async = spi_oc_simple_transceive_async, |
| 183 | #endif /* CONFIG_SPI_ASYNC */ |
| 184 | }; |
| 185 | |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 186 | int spi_oc_simple_init(const struct device *dev) |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 187 | { |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 188 | const struct spi_oc_simple_cfg *info = dev->config; |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 189 | |
| 190 | /* Clear chip selects */ |
| 191 | sys_write8(0, SPI_OC_SIMPLE_SPSS(info)); |
| 192 | |
| 193 | /* Make sure the context is unlocked */ |
| 194 | spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx); |
| 195 | |
| 196 | /* Initial clock stucks high, so add this workaround */ |
| 197 | sys_write8(SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info)); |
| 198 | sys_write8(0, SPI_OC_SIMPLE_SPDR(info)); |
| 199 | while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) { |
| 200 | } |
| 201 | |
| 202 | sys_read8(SPI_OC_SIMPLE_SPDR(info)); |
| 203 | |
| 204 | return 0; |
| 205 | } |
| 206 | |
Martí Bolívar | 87e1743 | 2020-05-05 16:06:32 -0700 | [diff] [blame] | 207 | #define SPI_OC_INIT(inst) \ |
| 208 | static struct spi_oc_simple_cfg spi_oc_simple_cfg_##inst = { \ |
| 209 | .base = DT_INST_REG_ADDR_BY_NAME(inst, control), \ |
| 210 | }; \ |
| 211 | \ |
| 212 | static struct spi_oc_simple_data spi_oc_simple_data_##inst = { \ |
| 213 | SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_##inst, ctx), \ |
| 214 | SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_##inst, ctx), \ |
| 215 | }; \ |
| 216 | \ |
Kumar Gala | 03ad31b | 2020-12-09 12:32:41 -0600 | [diff] [blame] | 217 | DEVICE_DT_INST_DEFINE(inst, \ |
Martí Bolívar | 87e1743 | 2020-05-05 16:06:32 -0700 | [diff] [blame] | 218 | spi_oc_simple_init, \ |
Kumar Gala | 03ad31b | 2020-12-09 12:32:41 -0600 | [diff] [blame] | 219 | device_pm_control_nop, \ |
Martí Bolívar | 87e1743 | 2020-05-05 16:06:32 -0700 | [diff] [blame] | 220 | &spi_oc_simple_data_##inst, \ |
| 221 | &spi_oc_simple_cfg_##inst, \ |
| 222 | POST_KERNEL, \ |
| 223 | CONFIG_SPI_INIT_PRIORITY, \ |
Kumar Gala | 0a7d4e2 | 2020-05-07 14:09:05 -0500 | [diff] [blame] | 224 | &spi_oc_simple_api); |
Olof Kindgren | d09614a | 2019-09-11 22:16:28 +0200 | [diff] [blame] | 225 | |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 226 | DT_INST_FOREACH_STATUS_OKAY(SPI_OC_INIT) |