diff --git a/CODEOWNERS b/CODEOWNERS index f9291745f1221..3dfffb34aef32 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -220,6 +220,7 @@ /drivers/flash/ @nashif @nvlsianpu /drivers/flash/*nrf* @nvlsianpu /drivers/gpio/ @mnkp +/drivers/gpio/*b91* @yurvyn /drivers/gpio/*lmp90xxx* @henrikbrixandersen /drivers/gpio/*stm32* @erwango /drivers/gpio/*eos_s3* @wtatarski @kowalewskijan @kgugala @@ -263,6 +264,7 @@ /drivers/modem/Kconfig.hl7800 @rerickson1 /drivers/pcie/ @dcpleung @nashif @jhedberg /drivers/peci/ @albertofloyd @franciscomunoz @scottwcpg +/drivers/pinmux/*b91* @yurvyn /drivers/pinmux/*hsdk* @iriszzw /drivers/pinmux/*it8xxx2* @ite /drivers/pm_cpu_ops/ @carlocaione @@ -286,6 +288,7 @@ /drivers/sensor/lsm*/ @avisconti /drivers/sensor/mpr/ @sven-hm /drivers/sensor/st*/ @avisconti +/drivers/serial/*b91* @yurvyn /drivers/serial/uart_altera_jtag_hal.c @nashif /drivers/serial/*ns16550* @dcpleung @nashif @jenmwms @aasthagr /drivers/serial/*nrfx* @Mierunski @anangl diff --git a/boards/riscv/tlsr9518adk80d/Kconfig.board b/boards/riscv/tlsr9518adk80d/Kconfig.board new file mode 100644 index 0000000000000..bd36cb0e481f3 --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/Kconfig.board @@ -0,0 +1,6 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_TLSR9518ADK80D + bool "Telink B91 Platform" + depends on SOC_RISCV_TELINK_B91 diff --git a/boards/riscv/tlsr9518adk80d/Kconfig.defconfig b/boards/riscv/tlsr9518adk80d/Kconfig.defconfig new file mode 100644 index 0000000000000..2b3208b03d1aa --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/Kconfig.defconfig @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_TLSR9518ADK80D + +config BOARD + default "tlsr9518adk80d" + +config COMPRESSED_ISA + default y + +config GPIO_TELINK_B91 + default y if GPIO + +config UART_TELINK_B91 + default y if SERIAL + +config PINMUX_TELINK_B91 + default y if PINMUX + +endif diff --git a/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518_block_diagram.jpg b/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518_block_diagram.jpg new file mode 100644 index 0000000000000..2b0b2818ed3fb Binary files /dev/null and b/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518_block_diagram.jpg differ diff --git a/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518adk80d.jpg b/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518adk80d.jpg new file mode 100644 index 0000000000000..76f34a9dc9c36 Binary files /dev/null and b/boards/riscv/tlsr9518adk80d/doc/img/tlsr9518adk80d.jpg differ diff --git a/boards/riscv/tlsr9518adk80d/doc/index.rst b/boards/riscv/tlsr9518adk80d/doc/index.rst new file mode 100644 index 0000000000000..8f54ed0df5bed --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/doc/index.rst @@ -0,0 +1,208 @@ +.. _tlst9518adk80d: + +Telink TLSR9518ADK80D +##################### + +Overview +******** + +The TLSR9518A Generic Starter Kit is a hardware platform which +can be used to verify the `Telink TLSR9 series chipset`_ and develop applications +for several 2.4 GHz air interface standards including Bluetooth 5.2 (Basic data +rate, Enhanced data rate, LE, Indoor positioning and BLE Mesh), +Zigbee 3.0, Homekit, 6LoWPAN, Thread and 2.4 Ghz proprietary. + +.. figure:: img/tlsr9518adk80d.jpg + :width: 400px + :align: center + :alt: TLSR9518ADK80D + +More information about the board can be found at the `Telink B91 Generic Starter Kit Hardware Guide`_ website. + +Hardware +******** + +The TLSR9518A SoC integrates a powerful 32-bit RISC-V MCU, DSP, AI Engine, 2.4 GHz ISM Radio, 256 +KB SRAM (128 KB of Data Local Memory and 128 KB of Instruction Local Memory), external Flash memory, +stereo audio codec, 14 bit AUX ADC, analog and digital Microphone input, PWM, flexible IO interfaces, +and other peripheral blocks required for advanced IoT, hearable, and wearable devices. + +.. figure:: img/tlsr9518_block_diagram.jpg + :width: 400px + :align: center + :alt: TLSR9518ADK80D_SOC + +The TLSR9518ADK80D default board configuration provides the following hardware components: + +- RF conducted antenna +- 1 MB External Flash memory with reset button +- Chip reset button +- Mini USB interface +- 4-wire JTAG +- 4 LEDs, Key matrix up to 4 keys +- 2 line-in function (Dual Analog microphone supported when switching jumper from microphone path) +- Dual Digital microphone +- Stereo line-out + +Supported Features +================== + +The Zephyr TLSR9518ADK80D board configuration supports the following hardware features: + ++----------------+------------+------------------------------+ +| Interface | Controller | Driver/Component | ++================+============+==============================+ +| PLIC | on-chip | interrupt_controller | ++----------------+------------+------------------------------+ +| RISC-V Machine | on-chip | timer | +| Timer (32 KHz) | | | ++----------------+------------+------------------------------+ +| PINMUX | on-chip | pinmux | ++----------------+------------+------------------------------+ +| GPIO | on-chip | gpio | ++----------------+------------+------------------------------+ +| UART | on-chip | serial | ++----------------+------------+------------------------------+ + +The following example projects are supported: + +- samples/hello_world +- samples/synchronization +- samples/philosophers +- samples/basic/threads +- samples/basic/blinky +- samples/basic/button +- samples/subsys/console/echo +- samples/subsys/console/getchar +- samples/subsys/console/getline +- samples/subsys/shell/shell_module +- samples/subsys/cpp/cpp_synchronization + +.. note:: + To support "button" example project PC3-KEY3 (J20-19, J20-20) jumper needs to be removed and KEY3 (J20-19) should be connected to VDD3_DCDC (J51-13) externally. + + For the rest example projects use the default jumpers configuration. + +Other hardware features and example projects are not supported yet. + +Limitations +----------- + +- Maximum 3 GPIO pins could be configured to generate interrupts simultaneously. All pins must be related to different ports and use different IRQ numbers. +- DMA mode is not supported by Serial Port. +- UART hardware flow control is not implemented. + +Default configuration and IOs +============================= + +System Clock +------------ + +The TLSR9518ADK80D board is configured to use the 24 MHz external crystal oscillator +with the on-chip PLL/DIV generating the 48 MHz system clock. +The following values also could be assigned to the system clock in the board DTS file +(``boards/riscv/tlsr9518adk80d/tlsr9518adk80d.dts``): + +- 16000000 +- 24000000 +- 32000000 +- 48000000 +- 64000000 +- 96000000 + +.. code-block:: + + &cpu0 { + clock-frequency = <48000000>; + }; + +PINs Configuration +------------------ + +The TLSR9518A SoC has five GPIO controllers (PORT_A to PORT_E), but only two are +currently enabled (PORT_B for LEDs control and PORT_C for buttons) in the board DTS file: + +- LED0 (blue): PB4, LED1 (green): PB5, LED2 (white): PB6, LED3 (red): PB7 +- Key Matrix SW0: PC2_PC3, SW1: PC2_PC1, SW2: PC0_PC3, SW3: PC0_PC1 + +Peripheral's pins on the SoC are mapped to the following GPIO pins in the +``boards/riscv/tlsr9518adk80d/tlsr9518adk80d.dts`` file: + +- UART0 RX: PB2, TX: PB3 +- UART1 RX: PC6, TX: PC7 + +Serial Port +----------- + +The TLSR9518A SoC has 2 UARTs. The Zephyr console output is assigned +to UART0 in the ``boards/riscv/tlsr9518adk80d/tlsr9518adk80d_defconfig`` file. +The default settings are 115200 8N1. + +Programming and debugging +************************* + +Building +======== + +You can build applications in the usual way. Here is an example for +the "hello_world" application. + +.. code-block:: console + + # From the root of the zephyr repository + west build -b tlsr9518adk80d samples/hello_world + +To use `Telink RISC-V Linux Toolchain`_, ``ZEPHYR_TOOLCHAIN_VARIANT`` and ``CROSS_COMPILE`` variables need to be set. + +.. code-block:: console + + # Set Zephyr toolchain variant to cross-compile + export ZEPHYR_TOOLCHAIN_VARIANT=cross-compile + # Specify the Telink RISC-V Toolchain location + export CROSS_COMPILE=~/toolchains/nds32le-elf-mculib-v5f/bin/riscv32-elf- + # From the root of the zephyr repository + west build -b tlsr9518adk80d samples/hello_world + +`Telink RISC-V Linux Toolchain`_ is available on the `Burning and Debugging Tools for TLSR9 Series in Linux`_ page. + +Open a serial terminal with the following settings: + +- Speed: 115200 +- Data: 8 bits +- Parity: None +- Stop bits: 1 + +Flash the board, reset and observe the following messages on the selected +serial port: + +.. code-block:: console + + *** Booting Zephyr OS version 2.5.0 *** + Hello World! tlsr9518adk80d + + +Flashing +======== + +In order to flash the TLSR9518ADK80D board check the following resources: + +- `Burning and Debugging Tools for all Series`_ +- `Burning and Debugging Tools for TLSR9 Series`_ +- `Burning and Debugging Tools for TLSR9 Series in Linux`_ + +Debugging +========= + +Supporting UART debug and OpenOCD+GDB. + +References +********** + +.. target-notes:: + +.. _Telink TLSR9 series chipset: http://wiki.telink-semi.cn/wiki/chip-series/TLSR9-Series/ +.. _Telink B91 Generic Starter Kit Hardware Guide: http://wiki.telink-semi.cn/wiki/Hardware/B91_Generic_Starter_Kit_Hardware_Guide/ +.. _Telink RISC-V Linux Toolchain: http://wiki.telink-semi.cn/tools_and_sdk/Tools/IDE/telink_riscv_linux_toolchain.zip +.. _Burning and Debugging Tools for all Series: http://wiki.telink-semi.cn/wiki/IDE-and-Tools/Burning-and-Debugging-Tools-for-all-Series/ +.. _Burning and Debugging Tools for TLSR9 Series: http://wiki.telink-semi.cn/wiki/IDE-and-Tools/Burning-and-Debugging-Tools-for-TLSR9-Series/ +.. _Burning and Debugging Tools for TLSR9 Series in Linux: http://wiki.telink-semi.cn/wiki/IDE-and-Tools/BDT_for_TLSR9_Series_in_Linux/ diff --git a/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.dts b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.dts new file mode 100644 index 0000000000000..82bee5e59e0db --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.dts @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include + +/ { + model = "telink,b91"; + compatible = "telink,tlsr9518adk80d"; + + aliases { + led0 = &led_blue; + led1 = &led_green; + sw0 = &key_1; + }; + + leds { + compatible = "gpio-leds"; + + led_blue: led_0 { + gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; + label = "LED Blue"; + }; + + led_green: led_1 { + gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; + label = "LED Green"; + }; + + led_white: led_2 { + gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; + label = "LED White"; + }; + + led_red: led_3 { + gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; + label = "LED Red"; + }; + }; + + keys { + compatible = "gpio-keys"; + key_1: button_1 { + label = "User KEY1"; + gpios = <&gpioc 2 GPIO_PULL_DOWN>; + }; + }; + + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,sram = &ram_dlm; + zephyr,flash = &flash; + zephyr,flash-controller = &flash_mspi; + }; +}; + +&cpu0 { + clock-frequency = <48000000>; +}; + +&ram_ilm { + reg = <0x00000000 0x00020000>; +}; + +&ram_dlm { + reg = <0x00080000 0x00020000>; +}; + +&flash { + reg = <0x20000000 0x100000>; +}; + +&gpiob { + status = "okay"; +}; + +&gpioc { + interrupts = <25 1>; + status = "okay"; +}; + +&pinmux { + status = "okay"; + pad-mul-sel = <1>; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_tx_pb2 &uart0_rx_pb3>; +}; + +&flash_mspi { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x8000>; + }; + slot0_partition: partition@8000 { + label = "image-0"; + reg = <0x00008000 0x1a000>; + }; + slot1_partition: partition@22000 { + label = "image-1"; + reg = <0x00022000 0x1a000>; + }; + scratch_partition: partition@3c000 { + label = "image-scratch"; + reg = <0x0003c000 0x2000>; + }; + storage_partition: partition@f0000 { + label = "storage"; + reg = <0x000f0000 0x00010000>; + }; + }; +}; diff --git a/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.yaml b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.yaml new file mode 100644 index 0000000000000..e661002a9ef40 --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d.yaml @@ -0,0 +1,11 @@ +identifier: tlsr9518adk80d +name: Telink TLSR9518ADK80D +type: mcu +arch: riscv32 +toolchain: + - cross-compile + - zephyr +ram: 128 +flash: 1024 +supported: + - gpio diff --git a/boards/riscv/tlsr9518adk80d/tlsr9518adk80d_defconfig b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d_defconfig new file mode 100644 index 0000000000000..0ed61f5764367 --- /dev/null +++ b/boards/riscv/tlsr9518adk80d/tlsr9518adk80d_defconfig @@ -0,0 +1,17 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_SOC_SERIES_RISCV_TELINK_B91=y +CONFIG_SOC_RISCV_TELINK_B91=y +CONFIG_BOARD_TLSR9518ADK80D=y +CONFIG_PLIC=y +CONFIG_RISCV_MACHINE_TIMER=y +CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 +CONFIG_PINMUX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# HW DSP options +CONFIG_TELINK_B91_HWDSP=n diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 26eb35825ab71..be2cfc29631c3 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() +zephyr_library_sources_ifdef(CONFIG_GPIO_TELINK_B91 gpio_b91.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CC13XX_CC26XX gpio_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CC32XX gpio_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CMSDK_AHB gpio_cmsdk_ahb.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8b71fff389ab9..7af434bff6ba2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -21,6 +21,8 @@ config GPIO_SHELL help Enable GPIO Shell for testing. +source "drivers/gpio/Kconfig.b91" + source "drivers/gpio/Kconfig.dw" source "drivers/gpio/Kconfig.pca95xx" diff --git a/drivers/gpio/Kconfig.b91 b/drivers/gpio/Kconfig.b91 new file mode 100644 index 0000000000000..f6c26077ca054 --- /dev/null +++ b/drivers/gpio/Kconfig.b91 @@ -0,0 +1,10 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Telink B91 GPIO configuration options + +config GPIO_TELINK_B91 + bool "Telink B91 GPIO driver" + depends on SOC_RISCV_TELINK_B91 + help + Enable the B91 GPIO driver. diff --git a/drivers/gpio/gpio_b91.c b/drivers/gpio/gpio_b91.c new file mode 100644 index 0000000000000..6e7d11d0ac9b3 --- /dev/null +++ b/drivers/gpio/gpio_b91.c @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "analog.h" + +#include +#include +#include "gpio_utils.h" + + +/* Driver dts compatibility: telink,b91_gpio */ +#define DT_DRV_COMPAT telink_b91_gpio + +/* Get GPIO instance */ +#define GET_GPIO(dev) ((volatile struct gpio_b91_t *) \ + ((const struct gpio_b91_config *)dev->config)->gpio_base) + +/* Get GPIO configuration */ +#define GET_CFG(dev) ((const struct gpio_b91_config *)dev->config) + +/* Get GPIO IRQ number defined in dts */ +#define GET_IRQ_NUM(dev) (((const struct gpio_b91_config *)dev->config)->irq_num) + +/* Get GPIO IRQ priority defined in dts */ +#define GET_IRQ_PRIORITY(dev) (((const struct gpio_b91_config *)dev->config)->irq_priority) + +/* Get GPIO port number: port A - 0, port B - 1, ..., port F - 5 */ +#define GET_PORT_NUM(gpio) ((uint8_t)(((uint32_t)gpio - DT_REG_ADDR(DT_NODELABEL(gpioa))) / \ + DT_REG_SIZE(DT_NODELABEL(gpioa)))) + +/* Check that gpio is port C */ +#define IS_PORT_C(gpio) ((uint32_t)gpio == DT_REG_ADDR(DT_NODELABEL(gpioc))) + +/* Check that gpio is port D */ +#define IS_PORT_D(gpio) ((uint32_t)gpio == DT_REG_ADDR(DT_NODELABEL(gpiod))) + +/* Check that 'inst' has only 1 interrupt selected in dts */ +#define IS_INST_IRQ_EN(inst) (DT_NUM_IRQS(DT_DRV_INST(inst)) == 1) + +/* Max pin number per port (pin 0..7) */ +#define PIN_NUM_MAX ((uint8_t)7u) + +/* IRQ Enable registers */ +#define reg_irq_risc0_en(i) REG_ADDR8(0x140338 + i) +#define reg_irq_risc1_en(i) REG_ADDR8(0x140340 + i) + +/* Pull-up/down resistors */ +#define GPIO_PIN_UP_DOWN_FLOAT ((uint8_t)0u) +#define GPIO_PIN_PULLDOWN_100K ((uint8_t)2u) +#define GPIO_PIN_PULLUP_10K ((uint8_t)3u) + +/* GPIO interrupt types */ +#define INTR_RISING_EDGE ((uint8_t)0u) +#define INTR_FALLING_EDGE ((uint8_t)1u) +#define INTR_HIGH_LEVEL ((uint8_t)2u) +#define INTR_LOW_LEVEL ((uint8_t)3u) + +/* Supported IRQ numbers */ +#define IRQ_GPIO ((uint8_t)25u) +#define IRQ_GPIO2_RISC0 ((uint8_t)26u) +#define IRQ_GPIO2_RISC1 ((uint8_t)27u) + + +/* B91 GPIO registers structure */ +struct gpio_b91_t { + uint8_t input; /* Input: read GPI input */ + uint8_t ie; /* IE: input enable, high active. 1: enable, 0: disable */ + uint8_t oen; /* OEN: output enable, low active. 0: enable, 1: disable */ + uint8_t output; /* Output: configure GPIO output */ + uint8_t polarity; /* Polarity: interrupt polarity: rising, falling */ + uint8_t ds; /* DS: drive strength. 1: maximum (default), 0: minimal */ + uint8_t actas_gpio; /* Act as GPIO: enable (1) or disable (0) GPIO function */ + uint8_t irq_en; /* Act as GPIO: enable (1) or disable (0) GPIO function */ +}; + +/* GPIO driver configuration structure */ +struct gpio_b91_config { + struct gpio_driver_config common; + uint32_t gpio_base; + uint8_t irq_num; + uint8_t irq_priority; + void (*pirq_connect)(void); +}; + +/* GPIO driver data structure */ +struct gpio_b91_data { + struct gpio_driver_data common; /* driver data */ + sys_slist_t callbacks; /* list of callbacks */ +}; + + +/* Set IRQ Enable bit based on IRQ number */ +static inline void gpiob_b91_irq_en_set(const struct device *dev, gpio_pin_t pin) +{ + uint8_t irq = GET_IRQ_NUM(dev); + + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + if (irq == IRQ_GPIO) { + BM_SET(gpio->irq_en, BIT(pin)); + } else if (irq == IRQ_GPIO2_RISC0) { + BM_SET(reg_irq_risc0_en(GET_PORT_NUM(gpio)), BIT(pin)); + } else if (irq == IRQ_GPIO2_RISC1) { + BM_SET(reg_irq_risc1_en(GET_PORT_NUM(gpio)), BIT(pin)); + } else { + __ASSERT(false, "Not supported GPIO IRQ number."); + } +} + +/* Clear IRQ Enable bit based on IRQ number */ +static inline void gpiob_b91_irq_en_clr(const struct device *dev, gpio_pin_t pin) +{ + uint8_t irq = GET_IRQ_NUM(dev); + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + if (irq == IRQ_GPIO) { + BM_CLR(gpio->irq_en, BIT(pin)); + } else if (irq == IRQ_GPIO2_RISC0) { + BM_CLR(reg_irq_risc0_en(GET_PORT_NUM(gpio)), BIT(pin)); + } else if (irq == IRQ_GPIO2_RISC1) { + BM_CLR(reg_irq_risc1_en(GET_PORT_NUM(gpio)), BIT(pin)); + } +} + +/* Get IRQ Enable register value */ +static inline uint8_t gpio_b91_irq_en_get(const struct device *dev) +{ + uint8_t status = 0; + uint8_t irq = GET_IRQ_NUM(dev); + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + if (irq == IRQ_GPIO) { + status = gpio->irq_en; + } else if (irq == IRQ_GPIO2_RISC0) { + status = reg_irq_risc0_en(GET_PORT_NUM(gpio)); + } else if (irq == IRQ_GPIO2_RISC1) { + status = reg_irq_risc1_en(GET_PORT_NUM(gpio)); + } + + return status; +} + +/* Clear IRQ Status bit */ +static inline void gpio_b91_irq_status_clr(uint8_t irq) +{ + gpio_irq_status_e status = 0; + + if (irq == IRQ_GPIO) { + status = FLD_GPIO_IRQ_CLR; + } else if (irq == IRQ_GPIO2_RISC0) { + status = FLD_GPIO_IRQ_GPIO2RISC0_CLR; + } else if (irq == IRQ_GPIO2_RISC1) { + status = FLD_GPIO_IRQ_GPIO2RISC1_CLR; + } + + reg_gpio_irq_clr = status; +} + +/* Set pin's irq type */ +void gpio_b91_irq_set(const struct device *dev, gpio_pin_t pin, + uint8_t trigger_type) +{ + uint8_t irq_lvl = 0; + uint8_t irq_mask = 0; + uint8_t irq_num = GET_IRQ_NUM(dev); + uint8_t irq_prioriy = GET_IRQ_PRIORITY(dev); + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + /* Get level and mask bsed on IRQ number */ + if (irq_num == IRQ_GPIO) { + irq_lvl = FLD_GPIO_IRQ_LVL_GPIO; + irq_mask = FLD_GPIO_IRQ_MASK_GPIO; + } else if (irq_num == IRQ_GPIO2_RISC0) { + irq_lvl = FLD_GPIO_IRQ_LVL_GPIO2RISC0; + irq_mask = FLD_GPIO_IRQ_MASK_GPIO2RISC0; + } else if (irq_num == IRQ_GPIO2_RISC1) { + irq_lvl = FLD_GPIO_IRQ_LVL_GPIO2RISC1; + irq_mask = FLD_GPIO_IRQ_MASK_GPIO2RISC1; + } + + /* Set polarity and level */ + switch (trigger_type) { + case INTR_RISING_EDGE: + BM_CLR(gpio->polarity, BIT(pin)); + BM_CLR(reg_gpio_irq_risc_mask, irq_lvl); + break; + + case INTR_FALLING_EDGE: + BM_SET(gpio->polarity, BIT(pin)); + BM_CLR(reg_gpio_irq_risc_mask, irq_lvl); + break; + + case INTR_HIGH_LEVEL: + BM_CLR(gpio->polarity, BIT(pin)); + BM_SET(reg_gpio_irq_risc_mask, irq_lvl); + break; + + case INTR_LOW_LEVEL: + BM_SET(gpio->polarity, BIT(pin)); + BM_SET(reg_gpio_irq_risc_mask, irq_lvl); + break; + } + + if (irq_num == IRQ_GPIO) { + reg_gpio_irq_ctrl |= FLD_GPIO_CORE_INTERRUPT_EN; + } + gpio_b91_irq_status_clr(irq_num); + BM_SET(reg_gpio_irq_risc_mask, irq_mask); + + /* Enable peripheral interrupt */ + gpiob_b91_irq_en_set(dev, pin); + + /* Enable PLIC interrupt */ + riscv_plic_irq_enable(irq_num); + riscv_plic_set_priority(irq_num, irq_prioriy); +} + +/* Set pin's pull-up/down resistor */ +static void gpio_b91_up_down_res_set(volatile struct gpio_b91_t *gpio, + gpio_pin_t pin, + uint8_t up_down_res) +{ + uint8_t val; + uint8_t mask; + uint8_t analog_reg; + + pin = BIT(pin); + val = up_down_res & 0x03; + analog_reg = 0x0e + (GET_PORT_NUM(gpio) << 1) + ((pin & 0xf0) ? 1 : 0); + + if (pin & 0x11) { + val = val << 0; + mask = 0xfc; + } else if (pin & 0x22) { + val = val << 2; + mask = 0xf3; + } else if (pin & 0x44) { + val = val << 4; + mask = 0xcf; + } else if (pin & 0x88) { + val = val << 6; + mask = 0x3f; + } else { + return; + } + + analog_write_reg8(analog_reg, (analog_read_reg8(analog_reg) & mask) | val); +} + +/* Config Pin pull-up / pull-down resistors */ +static void gpio_b91_config_up_down_res(volatile struct gpio_b91_t *gpio, + gpio_pin_t pin, + gpio_flags_t flags) +{ + if ((flags & GPIO_PULL_UP) != 0) { + gpio_b91_up_down_res_set(gpio, pin, GPIO_PIN_PULLUP_10K); + } else if ((flags & GPIO_PULL_DOWN) != 0) { + gpio_b91_up_down_res_set(gpio, pin, GPIO_PIN_PULLDOWN_100K); + } else { + gpio_b91_up_down_res_set(gpio, pin, GPIO_PIN_UP_DOWN_FLOAT); + } +} + +/* Config Pin In/Out direction */ +static void gpio_b91_config_in_out(volatile struct gpio_b91_t *gpio, + gpio_pin_t pin, + gpio_flags_t flags) +{ + uint8_t ie_addr = 0; + + /* Port C and D Input Enable registers are located in another place: analog */ + if (IS_PORT_C(gpio)) { + ie_addr = areg_gpio_pc_ie; + } else if (IS_PORT_D(gpio)) { + ie_addr = areg_gpio_pd_ie; + } + + /* Enable/disable output */ + WRITE_BIT(gpio->oen, pin, ~flags & GPIO_OUTPUT); + + /* Enable/disable input */ + if (ie_addr != 0) { + /* Port C and D are located in analog space */ + if (flags & GPIO_INPUT) { + analog_write_reg8(ie_addr, analog_read_reg8(ie_addr) | BIT(pin)); + } else { + analog_write_reg8(ie_addr, analog_read_reg8(ie_addr) & (~BIT(pin))); + } + } else { + /* Input Enable registers of all other ports are located in common GPIO space */ + WRITE_BIT(gpio->ie, pin, flags & GPIO_INPUT); + } +} + +/* GPIO driver initialization */ +static int gpio_b91_init(const struct device *dev) +{ + const struct gpio_b91_config *cfg = GET_CFG(dev); + + cfg->pirq_connect(); + + return 0; +} + +/* API implementation: pin_configure */ +static int gpio_b91_pin_configure(const struct device *dev, + gpio_pin_t pin, + gpio_flags_t flags) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + /* Check input parameters: pin number */ + if (pin > PIN_NUM_MAX) { + return -ENOTSUP; + } + + /* Check input parameters: open-source and open-drain */ + if ((flags & GPIO_SINGLE_ENDED) != 0) { + return -ENOTSUP; + } + + /* Check input parameters: simultaneous in/out mode */ + if ((flags & GPIO_OUTPUT) && (flags & GPIO_INPUT)) { + return -ENOTSUP; + } + + /* Strengths not implemented */ + if ((flags & (GPIO_DS_ALT_LOW | GPIO_DS_ALT_HIGH)) != 0) { + return -ENOTSUP; + } + + /* Set GPIO init state if defined to avoid glitches */ + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { + gpio->output |= BIT(pin); + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { + gpio->output &= ~BIT(pin); + } + + /* GPIO function enable */ + WRITE_BIT(gpio->actas_gpio, BIT(pin), 1); + + /* Set GPIO pull-up / pull-down resistors */ + gpio_b91_config_up_down_res(gpio, pin, flags); + + /* Enable/disable input/output */ + gpio_b91_config_in_out(gpio, pin, flags); + + return 0; +} + +/* API implementation: port_get_raw */ +static int gpio_b91_port_get_raw(const struct device *dev, + gpio_port_value_t *value) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + *value = gpio->input; + + return 0; +} + +/* API implementation: port_set_masked_raw */ +static int gpio_b91_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + gpio->output = (gpio->output & ~mask) | (value & mask); + + return 0; +} + +/* API implementation: port_set_bits_raw */ +static int gpio_b91_port_set_bits_raw(const struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + gpio->output |= mask; + + return 0; +} + +/* API implementation: port_clear_bits_raw */ +static int gpio_b91_port_clear_bits_raw(const struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + gpio->output &= ~mask; + + return 0; +} + +/* API implementation: port_toggle_bits */ +static int gpio_b91_port_toggle_bits(const struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_b91_t *gpio = GET_GPIO(dev); + + gpio->output ^= mask; + + return 0; +} + +/* API implementation: interrupts handler */ +#if IS_INST_IRQ_EN(0) || IS_INST_IRQ_EN(1) || IS_INST_IRQ_EN(2) || \ + IS_INST_IRQ_EN(3) || IS_INST_IRQ_EN(4) +static void gpio_b91_irq_handler(const struct device *dev) +{ + struct gpio_b91_data *data = dev->data; + uint8_t irq = GET_IRQ_NUM(dev); + uint8_t status = gpio_b91_irq_en_get(dev); + + gpio_b91_irq_status_clr(irq); + gpio_fire_callbacks(&data->callbacks, dev, status); +} +#endif + +/* API implementation: pin_interrupt_configure */ +static int gpio_b91_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + int ret_status = 0; + + switch (mode) { + case GPIO_INT_MODE_DISABLED: /* GPIO interrupt disable */ + gpiob_b91_irq_en_clr(dev, pin); + break; + + case GPIO_INT_MODE_LEVEL: + if (trig == GPIO_INT_TRIG_HIGH) { /* GPIO interrupt High level */ + gpio_b91_irq_set(dev, pin, INTR_HIGH_LEVEL); + } else if (trig == GPIO_INT_TRIG_LOW) { /* GPIO interrupt Low level */ + gpio_b91_irq_set(dev, pin, INTR_LOW_LEVEL); + } else { + ret_status = -ENOTSUP; + } + break; + + case GPIO_INT_MODE_EDGE: + if (trig == GPIO_INT_TRIG_HIGH) { /* GPIO interrupt Rising edge */ + gpio_b91_irq_set(dev, pin, INTR_RISING_EDGE); + } else if (trig == GPIO_INT_TRIG_LOW) { /* GPIO interrupt Falling edge */ + gpio_b91_irq_set(dev, pin, INTR_FALLING_EDGE); + } else { + ret_status = -ENOTSUP; + } + break; + + default: + ret_status = -ENOTSUP; + break; + } + + return ret_status; +} + +/* API implementation: manage_callback */ +static int gpio_b91_manage_callback(const struct device *dev, + struct gpio_callback *callback, + bool set) +{ + struct gpio_b91_data *data = dev->data; + + return gpio_manage_callback(&data->callbacks, callback, set); +} + +/* GPIO driver APIs structure */ +static const struct gpio_driver_api gpio_b91_driver_api = { + .pin_configure = gpio_b91_pin_configure, + .port_get_raw = gpio_b91_port_get_raw, + .port_set_masked_raw = gpio_b91_port_set_masked_raw, + .port_set_bits_raw = gpio_b91_port_set_bits_raw, + .port_clear_bits_raw = gpio_b91_port_clear_bits_raw, + .port_toggle_bits = gpio_b91_port_toggle_bits, + .pin_interrupt_configure = gpio_b91_pin_interrupt_configure, + .manage_callback = gpio_b91_manage_callback +}; + +/* If instance 0 is present and has interrupt enabled, connect IRQ */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 0 +static void gpio_b91_irq_connect_0(void) +{ + #if IS_INST_IRQ_EN(0) + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), + gpio_b91_irq_handler, + DEVICE_DT_INST_GET(0), 0); + #endif +} +#endif + +/* If instance 1 is present and has interrupt enabled, connect IRQ */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1 +static void gpio_b91_irq_connect_1(void) +{ + #if IS_INST_IRQ_EN(1) + IRQ_CONNECT(DT_INST_IRQN(1), DT_INST_IRQ(1, priority), + gpio_b91_irq_handler, + DEVICE_DT_INST_GET(1), 0); + #endif +} +#endif + +/* If instance 2 is present and has interrupt enabled, connect IRQ */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 2 +static void gpio_b91_irq_connect_2(void) +{ + #if IS_INST_IRQ_EN(2) + IRQ_CONNECT(DT_INST_IRQN(2), DT_INST_IRQ(2, priority), + gpio_b91_irq_handler, + DEVICE_DT_INST_GET(2), 0); + #endif +} +#endif + +/* If instance 3 is present and has interrupt enabled, connect IRQ */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 3 +static void gpio_b91_irq_connect_3(void) +{ + #if IS_INST_IRQ_EN(3) + IRQ_CONNECT(DT_INST_IRQN(3), DT_INST_IRQ(3, priority), + gpio_b91_irq_handler, + DEVICE_DT_INST_GET(3), 0); + #endif +} +#endif + +/* If instance 4 is present and has interrupt enabled, connect IRQ */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 4 +static void gpio_b91_irq_connect_4(void) +{ + #if IS_INST_IRQ_EN(4) + IRQ_CONNECT(DT_INST_IRQN(4), DT_INST_IRQ(4, priority), + gpio_b91_irq_handler, + DEVICE_DT_INST_GET(4), 0); + #endif +} +#endif + +/* GPIO driver registration */ +#define GPIO_B91_INIT(n) \ + static const struct gpio_b91_config gpio_b91_config_##n = { \ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n) \ + }, \ + .gpio_base = DT_INST_REG_ADDR(n), \ + .irq_num = DT_INST_IRQN(n), \ + .irq_priority = DT_INST_IRQ(n, priority), \ + .pirq_connect = gpio_b91_irq_connect_##n \ + }; \ + static struct gpio_b91_data gpio_b91_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, gpio_b91_init, \ + NULL, \ + &gpio_b91_data_##n, \ + &gpio_b91_config_##n, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &gpio_b91_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_B91_INIT) diff --git a/drivers/pinmux/CMakeLists.txt b/drivers/pinmux/CMakeLists.txt index 55fd58d73cb0e..7961fca8f4299 100644 --- a/drivers/pinmux/CMakeLists.txt +++ b/drivers/pinmux/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Board initialization +zephyr_sources_ifdef(CONFIG_PINMUX_TELINK_B91 pinmux_b91.c) zephyr_sources_ifdef(CONFIG_PINMUX_CC13XX_CC26XX pinmux_cc13xx_cc26xx.c) zephyr_sources_ifdef(CONFIG_PINMUX_ESP32 pinmux_esp32.c) zephyr_sources_ifdef(CONFIG_PINMUX_HSDK pinmux_hsdk.c) diff --git a/drivers/pinmux/Kconfig b/drivers/pinmux/Kconfig index 1572fd92b24ba..668e9fbc8f152 100644 --- a/drivers/pinmux/Kconfig +++ b/drivers/pinmux/Kconfig @@ -24,6 +24,8 @@ config PINMUX_INIT_PRIORITY rule for particular boards. Don't change this value unless you know what you are doing. +source "drivers/pinmux/Kconfig.b91" + source "drivers/pinmux/Kconfig.beetle" source "drivers/pinmux/Kconfig.cc13xx_cc26xx" diff --git a/drivers/pinmux/Kconfig.b91 b/drivers/pinmux/Kconfig.b91 new file mode 100644 index 0000000000000..cb257986cae13 --- /dev/null +++ b/drivers/pinmux/Kconfig.b91 @@ -0,0 +1,9 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config PINMUX_TELINK_B91 + bool "Telink Semiconductor B91 pinmux driver" + depends on SOC_RISCV_TELINK_B91 + default y + help + Enables Telink B91 pinmux driver. diff --git a/drivers/pinmux/pinmux_b91.c b/drivers/pinmux/pinmux_b91.c new file mode 100644 index 0000000000000..70912b32db979 --- /dev/null +++ b/drivers/pinmux/pinmux_b91.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT telink_b91_pinmux + +#include "analog.h" +#include + + +/** + * GPIO Function Enable Register + * ADDR PINS + * gpio_en: PORT_A[0-7] + * gpio_en + 1*8: PORT_B[0-7] + * gpio_en + 2*8: PORT_C[0-7] + * gpio_en + 3*8: PORT_D[0-7] + * gpio_en + 4*8: PORT_E[0-7] + * gpio_en + 5*8: PORT_F[0-7] + */ +#define reg_gpio_en(pin) (*(volatile uint8_t *)((uint32_t)DT_INST_REG_ADDR_BY_NAME(0, gpio_en) + \ + ((pin >> 8) * 8))) + +/** + * Function Multiplexer Register + * ADDR PINS + * pin_mux: PORT_A[0-3] + * pin_mux + 1: PORT_A[4-7] + * pin_mux + 2: PORT_B[0-3] + * pin_mux + 3: PORT_B[4-7] + * pin_mux + 4: PORT_C[0-3] + * pin_mux + 5: PORT_C[4-7] + * pin_mux + 6: PORT_D[0-3] + * pin_mux + 7: PORT_D[4-7] + * pin_mux + 0x20: PORT_E[0-3] + * pin_mux + 0x21: PORT_E[4-7] + * pin_mux + 0x26: PORT_F[0-3] + * pin_mux + 0x27: PORT_F[4-7] + */ +#define reg_pin_mux(pin) (*(volatile uint8_t *)((uint32_t)DT_INST_REG_ADDR_BY_NAME(0, pin_mux) + \ + (((pin >> 8) < 4) ? ((pin >> 8) * 2) : 0) + \ + (((pin >> 8) == 4) ? 0x20 : 0) + \ + (((pin >> 8) == 5) ? 0x26 : 0) + \ + ((pin & 0x0f0) ? 1 : 0))) + +/** + * Pull Up resistors enable + * ADDR PINS + * pull_up_en: PORT_A[0-3] + * pull_up_en + 1: PORT_A[4-7] + * pull_up_en + 2: PORT_B[0-3] + * pull_up_en + 3: PORT_B[4-7] + * pull_up_en + 4: PORT_C[0-3] + * pull_up_en + 5: PORT_C[4-7] + * pull_up_en + 6: PORT_D[0-3] + * pull_up_en + 7: PORT_D[4-7] + * pull_up_en + 8: PORT_E[0-3] + * pull_up_en + 9: PORT_E[4-7] + * pull_up_en + 10: PORT_F[0-3] + * pull_up_en + 11: PORT_F[4-7] + */ +#define reg_pull_up_en(pin) ((uint8_t)(DT_INST_REG_ADDR_BY_NAME(0, pull_up_en) + \ + ((pin >> 8) * 2) + \ + ((pin & 0xf0) ? 1 : 0))) + +/* GPIO Pull-Up options */ +#define PINMUX_B91_PULLUP_DISABLE ((uint8_t)0u) +#define PINMUX_B91_PULLUP_10K ((uint8_t)3u) + +/* Get PinMux configuration */ +#define GET_CFG(dev) ((const struct pinmux_b91_config *)dev->config) + + +/* B91 PinMux config structure */ +struct pinmux_b91_config { + uint8_t pad_mul_sel; +}; + + +/* Act as GPIO function disable */ +static inline void pinmux_b91_gpio_function_disable(uint32_t pin) +{ + uint8_t bit = pin & 0xff; + + reg_gpio_en(pin) &= ~bit; +} + +/* Get function value bits start position (offset) */ +static inline int pinmux_b91_get_func_offset(uint32_t pin, uint8_t *offset) +{ + switch ((pin & 0x0fu) != 0u ? pin & 0x0fu : (pin & 0xf0u) >> 4u) { + case BIT(0): + *offset = 0u; + break; + case BIT(1): + *offset = 2u; + break; + case BIT(2): + *offset = 4u; + break; + case BIT(3): + *offset = 6u; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* Set pin's pull-up/down resistor */ +static void pinmux_b91_set_pull_up(uint32_t pin, uint8_t val) +{ + uint8_t mask = 0; + uint8_t analog_reg = reg_pull_up_en(pin); + + if (pin & 0x11) { + val = val; + mask = 0xfc; + } else if (pin & 0x22) { + val = val << 2; + mask = 0xf3; + } else if (pin & 0x44) { + val = val << 4; + mask = 0xcf; + } else if (pin & 0x88) { + val = val << 6; + mask = 0x3f; + } else { + return; + } + + analog_write_reg8(analog_reg, (analog_read_reg8(analog_reg) & mask) | val); +} + +/* API implementation: init */ +static int pinmux_b91_init(const struct device *dev) +{ + const struct pinmux_b91_config *cfg = GET_CFG(dev); + + reg_gpio_pad_mul_sel |= cfg->pad_mul_sel; + + return 0; +} + +/* API implementation: set */ +static int pinmux_b91_set(const struct device *dev, uint32_t pin, uint32_t func) +{ + ARG_UNUSED(dev); + + uint8_t mask = 0; + uint8_t offset = 0; + int32_t status = 0; + + /* calculate offset and mask for the func value */ + status = pinmux_b91_get_func_offset(pin, &offset); + if (status != 0) { + return status; + } + mask = (uint8_t)~(BIT(offset) | BIT(offset + 1)); + + /* disable GPIO function (can be enabled back by GPIO init using GPIO driver) */ + pinmux_b91_gpio_function_disable(pin); + + /* set func value */ + reg_pin_mux(pin) = (reg_pin_mux(pin) & mask) | (func << offset); + + return status; +} + +/* API implementation: get */ +static int pinmux_b91_get(const struct device *dev, uint32_t pin, uint32_t *func) +{ + ARG_UNUSED(dev); + + uint8_t mask = 0u; + uint8_t offset = 0; + int32_t status = 0; + + /* calculate offset and mask for the func value */ + status = pinmux_b91_get_func_offset(pin, &offset); + if (status != 0) { + return status; + } + mask = (uint8_t)(BIT(offset) | BIT(offset + 1)); + + /* get func value */ + *func = (reg_pin_mux(pin) & mask) >> offset; + + return status; +} + +/* API implementation: pullup */ +static int pinmux_b91_pullup(const struct device *dev, uint32_t pin, uint8_t func) +{ + ARG_UNUSED(dev); + + switch (func) { + case PINMUX_PULLUP_ENABLE: + pinmux_b91_set_pull_up(pin, PINMUX_B91_PULLUP_10K); + break; + + case PINMUX_PULLUP_DISABLE: + pinmux_b91_set_pull_up(pin, PINMUX_B91_PULLUP_DISABLE); + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +/* API implementation: input */ +static int pinmux_b91_input(const struct device *dev, uint32_t pin, uint8_t func) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pin); + ARG_UNUSED(func); + + /* Implemented by GPIO driver */ + + return -ENOTSUP; +} + +/* PinMux driver APIs structure */ +static const struct pinmux_driver_api pinmux_b91_api = { + .set = pinmux_b91_set, + .get = pinmux_b91_get, + .pullup = pinmux_b91_pullup, + .input = pinmux_b91_input, +}; + +static const struct pinmux_b91_config pinmux_b91_cfg = { + .pad_mul_sel = DT_INST_PROP(0, pad_mul_sel) +}; + +/* PinMux driver registration */ +DEVICE_DT_INST_DEFINE(0, pinmux_b91_init, + NULL, NULL, &pinmux_b91_cfg, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &pinmux_b91_api); diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 51f792d61aed8..d47d8badb38ef 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_UART_ALTERA_JTAG uart_altera_jtag_hal.c) +zephyr_library_sources_ifdef(CONFIG_UART_TELINK_B91 uart_b91.c) zephyr_library_sources_ifdef(CONFIG_UART_IMX uart_imx.c) zephyr_library_sources_ifdef(CONFIG_UART_CC13XX_CC26XX uart_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_UART_CC32XX uart_cc32xx.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index dc6b1640e2f81..a598ebec124c6 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -79,6 +79,8 @@ config UART_DRV_CMD comment "Serial Drivers" +source "drivers/serial/Kconfig.b91" + source "drivers/serial/Kconfig.ns16550" source "drivers/serial/Kconfig.mcux" diff --git a/drivers/serial/Kconfig.b91 b/drivers/serial/Kconfig.b91 new file mode 100644 index 0000000000000..298aa2be6fcab --- /dev/null +++ b/drivers/serial/Kconfig.b91 @@ -0,0 +1,12 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +# Telink B91 UART configuration options + +config UART_TELINK_B91 + bool "Telink B91 UART driver" + depends on SOC_RISCV_TELINK_B91 + select SERIAL_HAS_DRIVER + select SERIAL_SUPPORT_INTERRUPT + help + This option enables the B91 serial driver. diff --git a/drivers/serial/uart_b91.c b/drivers/serial/uart_b91.c new file mode 100644 index 0000000000000..82b45f53b3ca1 --- /dev/null +++ b/drivers/serial/uart_b91.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "analog.h" +#include "clock.h" + +#include +#include +#include +#include + + +/* Driver dts compatibility: telink,b91_uart */ +#define DT_DRV_COMPAT telink_b91_uart + +/* Get UART instance */ +#define GET_UART(dev) ((volatile struct uart_b91_t *) \ + ((const struct uart_b91_config *)dev->config)->uart_addr) + +/* Get UART configuration */ +#define GET_CFG(dev) ((const struct uart_b91_config *)dev->config) + +/* Get instance data */ +#define DEV_DATA(dev) ((struct uart_b91_data *const)dev->data) + +/* UART TX buffer count max value */ +#define UART_TX_BUF_CNT ((uint8_t)8u) + +/* Parity type */ +#define UART_PARITY_NONE ((uint8_t)0u) +#define UART_PARITY_EVEN ((uint8_t)1u) +#define UART_PARITY_ODD ((uint8_t)2u) + +/* Stop bits length */ +#define UART_STOP_BIT_1 ((uint8_t)0u) +#define UART_STOP_BIT_1P5 BIT(4) +#define UART_STOP_BIT_2 BIT(5) + + +/* B91 UART registers structure */ +struct uart_b91_t { + uint8_t data_buf[4]; + uint16_t clk_div; + uint8_t ctrl0; + uint8_t ctrl1; + uint8_t ctrl2; + uint8_t ctrl3; + uint16_t rxtimeout; + uint8_t bufcnt; + uint8_t status; + uint8_t txrx_status; + uint8_t state; +}; + +/* B91 UART data structure */ +struct uart_b91_data { + uint8_t tx_byte_index; + uint8_t rx_byte_index; + struct uart_config cfg; +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_user_data_t callback; + void *cb_data; +#endif +}; + +/* B91 UART config structure */ +struct uart_b91_config { + const uint32_t *pinctrl_list; + size_t pinctrl_list_size; + uint32_t uart_addr; + uint32_t baud_rate; + void (*pirq_connect)(void); +}; + +/* rxtimeout register enums */ +enum { + UART_ERR_IRQ_MASK = BIT(15), +}; + +/* ctrl0 register enums */ +enum { + UART_RX_IRQ_MASK = BIT(6), + UART_TX_IRQ_MASK = BIT(7), +}; + +/* ctrl3 register enums */ +enum { + FLD_UART_RX_IRQ_TRIQ_LEV_OFFSET = 0, + FLD_UART_TX_IRQ_TRIQ_LEV_OFFSET = 4, +}; + +/* bufcnt register enums */ +enum { + FLD_UART_RX_BUF_CNT_OFFSET = 0, + FLD_UART_TX_BUF_CNT_OFFSET = 4, +}; + +/* status register enums */ +enum { + UART_IRQ_STATUS = BIT(3), + UART_RX_ERR_STATUS = BIT(7), +}; + + +/* Get tx fifo count */ +static inline uint8_t uart_b91_get_tx_bufcnt(volatile struct uart_b91_t *uart) +{ + return (uart->bufcnt & FLD_UART_TX_BUF_CNT) >> FLD_UART_TX_BUF_CNT_OFFSET; +} + +/* Get rx fifo count */ +static inline uint8_t uart_b91_get_rx_bufcnt(volatile struct uart_b91_t *uart) +{ + return (uart->bufcnt & FLD_UART_RX_BUF_CNT) >> FLD_UART_RX_BUF_CNT_OFFSET; +} + +/* Check for prime */ +static uint8_t uart_b91_is_prime(uint32_t n) +{ + uint32_t i = 5; + + if (n <= 3) { + return 1; + } else if ((n % 2 == 0) || (n % 3 == 0)) { + return 0; + } + + for (i = 5; i * i < n; i += 6) { + if ((n % i == 0) || (n % (i + 2)) == 0) { + return 0; + } + } + + return 1; +} + +/* Calculate the best bit width */ +static void uart_b91_cal_div_and_bwpc(uint32_t baudrate, uint32_t pclk, + uint16_t *divider, uint8_t *bwpc) +{ + uint8_t i = 0, j = 0; + uint32_t primeInt = 0; + uint8_t primeDec = 0; + uint32_t D_intdec[13], D_int[13]; + uint8_t D_dec[13]; + + primeInt = pclk / baudrate; + primeDec = 10 * pclk / baudrate - 10 * primeInt; + + if (uart_b91_is_prime(primeInt)) { + primeInt += 1; + } else if (primeDec > 5) { + primeInt += 1; + if (uart_b91_is_prime(primeInt)) { + primeInt -= 1; + } + } + + for (i = 3; i <= 15; i++) { + D_intdec[i - 3] = (10 * primeInt) / (i + 1); + D_dec[i - 3] = D_intdec[i - 3] - 10 * (D_intdec[i - 3] / 10); + D_int[i - 3] = D_intdec[i - 3] / 10; + } + + /* find the max and min one decimation point */ + uint8_t position_min = 0, position_max = 0; + uint32_t min = 0xffffffff, max = 0x00; + + for (j = 0; j < 13; j++) { + if ((D_dec[j] <= min) && (D_int[j] != 0x01)) { + min = D_dec[j]; + position_min = j; + } + if (D_dec[j] >= max) { + max = D_dec[j]; + position_max = j; + } + } + + if ((D_dec[position_min] < 5) && (D_dec[position_max] >= 5)) { + if (D_dec[position_min] < (10 - D_dec[position_max])) { + *bwpc = position_min + 3; + *divider = D_int[position_min] - 1; + } else { + *bwpc = position_max + 3; + *divider = D_int[position_max]; + } + } else if ((D_dec[position_min] < 5) && (D_dec[position_max] < 5)) { + *bwpc = position_min + 3; + *divider = D_int[position_min] - 1; + } else { + *bwpc = position_max + 3; + *divider = D_int[position_max]; + } +} + +/* Initializes the UART instance */ +static void uart_b91_init(volatile struct uart_b91_t *uart, uint16_t divider, + uint8_t bwpc, uint8_t parity, uint8_t stop_bit) +{ + /* config clock */ + divider = divider | FLD_UART_CLK_DIV_EN; + uart->ctrl0 = bwpc; + uart->clk_div = divider; + + /* config parity */ + if (parity) { + /* enable parity function */ + uart->ctrl1 |= FLD_UART_PARITY_ENABLE; + + if (parity == UART_PARITY_EVEN) { + /* enable even parity */ + uart->ctrl1 &= (~FLD_UART_PARITY_POLARITY); + } else if (parity == UART_PARITY_ODD) { + /* enable odd parity */ + uart->ctrl1 |= FLD_UART_PARITY_POLARITY; + } + } else { + uart->ctrl1 &= (~FLD_UART_PARITY_ENABLE); /* disable parity function */ + } + + /* stop bit config */ + uart->ctrl1 &= (~FLD_UART_STOP_SEL); + uart->ctrl1 |= stop_bit; +} + +/* API implementation: irq handler */ +static void uart_b91_irq_handler(const struct device *dev) +{ +#ifndef CONFIG_UART_INTERRUPT_DRIVEN + ARG_UNUSED(dev); +#else + struct uart_b91_data *data = DEV_DATA(dev); + + if (data->callback != NULL) { + data->callback(dev, data->cb_data); + } +#endif +} + +/* API implementation: configure */ +static int uart_b91_configure(const struct device *dev, + const struct uart_config *cfg) +{ + uint16_t divider; + uint8_t bwpc; + uint8_t parity; + uint8_t stop_bits; + + volatile struct uart_b91_t *uart = GET_UART(dev); + + /* check parity */ + if (cfg->parity == UART_CFG_PARITY_NONE) { + parity = UART_PARITY_NONE; + } else if (cfg->parity == UART_CFG_PARITY_ODD) { + parity = UART_PARITY_ODD; + } else if (cfg->parity == UART_CFG_PARITY_EVEN) { + parity = UART_PARITY_EVEN; + } else { + return -ENOTSUP; + } + + /* check stop bits */ + if (cfg->stop_bits == UART_CFG_STOP_BITS_1) { + stop_bits = UART_STOP_BIT_1; + } else if (cfg->stop_bits == UART_CFG_STOP_BITS_1_5) { + stop_bits = UART_STOP_BIT_1P5; + } else if (cfg->stop_bits == UART_CFG_STOP_BITS_2) { + stop_bits = UART_STOP_BIT_2; + } else { + return -ENOTSUP; + } + + /* check flow control */ + if (cfg->flow_ctrl != UART_CFG_FLOW_CTRL_NONE) { + return -ENOTSUP; + } + + /* UART configure */ + uart_b91_cal_div_and_bwpc(cfg->baudrate, sys_clk.pclk * 1000 * 1000, ÷r, &bwpc); + uart_b91_init(uart, divider, bwpc, parity, stop_bits); + + /* save configuration */ + DEV_DATA(dev)->cfg = *cfg; + + return 0; +} + +/* API implementation: config_get */ +static int uart_b91_config_get(const struct device *dev, + struct uart_config *cfg) +{ + *cfg = DEV_DATA(dev)->cfg; + + return 0; +} + +/* API implementation: driver initialization */ +static int uart_b91_driver_init(const struct device *dev) +{ + uint16_t divider = 0u; + uint8_t bwpc = 0u; + const struct device *pinmux; + volatile struct uart_b91_t *uart = GET_UART(dev); + const struct uart_b91_config *cfg = GET_CFG(dev); + + pinmux = DEVICE_DT_GET(DT_NODELABEL(pinmux)); + if (!device_is_ready(pinmux)) { + return -ENODEV; + } + + for (int i = 0; i < cfg->pinctrl_list_size; i++) { + pinmux_pin_set(pinmux, B91_PINMUX_GET_PIN(cfg->pinctrl_list[i]), + B91_PINMUX_GET_FUNC(cfg->pinctrl_list[i])); + } + + uart_b91_cal_div_and_bwpc(cfg->baud_rate, sys_clk.pclk * 1000 * 1000, ÷r, &bwpc); + uart_b91_init(uart, divider, bwpc, UART_PARITY_NONE, UART_STOP_BIT_1); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + cfg->pirq_connect(); +#endif + + return 0; +} + +/* API implementation: poll_out */ +static void uart_b91_poll_out(const struct device *dev, uint8_t c) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + struct uart_b91_data *data = DEV_DATA(dev); + + while (uart_b91_get_tx_bufcnt(uart) >= UART_TX_BUF_CNT) { + }; + + uart->data_buf[data->tx_byte_index] = c; + data->tx_byte_index = (data->tx_byte_index + 1) % ARRAY_SIZE(uart->data_buf); +} + +/* API implementation: poll_in */ +static int uart_b91_poll_in(const struct device *dev, unsigned char *c) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + struct uart_b91_data *data = DEV_DATA(dev); + + if (uart_b91_get_rx_bufcnt(uart) == 0) { + return -1; + } + + *c = uart->data_buf[data->rx_byte_index]; + data->rx_byte_index = (data->rx_byte_index + 1) % ARRAY_SIZE(uart->data_buf); + + return 0; +} + +/* API implementation: err_check */ +static int uart_b91_err_check(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return ((uart->status & UART_RX_ERR_STATUS) != 0) ? 1 : 0; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +/* API implementation: fifo_fill */ +static int uart_b91_fifo_fill(const struct device *dev, + const uint8_t *tx_data, + int size) +{ + int i = 0; + + for (i = 0; i < size; i++) { + uart_b91_poll_out(dev, tx_data[i]); + } + + return i; +} + +/* API implementation: fifo_read */ +static int uart_b91_fifo_read(const struct device *dev, + uint8_t *rx_data, + const int size) +{ + int rx_count; + volatile struct uart_b91_t *uart = GET_UART(dev); + + for (rx_count = 0; rx_count < size; rx_count++) { + if (uart_b91_get_rx_bufcnt(uart) == 0) { + break; + } + + uart_b91_poll_in(dev, &rx_data[rx_count]); + } + + return rx_count; +} + +/* API implementation: irq_tx_enable */ +static void uart_b91_irq_tx_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl3 = (uart->ctrl3 & (~FLD_UART_TX_IRQ_TRIQ_LEV)) | + BIT(FLD_UART_TX_IRQ_TRIQ_LEV_OFFSET); + uart->ctrl0 |= UART_TX_IRQ_MASK; +} + +/* API implementation: irq_tx_disable */ +static void uart_b91_irq_tx_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl0 &= ~UART_TX_IRQ_MASK; +} + +/* API implementation: irq_tx_ready */ +static int uart_b91_irq_tx_ready(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_tx_bufcnt(uart) < UART_TX_BUF_CNT) ? 1 : 0; +} + +/* API implementation: irq_tx_complete */ +static int uart_b91_irq_tx_complete(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_tx_bufcnt(uart) == 0) ? 1 : 0; +} + +/* API implementation: irq_rx_enable */ +static void uart_b91_irq_rx_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl3 = (uart->ctrl3 & (~FLD_UART_RX_IRQ_TRIQ_LEV)) | + BIT(FLD_UART_RX_IRQ_TRIQ_LEV_OFFSET); + uart->ctrl0 |= UART_RX_IRQ_MASK; +} + +/* API implementation: irq_rx_disable */ +static void uart_b91_irq_rx_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->ctrl0 &= ~UART_RX_IRQ_MASK; +} + +/* API implementation: irq_rx_ready */ +static int uart_b91_irq_rx_ready(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return (uart_b91_get_rx_bufcnt(uart) > 0) ? 1 : 0; +} + +/* API implementation: irq_err_enable */ +static void uart_b91_irq_err_enable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->rxtimeout |= UART_ERR_IRQ_MASK; +} + +/* API implementation: irq_err_disable*/ +static void uart_b91_irq_err_disable(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + uart->rxtimeout &= ~UART_ERR_IRQ_MASK; +} + +/* API implementation: irq_is_pending */ +static int uart_b91_irq_is_pending(const struct device *dev) +{ + volatile struct uart_b91_t *uart = GET_UART(dev); + + return ((uart->status & UART_IRQ_STATUS) != 0) ? 1 : 0; +} + +/* API implementation: irq_update */ +static int uart_b91_irq_update(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* nothing to be done */ + return 1; +} + +/* API implementation: irq_callback_set */ +static void uart_b91_irq_callback_set(const struct device *dev, + uart_irq_callback_user_data_t cb, + void *cb_data) +{ + struct uart_b91_data *data = DEV_DATA(dev); + + data->callback = cb; + data->cb_data = cb_data; +} + +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api uart_b91_driver_api = { + .poll_in = uart_b91_poll_in, + .poll_out = uart_b91_poll_out, + .err_check = uart_b91_err_check, + .configure = uart_b91_configure, + .config_get = uart_b91_config_get, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_b91_fifo_fill, + .fifo_read = uart_b91_fifo_read, + .irq_tx_enable = uart_b91_irq_tx_enable, + .irq_tx_disable = uart_b91_irq_tx_disable, + .irq_tx_ready = uart_b91_irq_tx_ready, + .irq_tx_complete = uart_b91_irq_tx_complete, + .irq_rx_enable = uart_b91_irq_rx_enable, + .irq_rx_disable = uart_b91_irq_rx_disable, + .irq_rx_ready = uart_b91_irq_rx_ready, + .irq_err_enable = uart_b91_irq_err_enable, + .irq_err_disable = uart_b91_irq_err_disable, + .irq_is_pending = uart_b91_irq_is_pending, + .irq_update = uart_b91_irq_update, + .irq_callback_set = uart_b91_irq_callback_set, +#endif +}; + + +#define UART_B91_INIT(n) \ + \ + static void uart_b91_irq_connect_##n(void); \ + \ + static const uint32_t uart_pins_##n[] = \ + B91_PINMUX_DT_INST_GET_ARRAY(n, 0); \ + \ + static const struct uart_b91_config uart_b91_cfg_##n = \ + { \ + .uart_addr = DT_INST_REG_ADDR(n), \ + .baud_rate = DT_INST_PROP(n, current_speed), \ + .pinctrl_list_size = ARRAY_SIZE(uart_pins_##n), \ + .pinctrl_list = uart_pins_##n, \ + .pirq_connect = uart_b91_irq_connect_##n \ + }; \ + \ + static struct uart_b91_data uart_b91_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, uart_b91_driver_init, \ + NULL, \ + &uart_b91_data_##n, \ + &uart_b91_cfg_##n, \ + PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + (void *)&uart_b91_driver_api); \ + \ + static void uart_b91_irq_connect_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + uart_b91_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + riscv_plic_irq_enable(DT_INST_IRQN(n)); \ + riscv_plic_set_priority(DT_INST_IRQN(n), DT_INST_IRQ(n, priority)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(UART_B91_INIT) diff --git a/dts/bindings/cpu/telink,b91.yaml b/dts/bindings/cpu/telink,b91.yaml new file mode 100644 index 0000000000000..f70640234df77 --- /dev/null +++ b/dts/bindings/cpu/telink,b91.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +description: Telink RISC-V CPU + +compatible: "telink,b91" + +include: cpu.yaml diff --git a/dts/bindings/gpio/telink,b91-gpio.yaml b/dts/bindings/gpio/telink,b91-gpio.yaml new file mode 100644 index 0000000000000..902be6ebb7333 --- /dev/null +++ b/dts/bindings/gpio/telink,b91-gpio.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2021, Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +description: Telink B91 GPIO node + +compatible: "telink,b91-gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + label: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/pinctrl/telink,b91-pinmux.yaml b/dts/bindings/pinctrl/telink,b91-pinmux.yaml new file mode 100644 index 0000000000000..68419f09f3627 --- /dev/null +++ b/dts/bindings/pinctrl/telink,b91-pinmux.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2021, Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +description: Telink B91 PinMux + +compatible: "telink,b91-pinmux" + +include: base.yaml + +properties: + reg: + required: true + + label: + required: true + + pad-mul-sel: + type: int + required: true + description: PinMux pad_mul_sel register values. + +child-binding: + description: + This binding gives a base representation of the Telink B91 pins configration. + + properties: + pinmux: + required: true + type: int diff --git a/dts/bindings/power/telink,b91-power.yaml b/dts/bindings/power/telink,b91-power.yaml new file mode 100644 index 0000000000000..7132f9d027276 --- /dev/null +++ b/dts/bindings/power/telink,b91-power.yaml @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +description: Telink B91 power control node + +compatible: "telink,b91-power" + +include: base.yaml + +properties: + reg: + required: true + + power-mode: + type: string + required: true + enum: + - "LDO_1P4_LDO_1P8" + - "DCDC_1P4_LDO_1P8" + - "DCDC_1P4_DCDC_1P8" + + vbat-type: + type: string + required: true + enum: + - "VBAT_MAX_VALUE_LESS_THAN_3V6" + - "VBAT_MAX_VALUE_GREATER_THAN_3V6" diff --git a/dts/bindings/serial/telink,b91-uart.yaml b/dts/bindings/serial/telink,b91-uart.yaml new file mode 100644 index 0000000000000..fffb4f0e76f40 --- /dev/null +++ b/dts/bindings/serial/telink,b91-uart.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2021, Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +description: Telink B91 UART + +compatible: "telink,b91-uart" + +include: uart-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + pinctrl-0: + type: phandles + required: true diff --git a/dts/riscv/telink_b91.dtsi b/dts/riscv/telink_b91.dtsi new file mode 100644 index 0000000000000..dc37953edae3d --- /dev/null +++ b/dts/riscv/telink_b91.dtsi @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu0: cpu@0 { + reg = <0>; + clock-frequency = <24000000>; + compatible ="telink,b91", "riscv"; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "telink,telink_b91-soc"; + ranges; + + ram_ilm: memory@0 { + compatible = "mmio-sram"; + }; + + ram_dlm: memory@80000 { + compatible = "mmio-sram"; + }; + + flash_mspi: flash-controller@80140100 { + compatible = "telink,b91-flash-controller"; + label = "flash_mspi"; + reg = <0x80140100 0x40>; + + #address-cells = <1>; + #size-cells = <1>; + + flash: flash@20000000 { + compatible = "soc-nv-flash"; + write-block-size = <1>; + }; + }; + + power: power@80140180 { + compatible = "telink,b91-power"; + reg = <0x80140180 0x40>; + power-mode = "LDO_1P4_LDO_1P8"; + vbat-type = "VBAT_MAX_VALUE_GREATER_THAN_3V6"; + status = "okay"; + }; + + gpioa: gpio@80140300 { + compatible = "telink,b91-gpio"; + gpio-controller; + interrupt-parent = <&plic0>; + interrupts = <25 1>, <26 1>, <27 1>; + reg = <0x80140300 0x08>; + label = "gpio_a"; + status = "disabled"; + #gpio-cells = <2>; + }; + + gpiob: gpio@80140308 { + compatible = "telink,b91-gpio"; + gpio-controller; + interrupt-parent = <&plic0>; + interrupts = <25 1>, <26 1>, <27 1>; + reg = <0x80140308 0x08>; + label = "gpio_b"; + status = "disabled"; + #gpio-cells = <2>; + }; + + gpioc: gpio@80140310 { + compatible = "telink,b91-gpio"; + gpio-controller; + interrupt-parent = <&plic0>; + interrupts = <25 1>, <26 1>, <27 1>; + reg = <0x80140310 0x08>; + label = "gpio_c"; + status = "disabled"; + #gpio-cells = <2>; + }; + + gpiod: gpio@80140318 { + compatible = "telink,b91-gpio"; + gpio-controller; + interrupt-parent = <&plic0>; + interrupts = <25 1>, <26 1>, <27 1>; + reg = <0x80140318 0x08>; + label = "gpio_d"; + status = "disabled"; + #gpio-cells = <2>; + }; + + gpioe: gpio@80140320 { + compatible = "telink,b91-gpio"; + gpio-controller; + interrupt-parent = <&plic0>; + interrupts = <25 1>, <26 1>, <27 1>; + reg = <0x80140320 0x08>; + label = "gpio_e"; + status = "disabled"; + #gpio-cells = <2>; + }; + + plic0: interrupt-controller@e4000000 { + compatible = "sifive,plic-1.0.0"; + #interrupt-cells = <2>; + interrupt-controller; + reg = < 0xe4000000 0x00001000 + 0xe4002000 0x00000800 + 0xe4200000 0x00010000 >; + reg-names = "prio", "irq_en", "reg"; + riscv,max-priority = <3>; + riscv,ndev = <63>; + }; + + uart0: serial@80140080 { + compatible = "telink,b91-uart"; + label = "uart_0"; + reg = <0x80140080 0x40>; + interrupts = <19 1>; + interrupt-parent = <&plic0>; + status = "disabled"; + }; + + uart1: serial@801400C0 { + compatible = "telink,b91-uart"; + label = "uart_1"; + reg = <0x801400C0 0x40>; + interrupts = <18 1>; + interrupt-parent = <&plic0>; + status = "disabled"; + }; + + pinmux: pinmux@80140330 { + compatible = "telink,b91-pinmux"; + reg = <0x80140330 0x28 + 0x80140306 0x28 + 0x0000000e 0x0C>; + reg-names = "pin_mux", + "gpio_en", + "pull_up_en"; + label = "pinmux"; + status = "disabled"; + + /* Define UART0 pins: TX(PA3 PB2 PD2), RX(PA4 PB3 PD3) */ + + uart0_tx_pa3: uart0_tx_pa3 { + pinmux = ; + }; + uart0_tx_pb2: uart0_tx_pb2 { + pinmux = ; + }; + uart0_tx_pd2: uart0_tx_pd2 { + pinmux = ; + }; + + uart0_rx_pa4: uart0_rx_pa4 { + pinmux = ; + }; + uart0_rx_pb3: uart0_rx_pb3 { + pinmux = ; + }; + uart0_rx_pd3: uart0_rx_pd3 { + pinmux = ; + }; + + /* Define UART1 pins: TX(PC6 PD6 PE0), RX(PC7 PD7 PE2) */ + + uart1_tx_pc6: uart1_tx_pc6 { + pinmux = ; + }; + uart1_tx_pd6: uart1_tx_pd6 { + pinmux = ; + }; + uart1_tx_pe0: uart1_tx_pe0 { + pinmux = ; + }; + + uart1_rx_pc7: uart1_rx_pc7 { + pinmux = ; + }; + uart1_rx_pd7: uart1_rx_pd7 { + pinmux = ; + }; + uart1_rx_pe2: uart1_rx_pe2 { + pinmux = ; + }; + }; + }; +}; diff --git a/include/dt-bindings/pinctrl/b91-pinctrl.h b/include/dt-bindings/pinctrl/b91-pinctrl.h new file mode 100644 index 0000000000000..8878b953acd36 --- /dev/null +++ b/include/dt-bindings/pinctrl/b91-pinctrl.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_B91_PINCTRL_COMMON_H_ +#define ZEPHYR_B91_PINCTRL_COMMON_H_ + +/* IDs for GPIO functions */ + +#define B91_FUNC_A 0x000000 +#define B91_FUNC_B 0x010000 +#define B91_FUNC_C 0x020000 + +/* IDs for GPIO Ports */ + +#define B91_PORT_A 0x0000 +#define B91_PORT_B 0x0100 +#define B91_PORT_C 0x0200 +#define B91_PORT_D 0x0300 +#define B91_PORT_E 0x0400 + +/* IDs for GPIO Pins */ + +#define B91_PIN_0 0x01 +#define B91_PIN_1 0x02 +#define B91_PIN_2 0x04 +#define B91_PIN_3 0x08 +#define B91_PIN_4 0x10 +#define B91_PIN_5 0x20 +#define B91_PIN_6 0x40 +#define B91_PIN_7 0x80 + +/* Setters and getters */ + +#define B91_PINMUX_SET(func, port, pin) (func | port | pin) +#define B91_PINMUX_GET_FUNC(pinmux) ((pinmux >> 16) & 0xFF) +#define B91_PINMUX_GET_PIN(pinmux) (pinmux & 0xFFFF) + +#define B91_PINMUX_DT_INST_GET_ELEM(idx, x, inst) \ + DT_PROP_BY_PHANDLE_IDX(DT_DRV_INST(inst), pinctrl_##x, idx, pinmux), + +#define B91_PINMUX_DT_INST_GET_ARRAY(inst, x) \ + { COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pinctrl_##x), \ + (UTIL_LISTIFY(DT_INST_PROP_LEN(inst, pinctrl_##x), \ + B91_PINMUX_DT_INST_GET_ELEM, \ + x, \ + inst)), \ + ()) \ + } + +#endif /* ZEPHYR_B91_PINCTRL_COMMON_H_ */ diff --git a/modules/Kconfig b/modules/Kconfig index abae13373af4d..c8aeb4eb9d04d 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -26,6 +26,7 @@ source "modules/Kconfig.sof" source "modules/Kconfig.st" source "modules/Kconfig.stm32" source "modules/Kconfig.syst" +source "modules/Kconfig.telink" source "modules/Kconfig.tinycbor" source "modules/Kconfig.tinycrypt" source "modules/Kconfig.vega" diff --git a/modules/Kconfig.telink b/modules/Kconfig.telink new file mode 100644 index 0000000000000..d4e5e771ba76a --- /dev/null +++ b/modules/Kconfig.telink @@ -0,0 +1,7 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config HAS_TELINK_DRIVERS + bool "Telink Drivers" + help + This option enables Telink Drivers APIs. diff --git a/soc/riscv/riscv-privilege/telink_b91/CMakeLists.txt b/soc/riscv/riscv-privilege/telink_b91/CMakeLists.txt new file mode 100644 index 0000000000000..a116969d93d65 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources( + start.S + soc_irq.S + soc.c +) + +# Force using BFD-LD +zephyr_ld_options(-fuse-ld=bfd) + +# Set compile options +zephyr_compile_options_ifdef(CONFIG_TELINK_B91_HWDSP -mext-dsp) +zephyr_compile_options(-mno-relax) diff --git a/soc/riscv/riscv-privilege/telink_b91/Kconfig.defconfig.series b/soc/riscv/riscv-privilege/telink_b91/Kconfig.defconfig.series new file mode 100644 index 0000000000000..7f2c12d6b480a --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/Kconfig.defconfig.series @@ -0,0 +1,46 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +if SOC_SERIES_RISCV_TELINK_B91 + +config SOC_SERIES + string + default "telink_b91" + +config SYS_CLOCK_HW_CYCLES_PER_SEC + int + default 32000 + +config RISCV_SOC_INTERRUPT_INIT + bool + default y + +config RISCV_HAS_CPU_IDLE + bool + default y + +config RISCV_HAS_PLIC + bool + default y + +config NUM_IRQS + int + default 64 + +config XIP + bool + default n + +config MAIN_STACK_SIZE + int + default 2048 + +config IDLE_STACK_SIZE + int + default 1536 + +config TEST_EXTRA_STACKSIZE + int + default 1024 + +endif # SOC_SERIES_RISCV_TELINK_B91 diff --git a/soc/riscv/riscv-privilege/telink_b91/Kconfig.series b/soc/riscv/riscv-privilege/telink_b91/Kconfig.series new file mode 100644 index 0000000000000..ba9aba0840e71 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/Kconfig.series @@ -0,0 +1,10 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config SOC_SERIES_RISCV_TELINK_B91 + bool "Telink B91 SoC Implementation" + select RISCV + select SOC_FAMILY_RISCV_PRIVILEGE + select HAS_TELINK_DRIVERS + help + Enable support for Telink B91 SoC diff --git a/soc/riscv/riscv-privilege/telink_b91/Kconfig.soc b/soc/riscv/riscv-privilege/telink_b91/Kconfig.soc new file mode 100644 index 0000000000000..973bef47a1931 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/Kconfig.soc @@ -0,0 +1,33 @@ +# Copyright (c) 2021 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +choice +prompt "CPU Architecture of SoC" +depends on SOC_SERIES_RISCV_TELINK_B91 + +config B91_CPU_RISCV32 + bool "RISCV32 CPU Architecture" + +endchoice + +config TELINK_B91_HWDSP + bool "Support Hardware DSP" + select RISCV_SOC_CONTEXT_SAVE + depends on SOC_SERIES_RISCV_TELINK_B91 + +config TELINK_B91_PFT_ARCH + bool "Support performance throttling" + default y + select RISCV_SOC_CONTEXT_SAVE + depends on SOC_SERIES_RISCV_TELINK_B91 + +choice +prompt "Telink B91 SoC implementation" +depends on SOC_SERIES_RISCV_TELINK_B91 + +config SOC_RISCV_TELINK_B91 + bool "Telink B91 SoC implementation" + select ATOMIC_OPERATIONS_BUILTIN + select CPU_HAS_FPU + +endchoice diff --git a/soc/riscv/riscv-privilege/telink_b91/linker.ld b/soc/riscv/riscv-privilege/telink_b91/linker.ld new file mode 100644 index 0000000000000..03aecd466775f --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/linker.ld @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Linker script for the Telink B91 SoC + */ + +#include +#include +#include + +MEMORY +{ + ROM_INIT (rx) : ORIGIN = DT_REG_ADDR(DT_CHOSEN(zephyr_flash)), LENGTH = DT_REG_SIZE(DT_CHOSEN(zephyr_flash)) + RAM_ILM (rwx) : ORIGIN = DT_REG_ADDR(DT_NODELABEL(ram_ilm)), LENGTH = DT_REG_SIZE(DT_NODELABEL(ram_ilm)) +} + +SECTIONS +{ + SECTION_PROLOGUE(vector,,) + { + . = ALIGN(4); + KEEP(*(.init.*)) + } GROUP_LINK_IN(ROM_INIT) +} + +#include + +SECTIONS +{ + SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,) + { + . = ALIGN(8); + PROVIDE (__global_pointer$ = __data_ram_start + 0x800); + } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) + + SECTION_DATA_PROLOGUE(retention_data,,) + { + . = ALIGN(8); + *(.retention_data) + *(".retention_data.*") + + PROVIDE (_RETENTION_DATA_VMA_END = .); + PROVIDE (_RETENTION_DATA_VMA_START = ADDR(retention_data)); + PROVIDE (_RETENTION_DATA_LMA_START = LOADADDR(retention_data)); + } GROUP_DATA_LINK_IN(RAM_ILM, ROMABLE_REGION) + + SECTION_DATA_PROLOGUE(ram_code,,) + { + . = ALIGN(8); + *(.ram_code) + *(".ram_code.*") + + PROVIDE (_RAMCODE_VMA_END = .); + PROVIDE (_RAMCODE_VMA_START = ADDR(ram_code)); + PROVIDE (_RAMCODE_LMA_START = LOADADDR(ram_code)); + } GROUP_DATA_LINK_IN(RAM_ILM, ROMABLE_REGION) +} diff --git a/soc/riscv/riscv-privilege/telink_b91/soc.c b/soc/riscv/riscv-privilege/telink_b91/soc.c new file mode 100644 index 0000000000000..05735b68ee61b --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/soc.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sys.h" +#include "clock.h" +#include + +/* Software reset defines */ +#define reg_reset REG_ADDR8(0x1401ef) +#define SOFT_RESET 0x20u + +/* List of supported CCLK fregencies */ +#define CLK_16MHZ 16000000u +#define CLK_24MHZ 24000000u +#define CLK_32MHZ 32000000u +#define CLK_48MHZ 48000000u +#define CLK_64MHZ 64000000u +#define CLK_96MHZ 96000000u + +/* Define 48 MHz and 96 MHz CCLK clock options (not present in HAL) */ +#define CCLK_64M_HCLK_32M_PCLK_16M clock_init(PLL_CLK_192M, \ + PAD_PLL_DIV, \ + PLL_DIV3_TO_CCLK, \ + CCLK_DIV2_TO_HCLK, \ + HCLK_DIV2_TO_PCLK, \ + PLL_DIV4_TO_MSPI_CLK) + +#define CCLK_96M_HCLK_48M_PCLK_24M clock_init(PLL_CLK_192M, \ + PAD_PLL_DIV, \ + PLL_DIV2_TO_CCLK, \ + CCLK_DIV2_TO_HCLK, \ + HCLK_DIV2_TO_PCLK, \ + PLL_DIV4_TO_MSPI_CLK) + +/* Power Mode value */ +#if DT_ENUM_IDX(DT_NODELABEL(power), power_mode) == 0 + #define POWER_MODE LDO_1P4_LDO_1P8 +#elif DT_ENUM_IDX(DT_NODELABEL(power), power_mode) == 1 + #define POWER_MODE DCDC_1P4_LDO_1P8 +#elif DT_ENUM_IDX(DT_NODELABEL(power), power_mode) == 2 + #define POWER_MODE DCDC_1P4_DCDC_1P8 +#else + #error "Wrong value for power-mode parameter" +#endif + +/* Vbat Type value */ +#if DT_ENUM_IDX(DT_NODELABEL(power), vbat_type) == 0 + #define VBAT_TYPE VBAT_MAX_VALUE_LESS_THAN_3V6 +#elif DT_ENUM_IDX(DT_NODELABEL(power), vbat_type) == 1 + #define VBAT_TYPE VBAT_MAX_VALUE_GREATER_THAN_3V6 +#else + #error "Wrong value for vbat-type parameter" +#endif + +/* Check System Clock value. */ +#if ((DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_16MHZ) && \ + (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_24MHZ) && \ + (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_32MHZ) && \ + (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_48MHZ) && \ + (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_64MHZ) && \ + (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) != CLK_96MHZ)) + #error "Unsupported clock-frequency. Supported values: 16, 24, 32, 48, 64 and 96 MHz" +#endif + +/** + * @brief Perform basic initialization at boot. + * + * @return 0 + */ +static int soc_b91_init(const struct device *arg) +{ + unsigned int cclk = DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency); + + ARG_UNUSED(arg); + + /* system init */ + sys_init(POWER_MODE, VBAT_TYPE); + + /* clocks init: CCLK, HCLK, PCLK */ + switch (cclk) { + case CLK_16MHZ: + CCLK_16M_HCLK_16M_PCLK_16M; + break; + + case CLK_24MHZ: + CCLK_24M_HCLK_24M_PCLK_24M; + break; + + case CLK_32MHZ: + CCLK_32M_HCLK_32M_PCLK_16M; + break; + + case CLK_48MHZ: + CCLK_48M_HCLK_48M_PCLK_24M; + break; + + case CLK_64MHZ: + CCLK_64M_HCLK_32M_PCLK_16M; + break; + + case CLK_96MHZ: + CCLK_96M_HCLK_48M_PCLK_24M; + break; + } + + /* Init Machine Timer source clock: 32 KHz RC */ + clock_32k_init(CLK_32K_RC); + clock_cal_32k_rc(); + + return 0; +} + +/** + * @brief Reset the system. + */ +void sys_arch_reboot(int type) +{ + ARG_UNUSED(type); + + reg_reset = SOFT_RESET; +} + +SYS_INIT(soc_b91_init, PRE_KERNEL_1, 0); diff --git a/soc/riscv/riscv-privilege/telink_b91/soc.h b/soc/riscv/riscv-privilege/telink_b91/soc.h new file mode 100644 index 0000000000000..2f932c09d40ce --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/soc.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef RISCV_TELINK_B91_SOC_H +#define RISCV_TELINK_B91_SOC_H + +#include +#include + +/* Machine timer memory-mapped registers */ +#define RISCV_MTIME_BASE 0xE6000000 +#define RISCV_MTIMECMP_BASE 0xE6000008 + +#endif /* RISCV_TELINK_B91_SOC_H */ diff --git a/soc/riscv/riscv-privilege/telink_b91/soc_context.h b/soc/riscv/riscv-privilege/telink_b91/soc_context.h new file mode 100644 index 0000000000000..0f06686453988 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/soc_context.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SOC_RISCV_TELINK_B91_SOC_CONTEXT_H +#define SOC_RISCV_TELINK_B91_SOC_CONTEXT_H + +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE + +/* Telink B91 specific registers. */ +#if defined(CONFIG_TELINK_B91_PFT_ARCH) && defined(__riscv_dsp) + #define SOC_ESF_MEMBERS \ + uint32_t mxstatus; \ + uint32_t ucode \ + + #define SOC_ESF_INIT \ + 0xdeadbaad, \ + 0xdeadbaad + + #define SOC_ESF_THREAD_INIT(soc_context) \ + (soc_context)->mxstatus = 0; \ + (soc_context)->ucode = 0 + +#elif defined(CONFIG_TELINK_B91_PFT_ARCH) + #define SOC_ESF_MEMBERS \ + uint32_t mxstatus + + #define SOC_ESF_INIT \ + 0xdeadbaad + + #define SOC_ESF_THREAD_INIT(soc_context) \ + (soc_context)->mxstatus = 0 + +#elif defined(__riscv_dsp) + + #define SOC_ESF_MEMBERS \ + uint32_t ucode + + #define SOC_ESF_INIT \ + 0xdeadbaad + + #define SOC_ESF_THREAD_INIT(soc_context) \ + (soc_context)->ucode = 0 +#endif + +#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */ + +#endif /* SOC_RISCV_TELINK_B91_SOC_CONTEXT_H */ diff --git a/soc/riscv/riscv-privilege/telink_b91/soc_irq.S b/soc/riscv/riscv-privilege/telink_b91/soc_irq.S new file mode 100644 index 0000000000000..abc9015b66be0 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/soc_irq.S @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define NDS_MXSTATUS 0x7C4 + +/* Exports */ +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE +GTEXT(__soc_save_context) +GTEXT(__soc_restore_context) +#endif + +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE + +SECTION_FUNC(exception.other, __soc_save_context) + +#ifdef CONFIG_TELINK_B91_PFT_ARCH + csrr t0, NDS_MXSTATUS +#endif +#ifdef __riscv_dsp + csrr t1, ucode +#endif + +#ifdef CONFIG_TELINK_B91_PFT_ARCH + sw t0, __soc_esf_t_mxstatus_OFFSET(a0) +#endif +#ifdef __riscv_dsp + sw t1, __soc_esf_t_ucode_OFFSET(a0) +#endif + ret + +SECTION_FUNC(exception.other, __soc_restore_context) + +#ifdef CONFIG_TELINK_B91_PFT_ARCH + lw t0, __soc_esf_t_mxstatus_OFFSET(a0) +#endif +#ifdef __riscv_dsp + lw t1, __soc_esf_t_ucode_OFFSET(a0) +#endif + +#ifdef CONFIG_TELINK_B91_PFT_ARCH + csrw NDS_MXSTATUS, t0 +#endif +#ifdef __riscv_dsp + csrw ucode, t1 +#endif + ret + +#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */ diff --git a/soc/riscv/riscv-privilege/telink_b91/soc_offsets.h b/soc/riscv/riscv-privilege/telink_b91/soc_offsets.h new file mode 100644 index 0000000000000..8b48bceedfffa --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/soc_offsets.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SOC_RISCV_TELINK_B91_SOC_OFFSETS_H +#define SOC_RISCV_TELINK_B91_SOC_OFFSETS_H + +#ifdef CONFIG_RISCV_SOC_OFFSETS + +/* Telink B91 specific registers. */ +#if defined(CONFIG_TELINK_B91_PFT_ARCH) && defined(__riscv_dsp) + #define GEN_SOC_OFFSET_SYMS() \ + GEN_OFFSET_SYM(soc_esf_t, mxstatus); \ + GEN_OFFSET_SYM(soc_esf_t, ucode) + +#elif defined(CONFIG_TELINK_B91_PFT_ARCH) + #define GEN_SOC_OFFSET_SYMS() \ + GEN_OFFSET_SYM(soc_esf_t, mxstatus) + +#elif defined(__riscv_dsp) + #define GEN_SOC_OFFSET_SYMS() \ + GEN_OFFSET_SYM(soc_esf_t, ucode) + +#endif + +#endif /* CONFIG_RISCV_SOC_OFFSETS */ + +#endif /* SOC_RISCV_TELINK_B91_SOC_OFFSETS_H*/ diff --git a/soc/riscv/riscv-privilege/telink_b91/start.S b/soc/riscv/riscv-privilege/telink_b91/start.S new file mode 100644 index 0000000000000..a791d2eeae980 --- /dev/null +++ b/soc/riscv/riscv-privilege/telink_b91/start.S @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define NDS_MCACHE_CTL 0x7CA +#define NDS_MMISC_CTL 0x7D0 + +#include + + .option push + .option norelax + .org 0x0 + +/* exports */ +GTEXT(entry) + +SECTION_FUNC(init, init) +entry: + + j start + + .org 0x20 + .word ('T'<<24 | 'L'<<16 | 'N'<<8 | 'K') + + .org 0x26 + .short (0x173B) + + .option pop + .align 2 + +start: + + /* Enable I/D-Cache */ + csrr t0, NDS_MCACHE_CTL + ori t0, t0, 1 #/I-Cache + ori t0, t0, 2 #/D-Cache + csrw NDS_MCACHE_CTL, t0 + fence.i + + /* Enable misaligned access and non-blocking load */ + li t0, (1 << 8) | (1 << 6) + csrs NDS_MMISC_CTL, t0 + +_RETENTION_DATA_INIT: + la t1, _RETENTION_DATA_LMA_START + la t2, _RETENTION_DATA_VMA_START + la t3, _RETENTION_DATA_VMA_END +_RETENTION_DATA_INIT_BEGIN: + bleu t3, t2, _RAMCODE_INIT + lw t0, 0(t1) + sw t0, 0(t2) + addi t1, t1, 4 + addi t2, t2, 4 + j _RETENTION_DATA_INIT_BEGIN + +_RAMCODE_INIT: + la t1, _RAMCODE_LMA_START + la t2, _RAMCODE_VMA_START + la t3, _RAMCODE_VMA_END +_RAMCODE_INIT_BEGIN: + bleu t3, t2, _START + lw t0, 0(t1) + sw t0, 0(t2) + addi t1, t1, 4 + addi t2, t2, 4 + j _RAMCODE_INIT_BEGIN + +_START: + j __start diff --git a/tests/drivers/gpio/gpio_basic_api/boards/tlsr9518adk80d.overlay b/tests/drivers/gpio/gpio_basic_api/boards/tlsr9518adk80d.overlay new file mode 100644 index 0000000000000..8a9b8f2394705 --- /dev/null +++ b/tests/drivers/gpio/gpio_basic_api/boards/tlsr9518adk80d.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021 Telink Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + resources { + compatible = "test,gpio_basic_api"; + out-gpios = <&gpioc 6 0>; /* Port C 6 */ + in-gpios = <&gpioc 7 0>; /* Port C 7 */ + }; +}; diff --git a/west.yml b/west.yml index 64f8dc154c357..9fc5ddedf3a1b 100644 --- a/west.yml +++ b/west.yml @@ -86,6 +86,9 @@ manifest: - name: hal_stm32 revision: dc83915685a5b32a299388e54e0fd259ded86bc4 path: modules/hal/stm32 + - name: hal_telink + revision: ffcfd6282aa213f1dc0848dbca6279b098f6b143 + path: modules/hal/telink - name: hal_ti revision: 3da6fae25fc44ec830fac4a92787b585ff55435e path: modules/hal/ti