GPIO driver module for Jetway NF98 board

Joan Pau Beltran joanpau.beltran at uib.cat
Wed Jan 18 15:48:43 EST 2012



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


-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20120118/0b52deb3/attachment.html 


More information about the Kernelnewbies mailing list