Skip to content

Commit 2192ee2

Browse files
committed
arm: cec: Implement CEC QMSPI host controller support
This is the Quad Master-only SPI host driver. Signed-off-by: Timo Teräs <[email protected]>
1 parent 96a953e commit 2192ee2

File tree

10 files changed

+393
-0
lines changed

10 files changed

+393
-0
lines changed

boards/arm/secureiot1702/doc/secureiot1702.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The following devices are supported:
3232
- Nested Vectored Interrupt Controller (NVIC)
3333
- System Tick System Clock (SYSTICK)
3434
- Serial Ports (NS16550)
35+
- Quad Master-only SPI controller (QMSPI)
3536

3637

3738
Connections and IOs

boards/arm/secureiot1702/pinmux.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ static int board_init(struct device *dev)
2929
UART1_INST->CONFIG = 0;
3030
UART1_INST->ACTIVATE = 1;
3131
#endif
32+
#ifdef CONFIG_SPI_0
33+
/* Set clock request, muxing and drive strength */
34+
PCR_INST->CLK_REQ_4_b.QSPI_CLK_REQ = 1;
35+
GPIO_040_076_INST->GPIO_055_PIN_CONTROL_b.MUX_CONTROL = 2;
36+
GPIO_040_076_INST->GPIO_056_PIN_CONTROL_b.MUX_CONTROL = 2;
37+
GPIO_200_236_INST->GPIO_223_PIN_CONTROL_b.MUX_CONTROL = 2;
38+
GPIO_200_236_INST->GPIO_224_PIN_CONTROL_b.MUX_CONTROL = 2;
39+
GPIO_200_236_INST->GPIO_227_PIN_CONTROL_b.MUX_CONTROL = 2;
40+
GPIO_000_036_INST->GPIO_016_PIN_CONTROL_b.MUX_CONTROL = 2;
41+
GPIO_PIN_CONTROL_2_INST->GPIO_055_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
42+
GPIO_PIN_CONTROL_2_INST->GPIO_056_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
43+
GPIO_PIN_CONTROL_2_INST->GPIO_223_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
44+
GPIO_PIN_CONTROL_2_INST->GPIO_224_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
45+
GPIO_PIN_CONTROL_2_INST->GPIO_227_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
46+
GPIO_PIN_CONTROL_2_INST->GPIO_016_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2;
47+
#endif
48+
3249
return 0;
3350
}
3451

drivers/spi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
zephyr_library()
44

55
zephyr_library_sources_ifdef(CONFIG_SPI_CC13XX_CC26XX spi_cc13xx_cc26xx.c)
6+
zephyr_library_sources_ifdef(CONFIG_SPI_CEC_QMSPI spi_cec_qmspi.c)
67
zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c)
78
zephyr_library_sources_ifdef(CONFIG_SPI_INTEL spi_intel.c)
89
zephyr_library_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c)

drivers/spi/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ config SPI_6_OP_MODES
180180

181181
endif # SPI_6
182182

183+
source "drivers/spi/Kconfig.cec_qmspi"
184+
183185
config SPI_INTEL
184186
bool "Intel SPI controller driver"
185187
depends on CPU_MINUTEIA

drivers/spi/Kconfig.cec_qmspi

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# Copyright (c) 2019 Crypta Labs Ltd.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
config SPI_CEC_QMSPI
8+
bool
9+
prompt "CEC MCU QMSPI controller driver"
10+
depends on SPI && SOC_SERIES_CEC
11+
default n
12+
help
13+
Enable Quad Master SPI support on the CEC series of processors.
14+
15+
if SPI_CEC_QMSPI
16+
17+
config SPI_CEC_QMSPI_INTERRUPT
18+
bool "CEC QMSPI Interrupt Support"
19+
help
20+
Enable Interrupt support for the SPI Driver of CEC QMSPI.
21+
NOTE: Work in progress. Not ready yet.
22+
23+
endif # SPI_CEC_QMSPI

