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,bool delay_cell_en,uint32_t delay_cell_value)21 static void femc_config_delay_cell(FEMC_Type *ptr, bool delay_cell_en, 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_SET(delay_cell_en);
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 = 18;
114 config->act_to_rw_in_ns = 18;
115 config->refresh_recover_in_ns = 60;
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 = 72;
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 config->cmd_data_width = 4;
127 }
128
femc_init(FEMC_Type * ptr,femc_config_t * config)129 void femc_init(FEMC_Type *ptr, femc_config_t *config)
130 {
131 uint32_t i;
132 femc_axi_q_weight_t *q;
133 for (i = 0; i < FEMC_BR_COUNT; i++) {
134 ptr->BR[i] = 0;
135 }
136
137 femc_sw_reset(ptr);
138 femc_disable(ptr);
139 ptr->CTRL |= FEMC_CTRL_BTO_SET(config->bus_timeout)
140 | FEMC_CTRL_CTO_SET(config->cmd_timeout)
141 | FEMC_CTRL_DQS_SET(config->dqs);
142
143 q = &config->axi_q_weight[FEMC_AXI_Q_A];
144 if (q->enable) {
145 ptr->BMW0 = FEMC_BMW0_QOS_SET(q->qos)
146 | FEMC_BMW0_AGE_SET(q->age)
147 | FEMC_BMW0_SH_SET(q->slave_hit)
148 | FEMC_BMW0_RWS_SET(q->slave_hit_wo_rw);
149 } else {
150 ptr->BMW0 = 0;
151 }
152
153 q = &config->axi_q_weight[FEMC_AXI_Q_B];
154 if (q->enable) {
155 ptr->BMW1 = FEMC_BMW1_QOS_SET(q->qos)
156 | FEMC_BMW1_AGE_SET(q->age)
157 | FEMC_BMW1_PH_SET(q->page_hit)
158 | FEMC_BMW1_BR_SET(q->bank_rotation)
159 | FEMC_BMW1_RWS_SET(q->slave_hit_wo_rw);
160 } else {
161 ptr->BMW1 = 0;
162 }
163
164 femc_enable(ptr);
165 }
166
femc_convert_actual_size_to_memory_size(uint32_t size_in_kb)167 static uint8_t femc_convert_actual_size_to_memory_size(uint32_t size_in_kb)
168 {
169 uint8_t size = 0;
170 if (size_in_kb == 4) {
171 return 0;
172 }
173
174 if (size_in_kb > 2 * 1 << 20) {
175 return 0x1F;
176 }
177
178 size = 1;
179 size_in_kb >>= 3;
180 while (size_in_kb > 1) {
181 size_in_kb >>= 1;
182 size++;
183 }
184 return size;
185 }
186
femc_convert_burst_len(uint8_t burst_len_in_byte)187 static uint8_t femc_convert_burst_len(uint8_t burst_len_in_byte)
188 {
189 if ((burst_len_in_byte == 0)
190 || (burst_len_in_byte > FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE)) {
191 return FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE + 1;
192 }
193
194 switch (burst_len_in_byte) {
195 case 1:
196 case 2:
197 case 4:
198 return burst_len_in_byte >> 1;
199 case 8:
200 return (burst_len_in_byte - 1) >> 1;
201 default:
202 return FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE + 1;
203 }
204 }
205
ns2cycle(uint32_t freq_in_hz,uint32_t ns,uint32_t max_cycle)206 static uint32_t ns2cycle(uint32_t freq_in_hz, uint32_t ns, uint32_t max_cycle)
207 {
208 uint32_t ns_per_cycle;
209 uint32_t cycle;
210
211 ns_per_cycle = 1000000000 / freq_in_hz;
212 cycle = ns / ns_per_cycle;
213 if (cycle > max_cycle) {
214 cycle = max_cycle;
215 }
216 return cycle;
217 }
218
femc_config_sdram(FEMC_Type * ptr,uint32_t clk_in_hz,femc_sdram_config_t * config)219 hpm_stat_t femc_config_sdram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sdram_config_t *config)
220 {
221 hpm_stat_t err;
222 uint32_t prescaler;
223 uint32_t refresh_cycle;
224 uint32_t clk_in_khz = clk_in_hz / 1000;
225 femc_cmd_t cmd = {0};
226 uint8_t size = femc_convert_actual_size_to_memory_size(config->size_in_byte >> 10);
227 uint8_t burst_len = femc_convert_burst_len(config->burst_len_in_byte);
228
229 prescaler = ((config->prescaler == 0) ? FEMC_PRESCALER_MAX : config->prescaler);
230 refresh_cycle = clk_in_khz * config->refresh_in_ms / config->refresh_count / (prescaler << 4);
231
232 if ((prescaler == 0) || (prescaler > FEMC_PRESCALER_MAX)
233 || (refresh_cycle == 0) || (refresh_cycle > FEMC_PRESCALER_MAX)) {
234 return status_invalid_argument;
235 }
236
237 if (prescaler == FEMC_PRESCALER_MAX) {
238 prescaler = 0;
239 }
240
241 if (refresh_cycle == FEMC_PRESCALER_MAX) {
242 refresh_cycle = 0;
243 }
244
245 ptr->BR[config->cs] = FEMC_BR_BASE_SET(config->base_address >> FEMC_BR_BASE_SHIFT)
246 | FEMC_BR_SIZE_SET(size) | FEMC_BR_VLD_MASK;
247
248 ptr->SDRCTRL0 = FEMC_SDRCTRL0_PORTSZ_SET(config->port_size)
249 | FEMC_SDRCTRL0_BURSTLEN_SET(burst_len)
250 | FEMC_SDRCTRL0_COL_SET(config->col_addr_bits)
251 | FEMC_SDRCTRL0_COL8_SET(config->col_addr_bits == FEMC_SDRAM_COLUMN_ADDR_8_BITS)
252 | FEMC_SDRCTRL0_CAS_SET(config->cas_latency)
253 | FEMC_SDRCTRL0_BANK2_SET(config->bank_num);
254
255 ptr->SDRCTRL1 = FEMC_SDRCTRL1_PRE2ACT_SET(ns2cycle(clk_in_hz, config->precharge_to_act_in_ns, FEMC_SDRCTRL1_PRE2ACT_MASK >> FEMC_SDRCTRL1_PRE2ACT_SHIFT))
256 | FEMC_SDRCTRL1_ACT2RW_SET(ns2cycle(clk_in_hz, config->act_to_rw_in_ns, FEMC_SDRCTRL1_ACT2RW_MASK >> FEMC_SDRCTRL1_ACT2RW_SHIFT))
257 | FEMC_SDRCTRL1_RFRC_SET(ns2cycle(clk_in_hz, config->refresh_recover_in_ns, FEMC_SDRCTRL1_RFRC_MASK >> FEMC_SDRCTRL1_RFRC_SHIFT))
258 | FEMC_SDRCTRL1_WRC_SET(ns2cycle(clk_in_hz, config->write_recover_in_ns, FEMC_SDRCTRL1_WRC_MASK >> FEMC_SDRCTRL1_WRC_SHIFT))
259 | FEMC_SDRCTRL1_CKEOFF_SET(ns2cycle(clk_in_hz, config->cke_off_in_ns, FEMC_SDRCTRL1_CKEOFF_MASK >> FEMC_SDRCTRL1_CKEOFF_SHIFT))
260 | FEMC_SDRCTRL1_ACT2PRE_SET(ns2cycle(clk_in_hz, config->act_to_precharge_in_ns, FEMC_SDRCTRL1_ACT2PRE_MASK >> FEMC_SDRCTRL1_ACT2PRE_SHIFT));
261
262 ptr->SDRCTRL2 = FEMC_SDRCTRL2_SRRC_SET(ns2cycle(clk_in_hz, config->self_refresh_recover_in_ns, FEMC_SDRCTRL2_SRRC_MASK >> FEMC_SDRCTRL2_SRRC_SHIFT))
263 | FEMC_SDRCTRL2_REF2REF_SET(ns2cycle(clk_in_hz, config->refresh_to_refresh_in_ns, FEMC_SDRCTRL2_REF2REF_MASK >> FEMC_SDRCTRL2_REF2REF_SHIFT))
264 | FEMC_SDRCTRL2_ACT2ACT_SET(ns2cycle(clk_in_hz, config->act_to_act_in_ns, FEMC_SDRCTRL2_ACT2ACT_MASK >> FEMC_SDRCTRL2_ACT2ACT_SHIFT))
265 | FEMC_SDRCTRL2_ITO_SET(ns2cycle(clk_in_hz, config->idle_timeout_in_ns, FEMC_SDRCTRL2_ITO_MASK >> FEMC_SDRCTRL2_ITO_SHIFT));
266
267 ptr->SDRCTRL3 = FEMC_SDRCTRL3_PRESCALE_SET(prescaler)
268 | FEMC_SDRCTRL3_RT_SET(refresh_cycle)
269 | FEMC_SDRCTRL3_UT_SET(refresh_cycle)
270 | FEMC_SDRCTRL3_REBL_SET(config->auto_refresh_count_in_one_burst - 1);
271
272 /*
273 * config delay cell
274 */
275 femc_config_delay_cell(ptr, !config->delay_cell_disable, config->delay_cell_value);
276
277 /*
278 *
279 * DATSZ[2:0]: Data size in byte
280 * 0b - 4
281 * 1b - 1
282 * 2b - 2
283 * 3b - 3
284 * > 3b - 4
285 */
286 ptr->DATSZ = FEMC_DATSZ_DATSZ_SET((config->cmd_data_width & (0x3UL)));
287 ptr->BYTEMSK = 0;
288
289 cmd.opcode = FEMC_CMD_SDRAM_PRECHARGE_ALL;
290 cmd.data = 0;
291 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
292 if (status_success != err) {
293 return err;
294 }
295
296 cmd.opcode = FEMC_CMD_SDRAM_AUTO_REFRESH;
297 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
298 if (status_success != err) {
299 return err;
300 }
301 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
302 if (status_success != err) {
303 return err;
304 }
305
306 cmd.opcode = FEMC_CMD_SDRAM_MODE_SET;
307 /* FIXME: the mode register layout definition better to be passed in? */
308 cmd.data = (uint32_t)(burst_len | config->cas_latency << 4);
309 err = femc_issue_ip_cmd(ptr, config->base_address, &cmd);
310 if (status_success != err) {
311 return err;
312 }
313 ptr->SDRCTRL3 |= FEMC_SDRCTRL3_REN_MASK;
314
315 return status_success;
316 }
317
femc_get_typical_sram_config(FEMC_Type * ptr,femc_sram_config_t * config)318 void femc_get_typical_sram_config(FEMC_Type *ptr, femc_sram_config_t *config)
319 {
320 (void) ptr;
321 config->base_address = 0x48000000;
322 config->size_in_byte = 4096;
323 config->address_mode = FEMC_SRAM_AD_NONMUX_MODE;
324 config->port_size = FEMC_SRAM_PORT_SIZE_8_BITS;
325 config->adv_hold_state = FEMC_SRAM_ADV_HOLD_LOW;
326 config->adv_polarity = FEMC_SRAM_ADV_ACTIVE_HIGH;
327 config->oeh_in_ns = 0;
328 config->oel_in_ns = 50;
329 config->weh_in_ns = 0;
330 config->wel_in_ns = 50;
331 config->ah_in_ns = 50;
332 config->as_in_ns = 0;
333 config->ceh_in_ns = 0;
334 config->ces_in_ns = 0;
335 }
336
femc_config_sram(FEMC_Type * ptr,uint32_t clk_in_hz,femc_sram_config_t * config)337 hpm_stat_t femc_config_sram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sram_config_t *config)
338 {
339 uint8_t size = femc_convert_actual_size_to_memory_size(config->size_in_byte >> 10);
340
341 ptr->IOCTRL = FEMC_IOCTRL_IO_CSX_SET(FEMC_IO_CSX_SRAM_CE);
342
343 ptr->BR[FEMC_BR_BASE6] = FEMC_BR_BASE_SET(config->base_address >> FEMC_BR_BASE_SHIFT)
344 | FEMC_BR_SIZE_SET(size)
345 | FEMC_BR_VLD_MASK;
346
347 ptr->SRCTRL0 = FEMC_SRCTRL0_ADVH_SET(config->adv_hold_state)
348 | FEMC_SRCTRL0_ADVP_SET(config->adv_polarity)
349 | FEMC_SRCTRL0_ADM_SET(config->address_mode)
350 | FEMC_SRCTRL0_PORTSZ_SET(config->port_size);
351
352 ptr->SRCTRL1 = FEMC_SRCTRL1_OEH_SET(ns2cycle(clk_in_hz, config->oeh_in_ns, FEMC_SRCTRL1_OEH_MASK >> FEMC_SRCTRL1_OEH_SHIFT))
353 | FEMC_SRCTRL1_OEL_SET(ns2cycle(clk_in_hz, config->oel_in_ns, FEMC_SRCTRL1_OEL_MASK >> FEMC_SRCTRL1_OEL_SHIFT))
354 | FEMC_SRCTRL1_WEH_SET(ns2cycle(clk_in_hz, config->weh_in_ns, FEMC_SRCTRL1_WEH_MASK >> FEMC_SRCTRL1_WEH_SHIFT))
355 | FEMC_SRCTRL1_WEL_SET(ns2cycle(clk_in_hz, config->wel_in_ns, FEMC_SRCTRL1_WEL_MASK >> FEMC_SRCTRL1_WEL_SHIFT))
356 | FEMC_SRCTRL1_AH_SET(ns2cycle(clk_in_hz, config->ah_in_ns, FEMC_SRCTRL1_AH_MASK >> FEMC_SRCTRL1_AH_SHIFT))
357 | FEMC_SRCTRL1_AS_SET(ns2cycle(clk_in_hz, config->as_in_ns, FEMC_SRCTRL1_AS_MASK >> FEMC_SRCTRL1_AS_SHIFT))
358 | FEMC_SRCTRL1_CEH_SET(ns2cycle(clk_in_hz, config->ceh_in_ns, FEMC_SRCTRL1_CEH_MASK >> FEMC_SRCTRL1_CEH_SHIFT))
359 | FEMC_SRCTRL1_CES_SET(ns2cycle(clk_in_hz, config->ces_in_ns, FEMC_SRCTRL1_CES_MASK >> FEMC_SRCTRL1_CES_SHIFT));
360
361 return status_success;
362 }
363