This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

gpio_request( ) Sitara

Hi, guys!

I was wondering where in the kernel code (ubuntu 14) is the implementation of the PSP function gpio_request( ) for Sitara? I find only this declaration:

In KERNEL/include/linunx/gpio.h :

static inline int gpio_request(unsigned gpio, const char *label)
{
    return -ENOSYS;
}

  • Nope.

    The "gpio_request" function is called from "drivers/gpio/gpiolib.c"

    int gpio_request(unsigned gpio, const char *label)
    {
    	return gpiod_request(gpio_to_desc(gpio), label);
    }

  • I've checked this file as well. My gpiolib.c has the implementation shown below. But in it I SEE NO device dependent register accesses like "base + GPIO_CTRL = .... "

    int gpio_request(unsigned gpio, const char *label)
    {
    	struct gpio_desc	*desc;
    	struct gpio_chip	*chip;
    	int			status = -EPROBE_DEFER;
    	unsigned long		flags;
    
    	spin_lock_irqsave(&gpio_lock, flags);
    
    	if (!gpio_is_valid(gpio)) {
    		status = -EINVAL;
    		goto done;
    	}
    	desc = &gpio_desc[gpio];
    	chip = desc->chip;
    	if (chip == NULL)
    		goto done;
    
    	if (!try_module_get(chip->owner))
    		goto done;
    
    	/* NOTE:  gpio_request() can be called in early boot,
    	 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
    	 */
    
    	if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
    		desc_set_label(desc, label ? : "?");
    		status = 0;
    	} else {
    		status = -EBUSY;
    		module_put(chip->owner);
    		goto done;
    	}
    
    	if (chip->request) {
    		/* chip->request may sleep */
    		spin_unlock_irqrestore(&gpio_lock, flags);
    		status = chip->request(chip, gpio - chip->base);
    		spin_lock_irqsave(&gpio_lock, flags);
    
    		if (status < 0) {
    			desc_set_label(desc, NULL);
    			module_put(chip->owner);
    			clear_bit(FLAG_REQUESTED, &desc->flags);
    			goto done;
    		}
    	}
    	if (chip->get_direction) {
    		/* chip->get_direction may sleep */
    		spin_unlock_irqrestore(&gpio_lock, flags);
    		gpio_get_direction(gpio);
    		spin_lock_irqsave(&gpio_lock, flags);
    	}
    done:
    	if (status)
    		pr_debug("gpio_request: gpio-%d (%s) status %d\n",
    			gpio, label ? : "?", status);
    	spin_unlock_irqrestore(&gpio_lock, flags);
    	return status;
    }
    EXPORT_SYMBOL_GPL(gpio_request);
    

  • What is your SDK version ?

    What is your intention please ?

    Refer to the 'gpio_desc" structure.
  • My intention is to learn how to control a peripheral using direct register access (e.g. on MSP430 we have P1DIR |= 0x55) from a device driver.

    I don't know which SDK I'm using since I've downloaded and built the kernel as described here (in section Mainline) https://eewiki.net/display/linuxonarm/BeagleBone+Black that suggests to use a script that does everything for me.

    So far I ioremap( ) and write to registers correctly as long as I call gpio_request( ) first. But I don't want to use this API, I want to understand what it does and do the same thing with registers.
  • You can access the GPIO for reading and writing GPIO MMR registers, say for ex : LED toggling.
    Yes it is possible without using APIs.

    You can use either "ioremap" for the GPIO's MMR (memory mapped register) or "__raw_writel" API to access the registers.

    Ex:

    1)

    #define GPIO0_BASE 0x44E07000
    unsigned int *GPIO_OE = GPIO_OE = ioremap(GPIO0_BASE + 0x134, 4);
    unsigned int *GPIO_CLEARDATAOUT = GPIO_CLEARDATAOUT = ioremap(GPIO0_BASE + 0x190, 4);
    unsigned int *GPIO_SETDATAOUT = GPIO_SETDATAOUT = ioremap(GPIO0_BASE + 0x194, 4);

    *GPIO_OE &= ~(1<<0); //Clearing the 0th bit to 1 for making as o/p

    *GPIO_CLEARDATAOUT |= (1<<0); //Setting 0th bit to 1 to drive '0'

    //put some delay here

    *GPIO_SETDATAOUT |= (1<<0); //Setting 0th bit to 1 to drive '1'


    2)

    //Make as output for the GPIO pin
    value = __raw_readl (GPIO0_BASE + 0x134);
    value &= ~(1 << (0));
    __raw_writel (value, GPIO0_BASE + 0x134);

    //Set the GPIO
    value = __raw_readl (GPIO0_BASE + 0x194);
    value |= (1 << (0));
    __raw_writel (value, GPIO0_BASE + 0x194);
    udelay(80000);

    //Clear the GPIO
    value = __raw_readl (GPIO0_BASE + 0x190);
    value |= (1 << (0));
    __raw_writel (value, GPIO0_BASE + 0x190);
    udelay(80000);
  • Hello again!

    Yes, your methods are correct! I know about the __raw_readl( )  and __raw_writel( ) APIs as well. But it's the initialization of the GPIO that fails in my driver. Here is a snippet of code from my driver...

    I use the header files from STARTERWARE and I do exactly as in the AM335X_StarterWare_02_00_01_01/examples/beaglebone/gpio/gpioLEDBlink.c example but my code does not work if I comment out gpio_request( ). I suspect that it has something to do with the pinmuxing. Here's my code:

    #include <linux/module.h>
    #include <linux/io.h> //ioremap, iounmap
    #include <linux/gpio.h>
    
    #include "soc_AM335x.h"
    #include "hw_cm_per.h"
    #include "hw_gpio_v2.h"
    
    #define GPIO_REG_SPACE					0x1000
    #define CMPER_REG_SPACE					0x1000
    
    void __iomem * gpio_start_address;
    void __iomem * cmper_start_address;
    unsigned long *p;
    
    static int __init hello_init(void)
    {
    	printk("*******seconddrv init *******\n");
    
    	gpio_request(65, "mygpio"); //--------DOES NOT WORK WITHOUT THIS LINE-----------
    
    	printk("cmper ioremap( )\n");
    	cmper_start_address = ioremap_nocache(SOC_CM_PER_REGS, CMPER_REG_SPACE);
    	if(cmper_start_address == 0){
    		printk("\n\n\nioremap( ) failed!\n");
    		return -1;
    	}else{
    		printk("start_addr: %p\n", cmper_start_address);
    	}
    
    	//Set CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE
    	p = (cmper_start_address+CM_PER_GPIO2_CLKCTRL);
    	*p |= CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE;
    	while((*p & CM_PER_GPIO2_CLKCTRL_MODULEMODE) != CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE){ }
    	//Set CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK
    	*p |= CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK;
    	while((*p & CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK) != CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK){	}
    	//Wait for IDLEST
    	while((*p & CM_PER_GPIO2_CLKCTRL_IDLEST) != (CM_PER_GPIO2_CLKCTRL_IDLEST_FUNC<<CM_PER_GPIO2_CLKCTRL_IDLEST_SHIFT)){	}
    	//Wait for CLKACTIVITY_GPIO_2_GDBCLK
    	p = (cmper_start_address+CM_PER_L4LS_CLKSTCTRL);
    	while((*p & CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK) != CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK){ }
    
    	printk("gpio ioremap( )\n");
    	gpio_start_address = ioremap_nocache(SOC_GPIO_2_REGS, GPIO_REG_SPACE);
    	if(gpio_start_address == 0){
    		printk("\n\n\nioremap( ) failed!\n");
    		return -1;
    	}else{
    		printk("start_addr: %p\n", gpio_start_address);
    	}
    
    	p = (gpio_start_address+GPIO_CTRL);
    	*p &= ~GPIO_CTRL_DISABLEMODULE;
    
    	p = (gpio_start_address+GPIO_SYSCONFIG);
    	*p |= GPIO_SYSCONFIG_SOFTRESET;
    	p = (gpio_start_address+GPIO_SYSSTATUS);
    	while(!(*p & GPIO_SYSSTATUS_RESETDONE)){ }
    
    	p = (gpio_start_address+GPIO_OE);
    	*p &= ~0x2;
    
    	p = (gpio_start_address+GPIO_SETDATAOUT);
    	*p = 0x02;
    
    	return 0;
    }
    
    static void __exit hello_exit(void)
    {
    	printk("*******seconddrv exit*******\n");
    	p = (gpio_start_address+GPIO_CLEARDATAOUT);
    	*p = 0x02;
    
    	iounmap(gpio_start_address);
    	iounmap(cmper_start_address);
    
    	gpio_free(65);
    }

  • I forgot to say about pinmuxing and I thought that you knew about this already.
    Yes, pimuxing is very first thing need to do for accessing any peripheral.

    We need to configure the pin "GPMC_CSn3" to mode 7 for GPIO2_0.
    Please refer to the data sheet.
    Also refer to the CONTROL_MODULE chapter to configure the particular GPIO in AM335x TRM guide.

    I think, we can access the GPIO without using "gpio_rquest" API also and just we get only warnings about regarding "do gpio request & access" like that.
  • I also did that. I replaced the gpio_request( ) line with the code shown below. I believe that pin GPIO2_1 has an offset 0x904 (not 0x888 like you showed) because when I do

    cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins

    I get the address

    pin 65 (44e10904) 00000030 pinctrl-single

    but then again ... the driver fails. And I don't know why - I have no /proc/kmsg.

    #define CONTR_REG_SPACE 0x20000
    void __iomem * control_start_address;

    ...

    printk("control ioremap( )\n");
    control_start_address = ioremap(SOC_CONTROL_REGS, CONTR_REG_SPACE);
    if(control_start_address == 0){
    printk("\n\n\nioremap( ) failed!\n");
    return -1;
    }else{
    printk("start_addr: %p\n", control_start_address);
    }
    //Select pin mux
    p = control_start_address + 0x904;
    //p = control_start_address + 0x888; //conf_gpmc_csn3
    *p = 0x0f; // MODE = 7 (GPIO), pull-up/pull-down disabled, pin = OUT, fast slew rate
  • Have you tried to access the GPIO through SYSFS ?

    Just try to access after you did pinmuxing in code.

    http://processors.wiki.ti.com/index.php/Linux_PSP_GPIO_Driver_Guide#User_Space_-_Sysfs_control
  • Hi!

    I was unable to access the pin from the SYSFS because when I pinmux with direct register access I do not get an entry in /sys/class/gpio.

    However I've discovered the problem with my code - for some reason I cannot pass the line:

    while((*p & CM_PER_GPIO2_CLKCTRL_IDLEST) != (CM_PER_GPIO2_CLKCTRL_IDLEST_FUNC<<CM_PER_GPIO2_CLKCTRL_IDLEST_SHIFT)){    }


    When I commented it out my simple device driver worked as expected. Thank you for your time, Titusrathinaraj!

    Here's the code to turn on a LED at pin GPIO2_1 on driver __init and turn it off on driver __exit using adapted code from Starterware for Sitara:

    #include <linux/module.h>
    #include <linux/io.h> //ioremap, iounmap
    #include <linux/gpio.h>
    
    #include "soc_AM335x.h"
    #include "hw_control_AM335x.h"
    #include "hw_cm_per.h"
    #include "hw_gpio_v2.h"
    
    #define GPIO_REG_SPACE	0x1000
    #define CMPER_REG_SPACE	0x1000
    #define CONTR_REG_SPACE	0x20000
    
    void __iomem * control_start_address;
    void __iomem * gpio_start_address;
    void __iomem * cmper_start_address;
    unsigned long *p;
    
    static int __init hello_init(void)
    {
    	printk("*******seconddrv init *******\n");
    
    	printk("control ioremap( )\n");
    	control_start_address = ioremap(SOC_CONTROL_REGS, CONTR_REG_SPACE);
    	if(control_start_address == 0){
    		printk("\n\n\nioremap( ) failed!\n");
    		return -1;
    	}else{
    		printk("start_addr: %p\n", control_start_address);
    	}
    	//Select pin mux
    	p = control_start_address + 0x904;
    	*p = 0x0f;
    
    	printk("cmper ioremap( )\n");
    	cmper_start_address = ioremap_nocache(SOC_CM_PER_REGS, CMPER_REG_SPACE);
    	if(cmper_start_address == 0){
    		printk("\n\n\nioremap( ) failed!\n");
    		return -1;
    	}else{
    		printk("start_addr: %p\n", cmper_start_address);
    	}
    
    	//Set CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE
    	p = (cmper_start_address+CM_PER_GPIO2_CLKCTRL);
    	*p |= CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE;
    	while((*p & CM_PER_GPIO2_CLKCTRL_MODULEMODE) != CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE){ }
    	//Set CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK
    	*p |= CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK;
    	while((*p & CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK) != CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK){	}
    
    	//Wait for IDLEST --- DOES NOT WORK
    	//while((*p & CM_PER_GPIO2_CLKCTRL_IDLEST) != (CM_PER_GPIO2_CLKCTRL_IDLEST_FUNC<<CM_PER_GPIO2_CLKCTRL_IDLEST_SHIFT)){	}
    
    	//Wait for CLKACTIVITY_GPIO_2_GDBCLK
    	p = (cmper_start_address+CM_PER_L4LS_CLKSTCTRL);
    	while((*p & CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK) != CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK){ }
    
    	printk("gpio ioremap( )\n");
    	gpio_start_address = ioremap_nocache(SOC_GPIO_2_REGS, GPIO_REG_SPACE);
    	if(gpio_start_address == 0){
    		printk("\n\n\nioremap( ) failed!\n");
    		return -1;
    	}else{
    		printk("start_addr: %p\n", gpio_start_address);
    	}
    
    	p = (gpio_start_address+GPIO_CTRL);
    	*p &= ~GPIO_CTRL_DISABLEMODULE;
    
    	p = (gpio_start_address+GPIO_SYSCONFIG);
    	*p |= GPIO_SYSCONFIG_SOFTRESET;
    	p = (gpio_start_address+GPIO_SYSSTATUS);
    	while(!(*p & GPIO_SYSSTATUS_RESETDONE)){ }
    
    	p = (gpio_start_address+GPIO_OE);
    	*p &= ~0x2;
    
    	p = (gpio_start_address+GPIO_SETDATAOUT);
    	*p = 0x02;
    
    	return 0;
    }
    
    static void __exit hello_exit(void)
    {
    	printk("*******seconddrv exit*******\n");
    	p = (gpio_start_address+GPIO_CLEARDATAOUT);
    	*p = 0x02;
    	iounmap(gpio_start_address);
    	iounmap(cmper_start_address);
    	iounmap(control_start_address);
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    
    MODULE_AUTHOR("L.B.");
    MODULE_DESCRIPTION("BeagleBone Black blink a 0.5 mA LED on GPIO2_1. Device driver.");
    MODULE_LICENSE("GPL");
    

  • Hi Lubomir,
    I'm glad that you were able to solve the problem.