Fast GPIOs

Started by Dami1, November 28, 2012, 03:38:08 PM

Previous topic - Next topic

Dami1

Hi !

I'm working with GPIOs but need to go faster than 250 KHz !

I tried a simple code in the driver module (sun4i-gpio.c) to measure the speed without file system slowness


if(!strcmp(gpio_i->name,"pg9")) {
    while(1){
        gpio_write_one_pin_value(gpio_i->gpio_handler, 1, NULL);
        gpio_write_one_pin_value(gpio_i->gpio_handler, 0, NULL);
    }
}


With this I obtain 500 KHz but it's not enough... does someone tried to use memory mapping with /dev/mem ?

Thomas

#1
I tried to use this :
https://github.com/OLIMEX/OLINUXINO/blob/master/SOFTWARE/iMX233/gpio-mmap.h

Without success. It is probably iMX233-specific...

Dami1


bianchina3

Try this code:

gpio_lib.h

/////////////////////////////////////////////////////////////////////////////////
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/

#ifndef _GPIO_LIB_H_
#define _GPIO_LIB_H_


//**********************************************************************************
//******************************** VERSIONE DA U-BOOT ******************************
//**********************************************************************************

#define SW_PORTC_IO_BASE  0x01c20800


#define SUNXI_GPIO_A    0
#define SUNXI_GPIO_B    1
#define SUNXI_GPIO_C    2
#define SUNXI_GPIO_D    3
#define SUNXI_GPIO_E    4
#define SUNXI_GPIO_F    5
#define SUNXI_GPIO_G    6
#define SUNXI_GPIO_H    7
#define SUNXI_GPIO_I    8

struct sunxi_gpio {
   unsigned int cfg[4];
   unsigned int dat;
   unsigned int drv[2];
   unsigned int pull[2];
};

/* gpio interrupt control */
struct sunxi_gpio_int {
   unsigned int cfg[3];
   unsigned int ctl;
   unsigned int sta;
   unsigned int deb;         /* interrupt debounce */
};

struct sunxi_gpio_reg {
   struct sunxi_gpio gpio_bank[9];
   unsigned char res[0xbc];
   struct sunxi_gpio_int gpio_int;
};

#define GPIO_BANK(pin)      ((pin) >> 5)
#define GPIO_NUM(pin)      ((pin) & 0x1F)

#define GPIO_CFG_INDEX(pin)      (((pin) & 0x1F) >> 3)
#define GPIO_CFG_OFFSET(pin)      ((((pin) & 0x1F) & 0x7) << 2)

/* GPIO bank sizes */
#define SUNXI_GPIO_A_NR    (32)
#define SUNXI_GPIO_B_NR    (32)
#define SUNXI_GPIO_C_NR    (32)
#define SUNXI_GPIO_D_NR    (32)
#define SUNXI_GPIO_E_NR    (32)
#define SUNXI_GPIO_F_NR    (32)
#define SUNXI_GPIO_G_NR    (32)
#define SUNXI_GPIO_H_NR    (32)
#define SUNXI_GPIO_I_NR    (32)

#define SUNXI_GPIO_NEXT(__gpio) \
   ((__gpio##_START) + (__gpio##_NR) + 0)

enum sunxi_gpio_number {
   SUNXI_GPIO_A_START = 0,
   SUNXI_GPIO_B_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_A),
   SUNXI_GPIO_C_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_B),
   SUNXI_GPIO_D_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_C),
   SUNXI_GPIO_E_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_D),
   SUNXI_GPIO_F_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_E),
   SUNXI_GPIO_G_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_F),
   SUNXI_GPIO_H_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_G),
   SUNXI_GPIO_I_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_H),
};

/* SUNXI GPIO number definitions */
#define SUNXI_GPA(_nr)   (SUNXI_GPIO_A_START + (_nr))
#define SUNXI_GPB(_nr)   (SUNXI_GPIO_B_START + (_nr))
#define SUNXI_GPC(_nr)   (SUNXI_GPIO_C_START + (_nr))
#define SUNXI_GPD(_nr)   (SUNXI_GPIO_D_START + (_nr))
#define SUNXI_GPE(_nr)   (SUNXI_GPIO_E_START + (_nr))
#define SUNXI_GPF(_nr)   (SUNXI_GPIO_F_START + (_nr))
#define SUNXI_GPG(_nr)   (SUNXI_GPIO_G_START + (_nr))
#define SUNXI_GPH(_nr)   (SUNXI_GPIO_H_START + (_nr))
#define SUNXI_GPI(_nr)   (SUNXI_GPIO_I_START + (_nr))

/* GPIO pin function config */
#define SUNXI_GPIO_INPUT        (0)
#define SUNXI_GPIO_OUTPUT       (1)

#define SUNXI_GPA0_ERXD3        (2)
#define SUNXI_GPA0_SPI1_CS0     (3)
#define SUNXI_GPA0_UART2_RTS    (4)

#define SUNXI_GPA1_ERXD2        (2)
#define SUNXI_GPA1_SPI1_CLK     (3)
#define SUNXI_GPA1_UART2_CTS    (4)

#define SUNXI_GPA2_ERXD1        (2)
#define SUNXI_GPA2_SPI1_MOSI    (3)
#define SUNXI_GPA2_UART2_TX     (4)

#define SUNXI_GPA10_UART1_TX    (4)
#define SUNXI_GPA11_UART1_RX    (4)

