AllwinnerA64(SOPINE)でGPIOとUARTをBaremetalで使ってみた

# ◆ はじめに
SOPINE(sun50i)でレジスタを直接操作してGPIOとUARTを使う方法について記載します。
本稿ではAllwinnerA64のUART0を設定する方法になりますが、他のUARTも同じだと思います（多分）。
GPIOもPortBの#2(PB2)を設定する方法になりますが、（おそらく）他も一緒だと思います。

# ◆ PIN SELECT
最初に、GPIOのPB2とUART0のTXとRXを使うようにPINの設定を行います。
GPIOのPB2は、そのままの意味ですがPB2(bank:1, pin:2)を使います。
UART0のTXはPB8(bank:1, pin:8)、UART0のRXはPB9(bank:1, pin:9)を使います。
それぞれの設定は該当するレジスタの値をセットすればOKです。
各ピンにどの機能が割り当てられているかは、AllwinnerA64のUserManualのP.３７６〜を見てください。

## ◆ Port Configure Register
PortControllerのBaseAddressは<font color=red>0x01c20800</font>になります。
各PINでどの機能を使うかはPort n Configure Register 0-3で設定します。
詳細は下記表になります。

|レジスタ|offset|対応GPIO|
|:----:|:-----:|:------:|
|PB_CFG0|+0x24|PB0〜PB7|
|PB_CFG1|+0x28|PB8〜PB9|

今回はPortBに限定してますが、PortC、PortD、PortDの場合はOffisetに+0x24、+0x48、+0x6Cを加算してください。
つまり一般化すると、
Pnmのピン(nはB,C,D,・・・,H、　mは０〜２４)のConfigure Registerのアドレスは、

BaseAddress(<font color=red>0x01c20800</font>) + 0x24 * p + 0x4 * q
(pは、B=1, C=2, D=3・・・、　q=0:0≦m≦7、1:8≦m≦15、2:16≦m≦23、3:24≦m≦31)

細かいのはUserManual参照でお願いします。

また、各ピンの機能はLSBから4bitごとに分けて設定します。
PB_CFG0の場合、[3:0]がPB0、[7:4]がPB1となります。
そこで、PB2をGPIOのOutput、PB8をUART0_TX、PB9をUART0_RXに設定する場合、下記のようにします。

|レジスタ|Offset|bit|設定値|
|:---:|:----:|:--:|:---:|
|PB_CFG0|+0x24|[11:8]|0b0001(0x1)|
|PB_CFG1|+0x28|[3:0]|0b0100(0x4)|
|PB_CFG0|+0x28|[7:4]|0b0100(0x4)|

# ◆ UART
UARTの設定では、順番に以下を設定します。

1. クロックの設定
2. ボーレートの設定
3. 一度の送信ビット数、パリティ、ストップビットの設定

## ◆ クロックの設定
BUS_CLK_GATING_REG3(<font color=red>0x01c2006c</font>)の[16]がUART0のクロックゲーティング用bitですので、
このbitを1にセットすればOKです。
なお、UserManualによるとこのクロックは24MHzのようです。
また、BUS_SOFT_RST_REG4(<font color=red>0x01c202d8</font>)の[16]に1をセットします。
これでソフトウェアリセットがかかります。

## ◆ ボーレートの設定
今回はFIFOを<font color=red>使わない</font>方法を記載します。
まずボーレートをDivisor Latch Low Registor(UART0_DLL)とDivisor Latch High Register(UART0_DLH)で指定することを、
UART0 Line Control Register(UART0_LCR:<font color=red>0x01c28000</font>)で指定します。
UART0_LCR[7]に1をセットすればOKです。
このあと、UART0_DLLとUART０_DLHを使いDivisorを設定しますが、この設定値は以下のように算出します。
ボーレート = クロック周波数 / (16 * Divisor)
AllwinnerのUART0のボーレートは115,200にすること通例のようです。
また、クロック周波数は前述のとおり24MHzです。
そうなるとDivisorは下記の通りになります

Divisor = 24 * 1000 * 1000 / 16 / 115200 = 0xD 
（余りが出るけどいいのだろうか・・・。サンプルでもコレだし動くから目をつぶります）

そこで、UART0_DDHに0x0、UART_DDLに0xDを設定します。

## ◆ 一度の送信ビット数、パリティ、ストップビットの設定
ここでは、一度の送信ビット数、パリティ、ストップビットの設定を行います。
それぞれは下記の通りにUART0_LCR(<font color=red>0x01c28000</font>)を設定します。

|設定項目|bit|設定値|
|:--:|:--:|:--|
|パリティ|[3]|1:enable<br/>0:disable|
|ストップビット|[2]|1:2bit<br/>0:1bit|
|送信bit数|[1:0]|11:8bit<br/>10:7bit<br/>01:6bit<br/>00:5bit|

今回は、一度の送信で8bit、パリティビットなし、ストップビットは1bitで設定します。
したがって、0x00000003をUART0_LCR(<font color=red>0x01c28000</font>)に書き込みます。

# ◆ 使ってみる
それでは、GPIOやUARTを使ってみます。
まずGPIOにHigh/Lowを出力させてみます。
PB2の場合、PB Data Registerを使います。
具体的には、PB_DATA_REG(<font color=red>0x01c20834</font>)[2]がそれに該当しますので、
ここに1をセットすればHigh、0をセットすればLowになります。

次にUART0を使います。
FIFOを使ってないので、UART Transmit Holding Register(送信)、UART Receive Buffer Register(受信)を使います。
両方ともアドレスは<font color=red>0x01c28000</font>となります。
ここの[7:0]に書き込み/読み込みした値がそれぞれ、UART0の送信/受信となります。
送信時は、UART Line Status Register(<font color=red>0x01c28014</font>)[6]が0の時はデータ送信中となるので、
1になるまでループなりで待つ必要があります。
受信時は、UART Line Status Register(<font color=red>0x01c28014</font>)[0]が0の時はデータ受信中になるので、
1になるまでループなりで待つ必要があります。

# ◆ おわりに
以上が、GPIO、UARTの設定と使う方法になります。
これでBareMetalプログラミングをする際にデバッグメッセージを出力させたり、
簡単なLED点灯による動作確認などができるかと思います。
次は今のコードをRust化させるところですが、どうなるやら。

あと、sunxi-felのhex[dump]が32bitのLSBの8bitしか表示されないことにハマりました。
自分の環境が悪いのかどうかは調査してないので不明ですが、結果として動いたのでよしとします。