From mboxrd@z Thu Jan 1 00:00:00 1970 From: joanpau.beltran@uib.cat (Joan Pau Beltran) Date: Wed, 18 Jan 2012 21:48:43 +0100 Subject: GPIO driver module for Jetway NF98 board In-Reply-To: <20111229175339.GE14353@joshcartwright.net> References: <20111222012154.GB14353@joshcartwright.net> <4EFB4BE4.2090409@uib.cat> <20111229175339.GE14353@joshcartwright.net> Message-ID: <4F17302B.3050402@uib.cat> To: kernelnewbies@lists.kernelnewbies.org List-Id: kernelnewbies.lists.kernelnewbies.org 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 #include #include /* 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