#define SUN4I_GPB22_UART0_TX    (2)
#define SUN4I_GPB23_UART0_RX    (2)

#define SUN5I_GPG3_UART0_TX     (4)
#define SUN5I_GPG4_UART0_RX     (4)

#define SUNXI_GPC2_NCLE         (2)
#define SUNXI_GPC2_SPI0_CLK     (3)

#define SUNXI_GPC6_NRB0         (2)
#define SUNXI_GPC6_SDC2_CMD     (3)

#define SUNXI_GPC7_NRB1         (2)
#define SUNXI_GPC7_SDC2_CLK     (3)

#define SUNXI_GPC8_NDQ0         (2)
#define SUNXI_GPC8_SDC2_D0      (3)

#define SUNXI_GPC9_NDQ1         (2)
#define SUNXI_GPC9_SDC2_D1      (3)

#define SUNXI_GPC10_NDQ2        (2)
#define SUNXI_GPC10_SDC2_D2     (3)

#define SUNXI_GPC11_NDQ3        (2)
#define SUNXI_GPC11_SDC2_D3     (3)

#define SUNXI_GPF2_SDC0_CLK     (2)
#define SUNXI_GPF2_UART0_TX     (4)

#define SUNXI_GPF4_SDC0_D3      (2)
#define SUNXI_GPF4_UART0_RX     (4)


#endif // _GPIO_LIB_H_



gpio_lib.c

///////////////////////////////////////////////////////////

/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/

#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>

#include "gpio_lib.h"


//**********************************************************************************
//******************************** VERSIONE DA U-BOOT ******************************
//**********************************************************************************


static unsigned int SUNXI_PIO_BASE = 0;


/************************************************/
/*                                              */
/*                                              */
/************************************************/
int sunxi_gpio_init() {

   int fd;
   unsigned int addr_start, addr_offset, addr;
   unsigned int PageSize, PageMask;
   void *pc;

   fd = open("/dev/mem", O_RDWR);
   if(fd < 0) {
      perror("Unable to open /dev/mem");
      return(-1);
      }

   PageSize = sysconf(_SC_PAGESIZE);
   PageMask = (~(PageSize-1));
     
   // DEBUG
   //printf("Page size:%d\n", PageSize);

   addr_start  = SW_PORTC_IO_BASE &  PageMask;
   addr_offset = SW_PORTC_IO_BASE & ~PageMask;
     
   pc = (void *)mmap(0, PageSize*2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, addr_start);
   if(pc == MAP_FAILED) {
      perror("Unable to mmap file");
      printf("pc:%8.8x\n", (unsigned int)pc);
      return(-1);
      }
         
   SUNXI_PIO_BASE = (unsigned int)pc;
   SUNXI_PIO_BASE += addr_offset;
     
   // DEBUG
   // printf("gpiobase:%8.8x\n", gpio_pbase);
 
   close(fd);

   return 0;
}


/************************************************/
/*                                              */
/*                                              */
/************************************************/
int sunxi_gpio_set_cfgpin(unsigned int pin, unsigned int val) {

   unsigned int cfg;
   unsigned int bank = GPIO_BANK(pin);
   unsigned int index = GPIO_CFG_INDEX(pin);
   unsigned int offset = GPIO_CFG_OFFSET(pin);

   if(SUNXI_PIO_BASE == 0) {
      return -1;
      }
      
   struct sunxi_gpio *pio =
      &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];

//   cfg = readl(&pio->cfg[0] + index);
   cfg = *(&pio->cfg[0] + index);
   cfg &= ~(0xf << offset);
   cfg |= val << offset;

//   writel(cfg, &pio->cfg[0] + index);
   *(&pio->cfg[0] + index) = cfg;

   return 0;
}


/************************************************/
/*                                              */
/*                                              */
/************************************************/
int sunxi_gpio_get_cfgpin(unsigned int pin) {

   unsigned int cfg;
   unsigned int bank = GPIO_BANK(pin);
   unsigned int index = GPIO_CFG_INDEX(pin);
   unsigned int offset = GPIO_CFG_OFFSET(pin);

   if(SUNXI_PIO_BASE == 0) {
      return -1;
      }
      
   struct sunxi_gpio *pio =
      &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];

//   cfg = readl(&pio->cfg[0] + index);
   cfg = *(&pio->cfg[0] + index);
   cfg >>= offset;

   return (cfg & 0xf);
}


/************************************************/
/*                                              */
/*                                              */
/************************************************/
int sunxi_gpio_output(unsigned int pin, unsigned int val) {

   unsigned int dat;
   unsigned int bank = GPIO_BANK(pin);
   unsigned int num = GPIO_NUM(pin);

   if(SUNXI_PIO_BASE == 0) {
      return -1;
      }
      
   struct sunxi_gpio *pio =
      &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];

   //dat = readl(&pio->dat);
   dat = *(&pio->dat);
   
   if(val)
      dat |= 1 << num;
   else
      dat &= ~(1 << num);

//   writel(dat, &pio->dat);
   *(&pio->dat) = dat;

   return 0;
}


/************************************************/
/*                                              */
/*                                              */
/************************************************/
int sunxi_gpio_input(unsigned int pin) {

   unsigned int dat;
   unsigned int bank = GPIO_BANK(pin);
   unsigned int num = GPIO_NUM(pin);

   if(SUNXI_PIO_BASE == 0) {
      return -1;
      }
      
   struct sunxi_gpio *pio =
      &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];

   //dat = readl(&pio->dat);
   dat = *(&pio->dat);
   dat >>= num;

   return (dat & 0x1);
}


