Raspberry PiでPPSをGPIOに出力する

GPSのPPS入力はやっていたが、カーネルでGPIOにPPSが出力できるだなんて、知らなかった。
ただし、内蔵クロックの精度で。多分。

# pps-gen-gpio
[pps-gen-gpio](https://github.com/jsln/pps-gen-gpio)というオープンソースがあるのを教えてもらった。知らなかった。
元は、パラレルポートに出力していたものを、Device Treeを参照するように変えたらしい。

# Device Tree
## BeagleBoneでのDTの書き方
[README.me](https://github.com/jsln/pps-gen-gpio/blob/master/README.md)には、BeagleBoneの例が書いてある。
GPIO1_19という端子があり、`&gpio1 19 GPIO_ACTIVE_HIGH`と書くらしい。
我らがRaspberry Piではどうか。

## Raspberry PiでのDTの書き方
まずは[Raspberry Pi本家のDT情報](https://www.raspberrypi.org/documentation/configuration/device-tree.md)。いっぱい書いてあるが、pps-genに使えそうな書き方については、えーと…。[Linux本家の情報](https://www.kernel.org/doc/Documentation/devicetree/bindings/gpio/gpio.txt)でも勉強しつつ…。

## Raspberry Piのdtsファイル
わからないときは、真似をするのも有効。というわけで、Raspberry Piで今使っているDTSを見る。
`bcm2708-rpi-b.dtb, bcm2708-rpi-b-plus.dtb, bcm2708-rpi-cm.dtb, and bcm2709-rpi-2-b.dtb`の4つがあるとのことで、確かに

```shell-session
~/git/raspberrypi/linux$ ls -1 arch/arm/boot/dts/bcm270*.dts
arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
arch/arm/boot/dts/bcm2708-rpi-b.dts
arch/arm/boot/dts/bcm2708-rpi-cm.dts
arch/arm/boot/dts/bcm2709-rpi-2-b.dts
```

あるのだが、いきなり、無い。自分の手元にあるModel A+用のものが。
よく読むと、

> Note that Model As and A+s will use the "b" and "b-plus" variants, respectively. 

とあるので、b-plusを使えということか。ややこしい。

## bcm2708-rpi-b-plus.dtsの内容
gpioで検索すると、

```
&spi0 {
        pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
        cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
```

という記載がある。[こちら](http://elinux.org/RPi_Low-level_peripherals)を見ると、SPI0はGPIO7と8で共用っぽいので、第1引数がGPIOの番号だろう。
それでは、第2引数にある`1`とは、何だろうか。

他の例を見ると、

```
&hdmi {
        hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
};
```

とあるので、`GPIO_ACTIVE_LOW`というマクロで書くのがわかりやすそうだ。
他にどんなマクロがあるか探してみると、

```c:include/dt-bindings/gpio/gpio.h
/* Bit 0 express polarity */
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW 1
```

とあるので、`1`は`GPIO_ACTIVE_LOW`か。想像と逆だった。

## GPIOの初期状態
[こちら](http://d.hatena.ne.jp/hnw/20150607)の記事によると、GPIOの番号によって初期状態が異なる。
初期状態が0(Low)のGPIOに対して、Active Highに設定すれば、問題ないのではないか(勘)。

## dtsへの追記

[pps-gen-gpio.txt](https://github.com/jsln/pps-gen-gpio/blob/master/pps-gen-gpio.txt)を見ながら、記載。

```
/ {
        compatible = "brcm,bcm2708";
        model = "Raspberry Pi Model B+";
        pps-gen {
                compatible = "pps-generator-gpios";
                pps-gen-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
                /* assert-falling-edge; */
        };
};
```

AssertはRising Edgeにしてみたかったので、コメントに。

## dtsのbuild

[Raspberry Pi本家のKernel building情報](https://www.raspberrypi.org/documentation/linux/kernel/building.md)をもとに、

```shell-session
~/git/raspberrypi/linux$ make -j8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
```

とすると、色々出た後、

```shell-session
  DTC     arch/arm/boot/dts/bcm2708-rpi-b-plus.dtb
```

と出て、dtbが作成される。

## dtbのインストール

[先ほどのbuild手順](https://www.raspberrypi.org/documentation/linux/kernel/building.md)の一部をそのまま

```shell-session
~/git/raspberrypi/linux$ sudo mount /dev/sdb1 mnt/fat32
~/git/raspberrypi/linux$ sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
~/git/raspberrypi/linux$ sudo umount mnt/fat32
```

## 起動してDTの確認

またも[本家DT情報](https://www.raspberrypi.org/documentation/linux/kernel/building.md)に、procでの確認方法が書いてある。なんて教育的なんだ(当たり前)。

```shell-session
pi@raspberrypi:~ $ dtc -I fs /proc/device-tree
:
	pps-gen {
		compatible = "pps-generator-gpios";
		pps-gen-gpios = <0xa 0x12 0x0>;
	};
```

反映されている模様。

# 再度pps-gen-gpio

ようやく[pps-gen-gpio](https://github.com/jsln/pps-gen-gpio)に戻ってきた。これをbuildする。
…が、build通らず。さらに、通して`insmod`しても、反応無しだった…。
そこで、2点修正。

## devm_gpiod_get()への第3引数の追加

kernel 4.4.30では、引数を3つ取るらしい。

```diff:pps_gen_gpio.c
@@ -198,7 +198,7 @@ static int pps_gen_gpio_probe(struct platform_device *pdev)
                return -ENOMEM;

        /* pps-gen is the function associated with gpio list pps-gen-gpios */
-       devdata->pps_gpio = devm_gpiod_get(dev, "pps-gen");
+       devdata->pps_gpio = devm_gpiod_get(dev, "pps-gen", GPIOD_ASIS); /* from enum gpiod_flags */
        if (IS_ERR(devdata->pps_gpio)) {
                dev_err(dev, "cannot get PPS GPIO %ld\n",
                        PTR_ERR(devdata->pps_gpio));
```

`GPIOD_ASIS`なら、デフォルトの端子状態から変えないんだろうな(また勘)。
これ以外には、こんなのがあるらしい。

```c:include/linux/gpio/consumer.h
enum gpiod_flags {
        GPIOD_ASIS      = 0,
        GPIOD_IN        = GPIOD_FLAGS_BIT_DIR_SET,
        GPIOD_OUT_LOW   = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
        GPIOD_OUT_HIGH  = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
                          GPIOD_FLAGS_BIT_DIR_VAL,
};
```

## compatibleがドキュメントと異なる

[README.me](https://github.com/jsln/pps-gen-gpio/blob/master/README.md)や[pps-gen-gpio](https://github.com/jsln/pps-gen-gpio/blob/master/pps-gen-gpio.txt)によると、

> pps-generator-gpios   value of ".compatible" property in pps-gen node

とあるが、ドライバのソース上は`{ .compatible = "pps-gen-gpios", },`となっているように見える。
DTとドライバでcompatibleが異なると、`probe`が呼ばれず、`dmesg`しても

```shell-session
[  369.273294] pps_gen_gpio: GPIO PPS signal generator
```

より先が表示されない気がしたので、ソースをドキュメントに合わせて修正。

```diff:pps_gen_gpio.c
@@ -233,7 +233,7 @@ static int pps_gen_gpio_remove(struct platform_device *pdev)
  * when a match is found, the corresponding DT node name is passed
  * backed in pdev->name */
 static const struct of_device_id pps_gen_gpio_dt_ids[] = {
-       { .compatible = "pps-gen-gpios", },
+       { .compatible = "pps-generator-gpios", },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, pps_gen_gpio_dt_ids);
```


## pps-gen-gpioのbuild例

カーネルソースの場所は環境に合わせて下さい。

```shell-session
$ make -C ~/git/raspberrypi/linux ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=`pwd` CONFIG_PPS_GENERATOR_GPIO=m
```

## 実行
```shell-session
pi@raspberrypi:~ $ sudo insmod pps_gen_gpio.ko
```

`dmesg`でこんなログが出てきていれば、きっと動いている。

```
[  671.795926] pps_gen_gpio: GPIO PPS signal generator
[  671.796269] pps_gen_gpio: found 1 GPIOS defined in DT
[  671.796391] pps_gen_gpio: port write takes 500ns
```

## 測定

<img width="1287" alt="active_high.png" src="https://qiita-image-store.s3.amazonaws.com/0/104225/f30a7f1e-72c7-7c75-822e-b768c98c9fc7.png">

パルス幅は約28usくらいらしい。

# おまけ::Active Low

<img width="1287" alt="active_low.png" src="https://qiita-image-store.s3.amazonaws.com/0/104225/2cb1b7bf-cb68-3156-9363-6ea9daa19847.png">

おお、逆になっている。
あ、DTに`assert-falling-edge`付けるの忘れたが、edgeはどっちになるんだろ。

# 自由研究
- ドリフトとジッタの調査
- `dmesg`に出るエラー(?)の内容の調査
- NTP補正との関係の調査

# 修正ソース
[こちら](https://github.com/air-gh/pps-gen-gpio/tree/recent-kernel)。