Monday 22 August 2011

PCI Driver Flow

1. Fill up the pci_device_id table as follows.

static const struct pci_device_id pci_ids[] = {
{
.vendor = VENDOR_ID,
.device = DEVICE_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long) ~HOST_FORCE_PCI,
},

{   /* all zero 's */ },
};

2. This pci_device_id structure needs to be exported to user space to allow the hotplug
and module loading systems know what module works with what hardware devices.
The macro MODULE_DEVICE_TABLE accomplishes this.

An example is:
MODULE_DEVICE_TABLE(pci, pci_ids);

3. Fill the standard PCI_Driver structure with the PCI_id table,probe and remove functions.

say for example:

static struct pci_driver mypci_driver = {
.name       = DRIVER_NAME,
.id_table    = pci_ids,
.probe       = mypci_probe,
.remove     = __devexit_p(mypci_remove),
.suspend    = NULL,
.resume     = NULL,
};

 The main structure that all PCI drivers must create in order to be registered with the kernel properly is the struct pci_driver structure. This structure consists of a number of function callbacks and variables that describe the PCI driver to the PCI core.

4. Registering a PCI Driver:

**To register the struct pci_driver with the PCI core, a call to pci_register_driver (for network register_netdev,for char misc_register,for block drivers register_blkdev) is made with a pointer to the struct pci_driver.

**This is traditionally done in the module initialization code for the PCI driver:

static int pci_init(void)
{
PRINT(" \n\nPCI DRIVER IS LOADED IN KERNEL SPACE \n\n");
return pci_register_driver(&mypci_driver);
}

**Note that the pci_register_driver function either returns a negative error number
or 0 if everything was registered successfully.

5.Accessing PCI configuration space:

The configuration space can be accessed through 8-bit, 16-bit, or 32-bit data transfers at any time.
The relevant functions are prototyped in <linux/pci.h>:

int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

6. Enabling the PCI Device:

**In the probe function for the PCI driver, before the driver can access any device resource
(I/O region or interrupt) of the PCI device, the driver must call the pci_enable_device
function:

**int pci_enable_device(struct pci_dev *dev);

**This function actually enables the device. It wakes up the device and in some cases also assigns its interrupt line and I/O regions.

struct pci_dev {
        struct list_head global_list;   /* node in list of all PCI devices */
        struct list_head bus_list;      /* node in per-bus list */
        struct pci_bus  *bus;           /* bus this device is on */
        struct pci_bus  *subordinate;   /* bus this device bridges to */

        void            *sysdata;       /* hook for sys-specific extension */
        struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */

        unsigned int    devfn;          /* encoded device & function index */
        unsigned short  vendor;
        unsigned short  device;
        unsigned short  subsystem_vendor;
        unsigned short  subsystem_device;
        unsigned int    class;          /* 3 bytes: (base,sub,prog-if) */
        u8              hdr_type;       /* PCI header type (`multi' flag masked out) */
        u8              rom_base_reg;   /* which config register controls the ROM */
        u8              pin;            /* which interrupt pin this device uses */

        struct pci_driver *driver;      /* which driver has allocated this device */
        u64             dma_mask;       /* Mask of the bits of bus address this
                                           device implements.  Normally this is
                                           0xffffffff.  You only need to change
                                           this if your device has broken DMA
                                           or supports 64-bit transfers.  */

        pci_power_t     current_state;  /* Current operating state. In ACPI-speak,
                                           this is D0-D3, D0 being fully functional,
                                           and D3 being off. */

        pci_channel_state_t error_state;        /* current connectivity state */
        struct  device  dev;            /* Generic device interface */

