1db3a4f0aSMihai Carabas // SPDX-License-Identifier: GPL-2.0+ 2db3a4f0aSMihai Carabas /* 3db3a4f0aSMihai Carabas * Pvpanic PCI Device Support 4db3a4f0aSMihai Carabas * 5db3a4f0aSMihai Carabas * Copyright (C) 2021 Oracle. 6db3a4f0aSMihai Carabas */ 7db3a4f0aSMihai Carabas 8db3a4f0aSMihai Carabas #include <linux/kernel.h> 9db3a4f0aSMihai Carabas #include <linux/module.h> 10db3a4f0aSMihai Carabas #include <linux/pci.h> 11db3a4f0aSMihai Carabas #include <linux/types.h> 12db3a4f0aSMihai Carabas #include <linux/slab.h> 13db3a4f0aSMihai Carabas 14db3a4f0aSMihai Carabas #include <uapi/misc/pvpanic.h> 15db3a4f0aSMihai Carabas 16db3a4f0aSMihai Carabas #include "pvpanic.h" 17db3a4f0aSMihai Carabas 18db3a4f0aSMihai Carabas #define PCI_VENDOR_ID_REDHAT 0x1b36 19db3a4f0aSMihai Carabas #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 20db3a4f0aSMihai Carabas 21db3a4f0aSMihai Carabas MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>"); 22db3a4f0aSMihai Carabas MODULE_DESCRIPTION("pvpanic device driver "); 23db3a4f0aSMihai Carabas MODULE_LICENSE("GPL"); 24db3a4f0aSMihai Carabas 25db3a4f0aSMihai Carabas static const struct pci_device_id pvpanic_pci_id_tbl[] = { 26db3a4f0aSMihai Carabas { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, 27db3a4f0aSMihai Carabas {} 28db3a4f0aSMihai Carabas }; 29db3a4f0aSMihai Carabas 30db3a4f0aSMihai Carabas static ssize_t capability_show(struct device *dev, 31db3a4f0aSMihai Carabas struct device_attribute *attr, char *buf) 32db3a4f0aSMihai Carabas { 33db3a4f0aSMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 34db3a4f0aSMihai Carabas 35db3a4f0aSMihai Carabas return sysfs_emit(buf, "%x\n", pi->capability); 36db3a4f0aSMihai Carabas } 37db3a4f0aSMihai Carabas static DEVICE_ATTR_RO(capability); 38db3a4f0aSMihai Carabas 39db3a4f0aSMihai Carabas static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) 40db3a4f0aSMihai Carabas { 41db3a4f0aSMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 42db3a4f0aSMihai Carabas 43db3a4f0aSMihai Carabas return sysfs_emit(buf, "%x\n", pi->events); 44db3a4f0aSMihai Carabas } 45db3a4f0aSMihai Carabas 46db3a4f0aSMihai Carabas static ssize_t events_store(struct device *dev, struct device_attribute *attr, 47db3a4f0aSMihai Carabas const char *buf, size_t count) 48db3a4f0aSMihai Carabas { 49db3a4f0aSMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(dev); 50db3a4f0aSMihai Carabas unsigned int tmp; 51db3a4f0aSMihai Carabas int err; 52db3a4f0aSMihai Carabas 53db3a4f0aSMihai Carabas err = kstrtouint(buf, 16, &tmp); 54db3a4f0aSMihai Carabas if (err) 55db3a4f0aSMihai Carabas return err; 56db3a4f0aSMihai Carabas 57db3a4f0aSMihai Carabas if ((tmp & pi->capability) != tmp) 58db3a4f0aSMihai Carabas return -EINVAL; 59db3a4f0aSMihai Carabas 60db3a4f0aSMihai Carabas pi->events = tmp; 61db3a4f0aSMihai Carabas 62db3a4f0aSMihai Carabas return count; 63db3a4f0aSMihai Carabas } 64db3a4f0aSMihai Carabas static DEVICE_ATTR_RW(events); 65db3a4f0aSMihai Carabas 66db3a4f0aSMihai Carabas static struct attribute *pvpanic_pci_dev_attrs[] = { 67db3a4f0aSMihai Carabas &dev_attr_capability.attr, 68db3a4f0aSMihai Carabas &dev_attr_events.attr, 69db3a4f0aSMihai Carabas NULL 70db3a4f0aSMihai Carabas }; 71db3a4f0aSMihai Carabas ATTRIBUTE_GROUPS(pvpanic_pci_dev); 72db3a4f0aSMihai Carabas 73db3a4f0aSMihai Carabas static int pvpanic_pci_probe(struct pci_dev *pdev, 74db3a4f0aSMihai Carabas const struct pci_device_id *ent) 75db3a4f0aSMihai Carabas { 76db3a4f0aSMihai Carabas struct device *dev = &pdev->dev; 77db3a4f0aSMihai Carabas struct pvpanic_instance *pi; 78db3a4f0aSMihai Carabas void __iomem *base; 79db3a4f0aSMihai Carabas int ret; 80db3a4f0aSMihai Carabas 81*372dae89SChristophe JAILLET ret = pcim_enable_device(pdev); 82db3a4f0aSMihai Carabas if (ret < 0) 83db3a4f0aSMihai Carabas return ret; 84db3a4f0aSMihai Carabas 85*372dae89SChristophe JAILLET base = pcim_iomap(pdev, 0, 0); 86642fa28bSQiheng Lin if (!base) 87642fa28bSQiheng Lin return -ENOMEM; 88db3a4f0aSMihai Carabas 89*372dae89SChristophe JAILLET pi = devm_kmalloc(&pdev->dev, sizeof(*pi), GFP_ATOMIC); 90db3a4f0aSMihai Carabas if (!pi) 91db3a4f0aSMihai Carabas return -ENOMEM; 92db3a4f0aSMihai Carabas 93db3a4f0aSMihai Carabas pi->base = base; 94db3a4f0aSMihai Carabas pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; 95db3a4f0aSMihai Carabas 96db3a4f0aSMihai Carabas /* initlize capability by RDPT */ 97db3a4f0aSMihai Carabas pi->capability &= ioread8(base); 98db3a4f0aSMihai Carabas pi->events = pi->capability; 99db3a4f0aSMihai Carabas 100db3a4f0aSMihai Carabas dev_set_drvdata(dev, pi); 101db3a4f0aSMihai Carabas 102db3a4f0aSMihai Carabas return pvpanic_probe(pi); 103db3a4f0aSMihai Carabas } 104db3a4f0aSMihai Carabas 105db3a4f0aSMihai Carabas static void pvpanic_pci_remove(struct pci_dev *pdev) 106db3a4f0aSMihai Carabas { 107db3a4f0aSMihai Carabas struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev); 108db3a4f0aSMihai Carabas 109db3a4f0aSMihai Carabas pvpanic_remove(pi); 110db3a4f0aSMihai Carabas } 111db3a4f0aSMihai Carabas 112db3a4f0aSMihai Carabas static struct pci_driver pvpanic_pci_driver = { 113db3a4f0aSMihai Carabas .name = "pvpanic-pci", 114db3a4f0aSMihai Carabas .id_table = pvpanic_pci_id_tbl, 115db3a4f0aSMihai Carabas .probe = pvpanic_pci_probe, 116db3a4f0aSMihai Carabas .remove = pvpanic_pci_remove, 117db3a4f0aSMihai Carabas .driver = { 118db3a4f0aSMihai Carabas .dev_groups = pvpanic_pci_dev_groups, 119db3a4f0aSMihai Carabas }, 120db3a4f0aSMihai Carabas }; 121db3a4f0aSMihai Carabas 122db3a4f0aSMihai Carabas module_pci_driver(pvpanic_pci_driver); 123