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