18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCI Backend - Handle special overlays for broken devices.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
68c2ecf20Sopenharmony_ci * Author: Chris Bookholt <hap10@epoch.ncsc.mil>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define dev_fmt(fmt) DRV_NAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include "pciback.h"
148c2ecf20Sopenharmony_ci#include "conf_space.h"
158c2ecf20Sopenharmony_ci#include "conf_space_quirks.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciLIST_HEAD(xen_pcibk_quirks);
188c2ecf20Sopenharmony_cistatic inline const struct pci_device_id *
198c2ecf20Sopenharmony_cimatch_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
228c2ecf20Sopenharmony_ci	    (id->device == PCI_ANY_ID || id->device == dev->device) &&
238c2ecf20Sopenharmony_ci	    (id->subvendor == PCI_ANY_ID ||
248c2ecf20Sopenharmony_ci				id->subvendor == dev->subsystem_vendor) &&
258c2ecf20Sopenharmony_ci	    (id->subdevice == PCI_ANY_ID ||
268c2ecf20Sopenharmony_ci				id->subdevice == dev->subsystem_device) &&
278c2ecf20Sopenharmony_ci	    !((id->class ^ dev->class) & id->class_mask))
288c2ecf20Sopenharmony_ci		return id;
298c2ecf20Sopenharmony_ci	return NULL;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct xen_pcibk_config_quirk *tmp_quirk;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	list_for_each_entry(tmp_quirk, &xen_pcibk_quirks, quirks_list)
378c2ecf20Sopenharmony_ci		if (match_one_device(&tmp_quirk->devid, dev) != NULL)
388c2ecf20Sopenharmony_ci			goto out;
398c2ecf20Sopenharmony_ci	tmp_quirk = NULL;
408c2ecf20Sopenharmony_ci	dev_printk(KERN_DEBUG, &dev->dev,
418c2ecf20Sopenharmony_ci		   "quirk didn't match any device known\n");
428c2ecf20Sopenharmony_ciout:
438c2ecf20Sopenharmony_ci	return tmp_quirk;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void register_quirk(struct xen_pcibk_config_quirk *quirk)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	list_add_tail(&quirk->quirks_list, &xen_pcibk_quirks);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint xen_pcibk_field_is_dup(struct pci_dev *dev, unsigned int reg)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int ret = 0;
548c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);
558c2ecf20Sopenharmony_ci	struct config_field_entry *cfg_entry;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
588c2ecf20Sopenharmony_ci		if (OFFSET(cfg_entry) == reg) {
598c2ecf20Sopenharmony_ci			ret = 1;
608c2ecf20Sopenharmony_ci			break;
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	return ret;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciint xen_pcibk_config_quirks_add_field(struct pci_dev *dev, struct config_field
678c2ecf20Sopenharmony_ci				    *field)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int err = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	switch (field->size) {
728c2ecf20Sopenharmony_ci	case 1:
738c2ecf20Sopenharmony_ci		field->u.b.read = xen_pcibk_read_config_byte;
748c2ecf20Sopenharmony_ci		field->u.b.write = xen_pcibk_write_config_byte;
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	case 2:
778c2ecf20Sopenharmony_ci		field->u.w.read = xen_pcibk_read_config_word;
788c2ecf20Sopenharmony_ci		field->u.w.write = xen_pcibk_write_config_word;
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci	case 4:
818c2ecf20Sopenharmony_ci		field->u.dw.read = xen_pcibk_read_config_dword;
828c2ecf20Sopenharmony_ci		field->u.dw.write = xen_pcibk_write_config_dword;
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	default:
858c2ecf20Sopenharmony_ci		err = -EINVAL;
868c2ecf20Sopenharmony_ci		goto out;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	xen_pcibk_config_add_field(dev, field);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciout:
928c2ecf20Sopenharmony_ci	return err;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciint xen_pcibk_config_quirks_init(struct pci_dev *dev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
988c2ecf20Sopenharmony_ci	int ret = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	quirk = kzalloc(sizeof(*quirk), GFP_KERNEL);
1018c2ecf20Sopenharmony_ci	if (!quirk) {
1028c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1038c2ecf20Sopenharmony_ci		goto out;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	quirk->devid.vendor = dev->vendor;
1078c2ecf20Sopenharmony_ci	quirk->devid.device = dev->device;
1088c2ecf20Sopenharmony_ci	quirk->devid.subvendor = dev->subsystem_vendor;
1098c2ecf20Sopenharmony_ci	quirk->devid.subdevice = dev->subsystem_device;
1108c2ecf20Sopenharmony_ci	quirk->devid.class = 0;
1118c2ecf20Sopenharmony_ci	quirk->devid.class_mask = 0;
1128c2ecf20Sopenharmony_ci	quirk->devid.driver_data = 0UL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	quirk->pdev = dev;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	register_quirk(quirk);
1178c2ecf20Sopenharmony_ciout:
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid xen_pcibk_config_field_free(struct config_field *field)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	kfree(field);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ciint xen_pcibk_config_quirk_release(struct pci_dev *dev)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
1298c2ecf20Sopenharmony_ci	int ret = 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	quirk = xen_pcibk_find_quirk(dev);
1328c2ecf20Sopenharmony_ci	if (!quirk) {
1338c2ecf20Sopenharmony_ci		ret = -ENXIO;
1348c2ecf20Sopenharmony_ci		goto out;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	list_del(&quirk->quirks_list);
1388c2ecf20Sopenharmony_ci	kfree(quirk);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciout:
1418c2ecf20Sopenharmony_ci	return ret;
1428c2ecf20Sopenharmony_ci}
143