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.

How to use spi_gpio.c on omapl138 as spi master

Other Parts Discussed in Thread: OMAPL138, DA8XX

Hi all,

I am planning to use 3 GPIOs pins as CLK, MOSI and SSN as SPI pin to write data from omapl138 to external device.

I saw there is spi_gpio.c under /linux/drivers/spi and it seems a good fit to my situation. I do following the note on source by adding six lines at begin of this source code.

#define DRIVER_NAME "myboard_spi2"
//#define SPI_MISO_GPIO 18
#define SPI_MOSI_GPIO 17
#define SPI_SCK_GPIO 16
#define SPI_N_CHIPSEL 44

at this point I don't know what is next step because after reboot there is no such myboard_spi2 under /dev

Following steps I just guess and try out. Since it is a master of SPI driver, therefore, I re-configure and remove the normal "spi_davinci" driver and rename DRIVER_NAME ="spi_davinci". Hope it will really replace the master driver, but there is no /dev/spidev after reboot. 

So, I come here to ask for help. Thank you

Joe

  • Usually you would declare a platform device. The driver code is left untouched. Search for "spi_gpio_platform_data" in the kernel source for an example. Enable "GPIO-based bitbanging SPI Master" in kernel config. Using GP1[0], GP1[1] and GP1[2] assumes that UART1, UART2 and I2C1 are not used by the kernel.

    ---arch/arm/mach-davinci/include/mach/mux.h---
    enum davinci_da850_index {
    ...
      DA850_GPIO1_0, // Add at end of enum
      DA850_GPIO1_1, // Add at end of enum
      DA850_GPIO1_1, // Add at end of enum
    };

    ---arch/arm/mach-davinci/da850.c---
    static const struct mux_config da850_pins[] = {
    ...
      MUX_CFG(DA850, GPIO1_0, 4, 28, 15, 8, false) // Shared with UART1_TXD
      MUX_CFG(DA850, GPIO1_1, 4, 24, 15, 8, false) // Shared with UART1_RXD
      MUX_CFG(DA850, GPIO1_2, 4, 20, 15, 8, false) // Shared with UART2_TXD and I2C1_SDA
    ...
    };

    ---arch/arm/mach-davinci/board-da850-evm.c---
    ...
    #include <linux/spi/spi_gpio.h>
    ...
    #define GPIO_SPI_CLK  GPIO_TO_PIN(1, 0) // 16 GP1[0]
    #define GPIO_SPI_MOSI GPIO_TO_PIN(1, 1) // 17 GP1[1]
    #define GPIO_SPI_MISO GPIO_TO_PIN(1, 2) // 18 GP1[2]
    ...
    struct spi_gpio_platform_data da850evm_spi_platform_data = {
     .sck            = GPIO_SPI_CLK,
     .mosi           = GPIO_SPI_MOSI,
     .miso           = GPIO_SPI_MISO,
     .num_chipselect = 3, // Not sure if this a bitmask
    };

    static struct platform_device da850evm_spi_device = {
     .name = "spi_gpio",
     .id    = 0,
     .dev   = {
          .platform_data = &da850evm_spi_platform_data,
         }
    };

    static const short da850_evm_spi_gpio_pins[] = {
     DA850_GPIO1_0, DA850_GPIO1_1, DA850_GPIO1_2,
     -1
    };
    ...
    static __init void da850_evm_init(void)
    {
    ...
      platform_device_register(&da850evm_spi_device);
      ret = davinci_cfg_reg_list(da850_evm_spi_gpio_pins);
      if (ret)
        pr_warning("da850_evm_init: spi gpio mux setup failed: %d\n", ret);
    ...
    }

  • Hi Norman,

    Thank you very much for detail instructions. You are so wonderful in this forum to help others like me.

    I do see spi_gpio.0 at  /sys/devices/platform/, but how do I access it from user space as normal spi driver under /dev?

    Regards,

    Joe

  • I would guess that adding the spi_gpio driver makes it available for use by kernel code. For userspace, You might need to enable userspace SPI dev in the kernel. In the kernel config the flag is SPI_SPIDEV. In the menu option it is "User mode SPI device driver support". The kernel may also need the EXPERIMENTAL flag on for the SPI Dev option to appear.

  • Hi Norman,

    The SPI_DEV has been configured and it has been used at the normal spi driver, the SPI_DAVINCI.

    What is the EXPERIMENTAL flag?

    Whether if should I do same as spi_davinci driver in board-da850-evm.c 

    {
    .modalias = "spidev",
    .controller_data = &da850evm_spi1s2_cfg,
    .mode = SPI_MODE_0,
    .max_speed_hz = 757000,
    .bus_num = 1,
    .chip_select = 2,
    },

    but I don't know how to set up the .resource, like da8xx_spi1_resources in device-da8xx.c

    Or do you know where to find the usage of this spi-gpio in general? Thank you

    Joe

  • I've never used spidev myself but I think you got it right. You are ahead of me on this. Other posts on this forum use a platform device definition for spidev. Just like you have already shown. I don't think you have to setup more da8xx_spi1_resources in device-da8xx.c. I think that is for real SPI controller ports. If you are already using a real SPI, you should be able to add the spidev definition to the existing structure.

    static struct platform_device da850evm_spi_device = {
     .name = "spi_gpio",
     .id    = 2, // The real SPI use 0 and 1
     .dev   = {
          .platform_data = &da850evm_spi_platform_data,
         }
    };

    static struct spi_board_info da850evm_spi_info[] = {
      {
        .modalias        = "m25p80",
        .platform_data   = &da850evm_spiflash_data,
        .controller_data = &da850evm_spiflash_cfg,
        .mode            = SPI_MODE_0,
        .max_speed_hz    = 30000000,
        .bus_num         = 1,
        .chip_select     = 0,
      },
      {
        .modalias     = "spidev",
        .mode         = SPI_MODE_0,
        .max_speed_hz = 757000,
        .irq          = -1,
        .bus_num      = 2, // This is the same as .id above? Or .id+1?
        .chip_select  = 2,
      },

    };

    The function da8xx_register_spi() won't notice the extra def not related to the real SPI port. If you are not using real SPI port, then da8xx_register_spi() cannot be used. You would hace to clone that code. Here's my guess at what the code should look like:

    static struct spi_board_info da850evm_spi_info2[] = {
      {
        .modalias     = "spidev",
        .mode         = SPI_MODE_0,
        .max_speed_hz = 757000,
        .irq          = -1,
        .bus_num      = 2, // This is the same as .id above? Or .id+1?
        .chip_select  = 2,
      },
    };
    ...
    static __init void da850_evm_init(void)
    {
    ...
      // Cloned from da8xx_register_spi()
      ret = spi_register_board_info(da850evm_spi_info2,
                                    ARRAY_SIZE(da850evm_spi_info2));
      if (ret)
        pr_warning("da850_evm_init: spidev register failed: %d\n", ret);

      ret = platform_device_register(&da850evm_spi_device);
      if (ret)
        pr_warning("da850_evm_init: spi gpio register failed: %d\n", ret);

      ret = davinci_cfg_reg_list(da850_evm_spi_gpio_pins);
      if (ret)
        pr_warning("da850_evm_init: spi gpio mux setup failed: %d\n", ret);
    ...
    }

  • Hi Norman,

    Thank you very much, you always help me at right point. Really, really thank you.

    You said "If you are not using real SPI port, then da8xx_register_spi() cannot be used" what if I used the real spi and this spi_gpio, can I still call da850evm_init_spi1()? I wish I can use two SPIs.

    Joe

  • My previous comments were based upon the 3.22 kernel code. That kernel does not have the da850evm_init_spi1() function. Looks like you are using the 3.21 kernel code. Code from da850evm_init_spi1() got moved into da8xx_register_spi(). My guess is that you would still just add your spidev definition to da850evm_spi_info assuming you are using the real SPI1 port with m25p80 attached to it.

    Please post your solution. Curious to see what is required for spidev and spi_gpio.

  • Hi Norman,

    I am still in 2.6.37. Basically I do following your post and summary as following

    --arch/arm/mach-davinci/include/mach/mux.h---
    enum davinci_da850_index {
    ...
    DA850_GPIO0_13,
    DA850_GPIO0_10,
    DA850_GPIO1_1, 
    DA850_GPIO2_12,
    ...
    };

    ---arch/arm/mach-davinci/da850.c---
    static const struct mux_config da850_pins[] = {
    ...
    MUX_CFG(DA850, GPIO0_13,0,8,15,8, false) 
    MUX_CFG(DA850, GPIO0_10,0, 20,15,8,false)
    MUX_CFG(DA850, GPIO1_1, 4, 24,15,8,false) 
    MUX_CFG(DA850, GPIO2_12, 5, 12,15,8,false) 
    ...
    };

    ---arch/arm/mach-davinci/board-da850-evm.c---
    #include <linux/spi/spi_gpio.h>
    #define GPIO_SPI_CLK GPIO_TO_PIN(0, 13) // GP0[13]
    #define GPIO_SPI_MISO GPIO_TO_PIN(0, 10) // GP0[10]
    #define GPIO_SPI_MOSI GPIO_TO_PIN(1, 1) // GP1[1]
    struct spi_gpio_platform_data da850evm_spi_platform_data = {
    .sck = GPIO_SPI_CLK,
    .mosi = GPIO_SPI_MOSI,
    .miso = GPIO_SPI_MISO,
    .num_chipselect = 3, // Not sure if this a bitmask
    };

    static struct platform_device da850evm_spi_device = {
    .name = "spi_gpio",
    .id = 2, // The real SPI use 0 and 1
    .dev = {
    .platform_data = &da850evm_spi_platform_data,
    }
    };
    static const short da850_evm_spi_gpio_pins[] = {
    DA850_GPIO0_11, DA850_GPIO1_1, DA850_GPIO0_10,
    -1
    };

    static struct spi_board_info da850evm_spi_info2[] = {
    {
    .modalias = "spidev",
    .mode = SPI_MODE_0,
    .max_speed_hz = 757000,
    .irq = -1,
    .bus_num = 2, // This is the same as .id above? Or .id+1?
    .chip_select = 2,
    },
    };
    static void __init da850evm_init_spi2(struct spi_board_info *info, unsigned len)
    {
    int ret;
    // Cloned from da8xx_register_spi()
    ret = spi_register_board_info(info, len);
    if (ret)
    pr_warning("da850_evm_init: spidev register failed: %d\n", ret);

    ret = platform_device_register(&da850evm_spi_device);
    if (ret)
    pr_warning("da850_evm_init: spi gpio register failed: %d\n", ret);

    ret = davinci_cfg_reg_list(da850_evm_spi_gpio_pins);
    if (ret)
    pr_warning("da850_evm_init: spi gpio mux setup failed: %d\n", ret);
    }

    da850evm_init_spi2(da850evm_spi_info2, ARRAY_SIZE(da850evm_spi_info2));

    after reboot I can see /dev/spidev2.2, but I have not tested it if it works, because my hardware is not ready yet.

    Joe

  • That code assumes no real SPI1. I was wondering if spi_register_board_info() could be callled twice. Once in da850evm_init_spi1() and once in the added code. Do you still get "/dev/m25p80"? To calling spi_register_board_info() twice, you would do something like this:

    --arch/arm/mach-davinci/include/mach/mux.h---
    enum davinci_da850_index {
    ...
      DA850_GPIO0_13,
      DA850_GPIO0_10,
      DA850_GPIO1_1,
      DA850_GPIO2_12,
    ...
    };

    ---arch/arm/mach-davinci/da850.c---
    static const struct mux_config da850_pins[] = {
    ...
      MUX_CFG(DA850, GPIO0_13,0,8,15,8, false)
      MUX_CFG(DA850, GPIO0_10,0, 20,15,8,false)
      MUX_CFG(DA850, GPIO1_1, 4, 24,15,8,false)
      MUX_CFG(DA850, GPIO2_12, 5, 12,15,8,false)
    ...
    };

    ---arch/arm/mach-davinci/board-da850-evm.c---
    #include <linux/spi/spi_gpio.h>
    #define GPIO_SPI_CLK GPIO_TO_PIN(0, 13) // GP0[13]
    #define GPIO_SPI_MISO GPIO_TO_PIN(0, 10) // GP0[10]
    #define GPIO_SPI_MOSI GPIO_TO_PIN(1, 1) // GP1[1]
    struct spi_gpio_platform_data da850evm_spi_platform_data = {
      .sck = GPIO_SPI_CLK,
      .mosi = GPIO_SPI_MOSI,
      .miso = GPIO_SPI_MISO,
      .num_chipselect = 3, // Not sure if this a bitmask
    };

    static struct platform_device da850evm_spi_device = {
      .name = "spi_gpio",
      .id = 2, // The real SPI use 0 and 1
      .dev = {
         .platform_data = &da850evm_spi_platform_data,
      }
    };
    static const short da850_evm_spi_gpio_pins[] = {
      DA850_GPIO0_11, DA850_GPIO1_1, DA850_GPIO0_10,
      -1
    };

    static struct spi_board_info da850evm_spi_info[] = {
      {
        .modalias        = "m25p80",
        .platform_data   = &da850evm_spiflash_data,
        .controller_data = &da850evm_spiflash_cfg,
        .mode            = SPI_MODE_0,
        .max_speed_hz    = 30000000,
        .bus_num         = 1,
        .chip_select     = 0,
      },
      {
        .modalias        = "spidev",
        .mode            = SPI_MODE_0,
        .max_speed_hz    = 757000,
        .irq             = -1,
        .bus_num         = 2, // This is the same as .id above? Or .id+1?
        .chip_select     = 2,
      },
    };

    static void __init da850evm_init_spi2(void)
    {
      int ret;

      // Cloned from da8xx_register_spi()
      // Note that da850evm_init_spi() will call spi_register_board_info()
      // with spidev board info.

      ret = platform_device_register(&da850evm_spi_device);
      if (ret)
         pr_warning("da850_evm_init: spi gpio register failed: %d\n", ret);

      ret = davinci_cfg_reg_list(da850_evm_spi_gpio_pins);
      if (ret)
        pr_warning("da850_evm_init: spi gpio mux setup failed: %d\n", ret);
    }

    da850evm_init_spi2();

  • Hi Norman,

    I used spidev twice and wish that one hooks on spi_davinci and other is on spi_gpio. The code like this

    static struct spi_board_info da850evm_spi_info[] = {

    {
    .modalias = "spidev",
    .controller_data = &da850evm_spi1s2_cfg,
    .mode = SPI_MODE_0,
    .max_speed_hz = 757000,
    .bus_num = 1,
    .chip_select = 2,
    },
    };

    static struct spi_board_info da850evm_spi_info2[] = {

    {
    .modalias = "spidev",
    .mode = SPI_MODE_0,
    .max_speed_hz = 757000,
    .irq = -1,
    .bus_num = 2, // This is the same as .id above? Or .id+1?
    .chip_select = 2,
    },
    };

    so after reboot, there are /dev/spidev1.2  /dev/spidev2.2. 

    I wish I can access real spi by opening spidev1.2 and access not real dspi by spidev2.2 in userspace.

    Is this what your understood? I don't understand why you said spi_register_board_info() cannot be called twice.

    I am not sure if it works.  

    Joe

  • I am guessing about being able to call spi_register_board_info() twice. Not saying that I know. The spi_register_board_info() code appears to be doing some one time only things. But I am just guessing. Since you see both dev/spidev1.2 and /dev/spidev2.2, it implies spi_register_board_info() is okay with being called once for each spidev. All good. Hopefully it all works.

  • Hi Norman,

    Just let you know that both driver work.

    Thank you for your help. Without your help mission impossible :-)

    Joe

  • That's great. Please close this thread by verifying a post.