# [BSP] Enable GPIO LEDs in uboot ###### tags: `DTS`, `device tree`, `gpio`, `led`, `ipq5018`, `uboot` [toc] ## Introduction On embedded devices, we may usually need to light up LEDs according to different application or scenario. For example, when we power on a device, it is nice to have a LED indication to tell you that it is working. This idea can be implemented by hardware or software. We talk about the software implementation here. In this scenario, we have to light up a LED not only in uboot but in linux kernel. Therefore, we have to implement uboot driver and kernel driver for it. In this note, I will show you not only **how to add a node and define its property and value in the u-boot device tree source(DTS)** but **how to add a piece of code in uboot driver**. ## Preparation Let's get started with this task by 1. Review the schematic for GPIO like: ![](https://i.imgur.com/cLPGFH4.png =80x200) 2. Review the datasheet for GPIO register like: - TLMM_GPIO_CFGn - TLMM_GPIO_IN_OUT_n - TLMM = Top-Level Mode Multiplexer https://img-blog.csdnimg.cn/20190314171758164.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW56aGVuMTA4MA==,size_16,color_FFFFFF,t_70 https://img-blog.csdnimg.cn/20190314171808157.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW56aGVuMTA4MA==,size_16,color_FFFFFF,t_70 https://img-blog.csdnimg.cn/20190314171808157.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW56aGVuMTA4MA==,size_16,color_FFFFFF,t_70 https://blog.csdn.net/chenzhen1080/article/details/88557274 3. Trace uboot source code and dts. 3.1 *u-boot-2016/arch/arm/include/asm/arch-qca-common/gpio.h* 3.2 *u-boot-2016/include/dt-bindings/qcom/gpio-ipq5018.h* 3.3 *u-boot-2016/drivers/gpio/ipq_gpio.c* 3.4 *u-boot-2016/arch/arm/dts/ipq5018-mp??.dts* 3.5 *u-boot-2016/include/configs/ipq5018.h* ## Implementation ### Light up LEDs by commands After booting into uboot, we can utilize *md* and *mw* commands to write memory (registers) to light up LED manually. ``` md - memory display mw - memory write (fill) ``` Assume our LED GPIO settings are 1. Pin 25 (0x19) 2. Pull up 3. no open drain 4. no drive strength 5. high active 6. output data enable This procedure consists of three steps. 1. Find and calculate GPIO configuration address and in/out address ```C #define TLMM_BASE 0x01000000 #define GPIO_CONFIG_ADDR(x) (TLMM_BASE + (x)*0x1000) #define GPIO_IN_OUT_ADDR(x) GPIO_CONFIG_ADDR(x) + 0x4 GPIO_CONFIG_ADDR = 0x01000000 + (0x19)*0x1000 = 0x01019000 GPIO_IN_OUT_ADDR = 0x01000000 + (0x19)*0x1000+ 0x4 = 0x01019004 ``` 2. Configure the GPIO pin for its property like: 2.1 gpio: pin number 0~64 2.2 function: the purpose of this pin like pure GPIO or others 2.3 out: output value 2.4 pull: pull up or pull down 2.5 drvstr: drive strength 2mA~16mA 2.6 oe: ouput data enable 2.7 vm: by gpio VDD 2.8 od_en: enable open drain 2.9 pu_res: pull-up resistors 0~3 2.10 sr_en: enable/disable the Slew Rate Control?? ```C #define SET_GPIO_PULL_BIT(x) x #define SET_GPIO_OUT_BIT(x) x<<1 #define SET_GPIO_FUNC_BIT(x) x<<2 #define SET_GPIO_DRVSTR_BIT(x) x<<6 #define SET_GPIO_OE_BIT(x) x<<9 #define SET_GPIO_OD_EN_BIT(x) x<<10 #define SET_GPIO_SR_EN_BIT(x) x<<11 #define SET_GPIO_PU_RES_BIT(x) x<<12 // The value of configuration GPIO is // 0x3 + 0 + (0x1 << 9) = 0x203 int val = SET_GPIO_PULL_BIT(3) + SET_GPIO_FUNC_BIT(0) + SET_GPIO_OE_BIT(1); ``` 3. Set the value to activate it. It's high active in uboot. ```C int val = SET_GPIO_OUT_BIT(1); // The value of GPIO is // 0x1 << 2 = 0x2 ``` #### Demo Light it up!! ```shell ipq5018# mw 0x01019000 0x203 ipq5018# mw 0x01019004 0x2 ipq5018# md 0x01019000 01019000: 00000203 00000003 000000e2 00000000 ................ 01019010: 00000000 00000000 00000000 00000000 ................ 01019020: 00000000 00000000 00000000 00000000 ................ 01019030: 00000000 00000000 00000000 00000000 ................ 01019040: 00000000 00000000 00000000 00000000 ................ 01019050: 00000000 00000000 00000000 00000000 ................ 01019060: 00000000 00000000 00000000 00000000 ................ 01019070: 00000000 00000000 00000000 00000000 ................ 01019080: 00000000 00000000 00000000 00000000 ................ 01019090: 00000000 00000000 00000000 00000000 ................ 010190a0: 00000000 00000000 00000000 00000000 ................ 010190b0: 00000000 00000000 00000000 00000000 ................ 010190c0: 00000000 00000000 00000000 00000000 ................ 010190d0: 00000000 00000000 00000000 00000000 ................ 010190e0: 00000000 00000000 00000000 00000000 ................ 010190f0: 00000000 00000000 00000000 00000000 ................ ``` ### Light up LEDs by drivers From the aforementioned demo, we make sure the GPIO configuration be good. And now, we try to light it up from the uboot driver. #### Naive version We can simply create a function and insert it in the uboot init sequence as shown below. ```C void my_board_led_init(void) { struct qca_gpio_config gpio_config = {0}; gpio_config.gpio = 25; gpio_config.out = GPIO_OUT_HIGH; //1 gpio_config.func = 0; gpio_config.pull = GPIO_PULL_UP; //3 gpio_config.oe = GPIO_OE_ENABLE; //1 gpio_tlmm_config(&gpio_config); //gpio_set_value(25, GPIO_OUT_HIGH); } ``` #### Sophisticated version Here, we can create a device subnode and define its proverty/value in DTS, and then we need to write a function in the driver to retrieve this node and corresponding values. Finally, these values will be configured successfully. 1. Add a subnode in DTS ``` / { aliases { gpio_led = "/gpio_leds"; } gpio_leds { power_led { gpio = <25>; func = <0>; pull = <GPIO_PULL_UP>; oe = <GPIO_OE_ENABLE>; out = <GPIO_OUT_HIGH>; }; }; }; ``` 2. Add an init function in the driver ```C void my_board_led_init(void) { int node_offet, pwrled_node; nodeoffet = fdt_path_offset(gd->fdt_blob, "gpio_led"); if (node_offet < 0) { printf("LED: Node Not found, skipping initalization\n"); return; } if (node_offet >= 0) { pwrled_node = fdt_subnode_offset(gd->fdt_blob, node_offet, "power_led"); if (pwrled_node >= 0) { // qca_gpio_init will parse property/value in subnodes of node_offet and config them into GPIO config address. qca_gpio_init(node_offet); // To turn off this LED /* int gpio = fdtdec_get_uint(gd->fdt_blob, pwrled_node, "gpio", 25); gpio_set_value(gpio, GPIO_OUT_LOW); */ } } } ``` ## Reference https://openwrt.org/docs/techref/hardware/port.gpio https://blog.csdn.net/chenzhen1080/article/details/88557274 https://blog.csdn.net/huzk4409/article/details/115089042 https://www.kernel.org/doc/Documentation/devicetree/bindings/gpio/gpio.txt https://loongembedded.blog.csdn.net/article/details/72834761?spm=1001.2101.3001.6650.14&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-14-72834761-blog-89416685.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-14-72834761-blog-89416685.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=18 80_10652_1_H_IPQ0518_WI_FI_ACCESS_POINT_SOC_DATA_S.pdf 80-10650-1_J_IPQ5018_wi-fi_Access Point_SoC_Data_Sheet.pdf 80-16052-17_e_ipq50xx_soc_software_user_guide.pdf