        /* device is compatible with these IDs */
        unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
        unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];

        int             cfg_size;       /* Size of configuration space */

        /*
         * Instead of touching interrupt line and base address registers
         * directly, use the values stored here. They might be different!
         */
        unsigned int    irq;
        struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

        /* These fields are used by common fixups */
        unsigned int    transparent:1;  /* Transparent PCI bridge */
        unsigned int    multifunction:1;/* Part of multi-function device */
        /* keep track of device state */
        unsigned int    is_enabled:1;   /* pci_enable_device has been called */
        unsigned int    is_busmaster:1; /* device is busmaster */
        unsigned int    no_msi:1;       /* device may not use msi */
        unsigned int    block_ucfg_access:1;    /* userspace config space access is blocked */

        u32             saved_config_space[16]; /* config space saved at suspend time */
        struct hlist_head saved_cap_space;
        struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
        int rom_attr_enabled;           /* has display of the rom attribute been enabled? */
        struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
};


7.Enabling the PCI-Bus mastering for the device by calling pci_set_master Kernel API.
  This API enables bus-mastering on the device and calls pcibios_set_master  to do the needed arch specific settings
  It is done in the driver,if it is needed.

**BusMastering:
bus mastering is the capability of devices on the PCI bus (other than the system chipset, of course) to
  take control of the bus and perform transfers directly.PCI's design allows bus mastering of multiple devices on the bus simultaneously,
  with the arbitration circuitry working to ensure that no device on the bus (including the processor!) locks out any other device.
  At the same time though, it allows any given device to use the full bus throughput if no other device needs to transfer anything.

**PCI transactions work in a master-slave relationship. A master is an agent that initiates a transaction (can be a read or a write).
While the host CPU is often the bus master, all PCI boards can potentially claim the bus and become a bus master.

**When a PCI device is enabled, it's bus mastering is also enabled.  This occurs before any driver code is
executed.

8. Accessing Memory Regions

**unsigned long pci_resource_start(struct pci_dev *dev, int bar);
The function returns the first address (memory address or I/O port number)
associated with one of the six PCI I/O regions. The region is selected by the integer
bar (the base address register), ranging from 0–5 (inclusive).

**unsigned long pci_resource_end(struct pci_dev *dev, int bar);
The function returns the last address that is part of the I/O region number bar.
Note that this is the last usable address, not the first address after the region.

**unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
This function returns the flags associated with this resource.
All resource flags are defined in <linux/ioport.h>; the most important are:
IORESOURCE_IO
IORESOURCE_MEM ( if the resource type is memory resource, then we can access the register by means of readl/b/w/writel/b/w functions)

#define pci_resource_start(dev,bar)   ((dev)->resource[(bar)].start)
#define pci_resource_end(dev,bar)     ((dev)->resource[(bar)].end)
#define pci_resource_flags(dev,bar)   ((dev)->resource[(bar)].flags)
#define pci_resource_len(dev,bar) \
       ((pci_resource_start((dev),(bar)) == 0 &&       \
         pci_resource_end((dev),(bar)) ==              \
         pci_resource_start((dev),(bar))) ? 0 :        \
                                                       \
        (pci_resource_end((dev),(bar)) -               \
         pci_resource_start((dev),(bar)) + 1))

9.Reserving the PCI I/O and memory regions.

int pci_request_region (struct pci_dev * pdev, int bar, char * res_name);

Arguments:
pdev
    PCI device whose resources are to be reserved
bar
    BAR to be reserved
res_name
    Name to be associated with resource.

Description
Mark the PCI region associated with PCI device pdev BR bar as being reserved by owner res_name.
Do not access any address inside the PCI regions unless this call returns successfully.

10. Converting physical address to virtual address.

pci.pVirtualBaseAddr=(unsigned int *)ioremap_nocache(pci.PciBaseStart,pci.length);

11.

else
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD  := $(shell pwd)
default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
Endif


static struct miscdevice miscdev= {
        /*
         * We don't care what minor number we end up with, so tell the
         * kernel to just pick one.
         */
        MISC_DYNAMIC_MINOR,
        /*
         * Name ourselves /dev/UniPro.
         */
        "miscdev",
        /*
         * What functions to call when a program performs file
         * operations on the device.
         */
        &fops
};

misc_register(&miscdev);

No comments:

Post a Comment