1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2017-2018 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include "drivers/bus/qspi.h"
28 
29 #define CS_LOW(self) mp_hal_pin_write(self->cs, 0)
30 #define CS_HIGH(self) mp_hal_pin_write(self->cs, 1)
31 
32 #ifdef MICROPY_HW_SOFTQSPI_SCK_LOW
33 
34 // Use externally provided functions for SCK control and IO reading
35 #define SCK_LOW(self) MICROPY_HW_SOFTQSPI_SCK_LOW(self)
36 #define SCK_HIGH(self) MICROPY_HW_SOFTQSPI_SCK_HIGH(self)
37 #define NIBBLE_READ(self) MICROPY_HW_SOFTQSPI_NIBBLE_READ(self)
38 
39 #else
40 
41 // Use generic pin functions for SCK control and IO reading
42 #define SCK_LOW(self) mp_hal_pin_write(self->clk, 0)
43 #define SCK_HIGH(self) mp_hal_pin_write(self->clk, 1)
44 #define NIBBLE_READ(self) ( \
45     mp_hal_pin_read(self->io0) \
46     | (mp_hal_pin_read(self->io1) << 1) \
47     | (mp_hal_pin_read(self->io2) << 2) \
48     | (mp_hal_pin_read(self->io3) << 3))
49 
50 #endif
51 
nibble_write(mp_soft_qspi_obj_t * self,uint8_t v)52 STATIC void nibble_write(mp_soft_qspi_obj_t *self, uint8_t v) {
53     mp_hal_pin_write(self->io0, v & 1);
54     mp_hal_pin_write(self->io1, (v >> 1) & 1);
55     mp_hal_pin_write(self->io2, (v >> 2) & 1);
56     mp_hal_pin_write(self->io3, (v >> 3) & 1);
57 }
58 
mp_soft_qspi_ioctl(void * self_in,uint32_t cmd)59 STATIC int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd) {
60     mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
61 
62     switch (cmd) {
63         case MP_QSPI_IOCTL_INIT:
64             mp_hal_pin_high(self->cs);
65             mp_hal_pin_output(self->cs);
66 
67             // Configure pins
68             mp_hal_pin_write(self->clk, 0);
69             mp_hal_pin_output(self->clk);
70             //mp_hal_pin_write(self->clk, 1);
71             mp_hal_pin_output(self->io0);
72             mp_hal_pin_input(self->io1);
73             mp_hal_pin_write(self->io2, 1);
74             mp_hal_pin_output(self->io2);
75             mp_hal_pin_write(self->io3, 1);
76             mp_hal_pin_output(self->io3);
77             break;
78     }
79 
80     return 0; // success
81 }
82 
mp_soft_qspi_transfer(mp_soft_qspi_obj_t * self,size_t len,const uint8_t * src,uint8_t * dest)83 STATIC void mp_soft_qspi_transfer(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *src, uint8_t *dest) {
84     // Will run as fast as possible, limited only by CPU speed and GPIO time
85     mp_hal_pin_input(self->io1);
86     mp_hal_pin_output(self->io0);
87     if (self->io3) {
88         mp_hal_pin_write(self->io2, 1);
89         mp_hal_pin_output(self->io2);
90         mp_hal_pin_write(self->io3, 1);
91         mp_hal_pin_output(self->io3);
92     }
93     if (src) {
94         for (size_t i = 0; i < len; ++i) {
95             uint8_t data_out = src[i];
96             uint8_t data_in = 0;
97             for (int j = 0; j < 8; ++j, data_out <<= 1) {
98                 mp_hal_pin_write(self->io0, (data_out >> 7) & 1);
99                 mp_hal_pin_write(self->clk, 1);
100                 data_in = (data_in << 1) | mp_hal_pin_read(self->io1);
101                 mp_hal_pin_write(self->clk, 0);
102             }
103             if (dest != NULL) {
104                 dest[i] = data_in;
105             }
106         }
107     } else {
108         for (size_t i = 0; i < len; ++i) {
109             uint8_t data_in = 0;
110             for (int j = 0; j < 8; ++j) {
111                 mp_hal_pin_write(self->clk, 1);
112                 data_in = (data_in << 1) | mp_hal_pin_read(self->io1);
113                 mp_hal_pin_write(self->clk, 0);
114             }
115             if (dest != NULL) {
116                 dest[i] = data_in;
117             }
118         }
119     }
120 }
121 
mp_soft_qspi_qread(mp_soft_qspi_obj_t * self,size_t len,uint8_t * buf)122 STATIC void mp_soft_qspi_qread(mp_soft_qspi_obj_t *self, size_t len, uint8_t *buf) {
123     // Make all IO lines input
124     mp_hal_pin_input(self->io2);
125     mp_hal_pin_input(self->io3);
126     mp_hal_pin_input(self->io0);
127     mp_hal_pin_input(self->io1);
128 
129     // Will run as fast as possible, limited only by CPU speed and GPIO time
130     while (len--) {
131         SCK_HIGH(self);
132         uint8_t data_in = NIBBLE_READ(self);
133         SCK_LOW(self);
134         SCK_HIGH(self);
135         *buf++ = (data_in << 4) | NIBBLE_READ(self);
136         SCK_LOW(self);
137     }
138 }
139 
mp_soft_qspi_qwrite(mp_soft_qspi_obj_t * self,size_t len,const uint8_t * buf)140 STATIC void mp_soft_qspi_qwrite(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *buf) {
141     // Make all IO lines output
142     mp_hal_pin_output(self->io2);
143     mp_hal_pin_output(self->io3);
144     mp_hal_pin_output(self->io0);
145     mp_hal_pin_output(self->io1);
146 
147     // Will run as fast as possible, limited only by CPU speed and GPIO time
148     for (size_t i = 0; i < len; ++i) {
149         nibble_write(self, buf[i] >> 4);
150         SCK_HIGH(self);
151         SCK_LOW(self);
152 
153         nibble_write(self, buf[i]);
154         SCK_HIGH(self);
155         SCK_LOW(self);
156     }
157 
158     //mp_hal_pin_input(self->io1);
159 }
160 
mp_soft_qspi_write_cmd_data(void * self_in,uint8_t cmd,size_t len,uint32_t data)161 STATIC void mp_soft_qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
162     mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
163     uint32_t cmd_buf = cmd | data << 8;
164     CS_LOW(self);
165     mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, NULL);
166     CS_HIGH(self);
167 }
168 
mp_soft_qspi_write_cmd_addr_data(void * self_in,uint8_t cmd,uint32_t addr,size_t len,const uint8_t * src)169 STATIC void mp_soft_qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
170     mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
171     uint8_t cmd_buf[5] = {cmd};
172     uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
173     CS_LOW(self);
174     mp_soft_qspi_transfer(self, addr_len + 1, cmd_buf, NULL);
175     mp_soft_qspi_transfer(self, len, src, NULL);
176     CS_HIGH(self);
177 }
178 
mp_soft_qspi_read_cmd(void * self_in,uint8_t cmd,size_t len)179 STATIC uint32_t mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) {
180     mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
181     uint32_t cmd_buf = cmd;
182     CS_LOW(self);
183     mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, (uint8_t*)&cmd_buf);
184     CS_HIGH(self);
185     return cmd_buf >> 8;
186 }
187 
mp_soft_qspi_read_cmd_qaddr_qdata(void * self_in,uint8_t cmd,uint32_t addr,size_t len,uint8_t * dest)188 STATIC void mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
189     mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
190     uint8_t cmd_buf[7] = {cmd};
191     uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
192     CS_LOW(self);
193     mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
194     mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles)
195     mp_soft_qspi_qread(self, len, dest);
196     CS_HIGH(self);
197 }
198 
199 const mp_qspi_proto_t mp_soft_qspi_proto = {
200     .ioctl = mp_soft_qspi_ioctl,
201     .write_cmd_data = mp_soft_qspi_write_cmd_data,
202     .write_cmd_addr_data = mp_soft_qspi_write_cmd_addr_data,
203     .read_cmd = mp_soft_qspi_read_cmd,
204     .read_cmd_qaddr_qdata = mp_soft_qspi_read_cmd_qaddr_qdata,
205 };
206