1
0
Fork 0

Merge branch 'for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata

Pull libata updates from Tejun Heo:

 - Adam added opt-in ATA command priority support.

 - There are machines which hide multiple nvme devices behind an ahci
   BAR. Dan Williams proposed a solution to force-switch the mode but
   deemed too hackishd. People are gonna discuss the proper way to
   handle the situation in nvme standard meetings. For now, detect and
   warn about the situation.

 - Low level driver specific changes.

Christoph Hellwig pipes in about the hidden nvme warning:
 "I wish that was the case. We've pretty much agreed that we'll want to
  implement it as a virtual PCIe root bridge, similar to Intels other
  'innovation' VMD that we work around that way.

  But Intel management has apparently decided that they don't want to
  spend more cycles on this now that Lenovo has an optional BIOS that
  doesn't force this broken mode anymore, and no one outside of Intel
  has enough information to implement something like this.

  So for now I guess this warning is it, until Intel reconsideres and
  spends resources on fixing up the damage their Chipset people caused"

* 'for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata:
  ahci: warn about remapped NVMe devices
  ahci-remap.h: add ahci remapping definitions
  nvme: move NVMe class code to pci_ids.h
  pata: imx: support controller modes up to PIO4
  pata: imx: add support of setting timings for PIO modes
  pata: imx: set controller PIO mode with .set_piomode callback
  pata: imx: sort headers out
  ata: set ncq_prio_enabled iff device has support
  ata: ATA Command Priority Disabled By Default
  ata: Enabling ATA Command Priorities
  block: Add iocontext priority to request
  ahci: qoriq: added ls1046a platform support
hifive-unleashed-5.1
Linus Torvalds 2016-12-13 13:26:24 -08:00
commit b92e09bb5b
14 changed files with 283 additions and 36 deletions

View File

@ -1154,6 +1154,7 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
blk_rq_init(q, rq);
blk_rq_set_rl(rq, rl);
blk_rq_set_prio(rq, ioc);
rq->cmd_flags = op;
rq->rq_flags = rq_flags;
@ -1626,7 +1627,8 @@ void init_request_from_bio(struct request *req, struct bio *bio)
req->errors = 0;
req->__sector = bio->bi_iter.bi_sector;
req->ioprio = bio_prio(bio);
if (ioprio_valid(bio_prio(bio)))
req->ioprio = bio_prio(bio);
blk_rq_bio_prep(req->q, req, bio);
}

View File

@ -46,6 +46,8 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <linux/ahci-remap.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include "ahci.h"
#define DRV_NAME "ahci"
@ -1400,6 +1402,40 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
}
#endif
static void ahci_remap_check(struct pci_dev *pdev, int bar,
struct ahci_host_priv *hpriv)
{
int i, count = 0;
u32 cap;
/*
* Check if this device might have remapped nvme devices.
*/
if (pdev->vendor != PCI_VENDOR_ID_INTEL ||
pci_resource_len(pdev, bar) < SZ_512K ||
bar != AHCI_PCI_BAR_STANDARD ||
!(readl(hpriv->mmio + AHCI_VSCAP) & 1))
return;
cap = readq(hpriv->mmio + AHCI_REMAP_CAP);
for (i = 0; i < AHCI_MAX_REMAP; i++) {
if ((cap & (1 << i)) == 0)
continue;
if (readl(hpriv->mmio + ahci_remap_dcc(i))
!= PCI_CLASS_STORAGE_EXPRESS)
continue;
/* We've found a remapped device */
count++;
}
if (!count)
return;
dev_warn(&pdev->dev, "Found %d remapped NVMe devices.\n", count);
dev_warn(&pdev->dev, "Switch your BIOS from RAID to AHCI mode to use them.\n");
}
static int ahci_get_irq_vector(struct ata_host *host, int port)
{
return pci_irq_vector(to_pci_dev(host->dev), port);
@ -1541,6 +1577,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
/* detect remapped nvme devices */
ahci_remap_check(pdev, ahci_pci_bar, hpriv);
/* must set flag prior to save config in order to take effect */
if (ahci_broken_devslp(pdev))
hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;

View File

@ -46,11 +46,13 @@
#define LS1021A_AXICC_ADDR 0xC0
#define SATA_ECC_DISABLE 0x00020000
#define LS1046A_SATA_ECC_DIS 0x80000000
enum ahci_qoriq_type {
AHCI_LS1021A,
AHCI_LS1043A,
AHCI_LS2080A,
AHCI_LS1046A,
};
struct ahci_qoriq_priv {
@ -63,6 +65,7 @@ static const struct of_device_id ahci_qoriq_of_match[] = {
{ .compatible = "fsl,ls1021a-ahci", .data = (void *)AHCI_LS1021A},
{ .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A},
{ .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A},
{ .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A},
{},
};
MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match);
@ -175,6 +178,13 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
break;
case AHCI_LS1046A:
writel(LS1046A_SATA_ECC_DIS, qpriv->ecc_addr);
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
break;
}
return 0;
@ -204,9 +214,9 @@ static int ahci_qoriq_probe(struct platform_device *pdev)
qoriq_priv->type = (enum ahci_qoriq_type)of_id->data;
if (qoriq_priv->type == AHCI_LS1021A) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"sata-ecc");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"sata-ecc");
if (res) {
qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(qoriq_priv->ecc_addr))
return PTR_ERR(qoriq_priv->ecc_addr);

