/* 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 #include #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); }