[PATCH RFC lora-next 2/4] net: lora: sx1301: add minimal to get AGC working prior to tx work

From: Ben Whitten
Date: Wed Dec 19 2018 - 10:56:38 EST


As part of initialisation when opening the lora device after loading
the AGC firmware we need to satisfy its startup procedure which involves
a few steps;

Loading a 16 entry lookup table.
For this I have hard coded the laird ETSI certified table for use on the
RG186-M2 (EU) cards, this will need investigation on how other devices
load calibration data.

Selecting the correct channel to transmit on.
Currently always 0 for the reference design.

Then ending the AGC init procedure and seeing that it has come up.

Signed-off-by: Ben Whitten <ben.whitten@xxxxxxxxxxxxx>
---
drivers/net/lora/sx1301.c | 254 +++++++++++++++++++++++++++++++++++++-
drivers/net/lora/sx1301.h | 16 +++
2 files changed, 268 insertions(+), 2 deletions(-)

diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
index e75df93b96d8..0c7b6d0b31af 100644
--- a/drivers/net/lora/sx1301.c
+++ b/drivers/net/lora/sx1301.c
@@ -24,6 +24,121 @@

#include "sx1301.h"

+static struct sx1301_tx_gain_lut tx_gain_lut[] = {
+ {
+ .dig_gain = 0,
+ .pa_gain = 0,
+ .dac_gain = 3,
+ .mix_gain = 8,
+ .rf_power = -3,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 0,
+ .dac_gain = 3,
+ .mix_gain = 9,
+ .rf_power = 0,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 0,
+ .dac_gain = 3,
+ .mix_gain = 12,
+ .rf_power = 3,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 0,
+ .dac_gain = 3,
+ .mix_gain = 13,
+ .rf_power = 4,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 8,
+ .rf_power = 6,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 9,
+ .rf_power = 9,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 10,
+ .rf_power = 10,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 11,
+ .rf_power = 12,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 12,
+ .rf_power = 13,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 13,
+ .rf_power = 14,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 1,
+ .dac_gain = 3,
+ .mix_gain = 15,
+ .rf_power = 16,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 2,
+ .dac_gain = 3,
+ .mix_gain = 10,
+ .rf_power = 19,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 2,
+ .dac_gain = 3,
+ .mix_gain = 11,
+ .rf_power = 21,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 2,
+ .dac_gain = 3,
+ .mix_gain = 12,
+ .rf_power = 22,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 2,
+ .dac_gain = 3,
+ .mix_gain = 13,
+ .rf_power = 24,
+ },
+ {
+ .dig_gain = 0,
+ .pa_gain = 2,
+ .dac_gain = 3,
+ .mix_gain = 14,
+ .rf_power = 25,
+ },
+};
+
static const struct regmap_range_cfg sx1301_regmap_ranges[] = {
{
.name = "Pages",
@@ -184,6 +299,34 @@ static int sx1301_load_firmware(struct sx1301_priv *priv, int mcu,
return 0;
}

+static int sx1301_agc_transaction(struct sx1301_priv *priv, unsigned int val,
+ unsigned int *status)
+{
+ int ret;
+
+ ret = regmap_write(priv->regmap, SX1301_CHRS, SX1301_AGC_CMD_WAIT);
+ if (ret) {
+ dev_err(priv->dev, "AGC transaction start failed\n");
+ return ret;
+ }
+ usleep_range(1000, 2000);
+
+ ret = regmap_write(priv->regmap, SX1301_CHRS, val);
+ if (ret) {
+ dev_err(priv->dev, "AGC transaction value failed\n");
+ return ret;
+ }
+ usleep_range(1000, 2000);
+
+ ret = regmap_read(priv->regmap, SX1301_AGCSTS, status);
+ if (ret) {
+ dev_err(priv->dev, "AGC status read failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int sx1301_agc_calibrate(struct sx1301_priv *priv)
{
const struct firmware *fw;
@@ -356,9 +499,53 @@ static int sx1301_load_all_firmware(struct sx1301_priv *priv)
return -ENXIO;
}

- return 0;
+ return ret;
}

+static int sx1301_load_tx_gain_lut(struct sx1301_priv *priv)
+{
+ struct sx1301_tx_gain_lut *lut = priv->tx_gain_lut;
+ unsigned int val, status;
+ int ret, i;
+
+ /* HACK use internal gain table in the short term */
+ lut = tx_gain_lut;
+ priv->tx_gain_lut_size = ARRAY_SIZE(tx_gain_lut);
+
+ for (i = 0; i < priv->tx_gain_lut_size; i++) {
+ val = lut->mix_gain + (lut->dac_gain << 4) +
+ (lut->pa_gain << 6);
+
+ ret = sx1301_agc_transaction(priv, val, &status);
+ if (ret) {
+ dev_err(priv->dev, "AGC LUT load failed\n");
+ return ret;
+ }
+ if (status != (0x30 + i)) {
+ dev_err(priv->dev,
+ "AGC firmware LUT init error: 0x%02X\n", val);
+ return -ENXIO;
+ }
+ lut++;
+ }
+
+ /* Abort the transaction if there are less then 16 entries */
+ if (priv->tx_gain_lut_size < SX1301_TX_GAIN_LUT_MAX) {
+ ret = sx1301_agc_transaction(priv, SX1301_AGC_CMD_ABORT, &val);
+ if (ret) {
+ dev_err(priv->dev, "AGC LUT abort failed\n");
+ return ret;
+ }
+ if (val != 0x30) {
+ dev_err(priv->dev,
+ "AGC firmware LUT abort error: 0x%02X\n", val);
+ return -ENXIO;
+ }
+ }
+
+ return ret;
+};
+
static netdev_tx_t sx130x_loradev_start_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
@@ -378,6 +565,7 @@ static int sx130x_loradev_open(struct net_device *netdev)
{
struct sx1301_priv *priv = netdev_priv(netdev);
int ret;
+ unsigned int val;

netdev_dbg(netdev, "%s", __func__);

@@ -416,12 +604,74 @@ static int sx130x_loradev_open(struct net_device *netdev)
if (ret)
return ret;

- /* TODO */
+ /* TODO Load constant adjustments, patches */
+
+ /* TODO Frequency time drift */
+
+ /* TODO Configure lora multi demods, bitfield of active */
+
+ /* TODO Load concenrator multi channel frequencies */
+
+ /* TODO enale to correlator on enabled frequenies */
+
+ /* TODO PPMi, and modem enable */

ret = sx1301_load_all_firmware(priv);
if (ret)
return ret;

+ usleep_range(1000, 2000);
+
+ ret = regmap_read(priv->regmap, SX1301_AGCSTS, &val);
+ if (ret) {
+ dev_err(priv->dev, "AGC status read failed\n");
+ return ret;
+ }
+ if (val != 0x10) {
+ dev_err(priv->dev, "AGC firmware init failure: 0x%02X\n", val);
+ return -ENXIO;
+ }
+
+ ret = sx1301_load_tx_gain_lut(priv);
+ if (ret)
+ return ret;
+
+ /* Load Tx freq MSBs
+ * Always 3 if f > 768 for SX1257 or f > 384 for SX1255
+ */
+ ret = sx1301_agc_transaction(priv, 3, &val);
+ if (ret) {
+ dev_err(priv->dev, "AGC Tx MSBs load failed\n");
+ return ret;
+ }
+ if (val != 0x33) {
+ dev_err(priv->dev, "AGC firmware Tx MSBs error: 0x%02X\n", val);
+ return -ENXIO;
+ }
+
+ /* Load chan_select firmware option */
+ ret = sx1301_agc_transaction(priv, 0, &val);
+ if (ret) {
+ dev_err(priv->dev, "AGC chan select failed\n");
+ return ret;
+ }
+ if (val != 0x30) {
+ dev_err(priv->dev,
+ "AGC firmware chan select error: 0x%02X", val);
+ return -ENXIO;
+ }
+
+ /* End AGC firmware init and check status */
+ ret = sx1301_agc_transaction(priv, 0, &val);
+ if (ret) {
+ dev_err(priv->dev, "AGC radio select failed\n");
+ return ret;
+ }
+ if (val != 0x40) {
+ dev_err(priv->dev, "AGC firmware init error: 0x%02X", val);
+ return -ENXIO;
+ }
+
ret = open_loradev(netdev);
if (ret)
return ret;
diff --git a/drivers/net/lora/sx1301.h b/drivers/net/lora/sx1301.h
index dd2b7da94fcc..04c9af64c181 100644
--- a/drivers/net/lora/sx1301.h
+++ b/drivers/net/lora/sx1301.h
@@ -20,6 +20,11 @@
#define SX1301_MCU_AGC_FW_VERSION 4
#define SX1301_MCU_AGC_CAL_FW_VERSION 2

+#define SX1301_AGC_CMD_WAIT 16
+#define SX1301_AGC_CMD_ABORT 17
+
+#define SX1301_TX_GAIN_LUT_MAX 16
+
/* Page independent */
#define SX1301_PAGE 0x00
#define SX1301_VER 0x01
@@ -105,6 +110,14 @@ static const struct reg_field sx1301_regmap_fields[] = {
REG_FIELD(SX1301_EMERGENCY_FORCE_HOST_CTRL, 0, 0),
};

+struct sx1301_tx_gain_lut {
+ unsigned int dig_gain;
+ unsigned int pa_gain;
+ unsigned int dac_gain;
+ unsigned int mix_gain;
+ int rf_power; /* dBm measured at board connector */
+};
+
struct sx1301_priv {
struct lora_dev_priv lora;
struct device *dev;
@@ -112,6 +125,9 @@ struct sx1301_priv {
struct gpio_desc *rst_gpio;
struct regmap *regmap;
struct regmap_field *regmap_fields[ARRAY_SIZE(sx1301_regmap_fields)];
+
+ struct sx1301_tx_gain_lut tx_gain_lut[SX1301_TX_GAIN_LUT_MAX];
+ u8 tx_gain_lut_size;
};

int __init sx130x_radio_init(void);
--
2.17.1