Re: GPIO driver module for Jetway NF98 board

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





Al 29/12/11 18:53, En/na Josh Cartwright ha escrit:
> I'd recommend getting the base address of the GPIO region as documented
> in the Intel manuals, instead of what looks like throw-away testing code
> from your vendor.
>
> Take a peak at drivers/watchdog/iTCO_wdt.c, as this watchdog timer is
> also accessed through the LPC device. It might give you a few ideas.


I inspected the file, but did not understand how to relate that code to my case.


> Yes, precisely. I'd recommend just using standard shift/masking (which
> looks like what you are already doing). Keep in mind, however,
> you'll need some locking strategy to ensure that a read-modify-write
> cycle happens atomically.


Is the blocking really needed, given the fact that the region is already requested?
I expected that requesting the region prevents interferences like that.
If not, how should I do that?


> While I appreciate it being inlined this time, your mailer seemed to
> have munged whitespace, such that the code is very difficult to read :(.
> You may want to see Documentation/email-clients.txt


Really sorry, here it goes again, hoping it is ok this time.

#include <linux/module.h>
#include <linux/types.h>
#include <asm-generic/io.h>

/* TODO: Look how to get the GPIO base address from the LPC interface.*/
#define GPIO_BASE 0x500;

/* pin 0 (GPIO 21) is controlled by an specific bit of a different set of ports: */
#define PIN0_FUNCTION  GPIO_BASE + 0x2;
#define PIN0_DIRECTION GPIO_BASE + 0x6;
#define PIN0_STATUS    GPIO_BASE + 0xE;
#define PIN0_BIT       5;

/* pins 1 to 7 (GPIO 33 to 39) correspond to the respective bit on these ports */
#define PINX_FUNCTION  GPIO_BASE + 0x30;
#define PINX_DIRECTION GPIO_BASE + 0x34;
#define PINX_STATUS    GPIO_BASE + 0x38;


static int jwnf98_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
    unsigned long dir_add;
    unsigned bit_off;
    uint8_t byte;
    if (off)
    {
        dir_add = PINX_DIRECTION;
        bit_off = off;
    }
    else
    {
        dir_add = PIN0_DIRECTION;
        bit_off = PIN0_BIT;
    }
    byte = inb(dir_add);
    byte |= (1 << bit_off);
    outb(byte, dir_add);
}

static int jwnf98_gpio_direction_output(struct gpio_chip *gc, unsigned off, int val)
{
    unsigned long dir_add;
    unsigned long val_add;
    unsigned bit_off;
    uint8_t byte;
    if (off)
    {
        dir_add = PINX_DIRECTION;
        val_add = PINX_STATUS;
        bit_off = off;
    }
    else
    {
        dir_add = PIN0_DIRECTION;
        val_add = PIN0_STATUS;
        bit_off = PIN0_BIT;
    }
    byte = inb(dir_add);
    byte &= ~(1 << bit_off);
    outb(byte, dir_add);
    if (val)
    {
        byte = inb(val_add);
        byte |= (1 << bit_off);
        outb(byte, val_add);
    }
    else
    {
        byte = inb(val_add);
        byte &= ~(1 << bit_off);
        outb(byte, val_add);
    }
}

static int jwnf98_gpio_get(struct gpio_chip *gc, unsigned off)
{
    unsigned long add;
    unsigned bit;
    uint8_t byte;
    if (off)
    {
        add = PINX_STATUS;
        bit = off;
    }
    else
    {
        add = PIN0_STATUS;
        bit = PIN0_BIT;
    }
    byte = inb(add);
    byte &= (1 << bit);
    return !!byte; /* Is the double negation !! needed? */
}

static void jwnf98_gpio_set(struct gpio_chip *gc, unsigned off, int val)
{
    unsigned long add;
    unsigned bit;
    uint8_t byte;
    if (off)
    {
        add = PINX_STATUS;
        bit = off;
    }
    else
    {
        add = PIN0_STATUS;
        bit = PIN0_BIT;
    }
    byte = inb(add);
    if (val)
        byte |= (1 << bit);
    else
        byte &= ~(1 << bit);
    outb(byte, add);
}

