162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Pvpanic PCI Device Support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2021 Oracle.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <uapi/misc/pvpanic.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "pvpanic.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define PCI_VENDOR_ID_REDHAT             0x1b36
1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_REDHAT_PVPANIC     0x0011
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciMODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>");
2262306a36Sopenharmony_ciMODULE_DESCRIPTION("pvpanic device driver");
2362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic ssize_t capability_show(struct device *dev, struct device_attribute *attr, char *buf)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct pvpanic_instance *pi = dev_get_drvdata(dev);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return sysfs_emit(buf, "%x\n", pi->capability);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(capability);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct pvpanic_instance *pi = dev_get_drvdata(dev);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return sysfs_emit(buf, "%x\n", pi->events);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic ssize_t events_store(struct device *dev, struct device_attribute *attr,
4162306a36Sopenharmony_ci			    const char *buf, size_t count)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct pvpanic_instance *pi = dev_get_drvdata(dev);
4462306a36Sopenharmony_ci	unsigned int tmp;
4562306a36Sopenharmony_ci	int err;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	err = kstrtouint(buf, 16, &tmp);
4862306a36Sopenharmony_ci	if (err)
4962306a36Sopenharmony_ci		return err;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if ((tmp & pi->capability) != tmp)
5262306a36Sopenharmony_ci		return -EINVAL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	pi->events = tmp;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return count;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(events);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct attribute *pvpanic_pci_dev_attrs[] = {
6162306a36Sopenharmony_ci	&dev_attr_capability.attr,
6262306a36Sopenharmony_ci	&dev_attr_events.attr,
6362306a36Sopenharmony_ci	NULL
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ciATTRIBUTE_GROUPS(pvpanic_pci_dev);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct pvpanic_instance *pi;
7062306a36Sopenharmony_ci	void __iomem *base;
7162306a36Sopenharmony_ci	int ret;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
7462306a36Sopenharmony_ci	if (ret < 0)
7562306a36Sopenharmony_ci		return ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	base = pcim_iomap(pdev, 0, 0);
7862306a36Sopenharmony_ci	if (!base)
7962306a36Sopenharmony_ci		return -ENOMEM;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	pi = devm_kmalloc(&pdev->dev, sizeof(*pi), GFP_KERNEL);
8262306a36Sopenharmony_ci	if (!pi)
8362306a36Sopenharmony_ci		return -ENOMEM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	pi->base = base;
8662306a36Sopenharmony_ci	pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* initlize capability by RDPT */
8962306a36Sopenharmony_ci	pi->capability &= ioread8(base);
9062306a36Sopenharmony_ci	pi->events = pi->capability;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return devm_pvpanic_probe(&pdev->dev, pi);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct pci_device_id pvpanic_pci_id_tbl[]  = {
9662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)},
9762306a36Sopenharmony_ci	{}
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic struct pci_driver pvpanic_pci_driver = {
10262306a36Sopenharmony_ci	.name =         "pvpanic-pci",
10362306a36Sopenharmony_ci	.id_table =     pvpanic_pci_id_tbl,
10462306a36Sopenharmony_ci	.probe =        pvpanic_pci_probe,
10562306a36Sopenharmony_ci	.driver = {
10662306a36Sopenharmony_ci		.dev_groups = pvpanic_pci_dev_groups,
10762306a36Sopenharmony_ci	},
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_cimodule_pci_driver(pvpanic_pci_driver);
110