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