/**************************************************************************** * drivers/rpmsg/rpmsg_virtio_ivshmem.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define RPMSG_VIRTIO_IVSHMEM_SHMEM_BAR 2 #define RPMSG_VIRTIO_IVSHMEM_READY 0x1 #define RPMSG_VIRTIO_IVSHMEM_WORK_DELAY 1 #define RPMSG_VIRTIO_VRING_ALIGNMENT 8 /**************************************************************************** * Private Types ****************************************************************************/ struct rpmsg_virtio_ivshmem_mem_s { volatile uint64_t basem; volatile uint32_t seqs; volatile uint32_t seqm; volatile uint32_t cmds; volatile uint32_t cmdm; struct rpmsg_virtio_rsc_s rsc; }; struct rpmsg_virtio_ivshmem_dev_s { struct rpmsg_virtio_s dev; rpmsg_virtio_callback_t callback; FAR void *arg; uint32_t seq; FAR struct rpmsg_virtio_ivshmem_mem_s *shmem; size_t shmem_size; struct simple_addrenv_s addrenv; int master; char cpuname[RPMSG_NAME_SIZE + 1]; FAR struct pci_device_s *ivshmem; /* Work queue for transmit */ struct work_s worker; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static const FAR char * rpmsg_virtio_ivshmem_get_cpuname(FAR struct rpmsg_virtio_s *dev); static FAR struct rpmsg_virtio_rsc_s * rpmsg_virtio_ivshmem_get_resource(FAR struct rpmsg_virtio_s *dev); static int rpmsg_virtio_ivshmem_is_master(FAR struct rpmsg_virtio_s *dev); static int rpmsg_virtio_ivshmem_notify(FAR struct rpmsg_virtio_s *dev, uint32_t notifyid); static int rpmsg_virtio_ivshmem_register_callback(FAR struct rpmsg_virtio_s *dev, rpmsg_virtio_callback_t callback, FAR void *arg); static int rpmsg_virtio_ivshmem_probe(FAR struct pci_device_s *dev); static void rpmsg_virtio_ivshmem_remove(FAR struct pci_device_s *dev); /**************************************************************************** * Private Data ****************************************************************************/ static const struct rpmsg_virtio_ops_s g_rpmsg_virtio_ivshmem_ops = { .get_cpuname = rpmsg_virtio_ivshmem_get_cpuname, .get_resource = rpmsg_virtio_ivshmem_get_resource, .is_master = rpmsg_virtio_ivshmem_is_master, .notify = rpmsg_virtio_ivshmem_notify, .register_callback = rpmsg_virtio_ivshmem_register_callback, }; static const struct pci_device_id_s g_rpmsg_virtio_ivshmem_ids[] = { { PCI_DEVICE(0x1af4, 0x1110) }, { 0, } }; static struct pci_driver_s g_rpmsg_virtio_ivshmem_drv = { g_rpmsg_virtio_ivshmem_ids, /* PCI id_tables */ rpmsg_virtio_ivshmem_probe, /* Probe function */ rpmsg_virtio_ivshmem_remove, /* Remove function */ }; static int g_rpmsg_virtio_ivshmem_idx = 0; /**************************************************************************** * Private Functions ****************************************************************************/ static const FAR char * rpmsg_virtio_ivshmem_get_cpuname(FAR struct rpmsg_virtio_s *dev) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = (FAR struct rpmsg_virtio_ivshmem_dev_s *)dev; return priv->cpuname; } static FAR struct rpmsg_virtio_rsc_s * rpmsg_virtio_ivshmem_get_resource(FAR struct rpmsg_virtio_s *dev) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = (FAR struct rpmsg_virtio_ivshmem_dev_s *)dev; FAR struct rpmsg_virtio_rsc_s *rsc; rsc = &priv->shmem->rsc; if (priv->master) { memset(priv->shmem, 0, priv->shmem_size); rsc->rpmsg_vdev.id = VIRTIO_ID_RPMSG; rsc->rpmsg_vdev.dfeatures = 1 << VIRTIO_RPMSG_F_NS | 1 << VIRTIO_RPMSG_F_ACK | 1 << VIRTIO_RPMSG_F_BUFSZ; rsc->rpmsg_vdev.config_len = sizeof(struct fw_rsc_config); rsc->rpmsg_vdev.num_of_vrings = 2; rsc->rpmsg_vring0.da = 0; rsc->rpmsg_vring0.align = RPMSG_VIRTIO_VRING_ALIGNMENT; rsc->rpmsg_vring0.num = CONFIG_RPMSG_VIRTIO_IVSHMEM_BUFFNUM; rsc->rpmsg_vring1.da = 0; rsc->rpmsg_vring1.align = RPMSG_VIRTIO_VRING_ALIGNMENT; rsc->rpmsg_vring1.num = CONFIG_RPMSG_VIRTIO_IVSHMEM_BUFFNUM; rsc->config.r2h_buf_size = CONFIG_RPMSG_VIRTIO_IVSHMEM_BUFFSIZE; rsc->config.h2r_buf_size = CONFIG_RPMSG_VIRTIO_IVSHMEM_BUFFSIZE; priv->shmem->basem = (uint64_t)(uintptr_t)priv->shmem; } else { /* Wait untils master is ready, salve need use master base to * initialize addrenv. */ while (priv->shmem->basem == 0) { usleep(1000); } priv->addrenv.va = (uint64_t)(uintptr_t)priv->shmem; priv->addrenv.pa = priv->shmem->basem; priv->addrenv.size = priv->shmem_size; simple_addrenv_initialize(&priv->addrenv); priv->shmem->basem = 0; } return rsc; } static int rpmsg_virtio_ivshmem_is_master(FAR struct rpmsg_virtio_s *dev) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = (FAR struct rpmsg_virtio_ivshmem_dev_s *)dev; return priv->master; } static int rpmsg_virtio_ivshmem_notify(FAR struct rpmsg_virtio_s *dev, uint32_t vqid) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = (FAR struct rpmsg_virtio_ivshmem_dev_s *)dev; if (priv->master) { priv->shmem->seqm++; } else { priv->shmem->seqs++; } return 0; } static int rpmsg_virtio_ivshmem_register_callback(FAR struct rpmsg_virtio_s *dev, rpmsg_virtio_callback_t callback, FAR void *arg) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = (FAR struct rpmsg_virtio_ivshmem_dev_s *)dev; priv->callback = callback; priv->arg = arg; return 0; } /**************************************************************************** * Name: rpmsg_virtio_ivshmem_work ****************************************************************************/ static void rpmsg_virtio_ivshmem_work(FAR void *arg) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv = arg; bool should_notify = false; if (priv->master && priv->seq != priv->shmem->seqs) { priv->seq = priv->shmem->seqs; should_notify = true; } else if (!priv->master && priv->seq != priv->shmem->seqm) { priv->seq = priv->shmem->seqm; should_notify = true; } if (should_notify && priv->callback != NULL) { priv->callback(priv->arg, RPMSG_VIRTIO_NOTIFY_ALL); } work_queue(HPWORK, &priv->worker, rpmsg_virtio_ivshmem_work, priv, RPMSG_VIRTIO_IVSHMEM_WORK_DELAY); } /**************************************************************************** * Name: rpmsg_virtio_ivshmem_get_info ****************************************************************************/ static int rpmsg_virtio_ivshmem_get_info(FAR char *cpuname, FAR int *master) { FAR const char *name = CONFIG_RPMSG_VIRTIO_IVSHMEM_NAME; int start = 0; int i; int j; for (i = 0, j = 0; name[start] != '\0'; i++) { if (name[i] == ';' || name[i] == '\0') { if (j++ == g_rpmsg_virtio_ivshmem_idx) { snprintf(cpuname, RPMSG_NAME_SIZE, "%.*s", i - start - 2, &name[start]); *master = name[i - 1] == 'm'; return 0; } start = i + 1; } } return -ENODEV; } /**************************************************************************** * Name: rpmsg_virtio_ivshmem_probe ****************************************************************************/ static int rpmsg_virtio_ivshmem_probe(FAR struct pci_device_s *dev) { FAR struct rpmsg_virtio_ivshmem_dev_s *priv; int ret; priv = kmm_zalloc(sizeof(*priv)); if (priv == NULL) { return -ENOMEM; } /* Do the rpmsg virtio ivshmem init */ priv->dev.ops = &g_rpmsg_virtio_ivshmem_ops; priv->ivshmem = dev; ret = rpmsg_virtio_ivshmem_get_info(priv->cpuname, &priv->master); if (ret < 0) { goto err_priv; } /* Configure the ivshmem device and get share memory address */ ret = pci_enable_device(dev); if (ret < 0) { pcierr("Enable device failed, ret=%d\n", ret); goto err_priv; } pci_set_master(dev); priv->shmem = (FAR struct rpmsg_virtio_ivshmem_mem_s *) pci_map_bar(dev, RPMSG_VIRTIO_IVSHMEM_SHMEM_BAR); if (priv->shmem == NULL) { ret = -ENOTSUP; pcierr("Device not support share memory bar\n"); goto err_master; } priv->shmem_size = pci_resource_len(dev, RPMSG_VIRTIO_IVSHMEM_SHMEM_BAR); pciinfo("shmem addr=%p size=%zu\n", priv->shmem, priv->shmem_size); /* Do rpmsg virtio initialize */ ret = rpmsg_virtio_initialize(&priv->dev); if (ret < 0) { pcierr("rpmsg virtio intialize failed, ret=%d\n", ret); goto err_master; } work_queue(HPWORK, &priv->worker, rpmsg_virtio_ivshmem_work, priv, 0); g_rpmsg_virtio_ivshmem_idx++; return ret; err_master: pci_clear_master(dev); pci_disable_device(dev); err_priv: kmm_free(priv); return ret; } /**************************************************************************** * Name: rpmsg_virtio_ivshmem_remove ****************************************************************************/ static void rpmsg_virtio_ivshmem_remove(FAR struct pci_device_s *dev) { pciwarn("Not support remove for now\n"); } /**************************************************************************** * Public Functions ****************************************************************************/ int pci_register_rpmsg_virtio_ivshmem_driver(void) { return pci_register_driver(&g_rpmsg_virtio_ivshmem_drv); }