1 /*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "los_debug.h"
17 #include "los_arch_interrupt.h"
18 #include "los_interrupt.h"
19 #include "lwip/mem.h"
20 #include "virtmmio.h"
21
22 #define IRQF_SHARED 0
23
VirtioGetStatus(const struct VirtmmioDev * dev)24 static inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev)
25 {
26 return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS);
27 }
28
VirtioAddStatus(const struct VirtmmioDev * dev,uint32_t val)29 static inline void VirtioAddStatus(const struct VirtmmioDev *dev, uint32_t val)
30 {
31 FENCE_WRITE_UINT32(VirtioGetStatus(dev) | val, dev->base + VIRTMMIO_REG_STATUS);
32 }
33
VirtioResetStatus(const struct VirtmmioDev * dev)34 static inline void VirtioResetStatus(const struct VirtmmioDev *dev)
35 {
36 FENCE_WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS);
37 }
38
VirtmmioDiscover(uint32_t devId,struct VirtmmioDev * dev)39 bool VirtmmioDiscover(uint32_t devId, struct VirtmmioDev *dev)
40 {
41 VADDR_T base;
42 int i;
43
44 base = IO_DEVICE_ADDR(VIRTMMIO_BASE_ADDR) + VIRTMMIO_BASE_SIZE * (NUM_VIRTIO_TRANSPORTS - 1);
45 for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) {
46 if ((GET_UINT32(base + VIRTMMIO_REG_MAGICVALUE) == VIRTMMIO_MAGIC) &&
47 (GET_UINT32(base + VIRTMMIO_REG_VERSION) == VIRTMMIO_VERSION) &&
48 (GET_UINT32(base + VIRTMMIO_REG_DEVICEID) == devId)) {
49 dev->base = base;
50 dev->irq = IRQ_SPI_BASE + VIRTMMIO_BASE_IRQ + i;
51 return true;
52 }
53
54 base -= VIRTMMIO_BASE_SIZE;
55 }
56
57 PRINT_ERR("virtio-mmio ID=%u device not found\n", devId);
58 return false;
59 }
60
VirtqSize(uint16_t qsz)61 unsigned VirtqSize(uint16_t qsz)
62 {
63 /* pretend we do not have an aligned start address */
64 return VIRTQ_ALIGN_DESC - 1 +
65 ALIGN(sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL) +
66 ALIGN(sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED) +
67 sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz;
68 }
69
VirtmmioInitBegin(const struct VirtmmioDev * dev)70 void VirtmmioInitBegin(const struct VirtmmioDev *dev)
71 {
72 VirtioResetStatus(dev);
73 VirtioAddStatus(dev, VIRTIO_STATUS_ACK);
74 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER);
75 while ((VirtioGetStatus(dev) & VIRTIO_STATUS_DRIVER) == 0) { }
76 }
77
VritmmioInitEnd(const struct VirtmmioDev * dev)78 void VritmmioInitEnd(const struct VirtmmioDev *dev)
79 {
80 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK);
81 }
82
VirtmmioInitFailed(const struct VirtmmioDev * dev)83 void VirtmmioInitFailed(const struct VirtmmioDev *dev)
84 {
85 VirtioAddStatus(dev, VIRTIO_STATUS_FAILED);
86 }
87
Negotiate(struct VirtmmioDev * baseDev,uint32_t nth,VirtioFeatureFn fn,void * dev)88 static bool Negotiate(struct VirtmmioDev *baseDev, uint32_t nth, VirtioFeatureFn fn, void *dev)
89 {
90 uint32_t features, supported, before, after;
91
92 FENCE_WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DEVFEATURESEL);
93 features = GET_UINT32(baseDev->base + VIRTMMIO_REG_DEVFEATURE);
94
95 do {
96 before = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION);
97
98 supported = 0;
99 if (!fn(features, &supported, dev)) {
100 return false;
101 }
102
103 after = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION);
104 } while (before != after);
105
106 FENCE_WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DRVFEATURESEL);
107 FENCE_WRITE_UINT32(supported, baseDev->base + VIRTMMIO_REG_DRVFEATURE);
108 return true;
109 }
110
VirtmmioNegotiate(struct VirtmmioDev * baseDev,VirtioFeatureFn f0,VirtioFeatureFn f1,void * dev)111 bool VirtmmioNegotiate(struct VirtmmioDev *baseDev, VirtioFeatureFn f0, VirtioFeatureFn f1, void *dev)
112 {
113 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD0, f0, dev)) {
114 return false;
115 }
116
117 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD1, f1, dev)) {
118 return false;
119 }
120
121 VirtioAddStatus(baseDev, VIRTIO_STATUS_FEATURES_OK);
122 if ((VirtioGetStatus(baseDev) & VIRTIO_STATUS_FEATURES_OK) == 0) {
123 PRINT_ERR("negotiate features failed\n");
124 return false;
125 }
126
127 return true;
128 }
129
130 #define U32_MASK 0xFFFFFFFF
131 #define U32_BYTES 4
132 #define U64_32_SHIFT 32
u32_to_u64(uint32_t addr)133 uint64_t u32_to_u64(uint32_t addr) {
134 uint64_t paddr = (uint64_t)addr & U32_MASK;
135 return paddr;
136 }
WriteQueueAddr(uint64_t addr,const struct VirtmmioDev * dev,uint32_t regLow)137 static void WriteQueueAddr(uint64_t addr, const struct VirtmmioDev *dev, uint32_t regLow)
138 {
139 uint32_t paddr;
140
141 paddr = addr & U32_MASK;
142 FENCE_WRITE_UINT32(paddr, dev->base + regLow);
143 paddr = addr >> U64_32_SHIFT;
144 FENCE_WRITE_UINT32(paddr, dev->base + regLow + U32_BYTES);
145 }
146
CompleteConfigQueue(uint32_t queue,const struct VirtmmioDev * dev)147 static bool CompleteConfigQueue(uint32_t queue, const struct VirtmmioDev *dev)
148 {
149 const struct Virtq *q = &dev->vq[queue];
150 uint32_t num;
151
152 FENCE_WRITE_UINT32(queue, dev->base + VIRTMMIO_REG_QUEUESEL);
153
154 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUEREADY);
155 LOS_ASSERT(num == 0);
156 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUENUMMAX);
157 if (num < q->qsz) {
158 PRINT_ERR("queue %u not available: max qsz=%u, requested=%u\n", queue, num, q->qsz);
159 return false;
160 }
161
162 FENCE_WRITE_UINT32(q->qsz, dev->base + VIRTMMIO_REG_QUEUENUM);
163 WriteQueueAddr(u32_to_u64(q->desc), dev, VIRTMMIO_REG_QUEUEDESCLOW);
164 WriteQueueAddr(u32_to_u64(q->avail), dev, VIRTMMIO_REG_QUEUEDRIVERLOW);
165 WriteQueueAddr(u32_to_u64(q->used), dev, VIRTMMIO_REG_QUEUEDEVICELOW);
166
167 FENCE_WRITE_UINT32(1, dev->base + VIRTMMIO_REG_QUEUEREADY);
168 return true;
169 }
170
CalculateQueueAddr(VADDR_T base,uint16_t qsz,struct Virtq * q)171 static VADDR_T CalculateQueueAddr(VADDR_T base, uint16_t qsz, struct Virtq *q)
172 {
173 base = ALIGN(base, VIRTQ_ALIGN_DESC);
174 q->desc = (struct VirtqDesc *)base;
175 q->qsz = qsz;
176 base = ALIGN(base + sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL);
177 q->avail = (struct VirtqAvail *)base;
178 base = ALIGN(base + sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED);
179 q->used = (struct VirtqUsed *)base;
180
181 return base + sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz;
182 }
183
VirtmmioConfigQueue(struct VirtmmioDev * dev,VADDR_T base,uint16_t qsz[],int num)184 VADDR_T VirtmmioConfigQueue(struct VirtmmioDev *dev, VADDR_T base, uint16_t qsz[], int num)
185 {
186 uint32_t i;
187
188 for (i = 0; i < num; i++) {
189 base = CalculateQueueAddr(base, qsz[i], &dev->vq[i]);
190 if (!CompleteConfigQueue(i, dev)) {
191 return 0;
192 }
193 }
194
195 return base;
196 }
197
VirtmmioRegisterIRQ(struct VirtmmioDev * dev,HWI_PROC_FUNC handle,void * argDev,const char * devName)198 bool VirtmmioRegisterIRQ(struct VirtmmioDev *dev, HWI_PROC_FUNC handle, void *argDev, const char *devName)
199 {
200 uint32_t ret;
201 HwiIrqParam *param = mem_calloc(1, sizeof(HwiIrqParam));
202 param->swIrq = dev->irq;
203 param->pDevId = argDev;
204 param->pName = devName;
205
206 ret = LOS_HwiCreate(dev->irq, OS_HWI_PRIO_HIGHEST, IRQF_SHARED, handle, param);
207 if (ret != 0) {
208 PRINT_ERR("virtio-mmio %s IRQ register failed: %u\n", devName, ret);
209 return false;
210 }
211
212 HalIrqEnable(dev->irq);
213 return true;
214 }
215