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.whit...@lairdtech.com>
---
 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

Reply via email to