| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2023-02-25 GuEe-GUI the first version
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #define DBG_TAG "mfd.edu"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <cpuport.h>
- #define PCI_EDU_REGS_BAR 0
- #define EDU_REG_VERSION 0x00
- #define EDU_REG_CARD_LIVENESS 0x04
- #define EDU_REG_VALUE 0x08
- #define EDU_REG_STATUS 0x20
- #define EDU_REG_STATUS_IRQ 0x80
- #define EDU_REG_IRQ_STATUS 0x24
- #define EDU_REG_ISR_FACT 0x00000001
- #define EDU_REG_ISR_DMA 0x00000100
- #define EDU_REG_IRQ_RAISE 0x60
- #define EDU_REG_IRQ_ACK 0x64
- #define EDU_REG_DMA_SRC 0x80
- #define EDU_REG_DMA_DST 0x88
- #define EDU_REG_DMA_SIZE 0x90
- #define EDU_REG_DMA_CMD 0x98
- #define EDU_DMA_CMD_RUN 0x1
- #define EDU_DMA_CMD_TO_PCI 0x0
- #define EDU_DMA_CMD_FROM_PCI 0x2
- #define EDU_DMA_CMD_IRQ 0x4
- #define EDU_FACTORIAL_ACK 0x00000001
- #define EDU_DMA_ACK 0x00000100
- #define EDU_DMA_FREE (~0UL)
- #define EDU_DMA_BASE 0x40000
- #define EDU_DMA_SIZE ((rt_size_t)(4096 - 1))
- #define EDU_DMA_POLL_SIZE 128
- struct edu_device
- {
- struct rt_device parent;
- struct rt_dma_controller dma_ctrl;
- void *regs;
- rt_uint32_t ack;
- rt_bool_t dma_work;
- struct rt_mutex lock;
- struct rt_completion done;
- };
- #define raw_to_edu_device(raw) rt_container_of(raw, struct edu_device, parent)
- #define raw_to_edu_dma(raw) rt_container_of(raw, struct edu_device, dma_ctrl)
- rt_inline rt_uint32_t edu_readl(struct edu_device *edu, int offset)
- {
- return HWREG32(edu->regs + offset);
- }
- rt_inline void edu_writel(struct edu_device *edu, int offset, rt_uint32_t value)
- {
- HWREG32(edu->regs + offset) = value;
- }
- static rt_err_t edu_dma_start(struct rt_dma_chan *chan)
- {
- rt_size_t len;
- rt_ubase_t dma_addr_src, dma_addr_dst;
- struct edu_device *edu = raw_to_edu_dma(chan->ctrl);
- rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
- edu->ack = EDU_DMA_ACK;
- edu->dma_work = RT_TRUE;
- len = chan->transfer.buffer_len;
- dma_addr_src = chan->transfer.src_addr;
- dma_addr_dst = chan->transfer.dst_addr;
- while ((rt_ssize_t)len > 0 && edu->dma_work)
- {
- rt_uint32_t cmd = EDU_DMA_CMD_RUN;
- rt_uint32_t blen = rt_min_t(rt_size_t, EDU_DMA_SIZE, len);
- if (blen > EDU_DMA_POLL_SIZE)
- {
- cmd |= EDU_DMA_CMD_IRQ;
- }
- edu_writel(edu, EDU_REG_DMA_SRC, dma_addr_src);
- edu_writel(edu, EDU_REG_DMA_DST, EDU_DMA_BASE);
- edu_writel(edu, EDU_REG_DMA_SIZE, blen);
- edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_TO_PCI);
- if (cmd & EDU_DMA_CMD_IRQ)
- {
- rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
- }
- else
- {
- while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN)
- {
- rt_hw_cpu_relax();
- }
- }
- edu_writel(edu, EDU_REG_DMA_SRC, EDU_DMA_BASE);
- edu_writel(edu, EDU_REG_DMA_DST, dma_addr_dst);
- edu_writel(edu, EDU_REG_DMA_SIZE, blen);
- edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_FROM_PCI);
- if (cmd & EDU_DMA_CMD_IRQ)
- {
- rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
- }
- else
- {
- while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN)
- {
- rt_hw_cpu_relax();
- }
- }
- len -= blen;
- dma_addr_src += blen;
- dma_addr_dst += blen;
- }
- rt_mutex_release(&edu->lock);
- rt_dma_chan_done(chan, chan->transfer.buffer_len - len);
- return RT_EOK;
- }
- static rt_err_t edu_dma_stop(struct rt_dma_chan *chan)
- {
- struct edu_device *edu = raw_to_edu_dma(chan->ctrl);
- edu->dma_work = RT_FALSE;
- return RT_EOK;
- }
- static rt_err_t edu_dma_config(struct rt_dma_chan *chan,
- struct rt_dma_slave_config *conf)
- {
- return RT_EOK;
- }
- static rt_err_t edu_dma_prep_memcpy(struct rt_dma_chan *chan,
- rt_ubase_t dma_addr_src, rt_ubase_t dma_addr_dst, rt_size_t len)
- {
- return RT_EOK;
- }
- const static struct rt_dma_controller_ops edu_dma_ops =
- {
- .start = edu_dma_start,
- .stop = edu_dma_stop,
- .config = edu_dma_config,
- .prep_memcpy = edu_dma_prep_memcpy,
- };
- static rt_ssize_t edu_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
- {
- rt_uint32_t number;
- struct edu_device *edu = raw_to_edu_device(dev);
- rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
- number = edu_readl(edu, EDU_REG_VALUE);
- rt_mutex_release(&edu->lock);
- rt_memcpy(buffer, &number, rt_min(sizeof(number), size));
- return rt_min(sizeof(number), size);
- }
- static rt_ssize_t edu_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
- {
- rt_uint32_t number = 0;
- struct edu_device *edu = raw_to_edu_device(dev);
- rt_memcpy(&number, buffer, rt_min(sizeof(number), size));
- rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
- edu->ack = EDU_FACTORIAL_ACK;
- edu_writel(edu, EDU_REG_STATUS, EDU_REG_STATUS_IRQ);
- edu_writel(edu, EDU_REG_VALUE, number);
- rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
- rt_mutex_release(&edu->lock);
- return rt_min(sizeof(number), size);
- }
- #ifdef RT_USING_DEVICE_OPS
- const static struct rt_device_ops edu_ops =
- {
- .read = edu_read,
- .write = edu_write,
- };
- #endif
- static void edu_isr(int irqno, void *param)
- {
- struct edu_device *edu = param;
- if (edu_readl(edu, EDU_REG_IRQ_STATUS) & (EDU_REG_ISR_FACT | EDU_REG_ISR_DMA))
- {
- edu_writel(edu, EDU_REG_IRQ_ACK, edu->ack);
- rt_completion_done(&edu->done);
- }
- }
- static rt_err_t edu_probe(struct rt_pci_device *pdev)
- {
- rt_err_t err;
- struct edu_device *edu = rt_calloc(1, sizeof(*edu));
- if (!edu)
- {
- return -RT_ENOMEM;
- }
- edu->regs = rt_pci_iomap(pdev, PCI_EDU_REGS_BAR);
- if (!edu->regs)
- {
- err = -RT_EIO;
- goto _fail;
- }
- edu->dma_ctrl.dev = &pdev->parent;
- edu->dma_ctrl.ops = &edu_dma_ops;
- rt_dma_controller_add_direction(&edu->dma_ctrl, RT_DMA_MEM_TO_MEM);
- /* Config in QEMU option: -device edu,dma_mask=0xffffffff */
- rt_dma_controller_set_addr_mask(&edu->dma_ctrl, RT_DMA_ADDR_MASK(32));
- if ((err = rt_dma_controller_register(&edu->dma_ctrl)))
- {
- goto _fail;
- }
- edu->parent.type = RT_Device_Class_Char;
- #ifdef RT_USING_DEVICE_OPS
- edu->parent.ops = &edu_ops;
- #else
- edu->parent.read = edu_read;
- edu->parent.write = edu_write;
- #endif
- if ((err = rt_device_register(&edu->parent, "edu", RT_DEVICE_FLAG_RDWR)))
- {
- goto _free_dma;
- }
- pdev->parent.user_data = edu;
- rt_mutex_init(&edu->lock, "edu", RT_IPC_FLAG_PRIO);
- rt_completion_init(&edu->done);
- rt_hw_interrupt_install(pdev->irq, edu_isr, edu, "edu");
- rt_pci_irq_unmask(pdev);
- LOG_D("EDU PCI device v%d.%d", edu_readl(edu, EDU_REG_VERSION) >> 16,
- (edu_readl(edu, EDU_REG_VERSION) >> 8) & 0xff);
- return RT_EOK;
- _free_dma:
- rt_dma_controller_unregister(&edu->dma_ctrl);
- _fail:
- if (edu->regs)
- {
- rt_iounmap(edu->regs);
- }
- rt_free(edu);
- return err;
- }
- static rt_err_t edu_remove(struct rt_pci_device *pdev)
- {
- struct edu_device *edu = pdev->parent.user_data;
- /* INTx is shared, don't mask all */
- rt_hw_interrupt_umask(pdev->irq);
- rt_pci_irq_mask(pdev);
- rt_dma_controller_unregister(&edu->dma_ctrl);
- rt_device_unregister(&edu->parent);
- rt_mutex_detach(&edu->lock);
- rt_iounmap(edu->regs);
- rt_free(edu);
- return RT_EOK;
- }
- static const struct rt_pci_device_id edu_ids[] =
- {
- { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_QEMU, 0x11e8), },
- { /* sentinel */ }
- };
- static struct rt_pci_driver edu_driver =
- {
- .name = "edu",
- .ids = edu_ids,
- .probe = edu_probe,
- .remove = edu_remove,
- };
- RT_PCI_DRIVER_EXPORT(edu_driver);
|