PCI Driver - Read/Write to Base Address - Linux

This is a discussion on PCI Driver - Read/Write to Base Address - Linux ; Hey everyone, I am trying to read and write to a PCI Base Address. Right now, I have: static int rpm_probe(struct pci_dev *dev, const struct pci_device_id *id) { pci_enable_device(dev); u32 BaseAddress0; pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &BaseAddress0); printk(KERN_ALERT "PCI_BASE_ADDRESS_0: %x\n", BaseAddress0); } To ...

+ Reply to Thread
Results 1 to 7 of 7

Thread: PCI Driver - Read/Write to Base Address

  1. PCI Driver - Read/Write to Base Address

    Hey everyone,

    I am trying to read and write to a PCI Base Address. Right now, I have:

    static int rpm_probe(struct pci_dev *dev, const struct pci_device_id
    *id)
    {
    pci_enable_device(dev);

    u32 BaseAddress0;
    pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &BaseAddress0);
    printk(KERN_ALERT "PCI_BASE_ADDRESS_0: %x\n", BaseAddress0);
    }


    To read/write to that particular base address, do we just need to add
    the following lines:
    u32 BaseAddress0data;
    pci_read_config_dword(dev, BaseAddress0, &BaseAddress0data);
    pci_write_config_dword(dev, BaseAddress0, 0xFF000000);

    or is there another pci function that I should use?


  2. Re: PCI Driver - Read/Write to Base Address

    elliotng.ee@gmail.com wrote:
    > I am trying to read and write to a PCI Base Address. Right now, I have:
    >
    > static int rpm_probe(struct pci_dev *dev, const struct pci_device_id
    > *id)
    > {
    > pci_enable_device(dev);
    >
    > u32 BaseAddress0;
    > pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &BaseAddress0);
    > printk(KERN_ALERT "PCI_BASE_ADDRESS_0: %x\n", BaseAddress0);
    > }
    >
    >
    > To read/write to that particular base address, do we just need to add
    > the following lines:
    > u32 BaseAddress0data;
    > pci_read_config_dword(dev, BaseAddress0, &BaseAddress0data);
    > pci_write_config_dword(dev, BaseAddress0, 0xFF000000);
    >
    > or is there another pci function that I should use?


    No, that's not correct, if I understand what you're doing.

    First, you should get the base address value (for memory-mapped I/O
    region 0) with:
    int BarNumber = 0;
    pci_request_region(dev, BarNumber, "MyDriverName");
    BaseAddress0 = pci_resource_start(dev, BarNumber);
    BarLen = pci_resource_len(dev, BarNumber);

    This marks you as the owner of the region and returns to you its base
    (physical) address and length.

    Second, when you actually read or write the memory-mapped I/O in your
    device, you don't use pci_read_config* / pci_write_config* -- those are
    only used when you are reading / writing the device's *configuration
    space registers*. Reading the memory-mapped I/O region is done
    something like this:

    char * Region0 = ioremap_nocache(BaseAddress0, BarLen);
    Data = readl(Region0 + offsetToRead);
    writel(DataToWrite, Region0 + offsetToRead);

    ioremap_nocache creates a virtual-to-physical mapping in kernel address
    space that you can use to access the device memory.

    readl() and writel() are arch-specific macros that typically boil down
    to something like this (however, you should still use them since there
    may be slight variations between architectures):
    unsigned long readl(void * addr) { return *((volatile unsigned long
    *)addr)); }
    void writel(unsigned long value, void * addr) { *((volatile unsigned
    long *)addr) = value; }

    You might want to check out the book "Linux Device Drivers, version 3"
    which is available for free online. It has a chapter on PCI Drivers as
    well as info on kernel memory management.

    Also, check out the skeleton network driver in
    /usr/src/linux/drivers/net/pci-skeleton.c for some sample code.

    GH


  3. Re: PCI Driver - Read/Write to Base Address

    Thanks for your help.

    Unfortunately, I get this message in the kernel log:
    PCI: Unable to reserve mem region #2:4000@de000000

    Is there a way to get around this error? Is it because of the
    pci_request_region function?

    In addition, I eventually want to take the reading configuration
    register, reading PCI memory, and writing PCI memory code out of the
    probe function and set each one as a function. After the device driver
    is loaded, how do I call those functions from another C program?

    gil_hamilton@hotmail.com wrote:
    > elliotng.ee@gmail.com wrote:
    > > I am trying to read and write to a PCI Base Address. Right now, I have:
    > >
    > > static int rpm_probe(struct pci_dev *dev, const struct pci_device_id
    > > *id)
    > > {
    > > pci_enable_device(dev);
    > >
    > > u32 BaseAddress0;
    > > pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &BaseAddress0);
    > > printk(KERN_ALERT "PCI_BASE_ADDRESS_0: %x\n", BaseAddress0);
    > > }
    > >
    > >
    > > To read/write to that particular base address, do we just need to add
    > > the following lines:
    > > u32 BaseAddress0data;
    > > pci_read_config_dword(dev, BaseAddress0, &BaseAddress0data);
    > > pci_write_config_dword(dev, BaseAddress0, 0xFF000000);
    > >
    > > or is there another pci function that I should use?

    >
    > No, that's not correct, if I understand what you're doing.
    >
    > First, you should get the base address value (for memory-mapped I/O
    > region 0) with:
    > int BarNumber = 0;
    > pci_request_region(dev, BarNumber, "MyDriverName");
    > BaseAddress0 = pci_resource_start(dev, BarNumber);
    > BarLen = pci_resource_len(dev, BarNumber);
    >
    > This marks you as the owner of the region and returns to you its base
    > (physical) address and length.
    >
    > Second, when you actually read or write the memory-mapped I/O in your
    > device, you don't use pci_read_config* / pci_write_config* -- those are
    > only used when you are reading / writing the device's *configuration
    > space registers*. Reading the memory-mapped I/O region is done
    > something like this:
    >
    > char * Region0 = ioremap_nocache(BaseAddress0, BarLen);
    > Data = readl(Region0 + offsetToRead);
    > writel(DataToWrite, Region0 + offsetToRead);
    >
    > ioremap_nocache creates a virtual-to-physical mapping in kernel address
    > space that you can use to access the device memory.
    >
    > readl() and writel() are arch-specific macros that typically boil down
    > to something like this (however, you should still use them since there
    > may be slight variations between architectures):
    > unsigned long readl(void * addr) { return *((volatile unsigned long
    > *)addr)); }
    > void writel(unsigned long value, void * addr) { *((volatile unsigned
    > long *)addr) = value; }
    >
    > You might want to check out the book "Linux Device Drivers, version 3"
    > which is available for free online. It has a chapter on PCI Drivers as
    > well as info on kernel memory management.
    >
    > Also, check out the skeleton network driver in
    > /usr/src/linux/drivers/net/pci-skeleton.c for some sample code.
    >
    > GH



  4. Re: PCI Driver - Read/Write to Base Address

    Never mind, I was able to take ownership of the region. I just needed
    to add pci_release_regions(dev) in the remove function.

    Still awaiting a response to this question:
    In addition, I eventually want to take the reading configuration
    register, reading PCI memory, and writing PCI memory code out of the
    probe function and set each one as a function. After the device driver
    is loaded, how do I call those functions from another C program?

    elliotng.ee@gmail.com wrote:
    > Thanks for your help.
    >
    > Unfortunately, I get this message in the kernel log:
    > PCI: Unable to reserve mem region #2:4000@de000000
    >
    > Is there a way to get around this error? Is it because of the
    > pci_request_region function?
    >
    > In addition, I eventually want to take the reading configuration
    > register, reading PCI memory, and writing PCI memory code out of the
    > probe function and set each one as a function. After the device driver
    > is loaded, how do I call those functions from another C program?
    >
    > gil_hamilton@hotmail.com wrote:
    > > elliotng.ee@gmail.com wrote:
    > > > I am trying to read and write to a PCI Base Address. Right now, I have:
    > > >
    > > > static int rpm_probe(struct pci_dev *dev, const struct pci_device_id
    > > > *id)
    > > > {
    > > > pci_enable_device(dev);
    > > >
    > > > u32 BaseAddress0;
    > > > pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &BaseAddress0);
    > > > printk(KERN_ALERT "PCI_BASE_ADDRESS_0: %x\n", BaseAddress0);
    > > > }
    > > >
    > > >
    > > > To read/write to that particular base address, do we just need to add
    > > > the following lines:
    > > > u32 BaseAddress0data;
    > > > pci_read_config_dword(dev, BaseAddress0, &BaseAddress0data);
    > > > pci_write_config_dword(dev, BaseAddress0, 0xFF000000);
    > > >
    > > > or is there another pci function that I should use?

    > >
    > > No, that's not correct, if I understand what you're doing.
    > >
    > > First, you should get the base address value (for memory-mapped I/O
    > > region 0) with:
    > > int BarNumber = 0;
    > > pci_request_region(dev, BarNumber, "MyDriverName");
    > > BaseAddress0 = pci_resource_start(dev, BarNumber);
    > > BarLen = pci_resource_len(dev, BarNumber);
    > >
    > > This marks you as the owner of the region and returns to you its base
    > > (physical) address and length.
    > >
    > > Second, when you actually read or write the memory-mapped I/O in your
    > > device, you don't use pci_read_config* / pci_write_config* -- those are
    > > only used when you are reading / writing the device's *configuration
    > > space registers*. Reading the memory-mapped I/O region is done
    > > something like this:
    > >
    > > char * Region0 = ioremap_nocache(BaseAddress0, BarLen);
    > > Data = readl(Region0 + offsetToRead);
    > > writel(DataToWrite, Region0 + offsetToRead);
    > >
    > > ioremap_nocache creates a virtual-to-physical mapping in kernel address
    > > space that you can use to access the device memory.
    > >
    > > readl() and writel() are arch-specific macros that typically boil down
    > > to something like this (however, you should still use them since there
    > > may be slight variations between architectures):
    > > unsigned long readl(void * addr) { return *((volatile unsigned long
    > > *)addr)); }
    > > void writel(unsigned long value, void * addr) { *((volatile unsigned
    > > long *)addr) = value; }
    > >
    > > You might want to check out the book "Linux Device Drivers, version 3"
    > > which is available for free online. It has a chapter on PCI Drivers as
    > > well as info on kernel memory management.
    > >
    > > Also, check out the skeleton network driver in
    > > /usr/src/linux/drivers/net/pci-skeleton.c for some sample code.
    > >
    > > GH



  5. Re: PCI Driver - Read/Write to Base Address


    elliotng.ee@gmail.com wrote:

    > Still awaiting a response to this question:
    > In addition, I eventually want to take the reading configuration
    > register, reading PCI memory, and writing PCI memory code out of the
    > probe function and set each one as a function. After the device driver
    > is loaded, how do I call those functions from another C program?


    Whatever way makes the most sense for your driver. An 'ioctl' on a
    device file might be one way. Manipulating a file in '/proc' might be
    another.

    DS


  6. Re: PCI Driver - Read/Write to Base Address

    Can you utilize ioctl for a pci device driver? I know it can be used
    for a char driver.

    Is there a sample code with ioctl being utilized for a pci device
    driver?

    David Schwartz wrote:
    > elliotng.ee@gmail.com wrote:
    >
    > > Still awaiting a response to this question:
    > > In addition, I eventually want to take the reading configuration
    > > register, reading PCI memory, and writing PCI memory code out of the
    > > probe function and set each one as a function. After the device driver
    > > is loaded, how do I call those functions from another C program?

    >
    > Whatever way makes the most sense for your driver. An 'ioctl' on a
    > device file might be one way. Manipulating a file in '/proc' might be
    > another.
    >
    > DS



  7. Re: PCI Driver - Read/Write to Base Address

    elliotng.ee@gmail.com wrote:
    > Can you utilize ioctl for a pci device driver? I know it can be used
    > for a char driver.
    >
    > Is there a sample code with ioctl being utilized for a pci device
    > driver?


    Yes. "PCI driver" and "char driver" are largely orthogonal. "PCI
    driver" refers to how the driver interfaces to the device. "Char
    driver" refers to how the driver interfaces with user programs and the
    rest of the operating system.

    To find some samples, cd to /usr/src/linux and type
    grep -l pci_dev drivers/char/*.c | xargs grep ioctl

    GH


+ Reply to Thread