at86rf230: state delay over hrtimer and calibration
Subsystems (2)
Alexander Aring
maintainercover at86rf230: state delay over hrtimer and calibration
Hi,
this patch series contains for doing the state delay via hrtimer and introduce the transceiver calibration by doing a TRX_OFF -> TX_ON state before transmit and transceiver leaves 5 minutes in PLL state.
- Alex
Alexander Aring (3):
at86rf230: replace state change sleeps with hrtimer
at86rf230: add support for calibration timeout
at86rf230: fix volatile regmap registers
drivers/net/ieee802154/at86rf230.c | 113 +++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 24 deletions(-)
Signature · 1 line
2.3.0
Alexander Aring
maintainerPATCH 1/3 at86rf230: replace state change sleeps with hrtimer
This patch replace the state change timing relevant sleeps with hrtimers. Currently the sleeps are done in the complete handler of spi_async. The relation of doing the state change timing sleep with a timer will get the sleep functionality out of spi_async complete handler context.
--- drivers/net/ieee802154/at86rf230.c | 39 ++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-)
drivers/net/ieee802154/at86rf230.c +29 -10
@@ -19,6 +19,7 @@*/ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/hrtimer.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/gpio.h>
@@ -64,6 +65,7 @@ struct at86rf230_state_change {struct at86rf230_local *lp;int irq; +struct hrtimer timer;struct spi_message msg;struct spi_transfer trx;u8 buf[AT86RF2XX_MAX_BUF ];
@@ -548,6 +550,19 @@ done: ctx->complete (context); } +static enum hrtimer_restart at86rf230_async_state_timer (struct hrtimer *timer) +{ +struct at86rf230_state_change *ctx = + container_of(timer, struct at86rf230_state_change, timer); +struct at86rf230_local *lp = ctx->lp; + +at86rf230_async_read_reg (lp,RG_TRX_STATUS , ctx, + at86rf230_async_state_assert, + ctx->irq_enable); + +return HRTIMER_NORESTART ; +} +/* Do state change timing delay. */ static void at86rf230_async_state_delay (void *context)
@@ -556,6 +571,7 @@ at86rf230_async_state_delay(void *context)struct at86rf230_local *lp = ctx->lp;struct at86rf2xx_chip_data *c = lp->data;bool force = false; +ktime_t tim;/* The force state changes are will show as normal states in the * state status subregister. We change the to_state to the
@@ -579,11 +595,10 @@ at86rf230_async_state_delay(void *context)case STATE_TRX_OFF :switch (ctx->to_state) {case STATE_RX_AACK_ON : -usleep_range (c->t_off_to_aack, c->t_off_to_aack +10 ); + tim =ktime_set (0 , c->t_off_to_aack *NSEC_PER_USEC ); goto change;case STATE_TX_ON : -usleep_range (c->t_off_to_tx_on, - c->t_off_to_tx_on +10 ); + tim =ktime_set (0 , c->t_off_to_tx_on *NSEC_PER_USEC ); goto change;default :break ;
@@ -597,8 +612,8 @@ at86rf230_async_state_delay(void *context)* to TX_ON. */ if (!force) { -usleep_range (c->t_frame + c->t_p_ack, - c->t_frame + c->t_p_ack +1000 ); + tim =ktime_set (0 , (c->t_frame + c->t_p_ack) * +NSEC_PER_USEC ); goto change; }break ;
@@ -610,7 +625,7 @@ at86rf230_async_state_delay(void *context)case STATE_P_ON :switch (ctx->to_state) {case STATE_TRX_OFF : -usleep_range (c->t_reset_to_off, c->t_reset_to_off +10 ); + tim =ktime_set (0 , c->t_reset_to_off *NSEC_PER_USEC ); goto change;default :break ;
@@ -621,12 +636,10 @@ at86rf230_async_state_delay(void *context) }/* Default delay is 1us in the most cases */ -udelay (1 ); + tim =ktime_set (0 ,NSEC_PER_USEC ); change: -at86rf230_async_read_reg (lp,RG_TRX_STATUS , ctx, - at86rf230_async_state_assert, - ctx->irq_enable); +hrtimer_start (&ctx->timer, tim,HRTIMER_MODE_REL ); } static void
@@ -1546,6 +1559,8 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp) lp->state.trx.tx_buf = lp->state.buf; lp->state.trx.rx_buf = lp->state.buf;spi_message_add_tail (&lp->state.trx, &lp->state.msg); +hrtimer_init (&lp->state.timer,CLOCK_MONOTONIC ,HRTIMER_MODE_REL ); + lp->state.timer.function = at86rf230_async_state_timer; lp->irq.lp = lp; lp->irq.irq = lp->spi->irq;
@@ -1555,6 +1570,8 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp) lp->irq.trx.tx_buf = lp->irq.buf; lp->irq.trx.rx_buf = lp->irq.buf;spi_message_add_tail (&lp->irq.trx, &lp->irq.msg); +hrtimer_init (&lp->irq.timer,CLOCK_MONOTONIC ,HRTIMER_MODE_REL ); + lp->irq.timer.function = at86rf230_async_state_timer; lp->tx.lp = lp; lp->tx.irq = lp->spi->irq;
@@ -1564,6 +1581,8 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp) lp->tx.trx.tx_buf = lp->tx.buf; lp->tx.trx.rx_buf = lp->tx.buf;spi_message_add_tail (&lp->tx.trx, &lp->tx.msg); +hrtimer_init (&lp->tx.timer,CLOCK_MONOTONIC ,HRTIMER_MODE_REL ); + lp->tx.timer.function = at86rf230_async_state_timer; }static int at86rf230_probe (struct spi_device *spi)
Signature · 1 line
2.3.0
Alexander Aring
maintainerPATCH 2/3 at86rf230: add support for calibration timeout
In reply to Alexander AringThis patch adds a handling for calibration if we are 5 minutes in PLL state. I first tried to implement the calibration functionality in TX_ON state via register values CF_START and DCU_START, but this occurs a one second delay at each calibration time.
An another solution to start a calibration is to switch from TRX_OFF state into TX_ON, then a calibration is done automatically by transceiver. This method will be used in this patch, after each transmit of a frame we check with jiffies if the PLL is set 5 minutes without doing a TRX_OFF->(TX_ON || RX_AACK_ON) or channel switch. The worst case would be a transceiver in receiving mode only, but this is under normal operation very unlikely.
--- drivers/net/ieee802154/at86rf230.c | 72 ++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 14 deletions(-)
drivers/net/ieee802154/at86rf230.c +58 -14
@@ -20,6 +20,7 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/hrtimer.h> +#include <linux/jiffies.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/gpio.h>
@@ -60,6 +61,8 @@ struct at86rf2xx_chip_data {* We assume the max_frame_retries (7) value of 802.15.4 here. */ #define AT86RF2XX_MAX_TX_RETRIES 7 +/* We use the recommended 5 minutes timeout to recalibrate */ +#define AT86RF2XX_CAL_LOOP_TIMEOUT (5 * 60 * HZ)struct at86rf230_state_change {struct at86rf230_local *lp;
@@ -90,6 +93,7 @@ struct at86rf230_local {struct at86rf230_state_change irq;bool tx_aret; +unsigned long cal_timeout;s8 max_frame_retries;bool is_tx;/* spinlock for is_tx protection */
@@ -491,6 +495,14 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, } } +static inline u8 at86rf230_state_to_force (u8 state) +{ +if (state ==STATE_TX_ON ) +return STATE_FORCE_TX_ON ; +else +return STATE_FORCE_TRX_OFF ; +} +static void at86rf230_async_state_assert (void *context) {
@@ -527,11 +539,12 @@ at86rf230_async_state_assert(void *context)* higher or equal than AT86RF2XX_MAX_TX_RETRIES we * will do a force change. */ -if (ctx->to_state ==STATE_TX_ON ) { -u8 state =STATE_TX_ON ; +if (ctx->to_state ==STATE_TX_ON || + ctx->to_state ==STATE_TRX_OFF ) { +u8 state = ctx->to_state;if (lp->tx_retry >=AT86RF2XX_MAX_TX_RETRIES ) - state =STATE_FORCE_TX_ON ; + state =at86rf230_state_to_force (state); lp->tx_retry++; at86rf230_async_state_change(lp, ctx, state,
@@ -599,6 +612,11 @@ at86rf230_async_state_delay(void *context) goto change;case STATE_TX_ON : tim =ktime_set (0 , c->t_off_to_tx_on *NSEC_PER_USEC ); +/* state change from TRX_OFF to TX_ON to do a +* calibration, we need to reset the timeout for the +* next one. +*/ + lp->cal_timeout = jiffies +AT86RF2XX_CAL_LOOP_TIMEOUT ; goto change;default :break ;
@@ -606,10 +624,11 @@ at86rf230_async_state_delay(void *context)break ;case STATE_BUSY_RX_AACK :switch (ctx->to_state) { +case STATE_TRX_OFF :case STATE_TX_ON :/* Wait for worst case receiving time if we * didn't make a force change from BUSY_RX_AACK -* to TX_ON. +* to TX_ON or TRX_OFF. */ if (!force) { tim = ktime_set(0, (c->t_frame + c->t_p_ack) *
@@ -969,25 +988,45 @@ at86rf230_xmit_tx_on(void *context) at86rf230_write_frame, false); } -static int -at86rf230_xmit (struct ieee802154_hw *hw,struct sk_buff *skb) +static void +at86rf230_xmit_start (void *context) { -struct at86rf230_local *lp = hw->priv; -struct at86rf230_state_change *ctx = &lp->tx; - -void (*tx_complete)(void *context) = at86rf230_write_frame; - - lp->tx_skb = skb; +struct at86rf230_state_change *ctx = context; +struct at86rf230_local *lp = ctx->lp;/* In ARET mode we need to go into STATE_TX_ARET_ON after we * are in STATE_TX_ON. The pfad differs here, so we change * the complete handler. */ if (lp->tx_aret) - tx_complete = at86rf230_xmit_tx_on; +at86rf230_async_state_change (lp, ctx,STATE_TX_ON , + at86rf230_xmit_tx_on, false); +else +at86rf230_async_state_change (lp, ctx,STATE_TX_ON , + at86rf230_write_frame, false); +} + +static int +at86rf230_xmit (struct ieee802154_hw *hw,struct sk_buff *skb) +{ +struct at86rf230_local *lp = hw->priv; +struct at86rf230_state_change *ctx = &lp->tx; + lp->tx_skb = skb; lp->tx_retry =0 ; -at86rf230_async_state_change (lp, ctx,STATE_TX_ON , tx_complete, false); + +/* After 5 minutes in PLL and the same frequency we run again the +* calibration loops which is recommended by at86rf2xx datasheets. +* +* The calibration is initiate by a state change from TRX_OFF +* to TX_ON, the lp->cal_timeout should be reinit by state_delay +* function then to start in the next 5 minutes. +*/ +if (time_is_before_jiffies (lp->cal_timeout)) +at86rf230_async_state_change (lp, ctx,STATE_TRX_OFF , + at86rf230_xmit_start, false); +else +at86rf230_xmit_start (ctx);return 0 ; }
@@ -1003,6 +1042,9 @@ at86rf230_ed(struct ieee802154_hw *hw, u8 *level)static int at86rf230_start (struct ieee802154_hw *hw) { +struct at86rf230_local *lp = hw->priv; + + lp->cal_timeout = jiffies +AT86RF2XX_CAL_LOOP_TIMEOUT ;return at86rf230_sync_state_change (hw->priv,STATE_RX_AACK_ON ); }
@@ -1083,6 +1125,8 @@ at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel)/* Wait for PLL */ usleep_range (lp->data->t_channel_switch, lp->data->t_channel_switch +10 ); + + lp->cal_timeout = jiffies +AT86RF2XX_CAL_LOOP_TIMEOUT ;return rc; }
Signature · 1 line
2.3.0
Alexander Aring
maintainerPATCH 3/3 at86rf230: fix volatile regmap registers
In reply to Alexander AringThese registers are also changed by transceiver and should be volatile for right accessing via regmap debugfs.
--- drivers/net/ieee802154/at86rf230.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/ieee802154/at86rf230.c +2 -0
@@ -421,6 +421,8 @@ at86rf230_reg_volatile(struct device *dev, unsigned int reg)case RG_PHY_ED_LEVEL :case RG_IRQ_STATUS :case RG_VREG_CTRL : +case RG_PLL_CF : +case RG_PLL_DCU :return true;default :return false;
Signature · 1 line
2.3.0
Hi Alex,
Quoted 6 lines
this patch series contains for doing the state delay via hrtimer and introduce the transceiver calibration by doing a
TRX_OFF->TX_ONstate before transmit and transceiver leaves 5 minutes in PLL state.- Alex
Quoted 7 lines
Alexander Aring (3):
at86rf230: replace state change sleeps with hrtimer
at86rf230: add support for calibration timeout
at86rf230: fix volatile regmap registers
drivers/net/ieee802154/at86rf230.c| 113 +++++++++++++++++++++++++++++--------1 file changed, 89 insertions(+), 24 deletions(-)
all 3 patches have been applied to bluetooth-next tree.
Regards
Marcel