1 /*
2 * SPI bitbang implementation using generic gpio
3 *
4 * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24 #include <libwebsockets.h>
25
26 int
lws_bb_spi_init(const lws_spi_ops_t * octx)27 lws_bb_spi_init(const lws_spi_ops_t *octx)
28 {
29 lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx;
30 int n;
31
32 for (n = 0; n < LWS_SPI_BB_MAX_CH; n++) {
33 if (ctx->flags & (1 << n))
34 ctx->gpio->mode(ctx->ncs[n], LWSGGPIO_FL_WRITE);
35 if (ctx->flags & (1 << (n + 4)))
36 ctx->gpio->mode(ctx->ncmd[n], LWSGGPIO_FL_WRITE);
37 }
38
39 ctx->gpio->mode(ctx->clk, LWSGGPIO_FL_WRITE |
40 ((octx->bus_mode & LWSSPIMODE_CPOL) ?
41 0 : LWSGGPIO_FL_START_LOW));
42 ctx->gpio->mode(ctx->mosi, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_START_LOW);
43 ctx->gpio->mode(ctx->miso, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP);
44
45 return 0;
46 }
47
48 /* if active, prepare DnC before this and call separately for Cmd / Data */
49
50 static void
lws_bb_spi_write(lws_bb_spi_t * ctx,const uint8_t * buf,size_t len)51 lws_bb_spi_write(lws_bb_spi_t *ctx, const uint8_t *buf, size_t len)
52 {
53 uint8_t u, inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL);
54
55 while (len--) {
56 int n;
57
58 u = *buf++;
59
60 for (n = 0; n < 4; n++) {
61 ctx->gpio->set(ctx->clk, inv);
62 ctx->gpio->set(ctx->mosi, !!(u & 0x80));
63 ctx->gpio->set(ctx->clk, !inv);
64 ctx->gpio->set(ctx->clk, inv);
65 ctx->gpio->set(ctx->mosi, !!(u & 0x40));
66 ctx->gpio->set(ctx->clk, !inv);
67 u <<= 2;
68 }
69 }
70
71 ctx->gpio->set(ctx->clk, 0 ^ inv);
72 }
73
74 static void
lws_bb_spi_read(lws_bb_spi_t * ctx,uint8_t * buf,size_t len)75 lws_bb_spi_read(lws_bb_spi_t *ctx, uint8_t *buf, size_t len)
76 {
77 uint8_t u = 0;
78 uint8_t inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL);
79
80 while (len--) {
81 int n;
82
83 for (n = 0; n < 8; n++) {
84 ctx->gpio->set(ctx->clk, inv);
85 u = (u << 1) | !!ctx->gpio->read(ctx->miso);
86 ctx->gpio->set(ctx->mosi, !!(u & 0x80));
87 ctx->gpio->set(ctx->clk, !inv);
88 }
89 *buf++ = u;
90 }
91
92 ctx->gpio->set(ctx->clk, 0 ^ inv);
93 }
94
95 int
lws_bb_spi_queue(const lws_spi_ops_t * octx,const lws_spi_desc_t * desc)96 lws_bb_spi_queue(const lws_spi_ops_t *octx, const lws_spi_desc_t *desc)
97 {
98 lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx;
99 const uint8_t *src = desc->src;
100
101 /* clock to idle */
102 ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL));
103 /* enable nCS */
104 ctx->gpio->set(ctx->ncs[desc->channel], 0);
105
106 if (desc->count_cmd) {
107 ctx->gpio->set(ctx->ncmd[desc->channel], 0);
108 lws_bb_spi_write(ctx, src, desc->count_cmd);
109 ctx->gpio->set(ctx->ncmd[desc->channel], 1);
110
111 src += desc->count_cmd;
112 }
113
114 if (desc->count_write)
115 lws_bb_spi_write(ctx, desc->data, desc->count_write);
116
117 if (desc->count_read)
118 lws_bb_spi_read(ctx, desc->dest, desc->count_read);
119
120 /* disable nCS */
121 ctx->gpio->set(ctx->ncs[desc->channel], 1);
122
123 /* clock to idle */
124 ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL));
125
126 return 0;
127 }
128