• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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