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