drivers/spi/spi_cec_qmspi.c

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
/*
2+
* Copyright (c) 2017 Crypta Labs Ltd
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define LOG_DOMAIN "QMSPI CEC"
8+
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
9+
#include <logging/log.h>
10+
LOG_MODULE_REGISTER(spi_cec);
11+
12+
#include <errno.h>
13+
#include <spi.h>
14+
#include <soc.h>
15+
16+
#include "spi_context.h"
17+
18+
typedef void (*irq_config_func_t)(struct device *port);
19+
20+
struct spi_qmspi_data {
21+
struct spi_context ctx;
22+
};
23+
24+
struct spi_qmspi_config {
25+
QMSPI_INST_Type *spi;
26+
};
27+
28+
static inline struct spi_qmspi_data *get_dev_data(struct device *dev)
29+
{
30+
return dev->driver_data;
31+
}
32+
33+
static inline const struct spi_qmspi_config *get_dev_config(struct device *dev)
34+
{
35+
return dev->config->config_info;
36+
}
37+
38+
static int spi_qmspi_configure(struct device *dev,
39+
const struct spi_config *spi_cfg)
40+
{
41+
const struct spi_qmspi_config *cfg = get_dev_config(dev);
42+
struct spi_qmspi_data *data = get_dev_data(dev);
43+
QMSPI_INST_Type *spi = cfg->spi;
44+
45+
if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE ||
46+
(spi_cfg->operation & SPI_TRANSFER_LSB) ||
47+
(spi_cfg->frequency == 0)) {
48+
return -ENOTSUP;
49+
}
50+
51+
if (SPI_WORD_SIZE_GET(spi_cfg->operation) != 8) {
52+
return -ENOTSUP;
53+
}
54+
55+
spi->QMSPI_MODE_b.CLOCK_DIVIDE =
56+
SYSCLK_DEFAULT_IOSC_HZ / spi_cfg->frequency;
57+
spi->QMSPI_MODE_b.ACTIVATE = 1;
58+
spi->QMSPI_MODE_b.CPOL =
59+
SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPOL;
60+
spi->QMSPI_MODE_b.CHPA_MISO =
61+
SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPHA;
62+
spi->QMSPI_MODE_b.CHPA_MOSI =
63+
SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPHA;
64+
65+
spi->QMSPI_CONTROL_b.CLOSE_TRANSFER_ENABLE =
66+
!(spi_cfg->operation & SPI_HOLD_ON_CS);
67+
spi->QMSPI_CONTROL_b.TRANSFER_UNITS = 1; /* Unit of byte */
68+
switch (spi_cfg->operation & SPI_LINES_MASK) {
69+
case SPI_LINES_SINGLE:
70+
spi->QMSPI_CONTROL_b.INTERFACE_MODE = 0;
71+
break;
72+
#if 0
73+
#error These modes require transfer direction which is not yet supported.
74+
case SPI_LINES_DUAL:
75+
spi->QMSPI_CONTROL_b.INTERFACE_MODE = 1;
76+
break;
77+
case SPI_LINES_QUAD:
78+
spi->QMSPI_CONTROL_b.INTERFACE_MODE = 2;
79+
break;
80+
#endif
81+
default:
82+
return -ENOTSUP;
83+
}
84+
85+
/* At this point, it's mandatory to set this on the context! */
86+
data->ctx.config = spi_cfg;
87+
88+
spi_context_cs_configure(&data->ctx);
89+
90+
LOG_DBG("Installed config %p: freq %uHz,"
91+
" mode %u/%u/%u, slave %u, if_mode=%d",
92+
spi_cfg, spi_cfg->frequency,
93+
(SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) ? 1 : 0,
94+
(SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) ? 1 : 0,
95+
(SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) ? 1 : 0,
96+
spi_cfg->slave,
97+
spi->QMSPI_CONTROL_b.INTERFACE_MODE);
98+
99+
return 0;
100+
}
101+
102+
static void spi_qmspi_complete(struct spi_qmspi_data *data,
103+
QMSPI_INST_Type *spi, int status)
104+
{
105+
#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT
106+
spi->QMSPI_INTERRUPT_ENABLE_b.TRANSFER_COMPLETE_ENABLE = 0;
107+
#endif
108+
109+
spi_context_cs_control(&data->ctx, false);
110+
111+
spi->QMSPI_EXECUTE_b.STOP = 1;
112+
spi->QMSPI_EXECUTE_b.CLEAR_DATA_BUFFER = 1;
113+
114+
if (status) {
115+
spi->QMSPI_MODE_b.SOFT_RESET = 1;
116+
while (spi->QMSPI_MODE_b.SOFT_RESET)
117+
;
118+
}
119+
120+
#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT
121+
spi_context_complete(&data->ctx, status);
122+
#endif
123+
}
124+
125+
static int spi_qmspi_shift(QMSPI_INST_Type *spi, struct spi_qmspi_data *data)
126+
{
127+
u8_t byte;
128+
129+
if (spi_context_tx_on(&data->ctx)) {
130+
byte = 0;
131+
if (data->ctx.tx_buf) {
132+
byte = UNALIGNED_GET((u8_t *)(data->ctx.tx_buf));
133+
}
134+
spi_context_update_tx(&data->ctx, 1, 1);
135+
while (!spi->QMSPI_STATUS_b.TRANSMIT_BUFFER_EMPTY)
136+
;
137+
sys_write8(byte, (mem_addr_t)&spi->QMSPI_TRAMSMIT_BUFFER);
138+
}
139+
140+
if (spi_context_rx_on(&data->ctx)) {
141+
while (spi->QMSPI_STATUS_b.RECEIVE_BUFFER_EMPTY)
142+
;
143+
byte = sys_read8((mem_addr_t)&spi->QMSPI_RECEIVE_BUFFER);
144+
if (data->ctx.rx_buf) {
145+
UNALIGNED_PUT(byte, (u8_t *)data->ctx.rx_buf);
146+
}
147+
spi_context_update_rx(&data->ctx, 1, 1);
148+
}
149+
150+
/* Check for error bits 0b11100 */
151+
return spi->QMSPI_STATUS & 0x1c;
152+
}
153+
154+
static int transceive(struct device *dev,
155+
const struct spi_config *spi_cfg,
156+
const struct spi_buf_set *tx_bufs,
157+
const struct spi_buf_set *rx_bufs,
158+
struct k_poll_signal *async)
159+
{
160+
const struct spi_qmspi_config *cfg = get_dev_config(dev);
161+
struct spi_qmspi_data *data = get_dev_data(dev);
162+
QMSPI_INST_Type *spi = cfg->spi;
163+
int ret;
164+
165+
#ifndef CONFIG_SPI_CEC_QMSPI_INTERRUPT
166+
if (async != NULL) {
167+
return -ENOTSUP;
168+
}
169+
#endif
170+
171+
spi_context_lock(&data->ctx, async != NULL, async);
172+
173+
ret = spi_qmspi_configure(dev, spi_cfg);
174+
if (ret) {
175+
return ret;
176+
}
177+
178+
spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
179+
180+
spi->QMSPI_CONTROL_b.TRANSFER_LENGTH =
181+
spi_context_transfer_length(&data->ctx);
182+
183+
spi->QMSPI_CONTROL_b.RX_TRANSFER_ENABLE = rx_bufs ? 1 : 0;
184+
spi->QMSPI_CONTROL_b.TX_TRANSFER_ENABLE = tx_bufs ? 1 : 0;
185+
186+
spi_context_cs_control(&data->ctx, true);
187+
spi->QMSPI_EXECUTE_b.START = 1;
188+
189+
#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT
190+
#error Not supported yet.
191+
spi->QMSPI_INTERRUPT_ENABLE_b.TRANSFER_COMPLETE_ENABLE = 1;
192+
ret = spi_context_wait_for_completion(&data->ctx);
193+
#else
194+
do {
195+
ret = spi_qmspi_shift(spi, data);
196+
} while (!ret &&
197+
(spi_context_tx_on(&data->ctx) ||
198+
spi_context_rx_on(&data->ctx)));
199+
200+
while (!spi->QMSPI_STATUS_b.TRANSMIT_BUFFER_EMPTY)
201+
;
202+
203+
spi_qmspi_complete(data, spi, ret);
204+
#endif
205+
206+
spi_context_release(&data->ctx, ret);
207+
208+
if (ret) {
209+
LOG_ERR("error mask 0x%x", ret);
210+
}
211+
212+
return ret ? -EIO : 0;
213+
}
214+
215+
static int spi_qmspi_transceive(struct device *dev,
216+
const struct spi_config *spi_cfg,
217+
const struct spi_buf_set *tx_bufs,
218+
const struct spi_buf_set *rx_bufs)
219+
{
220+
return transceive(dev, spi_cfg, tx_bufs, rx_bufs, NULL);
221+
}
222+
223+
#ifdef CONFIG_POLL
224+
static int spi_qmspi_transceive_async(struct device *dev,
225+
const struct spi_config *spi_cfg,
226+
const struct spi_buf_set *tx_bufs,
227+
const struct spi_buf_set *rx_bufs,
228+
struct k_poll_signal *async)
229+
{
230+
return transceive(dev, spi_cfg, tx_bufs, rx_bufs, async);
231+
}
232+
#endif /* CONFIG_POLL */
233+
234+
static int spi_qmspi_release(struct device *dev,
235+
const struct spi_config *config)
236+
{
237+
const struct spi_qmspi_config *cfg = get_dev_config(dev);
238+
struct spi_qmspi_data *data = get_dev_data(dev);
239+
QMSPI_INST_Type *spi = cfg->spi;
240+
241+
spi_context_unlock_unconditionally(&data->ctx);
242+
spi->QMSPI_MODE_b.ACTIVATE = 0;
243+
244+
return 0;
245+
}
246+
247+
static const struct spi_driver_api api_funcs = {
248+
.transceive = spi_qmspi_transceive,
249+
#ifdef CONFIG_POLL
250+
.transceive_async = spi_qmspi_transceive_async,
251+
#endif
252+
.release = spi_qmspi_release,
253+
};
254+
255+
static int spi_qmspi_init(struct device *dev)
256+
{
257+
const struct spi_qmspi_config *cfg = dev->config->config_info;
258+
struct spi_qmspi_data *data = dev->driver_data;
259+
QMSPI_INST_Type *spi = cfg->spi;
260+
261+
spi_context_unlock_unconditionally(&data->ctx);
262+
263+
/* Reset block */
264+
spi->QMSPI_MODE_b.ACTIVATE = 1;
265+
spi->QMSPI_MODE_b.SOFT_RESET = 1;
266+
while (spi->QMSPI_MODE_b.SOFT_RESET)
267+
;
268+
spi->QMSPI_MODE_b.ACTIVATE = 0;
269+
270+
return 0;
271+
}
272+
273+
#ifdef DT_MICROCHIP_CEC_QMSPI_0_LABEL
274+
275+
static const struct spi_qmspi_config spi_qmspi_cfg_0 = {
276+
.spi = (QMSPI_INST_Type *) DT_MICROCHIP_CEC_QMSPI_0_BASE_ADDRESS,
277+
};
278+
279+
static struct spi_qmspi_data spi_qmspi_dev_data_0 = {
280+
SPI_CONTEXT_INIT_LOCK(spi_qmspi_dev_data_0, ctx),
281+
SPI_CONTEXT_INIT_SYNC(spi_qmspi_dev_data_0, ctx),
282+
};
283+
284+
DEVICE_AND_API_INIT(spi_qmspi_0, DT_MICROCHIP_CEC_QMSPI_0_LABEL,
285+
&spi_qmspi_init, &spi_qmspi_dev_data_0,
286+
&spi_qmspi_cfg_0,
287+
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
288+
&api_funcs);
289+
290+
#endif

drivers/spi/spi_context.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,23 @@ static inline size_t spi_context_longest_current_buf(struct spi_context *ctx)
363363
return ctx->rx_len;
364364
}
365365

366+
static inline size_t spi_context_transfer_length(struct spi_context *ctx)
367+
{
368+
size_t i, rx = 0, tx = 0;
369+
370+
for (i = 0; i < ctx->rx_count; i++) {
371+
rx += ctx->current_rx[i].len;
372+
}
373+
for (i = 0; i < ctx->tx_count; i++) {
374+
tx += ctx->current_tx[i].len;
375+
}
376+
if (tx > rx) {
377+
return tx;
378+
}
379+
return rx;
380+
}
381+
382+
366383
#ifdef __cplusplus
367384
}
368385
#endif

0 commit comments

Comments
 (0)