18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in>
58c2ecf20Sopenharmony_ci *		      Md.Jamal Mohiuddin <mjmohiuddin@cdac.in>
68c2ecf20Sopenharmony_ci *		      P Sowjanya <sowjanyap@cdac.in>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/gpio.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
138c2ecf20Sopenharmony_ci#include <linux/spi/cc2520.h>
148c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
188c2ecf20Sopenharmony_ci#include <linux/ieee802154.h>
198c2ecf20Sopenharmony_ci#include <linux/crc-ccitt.h>
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <net/mac802154.h>
238c2ecf20Sopenharmony_ci#include <net/cfg802154.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define	SPI_COMMAND_BUFFER	3
268c2ecf20Sopenharmony_ci#define	HIGH			1
278c2ecf20Sopenharmony_ci#define	LOW			0
288c2ecf20Sopenharmony_ci#define	STATE_IDLE		0
298c2ecf20Sopenharmony_ci#define	RSSI_VALID		0
308c2ecf20Sopenharmony_ci#define	RSSI_OFFSET		78
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define	CC2520_RAM_SIZE		640
338c2ecf20Sopenharmony_ci#define	CC2520_FIFO_SIZE	128
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define	CC2520RAM_TXFIFO	0x100
368c2ecf20Sopenharmony_ci#define	CC2520RAM_RXFIFO	0x180
378c2ecf20Sopenharmony_ci#define	CC2520RAM_IEEEADDR	0x3EA
388c2ecf20Sopenharmony_ci#define	CC2520RAM_PANID		0x3F2
398c2ecf20Sopenharmony_ci#define	CC2520RAM_SHORTADDR	0x3F4
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define	CC2520_FREG_MASK	0x3F
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* status byte values */
448c2ecf20Sopenharmony_ci#define	CC2520_STATUS_XOSC32M_STABLE	BIT(7)
458c2ecf20Sopenharmony_ci#define	CC2520_STATUS_RSSI_VALID	BIT(6)
468c2ecf20Sopenharmony_ci#define	CC2520_STATUS_TX_UNDERFLOW	BIT(3)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */
498c2ecf20Sopenharmony_ci#define	CC2520_MINCHANNEL		11
508c2ecf20Sopenharmony_ci#define	CC2520_MAXCHANNEL		26
518c2ecf20Sopenharmony_ci#define	CC2520_CHANNEL_SPACING		5
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* command strobes */
548c2ecf20Sopenharmony_ci#define	CC2520_CMD_SNOP			0x00
558c2ecf20Sopenharmony_ci#define	CC2520_CMD_IBUFLD		0x02
568c2ecf20Sopenharmony_ci#define	CC2520_CMD_SIBUFEX		0x03
578c2ecf20Sopenharmony_ci#define	CC2520_CMD_SSAMPLECCA		0x04
588c2ecf20Sopenharmony_ci#define	CC2520_CMD_SRES			0x0f
598c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMORY_MASK		0x0f
608c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMORY_READ		0x10
618c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMORY_WRITE		0x20
628c2ecf20Sopenharmony_ci#define	CC2520_CMD_RXBUF		0x30
638c2ecf20Sopenharmony_ci#define	CC2520_CMD_RXBUFCP		0x38
648c2ecf20Sopenharmony_ci#define	CC2520_CMD_RXBUFMOV		0x32
658c2ecf20Sopenharmony_ci#define	CC2520_CMD_TXBUF		0x3A
668c2ecf20Sopenharmony_ci#define	CC2520_CMD_TXBUFCP		0x3E
678c2ecf20Sopenharmony_ci#define	CC2520_CMD_RANDOM		0x3C
688c2ecf20Sopenharmony_ci#define	CC2520_CMD_SXOSCON		0x40
698c2ecf20Sopenharmony_ci#define	CC2520_CMD_STXCAL		0x41
708c2ecf20Sopenharmony_ci#define	CC2520_CMD_SRXON		0x42
718c2ecf20Sopenharmony_ci#define	CC2520_CMD_STXON		0x43
728c2ecf20Sopenharmony_ci#define	CC2520_CMD_STXONCCA		0x44
738c2ecf20Sopenharmony_ci#define	CC2520_CMD_SRFOFF		0x45
748c2ecf20Sopenharmony_ci#define	CC2520_CMD_SXOSCOFF		0x46
758c2ecf20Sopenharmony_ci#define	CC2520_CMD_SFLUSHRX		0x47
768c2ecf20Sopenharmony_ci#define	CC2520_CMD_SFLUSHTX		0x48
778c2ecf20Sopenharmony_ci#define	CC2520_CMD_SACK			0x49
788c2ecf20Sopenharmony_ci#define	CC2520_CMD_SACKPEND		0x4A
798c2ecf20Sopenharmony_ci#define	CC2520_CMD_SNACK		0x4B
808c2ecf20Sopenharmony_ci#define	CC2520_CMD_SRXMASKBITSET	0x4C
818c2ecf20Sopenharmony_ci#define	CC2520_CMD_SRXMASKBITCLR	0x4D
828c2ecf20Sopenharmony_ci#define	CC2520_CMD_RXMASKAND		0x4E
838c2ecf20Sopenharmony_ci#define	CC2520_CMD_RXMASKOR		0x4F
848c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMCP		0x50
858c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMCPR		0x52
868c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMXCP		0x54
878c2ecf20Sopenharmony_ci#define	CC2520_CMD_MEMXWR		0x56
888c2ecf20Sopenharmony_ci#define	CC2520_CMD_BCLR			0x58
898c2ecf20Sopenharmony_ci#define	CC2520_CMD_BSET			0x59
908c2ecf20Sopenharmony_ci#define	CC2520_CMD_CTR_UCTR		0x60
918c2ecf20Sopenharmony_ci#define	CC2520_CMD_CBCMAC		0x64
928c2ecf20Sopenharmony_ci#define	CC2520_CMD_UCBCMAC		0x66
938c2ecf20Sopenharmony_ci#define	CC2520_CMD_CCM			0x68
948c2ecf20Sopenharmony_ci#define	CC2520_CMD_UCCM			0x6A
958c2ecf20Sopenharmony_ci#define	CC2520_CMD_ECB			0x70
968c2ecf20Sopenharmony_ci#define	CC2520_CMD_ECBO			0x72
978c2ecf20Sopenharmony_ci#define	CC2520_CMD_ECBX			0x74
988c2ecf20Sopenharmony_ci#define	CC2520_CMD_INC			0x78
998c2ecf20Sopenharmony_ci#define	CC2520_CMD_ABORT		0x7F
1008c2ecf20Sopenharmony_ci#define	CC2520_CMD_REGISTER_READ	0x80
1018c2ecf20Sopenharmony_ci#define	CC2520_CMD_REGISTER_WRITE	0xC0
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* status registers */
1048c2ecf20Sopenharmony_ci#define	CC2520_CHIPID			0x40
1058c2ecf20Sopenharmony_ci#define	CC2520_VERSION			0x42
1068c2ecf20Sopenharmony_ci#define	CC2520_EXTCLOCK			0x44
1078c2ecf20Sopenharmony_ci#define	CC2520_MDMCTRL0			0x46
1088c2ecf20Sopenharmony_ci#define	CC2520_MDMCTRL1			0x47
1098c2ecf20Sopenharmony_ci#define	CC2520_FREQEST			0x48
1108c2ecf20Sopenharmony_ci#define	CC2520_RXCTRL			0x4A
1118c2ecf20Sopenharmony_ci#define	CC2520_FSCTRL			0x4C
1128c2ecf20Sopenharmony_ci#define	CC2520_FSCAL0			0x4E
1138c2ecf20Sopenharmony_ci#define	CC2520_FSCAL1			0x4F
1148c2ecf20Sopenharmony_ci#define	CC2520_FSCAL2			0x50
1158c2ecf20Sopenharmony_ci#define	CC2520_FSCAL3			0x51
1168c2ecf20Sopenharmony_ci#define	CC2520_AGCCTRL0			0x52
1178c2ecf20Sopenharmony_ci#define	CC2520_AGCCTRL1			0x53
1188c2ecf20Sopenharmony_ci#define	CC2520_AGCCTRL2			0x54
1198c2ecf20Sopenharmony_ci#define	CC2520_AGCCTRL3			0x55
1208c2ecf20Sopenharmony_ci#define	CC2520_ADCTEST0			0x56
1218c2ecf20Sopenharmony_ci#define	CC2520_ADCTEST1			0x57
1228c2ecf20Sopenharmony_ci#define	CC2520_ADCTEST2			0x58
1238c2ecf20Sopenharmony_ci#define	CC2520_MDMTEST0			0x5A
1248c2ecf20Sopenharmony_ci#define	CC2520_MDMTEST1			0x5B
1258c2ecf20Sopenharmony_ci#define	CC2520_DACTEST0			0x5C
1268c2ecf20Sopenharmony_ci#define	CC2520_DACTEST1			0x5D
1278c2ecf20Sopenharmony_ci#define	CC2520_ATEST			0x5E
1288c2ecf20Sopenharmony_ci#define	CC2520_DACTEST2			0x5F
1298c2ecf20Sopenharmony_ci#define	CC2520_PTEST0			0x60
1308c2ecf20Sopenharmony_ci#define	CC2520_PTEST1			0x61
1318c2ecf20Sopenharmony_ci#define	CC2520_RESERVED			0x62
1328c2ecf20Sopenharmony_ci#define	CC2520_DPUBIST			0x7A
1338c2ecf20Sopenharmony_ci#define	CC2520_ACTBIST			0x7C
1348c2ecf20Sopenharmony_ci#define	CC2520_RAMBIST			0x7E
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* frame registers */
1378c2ecf20Sopenharmony_ci#define	CC2520_FRMFILT0			0x00
1388c2ecf20Sopenharmony_ci#define	CC2520_FRMFILT1			0x01
1398c2ecf20Sopenharmony_ci#define	CC2520_SRCMATCH			0x02
1408c2ecf20Sopenharmony_ci#define	CC2520_SRCSHORTEN0		0x04
1418c2ecf20Sopenharmony_ci#define	CC2520_SRCSHORTEN1		0x05
1428c2ecf20Sopenharmony_ci#define	CC2520_SRCSHORTEN2		0x06
1438c2ecf20Sopenharmony_ci#define	CC2520_SRCEXTEN0		0x08
1448c2ecf20Sopenharmony_ci#define	CC2520_SRCEXTEN1		0x09
1458c2ecf20Sopenharmony_ci#define	CC2520_SRCEXTEN2		0x0A
1468c2ecf20Sopenharmony_ci#define	CC2520_FRMCTRL0			0x0C
1478c2ecf20Sopenharmony_ci#define	CC2520_FRMCTRL1			0x0D
1488c2ecf20Sopenharmony_ci#define	CC2520_RXENABLE0		0x0E
1498c2ecf20Sopenharmony_ci#define	CC2520_RXENABLE1		0x0F
1508c2ecf20Sopenharmony_ci#define	CC2520_EXCFLAG0			0x10
1518c2ecf20Sopenharmony_ci#define	CC2520_EXCFLAG1			0x11
1528c2ecf20Sopenharmony_ci#define	CC2520_EXCFLAG2			0x12
1538c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKA0		0x14
1548c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKA1		0x15
1558c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKA2		0x16
1568c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKB0		0x18
1578c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKB1		0x19
1588c2ecf20Sopenharmony_ci#define	CC2520_EXCMASKB2		0x1A
1598c2ecf20Sopenharmony_ci#define	CC2520_EXCBINDX0		0x1C
1608c2ecf20Sopenharmony_ci#define	CC2520_EXCBINDX1		0x1D
1618c2ecf20Sopenharmony_ci#define	CC2520_EXCBINDY0		0x1E
1628c2ecf20Sopenharmony_ci#define	CC2520_EXCBINDY1		0x1F
1638c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL0		0x20
1648c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL1		0x21
1658c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL2		0x22
1668c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL3		0x23
1678c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL4		0x24
1688c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL5		0x25
1698c2ecf20Sopenharmony_ci#define	CC2520_GPIOPOLARITY		0x26
1708c2ecf20Sopenharmony_ci#define	CC2520_GPIOCTRL			0x28
1718c2ecf20Sopenharmony_ci#define	CC2520_DPUCON			0x2A
1728c2ecf20Sopenharmony_ci#define	CC2520_DPUSTAT			0x2C
1738c2ecf20Sopenharmony_ci#define	CC2520_FREQCTRL			0x2E
1748c2ecf20Sopenharmony_ci#define	CC2520_FREQTUNE			0x2F
1758c2ecf20Sopenharmony_ci#define	CC2520_TXPOWER			0x30
1768c2ecf20Sopenharmony_ci#define	CC2520_TXCTRL			0x31
1778c2ecf20Sopenharmony_ci#define	CC2520_FSMSTAT0			0x32
1788c2ecf20Sopenharmony_ci#define	CC2520_FSMSTAT1			0x33
1798c2ecf20Sopenharmony_ci#define	CC2520_FIFOPCTRL		0x34
1808c2ecf20Sopenharmony_ci#define	CC2520_FSMCTRL			0x35
1818c2ecf20Sopenharmony_ci#define	CC2520_CCACTRL0			0x36
1828c2ecf20Sopenharmony_ci#define	CC2520_CCACTRL1			0x37
1838c2ecf20Sopenharmony_ci#define	CC2520_RSSI			0x38
1848c2ecf20Sopenharmony_ci#define	CC2520_RSSISTAT			0x39
1858c2ecf20Sopenharmony_ci#define	CC2520_RXFIRST			0x3C
1868c2ecf20Sopenharmony_ci#define	CC2520_RXFIFOCNT		0x3E
1878c2ecf20Sopenharmony_ci#define	CC2520_TXFIFOCNT		0x3F
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/* CC2520_FRMFILT0 */
1908c2ecf20Sopenharmony_ci#define FRMFILT0_FRAME_FILTER_EN	BIT(0)
1918c2ecf20Sopenharmony_ci#define FRMFILT0_PAN_COORDINATOR	BIT(1)
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* CC2520_FRMCTRL0 */
1948c2ecf20Sopenharmony_ci#define FRMCTRL0_AUTOACK		BIT(5)
1958c2ecf20Sopenharmony_ci#define FRMCTRL0_AUTOCRC		BIT(6)
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* CC2520_FRMCTRL1 */
1988c2ecf20Sopenharmony_ci#define FRMCTRL1_SET_RXENMASK_ON_TX	BIT(0)
1998c2ecf20Sopenharmony_ci#define FRMCTRL1_IGNORE_TX_UNDERF	BIT(1)
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* Driver private information */
2028c2ecf20Sopenharmony_cistruct cc2520_private {
2038c2ecf20Sopenharmony_ci	struct spi_device *spi;		/* SPI device structure */
2048c2ecf20Sopenharmony_ci	struct ieee802154_hw *hw;	/* IEEE-802.15.4 device */
2058c2ecf20Sopenharmony_ci	u8 *buf;			/* SPI TX/Rx data buffer */
2068c2ecf20Sopenharmony_ci	struct mutex buffer_mutex;	/* SPI buffer mutex */
2078c2ecf20Sopenharmony_ci	bool is_tx;			/* Flag for sync b/w Tx and Rx */
2088c2ecf20Sopenharmony_ci	bool amplified;			/* Flag for CC2591 */
2098c2ecf20Sopenharmony_ci	int fifo_pin;			/* FIFO GPIO pin number */
2108c2ecf20Sopenharmony_ci	struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
2118c2ecf20Sopenharmony_ci	spinlock_t lock;		/* Lock for is_tx*/
2128c2ecf20Sopenharmony_ci	struct completion tx_complete;	/* Work completion for Tx */
2138c2ecf20Sopenharmony_ci	bool promiscuous;               /* Flag for promiscuous mode */
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* Generic Functions */
2178c2ecf20Sopenharmony_cistatic int
2188c2ecf20Sopenharmony_cicc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int ret;
2218c2ecf20Sopenharmony_ci	struct spi_message msg;
2228c2ecf20Sopenharmony_ci	struct spi_transfer xfer = {
2238c2ecf20Sopenharmony_ci		.len = 0,
2248c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
2258c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
2268c2ecf20Sopenharmony_ci	};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	spi_message_init(&msg);
2298c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
2328c2ecf20Sopenharmony_ci	priv->buf[xfer.len++] = cmd;
2338c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
2348c2ecf20Sopenharmony_ci		 "command strobe buf[0] = %02x\n",
2358c2ecf20Sopenharmony_ci		 priv->buf[0]);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ret = spi_sync(priv->spi, &msg);
2388c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
2398c2ecf20Sopenharmony_ci		 "buf[0] = %02x\n", priv->buf[0]);
2408c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return ret;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int
2468c2ecf20Sopenharmony_cicc2520_get_status(struct cc2520_private *priv, u8 *status)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	int ret;
2498c2ecf20Sopenharmony_ci	struct spi_message msg;
2508c2ecf20Sopenharmony_ci	struct spi_transfer xfer = {
2518c2ecf20Sopenharmony_ci		.len = 0,
2528c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
2538c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
2548c2ecf20Sopenharmony_ci	};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spi_message_init(&msg);
2578c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
2608c2ecf20Sopenharmony_ci	priv->buf[xfer.len++] = CC2520_CMD_SNOP;
2618c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
2628c2ecf20Sopenharmony_ci		 "get status command buf[0] = %02x\n", priv->buf[0]);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	ret = spi_sync(priv->spi, &msg);
2658c2ecf20Sopenharmony_ci	if (!ret)
2668c2ecf20Sopenharmony_ci		*status = priv->buf[0];
2678c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
2688c2ecf20Sopenharmony_ci		 "buf[0] = %02x\n", priv->buf[0]);
2698c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return ret;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int
2758c2ecf20Sopenharmony_cicc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int status;
2788c2ecf20Sopenharmony_ci	struct spi_message msg;
2798c2ecf20Sopenharmony_ci	struct spi_transfer xfer = {
2808c2ecf20Sopenharmony_ci		.len = 0,
2818c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
2828c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
2838c2ecf20Sopenharmony_ci	};
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spi_message_init(&msg);
2868c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (reg <= CC2520_FREG_MASK) {
2918c2ecf20Sopenharmony_ci		priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg;
2928c2ecf20Sopenharmony_ci		priv->buf[xfer.len++] = value;
2938c2ecf20Sopenharmony_ci	} else {
2948c2ecf20Sopenharmony_ci		priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE;
2958c2ecf20Sopenharmony_ci		priv->buf[xfer.len++] = reg;
2968c2ecf20Sopenharmony_ci		priv->buf[xfer.len++] = value;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	status = spi_sync(priv->spi, &msg);
2998c2ecf20Sopenharmony_ci	if (msg.status)
3008c2ecf20Sopenharmony_ci		status = msg.status;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return status;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int
3088c2ecf20Sopenharmony_cicc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	int status;
3118c2ecf20Sopenharmony_ci	struct spi_message msg;
3128c2ecf20Sopenharmony_ci	struct spi_transfer xfer_head = {
3138c2ecf20Sopenharmony_ci		.len        = 0,
3148c2ecf20Sopenharmony_ci		.tx_buf        = priv->buf,
3158c2ecf20Sopenharmony_ci		.rx_buf        = priv->buf,
3168c2ecf20Sopenharmony_ci	};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	struct spi_transfer xfer_buf = {
3198c2ecf20Sopenharmony_ci		.len = len,
3208c2ecf20Sopenharmony_ci		.tx_buf = data,
3218c2ecf20Sopenharmony_ci	};
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
3248c2ecf20Sopenharmony_ci	priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE |
3258c2ecf20Sopenharmony_ci						((reg >> 8) & 0xff));
3268c2ecf20Sopenharmony_ci	priv->buf[xfer_head.len++] = reg & 0xff;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	spi_message_init(&msg);
3298c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_head, &msg);
3308c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_buf, &msg);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	status = spi_sync(priv->spi, &msg);
3338c2ecf20Sopenharmony_ci	dev_dbg(&priv->spi->dev, "spi status = %d\n", status);
3348c2ecf20Sopenharmony_ci	if (msg.status)
3358c2ecf20Sopenharmony_ci		status = msg.status;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
3388c2ecf20Sopenharmony_ci	return status;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int
3428c2ecf20Sopenharmony_cicc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	int status;
3458c2ecf20Sopenharmony_ci	struct spi_message msg;
3468c2ecf20Sopenharmony_ci	struct spi_transfer xfer1 = {
3478c2ecf20Sopenharmony_ci		.len = 0,
3488c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
3498c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
3508c2ecf20Sopenharmony_ci	};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	struct spi_transfer xfer2 = {
3538c2ecf20Sopenharmony_ci		.len = 1,
3548c2ecf20Sopenharmony_ci		.rx_buf = data,
3558c2ecf20Sopenharmony_ci	};
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	spi_message_init(&msg);
3588c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer1, &msg);
3598c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer2, &msg);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
3628c2ecf20Sopenharmony_ci	priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ;
3638c2ecf20Sopenharmony_ci	priv->buf[xfer1.len++] = reg;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	status = spi_sync(priv->spi, &msg);
3668c2ecf20Sopenharmony_ci	dev_dbg(&priv->spi->dev,
3678c2ecf20Sopenharmony_ci		"spi status = %d\n", status);
3688c2ecf20Sopenharmony_ci	if (msg.status)
3698c2ecf20Sopenharmony_ci		status = msg.status;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return status;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int
3778c2ecf20Sopenharmony_cicc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	int status;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* length byte must include FCS even
3828c2ecf20Sopenharmony_ci	 * if it is calculated in the hardware
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci	int len_byte = pkt_len;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	struct spi_message msg;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	struct spi_transfer xfer_head = {
3898c2ecf20Sopenharmony_ci		.len = 0,
3908c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
3918c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
3928c2ecf20Sopenharmony_ci	};
3938c2ecf20Sopenharmony_ci	struct spi_transfer xfer_len = {
3948c2ecf20Sopenharmony_ci		.len = 1,
3958c2ecf20Sopenharmony_ci		.tx_buf = &len_byte,
3968c2ecf20Sopenharmony_ci	};
3978c2ecf20Sopenharmony_ci	struct spi_transfer xfer_buf = {
3988c2ecf20Sopenharmony_ci		.len = len,
3998c2ecf20Sopenharmony_ci		.tx_buf = data,
4008c2ecf20Sopenharmony_ci	};
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	spi_message_init(&msg);
4038c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_head, &msg);
4048c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_len, &msg);
4058c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_buf, &msg);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
4088c2ecf20Sopenharmony_ci	priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF;
4098c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
4108c2ecf20Sopenharmony_ci		 "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	status = spi_sync(priv->spi, &msg);
4138c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "status = %d\n", status);
4148c2ecf20Sopenharmony_ci	if (msg.status)
4158c2ecf20Sopenharmony_ci		status = msg.status;
4168c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "status = %d\n", status);
4178c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]);
4188c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return status;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int
4248c2ecf20Sopenharmony_cicc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	int status;
4278c2ecf20Sopenharmony_ci	struct spi_message msg;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	struct spi_transfer xfer_head = {
4308c2ecf20Sopenharmony_ci		.len = 0,
4318c2ecf20Sopenharmony_ci		.tx_buf = priv->buf,
4328c2ecf20Sopenharmony_ci		.rx_buf = priv->buf,
4338c2ecf20Sopenharmony_ci	};
4348c2ecf20Sopenharmony_ci	struct spi_transfer xfer_buf = {
4358c2ecf20Sopenharmony_ci		.len = len,
4368c2ecf20Sopenharmony_ci		.rx_buf = data,
4378c2ecf20Sopenharmony_ci	};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	spi_message_init(&msg);
4408c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_head, &msg);
4418c2ecf20Sopenharmony_ci	spi_message_add_tail(&xfer_buf, &msg);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	mutex_lock(&priv->buffer_mutex);
4448c2ecf20Sopenharmony_ci	priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]);
4478c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	status = spi_sync(priv->spi, &msg);
4508c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "status = %d\n", status);
4518c2ecf20Sopenharmony_ci	if (msg.status)
4528c2ecf20Sopenharmony_ci		status = msg.status;
4538c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "status = %d\n", status);
4548c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev,
4558c2ecf20Sopenharmony_ci		 "return status buf[0] = %02x\n", priv->buf[0]);
4568c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	mutex_unlock(&priv->buffer_mutex);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return status;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int cc2520_start(struct ieee802154_hw *hw)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void cc2520_stop(struct ieee802154_hw *hw)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic int
4748c2ecf20Sopenharmony_cicc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
4778c2ecf20Sopenharmony_ci	unsigned long flags;
4788c2ecf20Sopenharmony_ci	int rc;
4798c2ecf20Sopenharmony_ci	u8 status = 0;
4808c2ecf20Sopenharmony_ci	u8 pkt_len;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* In promiscuous mode we disable AUTOCRC so we can get the raw CRC
4838c2ecf20Sopenharmony_ci	 * values on RX. This means we need to manually add the CRC on TX.
4848c2ecf20Sopenharmony_ci	 */
4858c2ecf20Sopenharmony_ci	if (priv->promiscuous) {
4868c2ecf20Sopenharmony_ci		u16 crc = crc_ccitt(0, skb->data, skb->len);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		put_unaligned_le16(crc, skb_put(skb, 2));
4898c2ecf20Sopenharmony_ci		pkt_len = skb->len;
4908c2ecf20Sopenharmony_ci	} else {
4918c2ecf20Sopenharmony_ci		pkt_len = skb->len + 2;
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
4958c2ecf20Sopenharmony_ci	if (rc)
4968c2ecf20Sopenharmony_ci		goto err_tx;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len);
4998c2ecf20Sopenharmony_ci	if (rc)
5008c2ecf20Sopenharmony_ci		goto err_tx;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	rc = cc2520_get_status(priv, &status);
5038c2ecf20Sopenharmony_ci	if (rc)
5048c2ecf20Sopenharmony_ci		goto err_tx;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (status & CC2520_STATUS_TX_UNDERFLOW) {
5078c2ecf20Sopenharmony_ci		rc = -EINVAL;
5088c2ecf20Sopenharmony_ci		dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n");
5098c2ecf20Sopenharmony_ci		goto err_tx;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
5138c2ecf20Sopenharmony_ci	WARN_ON(priv->is_tx);
5148c2ecf20Sopenharmony_ci	priv->is_tx = 1;
5158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA);
5188c2ecf20Sopenharmony_ci	if (rc)
5198c2ecf20Sopenharmony_ci		goto err;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	rc = wait_for_completion_interruptible(&priv->tx_complete);
5228c2ecf20Sopenharmony_ci	if (rc < 0)
5238c2ecf20Sopenharmony_ci		goto err;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
5268c2ecf20Sopenharmony_ci	cc2520_cmd_strobe(priv, CC2520_CMD_SRXON);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return rc;
5298c2ecf20Sopenharmony_cierr:
5308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
5318c2ecf20Sopenharmony_ci	priv->is_tx = 0;
5328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
5338c2ecf20Sopenharmony_cierr_tx:
5348c2ecf20Sopenharmony_ci	return rc;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int cc2520_rx(struct cc2520_private *priv)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	u8 len = 0, lqi = 0, bytes = 1;
5408c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	/* Read single length byte from the radio. */
5438c2ecf20Sopenharmony_ci	cc2520_read_rxfifo(priv, &len, bytes);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (!ieee802154_is_valid_psdu_len(len)) {
5468c2ecf20Sopenharmony_ci		/* Corrupted frame received, clear frame buffer by
5478c2ecf20Sopenharmony_ci		 * reading entire buffer.
5488c2ecf20Sopenharmony_ci		 */
5498c2ecf20Sopenharmony_ci		dev_dbg(&priv->spi->dev, "corrupted frame received\n");
5508c2ecf20Sopenharmony_ci		len = IEEE802154_MTU;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(len);
5548c2ecf20Sopenharmony_ci	if (!skb)
5558c2ecf20Sopenharmony_ci		return -ENOMEM;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) {
5588c2ecf20Sopenharmony_ci		dev_dbg(&priv->spi->dev, "frame reception failed\n");
5598c2ecf20Sopenharmony_ci		kfree_skb(skb);
5608c2ecf20Sopenharmony_ci		return -EINVAL;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* In promiscuous mode, we configure the radio to include the
5648c2ecf20Sopenharmony_ci	 * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not
5658c2ecf20Sopenharmony_ci	 * in promiscuous mode, we check the CRC here, but leave the
5668c2ecf20Sopenharmony_ci	 * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer.
5678c2ecf20Sopenharmony_ci	 */
5688c2ecf20Sopenharmony_ci	if (!priv->promiscuous) {
5698c2ecf20Sopenharmony_ci		bool crc_ok;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		/* Check if the CRC is valid. With AUTOCRC set, the most
5728c2ecf20Sopenharmony_ci		 * significant bit of the last byte returned from the CC2520
5738c2ecf20Sopenharmony_ci		 * is CRC_OK flag. See section 20.3.4 of the datasheet.
5748c2ecf20Sopenharmony_ci		 */
5758c2ecf20Sopenharmony_ci		crc_ok = skb->data[len - 1] & BIT(7);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		/* If we failed CRC drop the packet in the driver layer. */
5788c2ecf20Sopenharmony_ci		if (!crc_ok) {
5798c2ecf20Sopenharmony_ci			dev_dbg(&priv->spi->dev, "CRC check failed\n");
5808c2ecf20Sopenharmony_ci			kfree_skb(skb);
5818c2ecf20Sopenharmony_ci			return -EINVAL;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		/* To calculate LQI, the lower 7 bits of the last byte (the
5858c2ecf20Sopenharmony_ci		 * correlation value provided by the radio) must be scaled to
5868c2ecf20Sopenharmony_ci		 * the range 0-255. According to section 20.6, the correlation
5878c2ecf20Sopenharmony_ci		 * value ranges from 50-110. Ideally this would be calibrated
5888c2ecf20Sopenharmony_ci		 * per hardware design, but we use roughly the datasheet values
5898c2ecf20Sopenharmony_ci		 * to get close enough while avoiding floating point.
5908c2ecf20Sopenharmony_ci		 */
5918c2ecf20Sopenharmony_ci		lqi = skb->data[len - 1] & 0x7f;
5928c2ecf20Sopenharmony_ci		if (lqi < 50)
5938c2ecf20Sopenharmony_ci			lqi = 50;
5948c2ecf20Sopenharmony_ci		else if (lqi > 113)
5958c2ecf20Sopenharmony_ci			lqi = 113;
5968c2ecf20Sopenharmony_ci		lqi = (lqi - 50) * 4;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	ieee802154_rx_irqsafe(priv->hw, skb, lqi);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic int
6078c2ecf20Sopenharmony_cicc2520_ed(struct ieee802154_hw *hw, u8 *level)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
6108c2ecf20Sopenharmony_ci	u8 status = 0xff;
6118c2ecf20Sopenharmony_ci	u8 rssi;
6128c2ecf20Sopenharmony_ci	int ret;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status);
6158c2ecf20Sopenharmony_ci	if (ret)
6168c2ecf20Sopenharmony_ci		return ret;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (status != RSSI_VALID)
6198c2ecf20Sopenharmony_ci		return -EINVAL;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ret = cc2520_read_register(priv, CC2520_RSSI, &rssi);
6228c2ecf20Sopenharmony_ci	if (ret)
6238c2ecf20Sopenharmony_ci		return ret;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */
6268c2ecf20Sopenharmony_ci	*level = rssi - RSSI_OFFSET;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int
6328c2ecf20Sopenharmony_cicc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
6358c2ecf20Sopenharmony_ci	int ret;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	dev_dbg(&priv->spi->dev, "trying to set channel\n");
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	WARN_ON(page != 0);
6408c2ecf20Sopenharmony_ci	WARN_ON(channel < CC2520_MINCHANNEL);
6418c2ecf20Sopenharmony_ci	WARN_ON(channel > CC2520_MAXCHANNEL);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_FREQCTRL,
6448c2ecf20Sopenharmony_ci				    11 + 5 * (channel - 11));
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	return ret;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic int
6508c2ecf20Sopenharmony_cicc2520_filter(struct ieee802154_hw *hw,
6518c2ecf20Sopenharmony_ci	      struct ieee802154_hw_addr_filt *filt, unsigned long changed)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
6548c2ecf20Sopenharmony_ci	int ret = 0;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (changed & IEEE802154_AFILT_PANID_CHANGED) {
6578c2ecf20Sopenharmony_ci		u16 panid = le16_to_cpu(filt->pan_id);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		dev_vdbg(&priv->spi->dev, "%s called for pan id\n", __func__);
6608c2ecf20Sopenharmony_ci		ret = cc2520_write_ram(priv, CC2520RAM_PANID,
6618c2ecf20Sopenharmony_ci				       sizeof(panid), (u8 *)&panid);
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
6658c2ecf20Sopenharmony_ci		dev_vdbg(&priv->spi->dev,
6668c2ecf20Sopenharmony_ci			 "%s called for IEEE addr\n", __func__);
6678c2ecf20Sopenharmony_ci		ret = cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
6688c2ecf20Sopenharmony_ci				       sizeof(filt->ieee_addr),
6698c2ecf20Sopenharmony_ci				       (u8 *)&filt->ieee_addr);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
6738c2ecf20Sopenharmony_ci		u16 addr = le16_to_cpu(filt->short_addr);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci		dev_vdbg(&priv->spi->dev, "%s called for saddr\n", __func__);
6768c2ecf20Sopenharmony_ci		ret = cc2520_write_ram(priv, CC2520RAM_SHORTADDR,
6778c2ecf20Sopenharmony_ci				       sizeof(addr), (u8 *)&addr);
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (changed & IEEE802154_AFILT_PANC_CHANGED) {
6818c2ecf20Sopenharmony_ci		u8 frmfilt0;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		dev_vdbg(&priv->spi->dev,
6848c2ecf20Sopenharmony_ci			 "%s called for panc change\n", __func__);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		if (filt->pan_coord)
6898c2ecf20Sopenharmony_ci			frmfilt0 |= FRMFILT0_PAN_COORDINATOR;
6908c2ecf20Sopenharmony_ci		else
6918c2ecf20Sopenharmony_ci			frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return ret;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic inline int cc2520_set_tx_power(struct cc2520_private *priv, s32 mbm)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	u8 power;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	switch (mbm) {
7048c2ecf20Sopenharmony_ci	case 500:
7058c2ecf20Sopenharmony_ci		power = 0xF7;
7068c2ecf20Sopenharmony_ci		break;
7078c2ecf20Sopenharmony_ci	case 300:
7088c2ecf20Sopenharmony_ci		power = 0xF2;
7098c2ecf20Sopenharmony_ci		break;
7108c2ecf20Sopenharmony_ci	case 200:
7118c2ecf20Sopenharmony_ci		power = 0xAB;
7128c2ecf20Sopenharmony_ci		break;
7138c2ecf20Sopenharmony_ci	case 100:
7148c2ecf20Sopenharmony_ci		power = 0x13;
7158c2ecf20Sopenharmony_ci		break;
7168c2ecf20Sopenharmony_ci	case 0:
7178c2ecf20Sopenharmony_ci		power = 0x32;
7188c2ecf20Sopenharmony_ci		break;
7198c2ecf20Sopenharmony_ci	case -200:
7208c2ecf20Sopenharmony_ci		power = 0x81;
7218c2ecf20Sopenharmony_ci		break;
7228c2ecf20Sopenharmony_ci	case -400:
7238c2ecf20Sopenharmony_ci		power = 0x88;
7248c2ecf20Sopenharmony_ci		break;
7258c2ecf20Sopenharmony_ci	case -700:
7268c2ecf20Sopenharmony_ci		power = 0x2C;
7278c2ecf20Sopenharmony_ci		break;
7288c2ecf20Sopenharmony_ci	case -1800:
7298c2ecf20Sopenharmony_ci		power = 0x03;
7308c2ecf20Sopenharmony_ci		break;
7318c2ecf20Sopenharmony_ci	default:
7328c2ecf20Sopenharmony_ci		return -EINVAL;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return cc2520_write_register(priv, CC2520_TXPOWER, power);
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic inline int cc2520_cc2591_set_tx_power(struct cc2520_private *priv,
7398c2ecf20Sopenharmony_ci					     s32 mbm)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	u8 power;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	switch (mbm) {
7448c2ecf20Sopenharmony_ci	case 1700:
7458c2ecf20Sopenharmony_ci		power = 0xF9;
7468c2ecf20Sopenharmony_ci		break;
7478c2ecf20Sopenharmony_ci	case 1600:
7488c2ecf20Sopenharmony_ci		power = 0xF0;
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci	case 1400:
7518c2ecf20Sopenharmony_ci		power = 0xA0;
7528c2ecf20Sopenharmony_ci		break;
7538c2ecf20Sopenharmony_ci	case 1100:
7548c2ecf20Sopenharmony_ci		power = 0x2C;
7558c2ecf20Sopenharmony_ci		break;
7568c2ecf20Sopenharmony_ci	case -100:
7578c2ecf20Sopenharmony_ci		power = 0x03;
7588c2ecf20Sopenharmony_ci		break;
7598c2ecf20Sopenharmony_ci	case -800:
7608c2ecf20Sopenharmony_ci		power = 0x01;
7618c2ecf20Sopenharmony_ci		break;
7628c2ecf20Sopenharmony_ci	default:
7638c2ecf20Sopenharmony_ci		return -EINVAL;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	return cc2520_write_register(priv, CC2520_TXPOWER, power);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci#define CC2520_MAX_TX_POWERS 0x8
7708c2ecf20Sopenharmony_cistatic const s32 cc2520_powers[CC2520_MAX_TX_POWERS + 1] = {
7718c2ecf20Sopenharmony_ci	500, 300, 200, 100, 0, -200, -400, -700, -1800,
7728c2ecf20Sopenharmony_ci};
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci#define CC2520_CC2591_MAX_TX_POWERS 0x5
7758c2ecf20Sopenharmony_cistatic const s32 cc2520_cc2591_powers[CC2520_CC2591_MAX_TX_POWERS + 1] = {
7768c2ecf20Sopenharmony_ci	1700, 1600, 1400, 1100, -100, -800,
7778c2ecf20Sopenharmony_ci};
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int
7808c2ecf20Sopenharmony_cicc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (!priv->amplified)
7858c2ecf20Sopenharmony_ci		return cc2520_set_tx_power(priv, mbm);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return cc2520_cc2591_set_tx_power(priv, mbm);
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic int
7918c2ecf20Sopenharmony_cicc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct cc2520_private *priv = hw->priv;
7948c2ecf20Sopenharmony_ci	u8 frmfilt0;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	priv->promiscuous = on;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (on) {
8038c2ecf20Sopenharmony_ci		/* Disable automatic ACK, automatic CRC, and frame filtering. */
8048c2ecf20Sopenharmony_ci		cc2520_write_register(priv, CC2520_FRMCTRL0, 0);
8058c2ecf20Sopenharmony_ci		frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN;
8068c2ecf20Sopenharmony_ci	} else {
8078c2ecf20Sopenharmony_ci		cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK |
8088c2ecf20Sopenharmony_ci							     FRMCTRL0_AUTOCRC);
8098c2ecf20Sopenharmony_ci		frmfilt0 |= FRMFILT0_FRAME_FILTER_EN;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci	return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic const struct ieee802154_ops cc2520_ops = {
8158c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
8168c2ecf20Sopenharmony_ci	.start = cc2520_start,
8178c2ecf20Sopenharmony_ci	.stop = cc2520_stop,
8188c2ecf20Sopenharmony_ci	.xmit_sync = cc2520_tx,
8198c2ecf20Sopenharmony_ci	.ed = cc2520_ed,
8208c2ecf20Sopenharmony_ci	.set_channel = cc2520_set_channel,
8218c2ecf20Sopenharmony_ci	.set_hw_addr_filt = cc2520_filter,
8228c2ecf20Sopenharmony_ci	.set_txpower = cc2520_set_txpower,
8238c2ecf20Sopenharmony_ci	.set_promiscuous_mode = cc2520_set_promiscuous_mode,
8248c2ecf20Sopenharmony_ci};
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic int cc2520_register(struct cc2520_private *priv)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops);
8318c2ecf20Sopenharmony_ci	if (!priv->hw)
8328c2ecf20Sopenharmony_ci		goto err_ret;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	priv->hw->priv = priv;
8358c2ecf20Sopenharmony_ci	priv->hw->parent = &priv->spi->dev;
8368c2ecf20Sopenharmony_ci	priv->hw->extra_tx_headroom = 0;
8378c2ecf20Sopenharmony_ci	ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* We do support only 2.4 Ghz */
8408c2ecf20Sopenharmony_ci	priv->hw->phy->supported.channels[0] = 0x7FFF800;
8418c2ecf20Sopenharmony_ci	priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
8428c2ecf20Sopenharmony_ci			  IEEE802154_HW_PROMISCUOUS;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (!priv->amplified) {
8478c2ecf20Sopenharmony_ci		priv->hw->phy->supported.tx_powers = cc2520_powers;
8488c2ecf20Sopenharmony_ci		priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_powers);
8498c2ecf20Sopenharmony_ci		priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[4];
8508c2ecf20Sopenharmony_ci	} else {
8518c2ecf20Sopenharmony_ci		priv->hw->phy->supported.tx_powers = cc2520_cc2591_powers;
8528c2ecf20Sopenharmony_ci		priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_cc2591_powers);
8538c2ecf20Sopenharmony_ci		priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[0];
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	priv->hw->phy->current_channel = 11;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "registered cc2520\n");
8598c2ecf20Sopenharmony_ci	ret = ieee802154_register_hw(priv->hw);
8608c2ecf20Sopenharmony_ci	if (ret)
8618c2ecf20Sopenharmony_ci		goto err_free_device;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	return 0;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cierr_free_device:
8668c2ecf20Sopenharmony_ci	ieee802154_free_hw(priv->hw);
8678c2ecf20Sopenharmony_cierr_ret:
8688c2ecf20Sopenharmony_ci	return ret;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic void cc2520_fifop_irqwork(struct work_struct *work)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct cc2520_private *priv
8748c2ecf20Sopenharmony_ci		= container_of(work, struct cc2520_private, fifop_irqwork);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	dev_dbg(&priv->spi->dev, "fifop interrupt received\n");
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (gpio_get_value(priv->fifo_pin))
8798c2ecf20Sopenharmony_ci		cc2520_rx(priv);
8808c2ecf20Sopenharmony_ci	else
8818c2ecf20Sopenharmony_ci		dev_dbg(&priv->spi->dev, "rxfifo overflow\n");
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX);
8848c2ecf20Sopenharmony_ci	cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX);
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic irqreturn_t cc2520_fifop_isr(int irq, void *data)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct cc2520_private *priv = data;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	schedule_work(&priv->fifop_irqwork);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic irqreturn_t cc2520_sfd_isr(int irq, void *data)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct cc2520_private *priv = data;
8998c2ecf20Sopenharmony_ci	unsigned long flags;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
9028c2ecf20Sopenharmony_ci	if (priv->is_tx) {
9038c2ecf20Sopenharmony_ci		priv->is_tx = 0;
9048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
9058c2ecf20Sopenharmony_ci		dev_dbg(&priv->spi->dev, "SFD for TX\n");
9068c2ecf20Sopenharmony_ci		complete(&priv->tx_complete);
9078c2ecf20Sopenharmony_ci	} else {
9088c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, flags);
9098c2ecf20Sopenharmony_ci		dev_dbg(&priv->spi->dev, "SFD for RX\n");
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic int cc2520_get_platform_data(struct spi_device *spi,
9168c2ecf20Sopenharmony_ci				    struct cc2520_platform_data *pdata)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct device_node *np = spi->dev.of_node;
9198c2ecf20Sopenharmony_ci	struct cc2520_private *priv = spi_get_drvdata(spi);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (!np) {
9228c2ecf20Sopenharmony_ci		struct cc2520_platform_data *spi_pdata = spi->dev.platform_data;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		if (!spi_pdata)
9258c2ecf20Sopenharmony_ci			return -ENOENT;
9268c2ecf20Sopenharmony_ci		*pdata = *spi_pdata;
9278c2ecf20Sopenharmony_ci		priv->fifo_pin = pdata->fifo;
9288c2ecf20Sopenharmony_ci		return 0;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
9328c2ecf20Sopenharmony_ci	priv->fifo_pin = pdata->fifo;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
9378c2ecf20Sopenharmony_ci	pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
9388c2ecf20Sopenharmony_ci	pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
9398c2ecf20Sopenharmony_ci	pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* CC2591 front end for CC2520 */
9428c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "amplified"))
9438c2ecf20Sopenharmony_ci		priv->amplified = true;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	return 0;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic int cc2520_hw_init(struct cc2520_private *priv)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	u8 status = 0, state = 0xff;
9518c2ecf20Sopenharmony_ci	int ret;
9528c2ecf20Sopenharmony_ci	int timeout = 100;
9538c2ecf20Sopenharmony_ci	struct cc2520_platform_data pdata;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	ret = cc2520_get_platform_data(priv->spi, &pdata);
9568c2ecf20Sopenharmony_ci	if (ret)
9578c2ecf20Sopenharmony_ci		goto err_ret;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state);
9608c2ecf20Sopenharmony_ci	if (ret)
9618c2ecf20Sopenharmony_ci		goto err_ret;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (state != STATE_IDLE)
9648c2ecf20Sopenharmony_ci		return -EINVAL;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	do {
9678c2ecf20Sopenharmony_ci		ret = cc2520_get_status(priv, &status);
9688c2ecf20Sopenharmony_ci		if (ret)
9698c2ecf20Sopenharmony_ci			goto err_ret;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		if (timeout-- <= 0) {
9728c2ecf20Sopenharmony_ci			dev_err(&priv->spi->dev, "oscillator start failed!\n");
9738c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
9748c2ecf20Sopenharmony_ci		}
9758c2ecf20Sopenharmony_ci		udelay(1);
9768c2ecf20Sopenharmony_ci	} while (!(status & CC2520_STATUS_XOSC32M_STABLE));
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	dev_vdbg(&priv->spi->dev, "oscillator brought up\n");
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	/* If the CC2520 is connected to a CC2591 amplifier, we must both
9818c2ecf20Sopenharmony_ci	 * configure GPIOs on the CC2520 to correctly configure the CC2591
9828c2ecf20Sopenharmony_ci	 * and change a couple settings of the CC2520 to work with the
9838c2ecf20Sopenharmony_ci	 * amplifier. See section 8 page 17 of TI application note AN065.
9848c2ecf20Sopenharmony_ci	 * http://www.ti.com/lit/an/swra229a/swra229a.pdf
9858c2ecf20Sopenharmony_ci	 */
9868c2ecf20Sopenharmony_ci	if (priv->amplified) {
9878c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16);
9888c2ecf20Sopenharmony_ci		if (ret)
9898c2ecf20Sopenharmony_ci			goto err_ret;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46);
9928c2ecf20Sopenharmony_ci		if (ret)
9938c2ecf20Sopenharmony_ci			goto err_ret;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47);
9968c2ecf20Sopenharmony_ci		if (ret)
9978c2ecf20Sopenharmony_ci			goto err_ret;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e);
10008c2ecf20Sopenharmony_ci		if (ret)
10018c2ecf20Sopenharmony_ci			goto err_ret;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1);
10048c2ecf20Sopenharmony_ci		if (ret)
10058c2ecf20Sopenharmony_ci			goto err_ret;
10068c2ecf20Sopenharmony_ci	} else {
10078c2ecf20Sopenharmony_ci		ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
10088c2ecf20Sopenharmony_ci		if (ret)
10098c2ecf20Sopenharmony_ci			goto err_ret;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* Registers default value: section 28.1 in Datasheet */
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* Set the CCA threshold to -50 dBm. This seems to have been copied
10158c2ecf20Sopenharmony_ci	 * from the TinyOS CC2520 driver and is much higher than the -84 dBm
10168c2ecf20Sopenharmony_ci	 * threshold suggested in the datasheet.
10178c2ecf20Sopenharmony_ci	 */
10188c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
10198c2ecf20Sopenharmony_ci	if (ret)
10208c2ecf20Sopenharmony_ci		goto err_ret;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85);
10238c2ecf20Sopenharmony_ci	if (ret)
10248c2ecf20Sopenharmony_ci		goto err_ret;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14);
10278c2ecf20Sopenharmony_ci	if (ret)
10288c2ecf20Sopenharmony_ci		goto err_ret;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f);
10318c2ecf20Sopenharmony_ci	if (ret)
10328c2ecf20Sopenharmony_ci		goto err_ret;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a);
10358c2ecf20Sopenharmony_ci	if (ret)
10368c2ecf20Sopenharmony_ci		goto err_ret;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b);
10398c2ecf20Sopenharmony_ci	if (ret)
10408c2ecf20Sopenharmony_ci		goto err_ret;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10);
10438c2ecf20Sopenharmony_ci	if (ret)
10448c2ecf20Sopenharmony_ci		goto err_ret;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e);
10478c2ecf20Sopenharmony_ci	if (ret)
10488c2ecf20Sopenharmony_ci		goto err_ret;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03);
10518c2ecf20Sopenharmony_ci	if (ret)
10528c2ecf20Sopenharmony_ci		goto err_ret;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/* Configure registers correctly for this driver. */
10558c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_FRMCTRL1,
10568c2ecf20Sopenharmony_ci				    FRMCTRL1_SET_RXENMASK_ON_TX |
10578c2ecf20Sopenharmony_ci				    FRMCTRL1_IGNORE_TX_UNDERF);
10588c2ecf20Sopenharmony_ci	if (ret)
10598c2ecf20Sopenharmony_ci		goto err_ret;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127);
10628c2ecf20Sopenharmony_ci	if (ret)
10638c2ecf20Sopenharmony_ci		goto err_ret;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	return 0;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cierr_ret:
10688c2ecf20Sopenharmony_ci	return ret;
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic int cc2520_probe(struct spi_device *spi)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	struct cc2520_private *priv;
10748c2ecf20Sopenharmony_ci	struct cc2520_platform_data pdata;
10758c2ecf20Sopenharmony_ci	int ret;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
10788c2ecf20Sopenharmony_ci	if (!priv)
10798c2ecf20Sopenharmony_ci		return -ENOMEM;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, priv);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	ret = cc2520_get_platform_data(spi, &pdata);
10848c2ecf20Sopenharmony_ci	if (ret < 0) {
10858c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "no platform data\n");
10868c2ecf20Sopenharmony_ci		return -EINVAL;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	priv->spi = spi;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	priv->buf = devm_kzalloc(&spi->dev,
10928c2ecf20Sopenharmony_ci				 SPI_COMMAND_BUFFER, GFP_KERNEL);
10938c2ecf20Sopenharmony_ci	if (!priv->buf)
10948c2ecf20Sopenharmony_ci		return -ENOMEM;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	mutex_init(&priv->buffer_mutex);
10978c2ecf20Sopenharmony_ci	INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork);
10988c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
10998c2ecf20Sopenharmony_ci	init_completion(&priv->tx_complete);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Assumption that CC2591 is not connected */
11028c2ecf20Sopenharmony_ci	priv->amplified = false;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/* Request all the gpio's */
11058c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.fifo)) {
11068c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "fifo gpio is not valid\n");
11078c2ecf20Sopenharmony_ci		ret = -EINVAL;
11088c2ecf20Sopenharmony_ci		goto err_hw_init;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.fifo,
11128c2ecf20Sopenharmony_ci				    GPIOF_IN, "fifo");
11138c2ecf20Sopenharmony_ci	if (ret)
11148c2ecf20Sopenharmony_ci		goto err_hw_init;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.cca)) {
11178c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "cca gpio is not valid\n");
11188c2ecf20Sopenharmony_ci		ret = -EINVAL;
11198c2ecf20Sopenharmony_ci		goto err_hw_init;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.cca,
11238c2ecf20Sopenharmony_ci				    GPIOF_IN, "cca");
11248c2ecf20Sopenharmony_ci	if (ret)
11258c2ecf20Sopenharmony_ci		goto err_hw_init;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.fifop)) {
11288c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "fifop gpio is not valid\n");
11298c2ecf20Sopenharmony_ci		ret = -EINVAL;
11308c2ecf20Sopenharmony_ci		goto err_hw_init;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.fifop,
11348c2ecf20Sopenharmony_ci				    GPIOF_IN, "fifop");
11358c2ecf20Sopenharmony_ci	if (ret)
11368c2ecf20Sopenharmony_ci		goto err_hw_init;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.sfd)) {
11398c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "sfd gpio is not valid\n");
11408c2ecf20Sopenharmony_ci		ret = -EINVAL;
11418c2ecf20Sopenharmony_ci		goto err_hw_init;
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.sfd,
11458c2ecf20Sopenharmony_ci				    GPIOF_IN, "sfd");
11468c2ecf20Sopenharmony_ci	if (ret)
11478c2ecf20Sopenharmony_ci		goto err_hw_init;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.reset)) {
11508c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "reset gpio is not valid\n");
11518c2ecf20Sopenharmony_ci		ret = -EINVAL;
11528c2ecf20Sopenharmony_ci		goto err_hw_init;
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.reset,
11568c2ecf20Sopenharmony_ci				    GPIOF_OUT_INIT_LOW, "reset");
11578c2ecf20Sopenharmony_ci	if (ret)
11588c2ecf20Sopenharmony_ci		goto err_hw_init;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata.vreg)) {
11618c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "vreg gpio is not valid\n");
11628c2ecf20Sopenharmony_ci		ret = -EINVAL;
11638c2ecf20Sopenharmony_ci		goto err_hw_init;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	ret = devm_gpio_request_one(&spi->dev, pdata.vreg,
11678c2ecf20Sopenharmony_ci				    GPIOF_OUT_INIT_LOW, "vreg");
11688c2ecf20Sopenharmony_ci	if (ret)
11698c2ecf20Sopenharmony_ci		goto err_hw_init;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	gpio_set_value(pdata.vreg, HIGH);
11728c2ecf20Sopenharmony_ci	usleep_range(100, 150);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	gpio_set_value(pdata.reset, HIGH);
11758c2ecf20Sopenharmony_ci	usleep_range(200, 250);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	ret = cc2520_hw_init(priv);
11788c2ecf20Sopenharmony_ci	if (ret)
11798c2ecf20Sopenharmony_ci		goto err_hw_init;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/* Set up fifop interrupt */
11828c2ecf20Sopenharmony_ci	ret = devm_request_irq(&spi->dev,
11838c2ecf20Sopenharmony_ci			       gpio_to_irq(pdata.fifop),
11848c2ecf20Sopenharmony_ci			       cc2520_fifop_isr,
11858c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_RISING,
11868c2ecf20Sopenharmony_ci			       dev_name(&spi->dev),
11878c2ecf20Sopenharmony_ci			       priv);
11888c2ecf20Sopenharmony_ci	if (ret) {
11898c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "could not get fifop irq\n");
11908c2ecf20Sopenharmony_ci		goto err_hw_init;
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	/* Set up sfd interrupt */
11948c2ecf20Sopenharmony_ci	ret = devm_request_irq(&spi->dev,
11958c2ecf20Sopenharmony_ci			       gpio_to_irq(pdata.sfd),
11968c2ecf20Sopenharmony_ci			       cc2520_sfd_isr,
11978c2ecf20Sopenharmony_ci			       IRQF_TRIGGER_FALLING,
11988c2ecf20Sopenharmony_ci			       dev_name(&spi->dev),
11998c2ecf20Sopenharmony_ci			       priv);
12008c2ecf20Sopenharmony_ci	if (ret) {
12018c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "could not get sfd irq\n");
12028c2ecf20Sopenharmony_ci		goto err_hw_init;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	ret = cc2520_register(priv);
12068c2ecf20Sopenharmony_ci	if (ret)
12078c2ecf20Sopenharmony_ci		goto err_hw_init;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	return 0;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cierr_hw_init:
12128c2ecf20Sopenharmony_ci	mutex_destroy(&priv->buffer_mutex);
12138c2ecf20Sopenharmony_ci	flush_work(&priv->fifop_irqwork);
12148c2ecf20Sopenharmony_ci	return ret;
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_cistatic int cc2520_remove(struct spi_device *spi)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct cc2520_private *priv = spi_get_drvdata(spi);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	mutex_destroy(&priv->buffer_mutex);
12228c2ecf20Sopenharmony_ci	flush_work(&priv->fifop_irqwork);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	ieee802154_unregister_hw(priv->hw);
12258c2ecf20Sopenharmony_ci	ieee802154_free_hw(priv->hw);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	return 0;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic const struct spi_device_id cc2520_ids[] = {
12318c2ecf20Sopenharmony_ci	{"cc2520", },
12328c2ecf20Sopenharmony_ci	{},
12338c2ecf20Sopenharmony_ci};
12348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, cc2520_ids);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic const struct of_device_id cc2520_of_ids[] = {
12378c2ecf20Sopenharmony_ci	{.compatible = "ti,cc2520", },
12388c2ecf20Sopenharmony_ci	{},
12398c2ecf20Sopenharmony_ci};
12408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cc2520_of_ids);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci/* SPI driver structure */
12438c2ecf20Sopenharmony_cistatic struct spi_driver cc2520_driver = {
12448c2ecf20Sopenharmony_ci	.driver = {
12458c2ecf20Sopenharmony_ci		.name = "cc2520",
12468c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(cc2520_of_ids),
12478c2ecf20Sopenharmony_ci	},
12488c2ecf20Sopenharmony_ci	.id_table = cc2520_ids,
12498c2ecf20Sopenharmony_ci	.probe = cc2520_probe,
12508c2ecf20Sopenharmony_ci	.remove = cc2520_remove,
12518c2ecf20Sopenharmony_ci};
12528c2ecf20Sopenharmony_cimodule_spi_driver(cc2520_driver);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>");
12558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CC2520 Transceiver Driver");
12568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1257