///////////////////////
pseudo C sample code:

   ret = sunxi_gpio_init();
   if(ret) {
      printf("sunxi_gpio_init ERROR\n");
      exit(-1);
      }
   
   sunxi_gpio_set_cfgpin(SUNXI_GPG(9), SUNXI_GPIO_OUTPUT);

   while(1)
     sunxi_gpio_output(SUNXI_GPG(9), 0);
     sunxi_gpio_output(SUNXI_GPG(9), 1);



Ciao.



Dami1

Thank you for your help bianchina3 ! 

It's faster ! it's about 653Khz. Not so fast that I hoped but faster... it's far from iMX233 5.7Mhz... Do you think we can now consider that A13 can't go faster than 653Khz for gpio toggle ?

It was for bitbanging application. Shame...



vinifr

Still is not available /dev/mem to gpio.  :'(
Linux sunxi developers are working on it and other things like gpu driver, NAND enabled u-boot version...

Thomas

Really thank you for this code !

xmegz

cc. 2Mhz

main.c :
/*******************************************************************************
Project  : Olimex A13
Module    : gpio-test.h
Version  : 0.1
Date      : 2013.02.15.
Authors  : Pádár Tamás
Company  : EMKE Kft.
Comments :
Chip type: A13
*******************************************************************************/

#include "my_gpio.h"


int main(void)
{
int ret = my_gpio_init();
   
if(ret)
{
   printf("gpio_init ERROR\n");
   exit(-1);
}

my_gpio_cfg_output(SUNXI_PORT_G_BASE, SUNXI_PIO_09);

my_gpio_pg9_test();

/*
while(1)
   {
     my_gpio_pg9_set_output();
     printf("State: %lu\r\n",my_gpio_get_input(SUNXI_PORT_G_BASE, SUNXI_PIO_09));
     sleep(1);
     
     my_gpio_pg9_clear_output();
     printf("State: %lu\r\n",my_gpio_get_input(SUNXI_PORT_G_BASE, SUNXI_PIO_09));
     sleep(1);
   }
*/
}

my_gpio.h:

/*******************************************************************************
Project  : Olimex A13
Module    : my_gpio.h
Version  : 0.1
Date      : 2013.02.15.
Authors  : Pádár Tamás
Company  : EMKE Kft.
Comments :
Chip type: A13
*******************************************************************************/
#ifndef _MY_GPIO_H_
#define _MY_GPIO_H_

//----------------------------------//
//       PORT BASE DEFINITIONS      //
//----------------------------------//

#define SUNXI_PORT_A_BASE      (0*0x24)
#define SUNXI_PORT_B_BASE      (1*0x24)
#define SUNXI_PORT_C_BASE      (2*0x24)
#define SUNXI_PORT_D_BASE      (3*0x24)
#define SUNXI_PORT_E_BASE      (4*0x24)
#define SUNXI_PORT_F_BASE      (5*0x24)
#define SUNXI_PORT_G_BASE      (6*0x24)
#define SUNXI_PORT_H_BASE      (7*0x24)
#define SUNXI_PORT_I_BASE      (8*0x24)


//----------------------------------//
//         PIO DEFINITIONS          //
//----------------------------------//

#define SUNXI_PIO_00           (0x00000001L <<  0)
#define SUNXI_PIO_01           (0x00000001L <<  1)
#define SUNXI_PIO_02           (0x00000001L <<  2)
#define SUNXI_PIO_03           (0x00000001L <<  3)
#define SUNXI_PIO_04           (0x00000001L <<  4)
#define SUNXI_PIO_05           (0x00000001L <<  5)
#define SUNXI_PIO_06           (0x00000001L <<  6)
#define SUNXI_PIO_07           (0x00000001L <<  7)
#define SUNXI_PIO_08           (0x00000001L <<  8)
#define SUNXI_PIO_09           (0x00000001L <<  9)
#define SUNXI_PIO_10           (0x00000001L <<  10)
#define SUNXI_PIO_11           (0x00000001L <<  11)
#define SUNXI_PIO_12           (0x00000001L <<  12)
#define SUNXI_PIO_13           (0x00000001L <<  13)
#define SUNXI_PIO_14           (0x00000001L <<  14)
#define SUNXI_PIO_15           (0x00000001L <<  15)


//----------------------------------//
//       CONSTANT DEFINITIONS       //
//----------------------------------//


#define SUNXI_SW_PORTC_IO_BASE  (0x01c20800)
#define SUNXI_GPIO_DATA_OFFSET  (0x10)
#define SUNXI_GPIO_INPUT        (0)
#define SUNXI_GPIO_OUTPUT       (1)

// Debug function
//#define SUNXI_GPIO_DEBUG

//----------------------------------//
//        METHOD DEFINITIONS        //
//----------------------------------//

#define SUNXI_PIO_GET_BIT_INDEX(a) ( (SUNXI_PIO_00)?1:(SUNXI_PIO_01)?2:(SUNXI_PIO_02)?3:(SUNXI_PIO_03)?4:(SUNXI_PIO_04)?5:(SUNXI_PIO_05)?6:(SUNXI_PIO_06)?7:(SUNXI_PIO_07)?8:(SUNXI_PIO_08)?9:(SUNXI_PIO_09)?10:(SUNXI_PIO_10)?11:(SUNXI_PIO_11)?12:(SUNXI_PIO_12)?13:(SUNXI_PIO_13)?14:(SUNXI_PIO_14)?15:(SUNXI_PIO_15)?16:0 )


