1861 lines
42 KiB
C
1861 lines
42 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))
|
|
|
|
/*stct sx1278_phy {
|
|
struct ieee802154_hw *hw;
|
|
struct regmap *map;
|
|
|
|
bool suspended;
|
|
u8 opmode;
|
|
struct timer_list timer;
|
|
struct work_struct irqwork;
|
|
|
|
spinlock_t buf_lock;
|
|
struct sk_buff *tx_buf;
|
|
u8 tx_delay;
|
|
bool one_to_be_sent;
|
|
bool post_tx_done;
|
|
bool is_busy;
|
|
};
|
|
|
|
int
|
|
sx127X_read_version(struct regmap *map)
|
|
{
|
|
u8 v;
|
|
int status;
|
|
|
|
status = sx127x_reg_read(hw, SX127X_REG_VERSION, &v);
|
|
|
|
if ((status == 0) && (v > 0) && (v < 0xFF))
|
|
status = v;
|
|
else
|
|
status = -ENODEV;
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
sx127X_set_mode(struct regmap *map, u8 op_mode)
|
|
{
|
|
sx127x_reg_write(hw, SX127X_REG_OP_MODE, op_mode);
|
|
}
|
|
|
|
u8
|
|
sx127X_get_mode(struct regmap *map)
|
|
{
|
|
u8 op_mode;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_OP_MODE, &op_mode);
|
|
|
|
return op_mode;
|
|
}
|
|
|
|
void
|
|
sx127X_set_state(struct regmap *map, u8 st)
|
|
{
|
|
u8 op_mode;
|
|
|
|
op_mode = sx127X_get_mode(map);
|
|
op_mode = (op_mode & 0xF8) | (st & 0x07);
|
|
sx127x_reg_write(hw, SX127X_REG_OP_MODE, op_mode);
|
|
}
|
|
|
|
u8
|
|
sx127X_get_state(struct regmap *map)
|
|
{
|
|
u8 op_mode;
|
|
|
|
op_mode = sx127X_get_mode(map) & 0x07;
|
|
|
|
return op_mode;
|
|
}
|
|
|
|
void
|
|
sx127X_set_lorafrq(struct regmap *map, u32 fr)
|
|
{
|
|
u64 frt;
|
|
u8 buf[3];
|
|
s8 i;
|
|
u32 f_xosc;
|
|
u8 op_mode;
|
|
|
|
struct device_node *of_node = (regmap_get_device(map))->of_node;
|
|
|
|
if (of_property_read_u32(of_node, "clock-frequency", &f_xosc))
|
|
f_xosc = xosc_frq;
|
|
|
|
frt = (uint64_t)fr * (uint64_t)__POW_2_19;
|
|
do_div(frt, f_xosc);
|
|
|
|
for (i = 2; i >= 0; i--)
|
|
buf[i] = do_div(frt, 256);
|
|
|
|
op_mode = sx127X_get_mode(map);
|
|
if (fr >= 779000000)
|
|
op_mode &= ~0x8;
|
|
else if (fr <= 525000000)
|
|
op_mode |= 0x8;
|
|
sx127X_set_state(map, SX127X_SLEEP_MODE);
|
|
regmap_raw_write(map, SX127X_REG_FRF_MSB, buf, 3);
|
|
sx127X_set_mode(map, op_mode);
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorafrq(struct regmap *map)
|
|
{
|
|
u64 frt = 0;
|
|
u8 buf[3];
|
|
u8 i;
|
|
int status;
|
|
u32 fr;
|
|
u32 f_xosc;
|
|
|
|
struct device_node *of_node = (regmap_get_device(map))->of_node;
|
|
|
|
if (of_property_read_u32(of_node, "clock-frequency", &f_xosc))
|
|
f_xosc = xosc_frq;
|
|
|
|
status = regmap_raw_read(map, SX127X_REG_FRF_MSB, buf, 3);
|
|
if (status < 0)
|
|
return 0.0;
|
|
|
|
for (i = 0; i <= 2; i++)
|
|
frt = frt * 256 + buf[i];
|
|
|
|
fr = frt * f_xosc / __POW_2_19;
|
|
|
|
return fr;
|
|
}
|
|
|
|
void
|
|
sx127X_set_lorapower(struct regmap *map, s32 pout)
|
|
{
|
|
u8 pacf;
|
|
u8 boost;
|
|
u8 output_power;
|
|
s32 pmax;
|
|
|
|
if (pout > 14) {
|
|
boost = 1;
|
|
pmax = 7;
|
|
output_power = pout - 2;
|
|
} else if (pout < 0) {
|
|
boost = 0;
|
|
pmax = 2;
|
|
output_power = 3 + pout;
|
|
} else {
|
|
boost = 0;
|
|
pmax = 7;
|
|
output_power = pout;
|
|
}
|
|
|
|
pacf = (boost << 7) | (pmax << 4) | (output_power);
|
|
sx127x_reg_write(hw, SX127X_REG_PA_CONFIG, pacf);
|
|
}
|
|
|
|
s32
|
|
sx127X_get_lorapower(struct regmap *map)
|
|
{
|
|
u8 pac;
|
|
u8 boost;
|
|
s32 output_power;
|
|
s32 pmax;
|
|
s32 pout;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_PA_CONFIG, &pac);
|
|
boost = (pac & 0x80) >> 7;
|
|
output_power = pac & 0x0F;
|
|
if (boost) {
|
|
pout = 2 + output_power;
|
|
} else {
|
|
pmax = (108 + 6 * ((pac & 0x70) >> 4));
|
|
pout = (pmax - (150 - output_power * 10)) / 10;
|
|
}
|
|
|
|
return pout;
|
|
}
|
|
|
|
#define sx127X_dbm2mbm(dbm) (dbm * 100)
|
|
|
|
#define sx127X_mbm2dbm(mbm) (mbm / 100)
|
|
|
|
s8 lna_gain[] = {
|
|
0,
|
|
-6,
|
|
-12,
|
|
-24,
|
|
-26,
|
|
-48
|
|
};
|
|
|
|
void
|
|
sx127X_set_loralna(struct regmap *map, s32 db)
|
|
{
|
|
u8 i, g;
|
|
u8 lnacf;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
if (lna_gain[i] <= db)
|
|
break;
|
|
}
|
|
g = i + 1;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_LNA, &lnacf);
|
|
lnacf = (lnacf & 0x1F) | (g << 5);
|
|
sx127x_reg_write(hw, SX127X_REG_LNA, lnacf);
|
|
}
|
|
|
|
s32
|
|
sx127X_get_loralna(struct regmap *map)
|
|
{
|
|
s32 db;
|
|
s8 i, g;
|
|
u8 lnacf;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_LNA, &lnacf);
|
|
g = (lnacf >> 5);
|
|
i = g - 1;
|
|
db = lna_gain[i];
|
|
|
|
return db;
|
|
}
|
|
|
|
void
|
|
sx127X_set_loralnaagc(struct regmap *map, s32 yesno)
|
|
{
|
|
u8 mcf3;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG3, &mcf3);
|
|
mcf3 = (yesno) ? (mcf3 | 0x04) : (mcf3 & (~0x04));
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG3, mcf3);
|
|
}
|
|
|
|
u8
|
|
sx127X_get_loraallflag(struct regmap *map)
|
|
{
|
|
u8 flags;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_IRQ_FLAGS, &flags);
|
|
|
|
return flags;
|
|
}
|
|
|
|
#define sx127X_get_loraflag(map, f) (sx127X_get_loraallflag(map) & (f))
|
|
|
|
void
|
|
sx127X_clear_loraflag(struct regmap *map, u8 f)
|
|
{
|
|
u8 flag;
|
|
|
|
flag = sx127X_get_loraallflag(map);
|
|
flag |= f;
|
|
sx127x_reg_write(hw, SX127X_REG_IRQ_FLAGS, flag);
|
|
}
|
|
|
|
#define sx127X_clear_loraallflag(spi) sx127X_clear_loraflag(spi, 0xFF)
|
|
|
|
void
|
|
sx127X_set_lorasprf(struct regmap *map, u32 c_s)
|
|
{
|
|
u8 sf;
|
|
u8 mcf2;
|
|
|
|
for (sf = 6; sf < 12; sf++) {
|
|
if (c_s == ((u32)1 << sf))
|
|
break;
|
|
}
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG2, &mcf2);
|
|
mcf2 = (mcf2 & 0x0F) | (sf << 4);
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG2, mcf2);
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorasprf(struct regmap *map)
|
|
{
|
|
u8 sf;
|
|
u32 c_s;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG2, &sf);
|
|
sf = sf >> 4;
|
|
c_s = 1 << sf;
|
|
|
|
return c_s;
|
|
}
|
|
|
|
const u32 hz[] = {
|
|
7800,
|
|
10400,
|
|
15600,
|
|
20800,
|
|
31250,
|
|
41700,
|
|
62500,
|
|
125000,
|
|
250000,
|
|
500000
|
|
};
|
|
|
|
void
|
|
sx127X_set_lorabw(struct regmap *map, u32 bw)
|
|
{
|
|
u8 i;
|
|
u8 mcf1;
|
|
|
|
for (i = 0; i < 9; i++) {
|
|
if (hz[i] >= bw)
|
|
break;
|
|
}
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &mcf1);
|
|
mcf1 = (mcf1 & 0x0F) | (i << 4);
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG1, mcf1);
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorabw(struct regmap *map)
|
|
{
|
|
u8 mcf1;
|
|
u8 bw;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &mcf1);
|
|
bw = mcf1 >> 4;
|
|
|
|
return hz[bw];
|
|
}
|
|
|
|
void
|
|
sx127X_set_loracr(struct regmap *map, u8 cr)
|
|
{
|
|
u8 mcf1;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &mcf1);
|
|
mcf1 = (mcf1 & 0x0E) | (((cr & 0xF) - 4) << 1);
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG1, mcf1);
|
|
}
|
|
|
|
u8
|
|
sx127X_get_loracr(struct regmap *map)
|
|
{
|
|
u8 mcf1;
|
|
u8 cr;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &mcf1);
|
|
cr = 0x40 + ((mcf1 & 0x0E) >> 1) + 4;
|
|
|
|
return cr;
|
|
}
|
|
|
|
void
|
|
sx127X_set_loraimplicit(struct regmap *map, u8 yesno)
|
|
{
|
|
u8 mcf1;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG1, &mcf1);
|
|
mcf1 = (yesno) ? (mcf1 | 0x01) : (mcf1 & 0xFE);
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG1, mcf1);
|
|
}
|
|
|
|
void
|
|
sx127X_set_lorarxbytetimeout(struct regmap *map, u32 n)
|
|
{
|
|
u8 buf[2];
|
|
u8 mcf2;
|
|
|
|
if (n < 1)
|
|
n = 1;
|
|
if (n > 1023)
|
|
n = 1023;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG2, &mcf2);
|
|
|
|
buf[1] = n % 256;
|
|
buf[0] = (mcf2 & 0xFC) | (n >> 8);
|
|
|
|
regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, buf, 2);
|
|
}
|
|
|
|
void
|
|
sx127X_set_lorarxtimeout(struct regmap *map, u32 ms)
|
|
{
|
|
u32 n;
|
|
|
|
n = ms * sx127X_get_lorabw(map) / (sx127X_get_lorasprf(map) * 1000);
|
|
|
|
sx127X_set_lorarxbytetimeout(map, n);
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorarxbytetimeout(struct regmap *map)
|
|
{
|
|
u32 n;
|
|
u8 buf[2];
|
|
|
|
regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, buf, 2);
|
|
|
|
n = (buf[0] & 0x03) * 256 + buf[1];
|
|
|
|
return n;
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorarxtimeout(struct regmap *map)
|
|
{
|
|
u32 ms;
|
|
|
|
ms = 1000 * sx127X_get_lorarxbytetimeout(map) *
|
|
sx127X_get_lorasprf(map) / sx127X_get_lorabw(map);
|
|
|
|
return ms;
|
|
}
|
|
|
|
void
|
|
sx127X_set_loramaxrxbuff(struct regmap *map, u8 len)
|
|
{
|
|
sx127x_reg_write(hw, SX127X_REG_MAX_PAYLOAD_LENGTH, len);
|
|
}
|
|
|
|
u8
|
|
sx127X_get_loralastpktpayloadlen(struct regmap *map)
|
|
{
|
|
u8 len;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_RX_NB_BYTES, &len);
|
|
|
|
return len;
|
|
}
|
|
|
|
ssize_t
|
|
sx127X_readloradata(struct regmap *map, u8 *buf, size_t len)
|
|
{
|
|
u8 start_adr;
|
|
int ret;
|
|
|
|
start_adr = SX127X_FIFO_RX_BASE_ADDRESS;
|
|
sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, start_adr);
|
|
|
|
len = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU;
|
|
ret = regmap_raw_read(map, SX127X_REG_FIFO, buf, len);
|
|
|
|
return (ret >= 0) ? len : ret;
|
|
}
|
|
|
|
size_t
|
|
sx127X_sendloradata(struct regmap *map, u8 *buf, size_t len)
|
|
{
|
|
u8 base_adr;
|
|
u8 blen;
|
|
|
|
base_adr = SX127X_FIFO_TX_BASE_ADDRESS;
|
|
sx127x_reg_write(hw, SX127X_REG_FIFO_ADDR_PTR, base_adr);
|
|
|
|
blen = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU;
|
|
regmap_raw_write(map, SX127X_REG_FIFO, buf, blen);
|
|
|
|
sx127x_reg_write(hw, SX127X_REG_PAYLOAD_LENGTH, blen);
|
|
|
|
return blen;
|
|
}
|
|
|
|
s32
|
|
sx127X_get_loralastpktsnr(struct regmap *map)
|
|
{
|
|
s32 db;
|
|
s8 snr;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_PKT_SNR_VALUE, &snr);
|
|
db = snr / 4;
|
|
|
|
return db;
|
|
}
|
|
|
|
s32
|
|
sx127X_get_loralastpktrssi(struct regmap *map)
|
|
{
|
|
s32 dbm;
|
|
u8 lhf;
|
|
u8 rssi;
|
|
s8 snr;
|
|
|
|
lhf = sx127X_get_mode(map) & 0x08;
|
|
sx127x_reg_read(hw, SX127X_REG_PKT_RSSI_VALUE, &rssi);
|
|
dbm = (lhf) ? -164 + rssi : -157 + rssi;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_PKT_SNR_VALUE, &snr);
|
|
if (snr < 0)
|
|
dbm += snr / 4;
|
|
|
|
return dbm;
|
|
}
|
|
|
|
s32
|
|
sx127X_get_lorarssi(struct regmap *map)
|
|
{
|
|
s32 dbm;
|
|
u8 lhf;
|
|
u8 rssi;
|
|
|
|
lhf = sx127X_get_mode(map) & 0x08;
|
|
sx127x_reg_read(hw, SX127X_REG_RSSI_VALUE, &rssi);
|
|
dbm = (lhf) ? -164 + rssi : -157 + rssi;
|
|
|
|
return dbm;
|
|
}
|
|
|
|
void
|
|
sx127X_set_lorapreamblelen(struct regmap *map, u32 len)
|
|
{
|
|
u8 pl[2];
|
|
|
|
pl[1] = len % 256;
|
|
pl[0] = (len >> 8) % 256;
|
|
|
|
regmap_raw_write(map, SX127X_REG_PREAMBLE_MSB, pl, 2);
|
|
}
|
|
|
|
u32
|
|
sx127X_get_lorapreamblelen(struct regmap *map)
|
|
{
|
|
u8 pl[2];
|
|
u32 len;
|
|
|
|
regmap_raw_read(map, SX127X_REG_PREAMBLE_MSB, pl, 2);
|
|
len = pl[0] * 256 + pl[1];
|
|
|
|
return len;
|
|
}
|
|
|
|
sx127X_set_loracrc(struct regmap *map, u8 yesno)
|
|
{
|
|
u8 mcf2;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_MODEM_CONFIG2, &mcf2);
|
|
mcf2 = (yesno) ? mcf2 | (1 << 2) : mcf2 & (~(1 << 2));
|
|
sx127x_reg_write(hw, SX127X_REG_MODEM_CONFIG2, mcf2);
|
|
}
|
|
|
|
void
|
|
sx127X_set_boost(struct regmap *map, u8 yesno)
|
|
{
|
|
u8 pacf;
|
|
|
|
sx127x_reg_read(hw, SX127X_REG_PA_CONFIG, &pacf);
|
|
pacf = (yesno) ? pacf | (1 << 7) : pacf & (~(1 << 7));
|
|
sx127x_reg_write(hw, SX127X_REG_PA_CONFIG, pacf);
|
|
}
|
|
|
|
void
|
|
sx127X_start_loramode(struct regmap *map)
|
|
{
|
|
u8 op_mode;
|
|
u8 base_adr;
|
|
struct device_node *of_node = (regmap_get_device(map))->of_node;
|
|
|
|
op_mode = sx127X_get_mode(map);
|
|
dev_dbg(regmap_get_device(map),
|
|
"the original OP mode is 0x%X\n", op_mode);
|
|
|
|
sx127X_set_state(map, SX127X_SLEEP_MODE);
|
|
op_mode = sx127X_get_mode(map);
|
|
op_mode = op_mode | 0x80;
|
|
sx127x_reg_write(hw, SX127X_REG_OP_MODE, op_mode);
|
|
sx127X_set_state(map, SX127X_STANDBY_MODE);
|
|
op_mode = sx127X_get_mode(map);
|
|
dev_dbg(regmap_get_device(map),
|
|
"the current OP mode is 0x%X\n", op_mode);
|
|
|
|
sx127X_set_loraimplicit(map, 0);
|
|
|
|
base_adr = SX127X_FIFO_RX_BASE_ADDRESS;
|
|
sx127x_reg_write(hw, SX127X_REG_FIFO_RX_BASE_ADDR, base_adr);
|
|
base_adr = SX127X_FIFO_TX_BASE_ADDRESS;
|
|
sx127x_reg_write(hw, SX127X_REG_FIFO_TX_BASE_ADDR, base_adr);
|
|
|
|
of_property_read_u32(of_node, "spreading-factor", &sprf);
|
|
sx127X_set_lorasprf(map, sprf);
|
|
|
|
sx127X_set_lorarxbytetimeout(map, rx_timeout);
|
|
|
|
sx127X_clear_loraallflag(map);
|
|
sx127X_set_state(map, SX127X_RXSINGLE_MODE);
|
|
}
|
|
|
|
int
|
|
init_sx127x(struct regmap *map)
|
|
{
|
|
int v;
|
|
#ifdef DEBUG
|
|
u8 fv, mv;
|
|
#endif
|
|
|
|
dev_dbg(regmap_get_device(map), "init sx127X\n");
|
|
|
|
v = sx127X_read_version(map);
|
|
if (v > 0) {
|
|
#ifdef DEBUG
|
|
fv = (v >> 4) & 0xF;
|
|
mv = v & 0xF;
|
|
dev_dbg(regmap_get_device(map), "chip version %d.%d\n", fv, mv);
|
|
#endif
|
|
return 0;
|
|
} else {
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
#ifndef SX1278_IEEE_SENSITIVITY
|
|
#define SX1278_IEEE_SENSITIVITY (-148)
|
|
#endif
|
|
static s32 sensitivity = SX1278_IEEE_SENSITIVITY;
|
|
module_param(sensitivity, int, 0000);
|
|
MODULE_PARM_DESC(sensitivity, "RF receiver's sensitivity");
|
|
|
|
#define SX1278_IEEE_ENERGY_RANGE (-sensitivity)
|
|
|
|
static int
|
|
sx1278_ieee_ed(struct ieee802154_hw *hw, u8 *level)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
s32 rssi;
|
|
s32 range = SX1278_IEEE_ENERGY_RANGE - 10;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "%s\n", __func__);
|
|
|
|
rssi = sx127X_get_lorarssi(phy->map);
|
|
if (rssi < (sensitivity + 10))
|
|
*level = 0;
|
|
else if (rssi >= 0)
|
|
*level = 255;
|
|
else
|
|
*level = ((s32)255 * (rssi + range) / range) % 255;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef SX1278_IEEE_CHANNEL_MIN
|
|
#define SX1278_IEEE_CHANNEL_MIN 11
|
|
#endif
|
|
static u8 channel_min = SX1278_IEEE_CHANNEL_MIN;
|
|
module_param(channel_min, byte, 0000);
|
|
MODULE_PARM_DESC(channel_min, "Minimal channel number");
|
|
|
|
#ifndef SX1278_IEEE_CHANNEL_MAX
|
|
#define SX1278_IEEE_CHANNEL_MAX 11
|
|
#endif
|
|
static u8 channel_max = SX1278_IEEE_CHANNEL_MAX;
|
|
module_param(channel_max, byte, 0000);
|
|
MODULE_PARM_DESC(channel_max, "Maximum channel number");
|
|
|
|
#ifndef SX1278_IEEE_CENTER_CARRIER_FRQ
|
|
#define SX1278_IEEE_CENTER_CARRIER_FRQ 434000000
|
|
#endif
|
|
static u32 carrier_frq = SX1278_IEEE_CENTER_CARRIER_FRQ;
|
|
module_param(carrier_frq, uint, 0000);
|
|
MODULE_PARM_DESC(carrier_frq, "Center carrier frequency in Hz");
|
|
|
|
#ifndef SX1278_IEEE_BANDWIDTH
|
|
#define SX1278_IEEE_BANDWIDTH 500000
|
|
#endif
|
|
static u32 bandwidth = SX1278_IEEE_BANDWIDTH;
|
|
module_param(bandwidth, uint, 0000);
|
|
MODULE_PARM_DESC(bandwidth, "Bandwidth in Hz");
|
|
|
|
struct rf_frq {
|
|
u32 carrier;
|
|
u32 bw;
|
|
u8 ch_min;
|
|
u8 ch_max;
|
|
};
|
|
|
|
void
|
|
sx1278_ieee_get_rf_config(struct ieee802154_hw *hw, struct rf_frq *rf)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
struct device_node *of_node = (regmap_get_device(phy->map))->of_node;
|
|
|
|
if (of_property_read_u32(of_node, "center-carrier-frq", &rf->carrier))
|
|
rf->carrier = carrier_frq;
|
|
|
|
if (of_property_read_u32(of_node, "rf-bandwidth", &rf->bw))
|
|
rf->bw = bandwidth;
|
|
|
|
if (of_property_read_u8(of_node, "minimal-RF-channel", &rf->ch_min))
|
|
rf->ch_min = channel_min;
|
|
|
|
if (of_property_read_u8(of_node, "maximum-RF-channel", &rf->ch_max))
|
|
rf->ch_max = channel_max;
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
struct rf_frq rf;
|
|
u32 fr;
|
|
s8 d;
|
|
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s channel: %u\n", __func__, channel);
|
|
|
|
sx1278_ieee_get_rf_config(hw, &rf);
|
|
|
|
if (channel < rf.ch_min)
|
|
channel = rf.ch_min;
|
|
else if (channel > rf.ch_max)
|
|
channel = rf.ch_max;
|
|
|
|
d = channel - (rf.ch_min + rf.ch_max) / 2;
|
|
fr = rf.carrier + d * rf.bw;
|
|
|
|
sx127X_set_lorafrq(phy->map, fr);
|
|
phy->opmode = sx127X_get_mode(phy->map);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_set_txpower(struct ieee802154_hw *hw, s32 mbm)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
s32 dbm = sx127X_mbm2dbm(mbm);
|
|
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s TX power: %d mbm\n", __func__, mbm);
|
|
|
|
sx127X_set_lorapower(phy->map, dbm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sx1278_ieee_rx(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
bool do_rx;
|
|
unsigned long f;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "%s\n", __func__);
|
|
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
if (!phy->is_busy) {
|
|
phy->is_busy = true;
|
|
do_rx = true;
|
|
} else {
|
|
do_rx = false;
|
|
}
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
|
|
if (do_rx) {
|
|
sx127X_set_state(phy->map, SX127X_RXSINGLE_MODE);
|
|
return 0;
|
|
} else {
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s: device is busy\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_rx_complete(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
struct sk_buff *skb;
|
|
u8 len;
|
|
u8 lqi;
|
|
s32 rssi;
|
|
s32 range = SX1278_IEEE_ENERGY_RANGE;
|
|
int err;
|
|
unsigned long f;
|
|
|
|
skb = dev_alloc_skb(IEEE802154_MTU);
|
|
if (!skb) {
|
|
err = -ENOMEM;
|
|
dev_err(regmap_get_device(phy->map),
|
|
"%s: driver is out of memory\n", __func__);
|
|
goto sx1278_ieee_rx_err;
|
|
}
|
|
|
|
len = sx127X_get_loralastpktpayloadlen(phy->map);
|
|
sx127X_readloradata(phy->map, skb_put(skb, len), len);
|
|
|
|
rssi = sx127X_get_loralastpktrssi(phy->map);
|
|
rssi = (rssi > 0) ? 0 : rssi;
|
|
lqi = ((s32)255 * (rssi + range) / range) % 255;
|
|
|
|
ieee802154_rx_irqsafe(hw, skb, lqi);
|
|
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s: len=%u LQI=%u\n", __func__, len, lqi);
|
|
|
|
err = 0;
|
|
|
|
sx1278_ieee_rx_err:
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
phy->is_busy = false;
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
sx1278_ieee_tx(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
struct sk_buff *tx_buf = phy->tx_buf;
|
|
bool do_tx = false;
|
|
unsigned long f;
|
|
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s: len=%u\n", __func__, tx_buf->len);
|
|
|
|
if (!phy->post_tx_done) {
|
|
sx127X_sendloradata(phy->map, tx_buf->data, tx_buf->len);
|
|
phy->post_tx_done = true;
|
|
}
|
|
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
if (!phy->is_busy) {
|
|
phy->is_busy = true;
|
|
do_tx = true;
|
|
phy->one_to_be_sent = false;
|
|
}
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
|
|
if (do_tx) {
|
|
phy->opmode = (phy->opmode & 0xF8) | SX127X_TX_MODE;
|
|
regmap_write_async(phy->map, SX127X_REG_OP_MODE, phy->opmode);
|
|
return 0;
|
|
} else {
|
|
dev_dbg(regmap_get_device(phy->map),
|
|
"%s: device is busy\n", __func__);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_tx_complete(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
struct sk_buff *skb = phy->tx_buf;
|
|
unsigned long f;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "%s\n", __func__);
|
|
|
|
ieee802154_xmit_complete(hw, skb, false);
|
|
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
phy->is_busy = false;
|
|
phy->tx_buf = NULL;
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
int ret;
|
|
unsigned long f;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "%s\n", __func__);
|
|
|
|
WARN_ON(phy->suspended);
|
|
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
if (phy->tx_buf) {
|
|
ret = -EBUSY;
|
|
} else {
|
|
phy->tx_buf = skb;
|
|
phy->one_to_be_sent = true;
|
|
phy->post_tx_done = false;
|
|
ret = 0;
|
|
}
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_start(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "interface up\n");
|
|
|
|
sx1278_ieee_set_channel(hw, 0, hw->phy->current_channel);
|
|
phy->suspended = false;
|
|
sx127X_start_loramode(phy->map);
|
|
phy->opmode = sx127X_get_mode(phy->map);
|
|
add_timer(&phy->timer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sx1278_ieee_stop(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
|
|
dev_dbg(regmap_get_device(phy->map), "interface down\n");
|
|
|
|
phy->suspended = true;
|
|
del_timer(&phy->timer);
|
|
sx127X_set_state(phy->map, SX127X_SLEEP_MODE);
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sx1278_ieee_statemachine(struct ieee802154_hw *hw)
|
|
{
|
|
struct sx1278_phy *phy = hw->priv;
|
|
u8 flags;
|
|
u8 state;
|
|
bool do_next_rx = false;
|
|
unsigned long f;
|
|
|
|
flags = sx127X_get_loraallflag(phy->map);
|
|
state = sx127X_get_state(phy->map);
|
|
|
|
if (flags & (SX127X_FLAG_RXTIMEOUT | SX127X_FLAG_PAYLOADCRCERROR)) {
|
|
sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXTIMEOUT
|
|
| SX127X_FLAG_PAYLOADCRCERROR
|
|
| SX127X_FLAG_RXDONE);
|
|
spin_lock_irqsave(&phy->buf_lock, f);
|
|
phy->is_busy = false;
|
|
spin_unlock_irqrestore(&phy->buf_lock, f);
|
|
do_next_rx = true;
|
|
} else if (flags & SX127X_FLAG_RXDONE) {
|
|
sx1278_ieee_rx_complete(phy->hw);
|
|
sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXDONE);
|
|
do_next_rx = true;
|
|
}
|
|
|
|
if (flags & SX127X_FLAG_TXDONE) {
|
|
sx1278_ieee_tx_complete(phy->hw);
|
|
sx127X_clear_loraflag(phy->map, SX127X_FLAG_TXDONE);
|
|
phy->tx_delay = 10;
|
|
do_next_rx = true;
|
|
}
|
|
|
|
if (phy->one_to_be_sent &&
|
|
(state == SX127X_STANDBY_MODE) &&
|
|
(phy->tx_delay == 0)) {
|
|
if (!sx1278_ieee_tx(phy->hw))
|
|
do_next_rx = false;
|
|
}
|
|
|
|
if (do_next_rx)
|
|
sx1278_ieee_rx(phy->hw);
|
|
|
|
if (phy->tx_delay > 0)
|
|
phy->tx_delay -= 1;
|
|
|
|
if (!phy->suspended) {
|
|
phy->timer.expires = jiffies_64 + 1;
|
|
add_timer(&phy->timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sx1278_timer_irqwork(struct work_struct *work)
|
|
{
|
|
struct sx1278_phy *phy;
|
|
|
|
phy = container_of(work, struct sx1278_phy, irqwork);
|
|
sx1278_ieee_statemachine(phy->hw);
|
|
}
|
|
|
|
static void
|
|
sx1278_timer_isr(struct timer_list *timer)
|
|
{
|
|
struct sx1278_phy *phy = container_of(timer, struct sx1278_phy, timer);
|
|
|
|
schedule_work(&phy->irqwork);
|
|
}
|
|
|
|
u32
|
|
sx1278_ieee_channel_mask(struct ieee802154_hw *hw)
|
|
{
|
|
struct rf_frq rf;
|
|
u32 mask;
|
|
|
|
sx1278_ieee_get_rf_config(hw, &rf);
|
|
|
|
mask = ((u32)(1 << (rf.ch_max + 1)) - (u32)(1 << rf.ch_min));
|
|
|
|
return mask;
|
|
}
|
|
|
|
static int
|
|
sx1278_ieee_add_one(struct sx1278_phy *phy)
|
|
{
|
|
INIT_WORK(&phy->irqwork, sx1278_timer_irqwork);
|
|
|
|
timer_setup(&phy->timer, sx1278_timer_isr, 0);
|
|
phy->timer.expires = jiffies_64 + HZ;
|
|
|
|
spin_lock_init(&phy->buf_lock);
|
|
|
|
err = init_sx127x(phy->map);
|
|
if (err)
|
|
goto err_reg;
|
|
|
|
return 0;
|
|
|
|
err_reg:
|
|
dev_err(regmap_get_device(phy->map),
|
|
"register as IEEE 802.15.4 device failed\n");
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
sx1278_ieee_del(struct sx1278_phy *phy)
|
|
{
|
|
del_timer(&phy->timer);
|
|
flush_work(&phy->irqwork);
|
|
}*/
|
|
|
|
/////////////////////////
|
|
// Frontera Chino/Soto //
|
|
// Frontera Chino/Soto //
|
|
// Frontera Chino/Soto //
|
|
/////////////////////////
|
|
|
|
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_TX_BASE_ADDR, SX127X_FIFO_TX_BASE_ADDRESS);
|
|
if (ret < 0)
|
|
goto exit_busy;
|
|
|
|
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;
|
|
|
|
hw->delayed_tx_length = size;
|
|
|
|
exit_busy:
|
|
spin_lock_irqsave(&hw->lock, spin_irq);
|
|
if (set_busy)
|
|
hw->busy = false;
|
|
|
|
if (ret && tx_skb)
|
|
hw->tx_skb = NULL;
|
|
spin_unlock_irqrestore(&hw->lock, spin_irq);
|
|
|
|
if (ret && tx_skb) {
|
|
#ifdef SX127X_LEGACY_KERNEL
|
|
ieee802154_xmit_complete(hw->ieee, tx_skb, false);
|
|
#else
|
|
ieee802154_xmit_error(hw->ieee, tx_skb, ret);
|
|
#endif
|
|
}
|
|
|
|
(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, *tx_skb = NULL;
|
|
|
|
spin_lock_irqsave(&hw->lock, spin_irq);
|
|
if (!hw->busy)
|
|
hw->busy = true;
|
|
else
|
|
was_busy = true;
|
|
|
|
tx_skb = hw->tx_skb;
|
|
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;
|
|
else
|
|
tx_skb = NULL;
|
|
|
|
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);
|
|
|
|
if (tx_skb)
|
|
ieee802154_xmit_complete(hw->ieee, tx_skb, false);
|
|
|
|
spin_lock_irqsave(&hw->lock, spin_irq);
|
|
hw->busy = false;
|
|
if (tx_skb)
|
|
hw->tx_skb = NULL;
|
|
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;
|
|
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);
|
|
dev_dbg(regmap_get_device(hw->regmap), "%s\n", __func__);
|
|
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);
|
|
dev_dbg(regmap_get_device(hw->regmap), "%s\n", __func__);
|
|
}
|
|
|
|
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");
|