lora_sx127/lora_sx127_rpi4.c

830 lines
22 KiB
C

/*-
* 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");