1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #ifndef __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
3 #define __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
4
5 #include <stdint.h>
6
7 #define _ASSERT_SIZEOF(type, size) _Static_assert( \
8 sizeof(type) == (size), \
9 #type " must be " #size " bytes wide")
10
11 // Chip Select Mode Register (csmode)
12
13 #define FU740_SPI_CSMODE_AUTO 0
14 #define FU740_SPI_CSMODE_HOLD 2
15 #define FU740_SPI_CSMODE_OFF 3
16
17 union fu740_spi_reg_sckmode {
18 struct {
19 uint32_t pha : 1;
20 uint32_t pol : 1;
21 uint32_t reserved : 30;
22 };
23 uint32_t raw_bits;
24 };
25 _ASSERT_SIZEOF(union fu740_spi_reg_sckmode, 4);
26
27 union fu740_spi_reg_csmode {
28 struct {
29 uint32_t mode : 2;
30 uint32_t reserved : 30;
31 };
32 uint32_t raw_bits;
33 };
34 _ASSERT_SIZEOF(union fu740_spi_reg_csmode, 4);
35
36 union fu740_spi_reg_delay0 {
37 struct {
38 uint32_t cssck : 8;
39 uint32_t reserved0 : 8;
40 uint32_t sckcs : 8;
41 uint32_t reserved1 : 8;
42 };
43 uint32_t raw_bits;
44 };
45 _ASSERT_SIZEOF(union fu740_spi_reg_delay0, 4);
46
47 union fu740_spi_reg_delay1 {
48 struct {
49 uint32_t intercs : 8;
50 uint32_t reserved0 : 8;
51 uint32_t interxfr : 8;
52 uint32_t reserved1 : 8;
53 };
54 uint32_t raw_bits;
55 };
56 _ASSERT_SIZEOF(union fu740_spi_reg_delay1, 4);
57
58 union fu740_spi_reg_fmt {
59 struct {
60 uint32_t proto : 2;
61 uint32_t endian : 1;
62 uint32_t dir : 1;
63 uint32_t reserved0 : 12;
64 uint32_t len : 4;
65 uint32_t reserved1 : 12;
66 };
67 uint32_t raw_bits;
68 };
69 _ASSERT_SIZEOF(union fu740_spi_reg_fmt, 4);
70
71 union fu740_spi_reg_txdata {
72 struct {
73 uint32_t data : 8;
74 uint32_t reserved : 23;
75 uint32_t full : 1;
76 };
77 uint32_t raw_bits;
78 };
79 _ASSERT_SIZEOF(union fu740_spi_reg_txdata, 4);
80
81 union fu740_spi_reg_rxdata {
82 struct {
83 uint32_t data : 8;
84 uint32_t reserved : 23;
85 uint32_t empty : 1;
86 };
87 uint32_t raw_bits;
88 };
89 _ASSERT_SIZEOF(union fu740_spi_reg_rxdata, 4);
90
91 union fu740_spi_reg_txmark {
92 struct {
93 uint32_t txmark : 3;
94 uint32_t reserved : 29;
95 };
96 uint32_t raw_bits;
97 };
98 _ASSERT_SIZEOF(union fu740_spi_reg_txmark, 4);
99
100 union fu740_spi_reg_rxmark {
101 struct {
102 uint32_t rxmark : 3;
103 uint32_t reserved : 29;
104 };
105 uint32_t raw_bits;
106 };
107 _ASSERT_SIZEOF(union fu740_spi_reg_rxmark, 4);
108
109 union fu740_spi_reg_fctrl {
110 struct {
111 uint32_t en : 1;
112 uint32_t reserved : 31;
113 };
114 uint32_t raw_bits;
115 };
116 _ASSERT_SIZEOF(union fu740_spi_reg_fctrl, 4);
117
118 union fu740_spi_reg_ffmt {
119 struct {
120 uint32_t cmd_en : 1;
121 uint32_t addr_len : 3;
122 uint32_t pad_cnt : 4;
123 uint32_t cmd_proto : 2;
124 uint32_t addr_proto : 2;
125 uint32_t data_proto : 2;
126 uint32_t reserved : 2;
127 uint32_t cmd_code : 8;
128 uint32_t pad_code : 8;
129 };
130 uint32_t raw_bits;
131 };
132 _ASSERT_SIZEOF(union fu740_spi_reg_ffmt, 4);
133
134 union fu740_spi_reg_ie {
135 struct {
136 uint32_t txwm : 1;
137 uint32_t rxwm : 1;
138 uint32_t reserved : 30;
139 };
140 uint32_t raw_bits;
141 };
142 _ASSERT_SIZEOF(union fu740_spi_reg_ie, 4);
143
144 union fu740_spi_reg_ip {
145 struct {
146 uint32_t txwm : 1;
147 uint32_t rxwm : 1;
148 uint32_t reserved : 30;
149 };
150 uint32_t raw_bits;
151 };
152 _ASSERT_SIZEOF(union fu740_spi_reg_ip, 4);
153
154 #undef _ASSERT_SIZEOF
155
156 /**
157 * SPI control register memory map.
158 *
159 * All functions take a pointer to a SPI device's control registers.
160 */
161 struct fu740_spi_ctrl {
162 uint32_t sckdiv;
163 union fu740_spi_reg_sckmode sckmode;
164 uint32_t reserved08;
165 uint32_t reserved0c;
166
167 uint32_t csid;
168 uint32_t csdef;
169 union fu740_spi_reg_csmode csmode;
170 uint32_t reserved1c;
171
172 uint32_t reserved20;
173 uint32_t reserved24;
174 union fu740_spi_reg_delay0 delay0;
175 union fu740_spi_reg_delay1 delay1;
176
177 uint32_t reserved30;
178 uint32_t reserved34;
179 uint32_t reserved38;
180 uint32_t reserved3c;
181
182 union fu740_spi_reg_fmt fmt;
183 uint32_t reserved44;
184 union fu740_spi_reg_txdata txdata;
185 union fu740_spi_reg_rxdata rxdata;
186
187 union fu740_spi_reg_txmark txmark;
188 union fu740_spi_reg_rxmark rxmark;
189 uint32_t reserved58;
190 uint32_t reserved5c;
191
192 union fu740_spi_reg_fctrl fctrl;
193 union fu740_spi_reg_ffmt ffmt;
194 uint32_t reserved68;
195 uint32_t reserved6c;
196
197 union fu740_spi_reg_ie ie;
198 union fu740_spi_reg_ip ip;
199 };
200
201 /**
202 * Get smallest clock divisor that divides input_khz to a quotient less than or
203 * equal to max_target_khz;
204 */
205 static inline unsigned int
fu740_spi_min_clk_divisor(unsigned int input_khz,unsigned int max_target_khz)206 fu740_spi_min_clk_divisor(unsigned int input_khz, unsigned int max_target_khz)
207 {
208 // f_sck = f_in / (2 * (div + 1)) => div = (f_in / (2*f_sck)) - 1
209 //
210 // The nearest integer solution for div requires rounding up as to not
211 // exceed max_target_khz.
212 //
213 // div = ceil(f_in / (2*f_sck)) - 1
214 // = floor((f_in - 1 + 2*f_sck) / (2*f_sck)) - 1
215 //
216 // This should not overflow as long as (f_in - 1 + 2*f_sck) does not
217 // exceed 2^32 - 1, which is unlikely since we represent frequencies
218 // in kHz.
219 unsigned int quotient =
220 (input_khz + 2 * max_target_khz - 1) / (2 * max_target_khz);
221 // Avoid underflow
222 if (quotient == 0)
223 return 0;
224 return quotient - 1;
225 }
226
227 #endif /* __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__ */
228