add lora sx127 driver
This commit is contained in:
commit
fc8ed8fb5e
812
lora_sx127_rpi3.c
Normal file
812
lora_sx127_rpi3.c
Normal file
|
@ -0,0 +1,812 @@
|
|||
/*-
|
||||
* Copyright (c) 2017 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
|
||||
* Copyright (c) 2024 Fabian Montero <fabian@posixlycorrect.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
* 3. Neither the names of the above-listed copyright holders nor the names
|
||||
* of any contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <net/mac802154.h>
|
||||
|
||||
#ifndef F_XOSC
|
||||
#define F_XOSC 32000000
|
||||
#endif
|
||||
static u32 xosc_frq = F_XOSC;
|
||||
module_param(xosc_frq, uint, 0000);
|
||||
MODULE_PARM_DESC(xosc_frq, "Crystal oscillator frequency of the LoRa chip");
|
||||
|
||||
#define __POW_2_19 0x80000
|
||||
|
||||
#ifndef SX127X_SPRF
|
||||
#define SX127X_SPRF 512
|
||||
#endif
|
||||
static u32 sprf = SX127X_SPRF;
|
||||
module_param(sprf, uint, 0000);
|
||||
MODULE_PARM_DESC(sprf, "Spreading factor of Chirp Spread Spectrum modulation");
|
||||
|
||||
#ifndef SX127X_RX_BYTE_TIMEOUT
|
||||
#define SX127X_RX_BYTE_TIMEOUT 1023
|
||||
#endif
|
||||
static u32 rx_timeout = SX127X_RX_BYTE_TIMEOUT;
|
||||
module_param(rx_timeout, uint, 0000);
|
||||
MODULE_PARM_DESC(rx_timeout, "RX time-out value as number of symbols");
|
||||
|
||||
/* SX127X Registers addresses */
|
||||
#define SX127X_REG_FIFO 0x00
|
||||
#define SX127X_REG_OP_MODE 0x01
|
||||
#define SX127X_REG_FRF_MSB 0x06
|
||||
#define SX127X_REG_FRF_MID 0x07
|
||||
#define SX127X_REG_FRF_LSB 0x08
|
||||
#define SX127X_REG_PA_CONFIG 0x09
|
||||
#define SX127X_REG_PA_RAMP 0x0A
|
||||
#define SX127X_REG_OCP 0x0B
|
||||
#define SX127X_REG_LNA 0x0C
|
||||
#define SX127X_REG_FIFO_ADDR_PTR 0x0D
|
||||
#define SX127X_REG_FIFO_TX_BASE_ADDR 0x0E
|
||||
#define SX127X_REG_FIFO_RX_BASE_ADDR 0x0F
|
||||
#define SX127X_REG_FIFO_RX_CURRENT_ADDR 0x10
|
||||
#define SX127X_REG_IRQ_FLAGS_MASK 0x11
|
||||
#define SX127X_REG_IRQ_FLAGS 0x12
|
||||
#define SX127X_REG_RX_NB_BYTES 0x13
|
||||
#define SX127X_REG_RX_HEADER_CNT_VALUE_MSB 0x14
|
||||
#define SX127X_REG_RX_HEADER_CNT_VALUE_LSB 0x15
|
||||
#define SX127X_REG_RX_PACKET_CNT_VALUE_MSB 0x16
|
||||
#define SX127X_REG_RX_PACKET_CNT_VALUE_LSB 0x17
|
||||
#define SX127X_REG_MODEM_STAT 0x18
|
||||
#define SX127X_REG_PKT_SNR_VALUE 0x19
|
||||
#define SX127X_REG_PKT_RSSI_VALUE 0x1A
|
||||
#define SX127X_REG_RSSI_VALUE 0x1B
|
||||
#define SX127X_REG_HOP_CHANNEL 0x1C
|
||||
#define SX127X_REG_MODEM_CONFIG1 0x1D
|
||||
#define SX127X_REG_MODEM_CONFIG2 0x1E
|
||||
#define SX127X_REG_SYMB_TIMEOUT_LSB 0x1F
|
||||
#define SX127X_REG_PREAMBLE_MSB 0x20
|
||||
#define SX127X_REG_PREAMBLE_LSB 0x21
|
||||
#define SX127X_REG_PAYLOAD_LENGTH 0x22
|
||||
#define SX127X_REG_MAX_PAYLOAD_LENGTH 0x23
|
||||
#define SX127X_REG_HOP_PERIOD 0x24
|
||||
#define SX127X_REG_FIFO_RX_BYTE_ADDR 0x25
|
||||
#define SX127X_REG_MODEM_CONFIG3 0x26
|
||||
#define SX127X_REG_FEI_MSB 0x28
|
||||
#define SX127X_REG_FEI_MID 0x29
|
||||
#define SX127X_REG_FEI_LSB 0x2A
|
||||
#define SX127X_REG_RSSI_WIDEBAND 0x2C
|
||||
#define SX127X_REG_DETECT_OPTIMIZE 0x31
|
||||
#define SX127X_REG_INVERT_IRQ 0x33
|
||||
#define SX127X_REG_DETECTION_THRESHOLD 0x37
|
||||
#define SX127X_REG_SYNC_WORD 0x39
|
||||
#define SX127X_REG_VERSION 0x42
|
||||
#define SX127X_REG_TCXO 0x4B
|
||||
#define SX127X_REG_PA_DAC 0x4D
|
||||
#define SX127X_REG_FORMER_TEMP 0x5B
|
||||
#define SX127X_REG_AGC_REF 0x61
|
||||
#define SX127X_REG_AGC_THRESH1 0x62
|
||||
#define SX127X_REG_AGC_THRESH2 0x63
|
||||
#define SX127X_REG_AGC_THRESH3 0x64
|
||||
#define SX127X_REG_PLL 0x70
|
||||
#define SX127X_MAX_REG SX127X_REG_PLL
|
||||
|
||||
/* SX127X's operating states in LoRa mode */
|
||||
#define SX127X_MODE_MASK 0x07
|
||||
#define SX127X_SLEEP_MODE 0x00
|
||||
#define SX127X_STANDBY_MODE 0x01
|
||||
#define SX127X_FSTX_MODE 0x02
|
||||
#define SX127X_TX_MODE 0x03
|
||||
#define SX127X_FSRX_MODE 0x04
|
||||
#define SX127X_RXCONTINUOUS_MODE 0x05
|
||||
#define SX127X_RXSINGLE_MODE 0x06
|
||||
#define SX127X_CAD_MODE 0x07
|
||||
#define SX127X_LORA_MODE 0x80
|
||||
|
||||
/* SX127X_REG_OCP flags */
|
||||
#define SX127X_FLAG_OCPON 0x20
|
||||
|
||||
/* SX127X's IRQ flags in LoRa mode */
|
||||
#define SX127X_FLAG_RXTIMEOUT 0x80
|
||||
#define SX127X_FLAG_RXDONE 0x40
|
||||
#define SX127X_FLAG_PAYLOADCRCERROR 0x20
|
||||
#define SX127X_FLAG_VALIDHEADER 0x10
|
||||
#define SX127X_FLAG_TXDONE 0x08
|
||||
#define SX127X_FLAG_CADDONE 0x04
|
||||
#define SX127X_FLAG_FHSSCHANGECHANNEL 0x02
|
||||
#define SX127X_FLAG_CADDETECTED 0x01
|
||||
|
||||
/* SX127X's IRQ flags' mask for output pins in LoRa mode */
|
||||
#define SX127X_FLAGMASK_RXTIMEOUT 0x80
|
||||
#define SX127X_FLAGMASK_RXDONE 0x40
|
||||
#define SX127X_FLAGMASK_PAYLOADCRCERROR 0x20
|
||||
#define SX127X_FLAGMASK_VALIDHEADER 0x10
|
||||
#define SX127X_FLAGMASK_TXDONE 0x08
|
||||
#define SX127X_FLAGMASK_CADDONE 0x04
|
||||
#define SX127X_FLAGMASK_FHSSCHANGECHANNEL 0x02
|
||||
#define SX127X_FLAGMASK_CADDETECTED 0x01
|
||||
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_MASK 0x01
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_EXPLICIT 0x00
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_IMPLICIT 0x01
|
||||
|
||||
/* SX127X's RX/TX FIFO base address */
|
||||
#define SX127X_FIFO_RX_BASE_ADDRESS 0x00
|
||||
#define SX127X_FIFO_TX_BASE_ADDRESS 0x80
|
||||
|
||||
#define POLL_JIFFIES (msecs_to_jiffies(10))
|
||||
|
||||
struct sx127x {
|
||||
struct regmap *regmap;
|
||||
struct ieee802154_hw *ieee;
|
||||
struct spi_device *spi;
|
||||
|
||||
spinlock_t lock;
|
||||
bool busy;
|
||||
struct sk_buff *tx_skb;
|
||||
size_t delayed_tx_length;
|
||||
|
||||
struct timer_list poll_timer;
|
||||
struct work_struct poll_work;
|
||||
struct work_struct tx_work;
|
||||
|
||||
u32 oscillator_freq;
|
||||
};
|
||||
|
||||
static int sx127x_reg_read(struct sx127x *hw, unsigned int address, u8 *data)
|
||||
{
|
||||
int ret = regmap_raw_read(hw->regmap, address, data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&hw->spi->dev, "regmap_raw_read(0x%x) failed: %d\n", address, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_reg_write(struct sx127x *hw, unsigned int address, u8 data)
|
||||
{
|
||||
int ret = regmap_raw_write(hw->regmap, address, &data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&hw->spi->dev, "regmap_raw_write(0x%x) failed: %d\n", address, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_mode(struct sx127x *hw, unsigned int mode)
|
||||
{
|
||||
return sx127x_reg_write(hw, SX127X_REG_OP_MODE, SX127X_LORA_MODE | mode);
|
||||
}
|
||||
|
||||
static int sx127x_set_frequency(struct sx127x *hw, unsigned long frequency)
|
||||
{
|
||||
u8 msb_mid_lsb[3];
|
||||
int i;
|
||||
u64 frf;
|
||||
|
||||
frf = (((u64) frequency) << 19) / hw->oscillator_freq;
|
||||
for (i = sizeof msb_mid_lsb - 1; i >= 0; --i) {
|
||||
msb_mid_lsb[i] = (frf & 0xff);
|
||||
frf >>= 8;
|
||||
}
|
||||
|
||||
return regmap_raw_write(hw->regmap, SX127X_REG_FRF_MSB, msb_mid_lsb, sizeof msb_mid_lsb);
|
||||
}
|
||||
|
||||
static int sx127x_get_frequency(struct sx127x *hw, unsigned long *frequency)
|
||||
{
|
||||
u8 msb_mid_lsb[3];
|
||||
int i, ret;
|
||||
u64 frt;
|
||||
|
||||
ret = regmap_raw_read(hw->regmap, SX127X_REG_FRF_MSB, msb_mid_lsb, sizeof msb_mid_lsb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
frt = 0;
|
||||
for (i = 0; i < sizeof msb_mid_lsb; ++i)
|
||||
frt = (frt << 8) | msb_mid_lsb[i];
|
||||
|
||||
*frequency = (frt * hw->oscillator_freq) >> 19;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sx127x_set_ocp(struct sx127x *hw, u8 current_ma)
|
||||
{
|
||||
u8 ocp_trim;
|
||||
|
||||
/* P. 109
|
||||
*
|
||||
* Trimming of OCP current:
|
||||
* Imax = 45+5*OcpTrim [mA] if OcpTrim <= 15 (120 mA) /
|
||||
* Imax = -30+10*OcpTrim [mA] if 15 < OcpTrim <= 27 (130 to
|
||||
* 240 mA)
|
||||
* Imax = 240mA for higher settings
|
||||
* Default Imax = 100mA
|
||||
*/
|
||||
if (current_ma <= 45)
|
||||
ocp_trim = 0;
|
||||
else if (current_ma <= 120)
|
||||
ocp_trim = (current_ma - 45) / 5;
|
||||
else if (current_ma <= 240)
|
||||
ocp_trim = (current_ma + 30) / 10;
|
||||
else
|
||||
ocp_trim = 28;
|
||||
|
||||
return sx127x_reg_write(hw, SX127X_REG_OCP, SX127X_FLAG_OCPON | ocp_trim);
|
||||
}
|
||||
|
||||
static int sx127x_set_tx_power(struct sx127x *hw, int level)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// PA BOOST
|
||||
if (level > 17) {
|
||||
if (level > 20)
|
||||
level = 20;
|
||||
|
||||
// subtract 3 from level, so 18 - 20 maps to 15 - 17
|
||||
level -= 3;
|
||||
|
||||
// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PA_DAC, 0x87);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_set_ocp(hw, 140);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
if (level < 2)
|
||||
level = 2;
|
||||
|
||||
// Default value PA_HF/LF or +17dBm
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PA_DAC, 0x84);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_set_ocp(hw, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// PA_CONFIG
|
||||
#define PA_BOOST 0x80
|
||||
return sx127x_reg_write(hw, SX127X_REG_PA_CONFIG, PA_BOOST | (level - 2));
|
||||
}
|
||||
|
||||
static int sx127x_init(struct sx127x *hw, u8 *major, u8 *minor)
|
||||
{
|
||||
u8 modem_config1, lna, version;
|
||||
int ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_VERSION, &version);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (version != 0x12) {
|
||||
dev_err(&hw->spi->dev, "%s: bad version 0x%02x\n", __func__, version);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*major = (version >> 4) & 0xf;
|
||||
*minor = (version >> 0) & 0xf;
|
||||
|
||||
ret = sx127x_mode(hw, SX127X_SLEEP_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&hw->spi->dev, "%s: sx127x_mode() failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_RX_BASE_ADDR, SX127X_FIFO_RX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_TX_BASE_ADDR, SX127X_FIFO_TX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_LNA, &lna);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
lna |= 0x3; // ???, pero lo hace el sketch
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_LNA, lna);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &modem_config1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
modem_config1 &= ~SX127X_MODEM_CONFIG1_HEADER_MODE_MASK;
|
||||
modem_config1 |= SX127X_MODEM_CONFIG1_HEADER_MODE_EXPLICIT;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG1, modem_config1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG3, 0x4);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sx127x_tx_work(struct work_struct *work)
|
||||
{
|
||||
u8 mode;
|
||||
int ret = 0;
|
||||
bool set_busy = false;
|
||||
size_t i, size;
|
||||
const u8 *buffer;
|
||||
struct sx127x *hw = container_of(work, struct sx127x, tx_work);
|
||||
struct sk_buff *tx_skb;
|
||||
unsigned long spin_irq;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
|
||||
tx_skb = hw->tx_skb;
|
||||
if (!hw->busy && !hw->delayed_tx_length) {
|
||||
hw->busy = true;
|
||||
set_busy = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (!set_busy) {
|
||||
ret = -EBUSY;
|
||||
goto exit_busy;
|
||||
} else if (!tx_skb) {
|
||||
ret = -EINVAL;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
size = tx_skb->len;
|
||||
buffer = tx_skb->data;
|
||||
|
||||
if (!size || size > 128) {
|
||||
ret = -EINVAL;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) == SX127X_TX_MODE) {
|
||||
ret = -EBUSY;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
// reset FIFO address and payload length
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, SX127X_FIFO_TX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
// write data
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO, buffer[i]);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PAYLOAD_LENGTH, (u8) size);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) == SX127X_STANDBY_MODE
|
||||
|| (mode & SX127X_MODE_MASK) == SX127X_RXCONTINUOUS_MODE)
|
||||
ret = sx127x_mode(hw, SX127X_TX_MODE);
|
||||
else
|
||||
hw->delayed_tx_length = size;
|
||||
|
||||
exit_busy:
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (set_busy)
|
||||
hw->busy = false;
|
||||
|
||||
if (tx_skb)
|
||||
hw->tx_skb = NULL;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (tx_skb) {
|
||||
#ifndef SX127X_LEGACY_KERNEL
|
||||
if (ret)
|
||||
ieee802154_xmit_error(hw->ieee, tx_skb, ret);
|
||||
else
|
||||
#endif
|
||||
ieee802154_xmit_complete(hw->ieee, tx_skb, false);
|
||||
}
|
||||
|
||||
(void) ret;
|
||||
}
|
||||
|
||||
static void sx127x_poll_work(struct work_struct *work)
|
||||
{
|
||||
u8 ack_flags, lqi, mode, read_flags, rx_base, *rx_data, rx_length;
|
||||
bool do_rx = false, was_busy = false;
|
||||
unsigned long spin_irq;
|
||||
struct sx127x *hw = container_of(work, struct sx127x, poll_work);
|
||||
struct sk_buff *rx_skb = NULL;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (!hw->busy)
|
||||
hw->busy = true;
|
||||
else
|
||||
was_busy = true;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (was_busy)
|
||||
return;
|
||||
|
||||
if (sx127x_reg_read(hw, SX127X_REG_IRQ_FLAGS, &read_flags) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
ack_flags = 0;
|
||||
|
||||
if (read_flags & SX127X_FLAG_TXDONE)
|
||||
ack_flags |= SX127X_FLAG_TXDONE;
|
||||
|
||||
if (read_flags & SX127X_FLAG_RXDONE) {
|
||||
do_rx = true;
|
||||
ack_flags |= SX127X_FLAG_RXDONE;
|
||||
|
||||
if (read_flags & SX127X_FLAG_VALIDHEADER)
|
||||
ack_flags |= SX127X_FLAG_VALIDHEADER;
|
||||
else
|
||||
do_rx = false;
|
||||
|
||||
if (read_flags & SX127X_FLAG_PAYLOADCRCERROR) {
|
||||
do_rx = false;
|
||||
ack_flags |= SX127X_FLAG_PAYLOADCRCERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_rx) {
|
||||
if (sx127x_reg_read(hw, SX127X_REG_FIFO_RX_CURRENT_ADDR, &rx_base) < 0
|
||||
|| sx127x_reg_read(hw, SX127X_REG_RX_NB_BYTES, &rx_length) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, rx_base) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
rx_skb = dev_alloc_skb(IEEE802154_MTU);
|
||||
if (!rx_skb)
|
||||
goto exit_busy;
|
||||
|
||||
rx_data = skb_put(rx_skb, rx_length);
|
||||
while (rx_length--)
|
||||
if (sx127x_reg_read(hw, SX127X_REG_FIFO, rx_data++) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_FIFO_RX_BASE_ADDR, SX127X_FIFO_RX_BASE_ADDRESS) < 0)
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_IRQ_FLAGS, ack_flags) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) != SX127X_TX_MODE) {
|
||||
if (hw->delayed_tx_length) {
|
||||
sx127x_mode(hw, SX127X_TX_MODE);
|
||||
hw->delayed_tx_length = 0;
|
||||
} else if ((mode & SX127X_MODE_MASK) != SX127X_RXCONTINUOUS_MODE)
|
||||
sx127x_mode(hw, SX127X_RXCONTINUOUS_MODE);
|
||||
}
|
||||
|
||||
if (rx_skb) {
|
||||
lqi = 69; //TODO
|
||||
ieee802154_rx_irqsafe(hw->ieee, rx_skb, lqi);
|
||||
rx_skb = NULL; // rx_skb ownership transferred to the mac802154 layer
|
||||
}
|
||||
|
||||
exit_busy:
|
||||
if (rx_skb)
|
||||
kfree_skb(rx_skb);
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
hw->busy = false;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
}
|
||||
|
||||
static void sx127x_poll_timer(struct timer_list *timer)
|
||||
{
|
||||
struct sx127x *hw = container_of(timer, struct sx127x, poll_timer);
|
||||
schedule_work(&hw->poll_work);
|
||||
mod_timer(&hw->poll_timer, jiffies + POLL_JIFFIES);
|
||||
}
|
||||
|
||||
static int sx127x_802154_xmit_async(struct ieee802154_hw *ieee, struct sk_buff *skb)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sx127x *hw = ieee->priv;
|
||||
unsigned long spin_irq;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (!hw->tx_skb)
|
||||
hw->tx_skb = skb_get(skb);
|
||||
else
|
||||
ret = -EBUSY;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
schedule_work(&hw->tx_work);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_802154_ed(struct ieee802154_hw *ieee, u8 *level)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_channel(struct ieee802154_hw *ieee, u8 page, u8 channel)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_txpower(struct ieee802154_hw *ieee, s32 mbm)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_start(struct ieee802154_hw *ieee)
|
||||
{
|
||||
int ret;
|
||||
struct sx127x *hw = ieee->priv;
|
||||
|
||||
ret = sx127x_mode(hw, SX127X_STANDBY_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&hw->spi->dev, "%s: sx127x_mode() failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mod_timer(&hw->poll_timer, jiffies + POLL_JIFFIES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sx127x_802154_stop(struct ieee802154_hw *ieee)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
|
||||
del_timer_sync(&hw->poll_timer);
|
||||
cancel_work_sync(&hw->poll_work);
|
||||
|
||||
(void) sx127x_mode(hw, SX127X_SLEEP_MODE);
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_promiscuous_mode(struct ieee802154_hw *ieee, const bool on)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ieee802154_ops sx127x_802154_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.xmit_async = sx127x_802154_xmit_async,
|
||||
.ed = sx127x_802154_ed,
|
||||
.set_channel = sx127x_802154_set_channel,
|
||||
.set_txpower = sx127x_802154_set_txpower,
|
||||
.start = sx127x_802154_start,
|
||||
.stop = sx127x_802154_stop,
|
||||
.set_promiscuous_mode = sx127x_802154_set_promiscuous_mode,
|
||||
};
|
||||
|
||||
static bool sx127x_reg_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct regmap_config sx127x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = SX127X_MAX_REG,
|
||||
.read_flag_mask = 0x00,
|
||||
.write_flag_mask = 0x80,
|
||||
.volatile_reg = sx127x_reg_volatile,
|
||||
};
|
||||
|
||||
/* in mbm */
|
||||
static const s32 sx127x_tx_powers[] = {
|
||||
-200, -100, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100,
|
||||
1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300};
|
||||
|
||||
static int sx127x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
u8 major, minor;
|
||||
int ret = 0;
|
||||
struct sx127x *hw;
|
||||
unsigned long frequency;
|
||||
struct ieee802154_hw *ieee;
|
||||
struct device_node *of_node;
|
||||
|
||||
dev_dbg(&spi->dev, "%s hello\n", __func__);
|
||||
|
||||
ieee = ieee802154_alloc_hw(sizeof *hw, &sx127x_802154_ops);
|
||||
if (!ieee) {
|
||||
dev_err(&spi->dev, "ieee802154_alloc_hw(): out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hw = ieee->priv;
|
||||
hw->spi = spi;
|
||||
hw->ieee = ieee;
|
||||
hw->busy = false;
|
||||
hw->delayed_tx_length = 0;
|
||||
spin_lock_init(&hw->lock);
|
||||
|
||||
INIT_WORK(&hw->tx_work, sx127x_tx_work);
|
||||
INIT_WORK(&hw->poll_work, sx127x_poll_work);
|
||||
timer_setup(&hw->poll_timer, sx127x_poll_timer, 0);
|
||||
|
||||
hw->regmap = devm_regmap_init_spi(spi, &sx127x_regmap_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&spi->dev, "%s: devm_regmap_init_spi() failed: %d\n", __func__, ret);
|
||||
return PTR_ERR(hw->regmap);
|
||||
}
|
||||
|
||||
of_node = regmap_get_device(hw->regmap)->of_node;
|
||||
if (of_property_read_u32(of_node, "clock-frequency", &hw->oscillator_freq)) {
|
||||
dev_err(&spi->dev, "%s: 'clock-frequency' missing from DT node\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ieee->parent = &spi->dev;
|
||||
spi_set_drvdata(spi, hw);
|
||||
|
||||
//TODO: pin de reset
|
||||
|
||||
ieee802154_random_extended_addr(&ieee->phy->perm_extended_addr);
|
||||
|
||||
ieee->flags = IEEE802154_HW_TX_OMIT_CKSUM
|
||||
| IEEE802154_HW_RX_OMIT_CKSUM
|
||||
| IEEE802154_HW_PROMISCUOUS;
|
||||
|
||||
ieee->phy->flags = WPAN_PHY_FLAG_TXPOWER;
|
||||
ieee->phy->transmit_power = sx127x_tx_powers[12];
|
||||
ieee->phy->supported.tx_powers = sx127x_tx_powers;
|
||||
ieee->phy->supported.tx_powers_size = ARRAY_SIZE(sx127x_tx_powers);
|
||||
|
||||
/* SX1278 phy channel 11 as default */
|
||||
ieee->phy->current_channel = 11;
|
||||
/* Define channels could be used. */
|
||||
ieee->phy->supported.channels[0] = 1 << 11; /*TODO: sx1278_ieee_channel_mask(hw);*/
|
||||
|
||||
ret = sx127x_init(hw, &major, &minor);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_init() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "IEEE 802.15.4 over LoRa SX127x compatible device v%u.%u\n", major, minor);
|
||||
|
||||
// Valor viene del sketch
|
||||
ret = sx127x_set_frequency(hw, 434000000);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_set_frequency() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
ret = sx127x_get_frequency(hw, &frequency);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_get_frequency() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "%s: frequency sanity test: %lu Hz\n", __func__, frequency);
|
||||
|
||||
ret = sx127x_set_tx_power(hw, 17); // 17 dBm
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_set_tx_power() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
ret = ieee802154_register_hw(ieee);
|
||||
if (ret < 0)
|
||||
dev_err(&spi->dev, "ieee802154_register_hw() failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
|
||||
fail_init:
|
||||
ieee802154_free_hw(ieee);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef SX127X_LEGACY_KERNEL
|
||||
static int sx127x_spi_remove(struct spi_device *spi)
|
||||
#else
|
||||
static void sx127x_spi_remove(struct spi_device *spi)
|
||||
#endif
|
||||
{
|
||||
struct sx127x *hw = spi_get_drvdata(spi);
|
||||
|
||||
dev_dbg(&hw->spi->dev, "%s bye\n", __func__);
|
||||
|
||||
ieee802154_unregister_hw(hw->ieee);
|
||||
ieee802154_free_hw(hw->ieee);
|
||||
|
||||
#ifdef SX127X_LEGACY_KERNEL
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct of_device_id sx127x_of_ids[] = {
|
||||
{ .compatible = "semtech,sx1276" },
|
||||
{ .compatible = "semtech,sx1277" },
|
||||
{ .compatible = "semtech,sx1278" },
|
||||
{ .compatible = "semtech,sx1279" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sx127x_of_ids);
|
||||
|
||||
static const struct spi_device_id sx127x_spi_ids[] = {
|
||||
{ .name = "sx1276" },
|
||||
{ .name = "sx1277" },
|
||||
{ .name = "sx1278" },
|
||||
{ .name = "sx1279" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, sx127x_spi_ids);
|
||||
|
||||
static struct spi_driver sx127x_spi_driver = {
|
||||
.driver = {
|
||||
.name = "sx127x",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(sx127x_of_ids),
|
||||
},
|
||||
.probe = sx127x_spi_probe,
|
||||
.remove = sx127x_spi_remove,
|
||||
.id_table = sx127x_spi_ids,
|
||||
};
|
||||
|
||||
module_spi_driver(sx127x_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>");
|
||||
MODULE_AUTHOR("Fabian Montero <fabian@posixlycorrect.com>");
|
||||
MODULE_DESCRIPTION("Semtech SX127x LoRa PHY driver with IEEE 802.15.4 MAC interface");
|
||||
MODULE_LICENSE("GPL");
|
829
lora_sx127_rpi4.c
Normal file
829
lora_sx127_rpi4.c
Normal file
|
@ -0,0 +1,829 @@
|
|||
/*-
|
||||
* Copyright (c) 2017 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
|
||||
* Copyright (c) 2024 Fabian Montero <fabian@posixlycorrect.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
* 3. Neither the names of the above-listed copyright holders nor the names
|
||||
* of any contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#define SX127X_LEGACY_KERNEL
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <net/mac802154.h>
|
||||
|
||||
#ifndef F_XOSC
|
||||
#define F_XOSC 32000000
|
||||
#endif
|
||||
static u32 xosc_frq = F_XOSC;
|
||||
module_param(xosc_frq, uint, 0000);
|
||||
MODULE_PARM_DESC(xosc_frq, "Crystal oscillator frequency of the LoRa chip");
|
||||
|
||||
#define __POW_2_19 0x80000
|
||||
|
||||
#ifndef SX127X_SPRF
|
||||
#define SX127X_SPRF 512
|
||||
#endif
|
||||
static u32 sprf = SX127X_SPRF;
|
||||
module_param(sprf, uint, 0000);
|
||||
MODULE_PARM_DESC(sprf, "Spreading factor of Chirp Spread Spectrum modulation");
|
||||
|
||||
#ifndef SX127X_RX_BYTE_TIMEOUT
|
||||
#define SX127X_RX_BYTE_TIMEOUT 1023
|
||||
#endif
|
||||
static u32 rx_timeout = SX127X_RX_BYTE_TIMEOUT;
|
||||
module_param(rx_timeout, uint, 0000);
|
||||
MODULE_PARM_DESC(rx_timeout, "RX time-out value as number of symbols");
|
||||
|
||||
/* SX127X Registers addresses */
|
||||
#define SX127X_REG_FIFO 0x00
|
||||
#define SX127X_REG_OP_MODE 0x01
|
||||
#define SX127X_REG_FRF_MSB 0x06
|
||||
#define SX127X_REG_FRF_MID 0x07
|
||||
#define SX127X_REG_FRF_LSB 0x08
|
||||
#define SX127X_REG_PA_CONFIG 0x09
|
||||
#define SX127X_REG_PA_RAMP 0x0A
|
||||
#define SX127X_REG_OCP 0x0B
|
||||
#define SX127X_REG_LNA 0x0C
|
||||
#define SX127X_REG_FIFO_ADDR_PTR 0x0D
|
||||
#define SX127X_REG_FIFO_TX_BASE_ADDR 0x0E
|
||||
#define SX127X_REG_FIFO_RX_BASE_ADDR 0x0F
|
||||
#define SX127X_REG_FIFO_RX_CURRENT_ADDR 0x10
|
||||
#define SX127X_REG_IRQ_FLAGS_MASK 0x11
|
||||
#define SX127X_REG_IRQ_FLAGS 0x12
|
||||
#define SX127X_REG_RX_NB_BYTES 0x13
|
||||
#define SX127X_REG_RX_HEADER_CNT_VALUE_MSB 0x14
|
||||
#define SX127X_REG_RX_HEADER_CNT_VALUE_LSB 0x15
|
||||
#define SX127X_REG_RX_PACKET_CNT_VALUE_MSB 0x16
|
||||
#define SX127X_REG_RX_PACKET_CNT_VALUE_LSB 0x17
|
||||
#define SX127X_REG_MODEM_STAT 0x18
|
||||
#define SX127X_REG_PKT_SNR_VALUE 0x19
|
||||
#define SX127X_REG_PKT_RSSI_VALUE 0x1A
|
||||
#define SX127X_REG_RSSI_VALUE 0x1B
|
||||
#define SX127X_REG_HOP_CHANNEL 0x1C
|
||||
#define SX127X_REG_MODEM_CONFIG1 0x1D
|
||||
#define SX127X_REG_MODEM_CONFIG2 0x1E
|
||||
#define SX127X_REG_SYMB_TIMEOUT_LSB 0x1F
|
||||
#define SX127X_REG_PREAMBLE_MSB 0x20
|
||||
#define SX127X_REG_PREAMBLE_LSB 0x21
|
||||
#define SX127X_REG_PAYLOAD_LENGTH 0x22
|
||||
#define SX127X_REG_MAX_PAYLOAD_LENGTH 0x23
|
||||
#define SX127X_REG_HOP_PERIOD 0x24
|
||||
#define SX127X_REG_FIFO_RX_BYTE_ADDR 0x25
|
||||
#define SX127X_REG_MODEM_CONFIG3 0x26
|
||||
#define SX127X_REG_FEI_MSB 0x28
|
||||
#define SX127X_REG_FEI_MID 0x29
|
||||
#define SX127X_REG_FEI_LSB 0x2A
|
||||
#define SX127X_REG_RSSI_WIDEBAND 0x2C
|
||||
#define SX127X_REG_DETECT_OPTIMIZE 0x31
|
||||
#define SX127X_REG_INVERT_IRQ 0x33
|
||||
#define SX127X_REG_DETECTION_THRESHOLD 0x37
|
||||
#define SX127X_REG_SYNC_WORD 0x39
|
||||
#define SX127X_REG_VERSION 0x42
|
||||
#define SX127X_REG_TCXO 0x4B
|
||||
#define SX127X_REG_PA_DAC 0x4D
|
||||
#define SX127X_REG_FORMER_TEMP 0x5B
|
||||
#define SX127X_REG_AGC_REF 0x61
|
||||
#define SX127X_REG_AGC_THRESH1 0x62
|
||||
#define SX127X_REG_AGC_THRESH2 0x63
|
||||
#define SX127X_REG_AGC_THRESH3 0x64
|
||||
#define SX127X_REG_PLL 0x70
|
||||
#define SX127X_MAX_REG SX127X_REG_PLL
|
||||
|
||||
/* SX127X's operating states in LoRa mode */
|
||||
#define SX127X_MODE_MASK 0x07
|
||||
#define SX127X_SLEEP_MODE 0x00
|
||||
#define SX127X_STANDBY_MODE 0x01
|
||||
#define SX127X_FSTX_MODE 0x02
|
||||
#define SX127X_TX_MODE 0x03
|
||||
#define SX127X_FSRX_MODE 0x04
|
||||
#define SX127X_RXCONTINUOUS_MODE 0x05
|
||||
#define SX127X_RXSINGLE_MODE 0x06
|
||||
#define SX127X_CAD_MODE 0x07
|
||||
#define SX127X_LORA_MODE 0x80
|
||||
|
||||
/* SX127X_REG_OCP flags */
|
||||
#define SX127X_FLAG_OCPON 0x20
|
||||
|
||||
/* SX127X's IRQ flags in LoRa mode */
|
||||
#define SX127X_FLAG_RXTIMEOUT 0x80
|
||||
#define SX127X_FLAG_RXDONE 0x40
|
||||
#define SX127X_FLAG_PAYLOADCRCERROR 0x20
|
||||
#define SX127X_FLAG_VALIDHEADER 0x10
|
||||
#define SX127X_FLAG_TXDONE 0x08
|
||||
#define SX127X_FLAG_CADDONE 0x04
|
||||
#define SX127X_FLAG_FHSSCHANGECHANNEL 0x02
|
||||
#define SX127X_FLAG_CADDETECTED 0x01
|
||||
|
||||
/* SX127X's IRQ flags' mask for output pins in LoRa mode */
|
||||
#define SX127X_FLAGMASK_RXTIMEOUT 0x80
|
||||
#define SX127X_FLAGMASK_RXDONE 0x40
|
||||
#define SX127X_FLAGMASK_PAYLOADCRCERROR 0x20
|
||||
#define SX127X_FLAGMASK_VALIDHEADER 0x10
|
||||
#define SX127X_FLAGMASK_TXDONE 0x08
|
||||
#define SX127X_FLAGMASK_CADDONE 0x04
|
||||
#define SX127X_FLAGMASK_FHSSCHANGECHANNEL 0x02
|
||||
#define SX127X_FLAGMASK_CADDETECTED 0x01
|
||||
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_MASK 0x01
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_EXPLICIT 0x00
|
||||
#define SX127X_MODEM_CONFIG1_HEADER_MODE_IMPLICIT 0x01
|
||||
|
||||
#define SX127X_MODEM_CONFIG2_RXPAYLOADCRCON 0x04
|
||||
|
||||
#define SX127X_MODEM_CONFIG3_AGCAUTOON 0x04
|
||||
|
||||
/* SX127X's RX/TX FIFO base address */
|
||||
#define SX127X_FIFO_RX_BASE_ADDRESS 0x00
|
||||
#define SX127X_FIFO_TX_BASE_ADDRESS 0x80
|
||||
|
||||
#define POLL_JIFFIES (msecs_to_jiffies(10))
|
||||
|
||||
struct sx127x {
|
||||
struct regmap *regmap;
|
||||
struct ieee802154_hw *ieee;
|
||||
struct spi_device *spi;
|
||||
|
||||
spinlock_t lock;
|
||||
bool busy;
|
||||
struct sk_buff *tx_skb;
|
||||
size_t delayed_tx_length;
|
||||
|
||||
struct timer_list poll_timer;
|
||||
struct work_struct poll_work;
|
||||
struct work_struct tx_work;
|
||||
|
||||
u32 oscillator_freq;
|
||||
};
|
||||
|
||||
static int sx127x_reg_read(struct sx127x *hw, unsigned int address, u8 *data)
|
||||
{
|
||||
int ret = regmap_raw_read(hw->regmap, address, data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&hw->spi->dev, "regmap_raw_read(0x%x) failed: %d\n", address, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_reg_write(struct sx127x *hw, unsigned int address, u8 data)
|
||||
{
|
||||
int ret = regmap_raw_write(hw->regmap, address, &data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&hw->spi->dev, "regmap_raw_write(0x%x) failed: %d\n", address, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_mode(struct sx127x *hw, unsigned int mode)
|
||||
{
|
||||
return sx127x_reg_write(hw, SX127X_REG_OP_MODE, SX127X_LORA_MODE | mode);
|
||||
}
|
||||
|
||||
static int sx127x_set_frequency(struct sx127x *hw, unsigned long frequency)
|
||||
{
|
||||
u8 msb_mid_lsb[3];
|
||||
int i;
|
||||
u64 frf;
|
||||
|
||||
frf = (((u64) frequency) << 19) / hw->oscillator_freq;
|
||||
for (i = sizeof msb_mid_lsb - 1; i >= 0; --i) {
|
||||
msb_mid_lsb[i] = (frf & 0xff);
|
||||
frf >>= 8;
|
||||
}
|
||||
|
||||
return regmap_raw_write(hw->regmap, SX127X_REG_FRF_MSB, msb_mid_lsb, sizeof msb_mid_lsb);
|
||||
}
|
||||
|
||||
static int sx127x_get_frequency(struct sx127x *hw, unsigned long *frequency)
|
||||
{
|
||||
u8 msb_mid_lsb[3];
|
||||
int i, ret;
|
||||
u64 frt;
|
||||
|
||||
ret = regmap_raw_read(hw->regmap, SX127X_REG_FRF_MSB, msb_mid_lsb, sizeof msb_mid_lsb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
frt = 0;
|
||||
for (i = 0; i < sizeof msb_mid_lsb; ++i)
|
||||
frt = (frt << 8) | msb_mid_lsb[i];
|
||||
|
||||
*frequency = (frt * hw->oscillator_freq) >> 19;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sx127x_set_ocp(struct sx127x *hw, u8 current_ma)
|
||||
{
|
||||
u8 ocp_trim;
|
||||
|
||||
/* P. 109
|
||||
*
|
||||
* Trimming of OCP current:
|
||||
* Imax = 45+5*OcpTrim [mA] if OcpTrim <= 15 (120 mA) /
|
||||
* Imax = -30+10*OcpTrim [mA] if 15 < OcpTrim <= 27 (130 to
|
||||
* 240 mA)
|
||||
* Imax = 240mA for higher settings
|
||||
* Default Imax = 100mA
|
||||
*/
|
||||
if (current_ma <= 45)
|
||||
ocp_trim = 0;
|
||||
else if (current_ma <= 120)
|
||||
ocp_trim = (current_ma - 45) / 5;
|
||||
else if (current_ma <= 240)
|
||||
ocp_trim = (current_ma + 30) / 10;
|
||||
else
|
||||
ocp_trim = 28;
|
||||
|
||||
return sx127x_reg_write(hw, SX127X_REG_OCP, SX127X_FLAG_OCPON | ocp_trim);
|
||||
}
|
||||
|
||||
static int sx127x_set_tx_power(struct sx127x *hw, int level)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// PA BOOST
|
||||
if (level > 17) {
|
||||
if (level > 20)
|
||||
level = 20;
|
||||
|
||||
// subtract 3 from level, so 18 - 20 maps to 15 - 17
|
||||
level -= 3;
|
||||
|
||||
// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PA_DAC, 0x87);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_set_ocp(hw, 140);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
if (level < 2)
|
||||
level = 2;
|
||||
|
||||
// Default value PA_HF/LF or +17dBm
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PA_DAC, 0x84);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_set_ocp(hw, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// PA_CONFIG
|
||||
#define PA_BOOST 0x80
|
||||
return sx127x_reg_write(hw, SX127X_REG_PA_CONFIG, PA_BOOST | (level - 2));
|
||||
}
|
||||
|
||||
static int sx127x_init(struct sx127x *hw, u8 *major, u8 *minor)
|
||||
{
|
||||
u8 modem_config1, modem_config2, lna, version;
|
||||
int ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_VERSION, &version);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (version != 0x12) {
|
||||
dev_err(&hw->spi->dev, "%s: bad version 0x%02x\n", __func__, version);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*major = (version >> 4) & 0xf;
|
||||
*minor = (version >> 0) & 0xf;
|
||||
|
||||
ret = sx127x_mode(hw, SX127X_SLEEP_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&hw->spi->dev, "%s: sx127x_mode() failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_RX_BASE_ADDR, SX127X_FIFO_RX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_TX_BASE_ADDR, SX127X_FIFO_TX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_LNA, &lna);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
lna |= 0x3; // ???, pero lo hace el sketch
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_LNA, lna);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &modem_config1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG2, &modem_config2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
modem_config1 &= ~SX127X_MODEM_CONFIG1_HEADER_MODE_MASK;
|
||||
modem_config1 |= SX127X_MODEM_CONFIG1_HEADER_MODE_EXPLICIT;
|
||||
modem_config2 |= SX127X_MODEM_CONFIG2_RXPAYLOADCRCON;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG1, modem_config1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG2, modem_config2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG3, SX127X_MODEM_CONFIG3_AGCAUTOON);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sx127x_tx_work(struct work_struct *work)
|
||||
{
|
||||
u8 mode;
|
||||
int ret = 0;
|
||||
bool set_busy = false;
|
||||
size_t i, size;
|
||||
const u8 *buffer;
|
||||
struct sx127x *hw = container_of(work, struct sx127x, tx_work);
|
||||
struct sk_buff *tx_skb;
|
||||
unsigned long spin_irq;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
|
||||
tx_skb = hw->tx_skb;
|
||||
if (!hw->busy && !hw->delayed_tx_length) {
|
||||
hw->busy = true;
|
||||
set_busy = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (!set_busy) {
|
||||
ret = -EBUSY;
|
||||
goto exit_busy;
|
||||
} else if (!tx_skb) {
|
||||
ret = -EINVAL;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
size = tx_skb->len;
|
||||
buffer = tx_skb->data;
|
||||
|
||||
if (!size || size > 128) {
|
||||
ret = -EINVAL;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) == SX127X_TX_MODE) {
|
||||
ret = -EBUSY;
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
// reset FIFO address and payload length
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, SX127X_FIFO_TX_BASE_ADDRESS);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
// write data
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_FIFO, buffer[i]);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
ret = sx127x_reg_write(hw, SX127X_REG_PAYLOAD_LENGTH, (u8) size);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
ret = sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode);
|
||||
if (ret < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) == SX127X_STANDBY_MODE
|
||||
|| (mode & SX127X_MODE_MASK) == SX127X_RXCONTINUOUS_MODE)
|
||||
ret = sx127x_mode(hw, SX127X_TX_MODE);
|
||||
else
|
||||
hw->delayed_tx_length = size;
|
||||
|
||||
exit_busy:
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (set_busy)
|
||||
hw->busy = false;
|
||||
|
||||
if (tx_skb)
|
||||
hw->tx_skb = NULL;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (tx_skb) {
|
||||
#ifndef SX127X_LEGACY_KERNEL
|
||||
if (ret)
|
||||
ieee802154_xmit_error(hw->ieee, tx_skb, ret);
|
||||
else
|
||||
#endif
|
||||
ieee802154_xmit_complete(hw->ieee, tx_skb, false);
|
||||
}
|
||||
|
||||
(void) ret;
|
||||
}
|
||||
|
||||
static void sx127x_poll_work(struct work_struct *work)
|
||||
{
|
||||
u8 ack_flags, lqi, mode, read_flags, rx_base, *rx_data, rx_length;
|
||||
bool do_rx = false, was_busy = false;
|
||||
unsigned long spin_irq;
|
||||
struct sx127x *hw = container_of(work, struct sx127x, poll_work);
|
||||
struct sk_buff *rx_skb = NULL;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (!hw->busy)
|
||||
hw->busy = true;
|
||||
else
|
||||
was_busy = true;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
if (was_busy)
|
||||
return;
|
||||
|
||||
if (sx127x_reg_read(hw, SX127X_REG_IRQ_FLAGS, &read_flags) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
ack_flags = 0;
|
||||
|
||||
if (read_flags & SX127X_FLAG_TXDONE)
|
||||
ack_flags |= SX127X_FLAG_TXDONE;
|
||||
|
||||
if (read_flags & SX127X_FLAG_RXDONE) {
|
||||
do_rx = true;
|
||||
ack_flags |= SX127X_FLAG_RXDONE;
|
||||
|
||||
if (read_flags & SX127X_FLAG_VALIDHEADER)
|
||||
ack_flags |= SX127X_FLAG_VALIDHEADER;
|
||||
else
|
||||
do_rx = false;
|
||||
|
||||
if (read_flags & SX127X_FLAG_PAYLOADCRCERROR) {
|
||||
do_rx = false;
|
||||
ack_flags |= SX127X_FLAG_PAYLOADCRCERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_rx) {
|
||||
if (sx127x_reg_read(hw, SX127X_REG_FIFO_RX_CURRENT_ADDR, &rx_base) < 0
|
||||
|| sx127x_reg_read(hw, SX127X_REG_RX_NB_BYTES, &rx_length) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, rx_base) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
rx_skb = dev_alloc_skb(IEEE802154_MTU);
|
||||
if (!rx_skb)
|
||||
goto exit_busy;
|
||||
|
||||
if (rx_length > IEEE802154_MTU)
|
||||
rx_length = IEEE802154_MTU;
|
||||
|
||||
rx_data = skb_put(rx_skb, rx_length);
|
||||
while (rx_length--)
|
||||
if (sx127x_reg_read(hw, SX127X_REG_FIFO, rx_data++) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_FIFO_RX_BASE_ADDR, SX127X_FIFO_RX_BASE_ADDRESS) < 0)
|
||||
goto exit_busy;
|
||||
}
|
||||
|
||||
if (sx127x_reg_write(hw, SX127X_REG_IRQ_FLAGS, ack_flags) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if (sx127x_reg_read(hw, SX127X_REG_OP_MODE, &mode) < 0)
|
||||
goto exit_busy;
|
||||
|
||||
if ((mode & SX127X_MODE_MASK) != SX127X_TX_MODE) {
|
||||
if (hw->delayed_tx_length) {
|
||||
sx127x_mode(hw, SX127X_TX_MODE);
|
||||
hw->delayed_tx_length = 0;
|
||||
} else if ((mode & SX127X_MODE_MASK) != SX127X_RXCONTINUOUS_MODE)
|
||||
sx127x_mode(hw, SX127X_RXCONTINUOUS_MODE);
|
||||
}
|
||||
|
||||
if (rx_skb) {
|
||||
lqi = 69; //TODO
|
||||
ieee802154_rx_irqsafe(hw->ieee, rx_skb, lqi);
|
||||
rx_skb = NULL; // rx_skb ownership transferred to the mac802154 layer
|
||||
}
|
||||
|
||||
exit_busy:
|
||||
if (rx_skb)
|
||||
kfree_skb(rx_skb);
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
hw->busy = false;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
}
|
||||
|
||||
static void sx127x_poll_timer(struct timer_list *timer)
|
||||
{
|
||||
struct sx127x *hw = container_of(timer, struct sx127x, poll_timer);
|
||||
schedule_work(&hw->poll_work);
|
||||
mod_timer(&hw->poll_timer, jiffies + POLL_JIFFIES);
|
||||
}
|
||||
|
||||
static int sx127x_802154_xmit_async(struct ieee802154_hw *ieee, struct sk_buff *skb)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sx127x *hw = ieee->priv;
|
||||
unsigned long spin_irq;
|
||||
|
||||
spin_lock_irqsave(&hw->lock, spin_irq);
|
||||
if (!hw->tx_skb)
|
||||
hw->tx_skb = skb_get(skb);
|
||||
else
|
||||
ret = -EBUSY;
|
||||
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
||||
|
||||
schedule_work(&hw->tx_work);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx127x_802154_ed(struct ieee802154_hw *ieee, u8 *level)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_channel(struct ieee802154_hw *ieee, u8 page, u8 channel)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_txpower(struct ieee802154_hw *ieee, s32 mbm)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
(void) hw;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sx127x_802154_start(struct ieee802154_hw *ieee)
|
||||
{
|
||||
int ret;
|
||||
struct sx127x *hw = ieee->priv;
|
||||
|
||||
ret = sx127x_mode(hw, SX127X_STANDBY_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&hw->spi->dev, "%s: sx127x_mode() failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mod_timer(&hw->poll_timer, jiffies + POLL_JIFFIES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sx127x_802154_stop(struct ieee802154_hw *ieee)
|
||||
{
|
||||
struct sx127x *hw = ieee->priv;
|
||||
|
||||
del_timer_sync(&hw->poll_timer);
|
||||
cancel_work_sync(&hw->poll_work);
|
||||
|
||||
(void) sx127x_mode(hw, SX127X_SLEEP_MODE);
|
||||
}
|
||||
|
||||
static int sx127x_802154_set_promiscuous_mode(struct ieee802154_hw *ieee, const bool on)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ieee802154_ops sx127x_802154_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.xmit_async = sx127x_802154_xmit_async,
|
||||
.ed = sx127x_802154_ed,
|
||||
.set_channel = sx127x_802154_set_channel,
|
||||
.set_txpower = sx127x_802154_set_txpower,
|
||||
.start = sx127x_802154_start,
|
||||
.stop = sx127x_802154_stop,
|
||||
.set_promiscuous_mode = sx127x_802154_set_promiscuous_mode,
|
||||
};
|
||||
|
||||
static bool sx127x_reg_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct regmap_config sx127x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = SX127X_MAX_REG,
|
||||
.read_flag_mask = 0x00,
|
||||
.write_flag_mask = 0x80,
|
||||
.volatile_reg = sx127x_reg_volatile,
|
||||
};
|
||||
|
||||
/* in mbm */
|
||||
static const s32 sx127x_tx_powers[] = {
|
||||
-200, -100, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100,
|
||||
1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300};
|
||||
|
||||
static int sx127x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
u8 major, minor;
|
||||
int ret = 0;
|
||||
struct sx127x *hw;
|
||||
unsigned long frequency;
|
||||
struct ieee802154_hw *ieee;
|
||||
struct device_node *of_node;
|
||||
|
||||
dev_dbg(&spi->dev, "%s hello\n", __func__);
|
||||
|
||||
ieee = ieee802154_alloc_hw(sizeof *hw, &sx127x_802154_ops);
|
||||
if (!ieee) {
|
||||
dev_err(&spi->dev, "ieee802154_alloc_hw(): out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hw = ieee->priv;
|
||||
hw->spi = spi;
|
||||
hw->ieee = ieee;
|
||||
hw->busy = false;
|
||||
hw->delayed_tx_length = 0;
|
||||
spin_lock_init(&hw->lock);
|
||||
|
||||
INIT_WORK(&hw->tx_work, sx127x_tx_work);
|
||||
INIT_WORK(&hw->poll_work, sx127x_poll_work);
|
||||
timer_setup(&hw->poll_timer, sx127x_poll_timer, 0);
|
||||
|
||||
hw->regmap = devm_regmap_init_spi(spi, &sx127x_regmap_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&spi->dev, "%s: devm_regmap_init_spi() failed: %d\n", __func__, ret);
|
||||
return PTR_ERR(hw->regmap);
|
||||
}
|
||||
|
||||
of_node = regmap_get_device(hw->regmap)->of_node;
|
||||
if (of_property_read_u32(of_node, "clock-frequency", &hw->oscillator_freq)) {
|
||||
dev_err(&spi->dev, "%s: 'clock-frequency' missing from DT node\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ieee->parent = &spi->dev;
|
||||
spi_set_drvdata(spi, hw);
|
||||
|
||||
//TODO: pin de reset
|
||||
|
||||
ieee802154_random_extended_addr(&ieee->phy->perm_extended_addr);
|
||||
|
||||
ieee->flags = IEEE802154_HW_TX_OMIT_CKSUM
|
||||
| IEEE802154_HW_RX_OMIT_CKSUM
|
||||
| IEEE802154_HW_PROMISCUOUS;
|
||||
|
||||
ieee->phy->flags = WPAN_PHY_FLAG_TXPOWER;
|
||||
ieee->phy->transmit_power = sx127x_tx_powers[12];
|
||||
ieee->phy->supported.tx_powers = sx127x_tx_powers;
|
||||
ieee->phy->supported.tx_powers_size = ARRAY_SIZE(sx127x_tx_powers);
|
||||
|
||||
/* SX1278 phy channel 11 as default */
|
||||
ieee->phy->current_channel = 11;
|
||||
/* Define channels could be used. */
|
||||
ieee->phy->supported.channels[0] = 1 << 11; /*TODO: sx1278_ieee_channel_mask(hw);*/
|
||||
|
||||
ret = sx127x_init(hw, &major, &minor);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_init() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "IEEE 802.15.4 over LoRa SX127x compatible device v%u.%u\n", major, minor);
|
||||
|
||||
// Valor viene del sketch
|
||||
ret = sx127x_set_frequency(hw, 434000000);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_set_frequency() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
ret = sx127x_get_frequency(hw, &frequency);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_get_frequency() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "%s: frequency sanity test: %lu Hz\n", __func__, frequency);
|
||||
|
||||
ret = sx127x_set_tx_power(hw, 17); // 17 dBm
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "%s: sx127x_set_tx_power() failed: %d\n", __func__, ret);
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
ret = ieee802154_register_hw(ieee);
|
||||
if (ret < 0)
|
||||
dev_err(&spi->dev, "ieee802154_register_hw() failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
|
||||
fail_init:
|
||||
ieee802154_free_hw(ieee);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef SX127X_LEGACY_KERNEL
|
||||
static int sx127x_spi_remove(struct spi_device *spi)
|
||||
#else
|
||||
static void sx127x_spi_remove(struct spi_device *spi)
|
||||
#endif
|
||||
{
|
||||
struct sx127x *hw = spi_get_drvdata(spi);
|
||||
|
||||
dev_dbg(&hw->spi->dev, "%s bye\n", __func__);
|
||||
|
||||
ieee802154_unregister_hw(hw->ieee);
|
||||
ieee802154_free_hw(hw->ieee);
|
||||
|
||||
#ifdef SX127X_LEGACY_KERNEL
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct of_device_id sx127x_of_ids[] = {
|
||||
{ .compatible = "semtech,sx1276" },
|
||||
{ .compatible = "semtech,sx1277" },
|
||||
{ .compatible = "semtech,sx1278" },
|
||||
{ .compatible = "semtech,sx1279" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sx127x_of_ids);
|
||||
|
||||
static const struct spi_device_id sx127x_spi_ids[] = {
|
||||
{ .name = "sx1276" },
|
||||
{ .name = "sx1277" },
|
||||
{ .name = "sx1278" },
|
||||
{ .name = "sx1279" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, sx127x_spi_ids);
|
||||
|
||||
static struct spi_driver sx127x_spi_driver = {
|
||||
.driver = {
|
||||
.name = "sx127x",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(sx127x_of_ids),
|
||||
},
|
||||
.probe = sx127x_spi_probe,
|
||||
.remove = sx127x_spi_remove,
|
||||
.id_table = sx127x_spi_ids,
|
||||
};
|
||||
|
||||
module_spi_driver(sx127x_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>");
|
||||
MODULE_AUTHOR("Fabian Montero <fabian@posixlycorrect.com>");
|
||||
MODULE_DESCRIPTION("Semtech SX127x LoRa PHY driver with IEEE 802.15.4 MAC interface");
|
||||
MODULE_LICENSE("GPL");
|
13
setup-lora_pi3.c
Executable file
13
setup-lora_pi3.c
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
modprobe regmap_spi
|
||||
modprobe mac802154
|
||||
insmod /home/loro/LoRo/LoRo/loro.ko
|
||||
sleep 1
|
||||
ip link set down wpan0
|
||||
iwpan dev wpan0 set pan_id 0xbeef
|
||||
ip link add link wpan0 name lowpan0 type lowpan
|
||||
ip link set up wpan0
|
||||
ip link set up lowpan0
|
13
setup-lora_pi4.c
Executable file
13
setup-lora_pi4.c
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
modprobe regmap_spi
|
||||
modprobe mac802154
|
||||
insmod ~/LoRo/LoRo/loro.ko
|
||||
sleep 0.5
|
||||
ip link set down wpan0
|
||||
iwpan dev wpan0 set pan_id 0xbeef
|
||||
ip link add link wpan0 name lowpan0 type lowpan
|
||||
ip link set up wpan0
|
||||
ip link set up lowpan0
|
Loading…
Reference in a new issue