diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Tue Apr 11 17:40:03 2006 @@ -1,7 +1,11 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback.o pciback-y := pci_stub.o pciback_ops.o xenbus.o -pciback-y += conf_space.o conf_space_header.o +pciback-y += conf_space.o conf_space_header.o \ + conf_space_capability.o \ + conf_space_capability_vpd.o \ + conf_space_capability_pm.o \ + conf_space_quirks.o conf_space_quirks_tg3.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_PASS) += passthrough.o diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c Tue Apr 11 17:40:03 2006 @@ -14,13 +14,10 @@ #include "pciback.h" #include "conf_space.h" -static int permissive = 0; -module_param(permissive, bool, 0644); - -#define DEFINE_PCI_CONFIG(op,size,type) \ -int pciback_##op##_config_##size \ +#define DEFINE_PCI_CONFIG(op,size,type) \ +int pciback_##op##_config_##size \ (struct pci_dev *dev, int offset, type value, void *data) \ -{ \ +{ \ return pci_##op##_config_##size (dev, offset, value); \ } @@ -175,8 +172,8 @@ req_start = offset; req_end = offset + size; - field_start = field->offset; - field_end = field->offset + field->size; + field_start = OFFSET(cfg_entry); + field_end = OFFSET(cfg_entry) + field->size; if ((req_start >= field_start && req_start < field_end) || (req_end > field_start && req_end <= field_end)) { @@ -222,8 +219,8 @@ req_start = offset; req_end = offset + size; - field_start = field->offset; - field_end = field->offset + field->size; + field_start = OFFSET(cfg_entry); + field_end = OFFSET(cfg_entry) + field->size; if ((req_start >= field_start && req_start < field_end) || (req_end > field_start && req_end <= field_end)) { @@ -239,60 +236,99 @@ err = conf_space_write(dev, cfg_entry, field_start, tmp_val); + + /* handled is set true here, but not every byte + * may have been written! Properly detecting if + * every byte is handled is unnecessary as the + * flag is used to detect devices that need + * special helpers to work correctly. + */ handled = 1; } } - if (!handled && !err && permissive) { - switch (size) { - case 1: - err = pci_write_config_byte(dev, offset, (u8)value); - break; - case 2: - err = pci_write_config_word(dev, offset, (u16)value); - break; - case 4: - err = pci_write_config_dword(dev, offset, (u32)value); - break; + if (!handled && !err) { + /* By default, anything not specificially handled above is + * read-only. The permissive flag changes this behavior so + * that anything not specifically handled above is writable. + * This means that some fields may still be read-only because + * they have entries in the config_field list that intercept + * the write and do nothing. */ + if (dev_data->permissive) { + switch (size) { + case 1: + err = pci_write_config_byte(dev, offset, + (u8)value); + break; + case 2: + err = pci_write_config_word(dev, offset, + (u16)value); + break; + case 4: + err = pci_write_config_dword(dev, offset, + (u32)value); + break; + } + } else if (!dev_data->warned_on_write) { + dev_data->warned_on_write = 1; + dev_warn(&dev->dev, "Driver wrote to a read-only " + "configuration space field!\n"); + dev_warn(&dev->dev, "Write at offset 0x%x size %d\n", + offset, size); + dev_warn(&dev->dev, "This may be harmless, but if "); + dev_warn(&dev->dev, "you have problems with your " + "device:\n"); + dev_warn(&dev->dev, "1) see the permissive " + "attribute in sysfs.\n"); + dev_warn(&dev->dev, "2) report problems to the " + "xen-devel mailing list along\n"); + dev_warn(&dev->dev, " with details of your device " + "obtained from lspci.\n"); } } return pcibios_err_to_errno(err); } -void pciback_config_reset(struct pci_dev *dev) +void pciback_config_reset_dev(struct pci_dev *dev) { struct pciback_dev_data *dev_data = pci_get_drvdata(dev); struct config_field_entry *cfg_entry; struct config_field *field; + dev_dbg(&dev->dev, "resetting virtual configuration space\n"); + list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { field = cfg_entry->field; if (field->reset) - field->reset(dev, field->offset, cfg_entry->data); - } -} - -void pciback_config_free(struct pci_dev *dev) + field->reset(dev, OFFSET(cfg_entry), cfg_entry->data); + } +} + +void pciback_config_free_dev(struct pci_dev *dev) { struct pciback_dev_data *dev_data = pci_get_drvdata(dev); struct config_field_entry *cfg_entry, *t; struct config_field *field; + dev_dbg(&dev->dev, "free-ing virtual configuration space fields\n"); + list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) { list_del(&cfg_entry->list); field = cfg_entry->field; if (field->release) - field->release(dev, field->offset, cfg_entry->data); + field->release(dev, OFFSET(cfg_entry), cfg_entry->data); kfree(cfg_entry); } } -int pciback_config_add_field(struct pci_dev *dev, struct config_field *field) +int pciback_config_add_field_offset(struct pci_dev *dev, + struct config_field *field, + unsigned int offset) { int err = 0; struct pciback_dev_data *dev_data = pci_get_drvdata(dev); @@ -307,9 +343,10 @@ cfg_entry->data = NULL; cfg_entry->field = field; + cfg_entry->base_offset = offset; if (field->init) { - tmp = field->init(dev, field->offset); + tmp = field->init(dev, OFFSET(cfg_entry)); if (IS_ERR(tmp)) { err = PTR_ERR(tmp); @@ -319,6 +356,8 @@ cfg_entry->data = tmp; } + dev_dbg(&dev->dev, "added config field at offset 0x%02x\n", + OFFSET(cfg_entry)); list_add_tail(&cfg_entry->list, &dev_data->config_fields); out: @@ -332,14 +371,39 @@ * certain registers (like the base address registers (BARs) so that we can * keep the client from manipulating them directly. */ -int pciback_config_init(struct pci_dev *dev) +int pciback_config_init_dev(struct pci_dev *dev) { int err = 0; struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + dev_dbg(&dev->dev, "initializing virtual configuration space\n"); + INIT_LIST_HEAD(&dev_data->config_fields); err = pciback_config_header_add_fields(dev); - + if (err) + goto out; + + err = pciback_config_capability_add_fields(dev); + if (err) + goto out; + + err = pciback_config_quirks_add_fields(dev); + + out: return err; } + +int pciback_config_init(void) +{ + int err; + + err = pciback_config_capability_init(); + if (err) + goto out; + + err = pciback_config_quirks_init(); + + out: + return err; +} diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h Tue Apr 11 17:40:03 2006 @@ -8,7 +8,9 @@ #define __XEN_PCIBACK_CONF_SPACE_H__ #include +#include +/* conf_field_init can return an errno in a ptr with ERR_PTR() */ typedef void *(*conf_field_init) (struct pci_dev * dev, int offset); typedef void (*conf_field_reset) (struct pci_dev * dev, int offset, void *data); typedef void (*conf_field_free) (struct pci_dev * dev, int offset, void *data); @@ -55,13 +57,25 @@ struct config_field_entry { struct list_head list; struct config_field *field; + unsigned int base_offset; void *data; }; + +#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) /* Add fields to a device - the add_fields macro expects to get a pointer to * the first entry in an array (of which the ending is marked by size==0) */ -int pciback_config_add_field(struct pci_dev *dev, struct config_field *field); +int pciback_config_add_field_offset(struct pci_dev *dev, + struct config_field *field, + unsigned int offset); + +static inline int pciback_config_add_field(struct pci_dev *dev, + struct config_field *field) +{ + return pciback_config_add_field_offset(dev, field, 0); +} + static inline int pciback_config_add_fields(struct pci_dev *dev, struct config_field *field) { @@ -74,11 +88,18 @@ return err; } -/* Initializers which add fields to the virtual configuration space - * ** We could add initializers to allow a guest domain to touch - * the capability lists (for power management, the AGP bridge, etc.) - */ -int pciback_config_header_add_fields(struct pci_dev *dev); +static inline int pciback_config_add_fields_offset(struct pci_dev *dev, + struct config_field *field, + unsigned int offset) +{ + int i, err = 0; + for (i = 0; field[i].size != 0; i++) { + err = pciback_config_add_field_offset(dev, &field[i], offset); + if (err) + break; + } + return err; +} /* Read/Write the real configuration space */ int pciback_read_config_byte(struct pci_dev *dev, int offset, u8 * value, @@ -94,4 +115,11 @@ int pciback_write_config_dword(struct pci_dev *dev, int offset, u32 value, void *data); +int pciback_config_quirks_init(void); +int pciback_config_capability_init(void); + +int pciback_config_header_add_fields(struct pci_dev *dev); +int pciback_config_capability_add_fields(struct pci_dev *dev); +int pciback_config_quirks_add_fields(struct pci_dev *dev); + #endif /* __XEN_PCIBACK_CONF_SPACE_H__ */ diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c Tue Apr 11 17:40:03 2006 @@ -169,29 +169,61 @@ return 0; } -struct config_field header_common[] = { +static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data) +{ + u8 cur_value; + int err; + + err = pci_read_config_byte(dev, offset, &cur_value); + if (err) + goto out; + + if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START) + || value == PCI_BIST_START) + err = pci_write_config_byte(dev, offset, value); + + out: + return err; +} + +static struct config_field header_common[] = { { .offset = PCI_COMMAND, .size = 2, .u.w.read = pciback_read_config_word, .u.w.write = command_write, - }, + }, { .offset = PCI_INTERRUPT_LINE, .size = 1, .u.b.read = interrupt_read, - .u.b.write = NULL, - }, + }, + { + .offset = PCI_INTERRUPT_PIN, + .size = 1, + .u.b.read = pciback_read_config_byte, + }, { /* Any side effects of letting driver domain control cache line? */ .offset = PCI_CACHE_LINE_SIZE, .size = 1, .u.b.read = pciback_read_config_byte, .u.b.write = pciback_write_config_byte, - }, + }, + { + .offset = PCI_LATENCY_TIMER, + .size = 1, + .u.b.read = pciback_read_config_byte, + }, + { + .offset = PCI_BIST, + .size = 1, + .u.b.read = pciback_read_config_byte, + .u.b.write = bist_write, + }, { .size = 0, - }, + }, }; #define CFG_FIELD_BAR(reg_offset) \ @@ -216,7 +248,7 @@ .u.dw.write = rom_write, \ } -struct config_field header_0[] = { +static struct config_field header_0[] = { CFG_FIELD_BAR(PCI_BASE_ADDRESS_0), CFG_FIELD_BAR(PCI_BASE_ADDRESS_1), CFG_FIELD_BAR(PCI_BASE_ADDRESS_2), @@ -226,16 +258,16 @@ CFG_FIELD_ROM(PCI_ROM_ADDRESS), { .size = 0, - }, -}; - -struct config_field header_1[] = { + }, +}; + +static struct config_field header_1[] = { CFG_FIELD_BAR(PCI_BASE_ADDRESS_0), CFG_FIELD_BAR(PCI_BASE_ADDRESS_1), CFG_FIELD_ROM(PCI_ROM_ADDRESS1), { .size = 0, - }, + }, }; int pciback_config_header_add_fields(struct pci_dev *dev) diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.c --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.c Tue Apr 11 17:40:03 2006 @@ -0,0 +1,71 @@ +/* + * PCI Backend - Handles the virtual fields found on the capability lists + * in the configuration space. + * + * Author: Ryan Wilson + */ + +#include +#include +#include "pciback.h" +#include "conf_space.h" +#include "conf_space_capability.h" + +static LIST_HEAD(capabilities); + +static struct config_field caplist_header[] = { + { + .offset = PCI_CAP_LIST_ID, + .size = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */ + .u.w.read = pciback_read_config_word, + .u.w.write = NULL, + }, + { + .size = 0, + }, +}; + +static inline void register_capability(struct pciback_config_capability *cap) +{ + list_add_tail(&cap->cap_list, &capabilities); +} + +int pciback_config_capability_add_fields(struct pci_dev *dev) +{ + int err = 0; + struct pciback_config_capability *cap; + int cap_offset; + + list_for_each_entry(cap, &capabilities, cap_list) { + cap_offset = pci_find_capability(dev, cap->capability); + if (cap_offset) { + dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n", + cap->capability, cap_offset); + + err = pciback_config_add_fields_offset(dev, + caplist_header, + cap_offset); + if (err) + goto out; + err = pciback_config_add_fields_offset(dev, + cap->fields, + cap_offset); + if (err) + goto out; + } + } + + out: + return err; +} + +extern struct pciback_config_capability pciback_config_capability_vpd; +extern struct pciback_config_capability pciback_config_capability_pm; + +int pciback_config_capability_init(void) +{ + register_capability(&pciback_config_capability_vpd); + register_capability(&pciback_config_capability_pm); + + return 0; +} diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.h --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.h Tue Apr 11 17:40:03 2006 @@ -0,0 +1,23 @@ +/* + * PCI Backend - Data structures for special overlays for structures on + * the capability list. + * + * Author: Ryan Wilson + */ + +#ifndef __PCIBACK_CONFIG_CAPABILITY_H__ +#define __PCIBACK_CONFIG_CAPABILITY_H__ + +#include +#include + +struct pciback_config_capability { + struct list_head cap_list; + + int capability; + + /* If the device has the capability found above, add these fields */ + struct config_field *fields; +}; + +#endif diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c Tue Apr 11 17:40:03 2006 @@ -0,0 +1,121 @@ +/* + * PCI Backend - Configuration space overlay for power management + * + * Author: Ryan Wilson + */ + +#include +#include "conf_space.h" +#include "conf_space_capability.h" + +static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value, + void *data) +{ + int err; + u16 real_value; + + err = pci_read_config_word(dev, offset, &real_value); + if (err) + goto out; + + dev_dbg(&dev->dev, "pm_caps_read offset %x real_value %04x\n", + offset, real_value); + + *value = real_value & ~PCI_PM_CAP_PME_MASK; + + dev_dbg(&dev->dev, "pm_caps_read offset %x value %04x\n", + offset, *value); + + out: + return err; +} + +/* PM_OK_BITS specifies the bits that the driver domain is allowed to change. + * Can't allow driver domain to enable PMEs - they're shared */ +#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK) + +static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, + void *data) +{ + int err; + u16 cur_value; + pci_power_t new_state; + + dev_dbg(&dev->dev, "pm_ctrl_write - value %04x\n", new_value); + + /* Handle setting power state separately */ + new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); + + err = pci_read_config_word(dev, offset, &cur_value); + if (err) + goto out; + + new_value &= PM_OK_BITS; + if ((cur_value & PM_OK_BITS) != new_value) { + new_value = (cur_value & ~PM_OK_BITS) | new_value; + err = pci_write_config_word(dev, offset, new_value); + if (err) + goto out; + } + + /* Let pci core handle the power management change */ + dev_dbg(&dev->dev, "set power state to %x\n", new_state); + err = pci_set_power_state(dev, new_state); + if (err) + err = PCIBIOS_SET_FAILED; + + out: + return err; +} + +/* Ensure PMEs are disabled */ +static void *pm_ctrl_init(struct pci_dev *dev, int offset) +{ + int err; + u16 value; + + err = pci_read_config_word(dev, offset, &value); + if (err) + goto out; + + if (value & PCI_PM_CTRL_PME_ENABLE) { + value &= ~PCI_PM_CTRL_PME_ENABLE; + err = pci_write_config_word(dev, offset, value); + } + + out: + return ERR_PTR(err); +} + +static struct config_field caplist_pm[] = { + { + .offset = PCI_PM_PMC, + .size = 2, + .u.w.read = pm_caps_read, + }, + { + .offset = PCI_PM_CTRL, + .size = 2, + .init = pm_ctrl_init, + .u.w.read = pciback_read_config_word, + .u.w.write = pm_ctrl_write, + }, + { + .offset = PCI_PM_PPB_EXTENSIONS, + .size = 1, + .u.b.read = pciback_read_config_byte, + }, + { + .offset = PCI_PM_DATA_REGISTER, + .size = 1, + .u.b.read = pciback_read_config_byte, + }, + { + .size = 0, + }, +}; + +struct pciback_config_capability pciback_config_capability_pm = { + .capability = PCI_CAP_ID_PM, + .fields = caplist_pm, +}; diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_vpd.c --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_vpd.c Tue Apr 11 17:40:03 2006 @@ -0,0 +1,42 @@ +/* + * PCI Backend - Configuration space overlay for Vital Product Data + * + * Author: Ryan Wilson + */ + +#include +#include "conf_space.h" +#include "conf_space_capability.h" + +static int vpd_address_write(struct pci_dev *dev, int offset, u16 value, + void *data) +{ + /* Disallow writes to the vital product data */ + if (value & PCI_VPD_ADDR_F) + return PCIBIOS_SET_FAILED; + else + return pci_write_config_word(dev, offset, value); +} + +static struct config_field caplist_vpd[] = { + { + .offset = PCI_VPD_ADDR, + .size = 2, + .u.w.read = pciback_read_config_word, + .u.w.write = vpd_address_write, + }, + { + .offset = PCI_VPD_DATA, + .size = 4, + .u.dw.read = pciback_read_config_dword, + .u.dw.write = NULL, + }, + { + .size = 0, + }, +}; + +struct pciback_config_capability pciback_config_capability_vpd = { + .capability = PCI_CAP_ID_VPD, + .fields = caplist_vpd, +}; diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.c --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.c Tue Apr 11 17:40:03 2006 @@ -0,0 +1,73 @@ +/* + * PCI Backend - Handle special overlays for broken devices. + * + * Author: Ryan Wilson + */ + +#include +#include +#include "pciback.h" +#include "conf_space.h" +#include "conf_space_quirks.h" + +static LIST_HEAD(quirks); + +static int match_dev_to_drv(struct pci_dev *dev, const char *pci_drv_name) +{ + struct device_driver *drv; + int match = 0; + + if (!pci_drv_name) + return 0; + + /* Unfortunately, we won't find the driver if it's not loaded and + * we can't do a request_module here because of a potential deadlock + * on the semaphore in struct device */ + drv = driver_find(pci_drv_name, &pci_bus_type); + if (!drv) + return 0; + + if (pci_match_device(to_pci_driver(drv), dev) != NULL) + match = 1; + + put_driver(drv); + + return match; +} + +static inline void register_quirk(struct pciback_config_quirk *quirk) +{ + list_add_tail(&quirk->quirks_list, &quirks); +} + +int pciback_config_quirks_add_fields(struct pci_dev *dev) +{ + int err = 0; + struct pciback_config_quirk *quirk; + + list_for_each_entry(quirk, &quirks, quirks_list) { + if (pci_match_id(quirk->id_table, dev) != NULL + || match_dev_to_drv(dev, quirk->pci_driver_name)) { + dev_dbg(&dev->dev, "loading %s config. space quirks\n", + quirk->pci_driver_name); + + err = pciback_config_add_fields(dev, quirk->fields); + if (err) + goto out; + } + } + + + out: + return err; +} + +/* Add new quirks here & in pciback_config_quirks_init() */ +extern struct pciback_config_quirk pciback_config_quirk_tg3; + +int pciback_config_quirks_init(void) +{ + register_quirk(&pciback_config_quirk_tg3); + + return 0; +} diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.h --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.h Tue Apr 11 17:40:03 2006 @@ -0,0 +1,27 @@ +/* + * PCI Backend - Data structures for special overlays for broken devices. + * + * Author: Ryan Wilson + */ + +#ifndef __PCIBACK_CONFIG_QUIRKS_H__ +#define __PCIBACK_CONFIG_QUIRKS_H__ + +#include +#include + +struct pciback_config_quirk { + struct list_head quirks_list; + + /* Quirks are identified by pci_device_id. A quirk can provide its + * own id_table and/or hook into the table that the driver/module + * provides (hooking into the driver/module requires that module to + * be compiled *and* loaded in the domain with the PCI Backend */ + struct pci_device_id *id_table; + char *pci_driver_name; + + /* If the device matches the ids found above, do the following */ + struct config_field *fields; +}; + +#endif diff -r c783f340bef8 -r f4e09625f1dd linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks_tg3.c --- /dev/null Tue Apr 11 08:58:04 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks_tg3.c Tue Apr 11 17:40:03 2006 @@ -0,0 +1,144 @@ +/* + * PCI Backend - Special Config. Space overlay for tg3 devices + * + * Author: Ryan Wilson + */ + +#include +#include +#include "pciback.h" +#include "conf_space.h" +#include "conf_space_quirks.h" + +#include +#include +#include +#define TG3_VLAN_TAG_USED 0 +#include "../../net/tg3.h" + +static struct pci_device_id tg3_pci_tbl[] = { + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705M_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702A3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703A3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5782, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5788, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5789, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704S_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5752M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5753F, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5714, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5715, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1001, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1003, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC9100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_TIGON3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { 0, } +}; + +#define TG3_DWORD_FLD(reg) \ + { \ + .offset = reg, \ + .size = 4, \ + .u.dw.read = pciback_read_config_dword, \ + .u.dw.write = pciback_write_config_dword, \ + } + +static struct config_field tg3_fields[] = { + TG3_DWORD_FLD(TG3PCI_REG_BASE_ADDR), + TG3_DWORD_FLD(TG3PCI_MEM_WIN_BASE_ADDR), + TG3_DWORD_FLD(TG3PCI_REG_DATA), + TG3_DWORD_FLD(TG3PCI_MEM_WIN_DATA), + TG3_DWORD_FLD(TG3PCI_MISC_LOCAL_CTRL), + TG3_DWORD_FLD(TG3PCI_MISC_HOST_CTRL), + TG3_DWORD_FLD(TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_LOW), + TG3_DWORD_FLD(TG3PCI_STD_RING_PROD_IDX + TG3_64BIT_REG_HIGH), + TG3_DWORD_FLD(TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_LOW), + TG3_DWORD_FLD(TG3PCI_RCV_RET_RING_CON_IDX + TG3_64BIT_REG_HIGH), + TG3_DWORD_FLD(TG3PCI_PCISTATE), + { + .size = 0, + }, +}; + +struct pciback_config_quirk pciback_config_quirk_tg3 = { + .id_table = tg3_pci_tbl, + .pci_driver_name = "tg3", + .fields = tg3_fields, +};