/*
direct GPIO access code for net5501 running linux
Matthew Kaufman 3/24/2009, 4/1/2009
Direct driverless access to PC87366 for GPIO and CS5536 for Error LED
For register details see pgs 480 onward of:
http://www.amd.com/files/connectivitysolutions/geode/geode_lx/33238G_cs5536_db.pdf
And page 129 onward of:
http://www.datasheetcatalog.org/datasheet/nationalsemiconductor/PC87366.pdf
This is example code. A fancier version would make it easier to work with more than one
pin at a time.
*/
#include <stdio.h>
#include <sys/io.h>
#define GPIO_BASE_CS5535 0x6100 /* rather than asking MSR_LBAR_GPIO which is harder to do */
#define SIO_INDEX 0x2e /* index register of PC87366 Super I/O */
#define SIO_DATA 0x2f /* data register of PC87366 Super I/O */
#define SIO_REG_SID 0x20 /* Configuration register containing ID */
#define SIO_SID_VALUE 0xe9 /* Expected ID value */
#define SIO_REG_LDN 0x07 /* Configuration register: logical device number selector */
#define SIO_LDN_GPIO 0x07 /* LDN Value to select GPIO configuration */
#define SIO_REG_ACTIVATE 0x30 /* (Shared) Device register: Activate */
#define SIO_REG_BASE_MSB_0 0x60 /* (Shared) Device register: I/O Port base, descriptor 0, MSB */
#define SIO_REG_BASE_LSB_0 0x61 /* (Shared) Device register: I/O Port base, descriptor 0, LSB */
#define SIO_GPIO_PIN_SELECT 0xf0 /* GPIO device-specific register: Pin select */
#define SIO_GPIO_PIN_CONFIG 0xf1 /* GPIO device-specific register: Pin configuration */
int gpio_base = 0;
int pin_init()
{
int i;
if(iopl(3)) /* allow direct I/O access at 0x2e, 0x6100, 0x6640 and thereabouts. must be root. */
return -1;
outb(SIO_REG_SID, SIO_INDEX); /* probe PC87366 */
i = inb(SIO_DATA);
if(i != SIO_SID_VALUE)
return -1;
outb(SIO_REG_BASE_MSB_0, SIO_INDEX); /* get MSB of GPIO I/O */
gpio_base = inb(SIO_DATA) << 8;
outb(SIO_REG_BASE_LSB_0, SIO_INDEX); /* get LSB of GPIO I/O */
gpio_base |= inb(SIO_DATA);
/* turn on GPIO */
/* Note: the pin configuration calls assume that this logical device select hasn't been overridden */
/* if that's a problem in practice, the next two lines must be added to the _pin_config function */
outb(SIO_REG_LDN, SIO_INDEX); /* logical device select */
outb(SIO_LDN_GPIO, SIO_DATA); /* select GPIO */
outb(SIO_REG_ACTIVATE, SIO_INDEX); /* activation for selected device, which is GPIO */
outb(0x01, SIO_DATA); /* active */
return 0;
}
void error_led_set()
{
/* error LED is GPIO 6 on the CS5535 */
outl(1 << 6, GPIO_BASE_CS5535); /* using CS5535 atomic set */
}
void error_led_clear()
{
outl(1 << (6+16), GPIO_BASE_CS5535); /* using CS5535 atomic clear */
}
static int _pin_to_gpio(int pin)
{
/* mapping table from email by Ralph Becker-Szendy */
switch(pin)
{
case 3: return 16;
case 4: return 17;
case 5: return 18;
case 6: return 19;
case 7: return 20;
case 8: return 21;
case 9: return 22;
case 10: return 23;
case 12: return 4;
case 13: return 5;
case 15: return 11;
case 16: return 10;
default: return -1;
}
}
int pin_test(int pin)
{
int gpio = _pin_to_gpio(pin);
int offset;
int i;
if(gpio < 0)
return;
if(gpio >= 16)
offset = 0x09;
else if(gpio >= 8)
offset = 0x05;
else
offset = 0x01;
i = inb(gpio_base + offset);
return(i & (1 << (gpio & 0x07)));
}
void pin_set_to(int pin, int set)
{
int gpio = _pin_to_gpio(pin);
int offset;
int i;
if(gpio_base == 0)
return; /* not initialized */
if(gpio < 0)
return;
if(gpio >= 16)
offset = 0x08;
else if(gpio >= 8)
offset = 0x04;
else
offset = 0x00;
i = inb(gpio_base + offset);
if(set)
{
i |= 1 << (gpio & 0x07);
}
else
{
i &= ~(1 << (gpio & 0x07));
}
outb(i, gpio_base + offset);
}
void pin_set(int pin)
{
pin_set_to(pin, 1);
}
void pin_clear(int pin)
{
pin_set_to(pin, 0);
}
static void _pin_config(int pin, int bit, int set)
{
int gpio = _pin_to_gpio(pin);
int i;
if(gpio_base == 0)
return; /* not initialized */
if(gpio < 0)
return;
/* See note above about how we assume GPIO logical device is already selected */
outb(SIO_GPIO_PIN_SELECT, SIO_INDEX);
outb(((gpio & 0x38) << 1) | (gpio & 0x07), SIO_DATA);
outb( SIO_GPIO_PIN_CONFIG, SIO_INDEX);
i = inb(SIO_DATA);
if(set)
{
i |= 1 << bit;
}
else
{
i &= ~(1 << bit);
}
outb( i, SIO_DATA);
}
void pin_output_enable(int pin)
{
_pin_config(pin, 0, 1);
}
void pin_output_disable(int pin)
{
_pin_config(pin, 0, 0);
}
void pin_output_pushpull(int pin)
{
_pin_config(pin, 1, 1);
}
void pin_output_opendrain(int pin)
{
_pin_config(pin, 1, 0);
}
void pin_output_pullup_enable(int pin)
{
_pin_config(pin, 2, 1);
}
void pin_output_pullup_disable(int pin)
{
_pin_config(pin, 2, 0);
}