Question

I have a custom board with Armada 370 SoC in which a Broadcom L2 switch is now being added via PCI-E to the Soc.
The board runs on linux. I want to just initialize the L2 switch registers.
I just want very minimal access so that I can access the registers of L2 switch(using a program which uses /dev/mem - I have the application).
Am new and I would like to know what needs to be done in PCI-E drivers and menuconfig and etc.
I would be happy if someone could point to a resource which explains all this stuff from scratch because I want to learn more. Will I be able to access the registers if I just do the memory mapping correctly? Do I need to do anything more?

Was it helpful?

Solution

This should get you on your way. It sets up BAR0 for access. All you have to do is figure out how you want software to access the driver and implement those handlers: read/write/open/close/ioctl, etc.

#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/tty.h>
#include <linux/version.h>

void error_msg (const char *msg, ...);  /* provide by calling possibly kprintf() */

static dev_t        dev_num;        /* major/minor device numbers */
static struct cdev  c_dev, pci_dev;     /* character device structure */
static const char   DEVICE_NAME[] = "mydevice"; /* name for /dev/... */

static unsigned long        bar0_len;
static unsigned char __iomem    *bar0_mem;

static struct file_operations mydevice_fops = {
    .owner      = THIS_MODULE,
//  .open       = (function to handle open),
//  .read       = (function to handle read),
//  .write      = (function to handle write),
//  .close      = (function to handle close),
//  .unlocked_ioctl = (function to handle ioctl),
};

static int mydevice_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
    int     ret;
    char        name[20];
    dev_t       unit_num;
    struct device   *dev_p;

    ret = pci_enable_device (dev);
    if (ret)
    {
        error_msg ("error %d enabling device");
        return ret;
    }
    bar0_len = pci_resource_len (dev, 0);
    bar0_mem = pci_iomap (dev, 0, bar0_len);
    if (!bar0_len  ||  !bar0_mem)       /* device not there */
    {
        error_msg ("device bar0 missing");
        return -1;
    }
    snprintf (name, sizeof name, "%s%d", DEVICE_NAME, 1);  /* create device name */
    unit_num = MKDEV(MAJOR(dev_num), 1);
    dev_p = device_create (NULL, NULL, unit_num, NULL, name);
    if (IS_ERR(dev_p))
    {
        error_msg ("error creating pci device %s", name);
        return -1;
    }
    cdev_init (&pci_dev, &mydevice_fops);
    mydevice_fops.owner = THIS_MODULE;
    ret = cdev_add (&pci_dev, unit_num, 1);
    if (ret < 0)
    {
        error_msg ("error adding pci device");
        device_destroy (NULL, unit_num);
        return ret;
    }

    pci_set_master (dev);
    return 0;
}

static void mydevice_pci_remove (struct pci_dev *dev)
{
    cdev_del (&c_dev);
    device_destroy (NULL, dev_num);
    pci_iounmap (dev, bar0_mem);
    pci_disable_device (dev);
}


static struct pci_device_id mydevice_ids[] = {
{
    0xabcd,     /* vendor/manufacturer ID */
    0x1234,     /* device/vendor device ID */
    PCI_ANY_ID, /* subvendor:  don't care */
    PCI_ANY_ID, /* subdevice:  don't care */
    0,      /* class:  don't care */
    0,      /* class_mask:  don't care */
    0,      /* ulong_t driver_data:  private driver data */
    },

    {}      /* end of pci device IDs */
};

static struct pci_driver mydriver_ops = {
    .name       = DEVICE_NAME,
    .id_table   = mydevice_ids,
    .probe      = mydevice_pci_probe,
    .remove     = mydevice_pci_remove,
    /*
     * For pci bus error recovery, see
     * https://www.kernel.org/doc/Documentation/PCI/pcieaer-howto.txt
     */
};

static struct file_operations mydriver_fops = {
    .owner = THIS_MODULE,
};


static int __init mydriver_init (void)
{
    struct device  *mydriver_device;

    int ret = alloc_chrdev_region (&dev_num, 0, 1, DEVICE_NAME);
    if (ret)
    {
        error_msg ("unable to allocate major/minor device number");
        return ret;
    }

    mydriver_device = device_create (NULL, NULL, dev_num, NULL, DEVICE_NAME);
    if (IS_ERR(mydriver_device))
    {
        error_msg ("error creating device");
        unregister_chrdev_region (dev_num, 1);
        return -ENODEV;
    }

    cdev_init (&c_dev, &mydevice_fops);
    c_dev.owner = THIS_MODULE;
    ret = cdev_add (&c_dev, dev_num, 1);
    if (ret < 0)
    {
        error_msg ("error adding device");
        device_destroy (NULL, dev_num);
        unregister_chrdev_region (dev_num, 1);
        return ret;
    }

    ret = pci_register_driver (&mydriver_ops);  // this is key to PCI devices
    if (ret < 0)
    {
        error_msg ("error %d from pci_register_driver", ret);
        cdev_del (&c_dev);
        device_destroy (NULL, dev_num);
        unregister_chrdev_region (dev_num, 1);
        return ret;
    }
    return 0;
}

static void __exit mydriver_exit (void)
{
    device_destroy (NULL, dev_num);
    unregister_chrdev (MAJOR(dev_num), DEVICE_NAME);
    unregister_chrdev_region (dev_num, 1);
}

module_init(mydriver_init);
module_exit(mydriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your name <youremail@example.com>");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top