View File

@ -140,6 +140,7 @@ EXPORT_SYMBOL_GPL(ahci_shost_attrs);
struct device_attribute *ahci_sdev_attrs[] = {
&dev_attr_sw_activity,
&dev_attr_unload_heads,
&dev_attr_ncq_prio_enable,
NULL
};
EXPORT_SYMBOL_GPL(ahci_sdev_attrs);

View File

@ -739,6 +739,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
* @n_block: Number of blocks
* @tf_flags: RW/FUA etc...
* @tag: tag
* @class: IO priority class
*
* LOCKING:
* None.
@ -753,7 +754,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
*/
int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag)
unsigned int tag, int class)
{
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->flags |= tf_flags;
@ -785,6 +786,12 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
tf->device = ATA_LBA;
if (tf->flags & ATA_TFLAG_FUA)
tf->device |= 1 << 7;
if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE) {
if (class == IOPRIO_CLASS_RT)
tf->hob_nsect |= ATA_PRIO_HIGH <<
ATA_SHIFT_PRIO;
}
} else if (dev->flags & ATA_DFLAG_LBA) {
tf->flags |= ATA_TFLAG_LBA;
@ -2156,6 +2163,30 @@ static void ata_dev_config_ncq_non_data(struct ata_device *dev)
}
}
static void ata_dev_config_ncq_prio(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
err_mask = ata_read_log_page(dev,
ATA_LOG_SATA_ID_DEV_DATA,
ATA_LOG_SATA_SETTINGS,
ap->sector_buf,
1);
if (err_mask) {
ata_dev_dbg(dev,
"failed to get Identify Device data, Emask 0x%x\n",
err_mask);
return;
}
if (ap->sector_buf[ATA_LOG_NCQ_PRIO_OFFSET] & BIT(3))
dev->flags |= ATA_DFLAG_NCQ_PRIO;
else
ata_dev_dbg(dev, "SATA page does not support priority\n");
}
static int ata_dev_config_ncq(struct ata_device *dev,
char *desc, size_t desc_sz)
{
@ -2205,6 +2236,8 @@ static int ata_dev_config_ncq(struct ata_device *dev,
ata_dev_config_ncq_send_recv(dev);
if (ata_id_has_ncq_non_data(dev->id))
ata_dev_config_ncq_non_data(dev);
if (ata_id_has_ncq_prio(dev->id))
ata_dev_config_ncq_prio(dev);
}
return 0;

View File

@ -50,6 +50,7 @@
#include <linux/uaccess.h>
#include <linux/suspend.h>
#include <asm/unaligned.h>
#include <linux/ioprio.h>
#include "libata.h"
#include "libata-transport.h"
@ -270,6 +271,79 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR,
ata_scsi_park_show, ata_scsi_park_store);
EXPORT_SYMBOL_GPL(dev_attr_unload_heads);
static ssize_t ata_ncq_prio_enable_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(device);
struct ata_port *ap;
struct ata_device *dev;
bool ncq_prio_enable;
int rc = 0;
ap = ata_shost_to_port(sdev->host);
spin_lock_irq(ap->lock);
dev = ata_scsi_find_dev(ap, sdev);
if (!dev) {
rc = -ENODEV;
goto unlock;
}
ncq_prio_enable = dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLE;
unlock:
spin_unlock_irq(ap->lock);
return rc ? rc : snprintf(buf, 20, "%u\n", ncq_prio_enable);
}
static ssize_t ata_ncq_prio_enable_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct scsi_device *sdev = to_scsi_device(device);
struct ata_port *ap;
struct ata_device *dev;
long int input;
unsigned long flags;
int rc;
rc = kstrtol(buf, 10, &input);
if (rc)
return rc;
if ((input < 0) || (input > 1))
return -EINVAL;
ap = ata_shost_to_port(sdev->host);
spin_lock_irqsave(ap->lock, flags);
dev = ata_scsi_find_dev(ap, sdev);
if (unlikely(!dev)) {
rc = -ENODEV;
goto unlock;
}
if (input) {
if (!(dev->flags & ATA_DFLAG_NCQ_PRIO)) {
rc = -EOPNOTSUPP;
goto unlock;
}
dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLE;
} else {
dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLE;
}
unlock:
spin_unlock_irqrestore(ap->lock, flags);
return rc ? rc : len;
}
DEVICE_ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR,
ata_ncq_prio_enable_show, ata_ncq_prio_enable_store);
EXPORT_SYMBOL_GPL(dev_attr_ncq_prio_enable);
void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd,
u8 sk, u8 asc, u8 ascq)
{
@ -401,6 +475,7 @@ EXPORT_SYMBOL_GPL(dev_attr_sw_activity);
struct device_attribute *ata_common_sdev_attrs[] = {
&dev_attr_unload_heads,
&dev_attr_ncq_prio_enable,
NULL
};
EXPORT_SYMBOL_GPL(ata_common_sdev_attrs);
@ -1756,6 +1831,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *scmd = qc->scsicmd;
const u8 *cdb = scmd->cmnd;
struct request *rq = scmd->request;
int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
unsigned int tf_flags = 0;
u64 block;
u32 n_block;
@ -1822,7 +1899,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
qc->nbytes = n_block * scmd->device->sector_size;
rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags,
qc->tag);
qc->tag, class);
if (likely(rc == 0))
return 0;

