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