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