Commit | Line | Data |
---|---|---|
5830b57b AC |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * MMIO interface for QFW | |
4 | * | |
5 | * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> | |
6 | * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> | |
7 | */ | |
8 | ||
9 | #define LOG_CATEGORY UCLASS_QFW | |
10 | ||
11 | #include <asm/types.h> | |
12 | #include <asm/io.h> | |
13 | #include <dm.h> | |
14 | #include <dm/device.h> | |
15 | #include <qfw.h> | |
16 | ||
17 | struct qfw_mmio { | |
18 | /* | |
19 | * Each access to the 64-bit data register can be 8/16/32/64 bits wide. | |
20 | */ | |
21 | union { | |
22 | u8 data8; | |
23 | u16 data16; | |
24 | u32 data32; | |
25 | u64 data64; | |
26 | }; | |
27 | u16 selector; | |
28 | u8 padding[6]; | |
29 | u64 dma; | |
30 | }; | |
31 | ||
32 | struct qfw_mmio_plat { | |
33 | volatile struct qfw_mmio *mmio; | |
34 | }; | |
35 | ||
36 | static void qfw_mmio_read_entry_io(struct udevice *dev, u16 entry, u32 size, | |
37 | void *address) | |
38 | { | |
39 | struct qfw_mmio_plat *plat = dev_get_plat(dev); | |
40 | ||
41 | /* | |
42 | * writing FW_CFG_INVALID will cause read operation to resume at last | |
43 | * offset, otherwise read will start at offset 0 | |
44 | * | |
45 | * Note: on platform where the control register is MMIO, the register | |
46 | * is big endian. | |
47 | */ | |
48 | if (entry != FW_CFG_INVALID) | |
49 | plat->mmio->selector = cpu_to_be16(entry); | |
50 | ||
51 | /* the endianness of data register is string-preserving */ | |
52 | while (size >= 8) { | |
53 | *(u64 *)address = plat->mmio->data64; | |
54 | address += 8; | |
55 | size -= 8; | |
56 | } | |
57 | while (size >= 4) { | |
58 | *(u32 *)address = plat->mmio->data32; | |
59 | address += 4; | |
60 | size -= 4; | |
61 | } | |
62 | while (size >= 2) { | |
63 | *(u16 *)address = plat->mmio->data16; | |
64 | address += 2; | |
65 | size -= 2; | |
66 | } | |
67 | while (size >= 1) { | |
68 | *(u8 *)address = plat->mmio->data8; | |
69 | address += 1; | |
70 | size -= 1; | |
71 | } | |
72 | } | |
73 | ||
74 | /* Read configuration item using fw_cfg DMA interface */ | |
75 | static void qfw_mmio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) | |
76 | { | |
77 | struct qfw_mmio_plat *plat = dev_get_plat(dev); | |
78 | ||
79 | /* the DMA address register is big-endian */ | |
80 | plat->mmio->dma = cpu_to_be64((uintptr_t)dma); | |
81 | ||
82 | while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); | |
83 | } | |
84 | ||
85 | static int qfw_mmio_of_to_plat(struct udevice *dev) | |
86 | { | |
87 | struct qfw_mmio_plat *plat = dev_get_plat(dev); | |
88 | ||
89 | plat->mmio = map_physmem(dev_read_addr(dev), | |
90 | sizeof(struct qfw_mmio), | |
91 | MAP_NOCACHE); | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static int qfw_mmio_probe(struct udevice *dev) | |
97 | { | |
98 | return qfw_register(dev); | |
99 | } | |
100 | ||
101 | static struct dm_qfw_ops qfw_mmio_ops = { | |
102 | .read_entry_io = qfw_mmio_read_entry_io, | |
103 | .read_entry_dma = qfw_mmio_read_entry_dma, | |
104 | }; | |
105 | ||
106 | static const struct udevice_id qfw_mmio_ids[] = { | |
107 | { .compatible = "qemu,fw-cfg-mmio" }, | |
108 | {} | |
109 | }; | |
110 | ||
111 | U_BOOT_DRIVER(qfw_mmio) = { | |
112 | .name = "qfw_mmio", | |
113 | .id = UCLASS_QFW, | |
114 | .of_match = qfw_mmio_ids, | |
115 | .plat_auto = sizeof(struct qfw_mmio_plat), | |
116 | .of_to_plat = qfw_mmio_of_to_plat, | |
117 | .probe = qfw_mmio_probe, | |
118 | .ops = &qfw_mmio_ops, | |
119 | }; |