diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index e08fe8de00413..38c9c4dcbf28e 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -2661,6 +2661,7 @@ LoRa and LoRaWAN: - Mani-Sadhasivam - martinjaeger - mniestroj + - Salamandar files: - drivers/lora/ - include/zephyr/drivers/lora.h @@ -2671,6 +2672,16 @@ LoRa and LoRaWAN: - include/zephyr/dt-bindings/lora/ - dts/bindings/lora/ - doc/connectivity/lora_lorawan/index.rst + + - boards/shields/semtech_lr11*/ + - boards/shields/semtech_sx12*/ + - drivers/lora_lbm/ + - dts/bindings/lora_lbm/ + - include/zephyr/dt-bindings/lora_lbm/ + - include/zephyr/lora_lbm/ + - include/zephyr/lorawan_lbm/ + - modules/lora_basics_modem/ + - subsys/lorawan_lbm/ labels: - "area: LoRa" tests: @@ -5138,6 +5149,15 @@ West: labels: - "area: LoRa" +"West project: lora_basics_modem": + status: maintained + maintainers: + - Salamandar + files: + - modules/lora_basics_modem/ + labels: + - "area: LoRa" + "West project: lvgl": status: maintained maintainers: diff --git a/boards/shields/semtech_lr11xxmb1xxs/Kconfig.shield b/boards/shields/semtech_lr11xxmb1xxs/Kconfig.shield new file mode 100644 index 0000000000000..8c9c8ba78c792 --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/Kconfig.shield @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_SEMTECH_LR1110MB1xxS + def_bool $(shields_list_contains,semtech_lr1110mb1xxs) + +config SHIELD_SEMTECH_LR1120MB1xxS + def_bool $(shields_list_contains,semtech_lr1120mb1xxs) + +config SHIELD_SEMTECH_LR1121MB1xxS + def_bool $(shields_list_contains,semtech_lr1121mb1xxs) diff --git a/boards/shields/semtech_lr11xxmb1xxs/doc/index.rst b/boards/shields/semtech_lr11xxmb1xxs/doc/index.rst new file mode 100644 index 0000000000000..54b091c624e88 --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/doc/index.rst @@ -0,0 +1,97 @@ +.. semtech_lr11xxmb1xxs: + +Semtech LR11xxMB1xxS LoRa Shields +################################# + +Overview +******** + +Semtech provides a series of Arduino compatible shields based on their LoRa +transceivers LR1110, LR1120, and LR1121. +Those shields are mostly similar and include a LIS2DE12 3-axis i2c accelerometer +from STMicroelectronics. + +More information about the shield can be found at the `Semtech LR1110MB1LBKS +website`_. + +Pins Assignment of the Semtech LR1110MB1xxS LoRa Shield +======================================================= + ++-------------+---------------------+ +| Shield Pin | Function | ++=============+=====================+ +| A0 | LR1110 NRESET | ++-------------+---------------------+ +| A1 | 32kHz Osc out | ++-------------+---------------------+ +| A2 | LR111x / LR1110 | ++-------------+---------------------+ +| A3 | LNA Control | ++-------------+---------------------+ +| A4 | LED TX | ++-------------+---------------------+ +| A5 | LED RX | ++-------------+---------------------+ +| D2 | To Display Shield | ++-------------+---------------------+ +| D3 | LR1110 DIO0 (BUSY) | ++-------------+---------------------+ +| D4 | LED Sniffing | ++-------------+---------------------+ +| D5 | LR1110 DIO9 | ++-------------+---------------------+ +| D7 | LR1110 SPI NSS | ++-------------+---------------------+ +| D8 | MEMS Accel INT1 | ++-------------+---------------------+ +| D9 | Display D/C | ++-------------+---------------------+ +| D10 | Display CS | ++-------------+---------------------+ +| D11 | LR1110 SPI MOSI | ++-------------+---------------------+ +| D12 | LR1110 SPI MISO | ++-------------+---------------------+ +| D13 | LR1110 SPI SCK | ++-------------+---------------------+ +| D14 | MEMS+EEPROM i2c SDA | ++-------------+---------------------+ +| D15 | MEMS+EEPROM i2c SCL | ++-------------+---------------------+ + +LR1110 and LR1120 based shields use a TCXO and LR1121 based shields use a crystal. + +Requirements +************ + +This shield can only be used with a board which provides a configuration for +Arduino connectors and defines node aliases for SPI and GPIO interfaces (see +:ref:`shields` for more details). + +Programming +*********** + +Set ``-DSHIELD=semtech_lr1110mb1xxs`` when you invoke ``west build``. For +example: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/lorawan/class_a + :board: nucleo_l073rz + :shield: semtech_lr1110mb1xxs + :goals: build + +References +********** + +.. target-notes:: + +.. _Semtech LR1110MB1LBKS website: + https://www.semtech.com/products/wireless-rf/lora-edge/lr1110mb1lbks + + +License +******* + +This document Copyright (c) 2024 Semtech Corporation + +SPDX-License-Identifier: Apache-2.0 diff --git a/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1110mb1xxs.overlay b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1110mb1xxs.overlay new file mode 100644 index 0000000000000..bb6d50dc79178 --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1110mb1xxs.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_lr11xxmb1xxs_common.dtsi" + +lora_semtech_lr1110mb1xxs: &lora_semtech_lr11xxmb1xxs { + compatible = "semtech,lr1110"; + + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1120mb1xxs.overlay b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1120mb1xxs.overlay new file mode 100644 index 0000000000000..aebe1d75c4980 --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1120mb1xxs.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_lr11xxmb1xxs_common.dtsi" + +lora_semtech_lr1120mb1xxs: &lora_semtech_lr11xxmb1xxs { + compatible = "semtech,lr1120"; + + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1121mb1xxs.overlay b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1121mb1xxs.overlay new file mode 100644 index 0000000000000..a25ee720013ad --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr1121mb1xxs.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_lr11xxmb1xxs_common.dtsi" + +lora_semtech_lr1121mb1xxs: &lora_semtech_lr11xxmb1xxs { + compatible = "semtech,lr1121"; + + /* LR1121 shields all have XTAL */ +}; diff --git a/boards/shields/semtech_lr11xxmb1xxs/semtech_lr11xxmb1xxs_common.dtsi b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr11xxmb1xxs_common.dtsi new file mode 100644 index 0000000000000..8dc0eb56e1d8e --- /dev/null +++ b/boards/shields/semtech_lr11xxmb1xxs/semtech_lr11xxmb1xxs_common.dtsi @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + chosen { + zephyr,lorawan-transceiver = &lora_semtech_lr11xxmb1xxs; + }; + aliases { + lora-transceiver = &lora_semtech_lr11xxmb1xxs; + }; + leds { + lora_rx_led: lr11xx_rx_led { + gpios = <&arduino_header 5 GPIO_ACTIVE_HIGH>; + label = "RX LED"; + }; + lora_tx_led: lr11xx_tx_led { + gpios = <&arduino_header 4 GPIO_ACTIVE_HIGH>; + label = "TX LED"; + }; + lora_scanning_led: lr11xx_scanning_led { + gpios = <&arduino_header 10 GPIO_ACTIVE_HIGH>; + label = "Scanning LED"; + }; + lora_gnss_lna_control: lr11xx_gnss_lna_control { + /* Not an LED but necessary */ + gpios = <&arduino_header 3 GPIO_ACTIVE_HIGH>; + label = "LR11xx GNSS LNA control"; + }; + }; +}; + + +&arduino_spi { + status = "okay"; + + cs-gpios = <&arduino_header 13 GPIO_ACTIVE_LOW>; + + lora_semtech_lr11xxmb1xxs: lora@0 { + reg = <0>; + spi-max-frequency = ; + + reset-gpios = <&arduino_header 0 GPIO_ACTIVE_LOW>; + + busy-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; + + event-gpios = <&arduino_header 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + + lf-tx-path = ; + + lf-clk = ; + reg-mode = ; + + rf-sw-enable = <(LR11XX_DIO5 | LR11XX_DIO6 | LR11XX_DIO7)>; + rf-sw-rx-mode = ; + rf-sw-tx-mode = <(LR11XX_DIO5 | LR11XX_DIO6)>; + rf-sw-tx-hp-mode = ; + rf-sw-gnss-mode = ; + + tcxo-voltage = ; + /* Default to XTAL board */ + tcxo-wakeup-time = <0>; + + tx-power-cfg-lf-lp = + /* power, pa_duty_cycle, pa_hp_sel */ + < (-15) 0x00 0x00>, /* Expected output power = -17dBm */ + < (-14) 0x00 0x00>, /* Expected output power = -16dBm */ + < (-13) 0x00 0x00>, /* Expected output power = -15dBm */ + < (-12) 0x00 0x00>, /* Expected output power = -14dBm */ + < (-11) 0x00 0x00>, /* Expected output power = -13dBm */ + < (-9) 0x00 0x00>, /* Expected output power = -12dBm */ + < (-8) 0x00 0x00>, /* Expected output power = -11dBm */ + < (-7) 0x00 0x00>, /* Expected output power = -10dBm */ + < (-6) 0x00 0x00>, /* Expected output power = -9dBm */ + < (-5) 0x00 0x00>, /* Expected output power = -8dBm */ + < (-4) 0x00 0x00>, /* Expected output power = -7dBm */ + < (-3) 0x00 0x00>, /* Expected output power = -6dBm */ + < (-2) 0x00 0x00>, /* Expected output power = -5dBm */ + < (-1) 0x00 0x00>, /* Expected output power = -4dBm */ + < ( 0) 0x00 0x00>, /* Expected output power = -3dBm */ + < ( 1) 0x00 0x00>, /* Expected output power = -2dBm */ + < ( 2) 0x00 0x00>, /* Expected output power = -1dBm */ + < ( 3) 0x00 0x00>, /* Expected output power = 0dBm */ + < ( 3) 0x01 0x00>, /* Expected output power = 1dBm */ + < ( 4) 0x01 0x00>, /* Expected output power = 2dBm */ + < ( 7) 0x00 0x00>, /* Expected output power = 3dBm */ + < ( 8) 0x00 0x00>, /* Expected output power = 4dBm */ + < ( 9) 0x00 0x00>, /* Expected output power = 5dBm */ + < ( 10) 0x00 0x00>, /* Expected output power = 6dBm */ + < ( 12) 0x00 0x00>, /* Expected output power = 7dBm */ + < ( 13) 0x00 0x00>, /* Expected output power = 8dBm */ + < ( 14) 0x00 0x00>, /* Expected output power = 9dBm */ + < ( 13) 0x01 0x00>, /* Expected output power = 10dBm */ + < ( 13) 0x02 0x00>, /* Expected output power = 11dBm */ + < ( 14) 0x02 0x00>, /* Expected output power = 12dBm */ + < ( 14) 0x03 0x00>, /* Expected output power = 13dBm */ + < ( 14) 0x04 0x00>, /* Expected output power = 14dBm */ + < ( 14) 0x07 0x00>; /* Expected output power = 15dBm */ + + tx-power-cfg-lf-hp = + /* power, pa_duty_cycle, pa_hp_sel */ + < ( 9) 0x00 0x00>, /* Expected output power = -9dBm */ + < ( 10) 0x00 0x00>, /* Expected output power = -8dBm */ + < ( 11) 0x00 0x00>, /* Expected output power = -7dBm */ + < ( 12) 0x00 0x00>, /* Expected output power = -6dBm */ + < ( 13) 0x00 0x00>, /* Expected output power = -5dBm */ + < ( 13) 0x01 0x00>, /* Expected output power = -4dBm */ + < ( 13) 0x02 0x00>, /* Expected output power = -3dBm */ + < ( 17) 0x02 0x00>, /* Expected output power = -2dBm */ + < ( 14) 0x04 0x00>, /* Expected output power = -1dBm */ + < ( 12) 0x00 0x01>, /* Expected output power = 0dBm */ + < ( 13) 0x00 0x01>, /* Expected output power = 1dBm */ + < ( 13) 0x01 0x01>, /* Expected output power = 2dBm */ + < ( 13) 0x02 0x01>, /* Expected output power = 3dBm */ + < ( 15) 0x00 0x02>, /* Expected output power = 4dBm */ + < ( 15) 0x04 0x01>, /* Expected output power = 5dBm */ + < ( 14) 0x02 0x02>, /* Expected output power = 6dBm */ + < ( 14) 0x01 0x03>, /* Expected output power = 7dBm */ + < ( 17) 0x04 0x02>, /* Expected output power = 8dBm */ + < ( 22) 0x00 0x01>, /* Expected output power = 9dBm */ + < ( 22) 0x01 0x01>, /* Expected output power = 10dBm */ + < ( 22) 0x02 0x01>, /* Expected output power = 11dBm */ + < ( 22) 0x03 0x01>, /* Expected output power = 12dBm */ + < ( 22) 0x00 0x03>, /* Expected output power = 13dBm */ + < ( 22) 0x01 0x03>, /* Expected output power = 14dBm */ + < ( 22) 0x04 0x02>, /* Expected output power = 15dBm */ + < ( 22) 0x01 0x04>, /* Expected output power = 16dBm */ + < ( 22) 0x02 0x04>, /* Expected output power = 17dBm */ + < ( 22) 0x01 0x06>, /* Expected output power = 18dBm */ + < ( 22) 0x03 0x05>, /* Expected output power = 19dBm */ + < ( 22) 0x03 0x07>, /* Expected output power = 20dBm */ + < ( 22) 0x04 0x06>, /* Expected output power = 21dBm */ + < ( 22) 0x04 0x07>; /* Expected output power = 22dBm */ + + tx-power-cfg-hf = + /* power, pa_duty_cycle, pa_hp_sel */ + < (-18) 0x04 0x00>, //Expected output power = -18dBm + < (-18) 0x04 0x00>, /* Expected output power = -17dBm */ + < (-17) 0x04 0x00>, /* Expected output power = -16dBm */ + < (-16) 0x04 0x00>, /* Expected output power = -15dBm */ + < (-15) 0x04 0x00>, /* Expected output power = -14dBm */ + < (-14) 0x04 0x00>, /* Expected output power = -13dBm */ + < (-14) 0x04 0x00>, /* Expected output power = -12dBm */ + < (-12) 0x04 0x00>, /* Expected output power = -11dBm */ + < (-10) 0x04 0x00>, /* Expected output power = -10dBm */ + < ( -9) 0x04 0x00>, /* Expected output power = -9dBm */ + < ( -8) 0x04 0x00>, /* Expected output power = -8dBm */ + < ( -7) 0x04 0x00>, /* Expected output power = -7dBm */ + < ( -6) 0x04 0x00>, /* Expected output power = -6dBm */ + < ( -5) 0x04 0x00>, /* Expected output power = -5dBm */ + < ( -4) 0x04 0x00>, /* Expected output power = -4dBm */ + < ( -3) 0x04 0x00>, /* Expected output power = -3dBm */ + < ( -2) 0x03 0x00>, /* Expected output power = -2dBm */ + < ( -1) 0x04 0x00>, /* Expected output power = -1dBm */ + < ( 0) 0x04 0x00>, /* Expected output power = 0dBm */ + < ( 1) 0x00 0x00>, /* Expected output power = 1dBm */ + < ( 2) 0x00 0x00>, /* Expected output power = 2dBm */ + < ( 4) 0x04 0x00>, /* Expected output power = 3dBm */ + < ( 5) 0x04 0x00>, /* Expected output power = 4dBm */ + < ( 6) 0x04 0x00>, /* Expected output power = 5dBm */ + < ( 7) 0x04 0x00>, /* Expected output power = 6dBm */ + < ( 8) 0x04 0x00>, /* Expected output power = 7dBm */ + < ( 9) 0x04 0x00>, /* Expected output power = 8dBm */ + < ( 10) 0x04 0x00>, /* Expected output power = 9dBm */ + < ( 11) 0x04 0x00>, /* Expected output power = 10dBm */ + < ( 12) 0x03 0x00>, /* Expected output power = 11dBm */ + < ( 13) 0x04 0x00>, /* Expected output power = 12dBm */ + < ( 13) 0x00 0x00>; /* Expected output power = 13dBm */ + + + /* Tune is G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 + * G13hp1 G13hp2 G13hp3 G13hp4 G13hp5 G13hp6 G13hp7 + */ + rssi-calibration-lf-offset = <0>; + rssi-calibration-lf-tune = <12 12 14 0 1 3 4 4 3 6 6 6 6 6 6 6 6>; + + rssi-calibration-mf-offset = <0>; + rssi-calibration-mf-tune = <2 2 2 3 3 4 5 4 4 6 5 5 6 6 6 7 6>; + + rssi-calibration-hf-offset = <2030>; + rssi-calibration-hf-tune = <6 7 6 4 3 4 14 12 14 12 12 12 12 8 8 9 9>; + + }; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/Kconfig.shield b/boards/shields/semtech_sx126xmb2xxs/Kconfig.shield new file mode 100644 index 0000000000000..4334e90ca78f2 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/Kconfig.shield @@ -0,0 +1,31 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SHIELD_SEMTECH_SX1261MB1BAS + def_bool $(shields_list_contains,semtech_sx1261mb1bas) + +config SHIELD_SEMTECH_SX1261MB1CAS + def_bool $(shields_list_contains,semtech_sx1261mb1cas) + +config SHIELD_SEMTECH_SX1261MB2BAS + def_bool $(shields_list_contains,semtech_sx1261mb2bas) + + +config SHIELD_SEMTECH_SX1262MB1CAS + def_bool $(shields_list_contains,semtech_sx1262mb1cas) + +config SHIELD_SEMTECH_SX1262MB1CBS + def_bool $(shields_list_contains,semtech_sx1262mb1cbs) + +config SHIELD_SEMTECH_SX1262MB1DAS + def_bool $(shields_list_contains,semtech_sx1262mb1das) + +config SHIELD_SEMTECH_SX1262MB1PAS + def_bool $(shields_list_contains,semtech_sx1262mb1pas) + +config SHIELD_SEMTECH_SX1262MB2CAS + def_bool $(shields_list_contains,semtech_sx1262mb2cas) + + +config SHIELD_SEMTECH_SX1268MB1GAS + def_bool $(shields_list_contains,semtech_sx1268mb1gas) diff --git a/boards/shields/semtech_sx126xmb2xxs/doc/index.rst b/boards/shields/semtech_sx126xmb2xxs/doc/index.rst new file mode 100644 index 0000000000000..3082a4466085c --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/doc/index.rst @@ -0,0 +1,87 @@ +.. semtech_sx126xmb2xxs: + +Semtech SX126xMB2xxS LoRa Shields +################################# + +Overview +******** + +Semtech provides a series of Arduino compatible shields based on their LoRa +transceivers SX1261, SX1262, and SX126 +Those shields are mostly similar and have Grove compatible connectors passthrough. + +More information about the shield can be found at the `Semtech SX1262MB2CAS website`_. + +Pins Assignment of the Semtech SX126xMB2xxS LoRa Shield +======================================================= + ++-------------+---------------------+ +| Shield Pin | Function | ++=============+=====================+ +| A0 | SX1261 NRESET | ++-------------+---------------------+ +| A1 | FR0/FR1 | ++-------------+---------------------+ +| A2 | SX1261 / | ++-------------+---------------------+ +| A3 | OPT (XTAL / TCXO) | ++-------------+---------------------+ +| A4 | Grove A5 | ++-------------+---------------------+ +| A5 | Grove A6 | ++-------------+---------------------+ +| D3 | SX1261 BUSY | ++-------------+---------------------+ +| D5 | SX1261 DIO1 | ++-------------+---------------------+ +| D7 | SX1261 SPI NSS | ++-------------+---------------------+ +| D8 | Antenna Switch | ++-------------+---------------------+ +| D9 | Grove D9 | ++-------------+---------------------+ +| D10 | Grove D10 | ++-------------+---------------------+ +| D11 | SX1261 SPI MOSI | ++-------------+---------------------+ +| D12 | SX1261 SPI MISO | ++-------------+---------------------+ +| D13 | SX1261 SPI SCK | ++-------------+---------------------+ +| D14 | Grove i2c SDA | ++-------------+---------------------+ +| D15 | Grove i2c SCL | ++-------------+---------------------+ + +Requirements +************ + +This shield can only be used with a board which provides a configuration for +Arduino connectors and defines node aliases for SPI and GPIO interfaces (see +:ref:`shields` for more details). + +Programming +*********** + +Set ``-DSHIELD=semtech_sx126xmb2xxs`` when you invoke ``west build``. For +example: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/lorawan/class_a + :board: nucleo_l073rz + :shield: semtech_sx126xmb2xxs + :goals: build + +References +********** + +.. target-notes:: + +.. _Semtech SX1262MB2CAS website: https://www.semtech.com/products/wireless-rf/lora-connect/sx1262mb2cas + +License +******* + +This document Copyright (c) 2024 Semtech Corporation + +SPDX-License-Identifier: Apache-2.0 diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1bas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1bas.overlay new file mode 100644 index 0000000000000..4c73dd03e5dd5 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1bas.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1261mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1261-new"; + + reg-mode = ; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1cas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1cas.overlay new file mode 100644 index 0000000000000..868422309f14c --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb1cas.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1261mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1261-new"; + + reg-mode = ; + + dio3-as-tcxo-control; + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb2bas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb2bas.overlay new file mode 100644 index 0000000000000..c872ab7a3702a --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1261mb2bas.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb2xxs_common.dtsi" + +lora_semtech_sx1261mb2xxs: &lora_semtech_sx126xmb2xxs { + compatible = "semtech,sx1261-new"; + + reg-mode = ; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cas.overlay new file mode 100644 index 0000000000000..bff67445f685a --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cas.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1262mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1262-new"; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cbs.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cbs.overlay new file mode 100644 index 0000000000000..8b1618bd0bf80 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1cbs.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1262mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1262-new"; + + dio3-as-tcxo-control; + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1das.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1das.overlay new file mode 100644 index 0000000000000..8b1618bd0bf80 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1das.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1262mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1262-new"; + + dio3-as-tcxo-control; + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1pas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1pas.overlay new file mode 100644 index 0000000000000..8b1618bd0bf80 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb1pas.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1262mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1262-new"; + + dio3-as-tcxo-control; + tcxo-wakeup-time = <8>; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb2cas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb2cas.overlay new file mode 100644 index 0000000000000..291edf0c5e4e2 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1262mb2cas.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb2xxs_common.dtsi" + +lora_semtech_sx1262mb2xxs: &lora_semtech_sx126xmb2xxs { + compatible = "semtech,sx1262-new"; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx1268mb1gas.overlay b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1268mb1gas.overlay new file mode 100644 index 0000000000000..6119c500f4877 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx1268mb1gas.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "semtech_sx126xmb1xxs_common.dtsi" + +lora_semtech_sx1268mb1xxs: &lora_semtech_sx126xmb1xxs { + compatible = "semtech,sx1268-new"; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb1xxs_common.dtsi b/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb1xxs_common.dtsi new file mode 100644 index 0000000000000..526d70d46c152 --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb1xxs_common.dtsi @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + chosen { + zephyr,lorawan-transceiver = &lora_semtech_sx126xmb1xxs; + }; + aliases { + lora-transceiver = &lora_semtech_sx126xmb1xxs; + }; + leds { + lora_rx_led: sx126x_rx_led { + gpios = <&arduino_header 5 GPIO_ACTIVE_HIGH>; + label = "RX LED"; + }; + lora_tx_led: sx126x_tx_led { + gpios = <&arduino_header 4 GPIO_ACTIVE_HIGH>; + label = "TX LED"; + }; + }; +}; + +&arduino_spi { + status = "okay"; + + cs-gpios = <&arduino_header 13 GPIO_ACTIVE_LOW>; + + lora_semtech_sx126xmb1xxs: lora@0 { + reg = <0>; + spi-max-frequency = ; + + reset-gpios = <&arduino_header 0 GPIO_ACTIVE_LOW>; + + busy-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; + + dio1-gpios = <&arduino_header 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + dio2-as-rf-switch; + + reg-mode = ; + + tcxo-wakeup-time = <0>; + tcxo-voltage = ; + }; +}; diff --git a/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb2xxs_common.dtsi b/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb2xxs_common.dtsi new file mode 100644 index 0000000000000..6b1f51f9631bf --- /dev/null +++ b/boards/shields/semtech_sx126xmb2xxs/semtech_sx126xmb2xxs_common.dtsi @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + chosen { + zephyr,lorawan-transceiver = &lora_semtech_sx126xmb2xxs; + }; + aliases { + lora-transceiver = &lora_semtech_sx126xmb2xxs; + }; +}; + +&arduino_spi { + status = "okay"; + + cs-gpios = <&arduino_header 13 GPIO_ACTIVE_LOW>; + + lora_semtech_sx126xmb2xxs: lora@0 { + reg = <0>; + spi-max-frequency = ; + + reset-gpios = <&arduino_header 0 GPIO_ACTIVE_LOW>; + + busy-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; + + dio1-gpios = <&arduino_header 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + dio2-as-rf-switch; + + reg-mode = ; + + tcxo-wakeup-time = <0>; + tcxo-voltage = ; + }; +}; diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 2536e2011710c..102e9c1b9d10d 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -1,3 +1,4 @@ +# Copyright (c) 2021 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 # FIXME: SHADOW_VARS: Remove this once we have enabled -Wshadow globally. @@ -58,6 +59,7 @@ add_subdirectory_ifdef(CONFIG_KSCAN kscan) add_subdirectory_ifdef(CONFIG_LED led) add_subdirectory_ifdef(CONFIG_LED_STRIP led_strip) add_subdirectory_ifdef(CONFIG_LORA lora) +add_subdirectory_ifdef(CONFIG_LORA_BASICS_MODEM_DRIVERS lora_lbm) add_subdirectory_ifdef(CONFIG_MBOX mbox) add_subdirectory_ifdef(CONFIG_MDIO mdio) add_subdirectory_ifdef(CONFIG_MEMC memc) diff --git a/drivers/Kconfig b/drivers/Kconfig index 65f097f2acc97..515c2a47b4601 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -53,6 +53,7 @@ source "drivers/kscan/Kconfig" source "drivers/led/Kconfig" source "drivers/led_strip/Kconfig" source "drivers/lora/Kconfig" +source "drivers/lora_lbm/Kconfig" source "drivers/mbox/Kconfig" source "drivers/mdio/Kconfig" source "drivers/memc/Kconfig" diff --git a/drivers/lora_lbm/CMakeLists.txt b/drivers/lora_lbm/CMakeLists.txt new file mode 100644 index 0000000000000..7aa863054b9f8 --- /dev/null +++ b/drivers/lora_lbm/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +# Disable all warnings for Semtech code. +# +# Zephyr is compiled with a lot more warnings enabled then the basics modem. +# Since we copy the Basics Modem directly with no modifications, the warnings clog up +# the build output in our applications. +# zephyr_library_compile_options(-w) + +if(CONFIG_SEMTECH_LR11XX) + # Library flag that disables some warnings + zephyr_library_compile_definitions(LR11XX_DISABLE_WARNINGS) + + zephyr_library_sources(lr11xx/lr11xx_board.c lr11xx/lr11xx_hal.c) + + zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_DRIVERS_RAL_RALF + lr11xx/lr11xx_ral_bsp.c lr11xx/lr11xx_ral_bsp_calibration.c + ) +endif() + +if(CONFIG_SEMTECH_SX126X) + zephyr_library_sources(sx126x/sx126x_hal.c sx126x/sx126x_board.c) + + zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_DRIVERS_RAL_RALF + sx126x/sx126x_ral_bsp.c + ) +endif() diff --git a/drivers/lora_lbm/Kconfig b/drivers/lora_lbm/Kconfig new file mode 100644 index 0000000000000..66c36e33e9eef --- /dev/null +++ b/drivers/lora_lbm/Kconfig @@ -0,0 +1,85 @@ +# LoRa Basics Modem drivers configuration options +# +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +# NOTE: Might be renamed LORA_DRIVERS for upstreaming + +menuconfig LORA_BASICS_MODEM_DRIVERS + bool "LoRa drivers from the new LoRa Basics Modem stack [EXPERIMENTAL]" + select POLL + select EXPERIMENTAL + select ZEPHYR_LORA_BASICS_MODEM_MODULE + depends on !LORA + help + Include LoRa drivers from the new LoRa Basics Modem stack in the system configuration. + +if LORA_BASICS_MODEM_DRIVERS + +module = LORA_BASICS_MODEM_DRIVERS +module-str = lora-lbm +source "subsys/logging/Kconfig.template.log_config" + +rsource "Kconfig.lr11xx" +rsource "Kconfig.sx12xx" + +config LORA_BASICS_MODEM_DRIVERS_INIT_PRIORITY + int "Init priority" + default 50 + + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + bool + +choice + prompt "Event trigger mode" + default LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + help + Specify the type of triggering to be used by the LORA_BASICS_MODEM_DRIVERS driver. + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_NONE + bool "No trigger on event" + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + +endchoice + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_PRIORITY + int "Thread priority" + depends on LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_STACK_SIZE + int "Thread stack size" + depends on LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +config LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC + int "Time to wait on BUSY pin in ms before aborting" + default 600000 + help + Busy pin wait time in milliseconds. As WiFi and GPS scanning can take + seconds/minutes, the default is set to 10 minutes. + + +config LORA_BASICS_MODEM_DRIVERS_RAL_RALF + bool "LoRa Radio Abstraction Layer from the new LoRa Basics Modem stack" + default y + help + Include the Radio Abstraction Layer from the new LoRa Basics Modem stack + +# TODO: LORA_SHELL + +endif # LORA_BASICS_MODEM_DRIVERS diff --git a/drivers/lora_lbm/Kconfig.lr11xx b/drivers/lora_lbm/Kconfig.lr11xx new file mode 100644 index 0000000000000..d816b031ad4df --- /dev/null +++ b/drivers/lora_lbm/Kconfig.lr11xx @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +# LoRa transceiver drivers configuration options + +config SEMTECH_LR11XX + bool "Semtech LR11xx family LoRa transceiver driver" + default y + depends on DT_HAS_SEMTECH_LR1110_ENABLED || DT_HAS_SEMTECH_LR1120_ENABLED || DT_HAS_SEMTECH_LR1121_ENABLED + select SPI + select GPIO + help + Enable driver for the LR11xx family LoRa transceiver driver + + +if SEMTECH_LR11XX + +config LR11XX_USE_CRC_OVER_SPI + bool "Use CRC over SPI communication" + +endif # SEMTECH_LR11XX diff --git a/drivers/lora_lbm/Kconfig.sx12xx b/drivers/lora_lbm/Kconfig.sx12xx new file mode 100644 index 0000000000000..9d2effa871f0a --- /dev/null +++ b/drivers/lora_lbm/Kconfig.sx12xx @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +# LoRa transceiver drivers configuration options + +config SEMTECH_SX126X + bool "Semtech SX126x family LoRa transceiver driver" + default y + depends on DT_HAS_SEMTECH_SX1261_NEW_ENABLED || DT_HAS_SEMTECH_SX1262_NEW_ENABLED || DT_HAS_SEMTECH_SX1268_NEW_ENABLED || DT_HAS_ST_STM32WL_SUBGHZ_RADIO_ENABLED + select SPI + select GPIO + help + Enable driver for the SX126x family and stm32wl embedded LoRa transceiver driver diff --git a/drivers/lora_lbm/lr11xx/lr11xx_board.c b/drivers/lora_lbm/lr11xx/lr11xx_board.c new file mode 100644 index 0000000000000..fd8063c0a08a2 --- /dev/null +++ b/drivers/lora_lbm/lr11xx/lr11xx_board.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "lr11xx_hal_context.h" + +LOG_MODULE_REGISTER(lora_lr11xx, CONFIG_LORA_BASICS_MODEM_DRIVERS_LOG_LEVEL); + +#define LR11XX_SPI_OPERATION (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB) + +/** + * @brief Event pin callback handler. + * + * @param dev + * @param cb + * @param pins + */ +static void lr11xx_board_event_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct lr11xx_hal_context_data_t *data = + CONTAINER_OF(cb, struct lr11xx_hal_context_data_t, event_cb); + const struct lr11xx_hal_context_cfg_t *config = data->lr11xx_dev->config; + + if ((pins & BIT(config->event.pin)) == 0U) { + return; + } + + if (gpio_pin_get_dt(&config->event)) { + /* Wait for value to drop */ + gpio_pin_interrupt_configure_dt(&config->event, GPIO_INT_EDGE_TO_INACTIVE); + /* Call provided callback */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + k_sem_give(&data->gpio_sem); +#elif CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + k_work_submit(&data->work); +#endif + } else { + gpio_pin_interrupt_configure_dt(&config->event, GPIO_INT_EDGE_TO_ACTIVE); + } +} + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD +static void lr11xx_thread(struct lr11xx_hal_context_data_t *data) +{ + while (1) { + k_sem_take(&data->gpio_sem, K_FOREVER); + if (data->event_interrupt_cb) { + data->event_interrupt_cb(data->lr11xx_dev); + } + } +} +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD +static void lr11xx_work_cb(struct k_work *work) +{ + struct lr11xx_hal_context_data_t *data = + CONTAINER_OF(work, struct lr11xx_hal_context_data_t, work); + if (data->event_interrupt_cb) { + data->event_interrupt_cb(data->lr11xx_dev); + } +} +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD */ + +void lora_transceiver_board_attach_interrupt(const struct device *dev, event_cb_t cb) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + struct lr11xx_hal_context_data_t *data = dev->data; + + data->event_interrupt_cb = cb; +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +void lora_transceiver_board_enable_interrupt(const struct device *dev) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + gpio_pin_interrupt_configure_dt(&config->event, GPIO_INT_EDGE_TO_ACTIVE); +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +void lora_transceiver_board_disable_interrupt(const struct device *dev) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + gpio_pin_interrupt_configure_dt(&config->event, GPIO_INT_DISABLE); +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +uint32_t lora_transceiver_get_tcxo_startup_delay_ms(const struct device *dev) +{ + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + return config->tcxo_cfg.wakeup_time_ms; +} + +int32_t lora_transceiver_get_model(const struct device *dev) +{ + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + return config->chip_type; +} + +/** + * @brief Initialise lr11xx. + * Initialise all GPIOs and configure interrupt on event pin. + * + * @param dev + * @return int + */ +static int lr11xx_init(const struct device *dev) +{ + const struct lr11xx_hal_context_cfg_t *config = dev->config; + struct lr11xx_hal_context_data_t *data = dev->data; + int ret; + + /* Check the SPI device */ + if (!device_is_ready(config->spi.bus)) { + LOG_ERR("Could not find SPI device"); + return -EINVAL; + } + + /* Busy pin */ + ret = gpio_pin_configure_dt(&config->busy, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure busy gpio"); + return ret; + } + + /* Reset pin */ + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE); + if (ret < 0) { + LOG_ERR("Could not configure reset gpio"); + return ret; + } + + /* Event pin */ + ret = gpio_pin_configure_dt(&config->event, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure event gpio"); + return ret; + } + + data->lr11xx_dev = dev; + data->radio_status = RADIO_AWAKE; + data->tx_offset = config->tx_offset; + + /* Event pin trigger config */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + data->work.handler = lr11xx_work_cb; +#elif CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT); + + k_thread_create(&data->thread, data->thread_stack, + CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_STACK_SIZE, + (k_thread_entry_t)lr11xx_thread, data, NULL, NULL, + K_PRIO_COOP(CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_PRIORITY), + 0, K_NO_WAIT); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ + + /* Init callback */ + gpio_init_callback(&data->event_cb, lr11xx_board_event_callback, BIT(config->event.pin)); + /* Add callback */ + if (gpio_add_callback(config->event.port, &data->event_cb)) { + LOG_ERR("Could not set event pin callback"); + return -EIO; + } +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ + return ret; +} + +#if IS_ENABLED(CONFIG_PM_DEVICE) +/** + * @brief Power management action define. + * Not implemented as LoRa Basics Modem handles this on its side. + * + * @param dev + * @param action + * @return int + */ +static int lr11xx_pm_action(const struct device *dev, enum pm_device_action action) +{ + int ret = 0; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + /* Put the lr11xx into normal operation mode */ + break; + case PM_DEVICE_ACTION_SUSPEND: + /* Put the lr11xx into sleep mode */ + break; + default: + return -ENOTSUP; + } + + return ret; +} +#endif /* IS_ENABLED(CONFIG_PM_DEVICE) */ + +/* + * Device creation macro. + */ + +#define DT_PROP_BY_IDX_U8(node_id, prop, idx) ((uint8_t)DT_PROP_BY_IDX(node_id, prop, idx)) + +#define DT_TABLE_U8(node_id, prop) \ + {DT_FOREACH_PROP_ELEM_SEP(node_id, prop, DT_PROP_BY_IDX_U8, (, ))} + +#define CONFIGURE_GPIO_IF_IN_DT(node_id, name, dt_prop) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, dt_prop), \ + (.name = GPIO_DT_SPEC_GET(node_id, dt_prop),), ()) + +#define LR11XX_CHIP_TYPE(node_id) \ + COND_CODE_1(DT_NODE_HAS_COMPAT(node_id, semtech_lr1110), \ + (LR11XX_SYSTEM_VERSION_TYPE_LR1110), \ + (COND_CODE_1(DT_NODE_HAS_COMPAT(node_id, semtech_lr1120), \ + (LR11XX_SYSTEM_VERSION_TYPE_LR1120), \ + (COND_CODE_1(DT_NODE_HAS_COMPAT(node_id, semtech_lr1121), \ + (LR11XX_SYSTEM_VERSION_TYPE_LR1121), (/**/)))))) + +#define LR11XX_XOSC_CFG(node_id) \ + COND_CODE_1(DT_PROP(node_id, tcxo_wakeup_time) == 0, \ + (RAL_XOSC_CFG_XTAL), (RAL_XOSC_CFG_TCXO_RADIO_CTRL)) + +/* LR11xx does not support external TCXO control (by the MCU) */ +#define LR11XX_CFG_TCXO(node_id) \ + .tcxo_cfg = { \ + .xosc_cfg = LR11XX_XOSC_CFG(node_id), \ + .voltage = DT_PROP(node_id, tcxo_voltage), \ + .wakeup_time_ms = DT_PROP(node_id, tcxo_wakeup_time), \ + } + +#define LR11XX_CFG_LF_CLCK(node_id) \ + .lf_clck_cfg = { \ + .lf_clk_cfg = DT_PROP(node_id, lf_clk), \ + .wait_32k_ready = true, \ + } + +#define LR11XX_CFG_RF_SW(node_id) \ + .rf_switch_cfg = { \ + .enable = DT_PROP_OR(node_id, rf_sw_enable, 0), \ + .standby = DT_PROP_OR(node_id, rf_sw_standby_mode, 0), \ + .rx = DT_PROP_OR(node_id, rf_sw_rx_mode, 0), \ + .tx = DT_PROP_OR(node_id, rf_sw_tx_mode, 0), \ + .tx_hp = DT_PROP_OR(node_id, rf_sw_tx_hp_mode, 0), \ + .tx_hf = DT_PROP_OR(node_id, rf_sw_tx_hf_mode, 0), \ + .wifi = DT_PROP_OR(node_id, rf_sw_wifi_mode, 0), \ + .gnss = DT_PROP_OR(node_id, rf_sw_gnss_mode, 0), \ + } + +#define LR11XX_RSSI_CFG(node_id, range) \ + { \ + .gain_offset = DT_PROP(node_id, DT_CAT3(rssi_calibration_, range, _offset)), \ + .gain_tune = DT_TABLE_U8(node_id, DT_CAT3(rssi_calibration_, range, _tune)), \ + } + +#define LR11XX_CONFIG(node_id) \ + { \ + .spi = SPI_DT_SPEC_GET(node_id, LR11XX_SPI_OPERATION, 0), \ + .reset = GPIO_DT_SPEC_GET(node_id, reset_gpios), \ + .busy = GPIO_DT_SPEC_GET(node_id, busy_gpios), \ + .event = GPIO_DT_SPEC_GET(node_id, event_gpios), \ + .lf_tx_path_options = DT_PROP(node_id, lf_tx_path), \ + .chip_type = LR11XX_CHIP_TYPE(node_id), \ + LR11XX_CFG_TCXO(node_id), \ + LR11XX_CFG_LF_CLCK(node_id), \ + LR11XX_CFG_RF_SW(node_id), \ + .reg_mode = DT_PROP(node_id, reg_mode), \ + .rx_boosted = DT_PROP(node_id, rx_boosted), \ + .tx_offset = DT_PROP_OR(node_id, tx_offset, 0), \ + .pa_lf_lp_cfg_table = (lr11xx_pa_pwr_cfg_t *)DT_CAT(pa_lf_lp_cfg_table_, node_id), \ + .pa_lf_hp_cfg_table = (lr11xx_pa_pwr_cfg_t *)DT_CAT(pa_lf_hp_cfg_table_, node_id), \ + .pa_hf_cfg_table = (lr11xx_pa_pwr_cfg_t *)DT_CAT(pa_hf_cfg_table_, node_id), \ + .rssi_calibration_table_below_600mhz = LR11XX_RSSI_CFG(node_id, lf), \ + .rssi_calibration_table_from_600mhz_to_2ghz = LR11XX_RSSI_CFG(node_id, mf), \ + .rssi_calibration_table_above_2ghz = LR11XX_RSSI_CFG(node_id, hf), \ + } + +#define LR11XX_DEVICE_INIT(node_id) \ + DEVICE_DT_DEFINE(node_id, lr11xx_init, PM_DEVICE_DT_GET(node_id), &lr11xx_data_##node_id, \ + &lr11xx_config_##node_id, POST_KERNEL, \ + CONFIG_LORA_BASICS_MODEM_DRIVERS_INIT_PRIORITY, NULL); + +#define LR11XX_DEFINE(node_id) \ + static struct lr11xx_hal_context_data_t lr11xx_data_##node_id; \ + static uint8_t pa_lf_lp_cfg_table_##node_id[] = DT_TABLE_U8(node_id, tx_power_cfg_lf_lp); \ + static uint8_t pa_lf_hp_cfg_table_##node_id[] = DT_TABLE_U8(node_id, tx_power_cfg_lf_hp); \ + static uint8_t pa_hf_cfg_table_##node_id[] = DT_TABLE_U8(node_id, tx_power_cfg_hf); \ + static const struct lr11xx_hal_context_cfg_t lr11xx_config_##node_id = \ + LR11XX_CONFIG(node_id); \ + PM_DEVICE_DT_DEFINE(node_id, lr11xx_pm_action); \ + LR11XX_DEVICE_INIT(node_id) + +DT_FOREACH_STATUS_OKAY(semtech_lr1110, LR11XX_DEFINE) +DT_FOREACH_STATUS_OKAY(semtech_lr1120, LR11XX_DEFINE) +DT_FOREACH_STATUS_OKAY(semtech_lr1121, LR11XX_DEFINE) diff --git a/drivers/lora_lbm/lr11xx/lr11xx_hal.c b/drivers/lora_lbm/lr11xx/lr11xx_hal.c new file mode 100644 index 0000000000000..79dc856dd5ff1 --- /dev/null +++ b/drivers/lora_lbm/lr11xx/lr11xx_hal.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "lr11xx_hal_context.h" + +LOG_MODULE_DECLARE(lora_lr11xx, CONFIG_LORA_BASICS_MODEM_DRIVERS_LOG_LEVEL); + +/** + * @brief Wait until radio busy pin returns to inactive state or + * until CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC passes. + * + * @retval LR11XX_HAL_STATUS_OK + */ +static void lr11xx_hal_wait_on_busy(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + const bool timed_out = + WAIT_FOR(gpio_pin_get_dt(&config->busy) == 0, + (1000 * CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC), + k_usleep(100)); + if (!timed_out) { + LOG_ERR("Timeout of %dms hit when waiting for lr11xx busy!", + CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC); + k_oops(); + } +} + +/** + * @brief Check if device is ready to receive spi transaction. + * + * If the device is in sleep mode, it will awake it and then wait until it is ready + * + */ +static void lr11xx_hal_check_device_ready(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + struct lr11xx_hal_context_data_t *data = dev->data; + + if (data->radio_status != RADIO_SLEEP) { + lr11xx_hal_wait_on_busy(context); + } else { + /* Busy is HIGH in sleep mode, wake-up the device with a small glitch on NSS */ + const struct gpio_dt_spec *cs = &(config->spi.config.cs.gpio); + + gpio_pin_set_dt(cs, 1); + gpio_pin_set_dt(cs, 0); + lr11xx_hal_wait_on_busy(context); + data->radio_status = RADIO_AWAKE; + } +} + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command, + const uint16_t command_length, const uint8_t *data, + const uint16_t data_length) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + struct lr11xx_hal_context_data_t *dev_data = dev->data; + int ret; + +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + /* Compute the CRC over command array first and over data array then */ + uint8_t cmd_crc = lr11xx_hal_compute_crc(0xFF, command, command_length); + + cmd_crc = lr11xx_hal_compute_crc(cmd_crc, data, data_length); +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + + const struct spi_buf tx_buf[] = {{ + .buf = (uint8_t *)command, + .len = command_length, + }, + {.buf = (uint8_t *)data, .len = data_length +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + }, + {.buf = &cmd_crc, .len = 1 +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + }}; + + const struct spi_buf_set tx = {.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)}; + + lr11xx_hal_check_device_ready(context); + ret = spi_write_dt(&config->spi, &tx); + if (ret) { + return LR11XX_HAL_STATUS_ERROR; + } + + /* LR11XX_SYSTEM_SET_SLEEP_OC=0x011B opcode. + * In sleep mode the radio busy line is held at 1 => do not test it + */ + if ((command[0] == 0x01) && (command_length > 1) && (command[1] == 0x1B)) { + dev_data->radio_status = RADIO_SLEEP; + + /* Add a incompressible delay to prevent trying to wake the radio + * before it is full asleep + */ + k_sleep(K_USEC(500)); + } + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_direct_read(const void *context, uint8_t *data, + const uint16_t data_length) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + int ret; + +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + uint8_t rx_crc; +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + + const struct spi_buf rx_buf[] = { + {.buf = data, .len = data_length +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + }, + {/* read crc sent by lr11xx at the end of the transaction */ + .buf = &rx_crc, + .len = 1 +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + }}; + + const struct spi_buf_set rx = {.buffers = rx_buf, .count = ARRAY_SIZE(rx_buf)}; + + lr11xx_hal_check_device_ready(context); + ret = spi_read_dt(&config->spi, &rx); + if (ret) { + return LR11XX_HAL_STATUS_ERROR; + } + +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + /* check crc value */ + uint8_t computed_crc = lr11xx_hal_compute_crc(0xFF, data, data_length); + + if (rx_crc != computed_crc) { + return LR11XX_HAL_STATUS_ERROR; + } +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, uint8_t *data, + const uint16_t data_length) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + int ret; + +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + /* Compute the CRC over command array first and over data array then */ + uint8_t cmd_crc = lr11xx_hal_compute_crc(0xFF, command, command_length); +#endif + + /* When hal_read is called by lr11xx_crypto_restore_from_flash during LoRa initialization, + * we sleep for 1 ms so we don't get stuck in an endless wait loop + */ + if ((command[0] == 0x05) && (command[1] == 0x0B)) { + k_sleep(K_MSEC(1)); + } + + const struct spi_buf tx_buf[] = {{ + .buf = (uint8_t *)command, + .len = command_length, +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + }, + {.buf = &cmd_crc, .len = 1 +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + }}; + + const struct spi_buf_set tx = {.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)}; + + lr11xx_hal_check_device_ready(context); + ret = spi_write_dt(&config->spi, &tx); + if (ret) { + return LR11XX_HAL_STATUS_ERROR; + } + + if (data_length > 0) { + uint8_t dummy_byte; + + const struct spi_buf rx_buf[] = { + /* save dummy for crc calculation */ + { + .buf = &dummy_byte, + .len = 1, + }, + {.buf = data, .len = data_length +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + }, + {/* read crc sent by lr11xx at the end of the transaction */ + .buf = &cmd_crc, + .len = 1 +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + }}; + + const struct spi_buf_set rx = {.buffers = rx_buf, .count = ARRAY_SIZE(rx_buf)}; + + lr11xx_hal_check_device_ready(context); + ret = spi_read_dt(&config->spi, &rx); + if (ret) { + return LR11XX_HAL_STATUS_ERROR; + } + +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + /* Check CRC value */ + uint8_t computed_crc = lr11xx_hal_compute_crc(0xFF, &dummy_byte, 1); + + computed_crc = lr11xx_hal_compute_crc(computed_crc, data, data_length); + if (cmd_crc != computed_crc) { + return LR11XX_HAL_STATUS_ERROR; + } +#endif /* defined( CONFIG_LR11XX_USE_CRC_OVER_SPI ) */ + } + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_reset(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + struct lr11xx_hal_context_data_t *data = dev->data; + + gpio_pin_set_dt(&config->reset, 1); + k_sleep(K_MSEC(1)); + gpio_pin_set_dt(&config->reset, 0); + k_sleep(K_MSEC(1)); + + /* Wait 200ms until internal lr11xx fw is ready */ + k_sleep(K_MSEC(200)); + data->radio_status = RADIO_AWAKE; + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_wakeup(const void *context) +{ + lr11xx_hal_check_device_ready(context); + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_abort_blocking_cmd(const void *context) +{ + /* Send a dummy command to abort the ongoing command */ + uint8_t abort_cmd[1] = {0x00}; + + return lr11xx_hal_write(context, abort_cmd, sizeof(abort_cmd), NULL, 0); +} diff --git a/drivers/lora_lbm/lr11xx/lr11xx_hal_context.h b/drivers/lora_lbm/lr11xx/lr11xx_hal_context.h new file mode 100644 index 0000000000000..06a3773241f7e --- /dev/null +++ b/drivers/lora_lbm/lr11xx/lr11xx_hal_context.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LR11XX_HAL_CONTEXT_H +#define LR11XX_HAL_CONTEXT_H + +#include + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback upon firing event trigger + * + */ +typedef void (*event_cb_t)(const struct device *dev); + +struct lr11xx_hal_context_tcxo_cfg_t { + ral_xosc_cfg_t xosc_cfg; + lr11xx_system_tcxo_supply_voltage_t voltage; + uint32_t wakeup_time_ms; +}; + +struct lr11xx_hal_context_lf_clck_cfg_t { + lr11xx_system_lfclk_cfg_t lf_clk_cfg; + bool wait_32k_ready; +}; + +typedef struct lr11xx_pa_pwr_cfg_s { + int8_t power; + uint8_t pa_duty_cycle; + uint8_t pa_hp_sel; +} lr11xx_pa_pwr_cfg_t; + +/** + * @brief lr11xx context device config structure + * + */ +struct lr11xx_hal_context_cfg_t { + struct spi_dt_spec spi; /* spi peripheral */ + + struct gpio_dt_spec reset; /* reset pin */ + struct gpio_dt_spec busy; /* busy pin */ + struct gpio_dt_spec event; /* event pin */ + + lr11xx_system_version_type_t chip_type; /* Which configured chip type in device tree */ + + uint8_t lf_tx_path_options; /* LF tx path options */ + + struct lr11xx_hal_context_tcxo_cfg_t tcxo_cfg; /* TCXO/XTAL options*/ + struct lr11xx_hal_context_lf_clck_cfg_t lf_clck_cfg; /* LF Clock options */ + lr11xx_system_rfswitch_cfg_t rf_switch_cfg; /* RF Switches options*/ + lr11xx_system_reg_mode_t reg_mode; /* Regulator mode */ + + bool rx_boosted; /* RXBoosted option */ + + uint8_t tx_offset; /* Board TX power offset */ + /* Calibration tables are stored as uint8_t because + * device tree provides them as flat arrays + */ + /* Power amplifier configuration for Low frequency / Low power */ + lr11xx_pa_pwr_cfg_t *pa_lf_lp_cfg_table; + /* Power amplifier configuration for Low frequency / High power */ + lr11xx_pa_pwr_cfg_t *pa_lf_hp_cfg_table; + /* Power amplifier configuration for High frequency */ + lr11xx_pa_pwr_cfg_t *pa_hf_cfg_table; + lr11xx_radio_rssi_calibration_table_t rssi_calibration_table_below_600mhz; + lr11xx_radio_rssi_calibration_table_t rssi_calibration_table_from_600mhz_to_2ghz; + lr11xx_radio_rssi_calibration_table_t rssi_calibration_table_above_2ghz; +}; + +/* This type holds the current sleep status of the radio */ +typedef enum { + RADIO_SLEEP, + RADIO_AWAKE +} radio_sleep_status_t; + +struct lr11xx_hal_context_data_t { +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct device *lr11xx_dev; + struct gpio_callback event_cb; /* event callback structure */ + event_cb_t event_interrupt_cb; /* event interrupt user provided callback */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + K_THREAD_STACK_MEMBER(thread_stack, + CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem trig_sem; +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ + radio_sleep_status_t radio_status; + uint8_t tx_offset; /* Board TX power offset */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LR11XX_HAL_CONTEXT_H */ diff --git a/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp.c b/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp.c new file mode 100644 index 0000000000000..13d01f623ab23 --- /dev/null +++ b/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include +#include "lr11xx_hal_context.h" + +LOG_MODULE_DECLARE(lora_lr11xx, CONFIG_LORA_BASICS_MODEM_DRIVERS_LOG_LEVEL); + +void ral_lr11xx_bsp_get_rf_switch_cfg(const void *context, + lr11xx_system_rfswitch_cfg_t *rf_switch_cfg) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + rf_switch_cfg->enable = config->rf_switch_cfg.enable; + rf_switch_cfg->standby = 0; + rf_switch_cfg->rx = config->rf_switch_cfg.rx; + rf_switch_cfg->tx = config->rf_switch_cfg.tx; + rf_switch_cfg->tx_hp = config->rf_switch_cfg.tx_hp; + rf_switch_cfg->tx_hf = config->rf_switch_cfg.tx_hf; + rf_switch_cfg->gnss = config->rf_switch_cfg.gnss; + rf_switch_cfg->wifi = config->rf_switch_cfg.wifi; +} + +void ral_lr11xx_bsp_get_reg_mode(const void *context, lr11xx_system_reg_mode_t *reg_mode) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + *reg_mode = config->reg_mode; +} + +void ral_lr11xx_bsp_get_xosc_cfg(const void *context, ral_xosc_cfg_t *xosc_cfg, + lr11xx_system_tcxo_supply_voltage_t *supply_voltage, + uint32_t *startup_time_in_tick) +{ + const struct device *transceiver = context; + const struct lr11xx_hal_context_cfg_t *config = transceiver->config; + const struct lr11xx_hal_context_tcxo_cfg_t tcxo_cfg = config->tcxo_cfg; + + *xosc_cfg = tcxo_cfg.xosc_cfg; + *supply_voltage = tcxo_cfg.voltage; + *startup_time_in_tick = + lr11xx_radio_convert_time_in_ms_to_rtc_step(tcxo_cfg.wakeup_time_ms); +} + +void ral_lr11xx_bsp_get_crc_state(const void *context, bool *crc_is_activated) +{ +#if defined(CONFIG_LR11XX_USE_CRC_OVER_SPI) + LOG_DBG("LR11XX CRC over spi is activated"); + *crc_is_activated = true; +#else + *crc_is_activated = false; +#endif +} + +void ral_lr11xx_bsp_get_lora_cad_det_peak(const void *context, ral_lora_sf_t sf, ral_lora_bw_t bw, + ral_lora_cad_symbs_t nb_symbol, + uint8_t *in_out_cad_det_peak) +{ + /* Function used to fine tune the cad detection peak, update if needed */ +} + +void ral_lr11xx_bsp_get_rx_boost_cfg(const void *context, bool *rx_boost_is_activated) +{ + const struct device *transceiver = context; + const struct lr11xx_hal_context_cfg_t *config = transceiver->config; + *rx_boost_is_activated = config->rx_boosted; +} + +void ral_lr11xx_bsp_get_lfclk_cfg_in_sleep(const void *context, bool *lfclk_is_running) +{ +#if defined(CONFIG_LORA_BASICS_MODEM_GEOLOCATION) + *lfclk_is_running = true; +#else + *lfclk_is_running = false; +#endif +} + +void radio_utilities_set_tx_power_offset(const void *context, uint8_t tx_pwr_offset_db) +{ + const struct device *dev = (const struct device *)context; + struct lr11xx_hal_context_data_t *data = dev->data; + + data->tx_offset = tx_pwr_offset_db; +} + +uint8_t radio_utilities_get_tx_power_offset(const void *context) +{ + const struct device *dev = (const struct device *)context; + struct lr11xx_hal_context_data_t *data = dev->data; + + return data->tx_offset; +} + +/* TODO: check values */ +#define LR11XX_GFSK_RX_CONSUMPTION_DCDC 5400 +#define LR11XX_GFSK_RX_BOOSTED_CONSUMPTION_DCDC 7500 + +#define LR11XX_GFSK_RX_CONSUMPTION_LDO 5400 +#define LR11XX_GFSK_RX_BOOSTED_CONSUMPTION_LDO 7500 + +#define LR11XX_LORA_RX_CONSUMPTION_DCDC 5700 +#define LR11XX_LORA_RX_BOOSTED_CONSUMPTION_DCDC 7800 + +#define LR11XX_LORA_RX_CONSUMPTION_LDO 5700 +#define LR11XX_LORA_RX_BOOSTED_CONSUMPTION_LDO 7800 + +#define LR11XX_LP_MIN_OUTPUT_POWER -17 +#define LR11XX_LP_MAX_OUTPUT_POWER 15 + +#define LR11XX_HP_MIN_OUTPUT_POWER -9 +#define LR11XX_HP_MAX_OUTPUT_POWER 22 + +#define LR11XX_HF_MIN_OUTPUT_POWER -18 +#define LR11XX_HF_MAX_OUTPUT_POWER 13 + +#define LR11XX_LP_CONVERT_TABLE_INDEX_OFFSET 17 +#define LR11XX_HP_CONVERT_TABLE_INDEX_OFFSET 9 +#define LR11XX_HF_CONVERT_TABLE_INDEX_OFFSET 18 + +#define LR11XX_PWR_VREG_VBAT_SWITCH 8 + +static const uint32_t ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_lp_vreg[] = { + 10820, /* -17 dBm */ + 10980, /* -16 dBm */ + 11060, /* -15 dBm */ + 11160, /* -14 dBm */ + 11300, /* -13 dBm */ + 11430, /* -12 dBm */ + 11550, /* -11 dBm */ + 11680, /* -10 dBm */ + 11930, /* -9 dBm */ + 12170, /* -8 dBm */ + 12420, /* -7 dBm */ + 12650, /* -6 dBm */ + 12900, /* -5 dBm */ + 13280, /* -4 dBm */ + 13600, /* -3 dBm */ + 14120, /* -2 dBm */ + 14600, /* -1 dBm */ + 15090, /* 0 dBm */ + 15780, /* 1 dBm */ + 16490, /* 2 dBm */ + 17250, /* 3 dBm */ + 17850, /* 4 dBm */ + 18720, /* 5 dBm */ + 19640, /* 6 dBm */ + 20560, /* 7 dBm */ + 21400, /* 8 dBm */ + 22620, /* 9 dBm */ + 23720, /* 10 dBm */ + 25050, /* 11 dBm */ + 26350, /* 12 dBm */ + 27870, /* 13 dBm */ + 28590, /* 14 dBm */ + 37820, /* 15 dBm */ +}; + +static const uint32_t ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_ldo_lp_vreg[] = { + 14950, /* -17 dBm */ + 15280, /* -16 dBm */ + 15530, /* -15 dBm */ + 15770, /* -14 dBm */ + 16020, /* -13 dBm */ + 16290, /* -12 dBm */ + 16550, /* -11 dBm */ + 16760, /* -10 dBm */ + 17280, /* -9 dBm */ + 17770, /* -8 dBm */ + 18250, /* -7 dBm */ + 18750, /* -6 dBm */ + 19250, /* -5 dBm */ + 19960, /* -4 dBm */ + 20710, /* -3 dBm */ + 21620, /* -2 dBm */ + 22570, /* -1 dBm */ + 23570, /* 0 dBm */ + 24990, /* 1 dBm */ + 26320, /* 2 dBm */ + 27830, /* 3 dBm */ + 29070, /* 4 dBm */ + 30660, /* 5 dBm */ + 32490, /* 6 dBm */ + 34220, /* 7 dBm */ + 35820, /* 8 dBm */ + 38180, /* 9 dBm */ + 40220, /* 10 dBm */ + 42800, /* 11 dBm */ + 45030, /* 12 dBm */ + 47900, /* 13 dBm */ + 51220, /* 14 dBm */ + 66060, /* 15 dBm */ +}; + +static const uint32_t ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_hp_vbat[] = { + 27750, /* -9 dBm */ + 29100, /* -8 dBm */ + 30320, /* -7 dBm */ + 31650, /* -6 dBm */ + 34250, /* -5 dBm */ + 35550, /* -4 dBm */ + 36770, /* -3 dBm */ + 39250, /* -2 dBm */ + 41480, /* -1 dBm */ + 43820, /* 0 dBm */ + 46000, /* 1 dBm */ + 49020, /* 2 dBm */ + 50900, /* 3 dBm */ + 54200, /* 4 dBm */ + 56330, /* 5 dBm */ + 59050, /* 6 dBm */ + 62210, /* 7 dBm */ + 65270, /* 8 dBm */ + 68600, /* 9 dBm */ + 71920, /* 10 dBm */ + 75500, /* 11 dBm */ + 79500, /* 12 dBm */ + 84130, /* 13 dBm */ + 88470, /* 14 dBm */ + 92200, /* 15 dBm */ + 94340, /* 16 dBm */ + 96360, /* 17 dBm */ + 98970, /* 18 dBm */ + 102220, /* 19 dBm */ + 106250, /* 20 dBm */ + 111300, /* 21 dBm */ + 113040, /* 22 dBm */ +}; + +static const uint32_t ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_ldo_hp_vbat[] = { + 31310, /* -9 dBm */ + 32700, /* -8 dBm */ + 33970, /* -7 dBm */ + 35270, /* -6 dBm */ + 37900, /* -5 dBm */ + 39140, /* -4 dBm */ + 40380, /* -3 dBm */ + 42860, /* -2 dBm */ + 45150, /* -1 dBm */ + 47400, /* 0 dBm */ + 49600, /* 1 dBm */ + 52600, /* 2 dBm */ + 54460, /* 3 dBm */ + 57690, /* 4 dBm */ + 59840, /* 5 dBm */ + 62550, /* 6 dBm */ + 65750, /* 7 dBm */ + 68520, /* 8 dBm */ + 72130, /* 9 dBm */ + 75230, /* 10 dBm */ + 78600, /* 11 dBm */ + 82770, /* 12 dBm */ + 87450, /* 13 dBm */ + 91700, /* 14 dBm */ + 95330, /* 15 dBm */ + 97520, /* 16 dBm */ + 99520, /* 17 dBm */ + 102080, /* 18 dBm */ + 105140, /* 19 dBm */ + 109300, /* 20 dBm */ + 114460, /* 21 dBm */ + 116530, /* 22 dBm */ +}; + +static const uint32_t ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_hf_vreg[] = { + 11800, /* -18 dBm */ + 11800, /* -17 dBm */ + 11800, /* -16 dBm */ + 11900, /* -15 dBm */ + 12020, /* -14 dBm */ + 12120, /* -13 dBm */ + 12230, /* -12 dBm */ + 12390, /* -11 dBm */ + 12540, /* -10 dBm */ + 12740, /* -9 dBm */ + 12960, /* -8 dBm */ + 13150, /* -7 dBm */ + 13460, /* -6 dBm */ + 13770, /* -5 dBm */ + 14070, /* -4 dBm */ + 14460, /* -3 dBm */ + 15030, /* -2 dBm */ + 15440, /* -1 dBm */ + 16030, /* 0 dBm */ + 16980, /* 1 dBm */ + 17590, /* 2 dBm */ + 18270, /* 3 dBm */ + 19060, /* 4 dBm */ + 19900, /* 5 dBm */ + 20740, /* 6 dBm */ + 21610, /* 7 dBm */ + 22400, /* 8 dBm */ + 23370, /* 9 dBm */ + 24860, /* 10 dBm */ + 26410, /* 11 dBm */ + 26430, /* 12 dBm */ + 27890, /* 13 dBm */ +}; + +__weak ral_status_t ral_lr11xx_bsp_get_instantaneous_tx_power_consumption( + const void *context, const ral_lr11xx_bsp_tx_cfg_output_params_t *tx_cfg, + lr11xx_system_reg_mode_t radio_reg_mode, uint32_t *pwr_consumption_in_ua) +{ + if (tx_cfg->pa_cfg.pa_sel == LR11XX_RADIO_PA_SEL_LP) { + if (tx_cfg->pa_cfg.pa_reg_supply == LR11XX_RADIO_PA_REG_SUPPLY_VREG) { + uint8_t index = 0; + + if (tx_cfg->chip_output_pwr_in_dbm_expected > LR11XX_LP_MAX_OUTPUT_POWER) { + index = LR11XX_LP_MAX_OUTPUT_POWER + + LR11XX_LP_CONVERT_TABLE_INDEX_OFFSET; + } else if (tx_cfg->chip_output_pwr_in_dbm_expected < + LR11XX_LP_MIN_OUTPUT_POWER) { + index = LR11XX_LP_MIN_OUTPUT_POWER + + LR11XX_LP_CONVERT_TABLE_INDEX_OFFSET; + } else { + index = tx_cfg->chip_output_pwr_in_dbm_expected + + LR11XX_LP_CONVERT_TABLE_INDEX_OFFSET; + } + + if (radio_reg_mode == LR11XX_SYSTEM_REG_MODE_DCDC) { + *pwr_consumption_in_ua = + ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_lp_vreg + [index]; + } else { + *pwr_consumption_in_ua = + ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_ldo_lp_vreg[index]; + } + } else { + return RAL_STATUS_UNSUPPORTED_FEATURE; + } + } else if (tx_cfg->pa_cfg.pa_sel == LR11XX_RADIO_PA_SEL_HP) { + if (tx_cfg->pa_cfg.pa_reg_supply == LR11XX_RADIO_PA_REG_SUPPLY_VBAT) { + uint8_t index = 0; + + if (tx_cfg->chip_output_pwr_in_dbm_expected > LR11XX_HP_MAX_OUTPUT_POWER) { + index = LR11XX_HP_MAX_OUTPUT_POWER + + LR11XX_HP_CONVERT_TABLE_INDEX_OFFSET; + } else if (tx_cfg->chip_output_pwr_in_dbm_expected < + LR11XX_HP_MIN_OUTPUT_POWER) { + index = LR11XX_HP_MIN_OUTPUT_POWER + + LR11XX_HP_CONVERT_TABLE_INDEX_OFFSET; + } else { + index = tx_cfg->chip_output_pwr_in_dbm_expected + + LR11XX_HP_CONVERT_TABLE_INDEX_OFFSET; + } + + if (radio_reg_mode == LR11XX_SYSTEM_REG_MODE_DCDC) { + *pwr_consumption_in_ua = + ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_hp_vbat + [index]; + } else { + *pwr_consumption_in_ua = + ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_ldo_hp_vbat[index]; + } + } else { + return RAL_STATUS_UNSUPPORTED_FEATURE; + } + } else if (tx_cfg->pa_cfg.pa_sel == LR11XX_RADIO_PA_SEL_HF) { + if (tx_cfg->pa_cfg.pa_reg_supply == LR11XX_RADIO_PA_REG_SUPPLY_VREG) { + uint8_t index = 0; + + if (tx_cfg->chip_output_pwr_in_dbm_expected > LR11XX_HF_MAX_OUTPUT_POWER) { + index = LR11XX_HF_MAX_OUTPUT_POWER + + LR11XX_HF_CONVERT_TABLE_INDEX_OFFSET; + } else if (tx_cfg->chip_output_pwr_in_dbm_expected < + LR11XX_HF_MIN_OUTPUT_POWER) { + index = LR11XX_HF_MIN_OUTPUT_POWER + + LR11XX_HF_CONVERT_TABLE_INDEX_OFFSET; + } else { + index = tx_cfg->chip_output_pwr_in_dbm_expected + + LR11XX_HF_CONVERT_TABLE_INDEX_OFFSET; + } + + if (radio_reg_mode == LR11XX_SYSTEM_REG_MODE_DCDC) { + *pwr_consumption_in_ua = + ral_lr11xx_convert_tx_dbm_to_ua_reg_mode_dcdc_hf_vreg + [index]; + } else { + return RAL_STATUS_UNSUPPORTED_FEATURE; + } + } else { + return RAL_STATUS_UNSUPPORTED_FEATURE; + } + } else { + return RAL_STATUS_UNKNOWN_VALUE; + } + + return RAL_STATUS_OK; +} + +__weak ral_status_t ral_lr11xx_bsp_get_instantaneous_gfsk_rx_power_consumption( + const void *context, lr11xx_system_reg_mode_t radio_reg_mode, bool rx_boosted, + uint32_t *pwr_consumption_in_ua) +{ + if (radio_reg_mode == LR11XX_SYSTEM_REG_MODE_DCDC) { + *pwr_consumption_in_ua = rx_boosted ? LR11XX_GFSK_RX_BOOSTED_CONSUMPTION_DCDC + : LR11XX_GFSK_RX_CONSUMPTION_DCDC; + } else { + /* TODO: find the good values */ + *pwr_consumption_in_ua = rx_boosted ? LR11XX_GFSK_RX_BOOSTED_CONSUMPTION_LDO + : LR11XX_GFSK_RX_CONSUMPTION_LDO; + } + + return RAL_STATUS_OK; +} + +__weak ral_status_t ral_lr11xx_bsp_get_instantaneous_lora_rx_power_consumption( + const void *context, lr11xx_system_reg_mode_t radio_reg_mode, bool rx_boosted, + uint32_t *pwr_consumption_in_ua) +{ + if (radio_reg_mode == LR11XX_SYSTEM_REG_MODE_DCDC) { + *pwr_consumption_in_ua = rx_boosted ? LR11XX_LORA_RX_BOOSTED_CONSUMPTION_DCDC + : LR11XX_LORA_RX_CONSUMPTION_DCDC; + } else { + /* TODO: find the good values */ + *pwr_consumption_in_ua = rx_boosted ? LR11XX_LORA_RX_BOOSTED_CONSUMPTION_LDO + : LR11XX_LORA_RX_CONSUMPTION_LDO; + } + + return RAL_STATUS_OK; +} diff --git a/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp_calibration.c b/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp_calibration.c new file mode 100644 index 0000000000000..490e95e41d4f6 --- /dev/null +++ b/drivers/lora_lbm/lr11xx/lr11xx_ral_bsp_calibration.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include "lr11xx_hal_context.h" + +typedef enum lr11xx_pa_type_s { + LR11XX_WITH_LF_LP_PA, + LR11XX_WITH_LF_HP_PA, + LR11XX_WITH_LF_LP_HP_PA, + LR11XX_WITH_HF_PA, +} lr11xx_pa_type_t; + +#define LR11XX_PWR_VREG_VBAT_SWITCH 8 + +#define LR11XX_MIN_PWR_LP_LF -17 +#define LR11XX_MAX_PWR_LP_LF 15 + +#define LR11XX_MIN_PWR_HP_LF -9 +#define LR11XX_MAX_PWR_HP_LF 22 + +#define LR11XX_MIN_PWR_PA_HF -18 +#define LR11XX_MAX_PWR_PA_HF 13 + +#define LR11XX_RSSI_CALIBRATION_TUNE_LENGTH 17 + +static void lr11xx_get_tx_cfg(const void *context, lr11xx_pa_type_t pa_type, + int8_t expected_output_pwr_in_dbm, + ral_lr11xx_bsp_tx_cfg_output_params_t *output_params) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + int8_t power = expected_output_pwr_in_dbm; + + /* Ramp time is the same for any config */ + output_params->pa_ramp_time = LR11XX_RADIO_RAMP_48_US; + + switch (pa_type) { + case LR11XX_WITH_LF_LP_PA: { + /* Check power boundaries for LP LF PA: + * The output power must be in range [ -17 , +15 ] dBm + */ + power = CLAMP(power, LR11XX_MIN_PWR_LP_LF, LR11XX_MAX_PWR_LP_LF); + lr11xx_pa_pwr_cfg_t *pwr_cfg = + &config->pa_lf_lp_cfg_table[power - LR11XX_MIN_PWR_LP_LF]; + + output_params->pa_cfg.pa_sel = LR11XX_RADIO_PA_SEL_LP; + output_params->pa_cfg.pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG; + output_params->pa_cfg.pa_duty_cycle = pwr_cfg->pa_duty_cycle; + output_params->pa_cfg.pa_hp_sel = pwr_cfg->pa_hp_sel; + output_params->chip_output_pwr_in_dbm_configured = pwr_cfg->power; + output_params->chip_output_pwr_in_dbm_expected = power; + break; + } + case LR11XX_WITH_LF_HP_PA: { + /* Check power boundaries for HP LF PA: + * The output power must be in range [ -9 , +22 ] dBm + */ + power = CLAMP(power, LR11XX_MIN_PWR_HP_LF, LR11XX_MAX_PWR_HP_LF); + lr11xx_pa_pwr_cfg_t *pwr_cfg = + &config->pa_lf_hp_cfg_table[power - LR11XX_MIN_PWR_HP_LF]; + + output_params->pa_cfg.pa_sel = LR11XX_RADIO_PA_SEL_HP; + + /* For powers below 8dBm use regulated supply for + * HP PA for a better efficiency. + */ + output_params->pa_cfg.pa_reg_supply = (power <= LR11XX_PWR_VREG_VBAT_SWITCH) + ? LR11XX_RADIO_PA_REG_SUPPLY_VREG + : LR11XX_RADIO_PA_REG_SUPPLY_VBAT; + output_params->pa_cfg.pa_duty_cycle = pwr_cfg->pa_duty_cycle; + output_params->pa_cfg.pa_hp_sel = pwr_cfg->pa_hp_sel; + output_params->chip_output_pwr_in_dbm_configured = pwr_cfg->power; + output_params->chip_output_pwr_in_dbm_expected = power; + break; + } + case LR11XX_WITH_LF_LP_HP_PA: { + /* Check power boundaries for LP/HP LF PA: + * The output power must be in range [ -17 , +22 ] dBm + */ + power = CLAMP(power, LR11XX_MIN_PWR_LP_LF, LR11XX_MAX_PWR_HP_LF); + + if (power <= LR11XX_MAX_PWR_LP_LF) { + lr11xx_pa_pwr_cfg_t *pwr_cfg = + &config->pa_lf_lp_cfg_table[power - LR11XX_MIN_PWR_LP_LF]; + + output_params->chip_output_pwr_in_dbm_expected = power; + output_params->pa_cfg.pa_sel = LR11XX_RADIO_PA_SEL_LP; + output_params->pa_cfg.pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG; + output_params->pa_cfg.pa_duty_cycle = pwr_cfg->pa_duty_cycle; + output_params->pa_cfg.pa_hp_sel = pwr_cfg->pa_hp_sel; + output_params->chip_output_pwr_in_dbm_configured = pwr_cfg->power; + } else { + lr11xx_pa_pwr_cfg_t *pwr_cfg = + &config->pa_lf_hp_cfg_table[power - LR11XX_MIN_PWR_HP_LF]; + + output_params->chip_output_pwr_in_dbm_expected = power; + output_params->pa_cfg.pa_sel = LR11XX_RADIO_PA_SEL_HP; + output_params->pa_cfg.pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT; + output_params->pa_cfg.pa_duty_cycle = pwr_cfg->pa_duty_cycle; + output_params->pa_cfg.pa_hp_sel = pwr_cfg->pa_hp_sel; + output_params->chip_output_pwr_in_dbm_configured = pwr_cfg->power; + } + break; + } + case LR11XX_WITH_HF_PA: { + /* Check power boundaries for HF PA: + * The output power must be in range [ -18 , +13 ] dBm + */ + power = CLAMP(power, LR11XX_MIN_PWR_PA_HF, LR11XX_MAX_PWR_PA_HF); + lr11xx_pa_pwr_cfg_t *pwr_cfg = + &config->pa_hf_cfg_table[power - LR11XX_MIN_PWR_PA_HF]; + + output_params->pa_cfg.pa_sel = LR11XX_RADIO_PA_SEL_HF; + output_params->pa_cfg.pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG; + output_params->pa_cfg.pa_duty_cycle = pwr_cfg->pa_duty_cycle; + output_params->pa_cfg.pa_hp_sel = pwr_cfg->pa_hp_sel; + output_params->chip_output_pwr_in_dbm_configured = pwr_cfg->power; + output_params->chip_output_pwr_in_dbm_expected = power; + break; + } + } +} + +void ral_lr11xx_bsp_get_tx_cfg(const void *context, + const ral_lr11xx_bsp_tx_cfg_input_params_t *input_params, + ral_lr11xx_bsp_tx_cfg_output_params_t *output_params) +{ + /* get board tx power offset */ + int8_t board_tx_pwr_offset_db = radio_utilities_get_tx_power_offset(context); + + int16_t power = input_params->system_output_pwr_in_dbm + board_tx_pwr_offset_db; + + lr11xx_pa_type_t pa_type; + + /* check frequency band first to choose LF of HF PA */ + if (input_params->freq_in_hz >= 2400000000) { + pa_type = LR11XX_WITH_HF_PA; + } else { + /* Modem is acting in subgig band: use LP/HP PA + * (both LP and HP are connected on lr11xx evk board) + */ + pa_type = LR11XX_WITH_LF_LP_HP_PA; + } + + /* call the configuration function */ + lr11xx_get_tx_cfg(context, pa_type, power, output_params); +} + +void ral_lr11xx_bsp_get_rssi_calibration_table( + const void *context, const uint32_t freq_in_hz, + lr11xx_radio_rssi_calibration_table_t *rssi_calibration_table) +{ + const struct device *dev = (const struct device *)context; + const struct lr11xx_hal_context_cfg_t *config = dev->config; + + if (freq_in_hz <= 600000000) { + *rssi_calibration_table = config->rssi_calibration_table_below_600mhz; + } else if ((600000000 <= freq_in_hz) && (freq_in_hz <= 2000000000)) { + *rssi_calibration_table = config->rssi_calibration_table_from_600mhz_to_2ghz; + } else { + /* freq_in_hz > 2000000000 */ + *rssi_calibration_table = config->rssi_calibration_table_above_2ghz; + } +} diff --git a/drivers/lora_lbm/sx126x/sx126x_board.c b/drivers/lora_lbm/sx126x/sx126x_board.c new file mode 100644 index 0000000000000..f3aec00eaf8c1 --- /dev/null +++ b/drivers/lora_lbm/sx126x/sx126x_board.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include "sx126x_hal_context.h" + +LOG_MODULE_REGISTER(lora_sx126x, CONFIG_LORA_BASICS_MODEM_DRIVERS_LOG_LEVEL); + +#define SX126X_SPI_OPERATION (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB) + +/** + * @brief Event pin callback handler. + * + * @param dev + * @param cb + * @param pins + */ +static void sx126x_board_event_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins, struct sx126x_hal_context_data_t *data) +{ + /* This code expects to always use EDGE interrupt triggers + * (so no possible duplicate triggers) + */ + + /* Call provided callback */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + k_sem_give(&data->gpio_sem); +#elif CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + k_work_submit(&data->work); +#endif +} + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER +static void sx126x_board_dio1_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct sx126x_hal_context_data_t *data = + CONTAINER_OF(cb, struct sx126x_hal_context_data_t, dio1_cb); + + return sx126x_board_event_callback(dev, cb, pins, data); +} +static void sx126x_board_dio2_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct sx126x_hal_context_data_t *data = + CONTAINER_OF(cb, struct sx126x_hal_context_data_t, dio2_cb); + + return sx126x_board_event_callback(dev, cb, pins, data); +} +static void sx126x_board_dio3_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct sx126x_hal_context_data_t *data = + CONTAINER_OF(cb, struct sx126x_hal_context_data_t, dio3_cb); + + return sx126x_board_event_callback(dev, cb, pins, data); +} +#endif + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD +static void sx126x_thread(struct sx126x_hal_context_data_t *data) +{ + while (1) { + k_sem_take(&data->gpio_sem, K_FOREVER); + if (data->event_interrupt_cb) { + data->event_interrupt_cb(data->sx126x_dev); + } + } +} +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD +static void sx126x_work_cb(struct k_work *work) +{ + struct sx126x_hal_context_data_t *data = + CONTAINER_OF(work, struct sx126x_hal_context_data_t, work); + if (data->event_interrupt_cb) { + data->event_interrupt_cb(data->sx126x_dev); + } +} +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD */ + +void lora_transceiver_board_attach_interrupt(const struct device *dev, event_cb_t cb) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + struct sx126x_hal_context_data_t *data = dev->data; + + data->event_interrupt_cb = cb; +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +void lora_transceiver_board_enable_interrupt(const struct device *dev) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct sx126x_hal_context_cfg_t *config = dev->config; + + if (config->dio1.port) { + gpio_pin_interrupt_configure_dt(&config->dio1, GPIO_INT_EDGE_TO_ACTIVE); + } + if (config->dio2.port) { + gpio_pin_interrupt_configure_dt(&config->dio2, GPIO_INT_EDGE_TO_ACTIVE); + } + if (config->dio3.port) { + gpio_pin_interrupt_configure_dt(&config->dio3, GPIO_INT_EDGE_TO_ACTIVE); + } +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +void lora_transceiver_board_disable_interrupt(const struct device *dev) +{ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct sx126x_hal_context_cfg_t *config = dev->config; + + if (config->dio1.port) { + gpio_pin_interrupt_configure_dt(&config->dio1, GPIO_INT_DISABLE); + } + if (config->dio2.port) { + gpio_pin_interrupt_configure_dt(&config->dio2, GPIO_INT_DISABLE); + } + if (config->dio3.port) { + gpio_pin_interrupt_configure_dt(&config->dio3, GPIO_INT_DISABLE); + } +#else + LOG_ERR("Event trigger not supported!"); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ +} + +uint32_t lora_transceiver_get_tcxo_startup_delay_ms(const struct device *dev) +{ + const struct sx126x_hal_context_cfg_t *config = dev->config; + + return config->tcxo_cfg.wakeup_time_ms; +} + +static int sx126x_init(const struct device *dev) +{ + const struct sx126x_hal_context_cfg_t *config = dev->config; + struct sx126x_hal_context_data_t *data = dev->data; + int ret; + + if (!device_is_ready(config->spi.bus)) { + LOG_ERR("Could not find SPI device"); + return -EINVAL; + } + + /* Reset pin */ + ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE); + if (ret < 0) { + LOG_ERR("Could not configure reset gpio"); + return ret; + } + + /* Busy pin */ + ret = gpio_pin_configure_dt(&config->busy, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure busy gpio"); + return ret; + } + + /* DIO1 event pin */ + if (config->dio1.port) { + ret = gpio_pin_configure_dt(&config->dio1, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure DIO1 event gpio"); + return ret; + } + } + /* DIO2 event pin */ + if (config->dio2.port) { + ret = gpio_pin_configure_dt(&config->dio2, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure DIO2 event gpio"); + return ret; + } + } + /* DIO3 event pin */ + if (config->dio3.port) { + ret = gpio_pin_configure_dt(&config->dio3, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure DIO3 event gpio"); + return ret; + } + } + + data->sx126x_dev = dev; + data->radio_status = RADIO_AWAKE; + data->tx_offset = config->tx_offset; + + /* Event pin trigger config */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + data->work.handler = sx126x_work_cb; +#elif CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT); + k_thread_create(&data->thread, data->thread_stack, + CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_STACK_SIZE, + (k_thread_entry_t)sx126x_thread, data, NULL, NULL, + K_PRIO_COOP(CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_PRIORITY), + 0, K_NO_WAIT); +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ + + if (config->dio1.port) { + /* Init callback */ + gpio_init_callback(&data->dio1_cb, sx126x_board_dio1_callback, + BIT(config->dio1.pin)); + /* Add callback */ + if (gpio_add_callback(config->dio1.port, &data->dio1_cb)) { + LOG_ERR("Could not set dio1 pin callback"); + return -EIO; + } + } + if (config->dio2.port) { + /* Init callback */ + gpio_init_callback(&data->dio2_cb, sx126x_board_dio2_callback, + BIT(config->dio2.pin)); + /* Add callback */ + if (gpio_add_callback(config->dio2.port, &data->dio2_cb)) { + LOG_ERR("Could not set dio2 pin callback"); + return -EIO; + } + } + if (config->dio3.port) { + /* Init callback */ + gpio_init_callback(&data->dio3_cb, sx126x_board_dio3_callback, + BIT(config->dio3.pin)); + /* Add callback */ + if (gpio_add_callback(config->dio3.port, &data->dio3_cb)) { + LOG_ERR("Could not set dio3 pin callback"); + return -EIO; + } + } +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ + + return ret; +} + +#if IS_ENABLED(CONFIG_PM_DEVICE) +/** + * @brief Power management action define. + * Not implemented as LoRa Basics Modem handles this on its side. + * + * @param dev + * @param action + * @return int + */ +static int sx126x_pm_action(const struct device *dev, enum pm_device_action action) +{ + int ret = 0; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + /* Put the lr11xx into normal operation mode */ + break; + case PM_DEVICE_ACTION_SUSPEND: + /* Put the lr11xx into sleep mode */ + break; + default: + return -ENOTSUP; + } + return ret; +} +#endif /* IS_ENABLED(CONFIG_PM_DEVICE) */ + +/* + * Device creation macro. + */ + +#define DIO2_CONFLICT(node_id) \ + (DT_NODE_HAS_PROP(node_id, dio2_gpios) && DT_NODE_HAS_PROP(node_id, dio2_as_rf_switch)) || + +#if DT_FOREACH_STATUS_OKAY(semtech_sx1261_new, DIO2_CONFLICT) \ + DT_FOREACH_STATUS_OKAY(semtech_sx1262_new, DIO2_CONFLICT) \ + DT_FOREACH_STATUS_OKAY(semtech_sx1268_new, DIO2_CONFLICT) 0 +#error Device tree properties dio2-gpios and dio2-as-rf-switch are conflicting, \ +please delete one. +#endif + +#define DIO3_CONFLICT(node_id) \ + (DT_NODE_HAS_PROP(node_id, dio3_gpios) && \ + DT_NODE_HAS_PROP(node_id, dio3_as_tcxo_control)) || + +#if DT_FOREACH_STATUS_OKAY(semtech_sx1261_new, DIO3_CONFLICT) \ + DT_FOREACH_STATUS_OKAY(semtech_sx1262_new, DIO3_CONFLICT) \ + DT_FOREACH_STATUS_OKAY(semtech_sx1268_new, DIO3_CONFLICT) 0 +#error Device tree properties dio3-gpios and dio3-as-tcxo-control are conflicting, \ +please delete one. +#endif + +#define CONFIGURE_GPIO_IF_IN_DT(node_id, name, dt_prop) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, dt_prop), \ + (.name = GPIO_DT_SPEC_GET(node_id, dt_prop),), ()) + +#define SX126X_XOSC_CFG(node_id) \ + COND_CODE_1(DT_PROP(node_id, tcxo_wakeup_time) == 0, (RAL_XOSC_CFG_XTAL), \ + (COND_CODE_1(DT_PROP(node_id, dio3_as_tcxo_control), \ + (RAL_XOSC_CFG_TCXO_RADIO_CTRL), \ + (RAL_XOSC_CFG_TCXO_EXT_CTRL)))) + +/* Derive dio3-as-tcxo-control to know xosc_cfg */ +#define SX126X_CFG_TCXO(node_id) \ + .tcxo_cfg = { \ + .xosc_cfg = SX126X_XOSC_CFG(node_id), \ + .voltage = DT_PROP(node_id, tcxo_voltage), \ + .wakeup_time_ms = DT_PROP(node_id, tcxo_wakeup_time), \ + } + +#define SX126X_CONFIG(node_id) \ + { \ + .spi = SPI_DT_SPEC_GET(node_id, SX126X_SPI_OPERATION, 0), \ + .reset = GPIO_DT_SPEC_GET(node_id, reset_gpios), \ + .busy = GPIO_DT_SPEC_GET(node_id, busy_gpios), \ + CONFIGURE_GPIO_IF_IN_DT(node_id, dio1, dio1_gpios) \ + CONFIGURE_GPIO_IF_IN_DT(node_id, dio2, dio2_gpios) \ + CONFIGURE_GPIO_IF_IN_DT(node_id, dio3, dio3_gpios) \ + .dio2_as_rf_switch = DT_PROP(node_id, dio2_as_rf_switch), \ + SX126X_CFG_TCXO(node_id), \ + .capa_xta = DT_PROP_OR(node_id, xtal_capacitor_value_xta, 0xFF), \ + .capa_xtb = DT_PROP_OR(node_id, xtal_capacitor_value_xtb, 0xFF), \ + .reg_mode = DT_PROP(node_id, reg_mode), \ + .rx_boosted = DT_PROP(node_id, rx_boosted), \ + .tx_offset = DT_PROP_OR(node_id, tx_offset, 0), \ + } + +#define SX126X_DEVICE_INIT(node_id) \ + DEVICE_DT_DEFINE(node_id, sx126x_init, PM_DEVICE_DT_GET(node_id), &sx126x_data_##node_id, \ + &sx126x_config_##node_id, POST_KERNEL, \ + CONFIG_LORA_BASICS_MODEM_DRIVERS_INIT_PRIORITY, NULL); + +#define SX126X_DEFINE(node_id) \ + static struct sx126x_hal_context_data_t sx126x_data_##node_id; \ + static const struct sx126x_hal_context_cfg_t sx126x_config_##node_id = \ + SX126X_CONFIG(node_id); \ + PM_DEVICE_DT_DEFINE(node_id, sx126x_pm_action); \ + SX126X_DEVICE_INIT(node_id) + +DT_FOREACH_STATUS_OKAY(semtech_sx1261_new, SX126X_DEFINE) +DT_FOREACH_STATUS_OKAY(semtech_sx1262_new, SX126X_DEFINE) +DT_FOREACH_STATUS_OKAY(semtech_sx1268_new, SX126X_DEFINE) diff --git a/drivers/lora_lbm/sx126x/sx126x_hal.c b/drivers/lora_lbm/sx126x/sx126x_hal.c new file mode 100644 index 0000000000000..448761a560e3f --- /dev/null +++ b/drivers/lora_lbm/sx126x/sx126x_hal.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include "sx126x_hal_context.h" + +LOG_MODULE_DECLARE(lora_sx126x, CONFIG_LORA_BASICS_MODEM_DRIVERS_LOG_LEVEL); + +/** + * @brief Wait until radio busy pin returns to inactive state or + * until CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC passes. + * + * @param context + */ +static void sx126x_hal_wait_on_busy(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + + const bool timed_out = + WAIT_FOR(gpio_pin_get_dt(&config->busy) == 0, + (1000 * CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC), + k_usleep(100)); + if (!timed_out) { + LOG_ERR("Timeout of %dms hit when waiting for sx126x busy!", + CONFIG_LORA_BASICS_MODEM_DRIVERS_HAL_WAIT_ON_BUSY_TIMEOUT_MSEC); + k_oops(); + } +} + +/** + * @brief Wake up the radio and ensure it's ready + * + * @param context + */ +static void sx126x_hal_check_device_ready(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + struct sx126x_hal_context_data_t *data = dev->data; + + if (data->radio_status != RADIO_SLEEP) { + sx126x_hal_wait_on_busy(context); + } else { + /* Busy is HIGH in sleep mode, wake-up the device with a small glitch on NSS */ + const struct gpio_dt_spec *cs = &(config->spi.config.cs.gpio); + + gpio_pin_set_dt(cs, 1); + k_usleep(100); + gpio_pin_set_dt(cs, 0); + sx126x_hal_wait_on_busy(context); + data->radio_status = RADIO_AWAKE; + } +} + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +sx126x_hal_status_t sx126x_hal_write(const void *context, const uint8_t *command, + const uint16_t command_length, const uint8_t *data, + const uint16_t data_length) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + struct sx126x_hal_context_data_t *dev_data = dev->data; + int ret; + + const struct spi_buf tx_bufs[] = { + {.buf = (void *)command, .len = command_length}, + {.buf = (void *)data, .len = data_length}, + }; + + const struct spi_buf_set tx_buf_set = {tx_bufs, .count = ARRAY_SIZE(tx_bufs)}; + + sx126x_hal_check_device_ready(context); + ret = spi_write_dt(&config->spi, &tx_buf_set); + if (ret) { + return SX126X_HAL_STATUS_ERROR; + } + + /* 0x84 - SX126x_SET_SLEEP opcode. In sleep mode the radio dio is struck to 1 + * => do not test it + */ + if (command[0] == 0x84) { + dev_data->radio_status = RADIO_SLEEP; + k_usleep(500); + } else { + sx126x_hal_check_device_ready(context); + } + + return SX126X_HAL_STATUS_OK; +} + +sx126x_hal_status_t sx126x_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, uint8_t *data, + const uint16_t data_length) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + int ret; + + const struct spi_buf tx_bufs[] = {{.buf = (uint8_t *)command, .len = command_length}, + {.buf = NULL, .len = data_length}}; + + const struct spi_buf rx_bufs[] = {{.buf = NULL, .len = command_length}, + {.buf = data, .len = data_length}}; + + const struct spi_buf_set tx_buf_set = {.buffers = tx_bufs, .count = ARRAY_SIZE(tx_bufs)}; + const struct spi_buf_set rx_buf_set = {.buffers = rx_bufs, .count = ARRAY_SIZE(rx_bufs)}; + + sx126x_hal_check_device_ready(context); + ret = spi_transceive_dt(&config->spi, &tx_buf_set, &rx_buf_set); + if (ret) { + return SX126X_HAL_STATUS_ERROR; + } + return SX126X_HAL_STATUS_OK; +} + +sx126x_hal_status_t sx126x_hal_reset(const void *context) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + struct sx126x_hal_context_data_t *data = dev->data; + + const struct gpio_dt_spec *nrst = &(config->reset); + + gpio_pin_set_dt(nrst, 1); + k_msleep(5); + gpio_pin_set_dt(nrst, 0); + k_msleep(5); + + data->radio_status = RADIO_AWAKE; + return SX126X_HAL_STATUS_OK; +} + +sx126x_hal_status_t sx126x_hal_wakeup(const void *context) +{ + sx126x_hal_check_device_ready(context); + return SX126X_HAL_STATUS_OK; +} diff --git a/drivers/lora_lbm/sx126x/sx126x_hal_context.h b/drivers/lora_lbm/sx126x/sx126x_hal_context.h new file mode 100644 index 0000000000000..a185c250247c0 --- /dev/null +++ b/drivers/lora_lbm/sx126x/sx126x_hal_context.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SX126X_HAL_CONTEXT_H +#define SX126X_HAL_CONTEXT_H + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct sx126x_hal_context_tcxo_cfg_t { + ral_xosc_cfg_t xosc_cfg; + sx126x_tcxo_ctrl_voltages_t voltage; + uint32_t wakeup_time_ms; +}; + +typedef struct sx126x_pa_pwr_cfg_s { + int8_t power; + uint8_t pa_duty_cycle; + uint8_t pa_hp_sel; +} sx126x_pa_pwr_cfg_t; + +struct sx126x_hal_context_cfg_t { + struct spi_dt_spec spi; /* spi peripheral */ + + struct gpio_dt_spec reset; /* reset pin */ + struct gpio_dt_spec busy; /* busy pin */ + + struct gpio_dt_spec dio1; /* DIO1 pin */ + struct gpio_dt_spec dio2; /* DIO2 pin */ + struct gpio_dt_spec dio3; /* DIO3 pin */ + + bool dio2_as_rf_switch; + struct sx126x_hal_context_tcxo_cfg_t tcxo_cfg; /* TCXO config, says if dio3-tcxo */ + uint8_t capa_xta; /* set to 0xFF if not configured*/ + uint8_t capa_xtb; /* set to 0xFF if not configured*/ + + sx126x_reg_mod_t reg_mode; + bool rx_boosted; /* RXBoosted option */ + + uint8_t tx_offset; /* Board TX power offset */ +}; + +/* This type holds the current sleep status of the radio */ +typedef enum { + RADIO_SLEEP, + RADIO_AWAKE +} radio_sleep_status_t; + +/** + * @brief Callback upon firing event trigger + * + */ +typedef void (*event_cb_t)(const struct device *dev); + +struct sx126x_hal_context_data_t { +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER + const struct device *sx126x_dev; + struct gpio_callback dio1_cb; /* event callback structure */ + struct gpio_callback dio2_cb; /* event callback structure */ + struct gpio_callback dio3_cb; /* event callback structure */ + event_cb_t event_interrupt_cb; /* event interrupt user provided callback */ + +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD */ +#ifdef CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD + K_THREAD_STACK_MEMBER(thread_stack, + CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem trig_sem; +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD */ +#endif /* CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER */ + radio_sleep_status_t radio_status; + uint8_t tx_offset; /* Board TX power offset at reset */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SX126X_HAL_CONTEXT_H */ diff --git a/drivers/lora_lbm/sx126x/sx126x_ral_bsp.c b/drivers/lora_lbm/sx126x/sx126x_ral_bsp.c new file mode 100644 index 0000000000000..dffe53dc6b64e --- /dev/null +++ b/drivers/lora_lbm/sx126x/sx126x_ral_bsp.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include +#include +#include "sx126x_hal_context.h" + +void ral_sx126x_bsp_get_reg_mode(const void *context, sx126x_reg_mod_t *reg_mode) +{ + const struct device *dev = context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + *reg_mode = config->reg_mode; +} + +void ral_sx126x_bsp_get_rf_switch_cfg(const void *context, bool *dio2_is_set_as_rf_switch) +{ + const struct device *dev = context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + *dio2_is_set_as_rf_switch = config->dio2_as_rf_switch; +} + +void ral_sx126x_bsp_get_tx_cfg(const void *context, + const ral_sx126x_bsp_tx_cfg_input_params_t *input_params, + ral_sx126x_bsp_tx_cfg_output_params_t *output_params) +{ + /* get board tx power offset */ + int8_t board_tx_pwr_offset_db = radio_utilities_get_tx_power_offset(context); + + int16_t power = input_params->system_output_pwr_in_dbm + board_tx_pwr_offset_db; + + output_params->pa_ramp_time = SX126X_RAMP_40_US; + /* reserved value, same for sx1261 sx1262 and sx1268 */ + output_params->pa_cfg.pa_lut = 0x01; + +#if defined(SX1262) || defined(SX1268) + /* Clamp power if needed */ + power = CLAMP(power, -9, 22); + + output_params->pa_cfg.device_sel = 0x00; /* select SX1262/SX1268 device */ + output_params->pa_cfg.hp_max = 0x07; /* to achieve 22dBm */ + output_params->pa_cfg.pa_duty_cycle = 0x04; + output_params->chip_output_pwr_in_dbm_configured = (int8_t)power; + output_params->chip_output_pwr_in_dbm_expected = (int8_t)power; +#else + /* Clamp power if needed */ + power = CLAMP(power, -17, 15); + + /* config pa according to power */ + if (power == 15) { + output_params->pa_cfg.device_sel = 0x01; /* select SX1261 device */ + output_params->pa_cfg.hp_max = 0x00; /* not used on sx1261 */ + output_params->pa_cfg.pa_duty_cycle = 0x06; + output_params->chip_output_pwr_in_dbm_configured = 14; + output_params->chip_output_pwr_in_dbm_expected = 15; + } else if (power == 14) { + output_params->pa_cfg.device_sel = 0x01; /* select SX1261 device */ + output_params->pa_cfg.hp_max = 0x00; /* not used on sx1261 */ + output_params->pa_cfg.pa_duty_cycle = 0x04; + output_params->chip_output_pwr_in_dbm_configured = 14; + output_params->chip_output_pwr_in_dbm_expected = 14; + } else { + output_params->pa_cfg.device_sel = 0x01; /* select SX1261 device */ + output_params->pa_cfg.hp_max = 0x00; /* not used on sx1261 */ + output_params->pa_cfg.pa_duty_cycle = 0x04; + output_params->chip_output_pwr_in_dbm_configured = (int8_t)power; + output_params->chip_output_pwr_in_dbm_expected = (int8_t)power; + } + +#endif +} + +void ral_sx126x_bsp_get_xosc_cfg(const void *context, ral_xosc_cfg_t *xosc_cfg, + sx126x_tcxo_ctrl_voltages_t *supply_voltage, + uint32_t *startup_time_in_tick) +{ + const struct device *dev = context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + const struct sx126x_hal_context_tcxo_cfg_t tcxo_cfg = config->tcxo_cfg; + + *xosc_cfg = tcxo_cfg.xosc_cfg; + *supply_voltage = tcxo_cfg.voltage; + *startup_time_in_tick = sx126x_convert_timeout_in_ms_to_rtc_step(tcxo_cfg.wakeup_time_ms); +} + +void ral_sx126x_bsp_get_trim_cap(const void *context, uint8_t *trimming_cap_xta, + uint8_t *trimming_cap_xtb) +{ + const struct device *dev = context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + + if (config->capa_xta != 0xFF) { + *trimming_cap_xta = config->capa_xta; + } + if (config->capa_xtb != 0xFF) { + *trimming_cap_xtb = config->capa_xtb; + } +} + +void ral_sx126x_bsp_get_rx_boost_cfg(const void *context, bool *rx_boost_is_activated) +{ + const struct device *dev = (const struct device *)context; + const struct sx126x_hal_context_cfg_t *config = dev->config; + *rx_boost_is_activated = config->rx_boosted; +} + +void ral_sx126x_bsp_get_ocp_value(const void *context, uint8_t *ocp_in_step_of_2_5_ma) +{ + /* Do nothing, let the driver choose the default values */ +} + +void ral_sx126x_bsp_get_lora_cad_det_peak(const void *context, ral_lora_sf_t sf, ral_lora_bw_t bw, + ral_lora_cad_symbs_t nb_symbol, + uint8_t *in_out_cad_det_peak) +{ + /* Function used to fine tune the cad detection peak, update if needed */ +} + +void radio_utilities_set_tx_power_offset(const void *context, uint8_t tx_pwr_offset_db) +{ + const struct device *dev = (const struct device *)context; + struct sx126x_hal_context_data_t *data = dev->data; + + data->tx_offset = tx_pwr_offset_db; +} + +uint8_t radio_utilities_get_tx_power_offset(const void *context) +{ + const struct device *dev = (const struct device *)context; + struct sx126x_hal_context_data_t *data = dev->data; + + return data->tx_offset; +} + +#define SX126X_LP_MIN_OUTPUT_POWER -17 +#define SX126X_LP_MAX_OUTPUT_POWER 15 + +#define SX126X_HP_MIN_OUTPUT_POWER -9 +#define SX126X_HP_MAX_OUTPUT_POWER 22 + +#define SX126X_LP_CONVERT_TABLE_INDEX_OFFSET 17 +#define SX126X_HP_CONVERT_TABLE_INDEX_OFFSET 9 + +/* TODO: check values */ +#define SX126X_GFSK_RX_CONSUMPTION_DCDC 4200 +#define SX126X_GFSK_RX_BOOSTED_CONSUMPTION_DCDC 4800 + +#define SX126X_GFSK_RX_CONSUMPTION_LDO 8000 +#define SX126X_GFSK_RX_BOOSTED_CONSUMPTION_LDO 9300 + +#define SX126X_LORA_RX_CONSUMPTION_DCDC 4600 +#define SX126X_LORA_RX_BOOSTED_CONSUMPTION_DCDC 5300 + +#define SX126X_LORA_RX_CONSUMPTION_LDO 8880 +#define SX126X_LORA_RX_BOOSTED_CONSUMPTION_LDO 10100 + +static const uint32_t ral_sx126x_convert_tx_dbm_to_ua_reg_mode_dcdc_lp[] = { + 5200, /* -17 dBm */ + 5400, /* -16 dBm */ + 5600, /* -15 dBm */ + 5700, /* -14 dBm */ + 5800, /* -13 dBm */ + 6000, /* -12 dBm */ + 6100, /* -11 dBm */ + 6200, /* -10 dBm */ + 6500, /* -9 dBm */ + 6800, /* -8 dBm */ + 7000, /* -7 dBm */ + 7300, /* -6 dBm */ + 7500, /* -5 dBm */ + 7900, /* -4 dBm */ + 8300, /* -3 dBm */ + 8800, /* -2 dBm */ + 9300, /* -1 dBm */ + 9800, /* 0 dBm */ + 10600, /* 1 dBm */ + 11400, /* 2 dBm */ + 12200, /* 3 dBm */ + 12900, /* 4 dBm */ + 13800, /* 5 dBm */ + 14700, /* 6 dBm */ + 15700, /* 7 dBm */ + 16600, /* 8 dBm */ + 17900, /* 9 dBm */ + 18500, /* 10 dBm */ + 20500, /* 11 dBm */ + 21900, /* 12 dBm */ + 23500, /* 13 dBm */ + 25500, /* 14 dBm */ + 32500, /* 15 dBm */ +}; + +static const uint32_t ral_sx126x_convert_tx_dbm_to_ua_reg_mode_ldo_lp[] = { + 9800, /* -17 dBm */ + 10300, /* -16 dBm */ + 10500, /* -15 dBm */ + 10800, /* -14 dBm */ + 11100, /* -13 dBm */ + 11300, /* -12 dBm */ + 11600, /* -11 dBm */ + 11900, /* -10 dBm */ + 12400, /* -9 dBm */ + 12900, /* -8 dBm */ + 13400, /* -7 dBm */ + 13900, /* -6 dBm */ + 14500, /* -5 dBm */ + 15300, /* -4 dBm */ + 16000, /* -3 dBm */ + 17000, /* -2 dBm */ + 18000, /* -1 dBm */ + 19000, /* 0 dBm */ + 20600, /* 1 dBm */ + 22000, /* 2 dBm */ + 23500, /* 3 dBm */ + 24900, /* 4 dBm */ + 26600, /* 5 dBm */ + 28400, /* 6 dBm */ + 30200, /* 7 dBm */ + 32000, /* 8 dBm */ + 34300, /* 9 dBm */ + 36600, /* 10 dBm */ + 39200, /* 11 dBm */ + 41700, /* 12 dBm */ + 44700, /* 13 dBm */ + 48200, /* 14 dBm */ + 52200, /* 15 dBm */ +}; + +static const uint32_t ral_sx126x_convert_tx_dbm_to_ua_reg_mode_dcdc_hp[] = { + 24000, /* -9 dBm */ + 25400, /* -8 dBm */ + 26700, /* -7 dBm */ + 28000, /* -6 dBm */ + 30600, /* -5 dBm */ + 31900, /* -4 dBm */ + 33200, /* -3 dBm */ + 35700, /* -2 dBm */ + 38200, /* -1 dBm */ + 40600, /* 0 dBm */ + 42900, /* 1 dBm */ + 46200, /* 2 dBm */ + 48200, /* 3 dBm */ + 51800, /* 4 dBm */ + 54100, /* 5 dBm */ + 57000, /* 6 dBm */ + 60300, /* 7 dBm */ + 63500, /* 8 dBm */ + 67100, /* 9 dBm */ + 70500, /* 10 dBm */ + 74200, /* 11 dBm */ + 78400, /* 12 dBm */ + 83500, /* 13 dBm */ + 89300, /* 14 dBm */ + 92400, /* 15 dBm */ + 94500, /* 16 dBm */ + 95400, /* 17 dBm */ + 97500, /* 18 dBm */ + 100100, /* 19 dBm */ + 103800, /* 20 dBm */ + 109100, /* 21 dBm */ + 117900, /* 22 dBm */ +}; + +static const uint32_t ral_sx126x_convert_tx_dbm_to_ua_reg_mode_ldo_hp[] = { + 25900, /* -9 dBm */ + 27400, /* -8 dBm */ + 28700, /* -7 dBm */ + 30000, /* -6 dBm */ + 32600, /* -5 dBm */ + 33900, /* -4 dBm */ + 35200, /* -3 dBm */ + 37700, /* -2 dBm */ + 40100, /* -1 dBm */ + 42600, /* 0 dBm */ + 44900, /* 1 dBm */ + 48200, /* 2 dBm */ + 50200, /* 3 dBm */ + 53800, /* 4 dBm */ + 56100, /* 5 dBm */ + 59000, /* 6 dBm */ + 62300, /* 7 dBm */ + 65500, /* 8 dBm */ + 69000, /* 9 dBm */ + 72500, /* 10 dBm */ + 76200, /* 11 dBm */ + 80400, /* 12 dBm */ + 85400, /* 13 dBm */ + 90200, /* 14 dBm */ + 94400, /* 15 dBm */ + 96500, /* 16 dBm */ + 97700, /* 17 dBm */ + 99500, /* 18 dBm */ + 102100, /* 19 dBm */ + 105800, /* 20 dBm */ + 111000, /* 21 dBm */ + 119800, /* 22 dBm */ +}; + +__weak ral_status_t ral_sx126x_bsp_get_instantaneous_tx_power_consumption( + const void *context, const ral_sx126x_bsp_tx_cfg_output_params_t *tx_cfg_output_params, + sx126x_reg_mod_t radio_reg_mode, uint32_t *pwr_consumption_in_ua) +{ + /* SX1261 */ + if (tx_cfg_output_params->pa_cfg.device_sel == 0x01) { + uint8_t index = 0; + + if (tx_cfg_output_params->chip_output_pwr_in_dbm_expected > + SX126X_LP_MAX_OUTPUT_POWER) { + index = SX126X_LP_MAX_OUTPUT_POWER + SX126X_LP_CONVERT_TABLE_INDEX_OFFSET; + } else if (tx_cfg_output_params->chip_output_pwr_in_dbm_expected < + SX126X_LP_MIN_OUTPUT_POWER) { + index = SX126X_LP_MIN_OUTPUT_POWER + SX126X_LP_CONVERT_TABLE_INDEX_OFFSET; + } else { + index = tx_cfg_output_params->chip_output_pwr_in_dbm_expected + + SX126X_LP_CONVERT_TABLE_INDEX_OFFSET; + } + + if (radio_reg_mode == SX126X_REG_MODE_DCDC) { + *pwr_consumption_in_ua = + ral_sx126x_convert_tx_dbm_to_ua_reg_mode_dcdc_lp[index]; + } else { + *pwr_consumption_in_ua = + ral_sx126x_convert_tx_dbm_to_ua_reg_mode_ldo_lp[index]; + } + } + /* SX1262 */ + else if (tx_cfg_output_params->pa_cfg.device_sel == 0x00) { + uint8_t index = 0; + + if (tx_cfg_output_params->chip_output_pwr_in_dbm_expected > + SX126X_HP_MAX_OUTPUT_POWER) { + index = SX126X_HP_MAX_OUTPUT_POWER + SX126X_HP_CONVERT_TABLE_INDEX_OFFSET; + } else if (tx_cfg_output_params->chip_output_pwr_in_dbm_expected < + SX126X_HP_MIN_OUTPUT_POWER) { + index = SX126X_HP_MIN_OUTPUT_POWER + SX126X_HP_CONVERT_TABLE_INDEX_OFFSET; + } else { + index = tx_cfg_output_params->chip_output_pwr_in_dbm_expected + + SX126X_HP_CONVERT_TABLE_INDEX_OFFSET; + } + + if (radio_reg_mode == SX126X_REG_MODE_DCDC) { + *pwr_consumption_in_ua = + ral_sx126x_convert_tx_dbm_to_ua_reg_mode_dcdc_hp[index]; + } else { + *pwr_consumption_in_ua = + ral_sx126x_convert_tx_dbm_to_ua_reg_mode_ldo_hp[index]; + } + } else { + return RAL_STATUS_UNKNOWN_VALUE; + } + + return RAL_STATUS_OK; +} + +__weak ral_status_t ral_sx126x_bsp_get_instantaneous_gfsk_rx_power_consumption( + const void *context, sx126x_reg_mod_t radio_reg_mode, bool rx_boosted, + uint32_t *pwr_consumption_in_ua) +{ + /* TODO: find the br/bw dependent values */ + + if (radio_reg_mode == SX126X_REG_MODE_DCDC) { + *pwr_consumption_in_ua = rx_boosted ? SX126X_GFSK_RX_BOOSTED_CONSUMPTION_DCDC + : SX126X_GFSK_RX_CONSUMPTION_DCDC; + } else { + *pwr_consumption_in_ua = rx_boosted ? SX126X_GFSK_RX_BOOSTED_CONSUMPTION_LDO + : SX126X_GFSK_RX_CONSUMPTION_LDO; + } + + return RAL_STATUS_OK; +} + +__weak ral_status_t ral_sx126x_bsp_get_instantaneous_lora_rx_power_consumption( + const void *context, sx126x_reg_mod_t radio_reg_mode, bool rx_boosted, + uint32_t *pwr_consumption_in_ua) +{ + /* TODO: find the bw dependent values */ + + if (radio_reg_mode == SX126X_REG_MODE_DCDC) { + *pwr_consumption_in_ua = rx_boosted ? SX126X_LORA_RX_BOOSTED_CONSUMPTION_DCDC + : SX126X_LORA_RX_CONSUMPTION_DCDC; + } else { + *pwr_consumption_in_ua = rx_boosted ? SX126X_LORA_RX_BOOSTED_CONSUMPTION_LDO + : SX126X_LORA_RX_CONSUMPTION_LDO; + } + + return RAL_STATUS_OK; +} diff --git a/dts/bindings/lora_lbm/semtech,lr1110.yaml b/dts/bindings/lora_lbm/semtech,lr1110.yaml new file mode 100644 index 0000000000000..c4a332f048e87 --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,lr1110.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech LR1110 LoRa radio module + +compatible: "semtech,lr1110" + +include: semtech,lr11xx-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,lr1120.yaml b/dts/bindings/lora_lbm/semtech,lr1120.yaml new file mode 100644 index 0000000000000..dc5bb3bf317ae --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,lr1120.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech LR1120 LoRa radio module + +compatible: "semtech,lr1120" + +include: semtech,lr11xx-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,lr1121.yaml b/dts/bindings/lora_lbm/semtech,lr1121.yaml new file mode 100644 index 0000000000000..f9c550a402d6c --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,lr1121.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech LR1121 LoRa radio module + +compatible: "semtech,lr1121" + +include: semtech,lr11xx-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,lr11xx-common.yaml b/dts/bindings/lora_lbm/semtech,lr11xx-common.yaml new file mode 100644 index 0000000000000..3d7489d1b0eaf --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,lr11xx-common.yaml @@ -0,0 +1,253 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech LR11XX LoRa radio module + +include: spi-device.yaml + +properties: + reset-gpios: + type: phandle-array + required: true + description: | + GPIO connected to the radio's NRESET signal. + + This signal is open-drain, active-low as interpreted by the modem. + + busy-gpios: + type: phandle-array + required: true + description: | + GPIO connected to the radio's BUSY/DIO0. + + This signal is high when the radio is ready to accept commands, and + low when it is processing a command. + + + event-gpios: + type: phandle-array + required: true + description: | + GPIO connected to the radio's LR_EVENT/DIO9. + + This signal is high to indicate an event is available on the radio. It + becomes low when all events are cleared. + + lf-tx-path: + type: int + required: true + enum: + - 0 + - 1 + - 2 + description: | + Configures which low frequency paths are available on the board. + + See constants LR11XX_TX_PATH_* in dt-bindings/lora_lbm/lr11xx.h + + tcxo-voltage: + type: int + required: true + description: | + Supply voltage on the radio's VTCXO signal, if enabled. + + See constants LR11XX_TCXO_SUPPLY_* in dt-bindings/lora_lbm/lr11xx.h + + tcxo-wakeup-time: + type: int + required: true + description: | + In milliseconds, the wakeup (or stabilization) time of the TCXO used + by the radio. + + Set this value to 0 to disable the TCXO management feature. + + lf-clk: + type: int + required: true + enum: + - 0 + - 1 + - 2 + description: | + Low frequency clock source for the radio. + + See constants LR11XX_LFCLK_* in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-enable: + type: int + description: | + Bitmask of all radio's rf switches RFSW0..RFSW4 (pins DIO5..DIO8) that + will be used in any of the RF modes. + + If a switch is selected here, it will be low by default and high when a + rf mode using it is entered. + If a switch is not selected here, it will always be left in High-Z state. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-standby-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in standby mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-rx-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in RX mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-tx-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in low power TX mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-tx-hp-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in high power TX mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-tx-hf-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in high frequency TX mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-wifi-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in wifi scanning mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + rf-sw-gnss-mode: + type: int + description: | + Bitmask of the radio's rf switches to be set high in GNSS scanning mode. + + See constants LR11XX_DIO5..8 in dt-bindings/lora_lbm/lr11xx.h + + reg-mode: + type: int + required: true + enum: + - 0 + - 1 + description: | + Configuration of the radio's power regulator mode. + + See constants LR11XX_REG_MODE_* in dt-bindings/lora_lbm/lr11xx.h + + rx-boosted: + type: boolean + description: | + Sets the radio in RX Boosted mode, allowing a ~2dB increased sensitivity, + at the expense of a ~2mA higher current consumption in RX mode. + + tx-power-offset: + type: int + description: | + Default board-specific TX Power offset in dB. Defaults to 0. + + Can be reconfigured at runtime via radio_utilities_set_tx_power_offset. + + tx-power-cfg-lf-lp: + type: array + required: true + description: | + Low-power, low-frequency board output TX power calibrated configuration, + from -17dBm to +15dBm (in 1dBm steps). + + An array of 3-element cells is expected. Each cell describes the + configuration for a particular TX power with the following format: + . + This config depends on the board BOM and layout. + + tx-power-cfg-lf-hp: + type: array + required: true + description: | + High-power, low-frequency board output TX power calibrated configuration, + from -9dBm to +22dBm (in 1dBm steps). + + An array of 3-element cells is expected. Each cell describes the + configuration for a particular TX power with the following format: + . + This config depends on the board BOM and layout. + + tx-power-cfg-hf: + type: array + required: true + description: | + High-frequency board output TX power calibrated configuration, + from -18dBm to +13dBm (in 1dBm steps). + + An array of 3-element cells is expected. Each cell describes the + configuration for a particular TX power with the following format: + . + This config depends on the board BOM and layout. + + rssi-calibration-lf-offset: + type: int + required: true + description: | + RSSI offset calibration value, for frequencies below 600MHz. + + This config depends on the board BOM and layout. + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. + + rssi-calibration-lf-tune: + type: array + required: true + description: | + RSSI tuning calibration values, for frequencies below 600MHz. + + This config depends on the board BOM and layout. Array order is: + G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 G13hp1 G13hp2 G13hp3 G13hp4 G13hp5 G13hp6 G13hp7 + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. + + rssi-calibration-mf-offset: + type: int + required: true + description: | + RSSI offset calibration value, for frequencies between 600MHz and 2GHz. + + This config depends on the board BOM and layout. + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. + + rssi-calibration-mf-tune: + type: array + required: true + description: | + RSSI tuning calibration values, for frequencies between 600MHz and 2GHz. + + This config depends on the board BOM and layout. Array order is: + G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 G13hp1 G13hp2 G13hp3 G13hp4 G13hp5 G13hp6 G13hp7 + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. + + rssi-calibration-hf-offset: + type: int + required: true + description: | + RSSI offset calibration value, for frequencies over 2GHz. + + This config depends on the board BOM and layout. + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. + + rssi-calibration-hf-tune: + type: array + required: true + description: | + RSSI tuning calibration values, for frequencies over 2GHz. + + This config depends on the board BOM and layout. Array order is: + G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 G13hp1 G13hp2 G13hp3 G13hp4 G13hp5 G13hp6 G13hp7 + See the LR1110 User Manual, 7.2.15 SetRssiCalibration. diff --git a/dts/bindings/lora_lbm/semtech,sx1261-new.yaml b/dts/bindings/lora_lbm/semtech,sx1261-new.yaml new file mode 100644 index 0000000000000..4d4d20da8a32b --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,sx1261-new.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech SX1261 LoRa radio module + +compatible: "semtech,sx1261-new" + +include: semtech,sx126x-new-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,sx1262-new.yaml b/dts/bindings/lora_lbm/semtech,sx1262-new.yaml new file mode 100644 index 0000000000000..9e4732b9ec468 --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,sx1262-new.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech SX1262 LoRa radio module + +compatible: "semtech,sx1262-new" + +include: semtech,sx126x-new-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,sx1268-new.yaml b/dts/bindings/lora_lbm/semtech,sx1268-new.yaml new file mode 100644 index 0000000000000..05c361dd3b38c --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,sx1268-new.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech SX1268 LoRa radio module + +compatible: "semtech,sx1268-new" + +include: semtech,sx126x-new-common.yaml diff --git a/dts/bindings/lora_lbm/semtech,sx126x-new-common.yaml b/dts/bindings/lora_lbm/semtech,sx126x-new-common.yaml new file mode 100644 index 0000000000000..eabe844021bf4 --- /dev/null +++ b/dts/bindings/lora_lbm/semtech,sx126x-new-common.yaml @@ -0,0 +1,125 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Semtech SX126X LoRa radio module + +include: spi-device.yaml + +properties: + reset-gpios: + type: phandle-array + required: true + description: | + GPIO connected to the radio's NRESET signal. + + This signal is open-drain, active-low as interpreted by the modem. + + busy-gpios: + type: phandle-array + required: true + description: | + GPIO connected to the radio's BUSY signal. + + This signal is high when the radio is ready to accept commands, and + low when it is processing a command. + + + dio1-gpios: + type: phandle-array + description: | + GPIO connected to the radio's DIO1 signal. This GPIO will be used as + a generic IRQ line from the chip. + + dio2-gpios: + type: phandle-array + description: | + GPIO connected to the radio's DIO2 signal. This GPIO will be used as + a generic IRQ line from the chip. + + Note that this conflicts with dio2-as-rf-switch. + + dio2-as-rf-switch: + type: boolean + description: | + Use DIO2 to drive an RF switch selecting between the TX and RX paths. + + When enabled, DIO2 goes high when the chip is transmitting. + Note that this conflicts with dio2-gpios. + + dio3-gpios: + type: phandle-array + description: | + GPIO connected to the radio's DIO3 signal. This GPIO will be used as + a generic IRQ line from the chip. + + Note that this conflicts with dio3-as-tcxo-enable. + + dio3-as-tcxo-control: + type: boolean + description: | + Use DIO3 to control a TCXO. + + When enabled, DIO3 supplies power to a TCXO when needed. Its voltage is + controlled by tcxo-voltage. + Note that this conflicts with dio3-gpios. + + tcxo-voltage: + type: int + required: true + description: | + Supply voltage on the radio's DIO3, if set as TCXO control. + + See constants SX126X_TCXO_SUPPLY_* in dt-bindings/lora_lbm/sx126x.h. + + tcxo-wakeup-time: + type: int + required: true + description: | + In milliseconds, the wakeup (or stabilization) time of the TCXO used + by the radio. + + Set this value to 0 to disable the TCXO management feature. + + xtal-capacitor-value-xta: + type: int + description: | + Internal trimming capacitor value on the radio's XTA pin as register value. + + Values from 0x00 to 0x2F code a capacitance from 11.3pF to 33.4pF, in + approximative 0.47pF steps, as described in section 4.1.3 of the datasheet. + + Values over 0x2F are not supported. Ignored when a TCXO is configured. + + xtal-capacitor-value-xtb: + type: int + description: | + Internal trimming capacitor value on the radio's XTB pin as register value. + + Values from 0x00 to 0x2F code a capacitance from 11.3pF to 33.4pF, in + approximative 0.47pF steps, as described in section 4.1.3 of the datasheet. + + Values over 0x2F are not supported. Ignored when a TCXO is configured. + + reg-mode: + type: int + required: true + enum: + - 0 + - 1 + description: | + Configuration of the radio's power regulator mode. + + See constants SX126X_REG_MODE_* in dt-bindings/lora_lbm/sx126x.h + + rx-boosted: + type: boolean + description: | + Sets the radio in RX Boosted mode, allowing a ~2dB increased sensitivity, + at the expense of a ~2mA higher current consumption in RX mode. + + tx-power-offset: + type: int + description: | + Default board-specific TX Power offset in dB. Defaults to 0. + + Can be reconfigured at runtime via radio_utilities_set_tx_power_offset. diff --git a/include/zephyr/dt-bindings/lora_lbm/lr11xx.h b/include/zephyr/dt-bindings/lora_lbm/lr11xx.h new file mode 100644 index 0000000000000..b671d7cae7b6c --- /dev/null +++ b/include/zephyr/dt-bindings/lora_lbm/lr11xx.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_LR11XX_BINDINGS_DEF_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_LR11XX_BINDINGS_DEF_H_ + +#define LR11XX_DIO5 (1 << 0) +#define LR11XX_DIO6 (1 << 1) +#define LR11XX_DIO7 (1 << 2) +#define LR11XX_DIO8 (1 << 3) +#define LR11XX_DIO9 (1 << 4) + +/* Those variables are already defined in the radio_drivers from LBM, but duplicated + * for the device tree preprocessor that can't read enums. + * See modules/lib/lora_basics_modem/lbm_lib/smtc_modem_core/radio_drivers/lr11xx_driver/src/lr11xx_system_types.h + */ + +#define LR11XX_SYSTEM_RFSW0_HIGH LR11XX_DIO5 +#define LR11XX_SYSTEM_RFSW1_HIGH LR11XX_DIO6 +#define LR11XX_SYSTEM_RFSW2_HIGH LR11XX_DIO7 +#define LR11XX_SYSTEM_RFSW3_HIGH LR11XX_DIO8 + +/* Only the low frequency low power path is placed. */ +#define LR11XX_TX_PATH_LF_LP 0 +/* Only the low frequency high power power path is placed. */ +#define LR11XX_TX_PATH_LF_HP 1 +/* Both the low frequency low power and low frequency high power paths are placed. */ +#define LR11XX_TX_PATH_LF_LP_HP 2 + +#define LR11XX_TCXO_SUPPLY_1_6V 0x00 /* Supply voltage = 1.6v */ +#define LR11XX_TCXO_SUPPLY_1_7V 0x01 /* Supply voltage = 1.7v */ +#define LR11XX_TCXO_SUPPLY_1_8V 0x02 /* Supply voltage = 1.8v */ +#define LR11XX_TCXO_SUPPLY_2_2V 0x03 /* Supply voltage = 2.2v */ +#define LR11XX_TCXO_SUPPLY_2_4V 0x04 /* Supply voltage = 2.4v */ +#define LR11XX_TCXO_SUPPLY_2_7V 0x05 /* Supply voltage = 2.7v */ +#define LR11XX_TCXO_SUPPLY_3_0V 0x06 /* Supply voltage = 3.0v */ +#define LR11XX_TCXO_SUPPLY_3_3V 0x07 /* Supply voltage = 3.3v */ + +#define LR11XX_LFCLK_RC 0x00 /* Use 32.768kHz RC oscillator */ +#define LR11XX_LFCLK_XTAL 0x01 /* Use 32.768kHz crystal oscillator */ +#define LR11XX_LFCLK_EXT 0x02 /* Use externally provided 32.768kHz signal on DIO11 pin */ + +#define LR11XX_REG_MODE_LDO 0x00 /* Do not switch on the DC-DC converter in any mode */ +#define LR11XX_REG_MODE_DCDC 0x01 /* Automatically switch on the DC-DC converter when required */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_LR11XX_BINDINGS_DEF_H_*/ diff --git a/include/zephyr/dt-bindings/lora_lbm/sx126x.h b/include/zephyr/dt-bindings/lora_lbm/sx126x.h new file mode 100644 index 0000000000000..6908bf7325169 --- /dev/null +++ b/include/zephyr/dt-bindings/lora_lbm/sx126x.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_SX126X_BINDINGS_DEF_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_SX126X_BINDINGS_DEF_H_ + +/* Those variables are already defined in the radio_drivers from LBM, but duplicated + * for the device tree preprocessor that can't read enums. + * See modules/lib/lora_basics_modem/lbm_lib/smtc_modem_core/radio_drivers/sx126x_driver/src/sx126x.h + */ + +#define SX126X_REG_MODE_LDO 0x00 /* default */ +#define SX126X_REG_MODE_DCDC 0x01 + +#define SX126X_TCXO_SUPPLY_1_6V 0x00 +#define SX126X_TCXO_SUPPLY_1_7V 0x01 +#define SX126X_TCXO_SUPPLY_1_8V 0x02 +#define SX126X_TCXO_SUPPLY_2_2V 0x03 +#define SX126X_TCXO_SUPPLY_2_4V 0x04 +#define SX126X_TCXO_SUPPLY_2_7V 0x05 +#define SX126X_TCXO_SUPPLY_3_0V 0x06 +#define SX126X_TCXO_SUPPLY_3_3V 0x07 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_LORA_LBM_SX126X_BINDINGS_DEF_H_*/ diff --git a/include/zephyr/lora_lbm/lora_lbm_transceiver.h b/include/zephyr/lora_lbm/lora_lbm_transceiver.h new file mode 100644 index 0000000000000..ecdda9557c888 --- /dev/null +++ b/include/zephyr/lora_lbm/lora_lbm_transceiver.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LORA_LBM_TRANSCEIVER_H +#define LORA_LBM_TRANSCEIVER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback upon firing event trigger + * + */ +typedef void (*event_cb_t)(const struct device *dev); + +/** + * @brief Attach interrupt cb to event pin. + * + * @param dev context + * @param cb cb function + */ +void lora_transceiver_board_attach_interrupt(const struct device *dev, event_cb_t cb); + +/** + * @brief Enable interrupt on event pin. + * + * @param dev context + */ +void lora_transceiver_board_enable_interrupt(const struct device *dev); + +/** + * @brief Disable interrupt on event pin. + * + * @param dev context + */ +void lora_transceiver_board_disable_interrupt(const struct device *dev); + +/** + * @brief Helper to get the tcxo startup delay for any model of transceiver + * + * @param dev context + */ +uint32_t lora_transceiver_get_tcxo_startup_delay_ms(const struct device *dev); + +/** + * @brief Returns lr11xx_system_version_type_t or -1 + * + * @param dev context + */ + +int32_t lora_transceiver_get_model(const struct device *dev); + +/** + * @brief Set the Tx power offset in dB + * + * @param [in] context Chip implementation context + * @param [in] tx_pwr_offset_db + */ +void radio_utilities_set_tx_power_offset(const void *context, uint8_t tx_pwr_offset_db); + +/** + * @brief Get the Tx power offset in dB + * + * @param [in] context Chip implementation context + * + * @return Tx power offset in dB + */ +uint8_t radio_utilities_get_tx_power_offset(const void *context); + +#ifdef __cplusplus +} +#endif + +#endif /* LORA_LBM_TRANSCEIVER_H */ diff --git a/include/zephyr/lorawan_lbm/lorawan_hal_init.h b/include/zephyr/lorawan_lbm/lorawan_hal_init.h new file mode 100644 index 0000000000000..3b13048d70f75 --- /dev/null +++ b/include/zephyr/lorawan_lbm/lorawan_hal_init.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SMTC_MODEM_HAL_INIT_H +#define SMTC_MODEM_HAL_INIT_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Defines the battery level callback handler function signature. + * + * @retval 0 if the node is connected to an external power source + * @retval 1..254 battery level, where 1 is the minimum and 254 is the maximum value + * @retval 255 if the node was not able to measure the battery level + */ +typedef uint8_t (*lorawan_battery_level_cb_t)(void); + +/** + * @brief Defines the battery voltage callback handler function signature. + * + * @retval The battery voltage, in mV + */ +typedef uint16_t (*lorawan_battery_voltage_cb_t)(void); + +/** + * @brief Defines the system temperature callback handler function signature. + * + * @retval The temperature, in degrees Celsius + */ +typedef int8_t (*lorawan_temperature_cb_t)(void); + +#ifdef CONFIG_LORA_BASICS_MODEM_FUOTA + +struct lorawan_fuota_cb { + /** + * @brief Callback to return the hardware version. + * + * See HW version in FMP Specification TS006-1.0.0. + * + * @return The device's hardware version. + */ + uint32_t (*get_hw_version)(void); + + /** + * @brief Callback to return the firmware version. + * + * See FW version in FMP Specification TS006-1.0.0. + * + * @return The device's firmware version. + */ + uint32_t (*get_fw_version)(void); + + /** + * @brief Callback to return the firmware status. + * + * See UpImageStatus in FMP Specification TS006-1.0.0. + * + * @return The device's firmware status. + */ + uint8_t (*get_fw_status_available)(); + + /** + * @brief Callback to return the firmware upgrade next version. + * + * See nextFirmwareVersion in FMP Specification TS006-1.0.0. + * + * @return The next firmware upgrade image version. + */ + uint32_t (*get_next_fw_version)(void); + + /** + * @brief Callback to delete a chosen firmware upgrade image. + * + * See DevDeleteImageReq in FMP Specification TS006-1.0.0. + * + * @param [in] fw_version The firmware version to delete. + * @return 0 if the firmware was deleted, or a DevDeleteImageAns. + */ + uint8_t (*get_fw_delete_status)(uint32_t fw_version); +}; + +#endif /* CONFIG_LORA_BASICS_MODEM_FUOTA */ + +#ifdef CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL + +struct lorawan_user_storage_cb { + + /** + * @brief Persistently store context from the lora basics modem + * + * The application should use some persistent storage to store the context. + * + * @param[in] ctx_id The ID of the context to store. Each ID must be stored + * separately. + * @param[in] offset Memory offset after ctx address + * @param[in] buffer The buffer to store. + * @param[in] size The size of the buffer to store, in bytes. + */ + void (*context_store)(const uint8_t ctx_id, const uint32_t offset, const uint8_t *buffer, + const uint32_t size); + + /** + * @brief Restore context to the lora basics modem + * + * The application should load the context from the persistent storage used in + * context_store. + * + * @param[in] ctx_id The ID of the context to restore. + * @param[in] offset Memory offset after ctx address + * @param[in] buffer The buffer to read into. + * @param[in] size The size of the buffer, in bytes. + */ + void (*context_restore)(const uint8_t ctx_id, const uint32_t offset, uint8_t *buffer, + const uint32_t size); +}; + +#endif /* CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL */ + +/** + * @brief Initialization of the hal implementation. + * + * This must be called before smtc_modem_init + * + * @param[in] transceiver The device pointer of the transceiver instance that will be used. + */ +void lorawan_smtc_modem_hal_init(const struct device *transceiver); + +/** + * @brief Register a battery level callback function. + * + * Provide the LoRaWAN stack with a function to be called whenever a battery + * level needs to be read. + * + * Should no callback be provided the lorawan backend will report 255. + * + * @param cb Pointer to the battery level function + */ +void lorawan_register_battery_level_callback(lorawan_battery_level_cb_t cb); + +/** + * @brief Register a battery voltage callback function. + * + * Provide the LoRaWAN stack with a function to be called whenever a battery + * voltage needs to be read. + * + * + * @param cb Pointer to the battery voltage function + */ +void lorawan_register_battery_voltage_callback(lorawan_battery_voltage_cb_t cb); + +/** + * @brief Register a temperature callback function. + * + * Provide the LoRaWAN stack with a function to be called whenever the system + * temperature needs to be read. + * + * @param cb Pointer to the temperature function + */ +void lorawan_register_temperature_callback(lorawan_temperature_cb_t cb); + +#ifdef CONFIG_LORA_BASICS_MODEM_FUOTA +/** + * @brief Register callbacks for the LoRaWAN Firmware Upgrade OTA feature. + * + * Calling this too late (after Device Management messages are received) might + * create undefined behaviour. See documentation at: + * https://resources.lora-alliance.org/technical-specifications/ts006-1-0-0-firmware-management-protocol + * + * @param[in] cb The callbacks to use for the hal implementation. All must be set. + * + */ +void lorawan_register_fuota_callbacks(struct lorawan_fuota_cb *cb); + +#endif /* CONFIG_LORA_BASICS_MODEM_FUOTA */ + +#ifdef CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL +/** + * @brief Register the user storage implementation callbacks for the LoRaWAN stack.. + * + * Calling this too late (after smtc_modem_init) might create undefined behaviour. + * + * @param[in] cb The callbacks to use for the hal implementation. All must be set. + * + */ +void lorawan_register_user_storage_callbacks(struct lorawan_user_storage_cb *cb); + +#endif /* CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL */ + +/** + * @brief Interruptible sleep that will exit when radio events happen. + * + */ +void smtc_modem_hal_interruptible_msleep(k_timeout_t timeout); + +/** + * @brief If smtc_modem_hal_interruptible_msleep is running, interrupt it. + * + * This function is used to trigger the LoRa Basics Modem stack when an event occurs, + * whether it is an interrupt from the transceiver, a timer or a user request. + * + */ +void smtc_modem_hal_wake_up(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SMTC_MODEM_HAL_INIT_H */ diff --git a/include/zephyr/lorawan_lbm/lorawan_main_thread.h b/include/zephyr/lorawan_lbm/lorawan_main_thread.h new file mode 100644 index 0000000000000..2e79f03be56ff --- /dev/null +++ b/include/zephyr/lorawan_lbm/lorawan_main_thread.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SUBSYS_LORAWAN_LBM_LBM_MAIN_THREAD_H +#define SUBSYS_LORAWAN_LBM_LBM_MAIN_THREAD_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the LoRa Basics Modem callbacks and starts its work thread. + * + * @param event_callback The callback that will be called each time an modem event + * is raised internally + */ + +void lora_basics_modem_start_work_thread(void (*event_callback)(void)); + +#ifdef __cplusplus +} +#endif + +#endif /* SUBSYS_LORAWAN_LBM_LBM_MAIN_THREAD_H */ diff --git a/modules/Kconfig b/modules/Kconfig index a8cf861a68096..d954048288c34 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -96,6 +96,9 @@ comment "Lz4 module not available." comment "loramac-node module not available." depends on !ZEPHYR_LORAMAC_NODE_MODULE +comment "LoRa Basics Modem module not available." + depends on !ZEPHYR_LORA_BASICS_MODEM_MODULE + comment "CANopenNode module not available." depends on !ZEPHYR_CANOPENNODE_MODULE diff --git a/modules/lora_basics_modem/CMakeLists.txt b/modules/lora_basics_modem/CMakeLists.txt new file mode 100644 index 0000000000000..a028ebee16a9d --- /dev/null +++ b/modules/lora_basics_modem/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_ZEPHYR_LORA_BASICS_MODEM_MODULE) + # Based on makefiles from Semtech + + set(LORA_BASICS_MODEM_DIR ${ZEPHYR_CURRENT_MODULE_DIR}) + set(LBM_LIB_DIR ${LORA_BASICS_MODEM_DIR}/lbm_lib) + set(LBM_SMTC_MODEM_CORE_DIR ${LBM_LIB_DIR}/smtc_modem_core) + set(LBM_RADIO_DRIVERS_DIR ${LBM_LIB_DIR}/smtc_modem_core/radio_drivers) + + zephyr_library() + + if(CONFIG_SEMTECH_LR11XX) + include(${CMAKE_CURRENT_LIST_DIR}/lr11xx.cmake) + endif() + + if(CONFIG_SEMTECH_SX126X) + include(${CMAKE_CURRENT_LIST_DIR}/sx126x.cmake) + endif() + + if(CONFIG_LORA_BASICS_MODEM_DRIVERS_RAL_RALF) + zephyr_include_directories( + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ral/src + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ralf/src + ) + zephyr_library_sources( + ${LBM_RAL_SOURCES} + ${LBM_RALF_SOURCES} + ) + endif() + + if(CONFIG_LORA_BASICS_MODEM) + + include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) + + zephyr_library_include_directories( + ${CMAKE_CURRENT_LIST_DIR}/smtc_modem_hal + ) + + zephyr_library_sources( + ${CMAKE_CURRENT_LIST_DIR}/smtc_modem_hal/smtc_modem_hal.c + ${CMAKE_CURRENT_LIST_DIR}/smtc_modem_hal/smtc_modem_hal_storage.c + ${CMAKE_CURRENT_LIST_DIR}/smtc_modem_hal/smtc_modem_hal_dbg_trace.c + ) + + endif() + +endif() diff --git a/modules/lora_basics_modem/Kconfig b/modules/lora_basics_modem/Kconfig new file mode 100644 index 0000000000000..324e8f7b66669 --- /dev/null +++ b/modules/lora_basics_modem/Kconfig @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Top-level configuration file for LoRa Basics Modem containing LoRa +# transceiver drivers and LBM LoRaWAN stack + +config ZEPHYR_LORA_BASICS_MODEM_MODULE + bool diff --git a/modules/lora_basics_modem/common.cmake b/modules/lora_basics_modem/common.cmake new file mode 100644 index 0000000000000..9f90411918407 --- /dev/null +++ b/modules/lora_basics_modem/common.cmake @@ -0,0 +1,380 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +#----------------------------------------------------------------------------- +# Regions +#----------------------------------------------------------------------------- + +include(${CMAKE_CURRENT_LIST_DIR}/regions.cmake) + +#----------------------------------------------------------------------------- +# Relay +#----------------------------------------------------------------------------- + +include(${CMAKE_CURRENT_LIST_DIR}/relay.cmake) + +#----------------------------------------------------------------------------- +# Common +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions(NUMBER_OF_STACKS=${CONFIG_LORA_BASICS_MODEM_NUMBER_OF_STACKS}) + +# SMTC_MODEM_CORE_C_SOURCES +zephyr_library_sources( + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_api/lorawan_api.c + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem.c + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_test.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_utilities/modem_event_utilities.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_utilities/fifo_ctrl.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_utilities/modem_core.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_supervisor/modem_supervisor_light.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_supervisor/modem_tx_protocol_manager.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/lorawan_certification/lorawan_certification.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager/lorawan_join_management.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager/lorawan_send_management.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager/lorawan_cid_request_management.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager/lorawan_dwn_ack_management.c +) + +# LR1MAC_C_SOURCES +zephyr_library_sources( + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1_stack_mac_layer.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_core.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_utilities.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/smtc_real/src/smtc_real.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services/smtc_duty_cycle.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services/smtc_lbt.c +) + +# SMTC_MODEM_CRYPTO_C_SOURCES +zephyr_library_sources( + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/smtc_modem_crypto.c +) + +# RADIO_PLANNER_C_SOURCES +zephyr_library_sources( + ${LBM_SMTC_MODEM_CORE_DIR}/radio_planner/src/radio_planner.c +) + +zephyr_include_directories( + ${LBM_LIB_DIR}/smtc_modem_api + # NOTE: it is only used by the samples, could be cleaned up. + ${LBM_LIB_DIR}/smtc_modem_hal +) + +zephyr_library_include_directories( + ${LBM_LIB_DIR} + ${LBM_SMTC_MODEM_CORE_DIR} + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_api + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/lorawan_certification + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/smtc_real/src + ${LBM_SMTC_MODEM_CORE_DIR}/modem_supervisor + ${LBM_SMTC_MODEM_CORE_DIR}/modem_utilities + ${LBM_SMTC_MODEM_CORE_DIR}/radio_planner/src + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/smtc_secure_element + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ral/src + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ralf/src +) + +#----------------------------------------------------------------------------- +# ALCSync +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_ALC_SYNC + ADD_SMTC_ALC_SYNC +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_ALC_SYNC_V1 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/application_layer_clock_synchronization/v1.0.0/lorawan_alcsync_v1.0.0.c +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_ALC_SYNC_V2 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/application_layer_clock_synchronization/v2.0.0/lorawan_alcsync_v2.0.0.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_ALC_SYNC + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/application_layer_clock_synchronization +) + +#----------------------------------------------------------------------------- +# FUOTA +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA + ADD_FUOTA=${CONFIG_LORA_BASICS_MODEM_FUOTA_VERSION} +) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_FMP + ENABLE_FUOTA_FMP +) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MPA + ENABLE_FUOTA_MPA +) + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_NB_OF_FRAGMENTS_CONFIG + FRAG_MAX_NB=${CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_NB_OF_FRAGMENTS} +) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_SIZE_OF_FRAGMENTS + FRAG_MAX_SIZE=${CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_SIZE_OF_FRAGMENTS} +) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_FRAGMENTS_REDUNDANCY + FRAG_MAX_REDUNDANCY=${CONFIG_LORA_BASICS_MODEM_FUOTA_MAX_FRAGMENTS_REDUNDANCY} +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_V1 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v1.0.0/fragmentation_helper_v1.0.0.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v1.0.0/lorawan_fragmentation_package_v1.0.0.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/remote_multicast_setup/v1.0.0/lorawan_remote_multicast_setup_package_v1.0.0.c +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_V2 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v2.0.0/fragmentation_helper_v2.0.0.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v2.0.0/lorawan_fragmentation_package_v2.0.0.c + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/remote_multicast_setup/v2.0.0/lorawan_remote_multicast_setup_package_v2.0.0.c +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_FMP + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/firmware_management_protocol/lorawan_fmp_package.c +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MPA + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/multi_package_access/lorawan_mpa_package.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/application_layer_clock_synchronization + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/remote_multicast_setup + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/firmware_management_protocol/ +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_V1 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v1.0.0 +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_V2 + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/fragmented_data_block_transport/v2.0.0 +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_FMP + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/firmware_management_protocol +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_FUOTA_MPA + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_packages/multi_package_access +) + +#----------------------------------------------------------------------------- +# Crypto soft +#----------------------------------------------------------------------------- + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/soft_secure_element/aes.c + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/soft_secure_element/cmac.c + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/soft_secure_element/soft_se.c +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/soft_secure_element +) + +#----------------------------------------------------------------------------- +# Class B +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_B + ADD_CLASS_B +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_B + ${LBM_SMTC_MODEM_CORE_DIR}/lorawan_manager/lorawan_class_b_management.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_class_b/smtc_beacon_sniff.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_class_b/smtc_ping_slot.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_B + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_class_b +) + +#----------------------------------------------------------------------------- +# Class C +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_C + ADD_CLASS_C +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_C + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_class_c/lr1mac_class_c.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_CLASS_C + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/lr1mac_class_c +) + +#----------------------------------------------------------------------------- +# Multicast +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_MULTICAST + SMTC_MULTICAST +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_MULTICAST + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services/smtc_multicast/smtc_multicast.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_MULTICAST + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services/smtc_multicast +) + +#----------------------------------------------------------------------------- +# CSMA +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CSMA + ADD_CSMA +) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CSMA_BY_DEFAULT + ENABLE_CSMA_BY_DEFAULT +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CSMA + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/services/smtc_lora_cad_bt.c +) + +#----------------------------------------------------------------------------- +# Almanac +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_ALMANAC + ADD_ALMANAC +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_ALMANAC + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/almanac_packages/almanac.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_ALMANAC + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/almanac_packages +) + +#----------------------------------------------------------------------------- +# Stream +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_STREAM + ADD_SMTC_STREAM +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_STREAM + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/stream_packages/stream.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/stream_packages/rose.c +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_STREAM + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/stream_packages +) + +#----------------------------------------------------------------------------- +# Large File Upload +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_LFU + ADD_SMTC_LFU +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_LFU + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/lfu_service/file_upload.c +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_LFU + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/lfu_service +) + +#----------------------------------------------------------------------------- +# Device Management +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_DEVICE_MANAGEMENT + ADD_SMTC_CLOUD_DEVICE_MANAGEMENT +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_DEVICE_MANAGEMENT + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/cloud_dm_package/cloud_dm_package.c +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_DEVICE_MANAGEMENT + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/cloud_dm_package +) + +#----------------------------------------------------------------------------- +# Geolocation +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_GEOLOCATION + ADD_LBM_GEOLOCATION +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_GEOLOCATION + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_common.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_gnss_scan.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_gnss_send.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_gnss_almanac.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/gnss_helpers.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_wifi_scan.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/mw_wifi_send.c + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services/wifi_helpers.c +) + +# Used in publicly-included headers +zephyr_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_GEOLOCATION + ${LBM_SMTC_MODEM_CORE_DIR}/geolocation_services +) + +#----------------------------------------------------------------------------- +# Store and Forward +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_STORE_AND_FORWARD + ADD_SMTC_STORE_AND_FORWARD +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_STORE_AND_FORWARD + ${LBM_SMTC_MODEM_CORE_DIR}/modem_utilities/circularfs.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/store_and_forward/store_and_forward_flash.c +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_STORE_AND_FORWARD + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/store_and_forward +) + +#----------------------------------------------------------------------------- +# Beacon TX +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_BEACON_TX + MODEM_BEACON_APP +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_BEACON_TX + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/beacon_tx_service/lorawan_beacon_tx_service_example.c +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_MULTICAST + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/beacon_tx_service +) + +#----------------------------------------------------------------------------- +# Misc +#----------------------------------------------------------------------------- + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_PERF_TEST + PERF_TEST_ENABLED +) + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_DISABLE_JOIN_DUTY_CYCLE + TEST_BYPASS_JOIN_DUTY_CYCLE +) + +if (CONFIG_LORA_BASICS_MODEM_DISABLE_JOIN_DUTY_CYCLE) + message(WARNING " + Warning: LORA_BASICS_MODEM_DISABLE_JOIN_DUTY_CYCLE should only be enabled + when testing in an isolated setup (e.g with coaxial cables between gateway + and device), and should absolutely not be enabled when emitting in open air.") +endif() diff --git a/modules/lora_basics_modem/lr11xx.cmake b/modules/lora_basics_modem/lr11xx.cmake new file mode 100644 index 0000000000000..52e9bd5a66116 --- /dev/null +++ b/modules/lora_basics_modem/lr11xx.cmake @@ -0,0 +1,68 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +set(LBM_LR11XX_DIR ${LBM_RADIO_DRIVERS_DIR}/lr11xx_driver/src) + +#----------------------------------------------------------------------------- +# Radio specific sources +#----------------------------------------------------------------------------- + +zephyr_library_sources( + ${LBM_LR11XX_DIR}/lr11xx_bootloader.c + ${LBM_LR11XX_DIR}/lr11xx_crypto_engine.c + ${LBM_LR11XX_DIR}/lr11xx_driver_version.c + ${LBM_LR11XX_DIR}/lr11xx_radio.c + ${LBM_LR11XX_DIR}/lr11xx_regmem.c + ${LBM_LR11XX_DIR}/lr11xx_system.c + ${LBM_LR11XX_DIR}/lr11xx_lr_fhss.c +) + +# LR1121 is the only one not supporting GNSS and WiFi +if(CONFIG_DT_HAS_SEMTECH_LR1110_ENABLED OR CONFIG_DT_HAS_SEMTECH_LR1120_ENABLED) + zephyr_library_sources( + ${LBM_LR11XX_DIR}/lr11xx_wifi.c + ${LBM_LR11XX_DIR}/lr11xx_gnss.c + ) +endif() + +set(LBM_RAL_SOURCES ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ral/src/ral_lr11xx.c) +set(LBM_RALF_SOURCES ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ralf/src/ralf_lr11xx.c) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/lr11xx_crypto_engine/lr11xx_ce.c +) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX_WITH_CREDENTIALS + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/lr11xx_crypto_engine/lr11xx_ce.c +) + +#----------------------------------------------------------------------------- +# Includes +#----------------------------------------------------------------------------- + +# Used in publicly-included headers +zephyr_include_directories(${LBM_LR11XX_DIR}) + +zephyr_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/lr11xx_crypto_engine +) +zephyr_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX_WITH_CREDENTIALS + ${LBM_SMTC_MODEM_CORE_DIR}/smtc_modem_crypto/lr11xx_crypto_engine +) + +#----------------------------------------------------------------------------- +# Radio specific compilation flags +#----------------------------------------------------------------------------- + +zephyr_compile_definitions(LR11XX) + +zephyr_library_compile_definitions(LR11XX_TRANSCEIVER LR11XX_DISABLE_WARNINGS) + +# Used in publicly-included headers +zephyr_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX + USE_LR11XX_CE) + +zephyr_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX_WITH_CREDENTIALS + USE_LR11XX_CE) + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX_WITH_CREDENTIALS + USE_PRE_PROVISIONED_FEATURES) diff --git a/modules/lora_basics_modem/regions.cmake b/modules/lora_basics_modem/regions.cmake new file mode 100644 index 0000000000000..b004e7216e0d4 --- /dev/null +++ b/modules/lora_basics_modem/regions.cmake @@ -0,0 +1,32 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +set(LBM_SMTC_REGIONS_DIR ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/smtc_real/src/) + +# Regional parameters version +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_RP2_101 RP2_101) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_RP2_103 RP2_103) + +# enabled regions +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_AS_923 REGION_AS_923) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_AU_915 REGION_AU_915) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_CN_470 REGION_CN_470) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_CN_470_RP_1_0 REGION_CN_470_RP_1_0) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_EU_868 REGION_EU_868) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_IN_865 REGION_IN_865) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_KR_920 REGION_KR_920) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_RU_864 REGION_RU_864) +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_US_915 REGION_US_915) +# the underscore missing here is intentional! +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_WW_2G4 REGION_WW2G4 WW2G4_SINGLE_DATARATE) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_AS_923 ${LBM_SMTC_REGIONS_DIR}/region_as_923.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_AU_915 ${LBM_SMTC_REGIONS_DIR}/region_au_915.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_CN_470 ${LBM_SMTC_REGIONS_DIR}/region_cn_470.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_CN_470_RP_1_0 ${LBM_SMTC_REGIONS_DIR}/region_cn_470_rp_1_0.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_EU_868 ${LBM_SMTC_REGIONS_DIR}/region_eu_868.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_IN_865 ${LBM_SMTC_REGIONS_DIR}/region_in_865.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_KR_920 ${LBM_SMTC_REGIONS_DIR}/region_kr_920.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_RU_864 ${LBM_SMTC_REGIONS_DIR}/region_ru_864.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_US_915 ${LBM_SMTC_REGIONS_DIR}/region_us_915.c) +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_REGION_WW_2G4 ${LBM_SMTC_REGIONS_DIR}/region_ww2g4.c) diff --git a/modules/lora_basics_modem/relay.cmake b/modules/lora_basics_modem/relay.cmake new file mode 100644 index 0000000000000..61f717dbc7a19 --- /dev/null +++ b/modules/lora_basics_modem/relay.cmake @@ -0,0 +1,42 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_RX + ADD_RELAY_RX +) + +zephyr_library_compile_definitions_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_TX + ADD_RELAY_TX + USE_RELAY_TX +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/common + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/relay_service +) +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_RX + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_rx +) + +zephyr_library_include_directories_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_TX + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_tx +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/common/wake_on_radio.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/common/relay_real.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/common/wake_on_radio_ral.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/common/relay_mac_parser.c +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_RX + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_rx/relay_rx_mac_parser.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_rx/relay_rx.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/relay_service/lorawan_relay_rx_service.c +) + +zephyr_library_sources_ifdef(CONFIG_LORA_BASICS_MODEM_RELAY_TX + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_tx/relay_tx_mac_parser.c + ${LBM_SMTC_MODEM_CORE_DIR}/lr1mac/src/relay/relay_tx/relay_tx.c + ${LBM_SMTC_MODEM_CORE_DIR}/modem_services/relay_service/lorawan_relay_tx_service.c +) diff --git a/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal.c b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal.c new file mode 100644 index 0000000000000..841491b5e6d04 --- /dev/null +++ b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_REGISTER(lorawan_hal, CONFIG_LORA_BASICS_MODEM_LOG_LEVEL); + +/* ------------ Local context ------------ */ + +/* transceiver device pointer */ +static const struct device *prv_transceiver_dev; + +/* External callbacks */ +static lorawan_battery_level_cb_t battery_level_cb; +static lorawan_battery_voltage_cb_t battery_voltage_cb; +static lorawan_temperature_cb_t temperature_cb; +#ifdef CONFIG_LORA_BASICS_MODEM_FUOTA +static struct lorawan_fuota_cb *fuota_cb; +#endif + +/* A binary semaphore to notify the main LBM loop */ +static K_SEM_DEFINE(lbm_main_loop_sem, 0, 1); + +/* context and callback for modem_hal_timer */ +static void *prv_smtc_modem_hal_timer_context; +static void (*prv_smtc_modem_hal_timer_callback)(void *context); + +/* flag for enabling/disabling timer interrupt. This is set by the libraray during "critical" + * sections + */ +static bool prv_modem_irq_enabled = true; +static bool prv_modem_irq_pending_while_disabled; +static bool prv_radio_irq_pending_while_disabled; + +/* The timer and work used for the modem_hal_timer */ +static void prv_smtc_modem_hal_timer_handler(struct k_timer *timer); +static K_TIMER_DEFINE(prv_smtc_modem_hal_timer, prv_smtc_modem_hal_timer_handler, NULL); + +/* context and callback for the event pin interrupt */ +static void *prv_smtc_modem_hal_radio_irq_context; +static void (*prv_smtc_modem_hal_radio_irq_callback)(void *context); + +/* ------------ Initialization ------------ + * + * This function is defined in lorawan_hal_init.h + * and is used to set everything up in here. + */ + +void lorawan_smtc_modem_hal_init(const struct device *transceiver) +{ + __ASSERT(transceiver, "transceiver must be provided"); + prv_transceiver_dev = transceiver; + smtc_modem_set_radio_context(prv_transceiver_dev); +} + +/* ------------ System management ------------ */ + +void smtc_modem_hal_reset_mcu(void) +{ + LOG_WRN("Resetting the MCU"); + log_panic(); /* To flush the logs */ + k_msleep(100); + sys_reboot(SYS_REBOOT_COLD); +} + +void smtc_modem_hal_reload_wdog(void) +{ + /* This is only provided for internal debugging purposes, so it was + * decided not to implement it in the Zephyr port. + */ +} + +uint32_t smtc_modem_hal_get_time_in_s(void) +{ + return k_uptime_seconds(); +} + +uint32_t smtc_modem_hal_get_time_in_ms(void) +{ + /* The wrapping every 49 days is expected by the modem lib */ + return k_uptime_get_32(); +} + +void smtc_modem_hal_set_offset_to_test_wrapping(const uint32_t offset_to_test_wrapping) +{ + /* This aims to add a virtual offset to values returned by smtc_modem_hal_get_time_in_ms. + * This is only provided for internal development purposes, so it was + * decided not to implement it in the Zephyr port. + */ +} + +void smtc_modem_hal_interruptible_msleep(k_timeout_t timeout) +{ + /* Sleep until we are notified by smtc_modem_hal_wake_up(). */ + k_sem_take(&lbm_main_loop_sem, timeout); +} + +void smtc_modem_hal_wake_up(void) +{ + /* Notify the main loop if it's sleeping */ + k_sem_give(&lbm_main_loop_sem); +} + +void smtc_modem_hal_user_lbm_irq(void) +{ + smtc_modem_hal_wake_up(); +} + +/* ------------ Timer management ------------ */ + +/** + * @brief Called when the prv_smtc_modem_hal_timer expires. + * + * Submits the prv_smtc_modem_hal_timer_work to be handle the callback. + */ +static void prv_smtc_modem_hal_timer_handler(struct k_timer *timer) +{ + ARG_UNUSED(timer); + + if (prv_modem_irq_enabled) { + prv_smtc_modem_hal_timer_callback(prv_smtc_modem_hal_timer_context); + } else { + prv_modem_irq_pending_while_disabled = true; + } +}; + +void smtc_modem_hal_start_timer(const uint32_t milliseconds, void (*callback)(void *context), + void *context) +{ + prv_smtc_modem_hal_timer_callback = callback; + prv_smtc_modem_hal_timer_context = context; + + /* start one-shot timer */ + k_timer_start(&prv_smtc_modem_hal_timer, K_MSEC(milliseconds), K_NO_WAIT); +} + +void smtc_modem_hal_stop_timer(void) +{ + k_timer_stop(&prv_smtc_modem_hal_timer); +} + +/* ------------ IRQ management ------------ */ + +void smtc_modem_hal_disable_modem_irq(void) +{ + prv_modem_irq_enabled = false; +} + +void smtc_modem_hal_enable_modem_irq(void) +{ + prv_modem_irq_enabled = true; + lora_transceiver_board_enable_interrupt(prv_transceiver_dev); + + if (prv_radio_irq_pending_while_disabled) { + prv_radio_irq_pending_while_disabled = false; + prv_smtc_modem_hal_radio_irq_callback(prv_smtc_modem_hal_radio_irq_context); + } + if (prv_modem_irq_pending_while_disabled) { + prv_modem_irq_pending_while_disabled = false; + prv_smtc_modem_hal_timer_callback(prv_smtc_modem_hal_timer_context); + } +} + +/* ------------ Panic management ------------ */ + +void smtc_modem_hal_on_panic(uint8_t *func, uint32_t line, const char *fmt, ...) +{ + const size_t buffer_size = 255; + uint8_t buffer[buffer_size]; + uint8_t length; + va_list args; + + /* NOTE: uint8_t *func parameter is actually __func__ casted to uint8_t*, + * so it can be safely printed with %s + */ + length = snprintf((char *)buffer, buffer_size, "%s:%u ", func, line); + va_start(args, fmt); + length += vsnprintf((char *)buffer + length, buffer_size - length, fmt, args); + va_end(args); + + LOG_ERR("Modem panic: %s", buffer); + + /* NOTE: smtc_modem_hal_crashlog_set_status(true) is done by + * smtc_modem_hal_crashlog_store() for simplicity of flash usage + */ + smtc_modem_hal_crashlog_store(buffer, length); + + smtc_modem_hal_reset_mcu(); +} + +/* ------------ Random management ------------ */ + +uint32_t smtc_modem_hal_get_random_nb_in_range(const uint32_t val_1, const uint32_t val_2) +{ + /* Implementation copied from the lbm_examples */ + uint32_t min = MIN(val_1, val_2); + uint32_t max = MAX(val_1, val_2); + uint32_t range = (max - min + 1); + + /* Fix cases when val1=0, val2=UINT32_MAX */ + range = range ? range : UINT32_MAX; + return (uint32_t)((sys_rand32_get() % range) + min); +} + +/* ------------ Radio env management ------------ */ + +/** + * @brief Called when the transceiver event pin interrupt is triggered. + * + * If CONFIG_LORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_GLOBAL_THREAD=y, + * this is called in the system workq. + * If CONFIGLORA_BASICS_MODEM_DRIVERS_EVENT_TRIGGER_OWN_THREAD=y, + * this is called in the transceiver event thread. + * + * @param[in] dev The transceiver device. + */ +void prv_transceiver_event_cb(const struct device *dev) +{ + if (prv_modem_irq_enabled) { + /* Due to the way the transceiver driver is implemented, + * this is called from the system workq. + */ + prv_smtc_modem_hal_radio_irq_callback(prv_smtc_modem_hal_radio_irq_context); + } else { + prv_radio_irq_pending_while_disabled = true; + } +} + +void smtc_modem_hal_irq_config_radio_irq(void (*callback)(void *context), void *context) +{ + /* save callback function and context */ + prv_smtc_modem_hal_radio_irq_context = context; + prv_smtc_modem_hal_radio_irq_callback = callback; + + /* enable callback via transceiver driver */ + lora_transceiver_board_attach_interrupt(prv_transceiver_dev, prv_transceiver_event_cb); + lora_transceiver_board_enable_interrupt(prv_transceiver_dev); +} + +void smtc_modem_hal_radio_irq_clear_pending(void) +{ + prv_radio_irq_pending_while_disabled = false; +} + +void smtc_modem_hal_start_radio_tcxo(void) +{ + /* TODO: We only support TCXO's that are wired to the transceiver. + * In such cases, this function must be empty according to the porting guide. + * https://github.com/Lora-net/SWL2001/blob/master/lbm_lib/PORTING_GUIDE.md#void-smtc_modem_hal_start_radio_tcxo-void- + */ +} + +void smtc_modem_hal_stop_radio_tcxo(void) +{ + /* TODO: We only support TCXO's that are wired to the transceiver. + * In such cases, this function must be empty according to the porting guide. + * https://github.com/Lora-net/SWL2001/blob/master/lbm_lib/PORTING_GUIDE.md#void-smtc_modem_hal_stop_radio_tcxo-void- + */ +} + +uint32_t smtc_modem_hal_get_radio_tcxo_startup_delay_ms(void) +{ + /* From the porting guide: + * If the TCXO is configured by the RAL BSP to start up automatically, then the value used + * here should be the same as the startup delay used in the RAL BSP. + * https://github.com/Lora-net/SWL2001/blob/master/lbm_lib/PORTING_GUIDE.md#uint32_t-smtc_modem_hal_get_radio_tcxo_startup_delay_ms-void- + */ + return lora_transceiver_get_tcxo_startup_delay_ms(prv_transceiver_dev); +} + +void smtc_modem_hal_set_ant_switch(bool is_tx_on) +{ + /* NOTE: We only support antenna switches managed by the transceiver. + */ +} + +/* ------------ Environment management ------------ */ + +void lorawan_register_battery_level_callback(lorawan_battery_level_cb_t cb) +{ + battery_level_cb = cb; +} + +void lorawan_register_battery_voltage_callback(lorawan_battery_voltage_cb_t cb) +{ + battery_voltage_cb = cb; +} + +void lorawan_register_temperature_callback(lorawan_temperature_cb_t cb) +{ + temperature_cb = cb; +} + +uint8_t smtc_modem_hal_get_battery_level(void) +{ + if (battery_level_cb) { + return battery_level_cb(); + } else { + return 255; + } +} + +uint16_t smtc_modem_hal_get_voltage_mv(void) +{ + if (battery_voltage_cb) { + return battery_voltage_cb(); + } else { + return 0; + } +} + +int8_t smtc_modem_hal_get_temperature(void) +{ + if (temperature_cb) { + return temperature_cb(); + } else { + return -127; + } +} + +/* ------------ Misc ------------ */ + +int8_t smtc_modem_hal_get_board_delay_ms(void) +{ + /* The wakeup time is probably closer to 0ms than 1ms, + * but just to be safe: + */ +#if defined(CONFIG_DT_HAS_SEMTECH_LR1121_ENABLED) + return 2; +#else + return 1; +#endif +} + +/* ------------ FUOTA ------------ */ + +#if defined(CONFIG_LORA_BASICS_MODEM_FUOTA) + +void lorawan_register_fuota_callbacks(struct lorawan_fuota_cb *cb) +{ + fuota_cb = cb; +} + +uint32_t smtc_modem_hal_get_hw_version_for_fuota(void) +{ + if (!fuota_cb || !fuota_cb->get_hw_version) { + LOG_WRN("Call to unimplemented get_hw_version_for_fuota"); + return 0; + } else { + return fuota_cb->get_hw_version(); + } +} + +uint32_t smtc_modem_hal_get_fw_version_for_fuota(void) +{ + if (!fuota_cb || !fuota_cb->get_fw_version) { + LOG_WRN("Call to unimplemented get_fw_version_for_fuota"); + return 0; + } else { + return fuota_cb->get_fw_version(); + } +} + +uint8_t smtc_modem_hal_get_fw_status_available_for_fuota(void) +{ + if (!fuota_cb || !fuota_cb->get_fw_status_available) { + LOG_WRN("Call to unimplemented get_fw_status_available_for_fuota"); + return 0; + } else { + return fuota_cb->get_fw_status_available(); + } +} + +uint32_t smtc_modem_hal_get_next_fw_version_for_fuota(void) +{ + if (!fuota_cb || !fuota_cb->get_next_fw_version) { + LOG_WRN("Call to unimplemented get_next_fw_version_for_fuota"); + return 0; + } else { + return fuota_cb->get_next_fw_version(); + } +} + +uint8_t smtc_modem_hal_get_fw_delete_status_for_fuota(uint32_t fw_version) +{ + if (!fuota_cb || !fuota_cb->get_fw_delete_status) { + LOG_WRN("Call to unimplemented get_fw_delete_status_for_fuota"); + return 0; + } else { + return fuota_cb->get_fw_delete_status(fw_version); + } +} +#endif /* USE_FUOTA */ diff --git a/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.c b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.c new file mode 100644 index 0000000000000..168464852cca6 --- /dev/null +++ b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +/* Prevent LOG_MODULE_DECLARE and LOG_MODULE_REGISTER being called together */ +#define SMTC_MODEM_HAL_DBG_TRACE_C +#include + +LOG_MODULE_REGISTER(lorawan, CONFIG_LORA_BASICS_MODEM_LOG_LEVEL); + +void smtc_str_trim_end(char *text) +{ + /* Find first trailing space */ + char *end = text + strlen(text) - 1; + while (end > text && isspace((unsigned char)*end)) { + end--; + } + + /* Write new null terminator character */ + end[1] = '\0'; +} diff --git a/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.h b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.h new file mode 100644 index 0000000000000..37fd40e36ce7d --- /dev/null +++ b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_dbg_trace.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SMTC_MODEM_HAL_DBG_TRACE_H__ +#define __SMTC_MODEM_HAL_DBG_TRACE_H__ + +/** + * @brief This file is provided *to* the LoRa Basics Modem source code as a + * HAL implementation of its logging API. + * It also provides some definitions like MODEM_HAL_FEATURE_ON. + */ + +#include + +#include + +/* NOTE: Only required because other sources are missing this include… */ +#include "smtc_modem_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SMTC_MODEM_HAL_DBG_TRACE_C +LOG_MODULE_DECLARE(lorawan, CONFIG_LORA_BASICS_MODEM_LOG_LEVEL); +#endif + +#define MODEM_HAL_FEATURE_OFF (0) +#define MODEM_HAL_FEATURE_ON (!MODEM_HAL_FEATURE_OFF) + +/** + * @brief Trims the end of a string + * + * This is used to trim Semtech's logging strings that contain trailing + * spaces and endlines. + * The trimming is done by writing a null character when the whitespaces start. + * + * @param[in] text The text to trim + */ +void smtc_str_trim_end(char *text); + +/* NOTE: modem_utilities/circularfs.c will be patched in LBM 4.9.0 to reduce this size */ +#define SMTC_PRINT_BUFFER_SIZE 220 + +/** + * @brief This macro matches the Semtech LBM logging macros with the logging + * macros LOG_* from zephyr, while trimming the endlines present in LBM. + */ +#define SMTC_LOG(log_fn, ...) \ + do { \ + char __log_buffer[SMTC_PRINT_BUFFER_SIZE]; \ + snprintf(__log_buffer, SMTC_PRINT_BUFFER_SIZE, __VA_ARGS__); \ + smtc_str_trim_end(__log_buffer); \ + log_fn("%s", __log_buffer); \ + } while (0) + +#define MODEM_HAL_DBG_TRACE MODEM_HAL_FEATURE_ON + +#define SMTC_MODEM_HAL_TRACE_MSG(msg) SMTC_LOG(LOG_INF, "%s", msg); +#define SMTC_MODEM_HAL_TRACE_PRINTF(...) SMTC_LOG(LOG_INF, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_INFO(...) SMTC_LOG(LOG_INF, __VA_ARGS__) +#define SMTC_MODEM_HAL_TRACE_WARNING(...) SMTC_LOG(LOG_WRN, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_ERROR(...) SMTC_LOG(LOG_ERR, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_ARRAY(msg, array, len) LOG_HEXDUMP_INF(array, len, msg); +#define SMTC_MODEM_HAL_TRACE_PACKARRAY(msg, array, len) LOG_HEXDUMP_INF(array, len, msg); + +#if CONFIG_LORA_BASICS_MODEM_LOG_VERBOSE + +#define MODEM_HAL_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON + +/* Deep debug trace default definitions*/ +#define SMTC_MODEM_HAL_TRACE_PRINTF_DEBUG(...) SMTC_LOG(LOG_DBG, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_MSG_DEBUG(msg) SMTC_LOG(LOG_DBG, "%s", msg); +#define SMTC_MODEM_HAL_TRACE_MSG_COLOR_DEBUG(msg, color) SMTC_LOG(LOG_DBG, "%s", msg); +#define SMTC_MODEM_HAL_TRACE_INFO_DEBUG(...) SMTC_LOG(LOG_INF, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_WARNING_DEBUG(...) SMTC_LOG(LOG_WRN, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_ERROR_DEBUG(...) SMTC_LOG(LOG_ERR, __VA_ARGS__); +#define SMTC_MODEM_HAL_TRACE_ARRAY_DEBUG(msg, array, len) LOG_HEXDUMP_DBG(array, len, msg); +#define SMTC_MODEM_HAL_TRACE_PACKARRAY_DEBUG(msg, array, len) LOG_HEXDUMP_DBG(array, len, msg); + +#else /* CONFIG_LORA_BASICS_MODEM_LOG_VERBOSE */ + +/* Deep debug trace default definitions*/ +#define SMTC_MODEM_HAL_TRACE_PRINTF_DEBUG(...) +#define SMTC_MODEM_HAL_TRACE_MSG_DEBUG(msg) +#define SMTC_MODEM_HAL_TRACE_MSG_COLOR_DEBUG(msg, color) +#define SMTC_MODEM_HAL_TRACE_INFO_DEBUG(...) +#define SMTC_MODEM_HAL_TRACE_WARNING_DEBUG(...) +#define SMTC_MODEM_HAL_TRACE_ERROR_DEBUG(...) +#define SMTC_MODEM_HAL_TRACE_ARRAY_DEBUG(msg, array, len) +#define SMTC_MODEM_HAL_TRACE_PACKARRAY_DEBUG(...) + +#endif /* CONFIG_LORA_BASICS_MODEM_LOG_VERBOSE */ + +#if CONFIG_LORA_BASICS_MODEM_RADIO_PLANNER_LOG_VERBOSE + +#define SMTC_MODEM_HAL_RP_TRACE_MSG(msg) SMTC_MODEM_HAL_TRACE_PRINTF(msg) +#define SMTC_MODEM_HAL_RP_TRACE_PRINTF(...) SMTC_MODEM_HAL_TRACE_PRINTF(__VA_ARGS__) + +#else /* CONFIG_LORA_BASICS_MODEM_RADIO_PLANNER_LOG_VERBOSE */ + +#define SMTC_MODEM_HAL_RP_TRACE_MSG(msg) +#define SMTC_MODEM_HAL_RP_TRACE_PRINTF(...) + +#endif /* CONFIG_LORA_BASICS_MODEM_RADIO_PLANNER_LOG_VERBOSE */ + +#if CONFIG_LORA_BASICS_MODEM_GEOLOCATION_LOG_VERBOSE +#define GNSS_ALMANAC_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON +#define GNSS_SCAN_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON +#define GNSS_SEND_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON +#define WIFI_SCAN_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON +#define WIFI_SEND_DEEP_DBG_TRACE MODEM_HAL_FEATURE_ON +#endif /* CONFIG_LORA_BASICS_MODEM_GEOLOCATION_LOG_VERBOSE */ + +#ifdef __cplusplus +} +#endif + +#endif /* __SMTC_MODEM_HAL_DBG_TRACE_H__*/ diff --git a/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_storage.c b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_storage.c new file mode 100644 index 0000000000000..459b6aadaeaee --- /dev/null +++ b/modules/lora_basics_modem/smtc_modem_hal/smtc_modem_hal_storage.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(lorawan_hal, CONFIG_LORA_BASICS_MODEM_LOG_LEVEL); + +#ifdef CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL +static struct lorawan_user_storage_cb *user_storage_cb; +#endif + +#ifdef CONFIG_LORA_BASICS_MODEM_PROVIDED_STORAGE_IMPL + +/* NOTE: That whole storage implementation uses direct flash access on the storage_partition + * from Zephyr instead of leaving it for NVS. + * Users are expected to provide `chosen/lora-basics-modem-context-partition` to prevent that. + * + * A second, more generic implementation via user-provided callbacks was started, with the goal + * to use the Zephyr NVS APIs, but the store-and-forward internal code from LBM prevents + * a simple use of this API, it is thus not complete nor working. + */ + +#if DT_HAS_CHOSEN(lora_basics_modem_context_partition) +#define CONTEXT_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(lora_basics_modem_context_partition)) +#else +#define CONTEXT_PARTITION FIXED_PARTITION_ID(storage_partition) +#endif + +const struct flash_area *context_flash_area; + +/* in case of multistack the size of the lorawan context shall be extended */ +#define ADDR_LORAWAN_CONTEXT_OFFSET 0 +#define ADDR_MODEM_KEY_CONTEXT_OFFSET 256 +#define ADDR_MODEM_CONTEXT_OFFSET 512 +#define ADDR_SECURE_ELEMENT_CONTEXT_OFFSET 768 +#define ADDR_CRASHLOG_CONTEXT_OFFSET 4096 +#define ADDR_STORE_AND_FORWARD_CONTEXT_OFFSET 8192 + +static void flash_init(void) +{ + if (context_flash_area) { + return; + } + int err = flash_area_open(CONTEXT_PARTITION, &context_flash_area); + + if (err != 0) { + LOG_ERR("Could not open flash area for context (%d)", err); + } + LOG_INF("Opened flash area of size %d", context_flash_area->fa_size); +} + +static uint32_t priv_hal_context_address(const modem_context_type_t ctx_type, uint32_t offset) +{ + switch (ctx_type) { + case CONTEXT_MODEM: + return ADDR_MODEM_CONTEXT_OFFSET + offset; + case CONTEXT_KEY_MODEM: + return ADDR_MODEM_KEY_CONTEXT_OFFSET + offset; + case CONTEXT_LORAWAN_STACK: + return ADDR_LORAWAN_CONTEXT_OFFSET + offset; + case CONTEXT_FUOTA: + /* no fuota example on stm32l0 */ + return 0; + case CONTEXT_STORE_AND_FORWARD: + return ADDR_STORE_AND_FORWARD_CONTEXT_OFFSET + offset; + case CONTEXT_SECURE_ELEMENT: + return ADDR_SECURE_ELEMENT_CONTEXT_OFFSET + offset; + } + k_oops(); + CODE_UNREACHABLE; +} + +void smtc_modem_hal_context_restore(const modem_context_type_t ctx_type, uint32_t offset, + uint8_t *buffer, const uint32_t size) +{ + const uint32_t real_offset = priv_hal_context_address(ctx_type, offset); + + flash_init(); + flash_area_read(context_flash_area, real_offset, buffer, size); +} + +/* NOTE: We take here the maximum erase size out there to do read-erase-write */ +#define PAGE_BUFFER_SIZE 4096 + +static uint8_t page_buffer[PAGE_BUFFER_SIZE]; + +/* NOTE: We assume that stores are only on one sector. + */ +void smtc_modem_hal_context_store(const modem_context_type_t ctx_type, uint32_t offset, + const uint8_t *buffer, const uint32_t size) +{ + const uint32_t real_offset = priv_hal_context_address(ctx_type, offset); + + flash_init(); + + /* read-erase-write */ + if (real_offset < ADDR_STORE_AND_FORWARD_CONTEXT_OFFSET) { + memset(page_buffer, 0, PAGE_BUFFER_SIZE); + flash_area_read(context_flash_area, 0, page_buffer, PAGE_BUFFER_SIZE); + memset(page_buffer + real_offset, 0, size); + memcpy(page_buffer + real_offset, buffer, size); + flash_area_erase(context_flash_area, 0, PAGE_BUFFER_SIZE); + flash_area_write(context_flash_area, 0, page_buffer, PAGE_BUFFER_SIZE); + } else { + /* Workaround because some 4-bytes writes will come while flash supports only 8 */ + const uint32_t real_size = size + 8 - (size % 8); + + memset(page_buffer, 0, real_size); + /* yes, size, not real_size */ + memcpy(page_buffer, buffer, size); + flash_area_write(context_flash_area, real_offset, page_buffer, real_size); + } +} + +/* NOTE: We assume that erases are aligned on sectors */ +void smtc_modem_hal_context_flash_pages_erase(const modem_context_type_t ctx_type, uint32_t offset, + uint8_t nb_page) +{ + const uint32_t real_offset = priv_hal_context_address(ctx_type, offset); + + flash_init(); + flash_area_erase(context_flash_area, real_offset, + smtc_modem_hal_flash_get_page_size() * nb_page); +} + +uint16_t smtc_modem_hal_flash_get_page_size(void) +{ + const struct device *flash_device; + struct flash_pages_info info; + + flash_init(); + flash_device = flash_area_get_device(context_flash_area); + flash_get_page_info_by_offs(flash_device, ADDR_STORE_AND_FORWARD_CONTEXT_OFFSET, &info); + return info.size; +} + +uint16_t smtc_modem_hal_store_and_forward_get_number_of_pages(void) +{ + uint16_t page_size; + size_t flash_size; + uint16_t pages_possible; + + page_size = smtc_modem_hal_flash_get_page_size(); + flash_size = context_flash_area->fa_size; + + /* 8192B are taken by contexts before store_and_forward */ + pages_possible = (flash_size - ADDR_STORE_AND_FORWARD_CONTEXT_OFFSET) / page_size; + + return pages_possible; +} + +void smtc_modem_hal_crashlog_store(const uint8_t *crashlog, uint8_t crash_string_length) +{ + flash_init(); + + memset(page_buffer, 0, PAGE_BUFFER_SIZE); + page_buffer[0] = 1; + page_buffer[1] = crash_string_length; + memcpy(page_buffer + 2, crashlog, crash_string_length); + + flash_area_erase(context_flash_area, ADDR_CRASHLOG_CONTEXT_OFFSET, PAGE_BUFFER_SIZE); + flash_area_write(context_flash_area, ADDR_CRASHLOG_CONTEXT_OFFSET, page_buffer, PAGE_BUFFER_SIZE); +} + +void smtc_modem_hal_crashlog_restore(uint8_t *crashlog, uint8_t *crash_string_length) +{ + flash_init(); + flash_area_read(context_flash_area, ADDR_CRASHLOG_CONTEXT_OFFSET, page_buffer, PAGE_BUFFER_SIZE); + int available = page_buffer[0]; + int length = page_buffer[1]; + + crashlog[0] = 0; + *crash_string_length = length; + + if (available != 0) { + memcpy(crashlog, page_buffer + 2, length); + } +} + +static int crashlog_status = -1; + +void smtc_modem_hal_crashlog_set_status(bool available) +{ + flash_init(); + if (!available) { + flash_area_erase(context_flash_area, ADDR_CRASHLOG_CONTEXT_OFFSET, + smtc_modem_hal_flash_get_page_size()); + } + crashlog_status = available ? 1 : 0; +} + +bool smtc_modem_hal_crashlog_get_status(void) +{ + if (crashlog_status == -1) { + flash_init(); + flash_area_read(context_flash_area, ADDR_CRASHLOG_CONTEXT_OFFSET, page_buffer, 1); + /* Any other state might mean uninitialized flash area */ + bool available = (page_buffer[0] == 1); + + crashlog_status = available ? 1 : 0; + } + + /* Any other state might mean uninitialized flash area */ + return crashlog_status == 1; +} + +#endif /* CONFIG_LORA_BASICS_MODEM_PROVIDED_STORAGE_IMPL */ + +#ifdef CONFIG_LORA_BASICS_MODEM_USER_STORAGE_IMPL + +/* As said in the comment at the beginning of the file, this implementation is neither + * finished nor working. This is because the store-and-forward implementation expects + * raw flash accesses, and "CONTEXT_*" have variable / unsure sizes. + */ + +void lorawan_register_user_storage_callbacks(struct lorawan_user_storage_cb *cb) +{ + user_storage_cb = cb; +} + +/* + * CONTEXT_MODEM -> size = 16 + * CONTEXT_KEY_MODEM -> size = 36 bytes + * CONTEXT_LORAWAN_STACK -> size = 32 + * CONTEXT_FUOTA -> variable size + * CONTEXT_SECURE_ELEMENT -> size = 24 or 483 (!!) + * CONTEXT_STORE_AND_FORWARD -> garbage + */ + +void smtc_modem_hal_context_restore(const modem_context_type_t ctx_type, uint32_t offset, + uint8_t *buffer, const uint32_t size) +{ + user_storage_cb->context_restore(ctx_type, offset, buffer, size); +} + +void smtc_modem_hal_context_store(const modem_context_type_t ctx_type, uint32_t offset, + const uint8_t *buffer, const uint32_t size) +{ + user_storage_cb->context_store(ctx_type, offset, buffer, size); +} + +#define CRASH_LOG_ID 0xFE +#define CRASH_LOG_STATUS_ID (CRASH_LOG_ID + 1) + +void smtc_modem_hal_crashlog_store(const uint8_t *crashlog, uint8_t crash_string_length) +{ + /* We use 0xFF as the ID so we do not overwrite any of the valid contexts */ + user_storage_cb->context_store(CRASH_LOG_ID, 0, crashlog, crash_string_length); +} + +void smtc_modem_hal_crashlog_restore(uint8_t *crashlog, uint8_t *crash_string_length) +{ + user_storage_cb->context_restore(CRASH_LOG_ID, 0, crashlog, *crash_string_length); +} + +void smtc_modem_hal_crashlog_set_status(bool available) +{ + user_storage_cb->context_store(CRASH_LOG_STATUS_ID, 0, (uint8_t *)&available, + sizeof(available)); +} + +bool smtc_modem_hal_crashlog_get_status(void) +{ + bool available; + + user_storage_cb->context_restore(CRASH_LOG_STATUS_ID, 0, (uint8_t *)&available, + sizeof(available)); + return available; +} + +/* NOTE: only used in store-and-forward */ +void smtc_modem_hal_context_flash_pages_erase(const modem_context_type_t ctx_type, uint32_t offset, + uint8_t nb_page) +{ +} + +uint16_t smtc_modem_hal_flash_get_page_size(void) +{ + return 0; +} + +uint16_t smtc_modem_hal_store_and_forward_get_number_of_pages(void) +{ + return 0; +} + +#endif diff --git a/modules/lora_basics_modem/sx126x.cmake b/modules/lora_basics_modem/sx126x.cmake new file mode 100644 index 0000000000000..f7dc93ccbb389 --- /dev/null +++ b/modules/lora_basics_modem/sx126x.cmake @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Used by LBM +zephyr_library_compile_definitions(SX126X) +zephyr_library_compile_definitions(SX126X_TRANSCEIVER) +zephyr_library_compile_definitions(SX126X_DISABLE_WARNINGS) +zephyr_library_compile_definitions_ifdef(CONFIG_DT_HAS_SEMTECH_SX1261_NEW_ENABLED SX1261) +zephyr_library_compile_definitions_ifdef(CONFIG_DT_HAS_SEMTECH_SX1262_NEW_ENABLED SX1262) +zephyr_library_compile_definitions_ifdef(CONFIG_DT_HAS_SEMTECH_SX1268_NEW_ENABLED SX1268) + +# Allow modem options +set(ALLOW_CSMA_BUILD true) + +set(LBM_SX126X_LIB_DIR ${LBM_RADIO_DRIVERS_DIR}/sx126x_driver/src) +zephyr_include_directories(${LBM_SX126X_LIB_DIR}) + +#----------------------------------------------------------------------------- +# Radio specific sources +#----------------------------------------------------------------------------- +zephyr_library_sources( + ${LBM_SX126X_LIB_DIR}/lr_fhss_mac.c + ${LBM_SX126X_LIB_DIR}/sx126x.c + ${LBM_SX126X_LIB_DIR}/sx126x_driver_version.c + ${LBM_SX126X_LIB_DIR}/sx126x_lr_fhss.c +) + +# Used later +set(LBM_RAL_SOURCES ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ral/src/ral_sx126x.c) +set(LBM_RALF_SOURCES ${LBM_SMTC_MODEM_CORE_DIR}/smtc_ralf/src/ralf_sx126x.c) diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 61f7f3cf940db..752b8603729b9 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -46,6 +46,7 @@ add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu) add_subdirectory_ifdef(CONFIG_INPUT input) add_subdirectory_ifdef(CONFIG_JWT jwt) add_subdirectory_ifdef(CONFIG_LLEXT llext) +add_subdirectory_ifdef(CONFIG_LORA_BASICS_MODEM lorawan_lbm) add_subdirectory_ifdef(CONFIG_MCTP mctp) add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem) add_subdirectory_ifdef(CONFIG_NETWORKING net) diff --git a/subsys/Kconfig b/subsys/Kconfig index 93880693f51b5..ac69b967b5de1 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -26,6 +26,7 @@ source "subsys/jwt/Kconfig" source "subsys/llext/Kconfig" source "subsys/logging/Kconfig" source "subsys/lorawan/Kconfig" +source "subsys/lorawan_lbm/Kconfig" source "subsys/mctp/Kconfig" source "subsys/mem_mgmt/Kconfig" source "subsys/mgmt/Kconfig" diff --git a/subsys/lorawan_lbm/CMakeLists.txt b/subsys/lorawan_lbm/CMakeLists.txt new file mode 100644 index 0000000000000..7697632412507 --- /dev/null +++ b/subsys/lorawan_lbm/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + + +if(CONFIG_LORA_BASICS_MODEM) + + +if(CONFIG_LORA_BASICS_MODEM_MAIN_THREAD) + zephyr_library() + zephyr_library_sources( + ${CMAKE_CURRENT_LIST_DIR}/lbm_main_thread.c + ) +endif() + +endif() diff --git a/subsys/lorawan_lbm/Kconfig b/subsys/lorawan_lbm/Kconfig new file mode 100644 index 0000000000000..7ca733c6346f9 --- /dev/null +++ b/subsys/lorawan_lbm/Kconfig @@ -0,0 +1,369 @@ +# LoRaWAN configuration options + +# Copyright (c) 2024 Semtech Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig LORA_BASICS_MODEM + bool "LoRa Basics Modem LoRaWAN stack support [EXPERIMENTAL]" + depends on LORA_BASICS_MODEM_DRIVERS + select EXPERIMENTAL + select LORA_BASICS_MODEM_DRIVERS_RAL_RALF + select REBOOT + # depends on REQUIRES_FULL_LIBC + select ENTROPY_GENERATOR + select ZEPHYR_LORA_BASICS_MODEM_MODULE + depends on !LORAWAN + help + This option enables the LoRa Basics Modem stack for LoRaWAN support. + Full libc is required for floorf. + Also make sure some source of randomness is used. + +if LORA_BASICS_MODEM + +choice + prompt "LoRaWAN storage implementation" + default LORA_BASICS_MODEM_PROVIDED_STORAGE_IMPL + help + Use a basic flash-based storage implementation or a user-provided implementation + +config LORA_BASICS_MODEM_PROVIDED_STORAGE_IMPL + bool "Use a flash-based LoRa Basics Modem storage implementation" + depends on FLASH + select FLASH_MAP + select FLASH_PAGE_LAYOUT + help + This storage implementation uses the standard storage-partition to store + the LBM context and logs. + +config LORA_BASICS_MODEM_USER_STORAGE_IMPL + bool "Use an application-provided LoRa Basics Modem storage implementation" + help + Enable if user storage implementation is required. + This disables the default storage implementation in the HAL + that uses the settings subsystem. + +endchoice + +module = LORA_BASICS_MODEM +module-str = LoRa Basics Modem +source "subsys/logging/Kconfig.template.log_config" + +config LORA_BASICS_MODEM_LOG_VERBOSE + bool "Additional very verbose logging" + help + An even more verbose log level than debug + +config LORA_BASICS_MODEM_RADIO_PLANNER_LOG_VERBOSE + bool "Additional very verbose logging for the Radio Planner" + default n + +config LORA_BASICS_MODEM_GEOLOCATION_LOG_VERBOSE + bool "Additional very verbose logging for the Geolocation and Wifi middlewares" + default n + +#----------------------------------------------------------------------------- +# Internal LBM features management +#----------------------------------------------------------------------------- + +choice + prompt "LoRaWAN Regional Parameters version" + default LORA_BASICS_MODEM_RP2_103 + help + Specify which Regional Parameters version to use + +config LORA_BASICS_MODEM_RP2_101 + bool "Use RP 1.0.1" + +config LORA_BASICS_MODEM_RP2_103 + bool "Use RP 1.0.3" + +endchoice + +config LORA_BASICS_MODEM_ENABLE_ALL_REGIONS + bool "Support for all supported regions" + default y + select LORA_BASICS_MODEM_REGION_AS_923 + select LORA_BASICS_MODEM_REGION_AU_915 + select LORA_BASICS_MODEM_REGION_CN_470 + select LORA_BASICS_MODEM_REGION_CN_470_RP_1_0 + select LORA_BASICS_MODEM_REGION_EU_868 + select LORA_BASICS_MODEM_REGION_IN_865 + select LORA_BASICS_MODEM_REGION_KR_920 + select LORA_BASICS_MODEM_REGION_RU_864 + select LORA_BASICS_MODEM_REGION_US_915 + select LORA_BASICS_MODEM_REGION_WW_2G4 + +config LORA_BASICS_MODEM_REGION_AS_923 + bool "Support for AS_923 region" + +config LORA_BASICS_MODEM_REGION_AU_915 + bool "Support for AU_915 region" + +config LORA_BASICS_MODEM_REGION_CN_470 + bool "Support for CN_470 region" + +config LORA_BASICS_MODEM_REGION_CN_470_RP_1_0 + bool "Support for CN_470_RP_1_0 region" + +config LORA_BASICS_MODEM_REGION_EU_868 + bool "Support for EU_868 region" + +config LORA_BASICS_MODEM_REGION_IN_865 + bool "Support for IN_865 region" + +config LORA_BASICS_MODEM_REGION_KR_920 + bool "Support for KR_920 region" + +config LORA_BASICS_MODEM_REGION_RU_864 + bool "Support for RU_864 region" + +config LORA_BASICS_MODEM_REGION_US_915 + bool "Support for US_915 region" + +config LORA_BASICS_MODEM_REGION_WW_2G4 + bool "Support for WW_2G4 region" + + +choice + prompt "Cryptography engine" + default LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + help + Specify which crypto engine will be used + +config LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + bool "Use software based cryptography module" + +config LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX + bool "Use lr11xx hardware cryptography module" + depends on DT_HAS_SEMTECH_LR1110_ENABLED || DT_HAS_SEMTECH_LR1120_ENABLED + +config LORA_BASICS_MODEM_CRYPTOGRAPHY_LR11XX_WITH_CREDENTIALS + bool "Use lr11xx hardware cryptography module with its embedded credentials" + depends on DT_HAS_SEMTECH_LR1110_ENABLED || DT_HAS_SEMTECH_LR1120_ENABLED + +endchoice + +#----------------------------------------------------------------------------- +# LoRaWAN Stack related options +#----------------------------------------------------------------------------- + +config LORA_BASICS_MODEM_CLASS_B + bool "LoRaWAN Class B support" + default n + +config LORA_BASICS_MODEM_CLASS_C + bool "LoRaWAN Class C support" + default n + +config LORA_BASICS_MODEM_MULTICAST + bool "LoRaWAN multicast support" + default n + depends on LORA_BASICS_MODEM_CLASS_B || LORA_BASICS_MODEM_CLASS_C + +config LORA_BASICS_MODEM_CSMA + bool "LoRaWAN CSMA support" + default y + depends on SEMTECH_LR11XX || SEMTECH_SX126X + +config LORA_BASICS_MODEM_CSMA_BY_DEFAULT + bool "LoRaWAN CSMA enabled at startup" + default n + depends on LORA_BASICS_MODEM_CSMA + +#----------------------------------------------------------------------------- +# LoRaWAN Package related options +#----------------------------------------------------------------------------- + +config LORA_BASICS_MODEM_ALC_SYNC + bool "ALCSync service" + default n + +choice + prompt "ALCSync service version to be used" + depends on LORA_BASICS_MODEM_ALC_SYNC + default LORA_BASICS_MODEM_ALC_SYNC_V1 if LORA_BASICS_MODEM_FUOTA_V1 + default LORA_BASICS_MODEM_ALC_SYNC_V2 if LORA_BASICS_MODEM_FUOTA_V2 + config LORA_BASICS_MODEM_ALC_SYNC_V1 + bool "1" + config LORA_BASICS_MODEM_ALC_SYNC_V2 + bool "2" +endchoice + +config LORA_BASICS_MODEM_ALC_SYNC_VERSION + int + default 1 if LORA_BASICS_MODEM_ALC_SYNC_V1 + default 2 if LORA_BASICS_MODEM_ALC_SYNC_V2 + + +config LORA_BASICS_MODEM_FUOTA + bool "LoRaWan services for Firmware Upgrade Over The Air (FUOTA)" + default n + select LORA_BASICS_MODEM_CLASS_B + select LORA_BASICS_MODEM_CLASS_C + select LORA_BASICS_MODEM_MULTICAST + select LORA_BASICS_MODEM_ALC_SYNC + +# FIXME: maybe use 0 as not-configured value? +config LORA_BASICS_MODEM_FUOTA_MAX_NB_OF_FRAGMENTS_CONFIG + bool "Allow configuration of the maximum number of fragments for LBM FUOTA" + default n + +config LORA_BASICS_MODEM_FUOTA_MAX_NB_OF_FRAGMENTS + int "The maximum number of fragments for LBM FUOTA" + depends on LORA_BASICS_MODEM_FUOTA_MAX_NB_OF_FRAGMENTS_CONFIG + +config LORA_BASICS_MODEM_FUOTA_MAX_SIZE_OF_FRAGMENTS_CONFIG + bool "Allow configuration of the maximum fragments size for LBM FUOTA" + default n + +config LORA_BASICS_MODEM_FUOTA_MAX_SIZE_OF_FRAGMENTS + int "The maximum fragments size for LBM FUOTA" + depends on LORA_BASICS_MODEM_FUOTA_MAX_SIZE_OF_FRAGMENTS_CONFIG + +config LORA_BASICS_MODEM_FUOTA_MAX_FRAGMENTS_REDUNDANCY_CONFIG + bool "Allow configuration of the maximum fragments redundancy for LBM FUOTA" + default n + +config LORA_BASICS_MODEM_FUOTA_MAX_FRAGMENTS_REDUNDANCY + int "The maximum fragments redundancy for LBM FUOTA" + depends on LORA_BASICS_MODEM_FUOTA_MAX_FRAGMENTS_REDUNDANCY_CONFIG + + +choice + prompt "LoRaWan services for Firmware Upgrade Over The Air (FUOTA) version to use" + depends on LORA_BASICS_MODEM_FUOTA + config LORA_BASICS_MODEM_FUOTA_V1 + bool "1" + config LORA_BASICS_MODEM_FUOTA_V2 + bool "2" +endchoice + +config LORA_BASICS_MODEM_FUOTA_VERSION + int + default 1 if LORA_BASICS_MODEM_FUOTA_V1 + default 2 if LORA_BASICS_MODEM_FUOTA_V2 + + +config LORA_BASICS_MODEM_FUOTA_FMP + bool "LoRaWAN FUOTA Firmware Management Package support" + depends on LORA_BASICS_MODEM_FUOTA + default y + +config LORA_BASICS_MODEM_FUOTA_MPA + bool "LoRaWAN FUOTA Multi-Package Access support" + depends on LORA_BASICS_MODEM_FUOTA + default n + +#----------------------------------------------------------------------------- +# LoRaCloud related options +#----------------------------------------------------------------------------- + +config LORA_BASICS_MODEM_ALMANAC + bool "Cloud Almanac Update service (only applicable for lr1110 and lr1120 targets)" + depends on DT_HAS_SEMTECH_LR1110_ENABLED || DT_HAS_SEMTECH_LR1120_ENABLED + default n + +config LORA_BASICS_MODEM_STREAM + bool "Stream service" + default n + +config LORA_BASICS_MODEM_LFU + bool "Large File Upload service" + default n + +config LORA_BASICS_MODEM_DEVICE_MANAGEMENT + bool "Device Management service" + default n + +config LORA_BASICS_MODEM_GEOLOCATION + bool "Geolocation service" + depends on DT_HAS_SEMTECH_LR1110_ENABLED || DT_HAS_SEMTECH_LR1120_ENABLED + default n + +config LORA_BASICS_MODEM_STORE_AND_FORWARD + bool "Store And Forward service" + default n + +#----------------------------------------------------------------------------- +# Relay and beacon options +#----------------------------------------------------------------------------- + +# TODO: +config LORA_BASICS_MODEM_BEACON_TX + bool "Beacon transmission support" + default n + + +config LORA_BASICS_MODEM_RELAY_RX + bool "Relay RX support" + depends on LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + select LORA_BASICS_MODEM_RELAY + default n + +config LORA_BASICS_MODEM_RELAY_TX + bool "Relay TX feature" + depends on LORA_BASICS_MODEM_CRYPTOGRAPHY_SOFT + select LORA_BASICS_MODEM_RELAY + default n + +config LORA_BASICS_MODEM_RELAY + bool + +#----------------------------------------------------------------------------- +# Miscellaneous options +#----------------------------------------------------------------------------- + +config LORA_BASICS_MODEM_NUMBER_OF_STACKS + int # "Number of LoRa Basics Modem stacks (only 1 is supported for now)" + default 1 + help + Number of stacks to be used by the modem library. + + +config LORA_BASICS_MODEM_PERF_TEST + bool "Build LoRa Basics Modem in perf test" + default n + help + Useful only when building the hardware modem sample + +config LORA_BASICS_MODEM_DISABLE_JOIN_DUTY_CYCLE + bool "Disable the Join duty cycle. ONLY ENABLE FOR ISOLATED SETUPS!" + default n + help + Enable this if you are testing in an isolated setup (e.g with coaxial cables + between gateway and device) for faster joins. + You should keep this disabled to follow the LoRaWAN standard. + + +config LORA_BASICS_MODEM_MAIN_THREAD + bool "Build a main loop thread that runs the LBM stack." + default n + help + This thread needs to be manually started. It initializes the LBM stack, + and runs it. + The chosen zephyr,lorawan-transceiver device will be used as transceiver. + TODO: Add this to the doc/build/dts/api/api.rst too. + +if LORA_BASICS_MODEM_MAIN_THREAD + +config LORA_BASICS_MODEM_MAIN_THREAD_STACK_SIZE + int "Stack size of the LBM main thread" + default 4096 + help + Stack size for the LBM main thread + +config LORA_BASICS_MODEM_MAIN_THREAD_PRIORITY + int "Priority of the LBM main thread" + default 2 + help + Thread priority of the LBM main thread + +config LORA_BASICS_MODEM_MAIN_THREAD_MAX_SLEEP_MS + int "Maximum sleeping time for the LBM main thread" + default 60000 + help + Present as a fail-safe if the thread wake-up fails for some reason + +endif # LORA_BASICS_MODEM_MAIN_THREAD + +endif # LORA_BASICS_MODEM diff --git a/subsys/lorawan_lbm/lbm_main_thread.c b/subsys/lorawan_lbm/lbm_main_thread.c new file mode 100644 index 0000000000000..b2213c03b9fed --- /dev/null +++ b/subsys/lorawan_lbm/lbm_main_thread.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 Semtech Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "zephyr/toolchain.h" +#include +#include + +#include +#include +#include + +LOG_MODULE_DECLARE(lorawan, CONFIG_LORA_BASICS_MODEM_LOG_LEVEL); + +static const struct device *transceiver = DEVICE_DT_GET(DT_CHOSEN(zephyr_lorawan_transceiver)); + +static struct k_thread lbm_main_thread_data; +static K_THREAD_STACK_DEFINE(lbm_main_thread_stack, + CONFIG_LORA_BASICS_MODEM_MAIN_THREAD_STACK_SIZE); + +static void lora_basics_modem_main_thread(void *p1, void *p2, void *p3) +{ + uint32_t sleep_time_ms = 0; + void (*event_callback)(void) = p1; + + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + lorawan_smtc_modem_hal_init(transceiver); + smtc_modem_init(event_callback); + + LOG_INF("Starting loop..."); + + while (true) { + sleep_time_ms = smtc_modem_run_engine(); + + if (smtc_modem_is_irq_flag_pending()) { + continue; + } + +#if CONFIG_LORA_BASICS_MODEM_MAIN_THREAD_MAX_SLEEP_MS + sleep_time_ms = + MIN(sleep_time_ms, CONFIG_LORA_BASICS_MODEM_MAIN_THREAD_MAX_SLEEP_MS); +#endif + LOG_INF("Sleeping for %dms", sleep_time_ms); + smtc_modem_hal_interruptible_msleep(K_MSEC(sleep_time_ms)); + } +} + +void lora_basics_modem_start_work_thread(void (*event_callback)(void)) +{ + k_thread_create(&lbm_main_thread_data, lbm_main_thread_stack, + K_THREAD_STACK_SIZEOF(lbm_main_thread_stack), lora_basics_modem_main_thread, + event_callback, NULL, NULL, CONFIG_LORA_BASICS_MODEM_MAIN_THREAD_PRIORITY, + 0, K_NO_WAIT); +} diff --git a/west.yml b/west.yml index 0b1597937687f..8d2d1c55343a3 100644 --- a/west.yml +++ b/west.yml @@ -23,6 +23,8 @@ manifest: url-base: https://github.com/zephyrproject-rtos - name: babblesim url-base: https://github.com/BabbleSim + - name: lora-net + url-base: https://github.com/lora-net group-filter: [-babblesim, -optional] @@ -291,6 +293,11 @@ manifest: groups: - fs revision: ed0531d59ee37f5fb2762bcf2fc8ba4efaf82656 + - name: lora_basics_modem + remote: lora-net + repo-path: SWL2001 + revision: v4.8.0 + path: modules/lib/lora_basics_modem - name: loramac-node revision: fb00b383072518c918e2258b0916c996f2d4eebe path: modules/lib/loramac-node