Skip to content

Commit efdb477

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 d3963c6 commit efdb477

File tree

7 files changed

+337
-0
lines changed

7 files changed

+337
-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 @@ 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
@@ -1,5 +1,6 @@
11
zephyr_library()
22

3+
zephyr_library_sources_ifdef(CONFIG_SPI_CEC_QMSPI spi_cec_qmspi.c)
34
zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c)
45
zephyr_library_sources_ifdef(CONFIG_SPI_INTEL spi_intel.c)
56
zephyr_library_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c)

drivers/spi/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ config SPI_5_OP_MODES
160160

161161
endif # SPI_5
162162

163+
config SPI_CEC_QMSPI
164+
bool
165+
prompt "CEC MCU QMSPI controller driver"
166+
depends on SPI && SOC_SERIES_CEC
167+
default n
168+
help
169+
Enable Quad Master SPI support on the CEC series of processors.
170+
163171
config SPI_INTEL
164172
bool "Intel SPI controller driver"
165173
depends on CPU_MINUTEIA

drivers/spi/spi_cec_qmspi.c

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

dts/arm/microchip/cec1702.dtsi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
label = "UART_1";
4646
reg-shift = <0>;
4747
};
48+
spi0: spi@40005400 {
49+
compatible = "microchip,cec-qmspi";
50+
reg = <0x40005400 0x44>;
51+
interrupts = <91 0>;
52+
status = "disabled";
53+
};
4854
};
4955
};
5056

0 commit comments

Comments
 (0)