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