162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in> 562306a36Sopenharmony_ci * Md.Jamal Mohiuddin <mjmohiuddin@cdac.in> 662306a36Sopenharmony_ci * P Sowjanya <sowjanyap@cdac.in> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/spi/spi.h> 1362306a36Sopenharmony_ci#include <linux/property.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/skbuff.h> 1762306a36Sopenharmony_ci#include <linux/ieee802154.h> 1862306a36Sopenharmony_ci#include <linux/crc-ccitt.h> 1962306a36Sopenharmony_ci#include <asm/unaligned.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <net/mac802154.h> 2262306a36Sopenharmony_ci#include <net/cfg802154.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SPI_COMMAND_BUFFER 3 2562306a36Sopenharmony_ci#define HIGH 1 2662306a36Sopenharmony_ci#define LOW 0 2762306a36Sopenharmony_ci#define STATE_IDLE 0 2862306a36Sopenharmony_ci#define RSSI_VALID 0 2962306a36Sopenharmony_ci#define RSSI_OFFSET 78 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define CC2520_RAM_SIZE 640 3262306a36Sopenharmony_ci#define CC2520_FIFO_SIZE 128 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define CC2520RAM_TXFIFO 0x100 3562306a36Sopenharmony_ci#define CC2520RAM_RXFIFO 0x180 3662306a36Sopenharmony_ci#define CC2520RAM_IEEEADDR 0x3EA 3762306a36Sopenharmony_ci#define CC2520RAM_PANID 0x3F2 3862306a36Sopenharmony_ci#define CC2520RAM_SHORTADDR 0x3F4 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CC2520_FREG_MASK 0x3F 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* status byte values */ 4362306a36Sopenharmony_ci#define CC2520_STATUS_XOSC32M_STABLE BIT(7) 4462306a36Sopenharmony_ci#define CC2520_STATUS_RSSI_VALID BIT(6) 4562306a36Sopenharmony_ci#define CC2520_STATUS_TX_UNDERFLOW BIT(3) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ 4862306a36Sopenharmony_ci#define CC2520_MINCHANNEL 11 4962306a36Sopenharmony_ci#define CC2520_MAXCHANNEL 26 5062306a36Sopenharmony_ci#define CC2520_CHANNEL_SPACING 5 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* command strobes */ 5362306a36Sopenharmony_ci#define CC2520_CMD_SNOP 0x00 5462306a36Sopenharmony_ci#define CC2520_CMD_IBUFLD 0x02 5562306a36Sopenharmony_ci#define CC2520_CMD_SIBUFEX 0x03 5662306a36Sopenharmony_ci#define CC2520_CMD_SSAMPLECCA 0x04 5762306a36Sopenharmony_ci#define CC2520_CMD_SRES 0x0f 5862306a36Sopenharmony_ci#define CC2520_CMD_MEMORY_MASK 0x0f 5962306a36Sopenharmony_ci#define CC2520_CMD_MEMORY_READ 0x10 6062306a36Sopenharmony_ci#define CC2520_CMD_MEMORY_WRITE 0x20 6162306a36Sopenharmony_ci#define CC2520_CMD_RXBUF 0x30 6262306a36Sopenharmony_ci#define CC2520_CMD_RXBUFCP 0x38 6362306a36Sopenharmony_ci#define CC2520_CMD_RXBUFMOV 0x32 6462306a36Sopenharmony_ci#define CC2520_CMD_TXBUF 0x3A 6562306a36Sopenharmony_ci#define CC2520_CMD_TXBUFCP 0x3E 6662306a36Sopenharmony_ci#define CC2520_CMD_RANDOM 0x3C 6762306a36Sopenharmony_ci#define CC2520_CMD_SXOSCON 0x40 6862306a36Sopenharmony_ci#define CC2520_CMD_STXCAL 0x41 6962306a36Sopenharmony_ci#define CC2520_CMD_SRXON 0x42 7062306a36Sopenharmony_ci#define CC2520_CMD_STXON 0x43 7162306a36Sopenharmony_ci#define CC2520_CMD_STXONCCA 0x44 7262306a36Sopenharmony_ci#define CC2520_CMD_SRFOFF 0x45 7362306a36Sopenharmony_ci#define CC2520_CMD_SXOSCOFF 0x46 7462306a36Sopenharmony_ci#define CC2520_CMD_SFLUSHRX 0x47 7562306a36Sopenharmony_ci#define CC2520_CMD_SFLUSHTX 0x48 7662306a36Sopenharmony_ci#define CC2520_CMD_SACK 0x49 7762306a36Sopenharmony_ci#define CC2520_CMD_SACKPEND 0x4A 7862306a36Sopenharmony_ci#define CC2520_CMD_SNACK 0x4B 7962306a36Sopenharmony_ci#define CC2520_CMD_SRXMASKBITSET 0x4C 8062306a36Sopenharmony_ci#define CC2520_CMD_SRXMASKBITCLR 0x4D 8162306a36Sopenharmony_ci#define CC2520_CMD_RXMASKAND 0x4E 8262306a36Sopenharmony_ci#define CC2520_CMD_RXMASKOR 0x4F 8362306a36Sopenharmony_ci#define CC2520_CMD_MEMCP 0x50 8462306a36Sopenharmony_ci#define CC2520_CMD_MEMCPR 0x52 8562306a36Sopenharmony_ci#define CC2520_CMD_MEMXCP 0x54 8662306a36Sopenharmony_ci#define CC2520_CMD_MEMXWR 0x56 8762306a36Sopenharmony_ci#define CC2520_CMD_BCLR 0x58 8862306a36Sopenharmony_ci#define CC2520_CMD_BSET 0x59 8962306a36Sopenharmony_ci#define CC2520_CMD_CTR_UCTR 0x60 9062306a36Sopenharmony_ci#define CC2520_CMD_CBCMAC 0x64 9162306a36Sopenharmony_ci#define CC2520_CMD_UCBCMAC 0x66 9262306a36Sopenharmony_ci#define CC2520_CMD_CCM 0x68 9362306a36Sopenharmony_ci#define CC2520_CMD_UCCM 0x6A 9462306a36Sopenharmony_ci#define CC2520_CMD_ECB 0x70 9562306a36Sopenharmony_ci#define CC2520_CMD_ECBO 0x72 9662306a36Sopenharmony_ci#define CC2520_CMD_ECBX 0x74 9762306a36Sopenharmony_ci#define CC2520_CMD_INC 0x78 9862306a36Sopenharmony_ci#define CC2520_CMD_ABORT 0x7F 9962306a36Sopenharmony_ci#define CC2520_CMD_REGISTER_READ 0x80 10062306a36Sopenharmony_ci#define CC2520_CMD_REGISTER_WRITE 0xC0 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* status registers */ 10362306a36Sopenharmony_ci#define CC2520_CHIPID 0x40 10462306a36Sopenharmony_ci#define CC2520_VERSION 0x42 10562306a36Sopenharmony_ci#define CC2520_EXTCLOCK 0x44 10662306a36Sopenharmony_ci#define CC2520_MDMCTRL0 0x46 10762306a36Sopenharmony_ci#define CC2520_MDMCTRL1 0x47 10862306a36Sopenharmony_ci#define CC2520_FREQEST 0x48 10962306a36Sopenharmony_ci#define CC2520_RXCTRL 0x4A 11062306a36Sopenharmony_ci#define CC2520_FSCTRL 0x4C 11162306a36Sopenharmony_ci#define CC2520_FSCAL0 0x4E 11262306a36Sopenharmony_ci#define CC2520_FSCAL1 0x4F 11362306a36Sopenharmony_ci#define CC2520_FSCAL2 0x50 11462306a36Sopenharmony_ci#define CC2520_FSCAL3 0x51 11562306a36Sopenharmony_ci#define CC2520_AGCCTRL0 0x52 11662306a36Sopenharmony_ci#define CC2520_AGCCTRL1 0x53 11762306a36Sopenharmony_ci#define CC2520_AGCCTRL2 0x54 11862306a36Sopenharmony_ci#define CC2520_AGCCTRL3 0x55 11962306a36Sopenharmony_ci#define CC2520_ADCTEST0 0x56 12062306a36Sopenharmony_ci#define CC2520_ADCTEST1 0x57 12162306a36Sopenharmony_ci#define CC2520_ADCTEST2 0x58 12262306a36Sopenharmony_ci#define CC2520_MDMTEST0 0x5A 12362306a36Sopenharmony_ci#define CC2520_MDMTEST1 0x5B 12462306a36Sopenharmony_ci#define CC2520_DACTEST0 0x5C 12562306a36Sopenharmony_ci#define CC2520_DACTEST1 0x5D 12662306a36Sopenharmony_ci#define CC2520_ATEST 0x5E 12762306a36Sopenharmony_ci#define CC2520_DACTEST2 0x5F 12862306a36Sopenharmony_ci#define CC2520_PTEST0 0x60 12962306a36Sopenharmony_ci#define CC2520_PTEST1 0x61 13062306a36Sopenharmony_ci#define CC2520_RESERVED 0x62 13162306a36Sopenharmony_ci#define CC2520_DPUBIST 0x7A 13262306a36Sopenharmony_ci#define CC2520_ACTBIST 0x7C 13362306a36Sopenharmony_ci#define CC2520_RAMBIST 0x7E 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* frame registers */ 13662306a36Sopenharmony_ci#define CC2520_FRMFILT0 0x00 13762306a36Sopenharmony_ci#define CC2520_FRMFILT1 0x01 13862306a36Sopenharmony_ci#define CC2520_SRCMATCH 0x02 13962306a36Sopenharmony_ci#define CC2520_SRCSHORTEN0 0x04 14062306a36Sopenharmony_ci#define CC2520_SRCSHORTEN1 0x05 14162306a36Sopenharmony_ci#define CC2520_SRCSHORTEN2 0x06 14262306a36Sopenharmony_ci#define CC2520_SRCEXTEN0 0x08 14362306a36Sopenharmony_ci#define CC2520_SRCEXTEN1 0x09 14462306a36Sopenharmony_ci#define CC2520_SRCEXTEN2 0x0A 14562306a36Sopenharmony_ci#define CC2520_FRMCTRL0 0x0C 14662306a36Sopenharmony_ci#define CC2520_FRMCTRL1 0x0D 14762306a36Sopenharmony_ci#define CC2520_RXENABLE0 0x0E 14862306a36Sopenharmony_ci#define CC2520_RXENABLE1 0x0F 14962306a36Sopenharmony_ci#define CC2520_EXCFLAG0 0x10 15062306a36Sopenharmony_ci#define CC2520_EXCFLAG1 0x11 15162306a36Sopenharmony_ci#define CC2520_EXCFLAG2 0x12 15262306a36Sopenharmony_ci#define CC2520_EXCMASKA0 0x14 15362306a36Sopenharmony_ci#define CC2520_EXCMASKA1 0x15 15462306a36Sopenharmony_ci#define CC2520_EXCMASKA2 0x16 15562306a36Sopenharmony_ci#define CC2520_EXCMASKB0 0x18 15662306a36Sopenharmony_ci#define CC2520_EXCMASKB1 0x19 15762306a36Sopenharmony_ci#define CC2520_EXCMASKB2 0x1A 15862306a36Sopenharmony_ci#define CC2520_EXCBINDX0 0x1C 15962306a36Sopenharmony_ci#define CC2520_EXCBINDX1 0x1D 16062306a36Sopenharmony_ci#define CC2520_EXCBINDY0 0x1E 16162306a36Sopenharmony_ci#define CC2520_EXCBINDY1 0x1F 16262306a36Sopenharmony_ci#define CC2520_GPIOCTRL0 0x20 16362306a36Sopenharmony_ci#define CC2520_GPIOCTRL1 0x21 16462306a36Sopenharmony_ci#define CC2520_GPIOCTRL2 0x22 16562306a36Sopenharmony_ci#define CC2520_GPIOCTRL3 0x23 16662306a36Sopenharmony_ci#define CC2520_GPIOCTRL4 0x24 16762306a36Sopenharmony_ci#define CC2520_GPIOCTRL5 0x25 16862306a36Sopenharmony_ci#define CC2520_GPIOPOLARITY 0x26 16962306a36Sopenharmony_ci#define CC2520_GPIOCTRL 0x28 17062306a36Sopenharmony_ci#define CC2520_DPUCON 0x2A 17162306a36Sopenharmony_ci#define CC2520_DPUSTAT 0x2C 17262306a36Sopenharmony_ci#define CC2520_FREQCTRL 0x2E 17362306a36Sopenharmony_ci#define CC2520_FREQTUNE 0x2F 17462306a36Sopenharmony_ci#define CC2520_TXPOWER 0x30 17562306a36Sopenharmony_ci#define CC2520_TXCTRL 0x31 17662306a36Sopenharmony_ci#define CC2520_FSMSTAT0 0x32 17762306a36Sopenharmony_ci#define CC2520_FSMSTAT1 0x33 17862306a36Sopenharmony_ci#define CC2520_FIFOPCTRL 0x34 17962306a36Sopenharmony_ci#define CC2520_FSMCTRL 0x35 18062306a36Sopenharmony_ci#define CC2520_CCACTRL0 0x36 18162306a36Sopenharmony_ci#define CC2520_CCACTRL1 0x37 18262306a36Sopenharmony_ci#define CC2520_RSSI 0x38 18362306a36Sopenharmony_ci#define CC2520_RSSISTAT 0x39 18462306a36Sopenharmony_ci#define CC2520_RXFIRST 0x3C 18562306a36Sopenharmony_ci#define CC2520_RXFIFOCNT 0x3E 18662306a36Sopenharmony_ci#define CC2520_TXFIFOCNT 0x3F 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* CC2520_FRMFILT0 */ 18962306a36Sopenharmony_ci#define FRMFILT0_FRAME_FILTER_EN BIT(0) 19062306a36Sopenharmony_ci#define FRMFILT0_PAN_COORDINATOR BIT(1) 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* CC2520_FRMCTRL0 */ 19362306a36Sopenharmony_ci#define FRMCTRL0_AUTOACK BIT(5) 19462306a36Sopenharmony_ci#define FRMCTRL0_AUTOCRC BIT(6) 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* CC2520_FRMCTRL1 */ 19762306a36Sopenharmony_ci#define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0) 19862306a36Sopenharmony_ci#define FRMCTRL1_IGNORE_TX_UNDERF BIT(1) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* Driver private information */ 20162306a36Sopenharmony_cistruct cc2520_private { 20262306a36Sopenharmony_ci struct spi_device *spi; /* SPI device structure */ 20362306a36Sopenharmony_ci struct ieee802154_hw *hw; /* IEEE-802.15.4 device */ 20462306a36Sopenharmony_ci u8 *buf; /* SPI TX/Rx data buffer */ 20562306a36Sopenharmony_ci struct mutex buffer_mutex; /* SPI buffer mutex */ 20662306a36Sopenharmony_ci bool is_tx; /* Flag for sync b/w Tx and Rx */ 20762306a36Sopenharmony_ci bool amplified; /* Flag for CC2591 */ 20862306a36Sopenharmony_ci struct gpio_desc *fifo_pin; /* FIFO GPIO pin number */ 20962306a36Sopenharmony_ci struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ 21062306a36Sopenharmony_ci spinlock_t lock; /* Lock for is_tx*/ 21162306a36Sopenharmony_ci struct completion tx_complete; /* Work completion for Tx */ 21262306a36Sopenharmony_ci bool promiscuous; /* Flag for promiscuous mode */ 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* Generic Functions */ 21662306a36Sopenharmony_cistatic int 21762306a36Sopenharmony_cicc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int ret; 22062306a36Sopenharmony_ci struct spi_message msg; 22162306a36Sopenharmony_ci struct spi_transfer xfer = { 22262306a36Sopenharmony_ci .len = 0, 22362306a36Sopenharmony_ci .tx_buf = priv->buf, 22462306a36Sopenharmony_ci .rx_buf = priv->buf, 22562306a36Sopenharmony_ci }; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci spi_message_init(&msg); 22862306a36Sopenharmony_ci spi_message_add_tail(&xfer, &msg); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 23162306a36Sopenharmony_ci priv->buf[xfer.len++] = cmd; 23262306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 23362306a36Sopenharmony_ci "command strobe buf[0] = %02x\n", 23462306a36Sopenharmony_ci priv->buf[0]); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = spi_sync(priv->spi, &msg); 23762306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 23862306a36Sopenharmony_ci "buf[0] = %02x\n", priv->buf[0]); 23962306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int 24562306a36Sopenharmony_cicc2520_get_status(struct cc2520_private *priv, u8 *status) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci int ret; 24862306a36Sopenharmony_ci struct spi_message msg; 24962306a36Sopenharmony_ci struct spi_transfer xfer = { 25062306a36Sopenharmony_ci .len = 0, 25162306a36Sopenharmony_ci .tx_buf = priv->buf, 25262306a36Sopenharmony_ci .rx_buf = priv->buf, 25362306a36Sopenharmony_ci }; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci spi_message_init(&msg); 25662306a36Sopenharmony_ci spi_message_add_tail(&xfer, &msg); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 25962306a36Sopenharmony_ci priv->buf[xfer.len++] = CC2520_CMD_SNOP; 26062306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 26162306a36Sopenharmony_ci "get status command buf[0] = %02x\n", priv->buf[0]); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = spi_sync(priv->spi, &msg); 26462306a36Sopenharmony_ci if (!ret) 26562306a36Sopenharmony_ci *status = priv->buf[0]; 26662306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 26762306a36Sopenharmony_ci "buf[0] = %02x\n", priv->buf[0]); 26862306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int 27462306a36Sopenharmony_cicc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int status; 27762306a36Sopenharmony_ci struct spi_message msg; 27862306a36Sopenharmony_ci struct spi_transfer xfer = { 27962306a36Sopenharmony_ci .len = 0, 28062306a36Sopenharmony_ci .tx_buf = priv->buf, 28162306a36Sopenharmony_ci .rx_buf = priv->buf, 28262306a36Sopenharmony_ci }; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci spi_message_init(&msg); 28562306a36Sopenharmony_ci spi_message_add_tail(&xfer, &msg); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (reg <= CC2520_FREG_MASK) { 29062306a36Sopenharmony_ci priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg; 29162306a36Sopenharmony_ci priv->buf[xfer.len++] = value; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE; 29462306a36Sopenharmony_ci priv->buf[xfer.len++] = reg; 29562306a36Sopenharmony_ci priv->buf[xfer.len++] = value; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci status = spi_sync(priv->spi, &msg); 29862306a36Sopenharmony_ci if (msg.status) 29962306a36Sopenharmony_ci status = msg.status; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return status; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int 30762306a36Sopenharmony_cicc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int status; 31062306a36Sopenharmony_ci struct spi_message msg; 31162306a36Sopenharmony_ci struct spi_transfer xfer_head = { 31262306a36Sopenharmony_ci .len = 0, 31362306a36Sopenharmony_ci .tx_buf = priv->buf, 31462306a36Sopenharmony_ci .rx_buf = priv->buf, 31562306a36Sopenharmony_ci }; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci struct spi_transfer xfer_buf = { 31862306a36Sopenharmony_ci .len = len, 31962306a36Sopenharmony_ci .tx_buf = data, 32062306a36Sopenharmony_ci }; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 32362306a36Sopenharmony_ci priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE | 32462306a36Sopenharmony_ci ((reg >> 8) & 0xff)); 32562306a36Sopenharmony_ci priv->buf[xfer_head.len++] = reg & 0xff; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spi_message_init(&msg); 32862306a36Sopenharmony_ci spi_message_add_tail(&xfer_head, &msg); 32962306a36Sopenharmony_ci spi_message_add_tail(&xfer_buf, &msg); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci status = spi_sync(priv->spi, &msg); 33262306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "spi status = %d\n", status); 33362306a36Sopenharmony_ci if (msg.status) 33462306a36Sopenharmony_ci status = msg.status; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 33762306a36Sopenharmony_ci return status; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int 34162306a36Sopenharmony_cicc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int status; 34462306a36Sopenharmony_ci struct spi_message msg; 34562306a36Sopenharmony_ci struct spi_transfer xfer1 = { 34662306a36Sopenharmony_ci .len = 0, 34762306a36Sopenharmony_ci .tx_buf = priv->buf, 34862306a36Sopenharmony_ci .rx_buf = priv->buf, 34962306a36Sopenharmony_ci }; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci struct spi_transfer xfer2 = { 35262306a36Sopenharmony_ci .len = 1, 35362306a36Sopenharmony_ci .rx_buf = data, 35462306a36Sopenharmony_ci }; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci spi_message_init(&msg); 35762306a36Sopenharmony_ci spi_message_add_tail(&xfer1, &msg); 35862306a36Sopenharmony_ci spi_message_add_tail(&xfer2, &msg); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 36162306a36Sopenharmony_ci priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ; 36262306a36Sopenharmony_ci priv->buf[xfer1.len++] = reg; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci status = spi_sync(priv->spi, &msg); 36562306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, 36662306a36Sopenharmony_ci "spi status = %d\n", status); 36762306a36Sopenharmony_ci if (msg.status) 36862306a36Sopenharmony_ci status = msg.status; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return status; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int 37662306a36Sopenharmony_cicc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci int status; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* length byte must include FCS even 38162306a36Sopenharmony_ci * if it is calculated in the hardware 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci int len_byte = pkt_len; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci struct spi_message msg; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci struct spi_transfer xfer_head = { 38862306a36Sopenharmony_ci .len = 0, 38962306a36Sopenharmony_ci .tx_buf = priv->buf, 39062306a36Sopenharmony_ci .rx_buf = priv->buf, 39162306a36Sopenharmony_ci }; 39262306a36Sopenharmony_ci struct spi_transfer xfer_len = { 39362306a36Sopenharmony_ci .len = 1, 39462306a36Sopenharmony_ci .tx_buf = &len_byte, 39562306a36Sopenharmony_ci }; 39662306a36Sopenharmony_ci struct spi_transfer xfer_buf = { 39762306a36Sopenharmony_ci .len = len, 39862306a36Sopenharmony_ci .tx_buf = data, 39962306a36Sopenharmony_ci }; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci spi_message_init(&msg); 40262306a36Sopenharmony_ci spi_message_add_tail(&xfer_head, &msg); 40362306a36Sopenharmony_ci spi_message_add_tail(&xfer_len, &msg); 40462306a36Sopenharmony_ci spi_message_add_tail(&xfer_buf, &msg); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 40762306a36Sopenharmony_ci priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF; 40862306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 40962306a36Sopenharmony_ci "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci status = spi_sync(priv->spi, &msg); 41262306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "status = %d\n", status); 41362306a36Sopenharmony_ci if (msg.status) 41462306a36Sopenharmony_ci status = msg.status; 41562306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "status = %d\n", status); 41662306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]); 41762306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return status; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int 42362306a36Sopenharmony_cicc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int status; 42662306a36Sopenharmony_ci struct spi_message msg; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci struct spi_transfer xfer_head = { 42962306a36Sopenharmony_ci .len = 0, 43062306a36Sopenharmony_ci .tx_buf = priv->buf, 43162306a36Sopenharmony_ci .rx_buf = priv->buf, 43262306a36Sopenharmony_ci }; 43362306a36Sopenharmony_ci struct spi_transfer xfer_buf = { 43462306a36Sopenharmony_ci .len = len, 43562306a36Sopenharmony_ci .rx_buf = data, 43662306a36Sopenharmony_ci }; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci spi_message_init(&msg); 43962306a36Sopenharmony_ci spi_message_add_tail(&xfer_head, &msg); 44062306a36Sopenharmony_ci spi_message_add_tail(&xfer_buf, &msg); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci mutex_lock(&priv->buffer_mutex); 44362306a36Sopenharmony_ci priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]); 44662306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci status = spi_sync(priv->spi, &msg); 44962306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "status = %d\n", status); 45062306a36Sopenharmony_ci if (msg.status) 45162306a36Sopenharmony_ci status = msg.status; 45262306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "status = %d\n", status); 45362306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 45462306a36Sopenharmony_ci "return status buf[0] = %02x\n", priv->buf[0]); 45562306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_unlock(&priv->buffer_mutex); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return status; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int cc2520_start(struct ieee802154_hw *hw) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void cc2520_stop(struct ieee802154_hw *hw) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int 47362306a36Sopenharmony_cicc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 47662306a36Sopenharmony_ci unsigned long flags; 47762306a36Sopenharmony_ci int rc; 47862306a36Sopenharmony_ci u8 status = 0; 47962306a36Sopenharmony_ci u8 pkt_len; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* In promiscuous mode we disable AUTOCRC so we can get the raw CRC 48262306a36Sopenharmony_ci * values on RX. This means we need to manually add the CRC on TX. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (priv->promiscuous) { 48562306a36Sopenharmony_ci u16 crc = crc_ccitt(0, skb->data, skb->len); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci put_unaligned_le16(crc, skb_put(skb, 2)); 48862306a36Sopenharmony_ci pkt_len = skb->len; 48962306a36Sopenharmony_ci } else { 49062306a36Sopenharmony_ci pkt_len = skb->len + 2; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); 49462306a36Sopenharmony_ci if (rc) 49562306a36Sopenharmony_ci goto err_tx; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len); 49862306a36Sopenharmony_ci if (rc) 49962306a36Sopenharmony_ci goto err_tx; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci rc = cc2520_get_status(priv, &status); 50262306a36Sopenharmony_ci if (rc) 50362306a36Sopenharmony_ci goto err_tx; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (status & CC2520_STATUS_TX_UNDERFLOW) { 50662306a36Sopenharmony_ci rc = -EINVAL; 50762306a36Sopenharmony_ci dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n"); 50862306a36Sopenharmony_ci goto err_tx; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 51262306a36Sopenharmony_ci WARN_ON(priv->is_tx); 51362306a36Sopenharmony_ci priv->is_tx = 1; 51462306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA); 51762306a36Sopenharmony_ci if (rc) 51862306a36Sopenharmony_ci goto err; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci rc = wait_for_completion_interruptible(&priv->tx_complete); 52162306a36Sopenharmony_ci if (rc < 0) 52262306a36Sopenharmony_ci goto err; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); 52562306a36Sopenharmony_ci cc2520_cmd_strobe(priv, CC2520_CMD_SRXON); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return rc; 52862306a36Sopenharmony_cierr: 52962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 53062306a36Sopenharmony_ci priv->is_tx = 0; 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 53262306a36Sopenharmony_cierr_tx: 53362306a36Sopenharmony_ci return rc; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int cc2520_rx(struct cc2520_private *priv) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci u8 len = 0, lqi = 0, bytes = 1; 53962306a36Sopenharmony_ci struct sk_buff *skb; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Read single length byte from the radio. */ 54262306a36Sopenharmony_ci cc2520_read_rxfifo(priv, &len, bytes); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!ieee802154_is_valid_psdu_len(len)) { 54562306a36Sopenharmony_ci /* Corrupted frame received, clear frame buffer by 54662306a36Sopenharmony_ci * reading entire buffer. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "corrupted frame received\n"); 54962306a36Sopenharmony_ci len = IEEE802154_MTU; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci skb = dev_alloc_skb(len); 55362306a36Sopenharmony_ci if (!skb) 55462306a36Sopenharmony_ci return -ENOMEM; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) { 55762306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "frame reception failed\n"); 55862306a36Sopenharmony_ci kfree_skb(skb); 55962306a36Sopenharmony_ci return -EINVAL; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* In promiscuous mode, we configure the radio to include the 56362306a36Sopenharmony_ci * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not 56462306a36Sopenharmony_ci * in promiscuous mode, we check the CRC here, but leave the 56562306a36Sopenharmony_ci * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (!priv->promiscuous) { 56862306a36Sopenharmony_ci bool crc_ok; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Check if the CRC is valid. With AUTOCRC set, the most 57162306a36Sopenharmony_ci * significant bit of the last byte returned from the CC2520 57262306a36Sopenharmony_ci * is CRC_OK flag. See section 20.3.4 of the datasheet. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci crc_ok = skb->data[len - 1] & BIT(7); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* If we failed CRC drop the packet in the driver layer. */ 57762306a36Sopenharmony_ci if (!crc_ok) { 57862306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "CRC check failed\n"); 57962306a36Sopenharmony_ci kfree_skb(skb); 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* To calculate LQI, the lower 7 bits of the last byte (the 58462306a36Sopenharmony_ci * correlation value provided by the radio) must be scaled to 58562306a36Sopenharmony_ci * the range 0-255. According to section 20.6, the correlation 58662306a36Sopenharmony_ci * value ranges from 50-110. Ideally this would be calibrated 58762306a36Sopenharmony_ci * per hardware design, but we use roughly the datasheet values 58862306a36Sopenharmony_ci * to get close enough while avoiding floating point. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci lqi = skb->data[len - 1] & 0x7f; 59162306a36Sopenharmony_ci if (lqi < 50) 59262306a36Sopenharmony_ci lqi = 50; 59362306a36Sopenharmony_ci else if (lqi > 113) 59462306a36Sopenharmony_ci lqi = 113; 59562306a36Sopenharmony_ci lqi = (lqi - 50) * 4; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ieee802154_rx_irqsafe(priv->hw, skb, lqi); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int 60662306a36Sopenharmony_cicc2520_ed(struct ieee802154_hw *hw, u8 *level) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 60962306a36Sopenharmony_ci u8 status = 0xff; 61062306a36Sopenharmony_ci u8 rssi; 61162306a36Sopenharmony_ci int ret; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status); 61462306a36Sopenharmony_ci if (ret) 61562306a36Sopenharmony_ci return ret; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (status != RSSI_VALID) 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = cc2520_read_register(priv, CC2520_RSSI, &rssi); 62162306a36Sopenharmony_ci if (ret) 62262306a36Sopenharmony_ci return ret; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */ 62562306a36Sopenharmony_ci *level = rssi - RSSI_OFFSET; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int 63162306a36Sopenharmony_cicc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 63462306a36Sopenharmony_ci int ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "trying to set channel\n"); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci WARN_ON(page != 0); 63962306a36Sopenharmony_ci WARN_ON(channel < CC2520_MINCHANNEL); 64062306a36Sopenharmony_ci WARN_ON(channel > CC2520_MAXCHANNEL); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FREQCTRL, 64362306a36Sopenharmony_ci 11 + 5 * (channel - 11)); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return ret; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic int 64962306a36Sopenharmony_cicc2520_filter(struct ieee802154_hw *hw, 65062306a36Sopenharmony_ci struct ieee802154_hw_addr_filt *filt, unsigned long changed) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 65362306a36Sopenharmony_ci int ret = 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_PANID_CHANGED) { 65662306a36Sopenharmony_ci u16 panid = le16_to_cpu(filt->pan_id); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "%s called for pan id\n", __func__); 65962306a36Sopenharmony_ci ret = cc2520_write_ram(priv, CC2520RAM_PANID, 66062306a36Sopenharmony_ci sizeof(panid), (u8 *)&panid); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { 66462306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 66562306a36Sopenharmony_ci "%s called for IEEE addr\n", __func__); 66662306a36Sopenharmony_ci ret = cc2520_write_ram(priv, CC2520RAM_IEEEADDR, 66762306a36Sopenharmony_ci sizeof(filt->ieee_addr), 66862306a36Sopenharmony_ci (u8 *)&filt->ieee_addr); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_SADDR_CHANGED) { 67262306a36Sopenharmony_ci u16 addr = le16_to_cpu(filt->short_addr); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "%s called for saddr\n", __func__); 67562306a36Sopenharmony_ci ret = cc2520_write_ram(priv, CC2520RAM_SHORTADDR, 67662306a36Sopenharmony_ci sizeof(addr), (u8 *)&addr); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_PANC_CHANGED) { 68062306a36Sopenharmony_ci u8 frmfilt0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, 68362306a36Sopenharmony_ci "%s called for panc change\n", __func__); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (filt->pan_coord) 68862306a36Sopenharmony_ci frmfilt0 |= FRMFILT0_PAN_COORDINATOR; 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return ret; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic inline int cc2520_set_tx_power(struct cc2520_private *priv, s32 mbm) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci u8 power; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci switch (mbm) { 70362306a36Sopenharmony_ci case 500: 70462306a36Sopenharmony_ci power = 0xF7; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case 300: 70762306a36Sopenharmony_ci power = 0xF2; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case 200: 71062306a36Sopenharmony_ci power = 0xAB; 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci case 100: 71362306a36Sopenharmony_ci power = 0x13; 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci case 0: 71662306a36Sopenharmony_ci power = 0x32; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci case -200: 71962306a36Sopenharmony_ci power = 0x81; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci case -400: 72262306a36Sopenharmony_ci power = 0x88; 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case -700: 72562306a36Sopenharmony_ci power = 0x2C; 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci case -1800: 72862306a36Sopenharmony_ci power = 0x03; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci default: 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return cc2520_write_register(priv, CC2520_TXPOWER, power); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic inline int cc2520_cc2591_set_tx_power(struct cc2520_private *priv, 73862306a36Sopenharmony_ci s32 mbm) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci u8 power; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci switch (mbm) { 74362306a36Sopenharmony_ci case 1700: 74462306a36Sopenharmony_ci power = 0xF9; 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci case 1600: 74762306a36Sopenharmony_ci power = 0xF0; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case 1400: 75062306a36Sopenharmony_ci power = 0xA0; 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci case 1100: 75362306a36Sopenharmony_ci power = 0x2C; 75462306a36Sopenharmony_ci break; 75562306a36Sopenharmony_ci case -100: 75662306a36Sopenharmony_ci power = 0x03; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case -800: 75962306a36Sopenharmony_ci power = 0x01; 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci default: 76262306a36Sopenharmony_ci return -EINVAL; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return cc2520_write_register(priv, CC2520_TXPOWER, power); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci#define CC2520_MAX_TX_POWERS 0x8 76962306a36Sopenharmony_cistatic const s32 cc2520_powers[CC2520_MAX_TX_POWERS + 1] = { 77062306a36Sopenharmony_ci 500, 300, 200, 100, 0, -200, -400, -700, -1800, 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci#define CC2520_CC2591_MAX_TX_POWERS 0x5 77462306a36Sopenharmony_cistatic const s32 cc2520_cc2591_powers[CC2520_CC2591_MAX_TX_POWERS + 1] = { 77562306a36Sopenharmony_ci 1700, 1600, 1400, 1100, -100, -800, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic int 77962306a36Sopenharmony_cicc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (!priv->amplified) 78462306a36Sopenharmony_ci return cc2520_set_tx_power(priv, mbm); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return cc2520_cc2591_set_tx_power(priv, mbm); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic int 79062306a36Sopenharmony_cicc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct cc2520_private *priv = hw->priv; 79362306a36Sopenharmony_ci u8 frmfilt0; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci priv->promiscuous = on; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (on) { 80262306a36Sopenharmony_ci /* Disable automatic ACK, automatic CRC, and frame filtering. */ 80362306a36Sopenharmony_ci cc2520_write_register(priv, CC2520_FRMCTRL0, 0); 80462306a36Sopenharmony_ci frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN; 80562306a36Sopenharmony_ci } else { 80662306a36Sopenharmony_ci cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK | 80762306a36Sopenharmony_ci FRMCTRL0_AUTOCRC); 80862306a36Sopenharmony_ci frmfilt0 |= FRMFILT0_FRAME_FILTER_EN; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic const struct ieee802154_ops cc2520_ops = { 81462306a36Sopenharmony_ci .owner = THIS_MODULE, 81562306a36Sopenharmony_ci .start = cc2520_start, 81662306a36Sopenharmony_ci .stop = cc2520_stop, 81762306a36Sopenharmony_ci .xmit_sync = cc2520_tx, 81862306a36Sopenharmony_ci .ed = cc2520_ed, 81962306a36Sopenharmony_ci .set_channel = cc2520_set_channel, 82062306a36Sopenharmony_ci .set_hw_addr_filt = cc2520_filter, 82162306a36Sopenharmony_ci .set_txpower = cc2520_set_txpower, 82262306a36Sopenharmony_ci .set_promiscuous_mode = cc2520_set_promiscuous_mode, 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int cc2520_register(struct cc2520_private *priv) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int ret = -ENOMEM; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops); 83062306a36Sopenharmony_ci if (!priv->hw) 83162306a36Sopenharmony_ci goto err_ret; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci priv->hw->priv = priv; 83462306a36Sopenharmony_ci priv->hw->parent = &priv->spi->dev; 83562306a36Sopenharmony_ci priv->hw->extra_tx_headroom = 0; 83662306a36Sopenharmony_ci ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* We do support only 2.4 Ghz */ 83962306a36Sopenharmony_ci priv->hw->phy->supported.channels[0] = 0x7FFF800; 84062306a36Sopenharmony_ci priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | 84162306a36Sopenharmony_ci IEEE802154_HW_PROMISCUOUS; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (!priv->amplified) { 84662306a36Sopenharmony_ci priv->hw->phy->supported.tx_powers = cc2520_powers; 84762306a36Sopenharmony_ci priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_powers); 84862306a36Sopenharmony_ci priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[4]; 84962306a36Sopenharmony_ci } else { 85062306a36Sopenharmony_ci priv->hw->phy->supported.tx_powers = cc2520_cc2591_powers; 85162306a36Sopenharmony_ci priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_cc2591_powers); 85262306a36Sopenharmony_ci priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[0]; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci priv->hw->phy->current_channel = 11; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "registered cc2520\n"); 85862306a36Sopenharmony_ci ret = ieee802154_register_hw(priv->hw); 85962306a36Sopenharmony_ci if (ret) 86062306a36Sopenharmony_ci goto err_free_device; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cierr_free_device: 86562306a36Sopenharmony_ci ieee802154_free_hw(priv->hw); 86662306a36Sopenharmony_cierr_ret: 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic void cc2520_fifop_irqwork(struct work_struct *work) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct cc2520_private *priv 87362306a36Sopenharmony_ci = container_of(work, struct cc2520_private, fifop_irqwork); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "fifop interrupt received\n"); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (gpiod_get_value(priv->fifo_pin)) 87862306a36Sopenharmony_ci cc2520_rx(priv); 87962306a36Sopenharmony_ci else 88062306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "rxfifo overflow\n"); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); 88362306a36Sopenharmony_ci cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic irqreturn_t cc2520_fifop_isr(int irq, void *data) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct cc2520_private *priv = data; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci schedule_work(&priv->fifop_irqwork); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return IRQ_HANDLED; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic irqreturn_t cc2520_sfd_isr(int irq, void *data) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct cc2520_private *priv = data; 89862306a36Sopenharmony_ci unsigned long flags; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 90162306a36Sopenharmony_ci if (priv->is_tx) { 90262306a36Sopenharmony_ci priv->is_tx = 0; 90362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 90462306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "SFD for TX\n"); 90562306a36Sopenharmony_ci complete(&priv->tx_complete); 90662306a36Sopenharmony_ci } else { 90762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 90862306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "SFD for RX\n"); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return IRQ_HANDLED; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int cc2520_hw_init(struct cc2520_private *priv) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci u8 status = 0, state = 0xff; 91762306a36Sopenharmony_ci int ret; 91862306a36Sopenharmony_ci int timeout = 100; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); 92162306a36Sopenharmony_ci if (ret) 92262306a36Sopenharmony_ci goto err_ret; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (state != STATE_IDLE) 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci do { 92862306a36Sopenharmony_ci ret = cc2520_get_status(priv, &status); 92962306a36Sopenharmony_ci if (ret) 93062306a36Sopenharmony_ci goto err_ret; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (timeout-- <= 0) { 93362306a36Sopenharmony_ci dev_err(&priv->spi->dev, "oscillator start failed!\n"); 93462306a36Sopenharmony_ci return -ETIMEDOUT; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci udelay(1); 93762306a36Sopenharmony_ci } while (!(status & CC2520_STATUS_XOSC32M_STABLE)); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* If the CC2520 is connected to a CC2591 amplifier, we must both 94262306a36Sopenharmony_ci * configure GPIOs on the CC2520 to correctly configure the CC2591 94362306a36Sopenharmony_ci * and change a couple settings of the CC2520 to work with the 94462306a36Sopenharmony_ci * amplifier. See section 8 page 17 of TI application note AN065. 94562306a36Sopenharmony_ci * http://www.ti.com/lit/an/swra229a/swra229a.pdf 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_ci if (priv->amplified) { 94862306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16); 94962306a36Sopenharmony_ci if (ret) 95062306a36Sopenharmony_ci goto err_ret; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46); 95362306a36Sopenharmony_ci if (ret) 95462306a36Sopenharmony_ci goto err_ret; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47); 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci goto err_ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e); 96162306a36Sopenharmony_ci if (ret) 96262306a36Sopenharmony_ci goto err_ret; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1); 96562306a36Sopenharmony_ci if (ret) 96662306a36Sopenharmony_ci goto err_ret; 96762306a36Sopenharmony_ci } else { 96862306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); 96962306a36Sopenharmony_ci if (ret) 97062306a36Sopenharmony_ci goto err_ret; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Registers default value: section 28.1 in Datasheet */ 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* Set the CCA threshold to -50 dBm. This seems to have been copied 97662306a36Sopenharmony_ci * from the TinyOS CC2520 driver and is much higher than the -84 dBm 97762306a36Sopenharmony_ci * threshold suggested in the datasheet. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); 98062306a36Sopenharmony_ci if (ret) 98162306a36Sopenharmony_ci goto err_ret; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85); 98462306a36Sopenharmony_ci if (ret) 98562306a36Sopenharmony_ci goto err_ret; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14); 98862306a36Sopenharmony_ci if (ret) 98962306a36Sopenharmony_ci goto err_ret; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f); 99262306a36Sopenharmony_ci if (ret) 99362306a36Sopenharmony_ci goto err_ret; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a); 99662306a36Sopenharmony_ci if (ret) 99762306a36Sopenharmony_ci goto err_ret; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b); 100062306a36Sopenharmony_ci if (ret) 100162306a36Sopenharmony_ci goto err_ret; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); 100462306a36Sopenharmony_ci if (ret) 100562306a36Sopenharmony_ci goto err_ret; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e); 100862306a36Sopenharmony_ci if (ret) 100962306a36Sopenharmony_ci goto err_ret; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03); 101262306a36Sopenharmony_ci if (ret) 101362306a36Sopenharmony_ci goto err_ret; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* Configure registers correctly for this driver. */ 101662306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 101762306a36Sopenharmony_ci FRMCTRL1_SET_RXENMASK_ON_TX | 101862306a36Sopenharmony_ci FRMCTRL1_IGNORE_TX_UNDERF); 101962306a36Sopenharmony_ci if (ret) 102062306a36Sopenharmony_ci goto err_ret; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127); 102362306a36Sopenharmony_ci if (ret) 102462306a36Sopenharmony_ci goto err_ret; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cierr_ret: 102962306a36Sopenharmony_ci return ret; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int cc2520_probe(struct spi_device *spi) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct cc2520_private *priv; 103562306a36Sopenharmony_ci struct gpio_desc *fifop; 103662306a36Sopenharmony_ci struct gpio_desc *cca; 103762306a36Sopenharmony_ci struct gpio_desc *sfd; 103862306a36Sopenharmony_ci struct gpio_desc *reset; 103962306a36Sopenharmony_ci struct gpio_desc *vreg; 104062306a36Sopenharmony_ci int ret; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); 104362306a36Sopenharmony_ci if (!priv) 104462306a36Sopenharmony_ci return -ENOMEM; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci spi_set_drvdata(spi, priv); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* CC2591 front end for CC2520 */ 104962306a36Sopenharmony_ci /* Assumption that CC2591 is not connected */ 105062306a36Sopenharmony_ci priv->amplified = false; 105162306a36Sopenharmony_ci if (device_property_read_bool(&spi->dev, "amplified")) 105262306a36Sopenharmony_ci priv->amplified = true; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci priv->spi = spi; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci priv->buf = devm_kzalloc(&spi->dev, 105762306a36Sopenharmony_ci SPI_COMMAND_BUFFER, GFP_KERNEL); 105862306a36Sopenharmony_ci if (!priv->buf) 105962306a36Sopenharmony_ci return -ENOMEM; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci mutex_init(&priv->buffer_mutex); 106262306a36Sopenharmony_ci INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); 106362306a36Sopenharmony_ci spin_lock_init(&priv->lock); 106462306a36Sopenharmony_ci init_completion(&priv->tx_complete); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* Request all the gpio's */ 106762306a36Sopenharmony_ci priv->fifo_pin = devm_gpiod_get(&spi->dev, "fifo", GPIOD_IN); 106862306a36Sopenharmony_ci if (IS_ERR(priv->fifo_pin)) { 106962306a36Sopenharmony_ci dev_err(&spi->dev, "fifo gpio is not valid\n"); 107062306a36Sopenharmony_ci ret = PTR_ERR(priv->fifo_pin); 107162306a36Sopenharmony_ci goto err_hw_init; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci cca = devm_gpiod_get(&spi->dev, "cca", GPIOD_IN); 107562306a36Sopenharmony_ci if (IS_ERR(cca)) { 107662306a36Sopenharmony_ci dev_err(&spi->dev, "cca gpio is not valid\n"); 107762306a36Sopenharmony_ci ret = PTR_ERR(cca); 107862306a36Sopenharmony_ci goto err_hw_init; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci fifop = devm_gpiod_get(&spi->dev, "fifop", GPIOD_IN); 108262306a36Sopenharmony_ci if (IS_ERR(fifop)) { 108362306a36Sopenharmony_ci dev_err(&spi->dev, "fifop gpio is not valid\n"); 108462306a36Sopenharmony_ci ret = PTR_ERR(fifop); 108562306a36Sopenharmony_ci goto err_hw_init; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci sfd = devm_gpiod_get(&spi->dev, "sfd", GPIOD_IN); 108962306a36Sopenharmony_ci if (IS_ERR(sfd)) { 109062306a36Sopenharmony_ci dev_err(&spi->dev, "sfd gpio is not valid\n"); 109162306a36Sopenharmony_ci ret = PTR_ERR(sfd); 109262306a36Sopenharmony_ci goto err_hw_init; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); 109662306a36Sopenharmony_ci if (IS_ERR(reset)) { 109762306a36Sopenharmony_ci dev_err(&spi->dev, "reset gpio is not valid\n"); 109862306a36Sopenharmony_ci ret = PTR_ERR(reset); 109962306a36Sopenharmony_ci goto err_hw_init; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci vreg = devm_gpiod_get(&spi->dev, "vreg", GPIOD_OUT_LOW); 110362306a36Sopenharmony_ci if (IS_ERR(vreg)) { 110462306a36Sopenharmony_ci dev_err(&spi->dev, "vreg gpio is not valid\n"); 110562306a36Sopenharmony_ci ret = PTR_ERR(vreg); 110662306a36Sopenharmony_ci goto err_hw_init; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci gpiod_set_value(vreg, HIGH); 111062306a36Sopenharmony_ci usleep_range(100, 150); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci gpiod_set_value(reset, HIGH); 111362306a36Sopenharmony_ci usleep_range(200, 250); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci ret = cc2520_hw_init(priv); 111662306a36Sopenharmony_ci if (ret) 111762306a36Sopenharmony_ci goto err_hw_init; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* Set up fifop interrupt */ 112062306a36Sopenharmony_ci ret = devm_request_irq(&spi->dev, 112162306a36Sopenharmony_ci gpiod_to_irq(fifop), 112262306a36Sopenharmony_ci cc2520_fifop_isr, 112362306a36Sopenharmony_ci IRQF_TRIGGER_RISING, 112462306a36Sopenharmony_ci dev_name(&spi->dev), 112562306a36Sopenharmony_ci priv); 112662306a36Sopenharmony_ci if (ret) { 112762306a36Sopenharmony_ci dev_err(&spi->dev, "could not get fifop irq\n"); 112862306a36Sopenharmony_ci goto err_hw_init; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* Set up sfd interrupt */ 113262306a36Sopenharmony_ci ret = devm_request_irq(&spi->dev, 113362306a36Sopenharmony_ci gpiod_to_irq(sfd), 113462306a36Sopenharmony_ci cc2520_sfd_isr, 113562306a36Sopenharmony_ci IRQF_TRIGGER_FALLING, 113662306a36Sopenharmony_ci dev_name(&spi->dev), 113762306a36Sopenharmony_ci priv); 113862306a36Sopenharmony_ci if (ret) { 113962306a36Sopenharmony_ci dev_err(&spi->dev, "could not get sfd irq\n"); 114062306a36Sopenharmony_ci goto err_hw_init; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci ret = cc2520_register(priv); 114462306a36Sopenharmony_ci if (ret) 114562306a36Sopenharmony_ci goto err_hw_init; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return 0; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cierr_hw_init: 115062306a36Sopenharmony_ci mutex_destroy(&priv->buffer_mutex); 115162306a36Sopenharmony_ci flush_work(&priv->fifop_irqwork); 115262306a36Sopenharmony_ci return ret; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic void cc2520_remove(struct spi_device *spi) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci struct cc2520_private *priv = spi_get_drvdata(spi); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci mutex_destroy(&priv->buffer_mutex); 116062306a36Sopenharmony_ci flush_work(&priv->fifop_irqwork); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci ieee802154_unregister_hw(priv->hw); 116362306a36Sopenharmony_ci ieee802154_free_hw(priv->hw); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_cistatic const struct spi_device_id cc2520_ids[] = { 116762306a36Sopenharmony_ci {"cc2520", }, 116862306a36Sopenharmony_ci {}, 116962306a36Sopenharmony_ci}; 117062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, cc2520_ids); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic const struct of_device_id cc2520_of_ids[] = { 117362306a36Sopenharmony_ci {.compatible = "ti,cc2520", }, 117462306a36Sopenharmony_ci {}, 117562306a36Sopenharmony_ci}; 117662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cc2520_of_ids); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/* SPI driver structure */ 117962306a36Sopenharmony_cistatic struct spi_driver cc2520_driver = { 118062306a36Sopenharmony_ci .driver = { 118162306a36Sopenharmony_ci .name = "cc2520", 118262306a36Sopenharmony_ci .of_match_table = cc2520_of_ids, 118362306a36Sopenharmony_ci }, 118462306a36Sopenharmony_ci .id_table = cc2520_ids, 118562306a36Sopenharmony_ci .probe = cc2520_probe, 118662306a36Sopenharmony_ci .remove = cc2520_remove, 118762306a36Sopenharmony_ci}; 118862306a36Sopenharmony_cimodule_spi_driver(cc2520_driver); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ciMODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>"); 119162306a36Sopenharmony_ciMODULE_DESCRIPTION("CC2520 Transceiver Driver"); 119262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1193