portdrv_pci.c 5.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * File:	portdrv_pci.c
 * Purpose:	PCI Express Port Bus Driver
 *
 * Copyright (C) 2004 Intel
 * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/pcieport_if.h>

#include "portdrv.h"

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
#define DRIVER_DESC "PCIE Port Bus Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

/* global data */
static const char device_name[] = "pcieport-driver";

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
static void pci_save_msi_state(struct pci_dev *dev)
{
	struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
	int i = 0, pos;
	u16 control;

   	if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSI)) <= 0)
		return;

	pci_read_config_dword(dev, pos, &p_ext->saved_msi_config_space[i++]);
	control = p_ext->saved_msi_config_space[0] >> 16;
	pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
		&p_ext->saved_msi_config_space[i++]);
	if (control & PCI_MSI_FLAGS_64BIT) {
		pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
			&p_ext->saved_msi_config_space[i++]);
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_64,
			&p_ext->saved_msi_config_space[i++]);
	} else
		pci_read_config_dword(dev, pos + PCI_MSI_DATA_32,
			&p_ext->saved_msi_config_space[i++]);
	if (control & PCI_MSI_FLAGS_MASKBIT)
		pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT,
			&p_ext->saved_msi_config_space[i++]);
}

static void pci_restore_msi_state(struct pci_dev *dev)
{
	struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
	int i = 0, pos;
	u16 control;

   	if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSI)) <= 0)
		return;

	control = p_ext->saved_msi_config_space[i++] >> 16;
	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
	pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
		p_ext->saved_msi_config_space[i++]);
	if (control & PCI_MSI_FLAGS_64BIT) {
		pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
			p_ext->saved_msi_config_space[i++]);
		pci_write_config_dword(dev, pos + PCI_MSI_DATA_64,
			p_ext->saved_msi_config_space[i++]);
	} else
		pci_write_config_dword(dev, pos + PCI_MSI_DATA_32,
			p_ext->saved_msi_config_space[i++]);
	if (control & PCI_MSI_FLAGS_MASKBIT)
		pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT,
			p_ext->saved_msi_config_space[i++]);
}

static void pcie_portdrv_save_config(struct pci_dev *dev)
{
	struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);

	pci_save_state(dev);
	if (p_ext->interrupt_mode == PCIE_PORT_MSI_MODE)
		pci_save_msi_state(dev);
}

93
static int pcie_portdrv_restore_config(struct pci_dev *dev)
94 95
{
	struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
96
	int retval;
97 98 99 100

	pci_restore_state(dev);
	if (p_ext->interrupt_mode == PCIE_PORT_MSI_MODE)
		pci_restore_msi_state(dev);
101 102 103
	retval = pci_enable_device(dev);
	if (retval)
		return retval;
104
	pci_set_master(dev);
105
	return 0;
106 107
}

L
Linus Torvalds 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/*
 * pcie_portdrv_probe - Probe PCI-Express port devices
 * @dev: PCI-Express port device being probed
 *
 * If detected invokes the pcie_port_device_register() method for 
 * this port device.
 *
 */
static int __devinit pcie_portdrv_probe (struct pci_dev *dev, 
				const struct pci_device_id *id )
{
	int			status;

	status = pcie_port_device_probe(dev);
	if (status)
		return status;

	if (pci_enable_device(dev) < 0) 
		return -ENODEV;
	
	pci_set_master(dev);
        if (!dev->irq) {
		printk(KERN_WARNING 
		"%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n", 
		__FUNCTION__, dev->device, dev->vendor);
	}
	if (pcie_port_device_register(dev)) 
		return -ENOMEM;

	return 0;
}

static void pcie_portdrv_remove (struct pci_dev *dev)
{
	pcie_port_device_remove(dev);
143
	kfree(pci_get_drvdata(dev));
L
Linus Torvalds 已提交
144 145 146
}

#ifdef CONFIG_PM
147
static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state)
L
Linus Torvalds 已提交
148
{
149 150 151 152
	int ret = pcie_port_device_suspend(dev, state);

	pcie_portdrv_save_config(dev);
	return ret;
L
Linus Torvalds 已提交
153 154 155 156
}

static int pcie_portdrv_resume (struct pci_dev *dev)
{
157
	pcie_portdrv_restore_config(dev);
L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	return pcie_port_device_resume(dev);
}
#endif

/*
 * LINUX Device Driver Model
 */
static const struct pci_device_id port_pci_ids[] = { {
	/* handle any PCI-Express port */
	PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
	}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);

static struct pci_driver pcie_portdrv = {
	.name		= (char *)device_name,
	.id_table	= &port_pci_ids[0],

	.probe		= pcie_portdrv_probe,
	.remove		= pcie_portdrv_remove,

#ifdef	CONFIG_PM
	.suspend	= pcie_portdrv_suspend,
	.resume		= pcie_portdrv_resume,
#endif	/* PM */
};

static int __init pcie_portdrv_init(void)
{
	int retval = 0;

	pcie_port_bus_register();
	retval = pci_register_driver(&pcie_portdrv);
	if (retval)
		pcie_port_bus_unregister();
	return retval;
}

static void __exit pcie_portdrv_exit(void) 
{
	pci_unregister_driver(&pcie_portdrv);
	pcie_port_bus_unregister();
}

module_init(pcie_portdrv_init);
module_exit(pcie_portdrv_exit);