1e4125c0cSAlexander Gordeev /*
2e4125c0cSAlexander Gordeev * QEMU "pci-testdev" PCI test device
3e4125c0cSAlexander Gordeev *
4e4125c0cSAlexander Gordeev * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <agordeev@redhat.com>
5e4125c0cSAlexander Gordeev *
6e4125c0cSAlexander Gordeev * This work is licensed under the terms of the GNU LGPL, version 2.
7e4125c0cSAlexander Gordeev */
8e4125c0cSAlexander Gordeev #include "pci.h"
9e4125c0cSAlexander Gordeev #include "asm/io.h"
10e4125c0cSAlexander Gordeev
11e4125c0cSAlexander Gordeev struct pci_testdev_ops {
12e4125c0cSAlexander Gordeev u8 (*io_readb)(const volatile void *addr);
13e4125c0cSAlexander Gordeev u16 (*io_readw)(const volatile void *addr);
14e4125c0cSAlexander Gordeev u32 (*io_readl)(const volatile void *addr);
15e4125c0cSAlexander Gordeev void (*io_writeb)(u8 value, volatile void *addr);
16e4125c0cSAlexander Gordeev void (*io_writew)(u16 value, volatile void *addr);
17e4125c0cSAlexander Gordeev void (*io_writel)(u32 value, volatile void *addr);
18e4125c0cSAlexander Gordeev };
19e4125c0cSAlexander Gordeev
pio_readb(const volatile void * addr)20e4125c0cSAlexander Gordeev static u8 pio_readb(const volatile void *addr)
21e4125c0cSAlexander Gordeev {
22e4125c0cSAlexander Gordeev return inb((unsigned long)addr);
23e4125c0cSAlexander Gordeev }
24e4125c0cSAlexander Gordeev
pio_readw(const volatile void * addr)25e4125c0cSAlexander Gordeev static u16 pio_readw(const volatile void *addr)
26e4125c0cSAlexander Gordeev {
27e4125c0cSAlexander Gordeev return inw((unsigned long)addr);
28e4125c0cSAlexander Gordeev }
29e4125c0cSAlexander Gordeev
pio_readl(const volatile void * addr)30e4125c0cSAlexander Gordeev static u32 pio_readl(const volatile void *addr)
31e4125c0cSAlexander Gordeev {
32e4125c0cSAlexander Gordeev return inl((unsigned long)addr);
33e4125c0cSAlexander Gordeev }
34e4125c0cSAlexander Gordeev
pio_writeb(u8 value,volatile void * addr)35e4125c0cSAlexander Gordeev static void pio_writeb(u8 value, volatile void *addr)
36e4125c0cSAlexander Gordeev {
37e4125c0cSAlexander Gordeev outb(value, (unsigned long)addr);
38e4125c0cSAlexander Gordeev }
39e4125c0cSAlexander Gordeev
pio_writew(u16 value,volatile void * addr)40e4125c0cSAlexander Gordeev static void pio_writew(u16 value, volatile void *addr)
41e4125c0cSAlexander Gordeev {
42e4125c0cSAlexander Gordeev outw(value, (unsigned long)addr);
43e4125c0cSAlexander Gordeev }
44e4125c0cSAlexander Gordeev
pio_writel(u32 value,volatile void * addr)45e4125c0cSAlexander Gordeev static void pio_writel(u32 value, volatile void *addr)
46e4125c0cSAlexander Gordeev {
47e4125c0cSAlexander Gordeev outl(value, (unsigned long)addr);
48e4125c0cSAlexander Gordeev }
49e4125c0cSAlexander Gordeev
50e4125c0cSAlexander Gordeev static struct pci_testdev_ops pci_testdev_io_ops = {
51e4125c0cSAlexander Gordeev .io_readb = pio_readb,
52e4125c0cSAlexander Gordeev .io_readw = pio_readw,
53e4125c0cSAlexander Gordeev .io_readl = pio_readl,
54e4125c0cSAlexander Gordeev .io_writeb = pio_writeb,
55e4125c0cSAlexander Gordeev .io_writew = pio_writew,
56e4125c0cSAlexander Gordeev .io_writel = pio_writel
57e4125c0cSAlexander Gordeev };
58e4125c0cSAlexander Gordeev
mmio_readb(const volatile void * addr)59e4125c0cSAlexander Gordeev static u8 mmio_readb(const volatile void *addr)
60e4125c0cSAlexander Gordeev {
61e4125c0cSAlexander Gordeev return *(const volatile u8 __force *)addr;
62e4125c0cSAlexander Gordeev }
63e4125c0cSAlexander Gordeev
mmio_readw(const volatile void * addr)64e4125c0cSAlexander Gordeev static u16 mmio_readw(const volatile void *addr)
65e4125c0cSAlexander Gordeev {
66e4125c0cSAlexander Gordeev return *(const volatile u16 __force *)addr;
67e4125c0cSAlexander Gordeev }
68e4125c0cSAlexander Gordeev
mmio_readl(const volatile void * addr)69e4125c0cSAlexander Gordeev static u32 mmio_readl(const volatile void *addr)
70e4125c0cSAlexander Gordeev {
71e4125c0cSAlexander Gordeev return *(const volatile u32 __force *)addr;
72e4125c0cSAlexander Gordeev }
73e4125c0cSAlexander Gordeev
mmio_writeb(u8 value,volatile void * addr)74e4125c0cSAlexander Gordeev static void mmio_writeb(u8 value, volatile void *addr)
75e4125c0cSAlexander Gordeev {
76e4125c0cSAlexander Gordeev *(volatile u8 __force *)addr = value;
77e4125c0cSAlexander Gordeev }
78e4125c0cSAlexander Gordeev
mmio_writew(u16 value,volatile void * addr)79e4125c0cSAlexander Gordeev static void mmio_writew(u16 value, volatile void *addr)
80e4125c0cSAlexander Gordeev {
81e4125c0cSAlexander Gordeev *(volatile u16 __force *)addr = value;
82e4125c0cSAlexander Gordeev }
83e4125c0cSAlexander Gordeev
mmio_writel(u32 value,volatile void * addr)84e4125c0cSAlexander Gordeev static void mmio_writel(u32 value, volatile void *addr)
85e4125c0cSAlexander Gordeev {
86e4125c0cSAlexander Gordeev *(volatile u32 __force *)addr = value;
87e4125c0cSAlexander Gordeev }
88e4125c0cSAlexander Gordeev
89e4125c0cSAlexander Gordeev static struct pci_testdev_ops pci_testdev_mem_ops = {
90e4125c0cSAlexander Gordeev .io_readb = mmio_readb,
91e4125c0cSAlexander Gordeev .io_readw = mmio_readw,
92e4125c0cSAlexander Gordeev .io_readl = mmio_readl,
93e4125c0cSAlexander Gordeev .io_writeb = mmio_writeb,
94e4125c0cSAlexander Gordeev .io_writew = mmio_writew,
95e4125c0cSAlexander Gordeev .io_writel = mmio_writel
96e4125c0cSAlexander Gordeev };
97e4125c0cSAlexander Gordeev
pci_testdev_one(struct pci_test_dev_hdr * test,int test_nr,struct pci_testdev_ops * ops)98e4125c0cSAlexander Gordeev static bool pci_testdev_one(struct pci_test_dev_hdr *test,
99e4125c0cSAlexander Gordeev int test_nr,
100e4125c0cSAlexander Gordeev struct pci_testdev_ops *ops)
101e4125c0cSAlexander Gordeev {
102e4125c0cSAlexander Gordeev u8 width;
103e4125c0cSAlexander Gordeev u32 count, sig, off;
104e4125c0cSAlexander Gordeev const int nr_writes = 16;
105e4125c0cSAlexander Gordeev int i;
106e4125c0cSAlexander Gordeev
107e4125c0cSAlexander Gordeev ops->io_writeb(test_nr, &test->test);
108e4125c0cSAlexander Gordeev count = ops->io_readl(&test->count);
109e4125c0cSAlexander Gordeev if (count != 0)
110e4125c0cSAlexander Gordeev return false;
111e4125c0cSAlexander Gordeev
112e4125c0cSAlexander Gordeev width = ops->io_readb(&test->width);
113e4125c0cSAlexander Gordeev if (width != 1 && width != 2 && width != 4)
114e4125c0cSAlexander Gordeev return false;
115e4125c0cSAlexander Gordeev
116e4125c0cSAlexander Gordeev sig = ops->io_readl(&test->data);
117e4125c0cSAlexander Gordeev off = ops->io_readl(&test->offset);
118e4125c0cSAlexander Gordeev
119e4125c0cSAlexander Gordeev for (i = 0; i < nr_writes; i++) {
120e4125c0cSAlexander Gordeev switch (width) {
121e4125c0cSAlexander Gordeev case 1: ops->io_writeb(sig, (void *)test + off); break;
122e4125c0cSAlexander Gordeev case 2: ops->io_writew(sig, (void *)test + off); break;
123e4125c0cSAlexander Gordeev case 4: ops->io_writel(sig, (void *)test + off); break;
124e4125c0cSAlexander Gordeev }
125e4125c0cSAlexander Gordeev }
126e4125c0cSAlexander Gordeev
127e4125c0cSAlexander Gordeev count = ops->io_readl(&test->count);
128e4125c0cSAlexander Gordeev if (!count)
129e4125c0cSAlexander Gordeev return true;
130e4125c0cSAlexander Gordeev
131e4125c0cSAlexander Gordeev return (int)count == nr_writes;
132e4125c0cSAlexander Gordeev }
133e4125c0cSAlexander Gordeev
pci_testdev_print(struct pci_test_dev_hdr * test,struct pci_testdev_ops * ops)13423b8916bSThomas Huth static void pci_testdev_print(struct pci_test_dev_hdr *test,
135e4125c0cSAlexander Gordeev struct pci_testdev_ops *ops)
136e4125c0cSAlexander Gordeev {
137e4125c0cSAlexander Gordeev bool io = (ops == &pci_testdev_io_ops);
138e4125c0cSAlexander Gordeev int i;
139e4125c0cSAlexander Gordeev
140e4125c0cSAlexander Gordeev printf("pci-testdev %3s: ", io ? "io" : "mem");
141e4125c0cSAlexander Gordeev for (i = 0;; ++i) {
142e4125c0cSAlexander Gordeev char c = ops->io_readb(&test->name[i]);
143e4125c0cSAlexander Gordeev if (!c)
144e4125c0cSAlexander Gordeev break;
145e4125c0cSAlexander Gordeev printf("%c", c);
146e4125c0cSAlexander Gordeev }
147e4125c0cSAlexander Gordeev printf("\n");
148e4125c0cSAlexander Gordeev }
149e4125c0cSAlexander Gordeev
pci_testdev_all(struct pci_test_dev_hdr * test,struct pci_testdev_ops * ops)150e4125c0cSAlexander Gordeev static int pci_testdev_all(struct pci_test_dev_hdr *test,
151e4125c0cSAlexander Gordeev struct pci_testdev_ops *ops)
152e4125c0cSAlexander Gordeev {
153e4125c0cSAlexander Gordeev int i;
154e4125c0cSAlexander Gordeev
155e4125c0cSAlexander Gordeev for (i = 0;; i++) {
156e4125c0cSAlexander Gordeev if (!pci_testdev_one(test, i, ops))
157e4125c0cSAlexander Gordeev break;
158e4125c0cSAlexander Gordeev pci_testdev_print(test, ops);
159e4125c0cSAlexander Gordeev }
160e4125c0cSAlexander Gordeev
161e4125c0cSAlexander Gordeev return i;
162e4125c0cSAlexander Gordeev }
163e4125c0cSAlexander Gordeev
pci_testdev(void)164e4125c0cSAlexander Gordeev int pci_testdev(void)
165e4125c0cSAlexander Gordeev {
1664d6cefa9SPeter Xu struct pci_dev pci_dev;
1674d6cefa9SPeter Xu pcidevaddr_t dev;
168e4125c0cSAlexander Gordeev phys_addr_t addr;
169e4125c0cSAlexander Gordeev void __iomem *mem, *io;
170e4125c0cSAlexander Gordeev int nr_tests = 0;
171e4125c0cSAlexander Gordeev bool ret;
172e4125c0cSAlexander Gordeev
173e4125c0cSAlexander Gordeev dev = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST);
174e4125c0cSAlexander Gordeev if (dev == PCIDEVADDR_INVALID) {
175e4125c0cSAlexander Gordeev printf("'pci-testdev' device is not found, "
176e4125c0cSAlexander Gordeev "check QEMU '-device pci-testdev' parameter\n");
177e4125c0cSAlexander Gordeev return -1;
178e4125c0cSAlexander Gordeev }
1794d6cefa9SPeter Xu pci_dev_init(&pci_dev, dev);
180e4125c0cSAlexander Gordeev
1814d6cefa9SPeter Xu ret = pci_bar_is_valid(&pci_dev, 0) && pci_bar_is_valid(&pci_dev, 1);
182e4125c0cSAlexander Gordeev assert(ret);
183e4125c0cSAlexander Gordeev
1844d6cefa9SPeter Xu addr = pci_bar_get_addr(&pci_dev, 0);
185e4125c0cSAlexander Gordeev mem = ioremap(addr, PAGE_SIZE);
186e4125c0cSAlexander Gordeev
1874d6cefa9SPeter Xu addr = pci_bar_get_addr(&pci_dev, 1);
188*340cf7dbSAndrew Jones #if defined(__i386__) || defined(__x86_64__)
189e4125c0cSAlexander Gordeev io = (void *)(unsigned long)addr;
190*340cf7dbSAndrew Jones #else
191*340cf7dbSAndrew Jones io = ioremap(addr, PAGE_SIZE);
192*340cf7dbSAndrew Jones #endif
193e4125c0cSAlexander Gordeev
194e4125c0cSAlexander Gordeev nr_tests += pci_testdev_all(mem, &pci_testdev_mem_ops);
195e4125c0cSAlexander Gordeev nr_tests += pci_testdev_all(io, &pci_testdev_io_ops);
196e4125c0cSAlexander Gordeev
197e4125c0cSAlexander Gordeev return nr_tests;
198e4125c0cSAlexander Gordeev }
199