/* * Copyright (C) 2016-2018 Spreadtrum Communications Inc. * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include "edma_engine.h" #include "mchn.h" static struct mchn_info_t g_mchn; struct mchn_info_t *mchn_info(void) { return &g_mchn; } struct mchn_ops_t *mchn_ops(int channel) { return g_mchn.ops[channel]; } int mbuf_link_alloc(int chn, struct mbuf_t **head, struct mbuf_t **tail, int *num) { int i; struct mbuf_t *cur, *head__, *tail__ = NULL; struct mchn_info_t *mchn = mchn_info(); struct buffer_pool *pool = &(mchn->chn_public[chn].pool); PCIE_INFO("pool=%p, chn=%d, free=%d\n", pool, chn, pool->free); spin_lock_irqsave(&(pool->lock), pool->irq_flags); if ((*num <= 0) || (pool->free <= 0)) { PCIE_ERR("[+]%s err, num %d, free %d)\n", __func__, *num, pool->free); *num = 0; *head = *tail = NULL; spin_unlock_irqrestore(&(pool->lock), pool->irq_flags); return -1; } if (*num > pool->free) *num = pool->free; for (i = 0, cur = head__ = pool->head; i < *num; i++) { if (i == (*num - 1)) tail__ = cur; cur = cur->next; } *head = head__; tail__->next = NULL; *tail = tail__; pool->free -= *num; pool->head = cur; spin_unlock_irqrestore(&(pool->lock), pool->irq_flags); return 0; } EXPORT_SYMBOL(mbuf_link_alloc); int mbuf_link_free(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct mchn_info_t *mchn = mchn_info(); struct buffer_pool *pool = &(mchn->chn_public[chn].pool); if ((head == NULL) || (tail == NULL) || (num == 0) || (tail->next != 0)) { PCIE_ERR("%s@%d(%d, 0x%p, 0x%p, %d)err\n", __func__, __LINE__, chn, head, tail, num); return -1; } spin_lock_irqsave(&(pool->lock), pool->irq_flags); tail->next = pool->head; pool->head = head; pool->free += num; spin_unlock_irqrestore(&(pool->lock), pool->irq_flags); return 0; } EXPORT_SYMBOL(mbuf_link_free); int mbuf_pool_init(struct buffer_pool *pool, int size, int payload) { int i; struct mbuf_t *mbuf, *next; pool->size = size; pool->payload = payload; spin_lock_init(&(pool->lock)); pool->mem = kmalloc((sizeof(struct mbuf_t) + payload) * size, GFP_KERNEL); PCIE_INFO("mbuf_pool->mem:0x%lx\n", (unsigned long)virt_to_phys(pool->mem)); memset(pool->mem, 0x00, (sizeof(struct mbuf_t) + payload) * size); pool->head = (struct mbuf_t *) (pool->mem); for (i = 0, mbuf = (struct mbuf_t *) (pool->head); i < (size - 1); i++) { mbuf->seq = i; PCIE_INFO("%s mbuf[%d]:{0x%lx, 0x%lx}\n", __func__, i, (unsigned long)mbuf, (unsigned long)virt_to_phys(mbuf)); next = (struct mbuf_t *) ((char *)mbuf + sizeof(struct mbuf_t) + payload); mbuf->buf = (char *)mbuf + sizeof(struct mbuf_t); mbuf->len = payload; mbuf->next = next; mbuf = next; } PCIE_INFO("%s mbuf[%d]:{0x%lx, 0x%lx}\n", __func__, i, (unsigned long)mbuf, (unsigned long)virt_to_phys(mbuf)); mbuf->seq = i; mbuf->buf = (char *)mbuf + sizeof(struct mbuf_t); mbuf->len = payload; mbuf->next = NULL; pool->free = size; PCIE_INFO("%s(0x%p, %d)\n", __func__, pool, pool->free); return 0; } int mbuf_pool_deinit(struct buffer_pool *pool) { memset(pool->mem, 0x00, (sizeof(struct mbuf_t) + pool->payload) * pool->size); kfree(pool->mem); return 0; } int mchn_hw_pop_link(int chn, void *head, void *tail, int num) { struct mchn_info_t *mchn = mchn_info(); if (mchn->ops[chn] == NULL) { WARN_ON(1); return -1; } if (mchn->ops[chn]->hif_type == HW_TYPE_PCIE) edma_tp_count(chn, head, tail, num); return mchn->ops[chn]->pop_link(chn, (struct mbuf_t *)head, (struct mbuf_t *)tail, num); } EXPORT_SYMBOL(mchn_hw_pop_link); int mchn_hw_tx_complete(int chn, int timeout) { struct mchn_info_t *mchn = mchn_info(); if (mchn->ops[chn] == NULL) WARN_ON(1); if (mchn->ops[chn]->tx_complete) mchn->ops[chn]->tx_complete(chn, timeout); return 0; } EXPORT_SYMBOL(mchn_hw_tx_complete); int mchn_hw_req_push_link(int chn, int need) { int ret; struct mbuf_t *head = NULL, *tail = NULL; struct mchn_info_t *mchn = mchn_info(); if (mchn->ops[chn] == NULL) return -1; ret = mchn->ops[chn]->push_link(chn, &head, &tail, &need); if (ret != 0) return ret; ret = mchn_push_link(chn, (void *)head, (void *)tail, need); return ret; } EXPORT_SYMBOL(mchn_hw_req_push_link); int mchn_hw_cb_in_irq(int chn) { if (!g_mchn.ops[chn]) { PCIE_ERR("%s: chn=%d is not register\n", __func__, chn); return -1; } return g_mchn.ops[chn]->cb_in_irq; } int mchn_hw_max_pending(int chn) { if (!g_mchn.ops[chn]) { PCIE_ERR("%s: chn=%d is not register\n", __func__, chn); return -1; } return g_mchn.ops[chn]->max_pending; } int mchn_push_link(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num) { int ret = -1; struct mchn_info_t *mchn = mchn_info(); if ((chn >= 16) || (mchn->ops[chn] == NULL) || (head == NULL) || (tail == NULL) || (num > mchn->ops[chn]->pool_size)) { WARN_ON(1); return -1; } switch (mchn->ops[chn]->hif_type) { case HW_TYPE_SDIO: break; case HW_TYPE_PCIE: if (mchn_hw_max_pending(chn) > 0) ret = edma_push_link_async(chn, (void *)head, (void *)tail, num); else ret = edma_push_link(chn, (void *)head, (void *)tail, num); break; default: break; } return ret; } EXPORT_SYMBOL(mchn_push_link); int mchn_push_link_wait_complete(int chn, struct mbuf_t *head, struct mbuf_t *tail, int num, int timeout) { int ret = -1; struct mchn_info_t *mchn = mchn_info(); if ((chn >= 32) || (mchn->ops[chn] == NULL)) { WARN_ON(1); return -1; } switch (mchn->ops[chn]->hif_type) { case HW_TYPE_SDIO: break; case HW_TYPE_PCIE: ret = edma_push_link_wait_complete(chn, (void *)head, (void *)tail, num, timeout); break; default: break; } return ret; } EXPORT_SYMBOL(mchn_push_link_wait_complete); int mchn_init(struct mchn_ops_t *ops) { int ret = -1; struct mchn_info_t *mchn = mchn_info(); PCIE_INFO("[+]%s(%d, %d)\n", __func__, ops->channel, ops->hif_type); if ((mchn->ops[ops->channel] != NULL) || ((ops->hif_type != HW_TYPE_SDIO) && (ops->hif_type != HW_TYPE_PCIE))) { PCIE_INFO("%s err, hif_type %d\n", __func__, ops->hif_type); WARN_ON(1); return -1; } mchn->ops[ops->channel] = ops; switch (ops->hif_type) { case HW_TYPE_SDIO: ret = 0; break; case HW_TYPE_PCIE: ret = edma_chn_init(ops->channel, 0, ops->inout, ops->pool_size); break; default: break; } if ((ret == 0) && (ops->pool_size > 0)) ret = mbuf_pool_init(&(mchn->chn_public[ops->channel].pool), ops->pool_size, 0); PCIE_INFO("[-]%s(%d)\n", __func__, ops->channel); return ret; } EXPORT_SYMBOL(mchn_init); int mchn_deinit(struct mchn_ops_t *ops) { int ret = 0; struct mchn_info_t *mchn = mchn_info(); PCIE_INFO("[+]%s(%d, %d)\n", __func__, ops->channel, ops->hif_type); if ((mchn->ops[ops->channel] == NULL) || ((ops->hif_type != HW_TYPE_SDIO) && (ops->hif_type != HW_TYPE_PCIE))) { PCIE_ERR("%s err\n", __func__); return -1; } switch (ops->hif_type) { case HW_TYPE_SDIO: break; case HW_TYPE_PCIE: break; default: break; } if (ops->pool_size > 0) ret = mbuf_pool_deinit(&(mchn->chn_public[ops->channel].pool)); mchn->ops[ops->channel] = NULL; PCIE_INFO("[-]%s(%d)\n", __func__, ops->channel); return ret; } EXPORT_SYMBOL(mchn_deinit);