2 * Allwinner R40 SDRAM Controller emulation
4 * CCopyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "qemu/error-report.h"
23 #include "hw/sysbus.h"
24 #include "migration/vmstate.h"
26 #include "qemu/module.h"
27 #include "exec/address-spaces.h"
28 #include "hw/qdev-properties.h"
29 #include "qapi/error.h"
30 #include "qemu/bitops.h"
31 #include "hw/misc/allwinner-r40-dramc.h"
34 #define REG_INDEX(offset) (offset / sizeof(uint32_t))
36 /* DRAMCOM register offsets */
38 REG_DRAMCOM_CR
= 0x0000, /* Control Register */
41 /* DRAMCOMM register flags */
43 REG_DRAMCOM_CR_DUAL_RANK
= (1 << 0),
46 /* DRAMCTL register offsets */
48 REG_DRAMCTL_PIR
= 0x0000, /* PHY Initialization Register */
49 REG_DRAMCTL_PGSR
= 0x0010, /* PHY General Status Register */
50 REG_DRAMCTL_STATR
= 0x0018, /* Status Register */
51 REG_DRAMCTL_PGCR
= 0x0100, /* PHY general configuration registers */
54 /* DRAMCTL register flags */
56 REG_DRAMCTL_PGSR_INITDONE
= (1 << 0),
57 REG_DRAMCTL_PGSR_READ_TIMEOUT
= (1 << 13),
58 REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT
= (1 << 25),
62 REG_DRAMCTL_STATR_ACTIVE
= (1 << 0),
65 #define DRAM_MAX_ROW_BITS 16
66 #define DRAM_MAX_COL_BITS 13 /* 8192 */
67 #define DRAM_MAX_BANK 3
69 static uint64_t dram_autodetect_cells
[DRAM_MAX_ROW_BITS
]
72 struct VirtualDDRChip
{
80 * Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported,
81 * 2GiB memory is not supported due to dual rank feature.
83 static const struct VirtualDDRChip dummy_ddr_chips
[] = {
104 static const struct VirtualDDRChip
*get_match_ddr(uint32_t ram_size
)
106 const struct VirtualDDRChip
*ddr
;
108 for (ddr
= &dummy_ddr_chips
[0]; ddr
->ram_size
; ddr
++) {
109 if (ddr
->ram_size
== ram_size
) {
117 static uint64_t *address_to_autodetect_cells(AwR40DramCtlState
*s
,
118 const struct VirtualDDRChip
*ddr
,
121 int row_index
= 0, bank_index
= 0, col_index
= 0;
122 uint32_t row_addr
, bank_addr
, col_addr
;
124 row_addr
= extract32(offset
, s
->set_col_bits
+ s
->set_bank_bits
,
126 bank_addr
= extract32(offset
, s
->set_col_bits
, s
->set_bank_bits
);
127 col_addr
= extract32(offset
, 0, s
->set_col_bits
);
129 for (int i
= 0; i
< ddr
->row_bits
; i
++) {
130 if (row_addr
& BIT(i
)) {
135 for (int i
= 0; i
< ddr
->bank_bits
; i
++) {
136 if (bank_addr
& BIT(i
)) {
141 for (int i
= 0; i
< ddr
->col_bits
; i
++) {
142 if (col_addr
& BIT(i
)) {
147 trace_allwinner_r40_dramc_offset_to_cell(offset
, row_index
, bank_index
,
149 return &dram_autodetect_cells
[row_index
][bank_index
][col_index
];
152 static void allwinner_r40_dramc_map_rows(AwR40DramCtlState
*s
, uint8_t row_bits
,
153 uint8_t bank_bits
, uint8_t col_bits
)
155 const struct VirtualDDRChip
*ddr
= get_match_ddr(s
->ram_size
);
156 bool enable_detect_cells
;
158 trace_allwinner_r40_dramc_map_rows(row_bits
, bank_bits
, col_bits
);
164 s
->set_row_bits
= row_bits
;
165 s
->set_bank_bits
= bank_bits
;
166 s
->set_col_bits
= col_bits
;
168 enable_detect_cells
= ddr
->bank_bits
!= bank_bits
169 || ddr
->row_bits
!= row_bits
170 || ddr
->col_bits
!= col_bits
;
172 if (enable_detect_cells
) {
173 trace_allwinner_r40_dramc_detect_cells_enable();
175 trace_allwinner_r40_dramc_detect_cells_disable();
178 memory_region_set_enabled(&s
->detect_cells
, enable_detect_cells
);
181 static uint64_t allwinner_r40_dramcom_read(void *opaque
, hwaddr offset
,
184 const AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
185 const uint32_t idx
= REG_INDEX(offset
);
187 if (idx
>= AW_R40_DRAMCOM_REGS_NUM
) {
188 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
189 __func__
, (uint32_t)offset
);
193 trace_allwinner_r40_dramcom_read(offset
, s
->dramcom
[idx
], size
);
194 return s
->dramcom
[idx
];
197 static void allwinner_r40_dramcom_write(void *opaque
, hwaddr offset
,
198 uint64_t val
, unsigned size
)
200 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
201 const uint32_t idx
= REG_INDEX(offset
);
203 trace_allwinner_r40_dramcom_write(offset
, val
, size
);
205 if (idx
>= AW_R40_DRAMCOM_REGS_NUM
) {
206 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
207 __func__
, (uint32_t)offset
);
212 case REG_DRAMCOM_CR
: /* Control Register */
213 if (!(val
& REG_DRAMCOM_CR_DUAL_RANK
)) {
214 allwinner_r40_dramc_map_rows(s
, ((val
>> 4) & 0xf) + 1,
215 ((val
>> 2) & 0x1) + 2,
216 (((val
>> 8) & 0xf) + 3));
221 s
->dramcom
[idx
] = (uint32_t) val
;
224 static uint64_t allwinner_r40_dramctl_read(void *opaque
, hwaddr offset
,
227 const AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
228 const uint32_t idx
= REG_INDEX(offset
);
230 if (idx
>= AW_R40_DRAMCTL_REGS_NUM
) {
231 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
232 __func__
, (uint32_t)offset
);
236 trace_allwinner_r40_dramctl_read(offset
, s
->dramctl
[idx
], size
);
237 return s
->dramctl
[idx
];
240 static void allwinner_r40_dramctl_write(void *opaque
, hwaddr offset
,
241 uint64_t val
, unsigned size
)
243 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
244 const uint32_t idx
= REG_INDEX(offset
);
246 trace_allwinner_r40_dramctl_write(offset
, val
, size
);
248 if (idx
>= AW_R40_DRAMCTL_REGS_NUM
) {
249 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
250 __func__
, (uint32_t)offset
);
255 case REG_DRAMCTL_PIR
: /* PHY Initialization Register */
256 s
->dramctl
[REG_INDEX(REG_DRAMCTL_PGSR
)] |= REG_DRAMCTL_PGSR_INITDONE
;
257 s
->dramctl
[REG_INDEX(REG_DRAMCTL_STATR
)] |= REG_DRAMCTL_STATR_ACTIVE
;
261 s
->dramctl
[idx
] = (uint32_t) val
;
264 static uint64_t allwinner_r40_dramphy_read(void *opaque
, hwaddr offset
,
267 const AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
268 const uint32_t idx
= REG_INDEX(offset
);
270 if (idx
>= AW_R40_DRAMPHY_REGS_NUM
) {
271 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
272 __func__
, (uint32_t)offset
);
276 trace_allwinner_r40_dramphy_read(offset
, s
->dramphy
[idx
], size
);
277 return s
->dramphy
[idx
];
280 static void allwinner_r40_dramphy_write(void *opaque
, hwaddr offset
,
281 uint64_t val
, unsigned size
)
283 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
284 const uint32_t idx
= REG_INDEX(offset
);
286 trace_allwinner_r40_dramphy_write(offset
, val
, size
);
288 if (idx
>= AW_R40_DRAMPHY_REGS_NUM
) {
289 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
290 __func__
, (uint32_t)offset
);
294 s
->dramphy
[idx
] = (uint32_t) val
;
297 static const MemoryRegionOps allwinner_r40_dramcom_ops
= {
298 .read
= allwinner_r40_dramcom_read
,
299 .write
= allwinner_r40_dramcom_write
,
300 .endianness
= DEVICE_NATIVE_ENDIAN
,
302 .min_access_size
= 4,
303 .max_access_size
= 4,
305 .impl
.min_access_size
= 4,
308 static const MemoryRegionOps allwinner_r40_dramctl_ops
= {
309 .read
= allwinner_r40_dramctl_read
,
310 .write
= allwinner_r40_dramctl_write
,
311 .endianness
= DEVICE_NATIVE_ENDIAN
,
313 .min_access_size
= 4,
314 .max_access_size
= 4,
316 .impl
.min_access_size
= 4,
319 static const MemoryRegionOps allwinner_r40_dramphy_ops
= {
320 .read
= allwinner_r40_dramphy_read
,
321 .write
= allwinner_r40_dramphy_write
,
322 .endianness
= DEVICE_NATIVE_ENDIAN
,
324 .min_access_size
= 4,
325 .max_access_size
= 4,
327 .impl
.min_access_size
= 4,
330 static uint64_t allwinner_r40_detect_read(void *opaque
, hwaddr offset
,
333 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
334 const struct VirtualDDRChip
*ddr
= get_match_ddr(s
->ram_size
);
338 data
= *address_to_autodetect_cells(s
, ddr
, (uint32_t)offset
);
341 trace_allwinner_r40_dramc_detect_cell_read(offset
, data
);
345 static void allwinner_r40_detect_write(void *opaque
, hwaddr offset
,
346 uint64_t data
, unsigned size
)
348 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
349 const struct VirtualDDRChip
*ddr
= get_match_ddr(s
->ram_size
);
352 uint64_t *cell
= address_to_autodetect_cells(s
, ddr
, (uint32_t)offset
);
353 trace_allwinner_r40_dramc_detect_cell_write(offset
, data
);
358 static const MemoryRegionOps allwinner_r40_detect_ops
= {
359 .read
= allwinner_r40_detect_read
,
360 .write
= allwinner_r40_detect_write
,
361 .endianness
= DEVICE_NATIVE_ENDIAN
,
363 .min_access_size
= 4,
364 .max_access_size
= 4,
366 .impl
.min_access_size
= 4,
370 * mctl_r40_detect_rank_count in u-boot will write the high 1G of DDR
371 * to detect wether the board support dual_rank or not. Create a virtual memory
372 * if the board's ram_size less or equal than 1G, and set read time out flag of
373 * REG_DRAMCTL_PGSR when the user touch this high dram.
375 static uint64_t allwinner_r40_dualrank_detect_read(void *opaque
, hwaddr offset
,
378 AwR40DramCtlState
*s
= AW_R40_DRAMC(opaque
);
381 reg
= s
->dramctl
[REG_INDEX(REG_DRAMCTL_PGCR
)];
382 if (reg
& REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT
) { /* Enable read time out */
384 * this driver only support one rank, mark READ_TIMEOUT when try
385 * read the second rank.
387 s
->dramctl
[REG_INDEX(REG_DRAMCTL_PGSR
)]
388 |= REG_DRAMCTL_PGSR_READ_TIMEOUT
;
394 static const MemoryRegionOps allwinner_r40_dualrank_detect_ops
= {
395 .read
= allwinner_r40_dualrank_detect_read
,
396 .endianness
= DEVICE_NATIVE_ENDIAN
,
398 .min_access_size
= 4,
399 .max_access_size
= 4,
401 .impl
.min_access_size
= 4,
404 static void allwinner_r40_dramc_reset(DeviceState
*dev
)
406 AwR40DramCtlState
*s
= AW_R40_DRAMC(dev
);
408 /* Set default values for registers */
409 memset(&s
->dramcom
, 0, sizeof(s
->dramcom
));
410 memset(&s
->dramctl
, 0, sizeof(s
->dramctl
));
411 memset(&s
->dramphy
, 0, sizeof(s
->dramphy
));
414 static void allwinner_r40_dramc_realize(DeviceState
*dev
, Error
**errp
)
416 AwR40DramCtlState
*s
= AW_R40_DRAMC(dev
);
418 if (!get_match_ddr(s
->ram_size
)) {
419 error_report("%s: ram-size %u MiB is not supported",
420 __func__
, s
->ram_size
);
425 sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s
), 3, s
->ram_addr
, 10);
426 memory_region_set_enabled(&s
->detect_cells
, false);
429 * We only support DRAM size up to 1G now, so prepare a high memory page
430 * after 1G for dualrank detect. index = 4
432 memory_region_init_io(&s
->dram_high
, OBJECT(s
),
433 &allwinner_r40_dualrank_detect_ops
, s
,
435 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->dram_high
);
436 sysbus_mmio_map(SYS_BUS_DEVICE(s
), 4, s
->ram_addr
+ GiB
);
439 static void allwinner_r40_dramc_init(Object
*obj
)
441 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
442 AwR40DramCtlState
*s
= AW_R40_DRAMC(obj
);
444 /* DRAMCOM registers, index 0 */
445 memory_region_init_io(&s
->dramcom_iomem
, OBJECT(s
),
446 &allwinner_r40_dramcom_ops
, s
,
448 sysbus_init_mmio(sbd
, &s
->dramcom_iomem
);
450 /* DRAMCTL registers, index 1 */
451 memory_region_init_io(&s
->dramctl_iomem
, OBJECT(s
),
452 &allwinner_r40_dramctl_ops
, s
,
454 sysbus_init_mmio(sbd
, &s
->dramctl_iomem
);
456 /* DRAMPHY registers. index 2 */
457 memory_region_init_io(&s
->dramphy_iomem
, OBJECT(s
),
458 &allwinner_r40_dramphy_ops
, s
,
460 sysbus_init_mmio(sbd
, &s
->dramphy_iomem
);
462 /* R40 support max 2G memory but we only support up to 1G now. index 3 */
463 memory_region_init_io(&s
->detect_cells
, OBJECT(s
),
464 &allwinner_r40_detect_ops
, s
,
465 "DRAMCELLS", 1 * GiB
);
466 sysbus_init_mmio(sbd
, &s
->detect_cells
);
469 static Property allwinner_r40_dramc_properties
[] = {
470 DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState
, ram_addr
, 0x0),
471 DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState
, ram_size
, 256), /* MiB */
472 DEFINE_PROP_END_OF_LIST()
475 static const VMStateDescription allwinner_r40_dramc_vmstate
= {
476 .name
= "allwinner-r40-dramc",
478 .minimum_version_id
= 1,
479 .fields
= (VMStateField
[]) {
480 VMSTATE_UINT32_ARRAY(dramcom
, AwR40DramCtlState
,
481 AW_R40_DRAMCOM_REGS_NUM
),
482 VMSTATE_UINT32_ARRAY(dramctl
, AwR40DramCtlState
,
483 AW_R40_DRAMCTL_REGS_NUM
),
484 VMSTATE_UINT32_ARRAY(dramphy
, AwR40DramCtlState
,
485 AW_R40_DRAMPHY_REGS_NUM
),
486 VMSTATE_END_OF_LIST()
490 static void allwinner_r40_dramc_class_init(ObjectClass
*klass
, void *data
)
492 DeviceClass
*dc
= DEVICE_CLASS(klass
);
494 dc
->reset
= allwinner_r40_dramc_reset
;
495 dc
->vmsd
= &allwinner_r40_dramc_vmstate
;
496 dc
->realize
= allwinner_r40_dramc_realize
;
497 device_class_set_props(dc
, allwinner_r40_dramc_properties
);
500 static const TypeInfo allwinner_r40_dramc_info
= {
501 .name
= TYPE_AW_R40_DRAMC
,
502 .parent
= TYPE_SYS_BUS_DEVICE
,
503 .instance_init
= allwinner_r40_dramc_init
,
504 .instance_size
= sizeof(AwR40DramCtlState
),
505 .class_init
= allwinner_r40_dramc_class_init
,
508 static void allwinner_r40_dramc_register(void)
510 type_register_static(&allwinner_r40_dramc_info
);
513 type_init(allwinner_r40_dramc_register
)