summaryrefslogtreecommitdiffstats
path: root/src/soc/sifive/fu540/spi_internal.h
blob: 6baee36b4f7cb7a09aa8018a9c1f3caaf96b2857 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/* SPDX-License-Identifier: GPL-2.0-only */
/* This file is part of the coreboot project. */
#ifndef __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
#define __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__

#include <stdint.h>

#define _ASSERT_SIZEOF(type, size) _Static_assert( \
		sizeof(type) == (size), \
		#type " must be " #size " bytes wide")

#define FU540_SPI_CSMODE_AUTO	0
#define FU540_SPI_CSMODE_HOLD	2
#define FU540_SPI_CSMODE_OFF	3

typedef union {
	struct {
		uint32_t pha : 1;
		uint32_t pol : 1;
		uint32_t reserved : 30;
	};
	uint32_t raw_bits;
} spi_reg_sckmode;
_ASSERT_SIZEOF(spi_reg_sckmode, 4);


typedef union {
	struct {
		uint32_t mode : 2;
		uint32_t reserved : 30;
	};
	uint32_t raw_bits;
} spi_reg_csmode;
_ASSERT_SIZEOF(spi_reg_csmode, 4);


typedef union {
	struct {
		uint32_t cssck : 8;
		uint32_t reserved0 : 8;
		uint32_t sckcs : 8;
		uint32_t reserved1 : 8;
	};
	uint32_t raw_bits;
} spi_reg_delay0;
_ASSERT_SIZEOF(spi_reg_delay0, 4);


typedef union {
	struct {
		uint32_t intercs : 8;
		uint32_t reserved0 : 8;
		uint32_t interxfr : 8;
		uint32_t reserved1 : 8;
	};
	uint32_t raw_bits;
} spi_reg_delay1;
_ASSERT_SIZEOF(spi_reg_delay1, 4);


typedef union {
	struct {
		uint32_t proto : 2;
		uint32_t endian : 1;
		uint32_t dir : 1;
		uint32_t reserved0 : 12;
		uint32_t len : 4;
		uint32_t reserved1 : 12;
	};
	uint32_t raw_bits;
} spi_reg_fmt;
_ASSERT_SIZEOF(spi_reg_fmt, 4);


typedef union {
	struct {
		uint32_t data : 8;
		uint32_t reserved : 23;
		uint32_t full : 1;
	};
	uint32_t raw_bits;
} spi_reg_txdata;
_ASSERT_SIZEOF(spi_reg_txdata, 4);


typedef union {
	struct {
		uint32_t data : 8;
		uint32_t reserved : 23;
		uint32_t empty : 1;
	};
	uint32_t raw_bits;
} spi_reg_rxdata;
_ASSERT_SIZEOF(spi_reg_rxdata, 4);


typedef union {
	struct {
		uint32_t txmark : 3;
		uint32_t reserved : 29;
	};
	uint32_t raw_bits;
} spi_reg_txmark;
_ASSERT_SIZEOF(spi_reg_txmark, 4);


typedef union {
	struct {
		uint32_t rxmark : 3;
		uint32_t reserved : 29;
	};
	uint32_t raw_bits;
} spi_reg_rxmark;
_ASSERT_SIZEOF(spi_reg_rxmark, 4);


typedef union {
	struct {
		uint32_t en : 1;
		uint32_t reserved : 31;
	};
	uint32_t raw_bits;
} spi_reg_fctrl;
_ASSERT_SIZEOF(spi_reg_fctrl, 4);


typedef union {
	struct {
		uint32_t cmd_en : 1;
		uint32_t addr_len : 3;
		uint32_t pad_cnt : 4;
		uint32_t command_proto : 2;
		uint32_t addr_proto : 2;
		uint32_t data_proto : 2;
		uint32_t reserved : 2;
		uint32_t command_code : 8;
		uint32_t pad_code : 8;
	};
	uint32_t raw_bits;
} spi_reg_ffmt;
_ASSERT_SIZEOF(spi_reg_ffmt, 4);


typedef union {
	struct {
		uint32_t txwm : 1;
		uint32_t rxwm : 1;
		uint32_t reserved : 30;
	};
	uint32_t raw_bits;
} spi_reg_ie;
typedef spi_reg_ie spi_reg_ip;
_ASSERT_SIZEOF(spi_reg_ie, 4);
_ASSERT_SIZEOF(spi_reg_ip, 4);

#undef _ASSERT_SIZEOF


/**
 * SPI control register memory map.
 *
 * All functions take a pointer to a SPI device's control registers.
 */
struct spi_ctrl {
	uint32_t sckdiv;
	spi_reg_sckmode sckmode;
	uint32_t reserved08;
	uint32_t reserved0c;

	uint32_t csid;
	uint32_t csdef;
	spi_reg_csmode csmode;
	uint32_t reserved1c;

	uint32_t reserved20;
	uint32_t reserved24;
	spi_reg_delay0 delay0;
	spi_reg_delay1 delay1;

	uint32_t reserved30;
	uint32_t reserved34;
	uint32_t reserved38;
	uint32_t reserved3c;

	spi_reg_fmt fmt;
	uint32_t reserved44;
	spi_reg_txdata txdata;
	spi_reg_rxdata rxdata;

	spi_reg_txmark txmark;
	spi_reg_rxmark rxmark;
	uint32_t reserved58;
	uint32_t reserved5c;

	spi_reg_fctrl fctrl;
	spi_reg_ffmt ffmt;
	uint32_t reserved68;
	uint32_t reserved6c;

	spi_reg_ie ie;
	spi_reg_ip ip;
};

/**
 * Get smallest clock divisor that divides input_khz to a quotient less than or
 * equal to max_target_khz;
 */
static inline unsigned int
spi_min_clk_divisor(unsigned int input_khz, unsigned int max_target_khz)
{
	// f_sck = f_in / (2 * (div + 1)) => div = (f_in / (2*f_sck)) - 1
	//
	// The nearest integer solution for div requires rounding up as to not
	// exceed max_target_khz.
	//
	// div = ceil(f_in / (2*f_sck)) - 1
	//     = floor((f_in - 1 + 2*f_sck) / (2*f_sck)) - 1
	//
	// This should not overflow as long as (f_in - 1 + 2*f_sck) does not
	// exceed 2^32 - 1, which is unlikely since we represent frequencies
	// in kHz.
	unsigned int quotient =
		(input_khz + 2 * max_target_khz - 1) / (2 * max_target_khz);
	// Avoid underflow
	if (quotient == 0)
		return 0;
	return quotient - 1;
}

#endif /* __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__ */