static struct gpio_chip gpio_pins = {
    .label = "jwnf98_gpio",
    .owner = THIS_MODULE,
    .direction_input  = jwnf98_gpio_direction_input,
    .get = jwnf98_gpio_get,
    .direction_output = jwnf98_gpio_direction_output,
    .set = jwnf98_gpio_set,
    .dbg_show = jwnf98_gpio_dbg_show,
    .can_sleep = 0,
};

static int __init jwnf98_gpio_init(void)
{
    /*
    Do preliminar work here:
        - Request ports? DONE.
        - Create the chip here instead of let it be static? How? NOT NEEDED.
        - Enable gpio function for each pin? DONE.
        - Something else?
    */
    uint8_t byte;
    request_region(PIN0_FUNCTION,  1, "jwnf98_gpio");
    request_region(PIN0_DIRECTION, 1, "jwnf98_gpio");
    request_region(PIN0_STATUS,    1, "jwnf98_gpio");
    request_region(PIN0_BIT,       1, "jwnf98_gpio");
    request_region(PINX_FUNCTION,  1, "jwnf98_gpio");
    request_region(PINX_DIRECTION, 1, "jwnf98_gpio");
    request_region(PINX_STATUS,    1, "jwnf98_gpio");
    /*
    Should we check that the requested memory is available? How?
    */
    byte = inb(PIN0_FUNCTION);
    byte |= (1 << PIN0_BIT);
    outb(byte, add);
    byte = inb(PINX_FUNCTION);
    byte |= ~1;
    outb(byte, add);
}

static void __exit jwnf98_gpio_exit(void)
{
    /*
    Do cleanup work here:
        - Release ports? DONE.
        - Delete the chip if it was created on init function
          instead of being static? How? NOT NEEDED.
        - Disable gpio function for each pin? DONE.
        - Something else?
    */
    uint8_t byte;
    byte = inb(PIN0_FUNCTION);
    byte &= ~(1 << PIN0_BIT);
    outb(byte, add);
    byte = inb(PINX_FUNCTION);
    byte &= 1;
    outb(byte, add);
    release_region(PIN0_FUNCTION,  1);
    release_region(PIN0_DIRECTION, 1);
    release_region(PIN0_STATUS,    1);
    release_region(PIN0_BIT,       1);
    release_region(PINX_FUNCTION,  1);
    release_region(PINX_DIRECTION, 1);
    release_region(PINX_STATUS,    1);
}

module_init(jwnf98_gpio_init);
module_exit(jwnf98_gpio_exit);

MODULE_DESCRIPTION("Jetway NF98 GPIO driver");
MODULE_LICENSE("GPL");


As suggested, an alternative of the #define GPIO_BASE will be to try this code
in the init function:

    /* Needed macros. */
    /* Vendor and device IDs of LPC device. */
    #define LPC_VENDOR_ID 0x8086
    #define LPC_DEVICE_ID 0x5031
    /* Offset into low pin count (LPC) config space of the GPIO base address. */
    #define GPIO_BAR_OFFSET 0x48
    #define GPIO_BAR_BITMASK 0x0000ff80
 
    /* Code in the init function */
    struct pci_dev *pdev = NULL;
    /* Get dev struct for the LPC device. */
    /* The GPIO BAR is located in the LPC device config space. */
    pdev = pci_get_device(LPC_VENDOR_ID, LPC_DEVICE_ID, NULL);
    /* Get base address from the LPC configuration space. */
    /* Where shoud we store this address? In a static global variable?
    unsigned int gpio_base;
    pci_read_config_dword(pdev, GPIO_BAR_OFFSET, gpio_base);
    /* Clear all but address bits. */
    gpio_base &= GPIO_BAR_BITMASK;
    /* release reference to device */
    pci_dev_put(pdev);
 
--
Joan Pau Beltran


_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux