All of lore.kernel.org
 help / color / mirror / Atom feed
* GPIO driver module for Jetway NF98 board
@ 2011-12-21 17:34 Joan Pau Beltran
  2011-12-22  1:21 ` Josh Cartwright
       [not found] ` <20111222001549.GA14353@joshcartwright.net>
  0 siblings, 2 replies; 7+ messages in thread
From: Joan Pau Beltran @ 2011-12-21 17:34 UTC (permalink / raw)
  To: kernelnewbies

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.

Since I have not written any kernel module before, after reading the 
pertinent documentation about memory management, i/o port access, and 
gpiolib, I have some 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?

2. I suppose that I should use the same addresses given by the support 
team, although their code is for windows. 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' ?


The second file attached is an skeleton of the module I am trying to 
write, any comments or suggestions are welcome.

Thanks in advance!

-- 
Joan Pau Beltran

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20111221/5e38755c/attachment.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gpio-jwnf98.c
Type: text/x-csrc
Size: 2748 bytes
Desc: not available
Url : http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20111221/5e38755c/attachment.bin 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: NF98GPIO.c
Type: text/x-csrc
Size: 2179 bytes
Desc: not available
Url : http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20111221/5e38755c/attachment-0001.bin 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
  2011-12-21 17:34 GPIO driver module for Jetway NF98 board Joan Pau Beltran
@ 2011-12-22  1:21 ` Josh Cartwright
       [not found] ` <20111222001549.GA14353@joshcartwright.net>
  1 sibling, 0 replies; 7+ messages in thread
From: Josh Cartwright @ 2011-12-22  1:21 UTC (permalink / raw)
  To: kernelnewbies

(resend, sorry for spam)

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:

   00h: GPIO_USE_SEL[31:0]
   04h: GP_IO_SEL[31:0]
   0Ch: GP_LVL[31:0]
   28h: GPI_NMI_EN[15:0]
   2Ch: GPI_INV[31:0]
   30h: GPIO_USE_SEL2[63:32]
   34h: GPI_IO_SEL2[63:32]
   38h: GP_LVL2[63:32]
   40h: GPIO_USE_SEL3[95:64]
   44h: GPI_IO_SEL3[95:64]
   48h: GP_LVL3[95:64]
   60h: GP_RST_SEL[31:0]
   64h: GP_RST_SEL2[63:32]
   68h: GP_RST_SEL3[95:64]

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

> Since I have not written any kernel module before, after reading the
> pertinent documentation about memory management, i/o port access, and
> gpiolib, I have some 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?
>
> 2. I suppose that I should use the same addresses given by the
> support team, although their code is for windows. Is it ok?

The above document seemed to insinuate that the actual IO region used
for GPIO access isn't statically allocated, but rather allocated during
PCI enumeration for a LPC device.  If this is the case, you have an
additional question you need answered:

   - What is the best way to get the base address for accessing these
     GPIOs?

(the example code provided in the above document uses pci_get_device
which sounds clunky...)

Actually, with some further grepping I found that a lot of this
infrastructure may already be available to you.  See
drivers/mfd/lpc_sch.c and drivers/gpio/sch_gpio.c.  Not sure what it
would take to get it working with your setup, but it should give you a
good starting point.

> 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.

> The second file attached is an skeleton of the module I am trying to
> write, any comments or suggestions are welcome.

(in the future, please inline any code you'd like reviewed)

Good luck!

-- 
                           joshc

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
       [not found] ` <20111222001549.GA14353@joshcartwright.net>
@ 2011-12-28 17:03   ` Joan Pau Beltran
  2011-12-29 17:53     ` Josh Cartwright
  0 siblings, 1 reply; 7+ messages in thread
From: Joan Pau Beltran @ 2011-12-28 17:03 UTC (permalink / raw)
  To: kernelnewbies

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
  2011-12-28 17:03   ` Joan Pau Beltran
@ 2011-12-29 17:53     ` Josh Cartwright
  2012-01-18 20:48       ` Joan Pau Beltran
  0 siblings, 1 reply; 7+ messages in thread
From: Josh Cartwright @ 2011-12-29 17:53 UTC (permalink / raw)
  To: kernelnewbies

On Wed, Dec 28, 2011 at 06:03:32PM +0100, Joan Pau Beltran wrote:
> Thank you very much for your response.
> It brings up a lot of useful information.

Great, glad it helped!

*snip*

> 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:
> > 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.

Yes, that is how I understand it, too.

> 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.

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.

> 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?

I think that would be fine (there are other drivers that currently do
this).

> > > 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?

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.

> Here it goes the code again.

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.

> 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?

Having one statically allocated gpio_chip object assumes that there will
be only one of these chips installed in a system.  I think that is a
safe assumption to make, so you should be okay.

-- 
                                 joshc

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
  2011-12-29 17:53     ` Josh Cartwright
@ 2012-01-18 20:48       ` Joan Pau Beltran
  2012-01-18 21:40         ` Joan Pau Beltran
  0 siblings, 1 reply; 7+ messages in thread
From: Joan Pau Beltran @ 2012-01-18 20:48 UTC (permalink / raw)
  To: kernelnewbies



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 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
  2012-01-18 20:48       ` Joan Pau Beltran
@ 2012-01-18 21:40         ` Joan Pau Beltran
  2012-01-23 21:00           ` Joan Pau Beltran
  0 siblings, 1 reply; 7+ messages in thread
From: Joan Pau Beltran @ 2012-01-18 21:40 UTC (permalink / raw)
  To: kernelnewbies


Sorry for the noise, but the last code was from a primitive version with
several typos and errors. Sorry for the noise.

Here it goes the fixed code:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/gpio.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);
     return 0;
}

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);
     }
     return 0;
}

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 jwnf98_gpio_chip = {
     .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 = NULL,
     .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, PIN0_FUNCTION);
     byte = inb(PINX_FUNCTION);
     byte |= ~1;
     outb(byte, PINX_FUNCTION);
     /*
     Should we add the gpio_chip here with gpiochip_add?
     */
     return gpiochip_add(&jwnf98_gpio_chip);
}

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, PIN0_FUNCTION);
     byte = inb(PINX_FUNCTION);
     byte &= 1;
     outb(byte, PIN0_FUNCTION);
     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);
     /*
     Should we remove the gpio_chip here? How to do that?
     */
}

module_init(jwnf98_gpio_init);
module_exit(jwnf98_gpio_exit);

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

-- 
Joan Pau Beltran

^ permalink raw reply	[flat|nested] 7+ messages in thread

* GPIO driver module for Jetway NF98 board
  2012-01-18 21:40         ` Joan Pau Beltran
@ 2012-01-23 21:00           ` Joan Pau Beltran
  0 siblings, 0 replies; 7+ messages in thread
From: Joan Pau Beltran @ 2012-01-23 21:00 UTC (permalink / raw)
  To: kernelnewbies


Now that the code compiles, I need some help to debug it.
I used the kernel headers shipped with my distribution (Ubuntu), namely 
(from uname -r)
   2.6.38-13-generic

Everything compiles fine, without any warning.

But calling insmod, immediately triggers this error (from dmesg):

[  798.129707] generic-usb 0003:413C:2101.0002: input,hidraw0: USB HID 
v1.11 Keyboard [Dell Dell Smart Card Reader Keyboard] on 
usb-0000:00:1a.0-1.2/input0
[ 1414.832761] BUG: unable to handle kernel NULL pointer dereference at 
0000000000000502
[ 1414.832880] IP: [<ffffffffa00300e4>] jwnf98_gpio_init+0xe4/0x1000 
[gpio_jwnf98]
[ 1414.832982] PGD 20a704067 PUD 21a601067 PMD 0
[ 1414.833036] Oops: 0000 [#1] SMP
[ 1414.833077] last sysfs file: 
/sys/devices/pci0000:00/0000:00:19.0/net/eth0/statistics/collisions
[ 1414.833152] CPU 3
[ 1414.833172] Modules linked in: gpio_jwnf98(+) btrfs zlib_deflate 
libcrc32c ufs qnx4 hfsplus hfs minix ntfs vfat msdos fat jfs xfs 
exportfs reiserfs ppdev snd_hda_codec_hdmi snd_hda_codec_via 
snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_seq_midi snd_rawmidi 
snd_seq_midi_event lm78 hwmon_vid snd_seq snd_timer snd_seq_device i915 
drm_kms_helper psmouse drm i2c_algo_bit snd video soundcore 
snd_page_alloc serio_raw lp parport intel_ips usbhid hid firewire_ohci 
e1000e firewire_core crc_itu_t
[ 1414.833725]
[ 1414.833744] Pid: 28764, comm: insmod Not tainted 2.6.38-13-generic 
#54~lucid1-Ubuntu To be filled by O.E.M. To be filled by O.E.M./To be 
filled by O.E.M.
[ 1414.833877] RIP: 0010:[<ffffffffa00300e4>]  [<ffffffffa00300e4>] 
jwnf98_gpio_init+0xe4/0x1000 [gpio_jwnf98]
[ 1414.833965] RSP: 0018:ffff880208adbf18  EFLAGS: 00010206
[ 1414.834011] RAX: ffff8801e8e8f900 RBX: ffffffffa04cf080 RCX: 
ffff8801e8e8f328
[ 1414.834071] RDX: 0000000000000538 RSI: 0000000000000538 RDI: 
ffffffff81a19438
[ 1414.834131] RBP: ffff880208adbf18 R08: 0000000000000000 R09: 
ffff8801e8e8f900
[ 1414.834191] R10: 0000000000000004 R11: 0000000000000001 R12: 
0000000000000000
[ 1414.834251] R13: ffffffffa0030000 R14: 0000000000000003 R15: 
0000000000004000
[ 1414.834312] FS:  00007fbb2f5a5700(0000) GS:ffff8800cb380000(0000) 
knlGS:0000000000000000
[ 1414.834380] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1414.834430] CR2: 0000000000000502 CR3: 00000002089b2000 CR4: 
00000000000006e0
[ 1414.834490] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 
0000000000000000
[ 1414.834549] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 
0000000000000400
[ 1414.834610] Process insmod (pid: 28764, threadinfo ffff880208ada000, 
task ffff88021bad16e0)
[ 1414.834678] Stack:
[ 1414.834698]  ffff880208adbf48 ffffffff81002053 ffffffffa04cf080 
0000000000000000
[ 1414.834775]  00000000024a7010 0000000000000003 ffff880208adbf78 
ffffffff810a3d4b
[ 1414.834851]  00007fff75d0d9da 00007fff75d0d9da 00000000024a7030 
00000000000019db
[ 1414.834927] Call Trace:
[ 1414.834958]  [<ffffffff81002053>] do_one_initcall+0x43/0x190
[ 1414.835011]  [<ffffffff810a3d4b>] sys_init_module+0xfb/0x250
[ 1414.835062]  [<ffffffff8100c082>] system_call_fastpath+0x16/0x1b
[ 1414.835114] Code: c7 c7 c0 93 a1 81 e8 5c d8 03 e1 45 31 c0 48 c7 c1 
24 e0 4c a0 ba 01 00 00 00 be 38 05 00 00 48 c7 c7 c0 93 a1 81 e8 3c d8 
03 e1 <8a> 04 25 02 05 00 00 48 c7 c7 00 f0 4c a0 83 c8 20 88 04 25 02
[ 1414.842138] RIP  [<ffffffffa00300e4>] jwnf98_gpio_init+0xe4/0x1000 
[gpio_jwnf98]
[ 1414.845597]  RSP <ffff880208adbf18>
[ 1414.849064] CR2: 0000000000000502
[ 1414.921662] ---[ end trace d6d694e0e28fc4ad ]---

I am not aware of any pointer dereference in the code, so it is dificult 
to me
to figure out what is wrong.

Fortunately, the system does not become unresponsive (what a nice 
kernel!!!).
The gpio_jwnf98 is listed in lsmod,
but no chip directory appears under /sys/class/gpio.

Finally, the module can not be removed, rmmod outputs:

ERROR: Module gpio_jwnf98 is in use

Thanks again for all the help.

-- 
Joan Pau Beltran

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2012-01-23 21:00 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-21 17:34 GPIO driver module for Jetway NF98 board Joan Pau Beltran
2011-12-22  1:21 ` Josh Cartwright
     [not found] ` <20111222001549.GA14353@joshcartwright.net>
2011-12-28 17:03   ` Joan Pau Beltran
2011-12-29 17:53     ` Josh Cartwright
2012-01-18 20:48       ` Joan Pau Beltran
2012-01-18 21:40         ` Joan Pau Beltran
2012-01-23 21:00           ` Joan Pau Beltran

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.