From 92a41a5a90167c423c72fd4c60fa2eb18e2db177 Mon Sep 17 00:00:00 2001 From: James Hui Date: Wed, 23 Oct 2024 12:42:06 +0100 Subject: [PATCH 1/2] Create wr-pci-leds example add WR PCI Leds device to system build steps add barebone example PCI LEDs device added documentation for wr-pci-leds specification --- docs/specs/wr-pci-leds.rst | 125 +++++++++++++++++++++++++++++++++++++ hw/misc/Kconfig | 5 ++ hw/misc/meson.build | 3 + hw/misc/wr-pci-leds.c | 80 ++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 docs/specs/wr-pci-leds.rst create mode 100644 hw/misc/wr-pci-leds.c diff --git a/docs/specs/wr-pci-leds.rst b/docs/specs/wr-pci-leds.rst new file mode 100644 index 0000000000..16bd1c8b6f --- /dev/null +++ b/docs/specs/wr-pci-leds.rst @@ -0,0 +1,125 @@ +================= +Wind River PCI LEDs example device +================= +------------------------- +Sharing how to create a barebone QEMU custom device +------------------------- + +Standalone run +============ +Run + :: + + ./qemu-system-x86_64 -device wr-pci-leds + >> Hello this is my-device PCI device + +Linux run +============ +Integrate the example device driver into Kernel image, and during the boot + :: + + >> Hello, this is your leds QEMU driver + >> Found device vid: 0xABCD pid: 0x525 + +On command shell, + :: + + # echo 11 > /sys/class/leds/qemu:led0/brightness + >> Write to 0 with 11 4 + >> Read from 0 for 4 + >> leds_qemu regs is 11 + +Linux, drivers/leds/leds-qemu.c +============ +.. code:: C + :linenos: + + #include + #include + #include + + #define DRV_NAME "leds_qemu" + #define PCI_VENDOR_ID_QEMU 0xabcd + #define PCI_DEVICE_ID_QEMU 0x0525 + + static const struct pci_device_id pci_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_QEMU), }, + { 0, }, + }; + MODULE_DEVICE_TABLE(pci, pci_table); + + struct leds_qemu_priv { + uint32_t* regs; + struct led_classdev led0_qemu_cdev; + }; + + static int leds_qemu_set(struct led_classdev *led_cdev, enum led_brightness value) + { + struct leds_qemu_priv* s= container_of(led_cdev, struct leds_qemu_priv, led0_qemu_cdev); + writel((u8)value, s->regs); + int v = readl(s->regs); + printk(KERN_INFO ">>\t leds_qemu regs is %d\n",v); + return 0; + } + + static int leds_qemu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + { + int rc = 0; + printk(KERN_INFO ">>\t Hello, this is your leds QEMU driver\n"); + + struct leds_qemu_priv* priv = devm_kzalloc(&pdev->dev,sizeof(struct leds_qemu_priv), GFP_KERNEL); + pci_set_drvdata(pdev, priv); + + uint16_t vendor, device; + pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(pdev, PCI_DEVICE_ID, &device); + printk(KERN_INFO ">>\t Found device vid: 0x%X pid: 0x%X\n", vendor, device); + + rc = pci_enable_device(pdev); + if (rc) return rc; + rc = pci_request_regions(pdev,DRV_NAME); + if (rc) { + pci_disable_device(pdev); + return rc; + } + + resource_size_t pciaddr = pci_resource_start(pdev,0); + uint32_t* regs = ioremap(pciaddr,32); + priv->regs = regs; + priv->led0_qemu_cdev.name="qemu:led0"; + priv->led0_qemu_cdev.max_brightness=255; + priv->led0_qemu_cdev.brightness_set_blocking=leds_qemu_set; + rc = led_classdev_register(&pdev->dev, &priv->led0_qemu_cdev); + + return 0; + } + static void leds_qemu_remove (struct pci_dev *pdev) + { + } + + static struct pci_driver leds_qemu_driver = { + .name = DRV_NAME, + .id_table = pci_table, + .probe = leds_qemu_probe, + .remove = leds_qemu_remove, + }; + + module_pci_driver(leds_qemu_driver); + MODULE_LICENSE("GPL"); + + +Linux, drivers/leds/KConfig +================= +.. code:: + + config LEDS_QEMU + tristate "LED support for my-device in QEMU" + depends on LEDS_CLASS + depends on PCI + + +Linux, drivers/leds/Makefile +================= +.. code:: + + obj-$(CONFIG_LEDS_QEMU) += leds-qemu.o diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1e08785b83..2a479992e8 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -213,4 +213,9 @@ config IOSB config XLNX_VERSAL_TRNG bool +config WR_PCI_LEDS + bool + default y if PCI_DEVICES + depends on PCI + source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 2ca8717be2..87a6ebbe39 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -157,3 +157,6 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) # HPPA devices system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) + +# WR PCI LEDs example device +system_ss.add(when: 'CONFIG_WR_PCI_LEDS', if_true: files('wr-pci-leds.c')) diff --git a/hw/misc/wr-pci-leds.c b/hw/misc/wr-pci-leds.c new file mode 100644 index 0000000000..af57c44c3b --- /dev/null +++ b/hw/misc/wr-pci-leds.c @@ -0,0 +1,80 @@ +#include "qemu/osdep.h" +#include "hw/pci/pci_device.h" + +#define TYPE_MY_DEVICE "wr-pci-leds" +OBJECT_DECLARE_SIMPLE_TYPE(MyDeviceState, MY_DEVICE) + +typedef struct MyDeviceState +{ + PCIDevice parent_obj; + MemoryRegion bar; + + int32_t led0,led1; +} MyDeviceState; +static void my_device_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) +{ + printf(">>\t Write to %lu with %lu %u\n",addr, data, size); + MyDeviceState* s = opaque; + switch (addr) { + case 0: s->led0=data; break; + case 4: s->led1=data; break; + default: return; + } +} +static uint64_t my_device_read(void *opaque, hwaddr addr, unsigned size) +{ + printf(">>\t Read from %lu for %u\n",addr, size); + MyDeviceState* s = opaque; + switch (addr) { + case 0: return s->led0; + case 4: return s->led1; + default: return ~0; + } +} +static const MemoryRegionOps my_device_ops = { + .write = my_device_write, + .read = my_device_read, +}; +static void pci_my_device_realize(PCIDevice *dev, Error **errp) +{ + printf(">>\t Hello this is my-device PCI device\n"); + MyDeviceState *s = MY_DEVICE(dev); + memory_region_init_io(&s->bar, OBJECT(s), &my_device_ops, s, "my-device", 32); + pci_register_bar(dev, 0 , PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + +} +static void my_device_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->vendor_id = 0xabcd; + k->device_id = 0x0525; + k->realize = pci_my_device_realize; + k->class_id = PCI_CLASS_OTHERS; +} +static const TypeInfo my_device_info = { + .name = TYPE_MY_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(MyDeviceState), + .class_init = my_device_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; +static void my_device_register_types(void) +{ + type_register_static(&my_device_info); +} +type_init(my_device_register_types) + +/* Notes: + * + // Kconfig + config MY_DEVICE + bool + default y + + // mesa.build + system_ss.add(when: 'CONFIG_MY_DEVICE', if_true: files('my-device.c')) +*/ From 0168ac698a3f4bdec226e541b258070536568892 Mon Sep 17 00:00:00 2001 From: James Hui Date: Thu, 24 Oct 2024 10:17:38 +0100 Subject: [PATCH 2/2] Update wr-pci-leds.c align led private data type with write data type and remove notes that no longer needed --- hw/misc/wr-pci-leds.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/hw/misc/wr-pci-leds.c b/hw/misc/wr-pci-leds.c index af57c44c3b..4e6827f524 100644 --- a/hw/misc/wr-pci-leds.c +++ b/hw/misc/wr-pci-leds.c @@ -4,12 +4,11 @@ #define TYPE_MY_DEVICE "wr-pci-leds" OBJECT_DECLARE_SIMPLE_TYPE(MyDeviceState, MY_DEVICE) -typedef struct MyDeviceState -{ +typedef struct MyDeviceState { PCIDevice parent_obj; MemoryRegion bar; - int32_t led0,led1; + uint64_t led0,led1; } MyDeviceState; static void my_device_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { @@ -67,14 +66,3 @@ static void my_device_register_types(void) type_register_static(&my_device_info); } type_init(my_device_register_types) - -/* Notes: - * - // Kconfig - config MY_DEVICE - bool - default y - - // mesa.build - system_ss.add(when: 'CONFIG_MY_DEVICE', if_true: files('my-device.c')) -*/