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, ¶m); 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