View File

@ -66,7 +66,7 @@ extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf);
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag);
extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag);
unsigned int tag, int class);
extern u64 ata_tf_read_block(const struct ata_taskfile *tf,
struct ata_device *dev);
extern unsigned ata_exec_internal(struct ata_device *dev,

View File

@ -11,19 +11,26 @@
*
* TODO:
* - dmaengine support
* - check if timing stuff needed
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <scsi/scsi_host.h>
#include <linux/ata.h>
#include <linux/libata.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/libata.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define DRV_NAME "pata_imx"
#define PATA_IMX_ATA_TIME_OFF 0x00
#define PATA_IMX_ATA_TIME_ON 0x01
#define PATA_IMX_ATA_TIME_1 0x02
#define PATA_IMX_ATA_TIME_2W 0x03
#define PATA_IMX_ATA_TIME_2R 0x04
#define PATA_IMX_ATA_TIME_AX 0x05
#define PATA_IMX_ATA_TIME_PIO_RDX 0x06
#define PATA_IMX_ATA_TIME_4 0x07
#define PATA_IMX_ATA_TIME_9 0x08
#define PATA_IMX_ATA_CONTROL 0x24
#define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7)
#define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6)
@ -33,6 +40,10 @@
#define PATA_IMX_DRIVE_DATA 0xA0
#define PATA_IMX_DRIVE_CONTROL 0xD8
static u32 pio_t4[] = { 30, 20, 15, 10, 10 };
static u32 pio_t9[] = { 20, 15, 10, 10, 10 };
static u32 pio_tA[] = { 35, 35, 35, 35, 35 };
struct pata_imx_priv {
struct clk *clk;
/* timings/interrupt/control regs */
@ -40,28 +51,49 @@ struct pata_imx_priv {
u32 ata_ctl;
};
static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused)
static void pata_imx_set_timing(struct ata_device *adev,
struct pata_imx_priv *priv)
{
struct ata_timing timing;
unsigned long clkrate;
u32 T, mode;
clkrate = clk_get_rate(priv->clk);
if (adev->pio_mode < XFER_PIO_0 || adev->pio_mode > XFER_PIO_4 ||
!clkrate)
return;
T = 1000000000 / clkrate;
ata_timing_compute(adev, adev->pio_mode, &timing, T * 1000, 0);
mode = adev->pio_mode - XFER_PIO_0;
writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_OFF);
writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_ON);
writeb(timing.setup, priv->host_regs + PATA_IMX_ATA_TIME_1);
writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2W);
writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2R);
writeb(1, priv->host_regs + PATA_IMX_ATA_TIME_PIO_RDX);
writeb(pio_t4[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_4);
writeb(pio_t9[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_9);
writeb(pio_tA[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_AX);
}
static void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
struct ata_device *dev;
struct ata_port *ap = link->ap;
struct pata_imx_priv *priv = ap->host->private_data;
u32 val;
ata_for_each_dev(dev, link, ENABLED) {
dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
dev->xfer_shift = ATA_SHIFT_PIO;
dev->flags |= ATA_DFLAG_PIO;
pata_imx_set_timing(adev, priv);
val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
if (ata_pio_need_iordy(dev))
val |= PATA_IMX_ATA_CTRL_IORDY_EN;
else
val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
__raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
ata_dev_info(dev, "configured for PIO\n");
}
return 0;
val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
if (ata_pio_need_iordy(adev))
val |= PATA_IMX_ATA_CTRL_IORDY_EN;
else
val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
__raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
}
static struct scsi_host_template pata_imx_sht = {
@ -72,7 +104,7 @@ static struct ata_port_operations pata_imx_port_ops = {
.inherits = &ata_sff_port_ops,
.sff_data_xfer = ata_sff_data_xfer_noirq,
.cable_detect = ata_cable_unknown,
.set_mode = pata_imx_set_mode,
.set_piomode = pata_imx_set_piomode,
};
static void pata_imx_setup_port(struct ata_ioports *ioaddr)
@ -128,7 +160,7 @@ static int pata_imx_probe(struct platform_device *pdev)
ap = host->ports[0];
ap->ops = &pata_imx_port_ops;
ap->pio_mask = ATA_PIO0;
ap->pio_mask = ATA_PIO4;
ap->flags |= ATA_FLAG_SLAVE_POSS;
io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@ -2086,9 +2086,6 @@ static const struct pci_error_handlers nvme_err_handler = {
.reset_notify = nvme_reset_notify,
};
/* Move to pci_ids.h later */
#define PCI_CLASS_STORAGE_EXPRESS 0x010802
static const struct pci_device_id nvme_id_table[] = {
{ PCI_VDEVICE(INTEL, 0x0953),
.driver_data = NVME_QUIRK_STRIPE_SIZE |

View File

@ -0,0 +1,28 @@
#ifndef _LINUX_AHCI_REMAP_H
#define _LINUX_AHCI_REMAP_H
#include <linux/sizes.h>
#define AHCI_VSCAP 0xa4
#define AHCI_REMAP_CAP 0x800
/* device class code */
#define AHCI_REMAP_N_DCC 0x880
/* remap-device base relative to ahci-bar */
#define AHCI_REMAP_N_OFFSET SZ_16K
#define AHCI_REMAP_N_SIZE SZ_16K
#define AHCI_MAX_REMAP 3
static inline unsigned int ahci_remap_dcc(int i)
{
return AHCI_REMAP_N_DCC + i * 0x80;
}
static inline unsigned int ahci_remap_base(int i)
{
return AHCI_REMAP_N_OFFSET + i * AHCI_REMAP_N_SIZE;
}
#endif /* _LINUX_AHCI_REMAP_H */

View File

@ -348,6 +348,7 @@ enum {
ATA_LOG_DEVSLP_DETO = 0x01,
ATA_LOG_DEVSLP_VALID = 0x07,
ATA_LOG_DEVSLP_VALID_MASK = 0x80,
ATA_LOG_NCQ_PRIO_OFFSET = 0x09,
/* NCQ send and receive log */
ATA_LOG_NCQ_SEND_RECV_SUBCMDS_OFFSET = 0x00,
@ -940,6 +941,11 @@ static inline bool ata_id_has_ncq_non_data(const u16 *id)
return id[ATA_ID_SATA_CAPABILITY_2] & BIT(5);
}
static inline bool ata_id_has_ncq_prio(const u16 *id)
{
return id[ATA_ID_SATA_CAPABILITY] & BIT(12);
}
static inline bool ata_id_has_trim(const u16 *id)
{
if (ata_id_major_version(id) >= 7 &&

View File

@ -1058,6 +1058,20 @@ static inline unsigned int blk_rq_count_bios(struct request *rq)
return nr_bios;
}
/*
* blk_rq_set_prio - associate a request with prio from ioc
* @rq: request of interest
* @ioc: target iocontext
*
* Assocate request prio with ioc prio so request based drivers
* can leverage priority information.
*/
static inline void blk_rq_set_prio(struct request *rq, struct io_context *ioc)
{
if (ioc)
rq->ioprio = ioc->ioprio;
}
/*
* Request issue related functions.
*/

View File

@ -166,6 +166,8 @@ enum {
ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */
ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */
ATA_DFLAG_NCQ_SEND_RECV = (1 << 19), /* device supports NCQ SEND and RECV */
ATA_DFLAG_NCQ_PRIO = (1 << 20), /* device supports NCQ priority */
ATA_DFLAG_NCQ_PRIO_ENABLE = (1 << 21), /* Priority cmds sent to dev */
ATA_DFLAG_INIT_MASK = (1 << 24) - 1,
ATA_DFLAG_DETACH = (1 << 24),
@ -342,7 +344,9 @@ enum {
ATA_SHIFT_PIO = 0,
ATA_SHIFT_MWDMA = ATA_SHIFT_PIO + ATA_NR_PIO_MODES,
ATA_SHIFT_UDMA = ATA_SHIFT_MWDMA + ATA_NR_MWDMA_MODES,
ATA_SHIFT_PRIO = 6,
ATA_PRIO_HIGH = 2,
/* size of buffer to pad xfers ending on unaligned boundaries */
ATA_DMA_PAD_SZ = 4,
@ -542,6 +546,7 @@ typedef void (*ata_postreset_fn_t)(struct ata_link *link, unsigned int *classes)
extern struct device_attribute dev_attr_link_power_management_policy;
extern struct device_attribute dev_attr_unload_heads;
extern struct device_attribute dev_attr_ncq_prio_enable;
extern struct device_attribute dev_attr_em_message_type;
extern struct device_attribute dev_attr_em_message;
extern struct device_attribute dev_attr_sw_activity;

View File

@ -23,8 +23,10 @@
#define PCI_CLASS_STORAGE_SATA 0x0106
#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601
#define PCI_CLASS_STORAGE_SAS 0x0107
#define PCI_CLASS_STORAGE_EXPRESS 0x010802
#define PCI_CLASS_STORAGE_OTHER 0x0180
#define PCI_BASE_CLASS_NETWORK 0x02
#define PCI_CLASS_NETWORK_ETHERNET 0x0200
#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201