GPIO driver module for Jetway NF98 board

Joan Pau Beltran joanpau.beltran at uib.cat
Wed Dec 28 12:03:32 EST 2011


Thank you very much for your response.
It brings up a lot of useful information.

Al 22/12/11 01:15, En/na Josh Cartwright ha escrit:
> On Wed, Dec 21, 2011 at 06:34:58PM +0100, Joan Pau Beltran wrote:
>> Hi everyone,
>>
>> I did not find any existing driver for the GPIO port of the Jetway
>> NF98 mini-itx board, so I would like to write a module for that,
>> using the gpiolib interface.
>>
>> Since the board documentation does not include any information about
>> the GPIOs, I contacted the Jetway support team. However, the only
>> answer was the attached code for Windows (NF98GPIO.c).
>> As you can see, it seems clear how to set the pin states for output,
>> but not how to read them in input mode. I asked again and the
>> response was (literally):
>>> input sample code:
>>> {
>>> data= ISA_RW(GPIO_BASE[pin]+0x2,highlow<<pin,1,~(1<<pin));
>>> }
>>> //final exit SIO
>>> outp(INDEX_PORT,0xaa);
>>> return data;
>> It makes no sense, and it is very upsetting.
>> I would try to read from the same address used for output, but this
>> is just an idea to be tested.
> I think you're on the right track. In researching this board a little
> more, I was able to uncover some information.  For the sake of
> learning, I've included my steps to discovery here:
>
>  From Jetway's website (http://www.jetwaycomputer.com/NF98.html), I found
> that the chipset used in the board is an Intel QM57.
>
> Searching for datasheets for the Intel QM57 brought me to Intel's site
> (http://www.intel.com/content/www/us/en/chipsets/5-chipset-3400-chipset-datasheet.html).
> Searching the document for GPIO, I found section 5.15.4, "GPIO Registers
> Lockdown".  The content of the text portion of the section didn't seem
> all that relevant, but there is a description of a register set that
> seems to match up with the values in your attached example:
>
Relevant information also appears in sections:
13.1.14 GPIOBASE—GPIO Base Address Register (LPC I/F—D31:F0)
13.1.15 GC—GPIO Control Register (LPC I/F—D31:F0)
13.10 General Purpose I/O Registers
Where the functionality of each register is described in detail.
> Searching for the symbolic names brings up this document, which looks
> like it may describe in more detail how to use this GPIO interface:
>
> http://download.intel.com/embedded/chipsets/appnote/322174.pdf
>

After looking at both documents, can you please confirm these ideas:
1.- The GPIO functionality of the jwnf98 comes from a hardware device in 
the chipset called LPC (Low Pin Cout) controller managed through the LPC 
Interface Bridge registers (section 9.1 page 366). This LPC controller 
provides other functionalities, too.
2.- The LPC resides in a PCI bus.
3.- GPIO is managed accessing the registers of the LPC. We should read 
from and write to these registers according to the notes in 13.10 
(GPIO_USE_SEL, GPIO_IO_SEL and GPIO_LVL). Not all the GPIOs are 
available, only the ones given in the Jetway code.
4.- The GPIO registers of the LPC are mapped to some I/O port, starting 
at the address specified in the GPIOBASE register. From the first 
paragraph in section 13.10, page 546:
> The control for the general purpose I/O signals is handled through a 
> 128-byte I/O
> space. The base offset for this space is selected by the GPIOBASE 
> register.
5.- From Jetway code, it seems that the base address in the GPIOBASE 
register described in 13.1.14 is set by the manufacturer to 0x500. 
Conversely, the Intel code gets that base address reading the GPIOBASE 
register. Note that specific pci functions are used for that. If Tech 
Support info is ok, I do not need to do that, and can simply request the 
needed i/o ports taking as base offset 0x500.

If the above points are ok, I have some doubts related with my previous 
questions:
>> 1. How should I request/release the ports mentioned in the attached
>> file? Should I use the request_region/release_region functions from
>> linux/ioport.h? In such case, what should I do with the returned
>> struct resource?
I think I should userequest_region/release_region from linux/ioport.h, 
and simply ignore the returned resource, is it ok?

>> 3. Should/could I use the test_bit, set_bit, clear_bit functions to
>> get, set the bit in the needed read/write functions I am writing? Or
>> should I use the sequence 'inb - mask the value properly - outb' ?
> Nope, as far as I know these bitops only work with memory operands.
Is this because we use port-based i/o instead of memory-mapped i/o?

Here it goes the code again.
The main question is, should the gpio_chip be statically allocated, or 
it should be created in the init function and destroyed in the exit 
function? If the latter, how to do that?

Thank you very much!

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

#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;
u8 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;
u8 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;
u8 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;
u8 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",
.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?
- Create the chip here instead of let it be static? How?
- Enable gpio function for each pin?
- Something else?
*/
}

static void __exit jwnf98_gpio_exit(void)
{
/*
Do cleanup work here:
- Release ports?
- Delete the chip if it was created on init function
instead of being static? How?
- Disable gpio function for each pin?
- Something else?
*/
}

module_init(jwnf98_gpio_init);
module_exit(jwnf98_gpio_exit);

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


-- 
Joan Pau Beltran
Grup de Sistemes, Robòtica i Visió - DMI
Universitat de les Illes Balears
Ctra. Valldemossa, km 7.5 07122 Palma
Campus Universitari, edifici Anselm Turmeda
Telf (+34) 971 17 28 13
Fax (+34) 971 17 30 03




More information about the Kernelnewbies mailing list