Skip to content

Commit e6732a8

Browse files
committed
[DM/MFD] Add QEMU EDU for PCI study
EDU Support DMA (lower 32 bits) and factorial, MSI-X, user can change device or driver to study PCI. Signed-off-by: GuEe-GUI <2991707448@qq.com>
1 parent 39d9900 commit e6732a8

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

components/drivers/mfd/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ menuconfig RT_USING_MFD
33
depends on RT_USING_DM
44
default n
55

6+
config RT_MFD_EDU
7+
bool "Educational device driver"
8+
depends on RT_USING_MFD
9+
depends on RT_USING_PCI
10+
depends on RT_USING_DMA
11+
default n
12+
613
config RT_MFD_SYSCON
714
bool "System Controller Register R/W"
815
depends on RT_USING_MFD

components/drivers/mfd/SConscript

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ cwd = GetCurrentDir()
99
CPPPATH = [cwd + '/../include']
1010
src = []
1111

12+
if GetDepend(['RT_MFD_EDU']):
13+
src += ['mfd-edu.c']
14+
1215
if GetDepend(['RT_MFD_SYSCON']):
1316
src += ['mfd-syscon.c']
1417

components/drivers/mfd/mfd-edu.c

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/*
2+
* Copyright (c) 2006-2023, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2023-02-25 GuEe-GUI the first version
9+
*/
10+
11+
#include <rthw.h>
12+
#include <rtthread.h>
13+
#include <rtdevice.h>
14+
15+
#define DBG_TAG "mfd.edu"
16+
#define DBG_LVL DBG_INFO
17+
#include <rtdbg.h>
18+
19+
#include <cpuport.h>
20+
21+
#define PCI_EDU_REGS_BAR 0
22+
#define EDU_REG_VERSION 0x00
23+
#define EDU_REG_CARD_LIVENESS 0x04
24+
#define EDU_REG_VALUE 0x08
25+
#define EDU_REG_STATUS 0x20
26+
#define EDU_REG_STATUS_IRQ 0x80
27+
#define EDU_REG_IRQ_STATUS 0x24
28+
#define EDU_REG_ISR_FACT 0x00000001
29+
#define EDU_REG_ISR_DMA 0x00000100
30+
#define EDU_REG_IRQ_RAISE 0x60
31+
#define EDU_REG_IRQ_ACK 0x64
32+
#define EDU_REG_DMA_SRC 0x80
33+
#define EDU_REG_DMA_DST 0x88
34+
#define EDU_REG_DMA_SIZE 0x90
35+
#define EDU_REG_DMA_CMD 0x98
36+
#define EDU_DMA_CMD_RUN 0x1
37+
#define EDU_DMA_CMD_TO_PCI 0x0
38+
#define EDU_DMA_CMD_FROM_PCI 0x2
39+
#define EDU_DMA_CMD_IRQ 0x4
40+
41+
#define EDU_FACTORIAL_ACK 0x00000001
42+
43+
#define EDU_DMA_ACK 0x00000100
44+
#define EDU_DMA_FREE (~0UL)
45+
#define EDU_DMA_BASE 0x40000
46+
#define EDU_DMA_SIZE ((rt_size_t)(4096 - 1))
47+
#define EDU_DMA_POLL_SIZE 128
48+
49+
struct edu_device
50+
{
51+
struct rt_device parent;
52+
struct rt_dma_controller dma_ctrl;
53+
54+
void *regs;
55+
rt_uint32_t ack;
56+
rt_bool_t dma_work;
57+
58+
rt_ubase_t dma_src;
59+
rt_ubase_t dma_dst;
60+
61+
struct rt_mutex lock;
62+
struct rt_completion done;
63+
};
64+
65+
#define raw_to_edu_device(raw) rt_container_of(raw, struct edu_device, parent)
66+
#define raw_to_edu_dma(raw) rt_container_of(raw, struct edu_device, dma_ctrl)
67+
68+
rt_inline rt_uint32_t edu_readl(struct edu_device *edu, int offset)
69+
{
70+
return HWREG32(edu->regs + offset);
71+
}
72+
73+
rt_inline void edu_writel(struct edu_device *edu, int offset, rt_uint32_t value)
74+
{
75+
HWREG32(edu->regs + offset) = value;
76+
}
77+
78+
static rt_err_t edu_dma_start(struct rt_dma_chan *chan)
79+
{
80+
rt_size_t len;
81+
rt_ubase_t dma_addr_src, dma_addr_dst;
82+
struct edu_device *edu = raw_to_edu_dma(chan->ctrl);
83+
84+
rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
85+
86+
edu->ack = EDU_DMA_ACK;
87+
edu->dma_work = RT_TRUE;
88+
89+
len = chan->transfer.buffer_len;
90+
dma_addr_src = chan->transfer.src_addr;
91+
dma_addr_dst = chan->transfer.dst_addr;
92+
93+
while ((rt_ssize_t)len > 0 && edu->dma_work)
94+
{
95+
rt_uint32_t cmd = EDU_DMA_CMD_RUN;
96+
rt_uint32_t blen = rt_min_t(rt_ssize_t, EDU_DMA_SIZE, len);
97+
98+
if (blen > EDU_DMA_POLL_SIZE)
99+
{
100+
cmd |= EDU_DMA_CMD_IRQ;
101+
}
102+
103+
edu_writel(edu, EDU_REG_DMA_SRC, dma_addr_src);
104+
edu_writel(edu, EDU_REG_DMA_DST, EDU_DMA_BASE);
105+
edu_writel(edu, EDU_REG_DMA_SIZE, blen);
106+
edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_TO_PCI);
107+
108+
if (cmd & EDU_DMA_CMD_IRQ)
109+
{
110+
rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
111+
}
112+
else
113+
{
114+
while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN)
115+
{
116+
rt_hw_cpu_relax();
117+
}
118+
}
119+
120+
edu_writel(edu, EDU_REG_DMA_SRC, EDU_DMA_BASE);
121+
edu_writel(edu, EDU_REG_DMA_DST, dma_addr_dst);
122+
edu_writel(edu, EDU_REG_DMA_SIZE, blen);
123+
edu_writel(edu, EDU_REG_DMA_CMD, cmd | EDU_DMA_CMD_FROM_PCI);
124+
125+
if (cmd & EDU_DMA_CMD_IRQ)
126+
{
127+
rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
128+
}
129+
else
130+
{
131+
while (edu_readl(edu, EDU_REG_DMA_CMD) & EDU_DMA_CMD_RUN)
132+
{
133+
rt_hw_cpu_relax();
134+
}
135+
}
136+
137+
len -= blen;
138+
dma_addr_src += blen;
139+
dma_addr_dst += blen;
140+
}
141+
142+
rt_mutex_release(&edu->lock);
143+
144+
rt_dma_chan_done(chan, chan->transfer.buffer_len);
145+
146+
return RT_EOK;
147+
}
148+
149+
static rt_err_t edu_dma_stop(struct rt_dma_chan *chan)
150+
{
151+
struct edu_device *edu = raw_to_edu_dma(chan->ctrl);
152+
153+
edu->dma_work = RT_FALSE;
154+
155+
return RT_EOK;
156+
}
157+
158+
static rt_err_t edu_dma_config(struct rt_dma_chan *chan,
159+
struct rt_dma_slave_config *conf)
160+
{
161+
return RT_EOK;
162+
}
163+
164+
static rt_err_t edu_dma_prep_memcpy(struct rt_dma_chan *chan,
165+
rt_ubase_t dma_addr_src, rt_ubase_t dma_addr_dst, rt_size_t len)
166+
{
167+
return RT_EOK;
168+
}
169+
170+
const static struct rt_dma_controller_ops edu_dma_ops =
171+
{
172+
.start = edu_dma_start,
173+
.stop = edu_dma_stop,
174+
.config = edu_dma_config,
175+
.prep_memcpy = edu_dma_prep_memcpy,
176+
};
177+
178+
static rt_ssize_t edu_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
179+
{
180+
rt_uint32_t number;
181+
struct edu_device *edu = raw_to_edu_device(dev);
182+
183+
rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
184+
185+
number = edu_readl(edu, EDU_REG_VALUE);
186+
187+
rt_mutex_release(&edu->lock);
188+
189+
rt_memcpy(buffer, &number, rt_min(sizeof(number), size));
190+
191+
return rt_min(sizeof(number), size);
192+
}
193+
194+
static rt_ssize_t edu_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
195+
{
196+
rt_uint32_t number = 0;
197+
struct edu_device *edu = raw_to_edu_device(dev);
198+
199+
rt_memcpy(&number, buffer, rt_min(sizeof(number), size));
200+
201+
rt_mutex_take(&edu->lock, RT_WAITING_FOREVER);
202+
203+
edu->ack = EDU_FACTORIAL_ACK;
204+
edu_writel(edu, EDU_REG_STATUS, EDU_REG_STATUS_IRQ);
205+
edu_writel(edu, EDU_REG_VALUE, number);
206+
207+
rt_completion_wait(&edu->done, RT_WAITING_FOREVER);
208+
209+
rt_mutex_release(&edu->lock);
210+
211+
return rt_min(sizeof(number), size);
212+
}
213+
214+
#ifdef RT_USING_DEVICE_OPS
215+
const static struct rt_device_ops edu_ops =
216+
{
217+
.read = edu_read,
218+
.write = edu_write,
219+
};
220+
#endif
221+
222+
static void edu_isr(int irqno, void *param)
223+
{
224+
struct edu_device *edu = param;
225+
226+
if (edu_readl(edu, EDU_REG_IRQ_STATUS) & (EDU_REG_ISR_FACT | EDU_REG_ISR_DMA))
227+
{
228+
edu_writel(edu, EDU_REG_IRQ_ACK, edu->ack);
229+
rt_completion_done(&edu->done);
230+
}
231+
}
232+
233+
static rt_err_t edu_probe(struct rt_pci_device *pdev)
234+
{
235+
rt_err_t err;
236+
struct edu_device *edu = rt_calloc(1, sizeof(*edu));
237+
238+
if (!edu)
239+
{
240+
return -RT_ENOMEM;
241+
}
242+
243+
edu->regs = rt_pci_iomap(pdev, PCI_EDU_REGS_BAR);
244+
245+
if (!edu->regs)
246+
{
247+
err = -RT_EIO;
248+
goto _fail;
249+
}
250+
251+
edu->dma_ctrl.dev = &pdev->parent;
252+
edu->dma_ctrl.ops = &edu_dma_ops;
253+
rt_dma_controller_add_direction(&edu->dma_ctrl, RT_DMA_MEM_TO_MEM);
254+
/* Config in QEMU option: -device edu,dma_mask=0xffffffff */
255+
rt_dma_controller_set_addr_mask(&edu->dma_ctrl, RT_DMA_ADDR_MASK(32));
256+
257+
if ((err = rt_dma_controller_register(&edu->dma_ctrl)))
258+
{
259+
goto _fail;
260+
}
261+
262+
edu->parent.type = RT_Device_Class_Char;
263+
#ifdef RT_USING_DEVICE_OPS
264+
edu->parent.ops = &edu_ops;
265+
#else
266+
edu->parent.read = edu_read;
267+
edu->parent.write = edu_write;
268+
#endif
269+
270+
if ((err = rt_device_register(&edu->parent, "edu", RT_DEVICE_FLAG_RDWR)))
271+
{
272+
goto _fail;
273+
}
274+
275+
rt_hw_interrupt_install(pdev->irq, edu_isr, edu, "edu");
276+
rt_pci_irq_unmask(pdev);
277+
278+
pdev->parent.user_data = edu;
279+
280+
rt_mutex_init(&edu->lock, "edu", RT_IPC_FLAG_PRIO);
281+
rt_completion_init(&edu->done);
282+
283+
LOG_D("EDU PCI device v%d.%d", edu_readl(edu, EDU_REG_VERSION) >> 16,
284+
(edu_readl(edu, EDU_REG_VERSION) >> 8) & 0xff);
285+
286+
return RT_EOK;
287+
288+
_fail:
289+
if (edu->dma_ctrl.ops)
290+
{
291+
rt_dma_controller_unregister(&edu->dma_ctrl);
292+
}
293+
294+
if (edu->regs)
295+
{
296+
rt_iounmap(edu->regs);
297+
}
298+
299+
rt_free(edu);
300+
301+
return err;
302+
}
303+
304+
static rt_err_t edu_remove(struct rt_pci_device *pdev)
305+
{
306+
struct edu_device *edu = pdev->parent.user_data;
307+
308+
rt_dma_controller_unregister(&edu->dma_ctrl);
309+
rt_device_unregister(&edu->parent);
310+
311+
rt_iounmap(edu->regs);
312+
rt_free(edu);
313+
314+
return RT_EOK;
315+
}
316+
317+
static const struct rt_pci_device_id edu_ids[] =
318+
{
319+
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_QEMU, 0x11e8), },
320+
{ /* sentinel */ }
321+
};
322+
323+
static struct rt_pci_driver edu_driver =
324+
{
325+
.name = "edu",
326+
327+
.ids = edu_ids,
328+
.probe = edu_probe,
329+
.remove = edu_remove,
330+
};
331+
RT_PCI_DRIVER_EXPORT(edu_driver);

0 commit comments

Comments
 (0)