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_vm_zone.h"
17d6aed566Sopenharmony_ci#include "virtmmio.h"
18d6aed566Sopenharmony_ci
19d6aed566Sopenharmony_cistatic inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev)
20d6aed566Sopenharmony_ci{
21d6aed566Sopenharmony_ci    return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS);
22d6aed566Sopenharmony_ci}
23d6aed566Sopenharmony_ci
24d6aed566Sopenharmony_cistatic inline void VirtioAddStatus(const struct VirtmmioDev *dev, uint32_t val)
25d6aed566Sopenharmony_ci{
26d6aed566Sopenharmony_ci    WRITE_UINT32(VirtioGetStatus(dev) | val, dev->base + VIRTMMIO_REG_STATUS);
27d6aed566Sopenharmony_ci}
28d6aed566Sopenharmony_ci
29d6aed566Sopenharmony_cistatic inline void VirtioResetStatus(const struct VirtmmioDev *dev)
30d6aed566Sopenharmony_ci{
31d6aed566Sopenharmony_ci    WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS);
32d6aed566Sopenharmony_ci}
33d6aed566Sopenharmony_ci
34d6aed566Sopenharmony_cibool VirtmmioDiscover(uint32_t devId, struct VirtmmioDev *dev)
35d6aed566Sopenharmony_ci{
36d6aed566Sopenharmony_ci    VADDR_T base;
37d6aed566Sopenharmony_ci    int i;
38d6aed566Sopenharmony_ci
39d6aed566Sopenharmony_ci    base = IO_DEVICE_ADDR(VIRTMMIO_BASE_ADDR) + VIRTMMIO_BASE_SIZE * (NUM_VIRTIO_TRANSPORTS - 1);
40d6aed566Sopenharmony_ci    for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) {
41d6aed566Sopenharmony_ci        if ((GET_UINT32(base + VIRTMMIO_REG_MAGICVALUE) == VIRTMMIO_MAGIC) &&
42d6aed566Sopenharmony_ci            (GET_UINT32(base + VIRTMMIO_REG_VERSION) == VIRTMMIO_VERSION) &&
43d6aed566Sopenharmony_ci            (GET_UINT32(base + VIRTMMIO_REG_DEVICEID) == devId)) {
44d6aed566Sopenharmony_ci            dev->base = base;
45d6aed566Sopenharmony_ci            dev->irq = IRQ_SPI_BASE + VIRTMMIO_BASE_IRQ + i;
46d6aed566Sopenharmony_ci            return true;
47d6aed566Sopenharmony_ci        }
48d6aed566Sopenharmony_ci
49d6aed566Sopenharmony_ci        base -= VIRTMMIO_BASE_SIZE;
50d6aed566Sopenharmony_ci    }
51d6aed566Sopenharmony_ci
52d6aed566Sopenharmony_ci    PRINT_ERR("virtio-mmio ID=%u device not found\n", devId);
53d6aed566Sopenharmony_ci    return false;
54d6aed566Sopenharmony_ci}
55d6aed566Sopenharmony_ci
56d6aed566Sopenharmony_ciunsigned VirtqSize(uint16_t qsz)
57d6aed566Sopenharmony_ci{
58d6aed566Sopenharmony_ci           /* pretend we do not have an aligned start address */
59d6aed566Sopenharmony_ci    return VIRTQ_ALIGN_DESC - 1 +
60d6aed566Sopenharmony_ci           ALIGN(sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL) +
61d6aed566Sopenharmony_ci           ALIGN(sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED) +
62d6aed566Sopenharmony_ci           sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz;
63d6aed566Sopenharmony_ci}
64d6aed566Sopenharmony_ci
65d6aed566Sopenharmony_civoid VirtmmioInitBegin(const struct VirtmmioDev *dev)
66d6aed566Sopenharmony_ci{
67d6aed566Sopenharmony_ci    VirtioResetStatus(dev);
68d6aed566Sopenharmony_ci    VirtioAddStatus(dev, VIRTIO_STATUS_ACK);
69d6aed566Sopenharmony_ci    VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER);
70d6aed566Sopenharmony_ci    while ((VirtioGetStatus(dev) & VIRTIO_STATUS_DRIVER) == 0) { }
71d6aed566Sopenharmony_ci}
72d6aed566Sopenharmony_ci
73d6aed566Sopenharmony_civoid VritmmioInitEnd(const struct VirtmmioDev *dev)
74d6aed566Sopenharmony_ci{
75d6aed566Sopenharmony_ci    VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK);
76d6aed566Sopenharmony_ci}
77d6aed566Sopenharmony_ci
78d6aed566Sopenharmony_civoid VirtmmioInitFailed(const struct VirtmmioDev *dev)
79d6aed566Sopenharmony_ci{
80d6aed566Sopenharmony_ci    VirtioAddStatus(dev, VIRTIO_STATUS_FAILED);
81d6aed566Sopenharmony_ci}
82d6aed566Sopenharmony_ci
83d6aed566Sopenharmony_cistatic bool Negotiate(struct VirtmmioDev *baseDev, uint32_t nth, VirtioFeatureFn fn, void *dev)
84d6aed566Sopenharmony_ci{
85d6aed566Sopenharmony_ci    uint32_t features, supported, before, after;
86d6aed566Sopenharmony_ci
87d6aed566Sopenharmony_ci    WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DEVFEATURESEL);
88d6aed566Sopenharmony_ci    features = GET_UINT32(baseDev->base + VIRTMMIO_REG_DEVFEATURE);
89d6aed566Sopenharmony_ci
90d6aed566Sopenharmony_ci    do {
91d6aed566Sopenharmony_ci        before = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION);
92d6aed566Sopenharmony_ci
93d6aed566Sopenharmony_ci        supported = 0;
94d6aed566Sopenharmony_ci        if (!fn(features, &supported, dev)) {
95d6aed566Sopenharmony_ci            return false;
96d6aed566Sopenharmony_ci        }
97d6aed566Sopenharmony_ci
98d6aed566Sopenharmony_ci        after = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION);
99d6aed566Sopenharmony_ci    } while (before != after);
100d6aed566Sopenharmony_ci
101d6aed566Sopenharmony_ci    WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DRVFEATURESEL);
102d6aed566Sopenharmony_ci    WRITE_UINT32(supported, baseDev->base + VIRTMMIO_REG_DRVFEATURE);
103d6aed566Sopenharmony_ci    return true;
104d6aed566Sopenharmony_ci}
105d6aed566Sopenharmony_ci
106d6aed566Sopenharmony_cibool VirtmmioNegotiate(struct VirtmmioDev *baseDev, VirtioFeatureFn f0, VirtioFeatureFn f1, void *dev)
107d6aed566Sopenharmony_ci{
108d6aed566Sopenharmony_ci    if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD0, f0, dev)) {
109d6aed566Sopenharmony_ci        return false;
110d6aed566Sopenharmony_ci    }
111d6aed566Sopenharmony_ci
112d6aed566Sopenharmony_ci    if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD1, f1, dev)) {
113d6aed566Sopenharmony_ci        return false;
114d6aed566Sopenharmony_ci    }
115d6aed566Sopenharmony_ci
116d6aed566Sopenharmony_ci    VirtioAddStatus(baseDev, VIRTIO_STATUS_FEATURES_OK);
117d6aed566Sopenharmony_ci    if ((VirtioGetStatus(baseDev) & VIRTIO_STATUS_FEATURES_OK) == 0) {
118d6aed566Sopenharmony_ci        PRINT_ERR("negotiate features failed\n");
119d6aed566Sopenharmony_ci        return false;
120d6aed566Sopenharmony_ci    }
121d6aed566Sopenharmony_ci
122d6aed566Sopenharmony_ci    return true;
123d6aed566Sopenharmony_ci}
124d6aed566Sopenharmony_ci
125d6aed566Sopenharmony_ci#define U32_MASK        0xFFFFFFFF
126d6aed566Sopenharmony_ci#define U32_BYTES       4
127d6aed566Sopenharmony_ci#define U64_32_SHIFT    32
128d6aed566Sopenharmony_cistatic void WriteQueueAddr(uint64_t addr, const struct VirtmmioDev *dev, uint32_t regLow)
129d6aed566Sopenharmony_ci{
130d6aed566Sopenharmony_ci    uint32_t paddr;
131d6aed566Sopenharmony_ci
132d6aed566Sopenharmony_ci    addr = VMM_TO_DMA_ADDR(addr);
133d6aed566Sopenharmony_ci    paddr = addr & U32_MASK;
134d6aed566Sopenharmony_ci    WRITE_UINT32(paddr, dev->base + regLow);
135d6aed566Sopenharmony_ci    paddr = addr >> U64_32_SHIFT;
136d6aed566Sopenharmony_ci    WRITE_UINT32(paddr, dev->base + regLow + U32_BYTES);
137d6aed566Sopenharmony_ci}
138d6aed566Sopenharmony_ci
139d6aed566Sopenharmony_cistatic bool CompleteConfigQueue(uint32_t queue, const struct VirtmmioDev *dev)
140d6aed566Sopenharmony_ci{
141d6aed566Sopenharmony_ci    const struct Virtq *q = &dev->vq[queue];
142d6aed566Sopenharmony_ci    uint32_t num;
143d6aed566Sopenharmony_ci
144d6aed566Sopenharmony_ci    WRITE_UINT32(queue, dev->base + VIRTMMIO_REG_QUEUESEL);
145d6aed566Sopenharmony_ci
146d6aed566Sopenharmony_ci    num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUEREADY);
147d6aed566Sopenharmony_ci    LOS_ASSERT(num == 0);
148d6aed566Sopenharmony_ci    num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUENUMMAX);
149d6aed566Sopenharmony_ci    if (num < q->qsz) {
150d6aed566Sopenharmony_ci        PRINT_ERR("queue %u not available: max qsz=%u, requested=%u\n", queue, num, q->qsz);
151d6aed566Sopenharmony_ci        return false;
152d6aed566Sopenharmony_ci    }
153d6aed566Sopenharmony_ci
154d6aed566Sopenharmony_ci    WRITE_UINT32(q->qsz, dev->base + VIRTMMIO_REG_QUEUENUM);
155d6aed566Sopenharmony_ci    WriteQueueAddr((uint64_t)q->desc, dev, VIRTMMIO_REG_QUEUEDESCLOW);
156d6aed566Sopenharmony_ci    WriteQueueAddr((uint64_t)q->avail, dev, VIRTMMIO_REG_QUEUEDRIVERLOW);
157d6aed566Sopenharmony_ci    WriteQueueAddr((uint64_t)q->used, dev, VIRTMMIO_REG_QUEUEDEVICELOW);
158d6aed566Sopenharmony_ci
159d6aed566Sopenharmony_ci    WRITE_UINT32(1, dev->base + VIRTMMIO_REG_QUEUEREADY);
160d6aed566Sopenharmony_ci    return true;
161d6aed566Sopenharmony_ci}
162d6aed566Sopenharmony_ci
163d6aed566Sopenharmony_cistatic VADDR_T CalculateQueueAddr(VADDR_T base, uint16_t qsz, struct Virtq *q)
164d6aed566Sopenharmony_ci{
165d6aed566Sopenharmony_ci    base = ALIGN(base, VIRTQ_ALIGN_DESC);
166d6aed566Sopenharmony_ci    q->desc = (struct VirtqDesc *)base;
167d6aed566Sopenharmony_ci    q->qsz = qsz;
168d6aed566Sopenharmony_ci    base = ALIGN(base + sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL);
169d6aed566Sopenharmony_ci    q->avail = (struct VirtqAvail *)base;
170d6aed566Sopenharmony_ci    base = ALIGN(base + sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED);
171d6aed566Sopenharmony_ci    q->used = (struct VirtqUsed *)base;
172d6aed566Sopenharmony_ci
173d6aed566Sopenharmony_ci    return base + sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz;
174d6aed566Sopenharmony_ci}
175d6aed566Sopenharmony_ci
176d6aed566Sopenharmony_ciVADDR_T VirtmmioConfigQueue(struct VirtmmioDev *dev, VADDR_T base, uint16_t qsz[], int num)
177d6aed566Sopenharmony_ci{
178d6aed566Sopenharmony_ci    uint32_t i;
179d6aed566Sopenharmony_ci
180d6aed566Sopenharmony_ci    for (i = 0; i < num; i++) {
181d6aed566Sopenharmony_ci        base = CalculateQueueAddr(base, qsz[i], &dev->vq[i]);
182d6aed566Sopenharmony_ci        if (!CompleteConfigQueue(i, dev)) {
183d6aed566Sopenharmony_ci            return 0;
184d6aed566Sopenharmony_ci        }
185d6aed566Sopenharmony_ci    }
186d6aed566Sopenharmony_ci
187d6aed566Sopenharmony_ci    return base;
188d6aed566Sopenharmony_ci}
189d6aed566Sopenharmony_ci
190d6aed566Sopenharmony_cibool VirtmmioRegisterIRQ(struct VirtmmioDev *dev, HWI_PROC_FUNC handle, void *argDev, const char *devName)
191d6aed566Sopenharmony_ci{
192d6aed566Sopenharmony_ci    uint32_t ret;
193d6aed566Sopenharmony_ci    HwiIrqParam param = {0, argDev, devName};
194d6aed566Sopenharmony_ci
195d6aed566Sopenharmony_ci    ret = LOS_HwiCreate(dev->irq, OS_HWI_PRIO_HIGHEST, IRQF_SHARED, handle, &param);
196d6aed566Sopenharmony_ci    if (ret != 0) {
197d6aed566Sopenharmony_ci        PRINT_ERR("virtio-mmio %s IRQ register failed: %u\n", devName, ret);
198d6aed566Sopenharmony_ci        return false;
199d6aed566Sopenharmony_ci    }
200d6aed566Sopenharmony_ci
201d6aed566Sopenharmony_ci    HalIrqUnmask(dev->irq);
202d6aed566Sopenharmony_ci    dev->irq |= ~_IRQ_MASK;
203d6aed566Sopenharmony_ci    return true;
204d6aed566Sopenharmony_ci}
205