162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCI Backend - Handle special overlays for broken devices.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
662306a36Sopenharmony_ci * Author: Chris Bookholt <hap10@epoch.ncsc.mil>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define dev_fmt(fmt) DRV_NAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include "pciback.h"
1462306a36Sopenharmony_ci#include "conf_space.h"
1562306a36Sopenharmony_ci#include "conf_space_quirks.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciLIST_HEAD(xen_pcibk_quirks);
1862306a36Sopenharmony_cistatic inline const struct pci_device_id *
1962306a36Sopenharmony_cimatch_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
2262306a36Sopenharmony_ci	    (id->device == PCI_ANY_ID || id->device == dev->device) &&
2362306a36Sopenharmony_ci	    (id->subvendor == PCI_ANY_ID ||
2462306a36Sopenharmony_ci				id->subvendor == dev->subsystem_vendor) &&
2562306a36Sopenharmony_ci	    (id->subdevice == PCI_ANY_ID ||
2662306a36Sopenharmony_ci				id->subdevice == dev->subsystem_device) &&
2762306a36Sopenharmony_ci	    !((id->class ^ dev->class) & id->class_mask))
2862306a36Sopenharmony_ci		return id;
2962306a36Sopenharmony_ci	return NULL;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct xen_pcibk_config_quirk *tmp_quirk;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	list_for_each_entry(tmp_quirk, &xen_pcibk_quirks, quirks_list)
3762306a36Sopenharmony_ci		if (match_one_device(&tmp_quirk->devid, dev) != NULL)
3862306a36Sopenharmony_ci			goto out;
3962306a36Sopenharmony_ci	tmp_quirk = NULL;
4062306a36Sopenharmony_ci	dev_printk(KERN_DEBUG, &dev->dev,
4162306a36Sopenharmony_ci		   "quirk didn't match any device known\n");
4262306a36Sopenharmony_ciout:
4362306a36Sopenharmony_ci	return tmp_quirk;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic inline void register_quirk(struct xen_pcibk_config_quirk *quirk)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	list_add_tail(&quirk->quirks_list, &xen_pcibk_quirks);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint xen_pcibk_field_is_dup(struct pci_dev *dev, unsigned int reg)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int ret = 0;
5462306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);
5562306a36Sopenharmony_ci	struct config_field_entry *cfg_entry;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
5862306a36Sopenharmony_ci		if (OFFSET(cfg_entry) == reg) {
5962306a36Sopenharmony_ci			ret = 1;
6062306a36Sopenharmony_ci			break;
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	return ret;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint xen_pcibk_config_quirks_add_field(struct pci_dev *dev, struct config_field
6762306a36Sopenharmony_ci				    *field)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	int err = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	switch (field->size) {
7262306a36Sopenharmony_ci	case 1:
7362306a36Sopenharmony_ci		field->u.b.read = xen_pcibk_read_config_byte;
7462306a36Sopenharmony_ci		field->u.b.write = xen_pcibk_write_config_byte;
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	case 2:
7762306a36Sopenharmony_ci		field->u.w.read = xen_pcibk_read_config_word;
7862306a36Sopenharmony_ci		field->u.w.write = xen_pcibk_write_config_word;
7962306a36Sopenharmony_ci		break;
8062306a36Sopenharmony_ci	case 4:
8162306a36Sopenharmony_ci		field->u.dw.read = xen_pcibk_read_config_dword;
8262306a36Sopenharmony_ci		field->u.dw.write = xen_pcibk_write_config_dword;
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci	default:
8562306a36Sopenharmony_ci		err = -EINVAL;
8662306a36Sopenharmony_ci		goto out;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	xen_pcibk_config_add_field(dev, field);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciout:
9262306a36Sopenharmony_ci	return err;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciint xen_pcibk_config_quirks_init(struct pci_dev *dev)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
9862306a36Sopenharmony_ci	int ret = 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	quirk = kzalloc(sizeof(*quirk), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (!quirk) {
10262306a36Sopenharmony_ci		ret = -ENOMEM;
10362306a36Sopenharmony_ci		goto out;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	quirk->devid.vendor = dev->vendor;
10762306a36Sopenharmony_ci	quirk->devid.device = dev->device;
10862306a36Sopenharmony_ci	quirk->devid.subvendor = dev->subsystem_vendor;
10962306a36Sopenharmony_ci	quirk->devid.subdevice = dev->subsystem_device;
11062306a36Sopenharmony_ci	quirk->devid.class = 0;
11162306a36Sopenharmony_ci	quirk->devid.class_mask = 0;
11262306a36Sopenharmony_ci	quirk->devid.driver_data = 0UL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	quirk->pdev = dev;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	register_quirk(quirk);
11762306a36Sopenharmony_ciout:
11862306a36Sopenharmony_ci	return ret;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid xen_pcibk_config_field_free(struct config_field *field)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	kfree(field);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint xen_pcibk_config_quirk_release(struct pci_dev *dev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
12962306a36Sopenharmony_ci	int ret = 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	quirk = xen_pcibk_find_quirk(dev);
13262306a36Sopenharmony_ci	if (!quirk) {
13362306a36Sopenharmony_ci		ret = -ENXIO;
13462306a36Sopenharmony_ci		goto out;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	list_del(&quirk->quirks_list);
13862306a36Sopenharmony_ci	kfree(quirk);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciout:
14162306a36Sopenharmony_ci	return ret;
14262306a36Sopenharmony_ci}
143