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