#endif

my_gpio.c :

/*******************************************************************************
Project  : Olimex A13
Module    : my_gpio.c
Version  : 0.1
Date      : 2013.02.15.
Authors  : Pádár Tamás
Company  : EMKE Kft.
Comments :
Chip type: A13
*******************************************************************************/

#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>

#include "my_gpio.h"

static unsigned int SUNXI_PIO_BASE   =0;
static unsigned int *SUNXI_PIO_G_DATA;


//------------------------------------------------------------------------------
int my_gpio_init()
//------------------------------------------------------------------------------
{
   int fd;
   unsigned int addr_start, addr_offset, addr;
   unsigned int PageSize, PageMask;
   void *pc;

   fd = open("/dev/mem", O_RDWR);
   if(fd < 0) {
      perror("Unable to open /dev/mem");
      return(-1);
      }

   PageSize = sysconf(_SC_PAGESIZE);
   PageMask = (~(PageSize-1));
     
   addr_start  = SUNXI_SW_PORTC_IO_BASE &  PageMask;
   addr_offset = SUNXI_SW_PORTC_IO_BASE & ~PageMask;
     
   pc = (void *)mmap(0, PageSize*2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, addr_start);
   if(pc == MAP_FAILED) {
      perror("Unable to mmap file");
      printf("pc:%8.8x\n", (unsigned int)pc);
      return(-1);
      }
         
   SUNXI_PIO_BASE = (unsigned int)pc;
   SUNXI_PIO_BASE += addr_offset;
   
   
   SUNXI_PIO_G_DATA=(unsigned int *) (SUNXI_PIO_BASE +  SUNXI_PORT_G_BASE + SUNXI_GPIO_DATA_OFFSET);
     
   close(fd);
         
   return 0;
}

//------------------------------------------------------------------------------
void my_gpio_cfg_output(unsigned int port_base,unsigned int pin)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = (SUNXI_PIO_GET_BIT_INDEX(pin)>>3);
   unsigned int offset = ((SUNXI_PIO_GET_BIT_INDEX(pin)& 0x7) << 2);
   unsigned int *c     = (unsigned int *)  ((SUNXI_PIO_BASE + port_base + index));

   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin);
   #endif
   
   cfg = *c;
   cfg &= ~(0xF << offset);
   cfg |= SUNXI_GPIO_OUTPUT << offset;

   *c = cfg;
}

//------------------------------------------------------------------------------
void my_gpio_cfg_input(unsigned int port_base,unsigned int pin)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = (SUNXI_PIO_GET_BIT_INDEX(pin)>>3);
   unsigned int offset = ((SUNXI_PIO_GET_BIT_INDEX(pin)& 0x7) << 2);
   unsigned int *c     = (unsigned int *) ((SUNXI_PIO_BASE + port_base + index));
   
   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin);
   #endif
   
   cfg = *c;
   cfg &= ~(0xF << offset);
   cfg |= SUNXI_GPIO_INPUT << offset;

   *c = cfg;
}

//------------------------------------------------------------------------------
void my_gpio_set_output(unsigned int port_base,unsigned int pin)
//------------------------------------------------------------------------------
{
  unsigned int  *dat =  (unsigned int *) (SUNXI_PIO_BASE  +  port_base + SUNXI_GPIO_DATA_OFFSET);
 
  #ifdef SUNXI_GPIO_DEBUG
    printf("%20s base:%lu, offset:%lu, addr:%lu, dat:%lu, pin:%lu\r\n",__func__,SUNXI_PIO_BASE,port_base + SUNXI_GPIO_DATA_OFFSET,dat,pin);
  #endif
 
  *(dat) |= pin;
}

//------------------------------------------------------------------------------
void my_gpio_clear_output(unsigned int port_base,unsigned int pin)
//------------------------------------------------------------------------------
{
  unsigned int  *dat = (unsigned int *) (SUNXI_PIO_BASE +  port_base + SUNXI_GPIO_DATA_OFFSET);

  #ifdef SUNXI_GPIO_DEBUG
    printf("%20s base:%lu, offset:%lu, addr:%lu, dat:%lu, pin:%lu\r\n",__func__,SUNXI_PIO_BASE,port_base + SUNXI_GPIO_DATA_OFFSET,dat,pin);
  #endif

  *(dat) &= ~(pin);
}

//------------------------------------------------------------------------------
int my_gpio_get_input(unsigned int port_base ,unsigned int pin)
//------------------------------------------------------------------------------
{
  unsigned int  *dat =  (unsigned int *) (SUNXI_PIO_BASE +  port_base + SUNXI_GPIO_DATA_OFFSET);
 
  #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base:%lu, offset:%lu, addr:%lu, dat:%lu, pin:%lu\r\n",__func__,SUNXI_PIO_BASE,port_base + SUNXI_GPIO_DATA_OFFSET,dat,pin);
  #endif
     
  return (*dat & pin);
}

//------------------------------------------------------------------------------
void my_gpio_pg9_set_output()
//------------------------------------------------------------------------------
{
  (*SUNXI_PIO_G_DATA)  |= SUNXI_PIO_09;
}

//------------------------------------------------------------------------------
void my_gpio_pg9_clear_output()
//------------------------------------------------------------------------------
{
  (*SUNXI_PIO_G_DATA) &= ~(SUNXI_PIO_09);
}

