struct at86rf230_local; /* at86rf2xx chip depend data. * All timings are in us.
*/ struct at86rf2xx_chip_data {
u16 t_sleep_cycle;
u16 t_channel_switch;
u16 t_reset_to_off;
u16 t_off_to_aack;
u16 t_off_to_tx_on;
u16 t_off_to_sleep;
u16 t_sleep_to_off;
u16 t_frame;
u16 t_p_ack; int rssi_base_val;
int (*set_channel)(struct at86rf230_local *, u8, u8); int (*set_txpower)(struct at86rf230_local *, s32);
};
#define AT86RF2XX_MAX_BUF (127 + 3) /* tx retries to access the TX_ON state * if it's above then force change will be started. * * 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; int irq;
staticbool
at86rf230_reg_writeable(struct device *dev, unsignedint reg)
{ switch (reg) { case RG_TRX_STATE: case RG_TRX_CTRL_0: case RG_TRX_CTRL_1: case RG_PHY_TX_PWR: case RG_PHY_ED_LEVEL: case RG_PHY_CC_CCA: case RG_CCA_THRES: case RG_RX_CTRL: case RG_SFD_VALUE: case RG_TRX_CTRL_2: case RG_ANT_DIV: case RG_IRQ_MASK: case RG_VREG_CTRL: case RG_BATMON: case RG_XOSC_CTRL: case RG_RX_SYN: case RG_XAH_CTRL_1: case RG_FTN_CTRL: case RG_PLL_CF: case RG_PLL_DCU: case RG_SHORT_ADDR_0: case RG_SHORT_ADDR_1: case RG_PAN_ID_0: case RG_PAN_ID_1: case RG_IEEE_ADDR_0: case RG_IEEE_ADDR_1: case RG_IEEE_ADDR_2: case RG_IEEE_ADDR_3: case RG_IEEE_ADDR_4: case RG_IEEE_ADDR_5: case RG_IEEE_ADDR_6: case RG_IEEE_ADDR_7: case RG_XAH_CTRL_0: case RG_CSMA_SEED_0: case RG_CSMA_SEED_1: case RG_CSMA_BE: returntrue; default: returnfalse;
}
}
/* all writeable are also readable */
rc = at86rf230_reg_writeable(dev, reg); if (rc) return rc;
/* readonly regs */ switch (reg) { case RG_TRX_STATUS: case RG_PHY_RSSI: case RG_IRQ_STATUS: case RG_PART_NUM: case RG_VERSION_NUM: case RG_MAN_ID_1: case RG_MAN_ID_0: returntrue; default: returnfalse;
}
}
staticbool
at86rf230_reg_volatile(struct device *dev, unsignedint reg)
{ /* can be changed during runtime */ switch (reg) { case RG_TRX_STATUS: case RG_TRX_STATE: case RG_PHY_RSSI: case RG_PHY_ED_LEVEL: case RG_IRQ_STATUS: case RG_VREG_CTRL: case RG_PLL_CF: case RG_PLL_DCU: returntrue; default: returnfalse;
}
}
staticbool
at86rf230_reg_precious(struct device *dev, unsignedint reg)
{ /* don't clear irq line on read */ switch (reg) { case RG_IRQ_STATUS: returntrue; default: returnfalse;
}
}
/* Generic function to get some register value in async mode */ staticvoid
at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg, struct at86rf230_state_change *ctx, void (*complete)(void *context))
{ int rc;
/* Assert state change */ if (trx_state != ctx->to_state) { /* Special handling if transceiver state is in * STATE_BUSY_RX_AACK and a SHR was detected.
*/ if (trx_state == STATE_BUSY_RX_AACK) { /* Undocumented race condition. If we send a state * change to STATE_RX_AACK_ON the transceiver could * change his state automatically to STATE_BUSY_RX_AACK * if a SHR was detected. This is not an error, but we * can't assert this.
*/ if (ctx->to_state == STATE_RX_AACK_ON) goto done;
/* If we change to STATE_TX_ON without forcing and * transceiver state is STATE_BUSY_RX_AACK, we wait * 'tFrame + tPAck' receiving time. In this time the * PDU should be received. If the transceiver is still * in STATE_BUSY_RX_AACK, we run a force state change * to STATE_TX_ON. This is a timeout handling, if the * transceiver stucks in STATE_BUSY_RX_AACK. * * Additional we do several retries to try to get into * TX_ON state without forcing. If the retries are * higher or equal than AT86RF2XX_MAX_TX_RETRIES we * will do a force change.
*/ 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_TRX_OFF;
lp->tx_retry++;
/* The force state changes are will show as normal states in the * state status subregister. We change the to_state to the * corresponding one and remember if it was a force change, this * differs if we do a state change from STATE_BUSY_RX_AACK.
*/ switch (ctx->to_state) { case STATE_FORCE_TX_ON:
ctx->to_state = STATE_TX_ON;
force = true; break; case STATE_FORCE_TRX_OFF:
ctx->to_state = STATE_TRX_OFF;
force = true; break; default: break;
}
switch (ctx->from_state) { case STATE_TRX_OFF: switch (ctx->to_state) { case STATE_RX_AACK_ON:
tim = c->t_off_to_aack * NSEC_PER_USEC; /* state change from TRX_OFF to RX_AACK_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; case STATE_TX_ARET_ON: case STATE_TX_ON:
tim = c->t_off_to_tx_on * NSEC_PER_USEC; /* state change from TRX_OFF to TX_ON or ARET_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;
} 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 or TRX_OFF.
*/ if (!force) {
tim = (c->t_frame + c->t_p_ack) * NSEC_PER_USEC; goto change;
} break; default: break;
} break; /* Default value, means RESET state */ case STATE_P_ON: switch (ctx->to_state) { case STATE_TRX_OFF:
tim = c->t_reset_to_off * NSEC_PER_USEC; goto change; default: break;
} break; default: break;
}
/* Default delay is 1us in the most cases */
udelay(1);
at86rf230_async_state_timer(&ctx->timer); return;
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */ if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
udelay(1);
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
at86rf230_async_state_change_start); return;
}
/* Check if we already are in the state which we change in */ if (trx_state == ctx->to_state) { if (ctx->complete)
ctx->complete(context); return;
}
/* Set current state to the context of state change */
ctx->from_state = trx_state;
/* Going into the next step for a state change which do a timing * relevant delay.
*/
at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx,
at86rf230_async_state_delay);
}
/* This function do a sync framework above the async state change. * Some callbacks of the IEEE 802.15.4 driver interface need to be * handled synchronously.
*/ staticint
at86rf230_sync_state_change(struct at86rf230_local *lp, unsignedint state)
{ unsignedlong rc;
/* check if we change from off state */ if (lp->is_tx_from_off)
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
at86rf230_write_frame); else
at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
at86rf230_xmit_tx_on);
}
/* 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)) {
lp->is_tx_from_off = true;
at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
at86rf230_xmit_start);
} else {
lp->is_tx_from_off = false;
at86rf230_xmit_start(ctx);
}
/* It's recommended to set random new csma_seeds before sleep state. * Makes only sense in the stop callback, not doing this inside of * at86rf230_sleep, this is also used when we don't transmit afterwards * when calling start callback again.
*/
get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
at86rf230_write_subreg(lp, SR_CSMA_SEED_1, csma_seed[1]);
if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
u16 addr = le16_to_cpu(filt->short_addr);
dev_vdbg(&lp->spi->dev, "%s called for saddr\n", __func__);
__at86rf230_write(lp, RG_SHORT_ADDR_0, addr);
__at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
}
if (changed & IEEE802154_AFILT_PANID_CHANGED) {
u16 pan = le16_to_cpu(filt->pan_id);
dev_vdbg(&lp->spi->dev, "%s called for pan id\n", __func__);
__at86rf230_write(lp, RG_PAN_ID_0, pan);
__at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
}
if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
u8 i, addr[8];
memcpy(addr, &filt->ieee_addr, 8);
dev_vdbg(&lp->spi->dev, "%s called for IEEE addr\n", __func__); for (i = 0; i < 8; i++)
__at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
}
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
dev_vdbg(&lp->spi->dev, "%s called for panc change\n", __func__); if (filt->pan_coord)
at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1); else
at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 0);
}
for (i = 0; i < lp->hw->phy->supported.tx_powers_size; i++) { if (lp->hw->phy->supported.tx_powers[i] == mbm) return at86rf230_write_subreg(lp, SR_TX_PWR_23X, i);
}
for (i = 0; i < lp->hw->phy->supported.tx_powers_size; i++) { if (lp->hw->phy->supported.tx_powers[i] == mbm) return at86rf230_write_subreg(lp, SR_TX_PWR_212, i);
}
/* mapping 802.15.4 to driver spec */ switch (cca->mode) { case NL802154_CCA_ENERGY:
val = 1; break; case NL802154_CCA_CARRIER:
val = 2; break; case NL802154_CCA_ENERGY_CARRIER: switch (cca->opt) { case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
val = 3; break; case NL802154_CCA_OPT_ENERGY_CARRIER_OR:
val = 0; break; default: return -EINVAL;
} break; default: return -EINVAL;
}
for (i = 0; i < hw->phy->supported.cca_ed_levels_size; i++) { if (hw->phy->supported.cca_ed_levels[i] == mbm) return at86rf230_write_subreg(lp, SR_CCA_ED_THRES, i);
}
rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol); if (rc) return rc;
rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); if (rc) return rc;
rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, IRQ_TRX_END); if (rc) return rc;
/* reset values differs in at86rf231 and at86rf233 */
rc = at86rf230_write_subreg(lp, SR_IRQ_MASK_MODE, 0); if (rc) return rc;
get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]); if (rc) return rc;
rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_1, csma_seed[1]); if (rc) return rc;
/* CLKM changes are applied immediately */
rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); if (rc) return rc;
/* Turn CLKM Off */
rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); if (rc) return rc; /* Wait the next SLEEP cycle */
usleep_range(lp->data->t_sleep_cycle,
lp->data->t_sleep_cycle + 100);
/* xtal_trim value is calculated by: * CL = 0.5 * (CX + CTRIM + CPAR) * * whereas: * CL = capacitor of used crystal * CX = connected capacitors at xtal pins * CPAR = in all at86rf2xx datasheets this is a constant value 3 pF, * but this is different on each board setup. You need to fine * tuning this value via CTRIM. * CTRIM = variable capacitor setting. Resolution is 0.3 pF range is * 0 pF upto 4.5 pF. * * Examples: * atben transceiver: * * CL = 8 pF * CX = 12 pF * CPAR = 3 pF (We assume the magic constant from datasheet) * CTRIM = 0.9 pF * * (12+0.9+3)/2 = 7.95 which is nearly at 8 pF * * xtal_trim = 0x3 * * openlabs transceiver: * * CL = 16 pF * CX = 22 pF * CPAR = 3 pF (We assume the magic constant from datasheet) * CTRIM = 4.5 pF * * (22+4.5+3)/2 = 14.75 which is the nearest value to 16 pF * * xtal_trim = 0xf
*/
rc = at86rf230_write_subreg(lp, SR_XTAL_TRIM, xtal_trim); if (rc) return rc;
rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd); if (rc) return rc; if (!dvdd) {
dev_err(&lp->spi->dev, "DVDD error\n"); return -EINVAL;
}
/* Force setting slotted operation bit to 0. Sometimes the atben * sets this bit and I don't know why. We set this always force * to zero while probing.
*/ return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.