--- zzzz-none-000/linux-3.10.107/drivers/net/ieee802154/at86rf230.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/net/ieee802154/at86rf230.c 2021-02-04 17:41:59.000000000 +0000 @@ -12,653 +12,1130 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Dmitry Eremin-Solenikov * Alexander Smirnov + * Alexander Aring */ #include #include +#include +#include #include +#include #include #include -#include -#include -#include #include #include +#include #include +#include +#include +#include #include -#include +#include -struct at86rf230_local { - struct spi_device *spi; - int rstn, slp_tr, dig2; +#include "at86rf230.h" + +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; + + struct hrtimer timer; + struct spi_message msg; + struct spi_transfer trx; + u8 buf[AT86RF2XX_MAX_BUF]; + + void (*complete)(void *context); + u8 from_state; + u8 to_state; + + bool free; +}; - u8 part; - u8 vers; +struct at86rf230_trac { + u64 success; + u64 success_data_pending; + u64 success_wait_for_ack; + u64 channel_access_failure; + u64 no_ack; + u64 invalid; +}; - u8 buf[2]; - struct mutex bmux; +struct at86rf230_local { + struct spi_device *spi; - struct work_struct irqwork; - struct completion tx_complete; + struct ieee802154_hw *hw; + struct at86rf2xx_chip_data *data; + struct regmap *regmap; + int slp_tr; + bool sleep; - struct ieee802154_dev *dev; + struct completion state_complete; + struct at86rf230_state_change state; - spinlock_t lock; - bool irq_busy; + unsigned long cal_timeout; bool is_tx; + bool is_tx_from_off; + u8 tx_retry; + struct sk_buff *tx_skb; + struct at86rf230_state_change tx; + + struct at86rf230_trac trac; }; -#define RG_TRX_STATUS (0x01) -#define SR_TRX_STATUS 0x01, 0x1f, 0 -#define SR_RESERVED_01_3 0x01, 0x20, 5 -#define SR_CCA_STATUS 0x01, 0x40, 6 -#define SR_CCA_DONE 0x01, 0x80, 7 -#define RG_TRX_STATE (0x02) -#define SR_TRX_CMD 0x02, 0x1f, 0 -#define SR_TRAC_STATUS 0x02, 0xe0, 5 -#define RG_TRX_CTRL_0 (0x03) -#define SR_CLKM_CTRL 0x03, 0x07, 0 -#define SR_CLKM_SHA_SEL 0x03, 0x08, 3 -#define SR_PAD_IO_CLKM 0x03, 0x30, 4 -#define SR_PAD_IO 0x03, 0xc0, 6 -#define RG_TRX_CTRL_1 (0x04) -#define SR_IRQ_POLARITY 0x04, 0x01, 0 -#define SR_IRQ_MASK_MODE 0x04, 0x02, 1 -#define SR_SPI_CMD_MODE 0x04, 0x0c, 2 -#define SR_RX_BL_CTRL 0x04, 0x10, 4 -#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5 -#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6 -#define SR_PA_EXT_EN 0x04, 0x80, 7 -#define RG_PHY_TX_PWR (0x05) -#define SR_TX_PWR 0x05, 0x0f, 0 -#define SR_PA_LT 0x05, 0x30, 4 -#define SR_PA_BUF_LT 0x05, 0xc0, 6 -#define RG_PHY_RSSI (0x06) -#define SR_RSSI 0x06, 0x1f, 0 -#define SR_RND_VALUE 0x06, 0x60, 5 -#define SR_RX_CRC_VALID 0x06, 0x80, 7 -#define RG_PHY_ED_LEVEL (0x07) -#define SR_ED_LEVEL 0x07, 0xff, 0 -#define RG_PHY_CC_CCA (0x08) -#define SR_CHANNEL 0x08, 0x1f, 0 -#define SR_CCA_MODE 0x08, 0x60, 5 -#define SR_CCA_REQUEST 0x08, 0x80, 7 -#define RG_CCA_THRES (0x09) -#define SR_CCA_ED_THRES 0x09, 0x0f, 0 -#define SR_RESERVED_09_1 0x09, 0xf0, 4 -#define RG_RX_CTRL (0x0a) -#define SR_PDT_THRES 0x0a, 0x0f, 0 -#define SR_RESERVED_0a_1 0x0a, 0xf0, 4 -#define RG_SFD_VALUE (0x0b) -#define SR_SFD_VALUE 0x0b, 0xff, 0 -#define RG_TRX_CTRL_2 (0x0c) -#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0 -#define SR_RESERVED_0c_2 0x0c, 0x7c, 2 -#define SR_RX_SAFE_MODE 0x0c, 0x80, 7 -#define RG_ANT_DIV (0x0d) -#define SR_ANT_CTRL 0x0d, 0x03, 0 -#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2 -#define SR_ANT_DIV_EN 0x0d, 0x08, 3 -#define SR_RESERVED_0d_2 0x0d, 0x70, 4 -#define SR_ANT_SEL 0x0d, 0x80, 7 -#define RG_IRQ_MASK (0x0e) -#define SR_IRQ_MASK 0x0e, 0xff, 0 -#define RG_IRQ_STATUS (0x0f) -#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0 -#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1 -#define SR_IRQ_2_RX_START 0x0f, 0x04, 2 -#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3 -#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4 -#define SR_IRQ_5_AMI 0x0f, 0x20, 5 -#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6 -#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7 -#define RG_VREG_CTRL (0x10) -#define SR_RESERVED_10_6 0x10, 0x03, 0 -#define SR_DVDD_OK 0x10, 0x04, 2 -#define SR_DVREG_EXT 0x10, 0x08, 3 -#define SR_RESERVED_10_3 0x10, 0x30, 4 -#define SR_AVDD_OK 0x10, 0x40, 6 -#define SR_AVREG_EXT 0x10, 0x80, 7 -#define RG_BATMON (0x11) -#define SR_BATMON_VTH 0x11, 0x0f, 0 -#define SR_BATMON_HR 0x11, 0x10, 4 -#define SR_BATMON_OK 0x11, 0x20, 5 -#define SR_RESERVED_11_1 0x11, 0xc0, 6 -#define RG_XOSC_CTRL (0x12) -#define SR_XTAL_TRIM 0x12, 0x0f, 0 -#define SR_XTAL_MODE 0x12, 0xf0, 4 -#define RG_RX_SYN (0x15) -#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0 -#define SR_RESERVED_15_2 0x15, 0x70, 4 -#define SR_RX_PDT_DIS 0x15, 0x80, 7 -#define RG_XAH_CTRL_1 (0x17) -#define SR_RESERVED_17_8 0x17, 0x01, 0 -#define SR_AACK_PROM_MODE 0x17, 0x02, 1 -#define SR_AACK_ACK_TIME 0x17, 0x04, 2 -#define SR_RESERVED_17_5 0x17, 0x08, 3 -#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4 -#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5 -#define SR_RESERVED_17_2 0x17, 0x40, 6 -#define SR_RESERVED_17_1 0x17, 0x80, 7 -#define RG_FTN_CTRL (0x18) -#define SR_RESERVED_18_2 0x18, 0x7f, 0 -#define SR_FTN_START 0x18, 0x80, 7 -#define RG_PLL_CF (0x1a) -#define SR_RESERVED_1a_2 0x1a, 0x7f, 0 -#define SR_PLL_CF_START 0x1a, 0x80, 7 -#define RG_PLL_DCU (0x1b) -#define SR_RESERVED_1b_3 0x1b, 0x3f, 0 -#define SR_RESERVED_1b_2 0x1b, 0x40, 6 -#define SR_PLL_DCU_START 0x1b, 0x80, 7 -#define RG_PART_NUM (0x1c) -#define SR_PART_NUM 0x1c, 0xff, 0 -#define RG_VERSION_NUM (0x1d) -#define SR_VERSION_NUM 0x1d, 0xff, 0 -#define RG_MAN_ID_0 (0x1e) -#define SR_MAN_ID_0 0x1e, 0xff, 0 -#define RG_MAN_ID_1 (0x1f) -#define SR_MAN_ID_1 0x1f, 0xff, 0 -#define RG_SHORT_ADDR_0 (0x20) -#define SR_SHORT_ADDR_0 0x20, 0xff, 0 -#define RG_SHORT_ADDR_1 (0x21) -#define SR_SHORT_ADDR_1 0x21, 0xff, 0 -#define RG_PAN_ID_0 (0x22) -#define SR_PAN_ID_0 0x22, 0xff, 0 -#define RG_PAN_ID_1 (0x23) -#define SR_PAN_ID_1 0x23, 0xff, 0 -#define RG_IEEE_ADDR_0 (0x24) -#define SR_IEEE_ADDR_0 0x24, 0xff, 0 -#define RG_IEEE_ADDR_1 (0x25) -#define SR_IEEE_ADDR_1 0x25, 0xff, 0 -#define RG_IEEE_ADDR_2 (0x26) -#define SR_IEEE_ADDR_2 0x26, 0xff, 0 -#define RG_IEEE_ADDR_3 (0x27) -#define SR_IEEE_ADDR_3 0x27, 0xff, 0 -#define RG_IEEE_ADDR_4 (0x28) -#define SR_IEEE_ADDR_4 0x28, 0xff, 0 -#define RG_IEEE_ADDR_5 (0x29) -#define SR_IEEE_ADDR_5 0x29, 0xff, 0 -#define RG_IEEE_ADDR_6 (0x2a) -#define SR_IEEE_ADDR_6 0x2a, 0xff, 0 -#define RG_IEEE_ADDR_7 (0x2b) -#define SR_IEEE_ADDR_7 0x2b, 0xff, 0 -#define RG_XAH_CTRL_0 (0x2c) -#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0 -#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1 -#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4 -#define RG_CSMA_SEED_0 (0x2d) -#define SR_CSMA_SEED_0 0x2d, 0xff, 0 -#define RG_CSMA_SEED_1 (0x2e) -#define SR_CSMA_SEED_1 0x2e, 0x07, 0 -#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3 -#define SR_AACK_DIS_ACK 0x2e, 0x10, 4 -#define SR_AACK_SET_PD 0x2e, 0x20, 5 -#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6 -#define RG_CSMA_BE (0x2f) -#define SR_MIN_BE 0x2f, 0x0f, 0 -#define SR_MAX_BE 0x2f, 0xf0, 4 - -#define CMD_REG 0x80 -#define CMD_REG_MASK 0x3f -#define CMD_WRITE 0x40 -#define CMD_FB 0x20 - -#define IRQ_BAT_LOW (1 << 7) -#define IRQ_TRX_UR (1 << 6) -#define IRQ_AMI (1 << 5) -#define IRQ_CCA_ED (1 << 4) -#define IRQ_TRX_END (1 << 3) -#define IRQ_RX_START (1 << 2) -#define IRQ_PLL_UNL (1 << 1) -#define IRQ_PLL_LOCK (1 << 0) - -#define IRQ_ACTIVE_HIGH 0 -#define IRQ_ACTIVE_LOW 1 - -#define STATE_P_ON 0x00 /* BUSY */ -#define STATE_BUSY_RX 0x01 -#define STATE_BUSY_TX 0x02 -#define STATE_FORCE_TRX_OFF 0x03 -#define STATE_FORCE_TX_ON 0x04 /* IDLE */ -/* 0x05 */ /* INVALID_PARAMETER */ -#define STATE_RX_ON 0x06 -/* 0x07 */ /* SUCCESS */ -#define STATE_TRX_OFF 0x08 -#define STATE_TX_ON 0x09 -/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */ -#define STATE_SLEEP 0x0F -#define STATE_BUSY_RX_AACK 0x11 -#define STATE_BUSY_TX_ARET 0x12 -#define STATE_RX_AACK_ON 0x16 -#define STATE_TX_ARET_ON 0x19 -#define STATE_RX_ON_NOCLK 0x1C -#define STATE_RX_AACK_ON_NOCLK 0x1D -#define STATE_BUSY_RX_AACK_NOCLK 0x1E -#define STATE_TRANSITION_IN_PROGRESS 0x1F +#define AT86RF2XX_NUMREGS 0x3F -static int -__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data) +static void +at86rf230_async_state_change(struct at86rf230_local *lp, + struct at86rf230_state_change *ctx, + const u8 state, void (*complete)(void *context)); + +static inline void +at86rf230_sleep(struct at86rf230_local *lp) { - u8 *buf = lp->buf; - int status; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = buf, - }; + if (gpio_is_valid(lp->slp_tr)) { + gpio_set_value(lp->slp_tr, 1); + usleep_range(lp->data->t_off_to_sleep, + lp->data->t_off_to_sleep + 10); + lp->sleep = true; + } +} + +static inline void +at86rf230_awake(struct at86rf230_local *lp) +{ + if (gpio_is_valid(lp->slp_tr)) { + gpio_set_value(lp->slp_tr, 0); + usleep_range(lp->data->t_sleep_to_off, + lp->data->t_sleep_to_off + 100); + lp->sleep = false; + } +} - buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE; - buf[1] = data; - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); +static inline int +__at86rf230_write(struct at86rf230_local *lp, + unsigned int addr, unsigned int data) +{ + bool sleep = lp->sleep; + int ret; - status = spi_sync(lp->spi, &msg); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - if (msg.status) - status = msg.status; + /* awake for register setting if sleep */ + if (sleep) + at86rf230_awake(lp); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + ret = regmap_write(lp->regmap, addr, data); - return status; + /* sleep again if was sleeping */ + if (sleep) + at86rf230_sleep(lp); + + return ret; } -static int -__at86rf230_read_subreg(struct at86rf230_local *lp, - u8 addr, u8 mask, int shift, u8 *data) +static inline int +__at86rf230_read(struct at86rf230_local *lp, + unsigned int addr, unsigned int *data) { - u8 *buf = lp->buf; - int status; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = buf, - .rx_buf = buf, - }; - - buf[0] = (addr & CMD_REG_MASK) | CMD_REG; - buf[1] = 0xff; - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - status = spi_sync(lp->spi, &msg); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - if (msg.status) - status = msg.status; - - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + bool sleep = lp->sleep; + int ret; + + /* awake for register setting if sleep */ + if (sleep) + at86rf230_awake(lp); + + ret = regmap_read(lp->regmap, addr, data); - if (status == 0) - *data = buf[1]; + /* sleep again if was sleeping */ + if (sleep) + at86rf230_sleep(lp); - return status; + return ret; } -static int +static inline int at86rf230_read_subreg(struct at86rf230_local *lp, - u8 addr, u8 mask, int shift, u8 *data) + unsigned int addr, unsigned int mask, + unsigned int shift, unsigned int *data) { - int status; + int rc; - mutex_lock(&lp->bmux); - status = __at86rf230_read_subreg(lp, addr, mask, shift, data); - mutex_unlock(&lp->bmux); + rc = __at86rf230_read(lp, addr, data); + if (!rc) + *data = (*data & mask) >> shift; - return status; + return rc; } -static int +static inline int at86rf230_write_subreg(struct at86rf230_local *lp, - u8 addr, u8 mask, int shift, u8 data) + unsigned int addr, unsigned int mask, + unsigned int shift, unsigned int data) { - int status; - u8 val; + bool sleep = lp->sleep; + int ret; - mutex_lock(&lp->bmux); - status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val); - if (status) - goto out; + /* awake for register setting if sleep */ + if (sleep) + at86rf230_awake(lp); + + ret = regmap_update_bits(lp->regmap, addr, mask, data << shift); + + /* sleep again if was sleeping */ + if (sleep) + at86rf230_sleep(lp); + + return ret; +} + +static inline void +at86rf230_slp_tr_rising_edge(struct at86rf230_local *lp) +{ + gpio_set_value(lp->slp_tr, 1); + udelay(1); + gpio_set_value(lp->slp_tr, 0); +} + +static bool +at86rf230_reg_writeable(struct device *dev, unsigned int 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: + return true; + default: + return false; + } +} - val &= ~mask; - val |= (data << shift) & mask; +static bool +at86rf230_reg_readable(struct device *dev, unsigned int reg) +{ + bool rc; - status = __at86rf230_write(lp, addr, val); -out: - mutex_unlock(&lp->bmux); + /* all writeable are also readable */ + rc = at86rf230_reg_writeable(dev, reg); + if (rc) + return rc; - return status; + /* 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: + return true; + default: + return false; + } } -static int -at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len) +static bool +at86rf230_reg_volatile(struct device *dev, unsigned int reg) { - u8 *buf = lp->buf; - int status; - struct spi_message msg; - struct spi_transfer xfer_head = { - .len = 2, - .tx_buf = buf, + /* 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: + return true; + default: + return false; + } +} - }; - struct spi_transfer xfer_buf = { - .len = len, - .tx_buf = data, - }; +static bool +at86rf230_reg_precious(struct device *dev, unsigned int reg) +{ + /* don't clear irq line on read */ + switch (reg) { + case RG_IRQ_STATUS: + return true; + default: + return false; + } +} - mutex_lock(&lp->bmux); - buf[0] = CMD_WRITE | CMD_FB; - buf[1] = len + 2; /* 2 bytes for CRC that isn't written */ +static const struct regmap_config at86rf230_regmap_spi_config = { + .reg_bits = 8, + .val_bits = 8, + .write_flag_mask = CMD_REG | CMD_WRITE, + .read_flag_mask = CMD_REG, + .cache_type = REGCACHE_RBTREE, + .max_register = AT86RF2XX_NUMREGS, + .writeable_reg = at86rf230_reg_writeable, + .readable_reg = at86rf230_reg_readable, + .volatile_reg = at86rf230_reg_volatile, + .precious_reg = at86rf230_reg_precious, +}; - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); +static void +at86rf230_async_error_recover(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - spi_message_init(&msg); - spi_message_add_tail(&xfer_head, &msg); - spi_message_add_tail(&xfer_buf, &msg); + lp->is_tx = 0; + at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL); + ieee802154_wake_queue(lp->hw); + if (ctx->free) + kfree(ctx); +} - status = spi_sync(lp->spi, &msg); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - if (msg.status) - status = msg.status; +static inline void +at86rf230_async_error(struct at86rf230_local *lp, + struct at86rf230_state_change *ctx, int rc) +{ + dev_err(&lp->spi->dev, "spi_async error %d\n", rc); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, + at86rf230_async_error_recover); +} + +/* Generic function to get some register value in async mode */ +static void +at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg, + struct at86rf230_state_change *ctx, + void (*complete)(void *context)) +{ + int rc; - mutex_unlock(&lp->bmux); - return status; + u8 *tx_buf = ctx->buf; + + tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG; + ctx->msg.complete = complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) + at86rf230_async_error(lp, ctx, rc); } -static int -at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi) +static void +at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val, + struct at86rf230_state_change *ctx, + void (*complete)(void *context)) { - u8 *buf = lp->buf; - int status; - struct spi_message msg; - struct spi_transfer xfer_head = { - .len = 2, - .tx_buf = buf, - .rx_buf = buf, - }; - struct spi_transfer xfer_head1 = { - .len = 2, - .tx_buf = buf, - .rx_buf = buf, - }; - struct spi_transfer xfer_buf = { - .len = 0, - .rx_buf = data, - }; + int rc; - mutex_lock(&lp->bmux); + ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE; + ctx->buf[1] = val; + ctx->msg.complete = complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) + at86rf230_async_error(lp, ctx, rc); +} - buf[0] = CMD_FB; - buf[1] = 0x00; +static void +at86rf230_async_state_assert(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + const u8 *buf = ctx->buf; + const u8 trx_state = buf[1] & TRX_STATE_MASK; + + /* 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++; + + at86rf230_async_state_change(lp, ctx, state, + ctx->complete); + return; + } + } - spi_message_init(&msg); - spi_message_add_tail(&xfer_head, &msg); + dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n", + ctx->from_state, ctx->to_state, trx_state); + } - status = spi_sync(lp->spi, &msg); - dev_vdbg(&lp->spi->dev, "status = %d\n", status); +done: + if (ctx->complete) + ctx->complete(context); +} - xfer_buf.len = *(buf + 1) + 1; - *len = buf[1]; +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; - buf[0] = CMD_FB; - buf[1] = 0x00; + at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, + at86rf230_async_state_assert); + + return HRTIMER_NORESTART; +} + +/* Do state change timing delay. */ +static void +at86rf230_async_state_delay(void *context) +{ + struct at86rf230_state_change *ctx = 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 + * 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; + } - spi_message_init(&msg); - spi_message_add_tail(&xfer_head1, &msg); - spi_message_add_tail(&xfer_buf, &msg); + switch (ctx->from_state) { + case STATE_TRX_OFF: + switch (ctx->to_state) { + case STATE_RX_AACK_ON: + tim = ktime_set(0, 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 = ktime_set(0, 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 = ktime_set(0, (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 = ktime_set(0, c->t_reset_to_off * NSEC_PER_USEC); + goto change; + default: + break; + } + break; + default: + break; + } - status = spi_sync(lp->spi, &msg); + /* Default delay is 1us in the most cases */ + udelay(1); + at86rf230_async_state_timer(&ctx->timer); + return; - if (msg.status) - status = msg.status; +change: + hrtimer_start(&ctx->timer, tim, HRTIMER_MODE_REL); +} - dev_vdbg(&lp->spi->dev, "status = %d\n", status); - dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); - dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); +static void +at86rf230_async_state_change_start(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + u8 *buf = ctx->buf; + const u8 trx_state = buf[1] & TRX_STATE_MASK; + + /* 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; + } - if (status) { - if (lqi && (*len > lp->buf[1])) - *lqi = data[lp->buf[1]]; + /* 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; } - mutex_unlock(&lp->bmux); - return status; + /* 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); +} + +static void +at86rf230_async_state_change(struct at86rf230_local *lp, + struct at86rf230_state_change *ctx, + const u8 state, void (*complete)(void *context)) +{ + /* Initialization for the state change context */ + ctx->to_state = state; + ctx->complete = complete; + at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, + at86rf230_async_state_change_start); } +static void +at86rf230_sync_state_change_complete(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + + complete(&lp->state_complete); +} + +/* 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. + */ static int -at86rf230_ed(struct ieee802154_dev *dev, u8 *level) +at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state) { - might_sleep(); - BUG_ON(!level); - *level = 0xbe; + unsigned long rc; + + at86rf230_async_state_change(lp, &lp->state, state, + at86rf230_sync_state_change_complete); + + rc = wait_for_completion_timeout(&lp->state_complete, + msecs_to_jiffies(100)); + if (!rc) { + at86rf230_async_error(lp, &lp->state, -ETIMEDOUT); + return -ETIMEDOUT; + } + return 0; } -static int -at86rf230_state(struct ieee802154_dev *dev, int state) +static void +at86rf230_tx_complete(void *context) { - struct at86rf230_local *lp = dev->priv; - int rc; - u8 val; - u8 desired_status; + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - might_sleep(); + ieee802154_xmit_complete(lp->hw, lp->tx_skb, false); + kfree(ctx); +} - if (state == STATE_FORCE_TX_ON) - desired_status = STATE_TX_ON; - else if (state == STATE_FORCE_TRX_OFF) - desired_status = STATE_TRX_OFF; - else - desired_status = state; +static void +at86rf230_tx_on(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - do { - rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); - if (rc) - goto err; - } while (val == STATE_TRANSITION_IN_PROGRESS); + at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, + at86rf230_tx_complete); +} - if (val == desired_status) - return 0; +static void +at86rf230_tx_trac_check(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - /* state is equal to phy states */ - rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state); - if (rc) - goto err; + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(ctx->buf[1]); - do { - rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); - if (rc) - goto err; - } while (val == STATE_TRANSITION_IN_PROGRESS); + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_DATA_PENDING: + lp->trac.success_data_pending++; + break; + case TRAC_CHANNEL_ACCESS_FAILURE: + lp->trac.channel_access_failure++; + break; + case TRAC_NO_ACK: + lp->trac.no_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received tx trac status %d\n", trac); + break; + } + } + at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on); +} - if (val == desired_status) - return 0; +static void +at86rf230_rx_read_frame_complete(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + const u8 *buf = ctx->buf; + struct sk_buff *skb; + u8 len, lqi; - pr_err("unexpected state change: %d, asked for %d\n", val, state); - return -EBUSY; + len = buf[1]; + if (!ieee802154_is_valid_psdu_len(len)) { + dev_vdbg(&lp->spi->dev, "corrupted frame received\n"); + len = IEEE802154_MTU; + } + lqi = buf[2 + len]; -err: - pr_err("error: %d\n", rc); - return rc; + skb = dev_alloc_skb(IEEE802154_MTU); + if (!skb) { + dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n"); + kfree(ctx); + return; + } + + memcpy(skb_put(skb, len), buf + 2, len); + ieee802154_rx_irqsafe(lp->hw, skb, lqi); + kfree(ctx); } -static int -at86rf230_start(struct ieee802154_dev *dev) +static void +at86rf230_rx_trac_check(void *context) { - struct at86rf230_local *lp = dev->priv; - u8 rc; + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + u8 *buf = ctx->buf; + int rc; - rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); - if (rc) - return rc; + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(buf[1]); + + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_WAIT_FOR_ACK: + lp->trac.success_wait_for_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received rx trac status %d\n", trac); + break; + } + } + + buf[0] = CMD_FB; + ctx->trx.len = AT86RF2XX_MAX_BUF; + ctx->msg.complete = at86rf230_rx_read_frame_complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) { + ctx->trx.len = 2; + at86rf230_async_error(lp, ctx, rc); + } +} - return at86rf230_state(dev, STATE_RX_ON); +static void +at86rf230_irq_trx_end(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + + if (lp->is_tx) { + lp->is_tx = 0; + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_tx_trac_check); + } else { + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_rx_trac_check); + } } static void -at86rf230_stop(struct ieee802154_dev *dev) +at86rf230_irq_status(void *context) { - at86rf230_state(dev, STATE_FORCE_TRX_OFF); + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + const u8 *buf = ctx->buf; + u8 irq = buf[1]; + + enable_irq(lp->spi->irq); + + if (irq & IRQ_TRX_END) { + at86rf230_irq_trx_end(ctx); + } else { + dev_err(&lp->spi->dev, "not supported irq %02x received\n", + irq); + kfree(ctx); + } } -static int -at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) +static void +at86rf230_setup_spi_messages(struct at86rf230_local *lp, + struct at86rf230_state_change *state) +{ + state->lp = lp; + state->irq = lp->spi->irq; + spi_message_init(&state->msg); + state->msg.context = state; + state->trx.len = 2; + state->trx.tx_buf = state->buf; + state->trx.rx_buf = state->buf; + spi_message_add_tail(&state->trx, &state->msg); + hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + state->timer.function = at86rf230_async_state_timer; +} + +static irqreturn_t at86rf230_isr(int irq, void *data) { - struct at86rf230_local *lp = dev->priv; + struct at86rf230_local *lp = data; + struct at86rf230_state_change *ctx; int rc; - might_sleep(); + disable_irq_nosync(irq); - if (page != 0 || channel < 11 || channel > 26) { - WARN_ON(1); - return -EINVAL; + ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) { + enable_irq(irq); + return IRQ_NONE; } - rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel); - msleep(1); /* Wait for PLL */ - dev->phy->current_channel = channel; + at86rf230_setup_spi_messages(lp, ctx); + /* tell on error handling to free ctx */ + ctx->free = true; + + ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; + ctx->msg.complete = at86rf230_irq_status; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) { + at86rf230_async_error(lp, ctx, rc); + enable_irq(irq); + return IRQ_NONE; + } - return 0; + return IRQ_HANDLED; } -static int -at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) +static void +at86rf230_write_frame_complete(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + + ctx->trx.len = 2; + + if (gpio_is_valid(lp->slp_tr)) + at86rf230_slp_tr_rising_edge(lp); + else + at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, + NULL); +} + +static void +at86rf230_write_frame(void *context) { - struct at86rf230_local *lp = dev->priv; + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; + struct sk_buff *skb = lp->tx_skb; + u8 *buf = ctx->buf; int rc; - unsigned long flags; - spin_lock(&lp->lock); - if (lp->irq_busy) { - spin_unlock(&lp->lock); - return -EBUSY; + lp->is_tx = 1; + + buf[0] = CMD_FB | CMD_WRITE; + buf[1] = skb->len + 2; + memcpy(buf + 2, skb->data, skb->len); + ctx->trx.len = skb->len + 2; + ctx->msg.complete = at86rf230_write_frame_complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) { + ctx->trx.len = 2; + at86rf230_async_error(lp, ctx, rc); } - spin_unlock(&lp->lock); +} - might_sleep(); +static void +at86rf230_xmit_tx_on(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - rc = at86rf230_state(dev, STATE_FORCE_TX_ON); - if (rc) - goto err; + at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, + at86rf230_write_frame); +} - spin_lock_irqsave(&lp->lock, flags); - lp->is_tx = 1; - INIT_COMPLETION(lp->tx_complete); - spin_unlock_irqrestore(&lp->lock, flags); +static void +at86rf230_xmit_start(void *context) +{ + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; - rc = at86rf230_write_fbuf(lp, skb->data, skb->len); - if (rc) - goto err_rx; + /* check if we change from off state */ + if (lp->is_tx_from_off) { + lp->is_tx_from_off = false; + 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); + } +} - rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX); - if (rc) - goto err_rx; +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; - rc = wait_for_completion_interruptible(&lp->tx_complete); - if (rc < 0) - goto err_rx; + lp->tx_skb = skb; + lp->tx_retry = 0; - rc = at86rf230_start(dev); + /* 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 { + at86rf230_xmit_start(ctx); + } - return rc; + return 0; +} -err_rx: - at86rf230_start(dev); -err: - pr_err("error: %d\n", rc); +static int +at86rf230_ed(struct ieee802154_hw *hw, u8 *level) +{ + BUG_ON(!level); + *level = 0xbe; + return 0; +} - spin_lock_irqsave(&lp->lock, flags); - lp->is_tx = 0; - spin_unlock_irqrestore(&lp->lock, flags); +static int +at86rf230_start(struct ieee802154_hw *hw) +{ + struct at86rf230_local *lp = hw->priv; - return rc; + /* reset trac stats on start */ + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) + memset(&lp->trac, 0, sizeof(struct at86rf230_trac)); + + at86rf230_awake(lp); + enable_irq(lp->spi->irq); + + return at86rf230_sync_state_change(lp, STATE_RX_AACK_ON); } -static int at86rf230_rx(struct at86rf230_local *lp) +static void +at86rf230_stop(struct ieee802154_hw *hw) { - u8 len = 128, lqi = 0; - struct sk_buff *skb; + struct at86rf230_local *lp = hw->priv; + u8 csma_seed[2]; - skb = alloc_skb(len, GFP_KERNEL); + at86rf230_sync_state_change(lp, STATE_FORCE_TRX_OFF); - if (!skb) - return -ENOMEM; + disable_irq(lp->spi->irq); - if (at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi)) - goto err; + /* 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 (len < 2) - goto err; + at86rf230_sleep(lp); +} - skb_trim(skb, len - 2); /* We do not put CRC into the frame */ +static int +at86rf23x_set_channel(struct at86rf230_local *lp, u8 page, u8 channel) +{ + return at86rf230_write_subreg(lp, SR_CHANNEL, channel); +} + +#define AT86RF2XX_MAX_ED_LEVELS 0xF +static const s32 at86rf23x_ed_levels[AT86RF2XX_MAX_ED_LEVELS + 1] = { + -9100, -8900, -8700, -8500, -8300, -8100, -7900, -7700, -7500, -7300, + -7100, -6900, -6700, -6500, -6300, -6100, +}; + +static const s32 at86rf212_ed_levels_100[AT86RF2XX_MAX_ED_LEVELS + 1] = { + -10000, -9800, -9600, -9400, -9200, -9000, -8800, -8600, -8400, -8200, + -8000, -7800, -7600, -7400, -7200, -7000, +}; - ieee802154_rx_irqsafe(lp->dev, skb, lqi); +static const s32 at86rf212_ed_levels_98[AT86RF2XX_MAX_ED_LEVELS + 1] = { + -9800, -9600, -9400, -9200, -9000, -8800, -8600, -8400, -8200, -8000, + -7800, -7600, -7400, -7200, -7000, -6800, +}; - dev_dbg(&lp->spi->dev, "READ_FBUF: %d %x\n", len, lqi); +static inline int +at86rf212_update_cca_ed_level(struct at86rf230_local *lp, int rssi_base_val) +{ + unsigned int cca_ed_thres; + int rc; + + rc = at86rf230_read_subreg(lp, SR_CCA_ED_THRES, &cca_ed_thres); + if (rc < 0) + return rc; + + switch (rssi_base_val) { + case -98: + lp->hw->phy->supported.cca_ed_levels = at86rf212_ed_levels_98; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf212_ed_levels_98); + lp->hw->phy->cca_ed_level = at86rf212_ed_levels_98[cca_ed_thres]; + break; + case -100: + lp->hw->phy->supported.cca_ed_levels = at86rf212_ed_levels_100; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf212_ed_levels_100); + lp->hw->phy->cca_ed_level = at86rf212_ed_levels_100[cca_ed_thres]; + break; + default: + WARN_ON(1); + } return 0; -err: - pr_debug("received frame is too small\n"); +} - kfree_skb(skb); - return -EINVAL; +static int +at86rf212_set_channel(struct at86rf230_local *lp, u8 page, u8 channel) +{ + int rc; + + if (channel == 0) + rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 0); + else + rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 1); + if (rc < 0) + return rc; + + if (page == 0) { + rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0); + lp->data->rssi_base_val = -100; + } else { + rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 1); + lp->data->rssi_base_val = -98; + } + if (rc < 0) + return rc; + + rc = at86rf212_update_cca_ed_level(lp, lp->data->rssi_base_val); + if (rc < 0) + return rc; + + /* This sets the symbol_duration according frequency on the 212. + * TODO move this handling while set channel and page in cfg802154. + * We can do that, this timings are according 802.15.4 standard. + * If we do that in cfg802154, this is a more generic calculation. + * + * This should also protected from ifs_timer. Means cancel timer and + * init with a new value. For now, this is okay. + */ + if (channel == 0) { + if (page == 0) { + /* SUB:0 and BPSK:0 -> BPSK-20 */ + lp->hw->phy->symbol_duration = 50; + } else { + /* SUB:1 and BPSK:0 -> BPSK-40 */ + lp->hw->phy->symbol_duration = 25; + } + } else { + if (page == 0) + /* SUB:0 and BPSK:1 -> OQPSK-100/200/400 */ + lp->hw->phy->symbol_duration = 40; + else + /* SUB:1 and BPSK:1 -> OQPSK-250/500/1000 */ + lp->hw->phy->symbol_duration = 16; + } + + lp->hw->phy->lifs_period = IEEE802154_LIFS_PERIOD * + lp->hw->phy->symbol_duration; + lp->hw->phy->sifs_period = IEEE802154_SIFS_PERIOD * + lp->hw->phy->symbol_duration; + + return at86rf230_write_subreg(lp, SR_CHANNEL, channel); +} + +static int +at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct at86rf230_local *lp = hw->priv; + int rc; + + rc = lp->data->set_channel(lp, page, 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; } static int -at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev, +at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, struct ieee802154_hw_addr_filt *filt, unsigned long changed) { - struct at86rf230_local *lp = dev->priv; + struct at86rf230_local *lp = hw->priv; + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + u16 addr = le16_to_cpu(filt->short_addr); - if (changed & IEEE802515_AFILT_SADDR_CHANGED) { dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for saddr\n"); - __at86rf230_write(lp, RG_SHORT_ADDR_0, filt->short_addr); - __at86rf230_write(lp, RG_SHORT_ADDR_1, filt->short_addr >> 8); + "at86rf230_set_hw_addr_filt called for saddr\n"); + __at86rf230_write(lp, RG_SHORT_ADDR_0, addr); + __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8); } - if (changed & IEEE802515_AFILT_PANID_CHANGED) { + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + u16 pan = le16_to_cpu(filt->pan_id); + dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for pan id\n"); - __at86rf230_write(lp, RG_PAN_ID_0, filt->pan_id); - __at86rf230_write(lp, RG_PAN_ID_1, filt->pan_id >> 8); + "at86rf230_set_hw_addr_filt called for pan id\n"); + __at86rf230_write(lp, RG_PAN_ID_0, pan); + __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8); } - if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + u8 i, addr[8]; + + memcpy(addr, &filt->ieee_addr, 8); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for IEEE addr\n"); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_0, filt->ieee_addr[7]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_1, filt->ieee_addr[6]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_2, filt->ieee_addr[5]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_3, filt->ieee_addr[4]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_4, filt->ieee_addr[3]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_5, filt->ieee_addr[2]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_6, filt->ieee_addr[1]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_7, filt->ieee_addr[0]); + "at86rf230_set_hw_addr_filt called for IEEE addr\n"); + for (i = 0; i < 8; i++) + __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]); } - if (changed & IEEE802515_AFILT_PANC_CHANGED) { + if (changed & IEEE802154_AFILT_PANC_CHANGED) { dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for panc change\n"); + "at86rf230_set_hw_addr_filt called for panc change\n"); if (filt->pan_coord) at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1); else @@ -668,114 +1145,250 @@ return 0; } -static struct ieee802154_ops at86rf230_ops = { - .owner = THIS_MODULE, - .xmit = at86rf230_xmit, - .ed = at86rf230_ed, - .set_channel = at86rf230_channel, - .start = at86rf230_start, - .stop = at86rf230_stop, - .set_hw_addr_filt = at86rf230_set_hw_addr_filt, +#define AT86RF23X_MAX_TX_POWERS 0xF +static const s32 at86rf233_powers[AT86RF23X_MAX_TX_POWERS + 1] = { + 400, 370, 340, 300, 250, 200, 100, 0, -100, -200, -300, -400, -600, + -800, -1200, -1700, }; -static void at86rf230_irqwork(struct work_struct *work) +static const s32 at86rf231_powers[AT86RF23X_MAX_TX_POWERS + 1] = { + 300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700, + -900, -1200, -1700, +}; + +#define AT86RF212_MAX_TX_POWERS 0x1F +static const s32 at86rf212_powers[AT86RF212_MAX_TX_POWERS + 1] = { + 500, 400, 300, 200, 100, 0, -100, -200, -300, -400, -500, -600, -700, + -800, -900, -1000, -1100, -1200, -1300, -1400, -1500, -1600, -1700, + -1800, -1900, -2000, -2100, -2200, -2300, -2400, -2500, -2600, +}; + +static int +at86rf23x_set_txpower(struct at86rf230_local *lp, s32 mbm) { - struct at86rf230_local *lp = - container_of(work, struct at86rf230_local, irqwork); - u8 status = 0, val; - int rc; - unsigned long flags; - - rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val); - status |= val; - - status &= ~IRQ_PLL_LOCK; /* ignore */ - status &= ~IRQ_RX_START; /* ignore */ - status &= ~IRQ_AMI; /* ignore */ - status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/ - - if (status & IRQ_TRX_END) { - spin_lock_irqsave(&lp->lock, flags); - status &= ~IRQ_TRX_END; - if (lp->is_tx) { - lp->is_tx = 0; - spin_unlock_irqrestore(&lp->lock, flags); - complete(&lp->tx_complete); - } else { - spin_unlock_irqrestore(&lp->lock, flags); - at86rf230_rx(lp); - } + u32 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_23X, i); } - spin_lock_irqsave(&lp->lock, flags); - lp->irq_busy = 0; - spin_unlock_irqrestore(&lp->lock, flags); + return -EINVAL; } -static void at86rf230_irqwork_level(struct work_struct *work) +static int +at86rf212_set_txpower(struct at86rf230_local *lp, s32 mbm) { - struct at86rf230_local *lp = - container_of(work, struct at86rf230_local, irqwork); + u32 i; - at86rf230_irqwork(work); + 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); + } - enable_irq(lp->spi->irq); + return -EINVAL; } -static irqreturn_t at86rf230_isr(int irq, void *data) +static int +at86rf230_set_txpower(struct ieee802154_hw *hw, s32 mbm) { - struct at86rf230_local *lp = data; + struct at86rf230_local *lp = hw->priv; - spin_lock(&lp->lock); - lp->irq_busy = 1; - spin_unlock(&lp->lock); + return lp->data->set_txpower(lp, mbm); +} - schedule_work(&lp->irqwork); +static int +at86rf230_set_lbt(struct ieee802154_hw *hw, bool on) +{ + struct at86rf230_local *lp = hw->priv; - return IRQ_HANDLED; + return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on); } -static irqreturn_t at86rf230_isr_level(int irq, void *data) +static int +at86rf230_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) { - disable_irq_nosync(irq); + struct at86rf230_local *lp = hw->priv; + u8 val; + + /* 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; + } - return at86rf230_isr(irq, data); + return at86rf230_write_subreg(lp, SR_CCA_MODE, val); } -static int at86rf230_irq_polarity(struct at86rf230_local *lp, int pol) + +static int +at86rf230_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) { - return at86rf230_write_subreg(lp, SR_IRQ_POLARITY, pol); + struct at86rf230_local *lp = hw->priv; + u32 i; + + 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); + } + + return -EINVAL; } -static int at86rf230_hw_init(struct at86rf230_local *lp) +static int +at86rf230_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, + u8 retries) { - struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data; - int rc, irq_pol; - u8 status; + struct at86rf230_local *lp = hw->priv; + int rc; - rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be); if (rc) return rc; - dev_info(&lp->spi->dev, "Status: %02x\n", status); - if (status == STATE_P_ON) { - rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF); - if (rc) + rc = at86rf230_write_subreg(lp, SR_MAX_BE, max_be); + if (rc) + return rc; + + return at86rf230_write_subreg(lp, SR_MAX_CSMA_RETRIES, retries); +} + +static int +at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct at86rf230_local *lp = hw->priv; + + return at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries); +} + +static int +at86rf230_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + struct at86rf230_local *lp = hw->priv; + int rc; + + if (on) { + rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 1); + if (rc < 0) return rc; - msleep(1); - rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); - if (rc) + + rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 1); + if (rc < 0) + return rc; + } else { + rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 0); + if (rc < 0) + return rc; + + rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 0); + if (rc < 0) return rc; - dev_info(&lp->spi->dev, "Status: %02x\n", status); } - /* configure irq polarity, defaults to high active */ - if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) + return 0; +} + +static const struct ieee802154_ops at86rf230_ops = { + .owner = THIS_MODULE, + .xmit_async = at86rf230_xmit, + .ed = at86rf230_ed, + .set_channel = at86rf230_channel, + .start = at86rf230_start, + .stop = at86rf230_stop, + .set_hw_addr_filt = at86rf230_set_hw_addr_filt, + .set_txpower = at86rf230_set_txpower, + .set_lbt = at86rf230_set_lbt, + .set_cca_mode = at86rf230_set_cca_mode, + .set_cca_ed_level = at86rf230_set_cca_ed_level, + .set_csma_params = at86rf230_set_csma_params, + .set_frame_retries = at86rf230_set_frame_retries, + .set_promiscuous_mode = at86rf230_set_promiscuous_mode, +}; + +static struct at86rf2xx_chip_data at86rf233_data = { + .t_sleep_cycle = 330, + .t_channel_switch = 11, + .t_reset_to_off = 26, + .t_off_to_aack = 80, + .t_off_to_tx_on = 80, + .t_off_to_sleep = 35, + .t_sleep_to_off = 210, + .t_frame = 4096, + .t_p_ack = 545, + .rssi_base_val = -91, + .set_channel = at86rf23x_set_channel, + .set_txpower = at86rf23x_set_txpower, +}; + +static struct at86rf2xx_chip_data at86rf231_data = { + .t_sleep_cycle = 330, + .t_channel_switch = 24, + .t_reset_to_off = 37, + .t_off_to_aack = 110, + .t_off_to_tx_on = 110, + .t_off_to_sleep = 35, + .t_sleep_to_off = 380, + .t_frame = 4096, + .t_p_ack = 545, + .rssi_base_val = -91, + .set_channel = at86rf23x_set_channel, + .set_txpower = at86rf23x_set_txpower, +}; + +static struct at86rf2xx_chip_data at86rf212_data = { + .t_sleep_cycle = 330, + .t_channel_switch = 11, + .t_reset_to_off = 26, + .t_off_to_aack = 200, + .t_off_to_tx_on = 200, + .t_off_to_sleep = 35, + .t_sleep_to_off = 380, + .t_frame = 4096, + .t_p_ack = 545, + .rssi_base_val = -100, + .set_channel = at86rf212_set_channel, + .set_txpower = at86rf212_set_txpower, +}; + +static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) +{ + int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH; + unsigned int dvdd; + u8 csma_seed[2]; + + rc = at86rf230_sync_state_change(lp, STATE_FORCE_TRX_OFF); + if (rc) + return rc; + + irq_type = irq_get_trigger_type(lp->spi->irq); + if (irq_type == IRQ_TYPE_EDGE_FALLING || + irq_type == IRQ_TYPE_LEVEL_LOW) irq_pol = IRQ_ACTIVE_LOW; - else - irq_pol = IRQ_ACTIVE_HIGH; - rc = at86rf230_irq_polarity(lp, irq_pol); + 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; @@ -783,213 +1396,386 @@ if (rc) return rc; - /* CLKM changes are applied immediately */ - rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); + /* reset values differs in at86rf231 and at86rf233 */ + rc = at86rf230_write_subreg(lp, SR_IRQ_MASK_MODE, 0); if (rc) return rc; - /* Turn CLKM Off */ - rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); + 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; - /* Wait the next SLEEP cycle */ - msleep(100); - rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON); + /* CLKM changes are applied immediately */ + rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); if (rc) return rc; - msleep(1); - rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + /* Turn CLKM Off */ + rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); if (rc) return rc; - dev_info(&lp->spi->dev, "Status: %02x\n", status); + /* Wait the next SLEEP cycle */ + usleep_range(lp->data->t_sleep_cycle, + lp->data->t_sleep_cycle + 100); - rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status); + /* 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; - if (!status) { - dev_err(&lp->spi->dev, "DVDD error\n"); - return -EINVAL; - } - rc = at86rf230_read_subreg(lp, SR_AVDD_OK, &status); + rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd); if (rc) return rc; - if (!status) { - dev_err(&lp->spi->dev, "AVDD error\n"); + if (!dvdd) { + dev_err(&lp->spi->dev, "DVDD error\n"); return -EINVAL; } - return 0; + /* 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); } -static void at86rf230_fill_data(struct spi_device *spi) +static int +at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr, + u8 *xtal_trim) { - struct at86rf230_local *lp = spi_get_drvdata(spi); struct at86rf230_platform_data *pdata = spi->dev.platform_data; + int ret; - lp->rstn = pdata->rstn; - lp->slp_tr = pdata->slp_tr; - lp->dig2 = pdata->dig2; + if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) { + if (!pdata) + return -ENOENT; + + *rstn = pdata->rstn; + *slp_tr = pdata->slp_tr; + *xtal_trim = pdata->xtal_trim; + return 0; + } + + *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); + *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); + ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim); + if (ret < 0 && ret != -EINVAL) + return ret; + + return 0; } -static int at86rf230_probe(struct spi_device *spi) +static int +at86rf230_detect_device(struct at86rf230_local *lp) { - struct at86rf230_platform_data *pdata; - struct ieee802154_dev *dev; - struct at86rf230_local *lp; - u8 man_id_0, man_id_1, status; - irq_handler_t irq_handler; - work_func_t irq_worker; - int rc, supported = 0; + unsigned int part, version, val; + u16 man_id = 0; const char *chip; + int rc; - if (!spi->irq) { - dev_err(&spi->dev, "no IRQ specified\n"); - return -EINVAL; - } + rc = __at86rf230_read(lp, RG_MAN_ID_0, &val); + if (rc) + return rc; + man_id |= val; + + rc = __at86rf230_read(lp, RG_MAN_ID_1, &val); + if (rc) + return rc; + man_id |= (val << 8); + + rc = __at86rf230_read(lp, RG_PART_NUM, &part); + if (rc) + return rc; + + rc = __at86rf230_read(lp, RG_VERSION_NUM, &version); + if (rc) + return rc; - pdata = spi->dev.platform_data; - if (!pdata) { - dev_err(&spi->dev, "no platform_data\n"); + if (man_id != 0x001f) { + dev_err(&lp->spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n", + man_id >> 8, man_id & 0xFF); return -EINVAL; } - dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); - if (!dev) - return -ENOMEM; + lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | + IEEE802154_HW_CSMA_PARAMS | + IEEE802154_HW_FRAME_RETRIES | IEEE802154_HW_AFILT | + IEEE802154_HW_PROMISCUOUS; + + lp->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER | + WPAN_PHY_FLAG_CCA_ED_LEVEL | + WPAN_PHY_FLAG_CCA_MODE; + + lp->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) | + BIT(NL802154_CCA_CARRIER) | BIT(NL802154_CCA_ENERGY_CARRIER); + lp->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND) | + BIT(NL802154_CCA_OPT_ENERGY_CARRIER_OR); - lp = dev->priv; - lp->dev = dev; + lp->hw->phy->supported.cca_ed_levels = at86rf23x_ed_levels; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf23x_ed_levels); - lp->spi = spi; + lp->hw->phy->cca.mode = NL802154_CCA_ENERGY; - dev->parent = &spi->dev; - dev->extra_tx_headroom = 0; - /* We do support only 2.4 Ghz */ - dev->phy->channels_supported[0] = 0x7FFF800; - dev->flags = IEEE802154_HW_OMIT_CKSUM; - - if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - irq_worker = at86rf230_irqwork; - irq_handler = at86rf230_isr; - } else { - irq_worker = at86rf230_irqwork_level; - irq_handler = at86rf230_isr_level; + switch (part) { + case 2: + chip = "at86rf230"; + rc = -ENOTSUPP; + goto not_supp; + case 3: + chip = "at86rf231"; + lp->data = &at86rf231_data; + lp->hw->phy->supported.channels[0] = 0x7FFF800; + lp->hw->phy->current_channel = 11; + lp->hw->phy->symbol_duration = 16; + lp->hw->phy->supported.tx_powers = at86rf231_powers; + lp->hw->phy->supported.tx_powers_size = ARRAY_SIZE(at86rf231_powers); + break; + case 7: + chip = "at86rf212"; + lp->data = &at86rf212_data; + lp->hw->flags |= IEEE802154_HW_LBT; + lp->hw->phy->supported.channels[0] = 0x00007FF; + lp->hw->phy->supported.channels[2] = 0x00007FF; + lp->hw->phy->current_channel = 5; + lp->hw->phy->symbol_duration = 25; + lp->hw->phy->supported.lbt = NL802154_SUPPORTED_BOOL_BOTH; + lp->hw->phy->supported.tx_powers = at86rf212_powers; + lp->hw->phy->supported.tx_powers_size = ARRAY_SIZE(at86rf212_powers); + lp->hw->phy->supported.cca_ed_levels = at86rf212_ed_levels_100; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf212_ed_levels_100); + break; + case 11: + chip = "at86rf233"; + lp->data = &at86rf233_data; + lp->hw->phy->supported.channels[0] = 0x7FFF800; + lp->hw->phy->current_channel = 13; + lp->hw->phy->symbol_duration = 16; + lp->hw->phy->supported.tx_powers = at86rf233_powers; + lp->hw->phy->supported.tx_powers_size = ARRAY_SIZE(at86rf233_powers); + break; + default: + chip = "unknown"; + rc = -ENOTSUPP; + goto not_supp; } - mutex_init(&lp->bmux); - INIT_WORK(&lp->irqwork, irq_worker); - spin_lock_init(&lp->lock); - init_completion(&lp->tx_complete); + lp->hw->phy->cca_ed_level = lp->hw->phy->supported.cca_ed_levels[7]; + lp->hw->phy->transmit_power = lp->hw->phy->supported.tx_powers[0]; - spi_set_drvdata(spi, lp); +not_supp: + dev_info(&lp->spi->dev, "Detected %s chip version %d\n", chip, version); - at86rf230_fill_data(spi); + return rc; +} - rc = gpio_request(lp->rstn, "rstn"); - if (rc) - goto err_rstn; +#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS +static struct dentry *at86rf230_debugfs_root; - if (gpio_is_valid(lp->slp_tr)) { - rc = gpio_request(lp->slp_tr, "slp_tr"); - if (rc) - goto err_slp_tr; +static int at86rf230_stats_show(struct seq_file *file, void *offset) +{ + struct at86rf230_local *lp = file->private; + + seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success); + seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n", + lp->trac.success_data_pending); + seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n", + lp->trac.success_wait_for_ack); + seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n", + lp->trac.channel_access_failure); + seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack); + seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid); + return 0; +} + +static int at86rf230_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, at86rf230_stats_show, inode->i_private); +} + +static const struct file_operations at86rf230_stats_fops = { + .open = at86rf230_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int at86rf230_debugfs_init(struct at86rf230_local *lp) +{ + char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-"; + struct dentry *stats; + + strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN); + + at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL); + if (!at86rf230_debugfs_root) + return -ENOMEM; + + stats = debugfs_create_file("trac_stats", S_IRUGO, + at86rf230_debugfs_root, lp, + &at86rf230_stats_fops); + if (!stats) + return -ENOMEM; + + return 0; +} + +static void at86rf230_debugfs_remove(void) +{ + debugfs_remove_recursive(at86rf230_debugfs_root); +} +#else +static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; } +static void at86rf230_debugfs_remove(void) { } +#endif + +static int at86rf230_probe(struct spi_device *spi) +{ + struct ieee802154_hw *hw; + struct at86rf230_local *lp; + unsigned int status; + int rc, irq_type, rstn, slp_tr; + u8 xtal_trim = 0; + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ specified\n"); + return -EINVAL; } - rc = gpio_direction_output(lp->rstn, 1); - if (rc) - goto err_gpio_dir; + rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim); + if (rc < 0) { + dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc); + return rc; + } - if (gpio_is_valid(lp->slp_tr)) { - rc = gpio_direction_output(lp->slp_tr, 0); + if (gpio_is_valid(rstn)) { + rc = devm_gpio_request_one(&spi->dev, rstn, + GPIOF_OUT_INIT_HIGH, "rstn"); + if (rc) + return rc; + } + + if (gpio_is_valid(slp_tr)) { + rc = devm_gpio_request_one(&spi->dev, slp_tr, + GPIOF_OUT_INIT_LOW, "slp_tr"); if (rc) - goto err_gpio_dir; + return rc; } /* Reset */ - msleep(1); - gpio_set_value(lp->rstn, 0); - msleep(1); - gpio_set_value(lp->rstn, 1); - msleep(1); + if (gpio_is_valid(rstn)) { + udelay(1); + gpio_set_value(rstn, 0); + udelay(1); + gpio_set_value(rstn, 1); + usleep_range(120, 240); + } - rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0); - if (rc) - goto err_gpio_dir; - rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1); - if (rc) - goto err_gpio_dir; + hw = ieee802154_alloc_hw(sizeof(*lp), &at86rf230_ops); + if (!hw) + return -ENOMEM; - if (man_id_1 != 0x00 || man_id_0 != 0x1f) { - dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n", - man_id_1, man_id_0); - rc = -EINVAL; - goto err_gpio_dir; + lp = hw->priv; + lp->hw = hw; + lp->spi = spi; + lp->slp_tr = slp_tr; + hw->parent = &spi->dev; + ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); + + lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config); + if (IS_ERR(lp->regmap)) { + rc = PTR_ERR(lp->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + rc); + goto free_dev; } - rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part); - if (rc) - goto err_gpio_dir; - - rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers); - if (rc) - goto err_gpio_dir; + at86rf230_setup_spi_messages(lp, &lp->state); + at86rf230_setup_spi_messages(lp, &lp->tx); - switch (lp->part) { - case 2: - chip = "at86rf230"; - /* supported = 1; FIXME: should be easy to support; */ - break; - case 3: - chip = "at86rf231"; - supported = 1; - break; - default: - chip = "UNKNOWN"; - break; - } + rc = at86rf230_detect_device(lp); + if (rc < 0) + goto free_dev; - dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers); - if (!supported) { - rc = -ENOTSUPP; - goto err_gpio_dir; - } + init_completion(&lp->state_complete); - rc = at86rf230_hw_init(lp); - if (rc) - goto err_gpio_dir; + spi_set_drvdata(spi, lp); - rc = request_irq(spi->irq, irq_handler, - IRQF_SHARED | pdata->irq_type, - dev_name(&spi->dev), lp); + rc = at86rf230_hw_init(lp, xtal_trim); if (rc) - goto err_gpio_dir; + goto free_dev; /* Read irq status register to reset irq line */ rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status); if (rc) - goto err_irq; + goto free_dev; + + irq_type = irq_get_trigger_type(spi->irq); + if (!irq_type) + irq_type = IRQF_TRIGGER_HIGH; - rc = ieee802154_register_device(lp->dev); + rc = devm_request_irq(&spi->dev, spi->irq, at86rf230_isr, + IRQF_SHARED | irq_type, dev_name(&spi->dev), lp); if (rc) - goto err_irq; + goto free_dev; + + /* disable_irq by default and wait for starting hardware */ + disable_irq(spi->irq); + + /* going into sleep by default */ + at86rf230_sleep(lp); + + rc = at86rf230_debugfs_init(lp); + if (rc) + goto free_dev; + + rc = ieee802154_register_hw(lp->hw); + if (rc) + goto free_debugfs; return rc; -err_irq: - free_irq(spi->irq, lp); - flush_work(&lp->irqwork); -err_gpio_dir: - if (gpio_is_valid(lp->slp_tr)) - gpio_free(lp->slp_tr); -err_slp_tr: - gpio_free(lp->rstn); -err_rstn: - spi_set_drvdata(spi, NULL); - mutex_destroy(&lp->bmux); - ieee802154_free_device(lp->dev); +free_debugfs: + at86rf230_debugfs_remove(); +free_dev: + ieee802154_free_hw(lp->hw); + return rc; } @@ -997,27 +1783,39 @@ { struct at86rf230_local *lp = spi_get_drvdata(spi); - ieee802154_unregister_device(lp->dev); - - free_irq(spi->irq, lp); - flush_work(&lp->irqwork); - - if (gpio_is_valid(lp->slp_tr)) - gpio_free(lp->slp_tr); - gpio_free(lp->rstn); - - spi_set_drvdata(spi, NULL); - mutex_destroy(&lp->bmux); - ieee802154_free_device(lp->dev); - + /* mask all at86rf230 irq's */ + at86rf230_write_subreg(lp, SR_IRQ_MASK, 0); + ieee802154_unregister_hw(lp->hw); + ieee802154_free_hw(lp->hw); + at86rf230_debugfs_remove(); dev_dbg(&spi->dev, "unregistered at86rf230\n"); + return 0; } +static const struct of_device_id at86rf230_of_match[] = { + { .compatible = "atmel,at86rf230", }, + { .compatible = "atmel,at86rf231", }, + { .compatible = "atmel,at86rf233", }, + { .compatible = "atmel,at86rf212", }, + { }, +}; +MODULE_DEVICE_TABLE(of, at86rf230_of_match); + +static const struct spi_device_id at86rf230_device_id[] = { + { .name = "at86rf230", }, + { .name = "at86rf231", }, + { .name = "at86rf233", }, + { .name = "at86rf212", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, at86rf230_device_id); + static struct spi_driver at86rf230_driver = { + .id_table = at86rf230_device_id, .driver = { + .of_match_table = of_match_ptr(at86rf230_of_match), .name = "at86rf230", - .owner = THIS_MODULE, }, .probe = at86rf230_probe, .remove = at86rf230_remove,