//------------------------------------------------------------------------------
void  my_gpio_pg9_test()
//------------------------------------------------------------------------------
{
  while(1)
  {
   (*SUNXI_PIO_G_DATA) |=   SUNXI_PIO_09;
   (*SUNXI_PIO_G_DATA) &= ~(SUNXI_PIO_09);
  }
}



Tele

Quote from: xmegz on February 15, 2013, 06:30:39 PM
cc. 2Mhz

Wow, thank you so much.
This is the best so far.
Could you make it even better please?

TVC

Dear

I used your code and it works perfect for some GPIO pins like the PG9. Therefore I tried to adapt the code:

In my_gpio.c, the init function:
I initialized a macro SUNXI_PIO_C_DATA by using SUNXI_PIO_C_DATA=(unsigned int *) (SUNXI_PIO_BASE +  SUNXI_PORT_C_BASE + SUNXI_GPIO_DATA_OFFSET)

I configured it as output by using the function void my_gpio_cfg_output(unsigned int port_base,unsigned int pin)
But I only succeeded to use PC0 in stead of all the C-pins.

Can somebody help me please?

nvd

Seems like the pin mux is no configured properly for the rest of the pins.

http://linux-sunxi.org/A13/PIO

Tele

#11
There is a macro:

#define SUNXI_PIO_GET_BIT_INDEX(a) ....


That is buggy, no macro parameter (a) used in it.

It seems like he wanted to get 1 for SUNXI_PIO_00, 2 for SUNXI_PIO_01 .... 16 for SUNXI_PIO_15 with that macro.

Its easier to define those things:

#define SUNXI_PIO_00_IDX           (1)
#define SUNXI_PIO_01_IDX           (2)
#define SUNXI_PIO_02_IDX           (3)
#define SUNXI_PIO_03_IDX           (4)
#define SUNXI_PIO_04_IDX           (5)
#define SUNXI_PIO_05_IDX           (6)
#define SUNXI_PIO_06_IDX           (7)
#define SUNXI_PIO_07_IDX           (8)
#define SUNXI_PIO_08_IDX           (9)
#define SUNXI_PIO_09_IDX           (10)
#define SUNXI_PIO_10_IDX           (11)
#define SUNXI_PIO_11_IDX           (12)
#define SUNXI_PIO_12_IDX           (13)
#define SUNXI_PIO_13_IDX           (14)
#define SUNXI_PIO_14_IDX           (15)
#define SUNXI_PIO_15_IDX           (16)


Then modify those functions:

//------------------------------------------------------------------------------
void my_gpio_cfg_output(unsigned int port_base,unsigned int pin_idx)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = pin_idx>>3;
   unsigned int offset = (pin_idx& 0x7) << 2;
   unsigned int *c     = (unsigned int*)  ((SUNXI_PIO_BASE + port_base + index));

   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin_idx: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin_idx);
   #endif
   ....


and the other one:

//------------------------------------------------------------------------------
void my_gpio_cfg_input(unsigned int port_base,unsigned int pin_idx)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = pin_idx>>3;
   unsigned int offset = (pin_idx& 0x7) << 2;
   unsigned int *c     = (unsigned int *) ((SUNXI_PIO_BASE + port_base + index));

   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin_idx: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin_idx);
   #endif
   ...


Then use those indexes when you call it, for example:

...
   my_gpio_cfg_output(SUNXI_PORT_C_BASE, SUNXI_PIO_00_IDX)  // C port pin0 output
   my_gpio_cfg_input(SUNXI_PORT_C_BASE, SUNXI_PIO_01_IDX)   // C port pin1 input
...


I just modified these things formally, I did not test anything, don't get mad on me pls, if it doesn't work.

soenke

Works, if you change the indexes to:


#define SUNXI_PIO_00_IDX           (0)
#define SUNXI_PIO_01_IDX           (1)
#define SUNXI_PIO_02_IDX           (2)
#define SUNXI_PIO_03_IDX           (3)
#define SUNXI_PIO_04_IDX           (4)
#define SUNXI_PIO_05_IDX           (5)
#define SUNXI_PIO_06_IDX           (6)
#define SUNXI_PIO_07_IDX           (7)
#define SUNXI_PIO_08_IDX           (8)
#define SUNXI_PIO_09_IDX           (9)
#define SUNXI_PIO_10_IDX           (10)
#define SUNXI_PIO_11_IDX           (11)
#define SUNXI_PIO_12_IDX           (12)
#define SUNXI_PIO_13_IDX           (13)
#define SUNXI_PIO_14_IDX           (14)
#define SUNXI_PIO_15_IDX           (15)


Tested on A13 and A10S

Results in ~2MHz on A13 and A10S @100%CPU

soenke

1 more correction: *4 was missing when addressing the next config register.



//------------------------------------------------------------------------------
void my_gpio_cfg_output(unsigned int port_base,unsigned int pin_idx)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = (pin_idx>>3)*4;
   unsigned int offset = (pin_idx& 0x7) << 2;
   unsigned int *c     = (unsigned int*)  ((SUNXI_PIO_BASE + port_base + index));

   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin_idx: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin_idx);
   #endif
   ....


