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