xref: /linux/drivers/misc/pvpanic/pvpanic-pci.c (revision 372dae89972594393b57f29ec44e351fa7eedbbe)
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