1 /*
2 * Copyright (c) 2021-2022 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_common.h"
9 #include "hpm_femc_drv.h"
10
11 #ifndef HPM_FEMC_DRV_DEFAULT_PRESCALER
12 #define HPM_FEMC_DRV_DEFAULT_PRESCALER (0x3UL)
13 #endif
14
15 #ifndef HPM_FEMC_DRV_RETRY_COUNT
16 #define HPM_FEMC_DRV_RETRY_COUNT (5000U)
17 #endif
18
19 #define FEMC_PRESCALER_MAX (256UL)
20
femc_config_delay_cell(FEMC_Type * ptr,uint32_t delay_cell_value)21 static void femc_config_delay_cell(FEMC_Type *ptr, uint32_t delay_cell_value)
22 {
23 ptr->DLYCFG &= ~FEMC_DLYCFG_OE_MASK;
24 ptr->DLYCFG = FEMC_DLYCFG_DLYSEL_SET(delay_cell_value) | FEMC_DLYCFG_DLYEN_MASK;
25 ptr->DLYCFG |= FEMC_DLYCFG_OE_MASK;
26 }
27
femc_ip_cmd_done(FEMC_Type * ptr)28 static hpm_stat_t femc_ip_cmd_done(FEMC_Type *ptr)
29 {
30 uint32_t intr_status = 0;
31 uint32_t retry = 0;
32 do {
33 if (retry > HPM_FEMC_DRV_RETRY_COUNT) {
34 break;
35 }
36 retry++;
37 intr_status = ptr->INTR
38 & (uint32_t)(FEMC_INTR_IPCMDDONE_MASK | FEMC_INTR_IPCMDERR_MASK);
39 } while (intr_status == 0);
40
41 if (retry > HPM_FEMC_DRV_RETRY_COUNT) {
42 return status_timeout;
43 }
44
45 ptr->INTR |= FEMC_INTR_IPCMDDONE_MASK | FEMC_INTR_IPCMDERR_MASK;
46 if (intr_status & FEMC_INTR_IPCMDERR_MASK) {
47 return status_femc_cmd_err;
48 }
49 return status_success;
50 }
51
femc_make_cmd(uint32_t opcode)52 static uint32_t femc_make_cmd(uint32_t opcode)
53 {
54 return (opcode & ~FEMC_CMD_WRITE_FLAG) | FEMC_CMD_KEY;
55 }
56
femc_is_write_cmd(uint32_t opcode)57 static bool femc_is_write_cmd(uint32_t opcode)
58 {
59 return ((opcode & FEMC_CMD_WRITE_FLAG) == FEMC_CMD_WRITE_FLAG);
60 }
61
femc_issue_ip_cmd(FEMC_Type * ptr,uint32_t base_address,femc_cmd_t * cmd)62 uint32_t femc_issue_ip_cmd(FEMC_Type *ptr, uint32_t base_address, femc_cmd_t *cmd)
63 {
64 bool read_data = !femc_is_write_cmd(cmd->opcode);
65 ptr->SADDR = base_address;
66 if (!read_data) {
67 ptr->IPTX = cmd->data;
68 }
69 ptr->IPCMD = femc_make_cmd(cmd->opcode);
70
71 if (femc_ip_cmd_done(ptr) != status_success) {
72 return status_femc_cmd_err;
73 }
74
75 if (read_data) {
76 cmd->data = ptr->IPRX;
77 }
78 return status_success;
79 }
80
femc_default_config(FEMC_Type * ptr,femc_config_t * config)81 void femc_default_config(FEMC_Type *ptr, femc_config_t *config)
82 {
83 (void) ptr;
84 femc_axi_q_weight_t *q;
85 config->dqs = FEMC_DQS_FROM_PAD;
86 config->cmd_timeout = 0;
87 config->bus_timeout = 0x10;
88 q = &config->axi_q_weight[FEMC_AXI_Q_A];
89 q->enable = true;
90 q->qos = 4;
91 q->age = 2;
92 q->slave_hit = 0x5;
93 q->slave_hit_wo_rw = 0x3;
94
95 q = &config->axi_q_weight[FEMC_AXI_Q_B];
96 q->enable = true;
97 q->qos = 4;
98 q->age = 2;
99 q->page_hit = 0x5;
100 q->slave_hit_wo_rw = 0x3;
101 q->bank_rotation = 0x6;
102 }
103
femc_get_typical_sdram_config(FEMC_Type * ptr,femc_sdram_config_t * config)104 void femc_get_typical_sdram_config(FEMC_Type *ptr, femc_sdram_config_t *config)
105 {
106 (void) ptr;
107 config->col_addr_bits = FEMC_SDRAM_COLUMN_ADDR_9_BITS;
108 config->cas_latency = FEMC_SDRAM_CAS_LATENCY_3;
109 config->bank_num = FEMC_SDRAM_BANK_NUM_4;
110 config->prescaler = HPM_FEMC_DRV_DEFAULT_PRESCALER;
111 config->burst_len_in_byte = 8;
112 config->auto_refresh_count_in_one_burst = 1;
113 config->precharge_to_act_in_ns = 20;
114 config->act_to_rw_in_ns = 20;
115 config->refresh_recover_in_ns = 70;
116 config->write_recover_in_ns = 12;
117 config->cke_off_in_ns = 42;
118 config->act_to_precharge_in_ns = 42;
119
120 config->self_refresh_recover_in_ns = 70;
121 config->refresh_to_refresh_in_ns = 60;
122 config->act_to_act_in_ns = 12;
123 config->idle_timeout_in_ns = 6;
124 config->cs_mux_pin = FEMC_IO_MUX_NOT_USED;
125 }
126
femc_init(FEMC_Type * ptr,femc_config_t * config)127 void femc_init(FEMC_Type *ptr, femc_config_t *config)
128 {
129 uint32_t i;
130 femc_axi_q_weight_t *q;
131 for (i = 0; i < FEMC_BR_COUNT; i++) {
132 ptr->BR[i] = 0;
133 }
134
135 femc_sw_reset(ptr);
136 femc_disable(ptr);
137 ptr->CTRL |= FEMC_CTRL_BTO_SET(config->bus_timeout)
138 | FEMC_CTRL_CTO_SET(config->cmd_timeout)
139 | FEMC_CTRL_DQS_SET(config->dqs);
140
141 q = &config->axi_q_weight[FEMC_AXI_Q_A];
142 if (q->enable) {
143 ptr->BMW0 = FEMC_BMW0_QOS_SET(q->qos)
144 | FEMC_BMW0_AGE_SET(q->age)
145 | FEMC_BMW0_SH_SET(q->slave_hit)
146 | FEMC_BMW0_RWS_SET(q->slave_hit_wo_rw);
147 } else {
148 ptr->BMW0 = 0;
149 }
150
151 q = &config->axi_q_weight[FEMC_AXI_Q_B];
152 if (q->enable) {
153 ptr->BMW1 = FEMC_BMW1_QOS_SET(q->qos)
154 | FEMC_BMW1_AGE_SET(q->age)
155 | FEMC_BMW1_PH_SET(q->page_hit)
156 | FEMC_BMW1_BR_SET(q->bank_rotation)
157 | FEMC_BMW1_RWS_SET(q->slave_hit_wo_rw);
158 } else {
159 ptr->BMW1 = 0;
160 }
161
162 femc_enable(ptr);
163 }
164
femc_convert_actual_size_to_memory_size(uint32_t size_in_kb)165 static uint8_t femc_convert_actual_size_to_memory_size(uint32_t size_in_kb)
166 {
167 uint8_t size = 0;
168 if (size_in_kb == 4) {
169 return 0;
170 }
171
172 if (size_in_kb > 2 * 1 << 20) {
173 return 0x1F;
174 }
175
176 size = 1;
177 size_in_kb >>= 3;
178 while (size_in_kb > 1) {
179 size_in_kb >>= 1;
180 size++;
181 }
182 return size;
183 }
184
femc_convert_burst_len(uint8_t burst_len_in_byte)185 static uint8_t femc_convert_burst_len(uint8_t burst_len_in_byte)
186 {
187 if ((burst_len_in_byte == 0)
188 || (burst_len_in_byte > FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE)) {
189 return FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE + 1;
190 }
191
192 switch (burst_len_in_byte) {
193 case 1:
194 case 2:
195 case 4:
196 return burst_len_in_byte >> 1;
197 case 8:
198 return (burst_len_in_byte - 1) >> 1;
199 default:
200 return FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE + 1;
201 }
202 }
203
ns2cycle(uint32_t freq_in_hz,uint32_t ns,uint32_t max_cycle)204 static uint32_t ns2cycle(uint32_t freq_in_hz, uint32_t ns, uint32_t max_cycle)
205 {
206 uint32_t ns_per_cycle;
207 uint32_t cycle;
208
209 ns_per_cycle = 1000000000 / freq_in_hz;
210 cycle = ns / ns_per_cycle;
211 if (cycle > max_cycle) {
212 cycle = max_cycle;
213 }
214 return cycle;
215 }
216
femc_config_sdram(FEMC_Type * ptr,uint32_t clk_in_hz,femc_sdram_config_t * config)217 hpm_stat_t femc_config_sdram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sdram_config_t *config)
218 {
219 hpm_stat_t err;
220 uint32_t prescaler;
221 uint32_t refresh_cycle;
222 uint32_t clk_in_khz = clk_in_hz / 1000;
223 femc_cmd_t cmd = {0};
224 uint8_t size = femc_convert_actual_size_to_memory_size(config->size_in_byte >> 10);
225 uint8_t burst_len = femc_convert_burst_len(config->burst_len_in_byte);
226
227 prescaler = ((config->prescaler == 0) ? FEMC_PRESCALER_MAX : config->prescaler);
228 refresh_cycle = clk_in_khz * config->refresh_in_ms / config->refresh_count / (prescaler << 4);
229
230 if ((prescaler == 0) || (prescaler > FEMC_PRESCALER_MAX)
231 || (refresh_cycle == 0) || (refresh_cycle > FEMC_PRESCALER_MAX)) {
232 return status_invalid_argument;
233 }
234
235 if (prescaler == FEMC_PRESCALER_MAX) {
236 prescaler = 0;
237 }
238
239 if (refresh_cycle == FEMC_PRESCALER_MAX) {
240 refresh_cycle = 0;
241 }
242
243 ptr->BR[config->cs] = FEMC_BR_BASE_SET(config->base_address >> FEMC_BR_BASE_SHIFT)
244 | FEMC_BR_SIZE_SET(size) | FEMC_BR_VLD_MASK;
245
246 ptr->SDRCTRL0 = FEMC_SDRCTRL0_PORTSZ_SET(config->port_size)
247 | FEMC_SDRCTRL0_BURSTLEN_SET(burst_len)
248 | FEMC_SDRCTRL0_COL_SET(config->col_addr_bits)
249 | FEMC_SDRCTRL0_COL8_SET(config->col_addr_bits == FEMC_SDRAM_COLUMN_ADDR_8_BITS)
250 | FEMC_SDRCTRL0_CAS_SET(config->cas_latency)
251 | FEMC_SDRCTRL0_BANK2_SET(config->bank_num);
252
253 ptr->SDRCTRL1 = FEMC_SDRCTRL1_PRE2ACT_SET(ns2cycle(clk_in_hz, config->precharge_to_act_in_ns, FEMC_SDRCTRL1_PRE2ACT_MASK >> FEMC_SDRCTRL1_PRE2ACT_SHIFT))
254 | FEMC_SDRCTRL1_ACT2RW_SET(ns2cycle(clk_in_hz, config->act_to_rw_in_ns, FEMC_SDRCTRL1_ACT2RW_MASK >> FEMC_SDRCTRL1_ACT2RW_SHIFT))
255 | FEMC_SDRCTRL1_RFRC_SET(ns2cycle(clk_in_hz, config->refresh_recover_in_ns, FEMC_SDRCTRL1_RFRC_MASK >> FEMC_SDRCTRL1_RFRC_SHIFT))
256 | FEMC_SDRCTRL1_WRC_SET(ns2cycle(clk_in_hz, config->write_recover_in_ns, FEMC_SDRCTRL1_WRC_MASK >> FEMC_SDRCTRL1_WRC_SHIFT))
257 | FEMC_SDRCTRL1_CKEOFF_SET(ns2cycle(clk_in_hz, config->cke_off_in_ns, FEMC_SDRCTRL1_CKEOFF_MASK >> FEMC_SDRCTRL1_CKEOFF_SHIFT))
258 | FEMC_SDRCTRL1_ACT2PRE_SET(ns2cycle(clk_in_hz, config->act_to_precharge_in_ns, FEMC_SDRCTRL1_ACT2PRE_MASK >> FEMC_SDRCTRL1_ACT2PRE_SHIFT));
259
260 ptr->SDRCTRL2 = FEMC_SDRCTRL2_SRRC_SET(ns2cycle(clk_in_hz, config->self_refresh_recover_in_ns, FEMC_SDRCTRL2_SRRC_MASK >> FEMC_SDRCTRL2_SRRC_SHIFT))
261 | FEMC_SDRCTRL2_REF2REF_SET(ns2cycle(clk_in_hz, config->refresh_to_refresh_in_ns, FEMC_SDRCTRL2_REF2REF_MASK >> FEMC_SDRCTRL2_REF2REF_SHIFT))
262 | FEMC_SDRCTRL2_ACT2ACT_SET(ns2cycle(clk_in_hz, config->act_to_act_in_ns, FEMC_SDRCTRL2_ACT2ACT_MASK >> FEMC_SDRCTRL2_ACT2ACT_SHIFT))
263 | FEMC_SDRCTRL2_ITO_SET(ns2cycle(clk_in_hz, config->idle_timeout_in_ns, FEMC_SDRCTRL2_ITO_MASK >> FEMC_SDRCTRL2_ITO_SHIFT));
264
265 ptr->SDRCTRL3 = FEMC_SDRCTRL3_PRESCALE_SET(prescaler)
266 | FEMC_SDRCTRL3_RT_SET(refresh_cycle)
267 | FEMC_SDRCTRL3_UT_SET(refresh_cycle)
268 | FEMC_SDRCTRL3_REBL_SET(config->auto_refresh_count_in_one_burst - 1);
269 /*
270 *
271 * DATSZ[2:0]: Data size in byte
272 * 0b - 4
273 * 1b - 1
274 * 2b - 2
275 * 3b - 3
276 * > 3b - 4
277 */
278 ptr->DATSZ = FEMC_DATSZ_DATSZ_SET((config->data_width_in_byte & (0x3UL)));
279 ptr->BYTEMSK = 0;
280
281 cmd.opcode = FEMC_CMD_SDRAM_PRECHARGE_ALL;
282 cmd.data = 0;
283 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
284 if (status_success != err) {
285 return err;
286 }
287
288 cmd.opcode = FEMC_CMD_SDRAM_AUTO_REFRESH;
289 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
290 if (status_success != err) {
291 return err;
292 }
293 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
294 if (status_success != err) {
295 return err;
296 }
297
298 /*
299 *
300 * DATSZ[2:0]: Data size in byte
301 * 0b - 4
302 * 1b - 1
303 * 2b - 2
304 * 3b - 3
305 * > 3b - 4
306 */
307 ptr->DATSZ = FEMC_DATSZ_DATSZ_SET((config->data_width_in_byte & (0x3UL)));
308 ptr->BYTEMSK = 0;
309
310 /*
311 * config delay cell
312 */
313 femc_config_delay_cell(ptr, config->delay_cell_value);
314
315 cmd.opcode = FEMC_CMD_SDRAM_PRECHARGE_ALL;
316 cmd.data = 0;
317 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
318 if (status_success != err) {
319 return err;
320 }
321
322 cmd.opcode = FEMC_CMD_SDRAM_AUTO_REFRESH;
323 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
324 if (status_success != err) {
325 return err;
326 }
327 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
328 if (status_success != err) {
329 return err;
330 }
331
332 cmd.opcode = FEMC_CMD_SDRAM_MODE_SET;
333 /* FIXME: the mode register layout definition better to be passed in? */
334 cmd.data = (uint32_t)(burst_len | config->cas_latency << 4);
335 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
336 if (status_success != err) {
337 return err;
338 }
339 ptr->SDRCTRL3 |= FEMC_SDRCTRL3_REN_MASK;
340
341 return status_success;
342 }
343
femc_get_typical_sram_config(FEMC_Type * ptr,femc_sram_config_t * config)344 void femc_get_typical_sram_config(FEMC_Type *ptr, femc_sram_config_t *config)
345 {
346 (void) ptr;
347 config->base_address = 0x48000000;
348 config->size_in_byte = 4096;
349 config->address_mode = FEMC_SRAM_AD_NONMUX_MODE;
350 config->port_size = FEMC_SRAM_PORT_SIZE_8_BITS;
351 config->adv_hold_state = FEMC_SRAM_ADV_HOLD_LOW;
352 config->adv_polarity = FEMC_SRAM_ADV_ACTIVE_HIGH;
353 config->oeh_in_ns = 0;
354 config->oel_in_ns = 50;
355 config->weh_in_ns = 0;
356 config->wel_in_ns = 50;
357 config->ah_in_ns = 50;
358 config->as_in_ns = 0;
359 config->ceh_in_ns = 0;
360 config->ces_in_ns = 0;
361 }
362
femc_config_sram(FEMC_Type * ptr,uint32_t clk_in_hz,femc_sram_config_t * config)363 hpm_stat_t femc_config_sram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sram_config_t *config)
364 {
365 uint8_t size = femc_convert_actual_size_to_memory_size(config->size_in_byte >> 10);
366
367 ptr->IOCTRL = FEMC_IOCTRL_IO_CSX_SET(FEMC_IO_CSX_SRAM_CE);
368
369 ptr->BR[FEMC_BR_BASE6] = FEMC_BR_BASE_SET(config->base_address >> FEMC_BR_BASE_SHIFT)
370 | FEMC_BR_SIZE_SET(size)
371 | FEMC_BR_VLD_MASK;
372
373 ptr->SRCTRL0 = FEMC_SRCTRL0_ADVH_SET(config->adv_hold_state)
374 | FEMC_SRCTRL0_ADVP_SET(config->adv_polarity)
375 | FEMC_SRCTRL0_ADM_SET(config->address_mode)
376 | FEMC_SRCTRL0_PORTSZ_SET(config->port_size);
377
378 ptr->SRCTRL1 = FEMC_SRCTRL1_OEH_SET(ns2cycle(clk_in_hz, config->oeh_in_ns, FEMC_SRCTRL1_OEH_MASK >> FEMC_SRCTRL1_OEH_SHIFT))
379 | FEMC_SRCTRL1_OEL_SET(ns2cycle(clk_in_hz, config->oel_in_ns, FEMC_SRCTRL1_OEL_MASK >> FEMC_SRCTRL1_OEL_SHIFT))
380 | FEMC_SRCTRL1_WEH_SET(ns2cycle(clk_in_hz, config->weh_in_ns, FEMC_SRCTRL1_WEH_MASK >> FEMC_SRCTRL1_WEH_SHIFT))
381 | FEMC_SRCTRL1_WEL_SET(ns2cycle(clk_in_hz, config->wel_in_ns, FEMC_SRCTRL1_WEL_MASK >> FEMC_SRCTRL1_WEL_SHIFT))
382 | FEMC_SRCTRL1_AH_SET(ns2cycle(clk_in_hz, config->ah_in_ns, FEMC_SRCTRL1_AH_MASK >> FEMC_SRCTRL1_AH_SHIFT))
383 | FEMC_SRCTRL1_AS_SET(ns2cycle(clk_in_hz, config->as_in_ns, FEMC_SRCTRL1_AS_MASK >> FEMC_SRCTRL1_AS_SHIFT))
384 | FEMC_SRCTRL1_CEH_SET(ns2cycle(clk_in_hz, config->ceh_in_ns, FEMC_SRCTRL1_CEH_MASK >> FEMC_SRCTRL1_CEH_SHIFT))
385 | FEMC_SRCTRL1_CES_SET(ns2cycle(clk_in_hz, config->ces_in_ns, FEMC_SRCTRL1_CES_MASK >> FEMC_SRCTRL1_CES_SHIFT));
386
387 return status_success;
388 }
389