//------------------------------------------------------------------------------
void my_gpio_cfg_input(unsigned int port_base,unsigned int pin_idx)
//------------------------------------------------------------------------------
{
   unsigned int cfg;

   unsigned int index  = (pin_idx>>3)*4;
   unsigned int offset = (pin_idx& 0x7) << 2;
   unsigned int *c     = (unsigned int *) ((SUNXI_PIO_BASE + port_base + index));

   #ifdef SUNXI_GPIO_DEBUG
     printf("%20s base: %lu addr: %lu dat:%lu pin_idx: %lu\r\n",__func__,SUNXI_PIO_BASE,c,pin_idx);
   #endif
   ...

Tele

Good job Soenke,

By the way, later I have rewritten all this gpio thingy based on Tom Cubie's gpiolib.
I make it public maybe you can find something useful in it. Its tested and it works good. It can work in kernel space and user space as well. The max. speed is the same as you said: about 2MHz with an infinite toggling loop, measured with scope. If I remember correctly, the 2MHz is the full period frequency, so the toggle frequency is 4MHz, am I right Soenke?

here you go:

/*
* gpioport.c
*
*  Created on: Mar 21, 2013
*      Author: Tele
*/

#ifdef __KERNEL__

#else // __KERNEL__

#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#endif // __KERNEL__

#include "gpioport.h"

static void* gpBase = 0;
volatile uint32_t* gpPB_data = 0;
volatile uint32_t* gpPE_data = 0;
volatile uint32_t* gpPG_data = 0;


#ifdef __KERNEL__

int ioport_init(void)
{
gpBase = SUNXI_PIO_BASE;
gpPB_data = (uint32_t*)(gpBase + SUNXI_PORT_B_BASE + SUNXI_GPIO_DATAPORT_OFFSET);
gpPE_data = (uint32_t*)(gpBase + SUNXI_PORT_E_BASE + SUNXI_GPIO_DATAPORT_OFFSET);
gpPG_data = (uint32_t*)(gpBase + SUNXI_PORT_G_BASE + SUNXI_GPIO_DATAPORT_OFFSET);
}

///////////////////////////
int sunxi_gpio_set_cfgpin(uint32_t pin, uint32_t val)
{
uint32_t cfg;
uint32_t bank = GPIO_BANK(pin);
uint32_t index = GPIO_CFG_INDEX(pin);
uint32_t offset = GPIO_CFG_OFFSET(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

cfg = readl(&pio->cfg[0] + index);
cfg &= ~(0xf << offset);
cfg |= val << offset;

writel(cfg, &pio->cfg[0] + index);

return 0;
}
// Example: sunxi_gpio_set_cfgpin(SUNXI_GPG(9), SUNXI_GPIO_OUTPUT);

///////////////////////////
int sunxi_gpio_get_cfgpin(uint32_t pin)
{
uint32_t cfg;
uint32_t bank = GPIO_BANK(pin);
uint32_t index = GPIO_CFG_INDEX(pin);
uint32_t offset = GPIO_CFG_OFFSET(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

cfg = readl(&pio->cfg[0] + index);
cfg >>= offset;

return (cfg & 0xf);
}

///////////////////////////
inline void setPE04(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000010; writel(gpPE_data, data); }
inline void setPE05(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000020; writel(gpPE_data, data); }
inline void setPE06(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000040; writel(gpPE_data, data); }
inline void setPE07(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000080; writel(gpPE_data, data); }
inline void setPE08(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000100; writel(gpPE_data, data); }
inline void setPE09(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000200; writel(gpPE_data, data); }
inline void setPE10(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000400; writel(gpPE_data, data); }
inline void setPE11(void) { UINT32_t data = readl(gpPE_data); data |= 0x00000800; writel(gpPE_data, data); }

inline void setPB03(void) { UINT32_t data = readl(gpPB_data); data |= 0x00000008; writel(gpPB_data, data); }
inline void setPB04(void) { UINT32_t data = readl(gpPB_data); data |= 0x00000010; writel(gpPB_data, data); }
inline void setPB10(void) { UINT32_t data = readl(gpPB_data); data |= 0x00000400; writel(gpPB_data, data); }
inline void setPG09(void) { UINT32_t data = readl(gpPG_data); data |= 0x00000200; writel(gpPG_data, data); }

inline void clrPE04(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000010; writel(gpPE_data, data); }
inline void clrPE05(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000020; writel(gpPE_data, data); }
inline void clrPE06(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000040; writel(gpPE_data, data); }
inline void clrPE07(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000080; writel(gpPE_data, data); }
inline void clrPE08(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000100; writel(gpPE_data, data); }
inline void clrPE09(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000200; writel(gpPE_data, data); }
inline void clrPE10(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000400; writel(gpPE_data, data); }
inline void clrPE11(void) { UINT32_t data = readl(gpPE_data); data &= ~0x00000800; writel(gpPE_data, data); }

inline void clrPB03(void) { UINT32_t data = readl(gpPB_data); data &= ~0x00000008; writel(gpPB_data, data); }
inline void clrPB04(void) { UINT32_t data = readl(gpPB_data); data &= ~0x00000010; writel(gpPB_data, data); }
inline void clrPB10(void) { UINT32_t data = readl(gpPB_data); data &= ~0x00000400; writel(gpPB_data, data); }
inline void clrPG09(void) { UINT32_t data = readl(gpPG_data); data &= ~0x00000200; writel(gpPG_data, data); }

inline uint32_t read_portE(void){ return readl(gpPE_data);}
inline void write_portE(uint32_t data){writel(gpPE_data, data);}


#else // __KERNEL__

///////////////////////////
int ioport_init(void)
{
int memfd;
off_t addr_start, addr_offset;
uint32_t pagesize, pagemask;
void* mapped_base;

memfd = open("/dev/mem", O_RDWR|O_SYNC);
if (memfd < 0)
{
// _log(LOG_ERROR, "ioport_init: Unable to open /dev/mem\n");
return memfd;
}

pagesize = sysconf(_SC_PAGESIZE);
pagemask = (~(pagesize-1));

addr_start  = SUNXI_PIO_BASE & pagemask;
addr_offset = SUNXI_PIO_BASE & ~pagemask;

mapped_base = mmap(0, pagesize*2, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, addr_start);
if (mapped_base == MAP_FAILED )
{
// _log(LOG_ERROR, "ioport_init: Unable to mmap file\n");
close(memfd);
return (-1);
}

gpBase = mapped_base + addr_offset;

gpPB_data = (uint32_t*)(gpBase + SUNXI_PORT_B_BASE + SUNXI_GPIO_DATAPORT_OFFSET);
gpPE_data = (uint32_t*)(gpBase + SUNXI_PORT_E_BASE + SUNXI_GPIO_DATAPORT_OFFSET);
gpPG_data = (uint32_t*)(gpBase + SUNXI_PORT_G_BASE + SUNXI_GPIO_DATAPORT_OFFSET);

close(memfd);

return 0;
}

///////////////////////////
void ioport_cleanup(void)
{
uint32_t addr_start;
uint32_t pagesize, pagemask;
void* mapped_base;

if(gpBase)
{
pagesize = sysconf(_SC_PAGESIZE);
pagemask = (~(pagesize-1));

addr_start = SUNXI_PIO_BASE & pagemask;
mapped_base = gpBase - addr_start;
munmap(mapped_base, pagesize*2);
}
}

///////////////////////////
int read_port(uint32_t port_offset)
{
return *(volatile uint32_t*)(gpBase + port_offset);
}

///////////////////////////
void write_port(uint32_t port_offset, uint32_t port_data)
{
*(volatile uint32_t*)(gpBase + port_offset) = port_data;
}

///////////////////////////
int sunxi_gpio_set_cfgpin(uint32_t pin, uint32_t val)
{
uint32_t cfg;
uint32_t bank = GPIO_BANK(pin);
uint32_t index = GPIO_CFG_INDEX(pin);
uint32_t offset = GPIO_CFG_OFFSET(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

cfg = *(&pio->cfg[0] + index);
cfg &= ~(0xf << offset);
cfg |= val << offset;

*(&pio->cfg[0] + index) = cfg;
return 0;
}
// Example: sunxi_gpio_set_cfgpin(SUNXI_GPG(9), SUNXI_GPIO_OUTPUT);

///////////////////////////
int sunxi_gpio_get_cfgpin(uint32_t pin)
{
uint32_t cfg;
uint32_t bank = GPIO_BANK(pin);
uint32_t index = GPIO_CFG_INDEX(pin);
uint32_t offset = GPIO_CFG_OFFSET(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

cfg = *(&pio->cfg[0] + index);
cfg >>= offset;
return (cfg & 0xf);
}

///////////////////////////
int sunxi_gpio_output_set(uint32_t pin)
{
uint32_t dat;
uint32_t bank = GPIO_BANK(pin);
uint32_t num = GPIO_NUM(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

dat = *(&pio->dat);
dat |= 1 << num;
*(&pio->dat) = dat;
return 0;
}


///////////////////////////
int sunxi_gpio_output_clr(uint32_t pin)
{
uint32_t dat;
uint32_t bank = GPIO_BANK(pin);
uint32_t num = GPIO_NUM(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

dat = *(&pio->dat);
dat &= ~(1 << num);

*(&pio->dat) = dat;
return 0;
}

int sunxi_gpio_input(uint32_t pin)
{
uint32_t dat;
uint32_t bank = GPIO_BANK(pin);
uint32_t num = GPIO_NUM(pin);

struct sunxi_gpio* pio = &((struct sunxi_gpio_reg*)gpBase)->gpio_bank[bank];

dat = *(&pio->dat);
dat >>= num;
return (dat & 0x1);
}

///////////////////////////
//
inline void setPE04(void) {*gpPE_data |= 0x00000010;}
inline void setPE05(void) {*gpPE_data |= 0x00000020;}
inline void setPE06(void) {*gpPE_data |= 0x00000040;}
inline void setPE07(void) {*gpPE_data |= 0x00000080;}
inline void setPE08(void) {*gpPE_data |= 0x00000100;}
inline void setPE09(void) {*gpPE_data |= 0x00000200;}
inline void setPE10(void) {*gpPE_data |= 0x00000400;}
inline void setPE11(void) {*gpPE_data |= 0x00000800;}

inline void setPB03(void) {*gpPB_data |= 0x00000008;}
inline void setPB04(void) {*gpPB_data |= 0x00000010;}
inline void setPB10(void) {*gpPB_data |= 0x00000400;}
inline void setPG09(void) {*gpPG_data |= 0x00000200;}

inline void clrPE04(void) {*gpPE_data &= ~0x00000010;}
inline void clrPE05(void) {*gpPE_data &= ~0x00000020;}
inline void clrPE06(void) {*gpPE_data &= ~0x00000040;}
inline void clrPE07(void) {*gpPE_data &= ~0x00000080;}
inline void clrPE08(void) {*gpPE_data &= ~0x00000100;}
inline void clrPE09(void) {*gpPE_data &= ~0x00000200;}
inline void clrPE10(void) {*gpPE_data &= ~0x00000400;}
inline void clrPE11(void) {*gpPE_data &= ~0x00000800;}

inline void clrPB03(void) {*gpPB_data &= ~0x00000008;}
inline void clrPB04(void) {*gpPB_data &= ~0x00000010;}
inline void clrPB10(void) {*gpPB_data &= ~0x00000400;}
inline void clrPG09(void) {*gpPG_data &= ~0x00000200;}

#endif // __KERNEL__


and the header

/*
* gpioport.h
*
*  Created on: Mar 21, 2013
*      Author: Tele
*/

#ifndef GPIOPORT_H_
#define GPIOPORT_H_


#define SUNXI_PIO_BASE (0x01c20800) // Programmable Input Output
#define SUNXI_GPIO_DATAPORT_OFFSET  (0x10)
#define SUNXI_GPIO_INPUT        (0)
#define SUNXI_GPIO_OUTPUT       (1)

//----------------------------------//
//       PORT BASE DEFINITIONS      //
//----------------------------------//

#define SUNXI_PORT_A_BASE      (0*0x24)
#define SUNXI_PORT_B_BASE      (1*0x24)
#define SUNXI_PORT_C_BASE      (2*0x24)
#define SUNXI_PORT_D_BASE      (3*0x24)
#define SUNXI_PORT_E_BASE      (4*0x24)
#define SUNXI_PORT_F_BASE      (5*0x24)
#define SUNXI_PORT_G_BASE      (6*0x24)
#define SUNXI_PORT_H_BASE      (7*0x24)
#define SUNXI_PORT_I_BASE      (8*0x24)

//----------------------------------//
//         PIO DEFINITIONS          //
//----------------------------------//

#define SUNXI_PIO_00           (0x00000001L <<  0)
#define SUNXI_PIO_01           (0x00000001L <<  1)
#define SUNXI_PIO_02           (0x00000001L <<  2)
#define SUNXI_PIO_03           (0x00000001L <<  3)
#define SUNXI_PIO_04           (0x00000001L <<  4)
#define SUNXI_PIO_05           (0x00000001L <<  5)
#define SUNXI_PIO_06           (0x00000001L <<  6)
#define SUNXI_PIO_07           (0x00000001L <<  7)
#define SUNXI_PIO_08           (0x00000001L <<  8)
#define SUNXI_PIO_09           (0x00000001L <<  9)
#define SUNXI_PIO_10           (0x00000001L <<  10)
#define SUNXI_PIO_11           (0x00000001L <<  11)
#define SUNXI_PIO_12           (0x00000001L <<  12)
#define SUNXI_PIO_13           (0x00000001L <<  13)
#define SUNXI_PIO_14           (0x00000001L <<  14)
#define SUNXI_PIO_15           (0x00000001L <<  15)

#define GPIO_BANK(pin) ((pin) >> 5)
#define GPIO_NUM(pin) ((pin) & 0x1F)
#define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3)
#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2)


/* SUNXI GPIO number definitions */
#define SUNXI_GPE04   (0x0000084)
#define SUNXI_GPE05   (0x0000085)
#define SUNXI_GPE06   (0x0000086)
#define SUNXI_GPE07   (0x0000087)
#define SUNXI_GPE08   (0x0000088)
#define SUNXI_GPE09   (0x0000089)
#define SUNXI_GPE10   (0x000008A)
#define SUNXI_GPE11   (0x000008B)

#define SUNXI_GPB03   (0x0000023)
#define SUNXI_GPB04   (0x0000024)
#define SUNXI_GPB10   (0x000002A)
#define SUNXI_GPG09   (0x00000C9)

#define GPIO_BANK_GPB03 (0x00000001)
#define GPIO_BANK_GPE04 (0x00000004)
#define GPIO_BANK_GPG09 (0x00000006)

struct sunxi_gpio
{
uint32_t cfg[4];
uint32_t dat;
uint32_t drv[2];
uint32_t pull[2];
};

// gpio interrupt control
struct sunxi_gpio_int
{
uint32_t cfg[4];
uint32_t ctl;
uint32_t sta;
uint32_t deb;    // interrupt debounce
};

struct sunxi_gpio_reg
{
struct sunxi_gpio gpio_bank[9];
uint8_t res[0xbc];
struct sunxi_gpio_int gpio_int;
};

int ioport_init(void);
void ioport_cleanup(void);
int sunxi_gpio_get_cfgpin(uint32_t pin);
int sunxi_gpio_set_cfgpin(uint32_t pin, uint32_t val);
int sunxi_gpio_output_set(uint32_t pin);
int sunxi_gpio_output_clr(uint32_t pin);

extern volatile uint32_t* gpPB_data;
extern volatile uint32_t* gpPE_data;
extern volatile uint32_t* gpPG_data;

void setPE04(void);
void setPE05(void);
void setPE06(void);
void setPE07(void);
void setPE08(void);
void setPE09(void);
void setPE10(void);
void setPE11(void);

void clrPE04(void);
void clrPE05(void);
void clrPE06(void);
void clrPE07(void);
void clrPE08(void);
void clrPE09(void);
void clrPE10(void);
void clrPE11(void);

void setPB03(void);
void setPB04(void);
void setPB10(void);
void setPG09(void);

void clrPB03(void);
void clrPB04(void);
void clrPB10(void);
void clrPG09(void);

#endif /* GPIOPORT_H_ */