4 Replies Latest reply on Jan 21, 2016 6:53 PM by N7QNM

    Let's make pps_gpio work on Quark/Galileo

    gigneil

      Hey all - first post.  Excited about my board. This shouldn't be impossible for this group

       

      ntpd offers a lot of different ways to get time from reference sources - most of them not very accurate at all.  The key technology for aligning nanoseconds to reference time is 1PPS - a single pulse per second that is aligned to UTC.

       

      Linux and FreeBSD offer a wide variety of PPS support (pps_core.ko) - you have to do it with real serial (not USB) or with a dedicated interrupt port.  With the advent of the Raspberry Pi, support for GPIO quickly became mainline via an additional kernel module called pps_gpio.n  With it, I can keep ntpd hosts disciplined to within a microsecond or less of each other and of UTC(USNO).

       

      Now here's the catch - GPIO isn't an enumerated device.  It isn't a printer port, or a wideband radio interface.  Its an on off switch.  So in order for the PPS kernel discipline to use it, it needs access to it as a platform device.  You configure platform devices as part of board setup usually, where you'd register other board specific drivers.

       

      On ARM, its pretty simple. In fact, for a Raspberry Pi, this is all you have to do:

       

      diff -ur linux-rpi/arch/arm/mach-bcm2708/bcm2708.c linux-rpi-pps/arch/arm/mach-bcm2708/bcm2708.c

      --- linux-rpi/arch/arm/mach-bcm2708/bcm2708.c

      +++ linux-rpi-pps/arch/arm/mach-bcm2708/bcm2708.c

      @@ -33,6 +33,7 @@

      #include <linux/module.h>

      #include <linux/spi/spi.h>

      #include <linux/w1-gpio.h>

      +#include <linux/pps-gpio.h>

       

      #include <linux/version.h>

      #include <linux/clkdev.h>

      @@ -419,6 +420,22 @@

      #ifdef CONFIG_BCM2708_GPIO

      #define BCM_GPIO_DRIVER_NAME "bcm2708_gpio"

       

      +/* PPS-GPIO platform data */

      +static struct pps_gpio_platform_data pps_gpio_info = {

      + .assert_falling_edge = false,

      + .capture_clear= false,

      + .gpio_pin=18,

      + .gpio_label="PPS",

      +};

      +

      +static struct platform_device pps_gpio_device = {

      + .name = "pps-gpio",

      + .id = -1,

      + .dev = {

      + .platform_data = &pps_gpio_info

      + },

      +};

      +

      static struct resource bcm2708_gpio_resources[] = {

        [0] = { /* general purpose I/O */

              .start = GPIO_BASE,

      @@ -709,6 +726,7 @@

        bcm_register_device(&bcm2708_vcio_device);

      #ifdef CONFIG_BCM2708_GPIO

        bcm_register_device(&bcm2708_gpio_device);

      + bcm_register_device(&pps_gpio_device);

      #endif

      #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE)

        platform_device_register(&w1_device);

       

      Basically in the main 2708 driver, you've enumerated this platform device that is on GPIO18.  The PPS core driver picks it up.  That's it.  It seems almost too easy.  There's even a GPIO18 on my Galileo.    And the pps_gpio module is built and waiting for me.

       

      Now I've been digging through the BSPs for hours - there's a lot more moving parts to a Galileo linux stack than a Raspberry Pi's.  (fundamental design decisions).  I am uncertain where exactly I would want to put this patch, and if the semantics are the same or similar for an IA32 machine - and even for the specific pieces and parts on the Galileo board.   The register_device function will clearly not be the same - and there are a whole lot of different ones for all the different GPIO devices the Quark chipset has.

       

      So I could really use some guidance here - I just want to register one platform device on one GPIO pin in the kernel...

       

      ...without blowing up Linux. yocto

       

      NS

        • 1. Re: Let's make pps_gpio work on Quark/Galileo

          Hi gigneil,

           

          This is an interesting  question, I will try to additional information I will post the results in this discussion as soon as I get something.

           

          Regards,

          • 2. Re: Let's make pps_gpio work on Quark/Galileo
            AKB_Intel

            LDiego_Intel, gigneil,

             

            This is something I'm very interested in getting working as well.

             

            I have a Galileo Gen2 board working with a modified Adafruit GPS shield (changed TX and RX to work with Galileo Pinout) and also have ntpd and gpsd running.  I've also modified my GPS shield to be able to use PPS out from the Adafruit GPS module to be able to jumper it to any of the digital pins of the arduino connector (in looking at the GEN2 schematics, it appears IO5 to Quark GPIO5 or IO6 to Quark GPIO6 would work best so as to get a direct GPIO interrupt to the Quark rather than running through the I/O expander chip).

             

            So from what I can tell, the last piece of the puzzle to get PPS working with NTPd is the PPS interrupt via GPIO.

             

            Is anyone else working on this, or do I need to start digging into the Galileo Yocto kernel and start making my own modifications?

             

            Thanks,

             

            Alan

             

            NOTE: I am an Intel Employee, but I don't work in the Galileo or the Yocto Linux team, I work as a system software engineer on a totally different Intel product and like most folks in this community, I'm a newbie to Galileo and Yocto (but in my case, not a newbie for embedded Linux).

            • 3. Re: Let's make pps_gpio work on Quark/Galileo
              AKB_Intel

              gigneil

               

              gigneil, LDiego_Int

               

              Good news, after working on this for a while, I finally got a breakthrough and I'm now able to get a PPS GPIO interrupt working in connecting an Adafruit Ultimate GPS logger shield to my Galileo Gen2 board and configuring and modifying the Linux kernel for PPS GPIO support.

               

              The key breakthrough was that when it comes to the Galileo gen2 board, GPIOs and interrupt, the key thing I found was that I could not get this to work until I moved call to register the GPIO PPS device to the gen2 board platform code after all the other I2C and SPI devices were initialized.  It appears that the way the Galileo GPIO and/or Interrupt setup code is structured, to get you own GPIO and/or interrupt, you have to request it after the Galileo is all done with its GPIO/Interrupt setup (probably due to some sequencing/loading/initialization).

               

              In getting this up and running, for me these were the most useful links for help:

               

              http://mythopoeic.org/pi-ntp/
              http://rdlazaro.info/compu-Raspberry_Pi-RPi-stratum0.html

              https://groups.google.com/forum/#!topic/beagleboard/bU_xZ9tWoiA

               

              To get this to work I first had to do a bitbake linux-yocto-clanton -c menuconfig -f and go through the kernel menu and enable PPS and PPS GPIO support.

               

              I then applied a similar set of changes (most useful for me was the beagle board info at Google Groups) to the Intel Gen2 platform driver code

               

              For my case, I put all my Galileo Yocto source under a folder my home directory "bsp" and then use the convention of using "yocto_build" for the builds.

               

              So for the Gailelo board platform source code, it is at:

              ~/bsp/meta-clanton_v1.0.1/yocto_build/tmp/work/clanton-poky-linux-uclibc/linux-yocto-clanton/3.8-r0/linux/drivers/platform/x86/quark

               

              The file I modified (Gen2 board, I haven't looked nor do I plan to look at how to do this on a Gen1 board) is: intel_qrk_plat_galileo_gen2.c

               

              Here is a summary of the modifications:

               

              At the top of the file add the .h file for GPIO PPS

               

              #include <linux/pps-gpio.h> // Added for PPS support
              
              

               

              Add code for PPS structures and init function (for my case I added this right after the spi1_onboard_devs struct).  NOTE: for my case I chose gpio_pin of 1 as due to the way the Galileo Linux code is structured and the way the actual board routes the GPIO, I wanted a real Quark GPIO (not one off of the expander chip), so I used real Quark GPIO_9 which maps to Digital IO pin IO6 on the Arduino connector and in linux software for some reason is setup as GPIO 1.

               

              /* PPS GPIO data */
              
              static struct pps_gpio_platform_data pps_gpio_info = {
                  .assert_falling_edge = false,
                  .capture_clear = false,
                  .gpio_pin = 1,
                  .gpio_label = "PPS",
              };
              
              
              static struct platform_device pps_gpio_device = {
                  .name = "pps-gpio",
                  .id = -1,
                  .dev = {
                      .platform_data = &pps_gpio_info
                  },
              };
              
              static void pps_init(void) {
                  int err;
              
                  pr_info("%s:\n",__func__);
                  err = platform_device_register(&pps_gpio_device);
                  if (err) {
                      pr_err("%s: Could not register PPS_GPIO device\n",__func__);
                  } else {
                      pr_info("%s: PPS_GPIO device registered OK\n",__func__);
                  }
              }
              
              

              Here is the important part (and got it to work for me), add the call to pps_init at the end of the restricted GPIO probe function:

               

              /**
              * intel_qrk_gpio_restrict_probe
              *
              * Make GPIOs pertaining to Firmware inaccessible by requesting them. The
              * GPIOs are never released nor accessed by this driver.
              *
              * Registers devices which are dependent on this GPIO driver
              */
              static int intel_qrk_gpio_restrict_probe(struct platform_device *pdev)
              {
                  int ret = 0;
                  struct i2c_adapter *i2c_adap = NULL;
                  struct i2c_client *client = NULL;
              
                  // << OTHER CODE NOT SHOWN HERE >>
                  // << Added pps code at the end right before return >>
                
                  i2c_put_adapter(i2c_adap);
              
                  pr_info("%s: Calling pps_init\n", __func__);
                  pps_init();
                  pr_info("%s: PPS init completed\n",__func__);
              
                  return ret;
              }
              
              

              With that (and other changes I'm doing for my project), I then did a bitbake linux-yocto-clanton -c compile -f to get a clean compile, then a bitbake linux-yocto-clanton to do a full kernel build, and finally a bitbake image-full-galileo (actually in my project I have a custom bitbake recipe called bitbake-image-full-timeserver as I'm also building loading the GPS daemon, NTP and PTP software as well).

               

              I then copied the image files to my SD card, loaded up on my Galileo GEN2 card and then booted, here are the key messages showing PPS/GPIO stuff loading OK (NOTE: In my case, I've added a lot of additional pr_info to multiple places my custom kernel as part of my debugging this over the last several weekends).

               

              Text below from boot messages (also can be seen using the dmesg command), first the pps core is setup during platform init, this happens due to the fact the PPS driver was selected in the menu config options:

               

              [    4.005545] pps_core: pps_init
              [    4.065081] pps_core: pps_init: LinuxPPS API ver. 1 registered
              [    4.071045] pps_core: pps_init: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
              
              

               

              Later in the boot, the GEN2 restricted GPIO probe executes (NOTE: My kernel has lots of pr_info added, you won't see all of these messages).  At the end, the pps_init in the platform driver is called.

               

              [  10.070250] intel_qrk_gip_probe:
              [  10.073556] intel_qrk_gip 0000:00:15.2: enabling device (0000 -> 0002)
              [  10.230188] intel_qrk_gpio_probe:
              [  10.234546] intel_qrk_gpio_restrict_probe:
              [  10.238686] intel_qrk_gpio_restrict_probe: Getting I2C adapter
              [  10.623196] intel_qrk_gpio_restrict_probe: i2c adapter not ready yet. Deferring..
              [  10.760678] platform qrk-gpio-restrict-sc.0: Driver qrk-gpio-restrict-sc requests probe deferral
              [  10.987471] intel_qrk_gpio_probe UIO addr 0x90006000 internal_addr 0xd262c000 size 4096 memtype 1
              [  11.200571] sch_gpio_probe UIO port addr 0x1080 size 64 porttype 1
              [  11.206925] intel_qrk_gpio_restrict_probe:
              [  11.211146] intel_qrk_gpio_restrict_probe: Getting I2C adapter
              [  11.221741] intel_qrk_gip 0000:00:15.2: i2c speed set to 400kHz
              [  11.380116] intel_qrk_gpio_restrict_probe: i2c adapter not ready yet. Deferring..
              [  11.387699] platform qrk-gpio-restrict-sc.0: Driver qrk-gpio-restrict-sc requests probe deferral
              [  11.397153] intel_qrk_gip 0000:00:15.2: enabling bus mastering
              [  11.397206] intel_qrk_gip 0000:00:15.2: setting latency timer to 64
              [  11.397354] intel_qrk_gip 0000:00:15.2: irq 43 for MSI/MSI-X
              [  13.539455] pxa2xx-spi pxa2xx-spi.0: registered master spi0
              [  13.555749] spi_master spi0: will run message pump with realtime priority
              [  13.582953] intel_qrk_gpio_restrict_probe:
              [  13.587094] intel_qrk_gpio_restrict_probe: Getting I2C adapter
              [  13.593567] pxa2xx-spi pxa2xx-spi.1: registered master spi1
              [  13.600173] spi_master spi1: will run message pump with realtime priority
              [  13.625882] intel_qrk_gpio_restrict_probe: Reserving Gen2 specific GPIOs
              [  13.632723] intel_qrk_gpio_restrict_probe: Probing I2C devices
              [  13.711894] intel_qrk_gpio_restrict_probe: SPI: Add onboard devices
              [  13.718206] intel_qrk_spi_add_onboard_devs:
              [  13.786481] spi spi0.0: 1666666 Hz actual, PIO
              [  13.786543] spi spi0.0: setup mode 3, 8 bits/w, 16667000 Hz max --> 0
              [  13.786904] pxa2xx-spi pxa2xx-spi.0: registered child spi0.0
              [  13.787011] spi spi1.0: 5000000 Hz actual, PIO
              [  13.787056] spi spi1.0: setup mode 0, 8 bits/w, 50000000 Hz max --> 0
              [  13.787411] pxa2xx-spi pxa2xx-spi.1: registered child spi1.0
              [  13.787450] intel_qrk_gpio_restrict_probe: I2C devices' probes complete, releasing I2C bus
              [  13.795838] intel_qrk_gpio_restrict_probe: Calling pps_init
              [  13.801513] pps_init:
              [  13.890479] pps_init: PPS_GPIO device registered OK
              [  13.895400] intel_qrk_gpio_restrict_probe: PPS init completed
              
              

               

               

              Then after boot, to test this I followed the guidelines in the other RPi and beaglebone "how to" posts, and I then did a modprobe pps_gpio to load and enable GPIO PPS driver which during the probes, sets up a PPS GPIO source and assigns the proper interrupt to it:

               

              root@clanton:~# modprobe pps_gpio
              [  142.681622] pps-gpio: pps_gpio_probe: Pin: 1, Label:PPS
              [  142.686894] pps-gpio: pps_gpio_setup: Pin: 1, Label:PPS
              [  142.692264] pps-gpio: pps_gpio_probe: GPIO 1 mapped to IRQ 23
              [  142.706485] pps_core: pps_register_source: name: pps-gpio.-1, default_params: 17
              [  142.714674] pps_core: pps_register_cdev
              [  142.726165] pps_core: pps_register_source: pps_regigster_cdev for name: pps-gpio.-1 OK
              [  142.734238] pps pps0: new PPS source pps-gpio.-1
              [  142.746485] pps pps0: Registered IRQ 23 as PPS source
              root@clanton:~# 
              

               

              To make sure it and the PPS core is loaded and running OK, I used the lsmod command and fgrep pps to get a list of all modules with "pps" in it.  Note that now, there is both pps_gpio and pps_core running:

               

              root@clanton:~# lsmod | fgrep pps
              pps_gpio 12590 0 - Live 0xd26d7000
              pps_core 13813 2 pps_gpio,ptp, Live 0xd261d000
              root@clanton:~#
              

               

               

              Now to test PPS, I first checked the schematic to make sure I wouldn't short anything by doing so (it was OK, there are several resistors on the GPIO lines of interest) and took a wire from 3.3V on the GPS shield to IO6 input and ran the command to make sure the PPS was interrupting and logging time when it occurs in the kernel.

               

              First I ran the checked the pps0 assert with no triggers.  In running it multiple times, the value is always 0.0

              root@clanton:~# cat /sys/class/pps/pps0/assert
              0.000000000#0
              root@clanton:~#
              

              Now I temporarily touched the 3.3V wire to IO6 and checked the assert.  Now I'm getting interrupts and the PPS GPIO interrupt software gets the time when they occur :-)

               

              root@clanton:~# cat /sys/class/pps/pps0/assert
              978310016.813620760#1
              root@clanton:~#
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978310030.491348967#2
              root@clanton:~#
              

              I then connected a wire connected from Adafruit shield PPS pin (output from real GPS module locked to time and position) to IO6 (real PPS) and I'm getting PPS interrupts :-)

              You can see that the Galileo clock is running fast.

               

              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311150.513781501#186
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311151.513850230#187
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311152.513913046#188
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311153.513976308#189
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311154.514036989#190
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311155.514100186#191
              root@clanton:~# cat /sys/class/pps/pps0/assert
              978311156.514142334#192
              root@clanton:~#
              

               

              So that is enough for me this weekend.  Next weekend I'm going to try and connect PPS interrupts to NTP to hopefully get my Galileo GEN2 working as well (or preferably better) than a Raspberry PI can :-)

               

              Hope this helps and best regards,

               

              Alan

              • 4. Re: Let's make pps_gpio work on Quark/Galileo
                N7QNM

                Hi - did you ever get NTP working?  I'm building a combination Time/Frequency standard based on a surplus Trimble Thunderbolt, which has a GPS disciplined oscillator that's crazy accurate and provides PPS output that I'd like to interface to an SBC, and I have a "spare" Galileo.

                 

                Thanks!

                Clay Jackson