162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * http://www.cascoda.com/products/ca-821x/ 362306a36Sopenharmony_ci * Copyright (c) 2016, Cascoda, Ltd. 462306a36Sopenharmony_ci * All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This code is dual-licensed under both GPLv2 and 3-clause BSD. What follows is 762306a36Sopenharmony_ci * the license notice for both respectively. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci ******************************************************************************* 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 1262306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License 1362306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2 1462306a36Sopenharmony_ci * of the License, or (at your option) any later version. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1762306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1862306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1962306a36Sopenharmony_ci * GNU General Public License for more details. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci ******************************************************************************* 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 2462306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice, 2762306a36Sopenharmony_ci * this list of conditions and the following disclaimer. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice, 3062306a36Sopenharmony_ci * this list of conditions and the following disclaimer in the documentation 3162306a36Sopenharmony_ci * and/or other materials provided with the distribution. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * 3. Neither the name of the copyright holder nor the names of its contributors 3462306a36Sopenharmony_ci * may be used to endorse or promote products derived from this software without 3562306a36Sopenharmony_ci * specific prior written permission. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 3862306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3962306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4062306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 4162306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4262306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 4362306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 4462306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 4562306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 4662306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 4762306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <linux/cdev.h> 5162306a36Sopenharmony_ci#include <linux/clk-provider.h> 5262306a36Sopenharmony_ci#include <linux/debugfs.h> 5362306a36Sopenharmony_ci#include <linux/delay.h> 5462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 5562306a36Sopenharmony_ci#include <linux/gpio.h> 5662306a36Sopenharmony_ci#include <linux/ieee802154.h> 5762306a36Sopenharmony_ci#include <linux/io.h> 5862306a36Sopenharmony_ci#include <linux/kfifo.h> 5962306a36Sopenharmony_ci#include <linux/of.h> 6062306a36Sopenharmony_ci#include <linux/of_gpio.h> 6162306a36Sopenharmony_ci#include <linux/module.h> 6262306a36Sopenharmony_ci#include <linux/mutex.h> 6362306a36Sopenharmony_ci#include <linux/poll.h> 6462306a36Sopenharmony_ci#include <linux/skbuff.h> 6562306a36Sopenharmony_ci#include <linux/slab.h> 6662306a36Sopenharmony_ci#include <linux/spi/spi.h> 6762306a36Sopenharmony_ci#include <linux/spinlock.h> 6862306a36Sopenharmony_ci#include <linux/string.h> 6962306a36Sopenharmony_ci#include <linux/workqueue.h> 7062306a36Sopenharmony_ci#include <linux/interrupt.h> 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#include <net/ieee802154_netdev.h> 7362306a36Sopenharmony_ci#include <net/mac802154.h> 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define DRIVER_NAME "ca8210" 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* external clock frequencies */ 7862306a36Sopenharmony_ci#define ONE_MHZ 1000000 7962306a36Sopenharmony_ci#define TWO_MHZ (2 * ONE_MHZ) 8062306a36Sopenharmony_ci#define FOUR_MHZ (4 * ONE_MHZ) 8162306a36Sopenharmony_ci#define EIGHT_MHZ (8 * ONE_MHZ) 8262306a36Sopenharmony_ci#define SIXTEEN_MHZ (16 * ONE_MHZ) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* spi constants */ 8562306a36Sopenharmony_ci#define CA8210_SPI_BUF_SIZE 256 8662306a36Sopenharmony_ci#define CA8210_SYNC_TIMEOUT 1000 /* Timeout for synchronous commands [ms] */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* test interface constants */ 8962306a36Sopenharmony_ci#define CA8210_TEST_INT_FILE_NAME "ca8210_test" 9062306a36Sopenharmony_ci#define CA8210_TEST_INT_FIFO_SIZE 256 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* HWME attribute IDs */ 9362306a36Sopenharmony_ci#define HWME_EDTHRESHOLD (0x04) 9462306a36Sopenharmony_ci#define HWME_EDVALUE (0x06) 9562306a36Sopenharmony_ci#define HWME_SYSCLKOUT (0x0F) 9662306a36Sopenharmony_ci#define HWME_LQILIMIT (0x11) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* TDME attribute IDs */ 9962306a36Sopenharmony_ci#define TDME_CHANNEL (0x00) 10062306a36Sopenharmony_ci#define TDME_ATM_CONFIG (0x06) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define MAX_HWME_ATTRIBUTE_SIZE 16 10362306a36Sopenharmony_ci#define MAX_TDME_ATTRIBUTE_SIZE 2 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* PHY/MAC PIB Attribute Enumerations */ 10662306a36Sopenharmony_ci#define PHY_CURRENT_CHANNEL (0x00) 10762306a36Sopenharmony_ci#define PHY_TRANSMIT_POWER (0x02) 10862306a36Sopenharmony_ci#define PHY_CCA_MODE (0x03) 10962306a36Sopenharmony_ci#define MAC_ASSOCIATION_PERMIT (0x41) 11062306a36Sopenharmony_ci#define MAC_AUTO_REQUEST (0x42) 11162306a36Sopenharmony_ci#define MAC_BATT_LIFE_EXT (0x43) 11262306a36Sopenharmony_ci#define MAC_BATT_LIFE_EXT_PERIODS (0x44) 11362306a36Sopenharmony_ci#define MAC_BEACON_PAYLOAD (0x45) 11462306a36Sopenharmony_ci#define MAC_BEACON_PAYLOAD_LENGTH (0x46) 11562306a36Sopenharmony_ci#define MAC_BEACON_ORDER (0x47) 11662306a36Sopenharmony_ci#define MAC_GTS_PERMIT (0x4d) 11762306a36Sopenharmony_ci#define MAC_MAX_CSMA_BACKOFFS (0x4e) 11862306a36Sopenharmony_ci#define MAC_MIN_BE (0x4f) 11962306a36Sopenharmony_ci#define MAC_PAN_ID (0x50) 12062306a36Sopenharmony_ci#define MAC_PROMISCUOUS_MODE (0x51) 12162306a36Sopenharmony_ci#define MAC_RX_ON_WHEN_IDLE (0x52) 12262306a36Sopenharmony_ci#define MAC_SHORT_ADDRESS (0x53) 12362306a36Sopenharmony_ci#define MAC_SUPERFRAME_ORDER (0x54) 12462306a36Sopenharmony_ci#define MAC_ASSOCIATED_PAN_COORD (0x56) 12562306a36Sopenharmony_ci#define MAC_MAX_BE (0x57) 12662306a36Sopenharmony_ci#define MAC_MAX_FRAME_RETRIES (0x59) 12762306a36Sopenharmony_ci#define MAC_RESPONSE_WAIT_TIME (0x5A) 12862306a36Sopenharmony_ci#define MAC_SECURITY_ENABLED (0x5D) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define MAC_AUTO_REQUEST_SECURITY_LEVEL (0x78) 13162306a36Sopenharmony_ci#define MAC_AUTO_REQUEST_KEY_ID_MODE (0x79) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define NS_IEEE_ADDRESS (0xFF) /* Non-standard IEEE address */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* MAC Address Mode Definitions */ 13662306a36Sopenharmony_ci#define MAC_MODE_NO_ADDR (0x00) 13762306a36Sopenharmony_ci#define MAC_MODE_SHORT_ADDR (0x02) 13862306a36Sopenharmony_ci#define MAC_MODE_LONG_ADDR (0x03) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* MAC constants */ 14162306a36Sopenharmony_ci#define MAX_BEACON_OVERHEAD (75) 14262306a36Sopenharmony_ci#define MAX_BEACON_PAYLOAD_LENGTH (IEEE802154_MTU - MAX_BEACON_OVERHEAD) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define MAX_ATTRIBUTE_SIZE (122) 14562306a36Sopenharmony_ci#define MAX_DATA_SIZE (114) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define CA8210_VALID_CHANNELS (0x07FFF800) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* MAC workarounds for V1.1 and MPW silicon (V0.x) */ 15062306a36Sopenharmony_ci#define CA8210_MAC_WORKAROUNDS (0) 15162306a36Sopenharmony_ci#define CA8210_MAC_MPW (0) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* memory manipulation macros */ 15462306a36Sopenharmony_ci#define LS_BYTE(x) ((u8)((x) & 0xFF)) 15562306a36Sopenharmony_ci#define MS_BYTE(x) ((u8)(((x) >> 8) & 0xFF)) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* message ID codes in SPI commands */ 15862306a36Sopenharmony_ci/* downstream */ 15962306a36Sopenharmony_ci#define MCPS_DATA_REQUEST (0x00) 16062306a36Sopenharmony_ci#define MLME_ASSOCIATE_REQUEST (0x02) 16162306a36Sopenharmony_ci#define MLME_ASSOCIATE_RESPONSE (0x03) 16262306a36Sopenharmony_ci#define MLME_DISASSOCIATE_REQUEST (0x04) 16362306a36Sopenharmony_ci#define MLME_GET_REQUEST (0x05) 16462306a36Sopenharmony_ci#define MLME_ORPHAN_RESPONSE (0x06) 16562306a36Sopenharmony_ci#define MLME_RESET_REQUEST (0x07) 16662306a36Sopenharmony_ci#define MLME_RX_ENABLE_REQUEST (0x08) 16762306a36Sopenharmony_ci#define MLME_SCAN_REQUEST (0x09) 16862306a36Sopenharmony_ci#define MLME_SET_REQUEST (0x0A) 16962306a36Sopenharmony_ci#define MLME_START_REQUEST (0x0B) 17062306a36Sopenharmony_ci#define MLME_POLL_REQUEST (0x0D) 17162306a36Sopenharmony_ci#define HWME_SET_REQUEST (0x0E) 17262306a36Sopenharmony_ci#define HWME_GET_REQUEST (0x0F) 17362306a36Sopenharmony_ci#define TDME_SETSFR_REQUEST (0x11) 17462306a36Sopenharmony_ci#define TDME_GETSFR_REQUEST (0x12) 17562306a36Sopenharmony_ci#define TDME_SET_REQUEST (0x14) 17662306a36Sopenharmony_ci/* upstream */ 17762306a36Sopenharmony_ci#define MCPS_DATA_INDICATION (0x00) 17862306a36Sopenharmony_ci#define MCPS_DATA_CONFIRM (0x01) 17962306a36Sopenharmony_ci#define MLME_RESET_CONFIRM (0x0A) 18062306a36Sopenharmony_ci#define MLME_SET_CONFIRM (0x0E) 18162306a36Sopenharmony_ci#define MLME_START_CONFIRM (0x0F) 18262306a36Sopenharmony_ci#define HWME_SET_CONFIRM (0x12) 18362306a36Sopenharmony_ci#define HWME_GET_CONFIRM (0x13) 18462306a36Sopenharmony_ci#define HWME_WAKEUP_INDICATION (0x15) 18562306a36Sopenharmony_ci#define TDME_SETSFR_CONFIRM (0x17) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* SPI command IDs */ 18862306a36Sopenharmony_ci/* bit indicating a confirm or indication from slave to master */ 18962306a36Sopenharmony_ci#define SPI_S2M (0x20) 19062306a36Sopenharmony_ci/* bit indicating a synchronous message */ 19162306a36Sopenharmony_ci#define SPI_SYN (0x40) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* SPI command definitions */ 19462306a36Sopenharmony_ci#define SPI_IDLE (0xFF) 19562306a36Sopenharmony_ci#define SPI_NACK (0xF0) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define SPI_MCPS_DATA_REQUEST (MCPS_DATA_REQUEST) 19862306a36Sopenharmony_ci#define SPI_MCPS_DATA_INDICATION (MCPS_DATA_INDICATION + SPI_S2M) 19962306a36Sopenharmony_ci#define SPI_MCPS_DATA_CONFIRM (MCPS_DATA_CONFIRM + SPI_S2M) 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define SPI_MLME_ASSOCIATE_REQUEST (MLME_ASSOCIATE_REQUEST) 20262306a36Sopenharmony_ci#define SPI_MLME_RESET_REQUEST (MLME_RESET_REQUEST + SPI_SYN) 20362306a36Sopenharmony_ci#define SPI_MLME_SET_REQUEST (MLME_SET_REQUEST + SPI_SYN) 20462306a36Sopenharmony_ci#define SPI_MLME_START_REQUEST (MLME_START_REQUEST + SPI_SYN) 20562306a36Sopenharmony_ci#define SPI_MLME_RESET_CONFIRM (MLME_RESET_CONFIRM + SPI_S2M + SPI_SYN) 20662306a36Sopenharmony_ci#define SPI_MLME_SET_CONFIRM (MLME_SET_CONFIRM + SPI_S2M + SPI_SYN) 20762306a36Sopenharmony_ci#define SPI_MLME_START_CONFIRM (MLME_START_CONFIRM + SPI_S2M + SPI_SYN) 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define SPI_HWME_SET_REQUEST (HWME_SET_REQUEST + SPI_SYN) 21062306a36Sopenharmony_ci#define SPI_HWME_GET_REQUEST (HWME_GET_REQUEST + SPI_SYN) 21162306a36Sopenharmony_ci#define SPI_HWME_SET_CONFIRM (HWME_SET_CONFIRM + SPI_S2M + SPI_SYN) 21262306a36Sopenharmony_ci#define SPI_HWME_GET_CONFIRM (HWME_GET_CONFIRM + SPI_S2M + SPI_SYN) 21362306a36Sopenharmony_ci#define SPI_HWME_WAKEUP_INDICATION (HWME_WAKEUP_INDICATION + SPI_S2M) 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#define SPI_TDME_SETSFR_REQUEST (TDME_SETSFR_REQUEST + SPI_SYN) 21662306a36Sopenharmony_ci#define SPI_TDME_SET_REQUEST (TDME_SET_REQUEST + SPI_SYN) 21762306a36Sopenharmony_ci#define SPI_TDME_SETSFR_CONFIRM (TDME_SETSFR_CONFIRM + SPI_S2M + SPI_SYN) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* TDME SFR addresses */ 22062306a36Sopenharmony_ci/* Page 0 */ 22162306a36Sopenharmony_ci#define CA8210_SFR_PACFG (0xB1) 22262306a36Sopenharmony_ci#define CA8210_SFR_MACCON (0xD8) 22362306a36Sopenharmony_ci#define CA8210_SFR_PACFGIB (0xFE) 22462306a36Sopenharmony_ci/* Page 1 */ 22562306a36Sopenharmony_ci#define CA8210_SFR_LOTXCAL (0xBF) 22662306a36Sopenharmony_ci#define CA8210_SFR_PTHRH (0xD1) 22762306a36Sopenharmony_ci#define CA8210_SFR_PRECFG (0xD3) 22862306a36Sopenharmony_ci#define CA8210_SFR_LNAGX40 (0xE1) 22962306a36Sopenharmony_ci#define CA8210_SFR_LNAGX41 (0xE2) 23062306a36Sopenharmony_ci#define CA8210_SFR_LNAGX42 (0xE3) 23162306a36Sopenharmony_ci#define CA8210_SFR_LNAGX43 (0xE4) 23262306a36Sopenharmony_ci#define CA8210_SFR_LNAGX44 (0xE5) 23362306a36Sopenharmony_ci#define CA8210_SFR_LNAGX45 (0xE6) 23462306a36Sopenharmony_ci#define CA8210_SFR_LNAGX46 (0xE7) 23562306a36Sopenharmony_ci#define CA8210_SFR_LNAGX47 (0xE9) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define PACFGIB_DEFAULT_CURRENT (0x3F) 23862306a36Sopenharmony_ci#define PTHRH_DEFAULT_THRESHOLD (0x5A) 23962306a36Sopenharmony_ci#define LNAGX40_DEFAULT_GAIN (0x29) /* 10dB */ 24062306a36Sopenharmony_ci#define LNAGX41_DEFAULT_GAIN (0x54) /* 21dB */ 24162306a36Sopenharmony_ci#define LNAGX42_DEFAULT_GAIN (0x6C) /* 27dB */ 24262306a36Sopenharmony_ci#define LNAGX43_DEFAULT_GAIN (0x7A) /* 30dB */ 24362306a36Sopenharmony_ci#define LNAGX44_DEFAULT_GAIN (0x84) /* 33dB */ 24462306a36Sopenharmony_ci#define LNAGX45_DEFAULT_GAIN (0x8B) /* 34dB */ 24562306a36Sopenharmony_ci#define LNAGX46_DEFAULT_GAIN (0x92) /* 36dB */ 24662306a36Sopenharmony_ci#define LNAGX47_DEFAULT_GAIN (0x96) /* 37dB */ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci#define CA8210_IOCTL_HARD_RESET (0x00) 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* Structs/Enums */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/** 25362306a36Sopenharmony_ci * struct cas_control - spi transfer structure 25462306a36Sopenharmony_ci * @msg: spi_message for each exchange 25562306a36Sopenharmony_ci * @transfer: spi_transfer for each exchange 25662306a36Sopenharmony_ci * @tx_buf: source array for transmission 25762306a36Sopenharmony_ci * @tx_in_buf: array storing bytes received during transmission 25862306a36Sopenharmony_ci * @priv: pointer to private data 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * This structure stores all the necessary data passed around during a single 26162306a36Sopenharmony_ci * spi exchange. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistruct cas_control { 26462306a36Sopenharmony_ci struct spi_message msg; 26562306a36Sopenharmony_ci struct spi_transfer transfer; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci u8 tx_buf[CA8210_SPI_BUF_SIZE]; 26862306a36Sopenharmony_ci u8 tx_in_buf[CA8210_SPI_BUF_SIZE]; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci struct ca8210_priv *priv; 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/** 27462306a36Sopenharmony_ci * struct ca8210_test - ca8210 test interface structure 27562306a36Sopenharmony_ci * @ca8210_dfs_spi_int: pointer to the entry in the debug fs for this device 27662306a36Sopenharmony_ci * @up_fifo: fifo for upstream messages 27762306a36Sopenharmony_ci * @readq: read wait queue 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * This structure stores all the data pertaining to the debug interface 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistruct ca8210_test { 28262306a36Sopenharmony_ci struct dentry *ca8210_dfs_spi_int; 28362306a36Sopenharmony_ci struct kfifo up_fifo; 28462306a36Sopenharmony_ci wait_queue_head_t readq; 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/** 28862306a36Sopenharmony_ci * struct ca8210_priv - ca8210 private data structure 28962306a36Sopenharmony_ci * @spi: pointer to the ca8210 spi device object 29062306a36Sopenharmony_ci * @hw: pointer to the ca8210 ieee802154_hw object 29162306a36Sopenharmony_ci * @hw_registered: true if hw has been registered with ieee802154 29262306a36Sopenharmony_ci * @lock: spinlock protecting the private data area 29362306a36Sopenharmony_ci * @mlme_workqueue: workqueue for triggering MLME Reset 29462306a36Sopenharmony_ci * @irq_workqueue: workqueue for irq processing 29562306a36Sopenharmony_ci * @tx_skb: current socket buffer to transmit 29662306a36Sopenharmony_ci * @nextmsduhandle: msdu handle to pass to the 15.4 MAC layer for the 29762306a36Sopenharmony_ci * next transmission 29862306a36Sopenharmony_ci * @clk: external clock provided by the ca8210 29962306a36Sopenharmony_ci * @last_dsn: sequence number of last data packet received, for 30062306a36Sopenharmony_ci * resend detection 30162306a36Sopenharmony_ci * @test: test interface data section for this instance 30262306a36Sopenharmony_ci * @async_tx_pending: true if an asynchronous transmission was started and 30362306a36Sopenharmony_ci * is not complete 30462306a36Sopenharmony_ci * @sync_command_response: pointer to buffer to fill with sync response 30562306a36Sopenharmony_ci * @ca8210_is_awake: nonzero if ca8210 is initialised, ready for comms 30662306a36Sopenharmony_ci * @sync_down: counts number of downstream synchronous commands 30762306a36Sopenharmony_ci * @sync_up: counts number of upstream synchronous commands 30862306a36Sopenharmony_ci * @spi_transfer_complete: completion object for a single spi_transfer 30962306a36Sopenharmony_ci * @sync_exchange_complete: completion object for a complete synchronous API 31062306a36Sopenharmony_ci * exchange 31162306a36Sopenharmony_ci * @promiscuous: whether the ca8210 is in promiscuous mode or not 31262306a36Sopenharmony_ci * @retries: records how many times the current pending spi 31362306a36Sopenharmony_ci * transfer has been retried 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_cistruct ca8210_priv { 31662306a36Sopenharmony_ci struct spi_device *spi; 31762306a36Sopenharmony_ci struct ieee802154_hw *hw; 31862306a36Sopenharmony_ci bool hw_registered; 31962306a36Sopenharmony_ci spinlock_t lock; 32062306a36Sopenharmony_ci struct workqueue_struct *mlme_workqueue; 32162306a36Sopenharmony_ci struct workqueue_struct *irq_workqueue; 32262306a36Sopenharmony_ci struct sk_buff *tx_skb; 32362306a36Sopenharmony_ci u8 nextmsduhandle; 32462306a36Sopenharmony_ci struct clk *clk; 32562306a36Sopenharmony_ci int last_dsn; 32662306a36Sopenharmony_ci struct ca8210_test test; 32762306a36Sopenharmony_ci bool async_tx_pending; 32862306a36Sopenharmony_ci u8 *sync_command_response; 32962306a36Sopenharmony_ci struct completion ca8210_is_awake; 33062306a36Sopenharmony_ci int sync_down, sync_up; 33162306a36Sopenharmony_ci struct completion spi_transfer_complete, sync_exchange_complete; 33262306a36Sopenharmony_ci bool promiscuous; 33362306a36Sopenharmony_ci int retries; 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/** 33762306a36Sopenharmony_ci * struct work_priv_container - link between a work object and the relevant 33862306a36Sopenharmony_ci * device's private data 33962306a36Sopenharmony_ci * @work: work object being executed 34062306a36Sopenharmony_ci * @priv: device's private data section 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistruct work_priv_container { 34462306a36Sopenharmony_ci struct work_struct work; 34562306a36Sopenharmony_ci struct ca8210_priv *priv; 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/** 34962306a36Sopenharmony_ci * struct ca8210_platform_data - ca8210 platform data structure 35062306a36Sopenharmony_ci * @extclockenable: true if the external clock is to be enabled 35162306a36Sopenharmony_ci * @extclockfreq: frequency of the external clock 35262306a36Sopenharmony_ci * @extclockgpio: ca8210 output gpio of the external clock 35362306a36Sopenharmony_ci * @gpio_reset: gpio number of ca8210 reset line 35462306a36Sopenharmony_ci * @gpio_irq: gpio number of ca8210 interrupt line 35562306a36Sopenharmony_ci * @irq_id: identifier for the ca8210 irq 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistruct ca8210_platform_data { 35962306a36Sopenharmony_ci bool extclockenable; 36062306a36Sopenharmony_ci unsigned int extclockfreq; 36162306a36Sopenharmony_ci unsigned int extclockgpio; 36262306a36Sopenharmony_ci int gpio_reset; 36362306a36Sopenharmony_ci int gpio_irq; 36462306a36Sopenharmony_ci int irq_id; 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/** 36862306a36Sopenharmony_ci * struct fulladdr - full MAC addressing information structure 36962306a36Sopenharmony_ci * @mode: address mode (none, short, extended) 37062306a36Sopenharmony_ci * @pan_id: 16-bit LE pan id 37162306a36Sopenharmony_ci * @address: LE address, variable length as specified by mode 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistruct fulladdr { 37562306a36Sopenharmony_ci u8 mode; 37662306a36Sopenharmony_ci u8 pan_id[2]; 37762306a36Sopenharmony_ci u8 address[8]; 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/** 38162306a36Sopenharmony_ci * union macaddr: generic MAC address container 38262306a36Sopenharmony_ci * @short_address: 16-bit short address 38362306a36Sopenharmony_ci * @ieee_address: 64-bit extended address as LE byte array 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ciunion macaddr { 38762306a36Sopenharmony_ci u16 short_address; 38862306a36Sopenharmony_ci u8 ieee_address[8]; 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/** 39262306a36Sopenharmony_ci * struct secspec: security specification for SAP commands 39362306a36Sopenharmony_ci * @security_level: 0-7, controls level of authentication & encryption 39462306a36Sopenharmony_ci * @key_id_mode: 0-3, specifies how to obtain key 39562306a36Sopenharmony_ci * @key_source: extended key retrieval data 39662306a36Sopenharmony_ci * @key_index: single-byte key identifier 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistruct secspec { 40062306a36Sopenharmony_ci u8 security_level; 40162306a36Sopenharmony_ci u8 key_id_mode; 40262306a36Sopenharmony_ci u8 key_source[8]; 40362306a36Sopenharmony_ci u8 key_index; 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* downlink functions parameter set definitions */ 40762306a36Sopenharmony_cistruct mcps_data_request_pset { 40862306a36Sopenharmony_ci u8 src_addr_mode; 40962306a36Sopenharmony_ci struct fulladdr dst; 41062306a36Sopenharmony_ci u8 msdu_length; 41162306a36Sopenharmony_ci u8 msdu_handle; 41262306a36Sopenharmony_ci u8 tx_options; 41362306a36Sopenharmony_ci u8 msdu[MAX_DATA_SIZE]; 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistruct mlme_set_request_pset { 41762306a36Sopenharmony_ci u8 pib_attribute; 41862306a36Sopenharmony_ci u8 pib_attribute_index; 41962306a36Sopenharmony_ci u8 pib_attribute_length; 42062306a36Sopenharmony_ci u8 pib_attribute_value[MAX_ATTRIBUTE_SIZE]; 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistruct hwme_set_request_pset { 42462306a36Sopenharmony_ci u8 hw_attribute; 42562306a36Sopenharmony_ci u8 hw_attribute_length; 42662306a36Sopenharmony_ci u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistruct hwme_get_request_pset { 43062306a36Sopenharmony_ci u8 hw_attribute; 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistruct tdme_setsfr_request_pset { 43462306a36Sopenharmony_ci u8 sfr_page; 43562306a36Sopenharmony_ci u8 sfr_address; 43662306a36Sopenharmony_ci u8 sfr_value; 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* uplink functions parameter set definitions */ 44062306a36Sopenharmony_cistruct hwme_set_confirm_pset { 44162306a36Sopenharmony_ci u8 status; 44262306a36Sopenharmony_ci u8 hw_attribute; 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistruct hwme_get_confirm_pset { 44662306a36Sopenharmony_ci u8 status; 44762306a36Sopenharmony_ci u8 hw_attribute; 44862306a36Sopenharmony_ci u8 hw_attribute_length; 44962306a36Sopenharmony_ci u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistruct tdme_setsfr_confirm_pset { 45362306a36Sopenharmony_ci u8 status; 45462306a36Sopenharmony_ci u8 sfr_page; 45562306a36Sopenharmony_ci u8 sfr_address; 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistruct mac_message { 45962306a36Sopenharmony_ci u8 command_id; 46062306a36Sopenharmony_ci u8 length; 46162306a36Sopenharmony_ci union { 46262306a36Sopenharmony_ci struct mcps_data_request_pset data_req; 46362306a36Sopenharmony_ci struct mlme_set_request_pset set_req; 46462306a36Sopenharmony_ci struct hwme_set_request_pset hwme_set_req; 46562306a36Sopenharmony_ci struct hwme_get_request_pset hwme_get_req; 46662306a36Sopenharmony_ci struct tdme_setsfr_request_pset tdme_set_sfr_req; 46762306a36Sopenharmony_ci struct hwme_set_confirm_pset hwme_set_cnf; 46862306a36Sopenharmony_ci struct hwme_get_confirm_pset hwme_get_cnf; 46962306a36Sopenharmony_ci struct tdme_setsfr_confirm_pset tdme_set_sfr_cnf; 47062306a36Sopenharmony_ci u8 u8param; 47162306a36Sopenharmony_ci u8 status; 47262306a36Sopenharmony_ci u8 payload[148]; 47362306a36Sopenharmony_ci } pdata; 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciunion pa_cfg_sfr { 47762306a36Sopenharmony_ci struct { 47862306a36Sopenharmony_ci u8 bias_current_trim : 3; 47962306a36Sopenharmony_ci u8 /* reserved */ : 1; 48062306a36Sopenharmony_ci u8 buffer_capacitor_trim : 3; 48162306a36Sopenharmony_ci u8 boost : 1; 48262306a36Sopenharmony_ci }; 48362306a36Sopenharmony_ci u8 paib; 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistruct preamble_cfg_sfr { 48762306a36Sopenharmony_ci u8 timeout_symbols : 3; 48862306a36Sopenharmony_ci u8 acquisition_symbols : 3; 48962306a36Sopenharmony_ci u8 search_symbols : 2; 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int (*cascoda_api_upstream)( 49362306a36Sopenharmony_ci const u8 *buf, 49462306a36Sopenharmony_ci size_t len, 49562306a36Sopenharmony_ci void *device_ref 49662306a36Sopenharmony_ci); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/** 49962306a36Sopenharmony_ci * link_to_linux_err() - Translates an 802.15.4 return code into the closest 50062306a36Sopenharmony_ci * linux error 50162306a36Sopenharmony_ci * @link_status: 802.15.4 status code 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Return: 0 or Linux error code 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_cistatic int link_to_linux_err(int link_status) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci if (link_status < 0) { 50862306a36Sopenharmony_ci /* status is already a Linux code */ 50962306a36Sopenharmony_ci return link_status; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci switch (link_status) { 51262306a36Sopenharmony_ci case IEEE802154_SUCCESS: 51362306a36Sopenharmony_ci case IEEE802154_REALIGNMENT: 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci case IEEE802154_IMPROPER_KEY_TYPE: 51662306a36Sopenharmony_ci return -EKEYREJECTED; 51762306a36Sopenharmony_ci case IEEE802154_IMPROPER_SECURITY_LEVEL: 51862306a36Sopenharmony_ci case IEEE802154_UNSUPPORTED_LEGACY: 51962306a36Sopenharmony_ci case IEEE802154_DENIED: 52062306a36Sopenharmony_ci return -EACCES; 52162306a36Sopenharmony_ci case IEEE802154_BEACON_LOST: 52262306a36Sopenharmony_ci case IEEE802154_NO_ACK: 52362306a36Sopenharmony_ci case IEEE802154_NO_BEACON: 52462306a36Sopenharmony_ci return -ENETUNREACH; 52562306a36Sopenharmony_ci case IEEE802154_CHANNEL_ACCESS_FAILURE: 52662306a36Sopenharmony_ci case IEEE802154_TX_ACTIVE: 52762306a36Sopenharmony_ci case IEEE802154_SCAN_IN_PROGRESS: 52862306a36Sopenharmony_ci return -EBUSY; 52962306a36Sopenharmony_ci case IEEE802154_DISABLE_TRX_FAILURE: 53062306a36Sopenharmony_ci case IEEE802154_OUT_OF_CAP: 53162306a36Sopenharmony_ci return -EAGAIN; 53262306a36Sopenharmony_ci case IEEE802154_FRAME_TOO_LONG: 53362306a36Sopenharmony_ci return -EMSGSIZE; 53462306a36Sopenharmony_ci case IEEE802154_INVALID_GTS: 53562306a36Sopenharmony_ci case IEEE802154_PAST_TIME: 53662306a36Sopenharmony_ci return -EBADSLT; 53762306a36Sopenharmony_ci case IEEE802154_INVALID_HANDLE: 53862306a36Sopenharmony_ci return -EBADMSG; 53962306a36Sopenharmony_ci case IEEE802154_INVALID_PARAMETER: 54062306a36Sopenharmony_ci case IEEE802154_UNSUPPORTED_ATTRIBUTE: 54162306a36Sopenharmony_ci case IEEE802154_ON_TIME_TOO_LONG: 54262306a36Sopenharmony_ci case IEEE802154_INVALID_INDEX: 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci case IEEE802154_NO_DATA: 54562306a36Sopenharmony_ci return -ENODATA; 54662306a36Sopenharmony_ci case IEEE802154_NO_SHORT_ADDRESS: 54762306a36Sopenharmony_ci return -EFAULT; 54862306a36Sopenharmony_ci case IEEE802154_PAN_ID_CONFLICT: 54962306a36Sopenharmony_ci return -EADDRINUSE; 55062306a36Sopenharmony_ci case IEEE802154_TRANSACTION_EXPIRED: 55162306a36Sopenharmony_ci return -ETIME; 55262306a36Sopenharmony_ci case IEEE802154_TRANSACTION_OVERFLOW: 55362306a36Sopenharmony_ci return -ENOBUFS; 55462306a36Sopenharmony_ci case IEEE802154_UNAVAILABLE_KEY: 55562306a36Sopenharmony_ci return -ENOKEY; 55662306a36Sopenharmony_ci case IEEE802154_INVALID_ADDRESS: 55762306a36Sopenharmony_ci return -ENXIO; 55862306a36Sopenharmony_ci case IEEE802154_TRACKING_OFF: 55962306a36Sopenharmony_ci case IEEE802154_SUPERFRAME_OVERLAP: 56062306a36Sopenharmony_ci return -EREMOTEIO; 56162306a36Sopenharmony_ci case IEEE802154_LIMIT_REACHED: 56262306a36Sopenharmony_ci return -EDQUOT; 56362306a36Sopenharmony_ci case IEEE802154_READ_ONLY: 56462306a36Sopenharmony_ci return -EROFS; 56562306a36Sopenharmony_ci default: 56662306a36Sopenharmony_ci return -EPROTO; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/** 57162306a36Sopenharmony_ci * ca8210_test_int_driver_write() - Writes a message to the test interface to be 57262306a36Sopenharmony_ci * read by the userspace 57362306a36Sopenharmony_ci * @buf: Buffer containing upstream message 57462306a36Sopenharmony_ci * @len: length of message to write 57562306a36Sopenharmony_ci * @spi: SPI device of message originator 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci * Return: 0 or linux error code 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic int ca8210_test_int_driver_write( 58062306a36Sopenharmony_ci const u8 *buf, 58162306a36Sopenharmony_ci size_t len, 58262306a36Sopenharmony_ci void *spi 58362306a36Sopenharmony_ci) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct ca8210_priv *priv = spi_get_drvdata(spi); 58662306a36Sopenharmony_ci struct ca8210_test *test = &priv->test; 58762306a36Sopenharmony_ci char *fifo_buffer; 58862306a36Sopenharmony_ci int i; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci dev_dbg( 59162306a36Sopenharmony_ci &priv->spi->dev, 59262306a36Sopenharmony_ci "test_interface: Buffering upstream message:\n" 59362306a36Sopenharmony_ci ); 59462306a36Sopenharmony_ci for (i = 0; i < len; i++) 59562306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "%#03x\n", buf[i]); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci fifo_buffer = kmemdup(buf, len, GFP_KERNEL); 59862306a36Sopenharmony_ci if (!fifo_buffer) 59962306a36Sopenharmony_ci return -ENOMEM; 60062306a36Sopenharmony_ci kfifo_in(&test->up_fifo, &fifo_buffer, 4); 60162306a36Sopenharmony_ci wake_up_interruptible(&priv->test.readq); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* SPI Operation */ 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic int ca8210_net_rx( 60962306a36Sopenharmony_ci struct ieee802154_hw *hw, 61062306a36Sopenharmony_ci u8 *command, 61162306a36Sopenharmony_ci size_t len 61262306a36Sopenharmony_ci); 61362306a36Sopenharmony_cistatic u8 mlme_reset_request_sync( 61462306a36Sopenharmony_ci u8 set_default_pib, 61562306a36Sopenharmony_ci void *device_ref 61662306a36Sopenharmony_ci); 61762306a36Sopenharmony_cistatic int ca8210_spi_transfer( 61862306a36Sopenharmony_ci struct spi_device *spi, 61962306a36Sopenharmony_ci const u8 *buf, 62062306a36Sopenharmony_ci size_t len 62162306a36Sopenharmony_ci); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/** 62462306a36Sopenharmony_ci * ca8210_reset_send() - Hard resets the ca8210 for a given time 62562306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 62662306a36Sopenharmony_ci * @ms: Milliseconds to hold the reset line low for 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cistatic void ca8210_reset_send(struct spi_device *spi, unsigned int ms) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct ca8210_platform_data *pdata = spi->dev.platform_data; 63162306a36Sopenharmony_ci struct ca8210_priv *priv = spi_get_drvdata(spi); 63262306a36Sopenharmony_ci long status; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci gpio_set_value(pdata->gpio_reset, 0); 63562306a36Sopenharmony_ci reinit_completion(&priv->ca8210_is_awake); 63662306a36Sopenharmony_ci msleep(ms); 63762306a36Sopenharmony_ci gpio_set_value(pdata->gpio_reset, 1); 63862306a36Sopenharmony_ci priv->promiscuous = false; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Wait until wakeup indication seen */ 64162306a36Sopenharmony_ci status = wait_for_completion_interruptible_timeout( 64262306a36Sopenharmony_ci &priv->ca8210_is_awake, 64362306a36Sopenharmony_ci msecs_to_jiffies(CA8210_SYNC_TIMEOUT) 64462306a36Sopenharmony_ci ); 64562306a36Sopenharmony_ci if (status == 0) { 64662306a36Sopenharmony_ci dev_crit( 64762306a36Sopenharmony_ci &spi->dev, 64862306a36Sopenharmony_ci "Fatal: No wakeup from ca8210 after reset!\n" 64962306a36Sopenharmony_ci ); 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci dev_dbg(&spi->dev, "Reset the device\n"); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/** 65662306a36Sopenharmony_ci * ca8210_mlme_reset_worker() - Resets the MLME, Called when the MAC OVERFLOW 65762306a36Sopenharmony_ci * condition happens. 65862306a36Sopenharmony_ci * @work: Pointer to work being executed 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_cistatic void ca8210_mlme_reset_worker(struct work_struct *work) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct work_priv_container *wpc = container_of( 66362306a36Sopenharmony_ci work, 66462306a36Sopenharmony_ci struct work_priv_container, 66562306a36Sopenharmony_ci work 66662306a36Sopenharmony_ci ); 66762306a36Sopenharmony_ci struct ca8210_priv *priv = wpc->priv; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci mlme_reset_request_sync(0, priv->spi); 67062306a36Sopenharmony_ci kfree(wpc); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/** 67462306a36Sopenharmony_ci * ca8210_rx_done() - Calls various message dispatches responding to a received 67562306a36Sopenharmony_ci * command 67662306a36Sopenharmony_ci * @cas_ctl: Pointer to the cas_control object for the relevant spi transfer 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * Presents a received SAP command from the ca8210 to the Cascoda EVBME, test 67962306a36Sopenharmony_ci * interface and network driver. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic void ca8210_rx_done(struct cas_control *cas_ctl) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci u8 *buf; 68462306a36Sopenharmony_ci unsigned int len; 68562306a36Sopenharmony_ci struct work_priv_container *mlme_reset_wpc; 68662306a36Sopenharmony_ci struct ca8210_priv *priv = cas_ctl->priv; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci buf = cas_ctl->tx_in_buf; 68962306a36Sopenharmony_ci len = buf[1] + 2; 69062306a36Sopenharmony_ci if (len > CA8210_SPI_BUF_SIZE) { 69162306a36Sopenharmony_ci dev_crit( 69262306a36Sopenharmony_ci &priv->spi->dev, 69362306a36Sopenharmony_ci "Received packet len (%u) erroneously long\n", 69462306a36Sopenharmony_ci len 69562306a36Sopenharmony_ci ); 69662306a36Sopenharmony_ci goto finish; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (buf[0] & SPI_SYN) { 70062306a36Sopenharmony_ci if (priv->sync_command_response) { 70162306a36Sopenharmony_ci memcpy(priv->sync_command_response, buf, len); 70262306a36Sopenharmony_ci complete(&priv->sync_exchange_complete); 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci if (cascoda_api_upstream) 70562306a36Sopenharmony_ci cascoda_api_upstream(buf, len, priv->spi); 70662306a36Sopenharmony_ci priv->sync_up++; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci } else { 70962306a36Sopenharmony_ci if (cascoda_api_upstream) 71062306a36Sopenharmony_ci cascoda_api_upstream(buf, len, priv->spi); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ca8210_net_rx(priv->hw, buf, len); 71462306a36Sopenharmony_ci if (buf[0] == SPI_MCPS_DATA_CONFIRM) { 71562306a36Sopenharmony_ci if (buf[3] == IEEE802154_TRANSACTION_OVERFLOW) { 71662306a36Sopenharmony_ci dev_info( 71762306a36Sopenharmony_ci &priv->spi->dev, 71862306a36Sopenharmony_ci "Waiting for transaction overflow to stabilise...\n"); 71962306a36Sopenharmony_ci msleep(2000); 72062306a36Sopenharmony_ci dev_info( 72162306a36Sopenharmony_ci &priv->spi->dev, 72262306a36Sopenharmony_ci "Resetting MAC...\n"); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mlme_reset_wpc = kmalloc(sizeof(*mlme_reset_wpc), 72562306a36Sopenharmony_ci GFP_KERNEL); 72662306a36Sopenharmony_ci if (!mlme_reset_wpc) 72762306a36Sopenharmony_ci goto finish; 72862306a36Sopenharmony_ci INIT_WORK( 72962306a36Sopenharmony_ci &mlme_reset_wpc->work, 73062306a36Sopenharmony_ci ca8210_mlme_reset_worker 73162306a36Sopenharmony_ci ); 73262306a36Sopenharmony_ci mlme_reset_wpc->priv = priv; 73362306a36Sopenharmony_ci queue_work(priv->mlme_workqueue, &mlme_reset_wpc->work); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci } else if (buf[0] == SPI_HWME_WAKEUP_INDICATION) { 73662306a36Sopenharmony_ci dev_notice( 73762306a36Sopenharmony_ci &priv->spi->dev, 73862306a36Sopenharmony_ci "Wakeup indication received, reason:\n" 73962306a36Sopenharmony_ci ); 74062306a36Sopenharmony_ci switch (buf[2]) { 74162306a36Sopenharmony_ci case 0: 74262306a36Sopenharmony_ci dev_notice( 74362306a36Sopenharmony_ci &priv->spi->dev, 74462306a36Sopenharmony_ci "Transceiver woken up from Power Up / System Reset\n" 74562306a36Sopenharmony_ci ); 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case 1: 74862306a36Sopenharmony_ci dev_notice( 74962306a36Sopenharmony_ci &priv->spi->dev, 75062306a36Sopenharmony_ci "Watchdog Timer Time-Out\n" 75162306a36Sopenharmony_ci ); 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case 2: 75462306a36Sopenharmony_ci dev_notice( 75562306a36Sopenharmony_ci &priv->spi->dev, 75662306a36Sopenharmony_ci "Transceiver woken up from Power-Off by Sleep Timer Time-Out\n"); 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case 3: 75962306a36Sopenharmony_ci dev_notice( 76062306a36Sopenharmony_ci &priv->spi->dev, 76162306a36Sopenharmony_ci "Transceiver woken up from Power-Off by GPIO Activity\n" 76262306a36Sopenharmony_ci ); 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci case 4: 76562306a36Sopenharmony_ci dev_notice( 76662306a36Sopenharmony_ci &priv->spi->dev, 76762306a36Sopenharmony_ci "Transceiver woken up from Standby by Sleep Timer Time-Out\n" 76862306a36Sopenharmony_ci ); 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case 5: 77162306a36Sopenharmony_ci dev_notice( 77262306a36Sopenharmony_ci &priv->spi->dev, 77362306a36Sopenharmony_ci "Transceiver woken up from Standby by GPIO Activity\n" 77462306a36Sopenharmony_ci ); 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci case 6: 77762306a36Sopenharmony_ci dev_notice( 77862306a36Sopenharmony_ci &priv->spi->dev, 77962306a36Sopenharmony_ci "Sleep-Timer Time-Out in Active Mode\n" 78062306a36Sopenharmony_ci ); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci default: 78362306a36Sopenharmony_ci dev_warn(&priv->spi->dev, "Wakeup reason unknown\n"); 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci complete(&priv->ca8210_is_awake); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cifinish:; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic void ca8210_remove(struct spi_device *spi_device); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/** 79562306a36Sopenharmony_ci * ca8210_spi_transfer_complete() - Called when a single spi transfer has 79662306a36Sopenharmony_ci * completed 79762306a36Sopenharmony_ci * @context: Pointer to the cas_control object for the finished transfer 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_cistatic void ca8210_spi_transfer_complete(void *context) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct cas_control *cas_ctl = context; 80262306a36Sopenharmony_ci struct ca8210_priv *priv = cas_ctl->priv; 80362306a36Sopenharmony_ci bool duplex_rx = false; 80462306a36Sopenharmony_ci int i; 80562306a36Sopenharmony_ci u8 retry_buffer[CA8210_SPI_BUF_SIZE]; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if ( 80862306a36Sopenharmony_ci cas_ctl->tx_in_buf[0] == SPI_NACK || 80962306a36Sopenharmony_ci (cas_ctl->tx_in_buf[0] == SPI_IDLE && 81062306a36Sopenharmony_ci cas_ctl->tx_in_buf[1] == SPI_NACK) 81162306a36Sopenharmony_ci ) { 81262306a36Sopenharmony_ci /* ca8210 is busy */ 81362306a36Sopenharmony_ci dev_info(&priv->spi->dev, "ca8210 was busy during attempted write\n"); 81462306a36Sopenharmony_ci if (cas_ctl->tx_buf[0] == SPI_IDLE) { 81562306a36Sopenharmony_ci dev_warn( 81662306a36Sopenharmony_ci &priv->spi->dev, 81762306a36Sopenharmony_ci "IRQ servicing NACKd, dropping transfer\n" 81862306a36Sopenharmony_ci ); 81962306a36Sopenharmony_ci kfree(cas_ctl); 82062306a36Sopenharmony_ci return; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci if (priv->retries > 3) { 82362306a36Sopenharmony_ci dev_err(&priv->spi->dev, "too many retries!\n"); 82462306a36Sopenharmony_ci kfree(cas_ctl); 82562306a36Sopenharmony_ci ca8210_remove(priv->spi); 82662306a36Sopenharmony_ci return; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci memcpy(retry_buffer, cas_ctl->tx_buf, CA8210_SPI_BUF_SIZE); 82962306a36Sopenharmony_ci kfree(cas_ctl); 83062306a36Sopenharmony_ci ca8210_spi_transfer( 83162306a36Sopenharmony_ci priv->spi, 83262306a36Sopenharmony_ci retry_buffer, 83362306a36Sopenharmony_ci CA8210_SPI_BUF_SIZE 83462306a36Sopenharmony_ci ); 83562306a36Sopenharmony_ci priv->retries++; 83662306a36Sopenharmony_ci dev_info(&priv->spi->dev, "retried spi write\n"); 83762306a36Sopenharmony_ci return; 83862306a36Sopenharmony_ci } else if ( 83962306a36Sopenharmony_ci cas_ctl->tx_in_buf[0] != SPI_IDLE && 84062306a36Sopenharmony_ci cas_ctl->tx_in_buf[0] != SPI_NACK 84162306a36Sopenharmony_ci ) { 84262306a36Sopenharmony_ci duplex_rx = true; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (duplex_rx) { 84662306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "READ CMD DURING TX\n"); 84762306a36Sopenharmony_ci for (i = 0; i < cas_ctl->tx_in_buf[1] + 2; i++) 84862306a36Sopenharmony_ci dev_dbg( 84962306a36Sopenharmony_ci &priv->spi->dev, 85062306a36Sopenharmony_ci "%#03x\n", 85162306a36Sopenharmony_ci cas_ctl->tx_in_buf[i] 85262306a36Sopenharmony_ci ); 85362306a36Sopenharmony_ci ca8210_rx_done(cas_ctl); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci complete(&priv->spi_transfer_complete); 85662306a36Sopenharmony_ci kfree(cas_ctl); 85762306a36Sopenharmony_ci priv->retries = 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/** 86162306a36Sopenharmony_ci * ca8210_spi_transfer() - Initiate duplex spi transfer with ca8210 86262306a36Sopenharmony_ci * @spi: Pointer to spi device for transfer 86362306a36Sopenharmony_ci * @buf: Octet array to send 86462306a36Sopenharmony_ci * @len: length of the buffer being sent 86562306a36Sopenharmony_ci * 86662306a36Sopenharmony_ci * Return: 0 or linux error code 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_cistatic int ca8210_spi_transfer( 86962306a36Sopenharmony_ci struct spi_device *spi, 87062306a36Sopenharmony_ci const u8 *buf, 87162306a36Sopenharmony_ci size_t len 87262306a36Sopenharmony_ci) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci int i, status = 0; 87562306a36Sopenharmony_ci struct ca8210_priv *priv; 87662306a36Sopenharmony_ci struct cas_control *cas_ctl; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (!spi) { 87962306a36Sopenharmony_ci pr_crit("NULL spi device passed to %s\n", __func__); 88062306a36Sopenharmony_ci return -ENODEV; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci priv = spi_get_drvdata(spi); 88462306a36Sopenharmony_ci reinit_completion(&priv->spi_transfer_complete); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci dev_dbg(&spi->dev, "%s called\n", __func__); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci cas_ctl = kzalloc(sizeof(*cas_ctl), GFP_ATOMIC); 88962306a36Sopenharmony_ci if (!cas_ctl) 89062306a36Sopenharmony_ci return -ENOMEM; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci cas_ctl->priv = priv; 89362306a36Sopenharmony_ci memset(cas_ctl->tx_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); 89462306a36Sopenharmony_ci memset(cas_ctl->tx_in_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); 89562306a36Sopenharmony_ci memcpy(cas_ctl->tx_buf, buf, len); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci for (i = 0; i < len; i++) 89862306a36Sopenharmony_ci dev_dbg(&spi->dev, "%#03x\n", cas_ctl->tx_buf[i]); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci spi_message_init(&cas_ctl->msg); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci cas_ctl->transfer.tx_nbits = 1; /* 1 MOSI line */ 90362306a36Sopenharmony_ci cas_ctl->transfer.rx_nbits = 1; /* 1 MISO line */ 90462306a36Sopenharmony_ci cas_ctl->transfer.speed_hz = 0; /* Use device setting */ 90562306a36Sopenharmony_ci cas_ctl->transfer.bits_per_word = 0; /* Use device setting */ 90662306a36Sopenharmony_ci cas_ctl->transfer.tx_buf = cas_ctl->tx_buf; 90762306a36Sopenharmony_ci cas_ctl->transfer.rx_buf = cas_ctl->tx_in_buf; 90862306a36Sopenharmony_ci cas_ctl->transfer.delay.value = 0; 90962306a36Sopenharmony_ci cas_ctl->transfer.delay.unit = SPI_DELAY_UNIT_USECS; 91062306a36Sopenharmony_ci cas_ctl->transfer.cs_change = 0; 91162306a36Sopenharmony_ci cas_ctl->transfer.len = sizeof(struct mac_message); 91262306a36Sopenharmony_ci cas_ctl->msg.complete = ca8210_spi_transfer_complete; 91362306a36Sopenharmony_ci cas_ctl->msg.context = cas_ctl; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci spi_message_add_tail( 91662306a36Sopenharmony_ci &cas_ctl->transfer, 91762306a36Sopenharmony_ci &cas_ctl->msg 91862306a36Sopenharmony_ci ); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci status = spi_async(spi, &cas_ctl->msg); 92162306a36Sopenharmony_ci if (status < 0) { 92262306a36Sopenharmony_ci dev_crit( 92362306a36Sopenharmony_ci &spi->dev, 92462306a36Sopenharmony_ci "status %d from spi_sync in write\n", 92562306a36Sopenharmony_ci status 92662306a36Sopenharmony_ci ); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return status; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/** 93362306a36Sopenharmony_ci * ca8210_spi_exchange() - Exchange API/SAP commands with the radio 93462306a36Sopenharmony_ci * @buf: Octet array of command being sent downstream 93562306a36Sopenharmony_ci * @len: length of buf 93662306a36Sopenharmony_ci * @response: buffer for storing synchronous response 93762306a36Sopenharmony_ci * @device_ref: spi_device pointer for ca8210 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * Effectively calls ca8210_spi_transfer to write buf[] to the spi, then for 94062306a36Sopenharmony_ci * synchronous commands waits for the corresponding response to be read from 94162306a36Sopenharmony_ci * the spi before returning. The response is written to the response parameter. 94262306a36Sopenharmony_ci * 94362306a36Sopenharmony_ci * Return: 0 or linux error code 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_cistatic int ca8210_spi_exchange( 94662306a36Sopenharmony_ci const u8 *buf, 94762306a36Sopenharmony_ci size_t len, 94862306a36Sopenharmony_ci u8 *response, 94962306a36Sopenharmony_ci void *device_ref 95062306a36Sopenharmony_ci) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci int status = 0; 95362306a36Sopenharmony_ci struct spi_device *spi = device_ref; 95462306a36Sopenharmony_ci struct ca8210_priv *priv = spi->dev.driver_data; 95562306a36Sopenharmony_ci long wait_remaining; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if ((buf[0] & SPI_SYN) && response) { /* if sync wait for confirm */ 95862306a36Sopenharmony_ci reinit_completion(&priv->sync_exchange_complete); 95962306a36Sopenharmony_ci priv->sync_command_response = response; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci do { 96362306a36Sopenharmony_ci reinit_completion(&priv->spi_transfer_complete); 96462306a36Sopenharmony_ci status = ca8210_spi_transfer(priv->spi, buf, len); 96562306a36Sopenharmony_ci if (status) { 96662306a36Sopenharmony_ci dev_warn( 96762306a36Sopenharmony_ci &spi->dev, 96862306a36Sopenharmony_ci "spi write failed, returned %d\n", 96962306a36Sopenharmony_ci status 97062306a36Sopenharmony_ci ); 97162306a36Sopenharmony_ci if (status == -EBUSY) 97262306a36Sopenharmony_ci continue; 97362306a36Sopenharmony_ci if (((buf[0] & SPI_SYN) && response)) 97462306a36Sopenharmony_ci complete(&priv->sync_exchange_complete); 97562306a36Sopenharmony_ci goto cleanup; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci wait_remaining = wait_for_completion_interruptible_timeout( 97962306a36Sopenharmony_ci &priv->spi_transfer_complete, 98062306a36Sopenharmony_ci msecs_to_jiffies(1000) 98162306a36Sopenharmony_ci ); 98262306a36Sopenharmony_ci if (wait_remaining == -ERESTARTSYS) { 98362306a36Sopenharmony_ci status = -ERESTARTSYS; 98462306a36Sopenharmony_ci } else if (wait_remaining == 0) { 98562306a36Sopenharmony_ci dev_err( 98662306a36Sopenharmony_ci &spi->dev, 98762306a36Sopenharmony_ci "SPI downstream transfer timed out!\n" 98862306a36Sopenharmony_ci ); 98962306a36Sopenharmony_ci status = -ETIME; 99062306a36Sopenharmony_ci goto cleanup; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci } while (status < 0); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (!((buf[0] & SPI_SYN) && response)) 99562306a36Sopenharmony_ci goto cleanup; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci wait_remaining = wait_for_completion_interruptible_timeout( 99862306a36Sopenharmony_ci &priv->sync_exchange_complete, 99962306a36Sopenharmony_ci msecs_to_jiffies(CA8210_SYNC_TIMEOUT) 100062306a36Sopenharmony_ci ); 100162306a36Sopenharmony_ci if (wait_remaining == -ERESTARTSYS) { 100262306a36Sopenharmony_ci status = -ERESTARTSYS; 100362306a36Sopenharmony_ci } else if (wait_remaining == 0) { 100462306a36Sopenharmony_ci dev_err( 100562306a36Sopenharmony_ci &spi->dev, 100662306a36Sopenharmony_ci "Synchronous confirm timeout\n" 100762306a36Sopenharmony_ci ); 100862306a36Sopenharmony_ci status = -ETIME; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cicleanup: 101262306a36Sopenharmony_ci priv->sync_command_response = NULL; 101362306a36Sopenharmony_ci return status; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci/** 101762306a36Sopenharmony_ci * ca8210_interrupt_handler() - Called when an irq is received from the ca8210 101862306a36Sopenharmony_ci * @irq: Id of the irq being handled 101962306a36Sopenharmony_ci * @dev_id: Pointer passed by the system, pointing to the ca8210's private data 102062306a36Sopenharmony_ci * 102162306a36Sopenharmony_ci * This function is called when the irq line from the ca8210 is asserted, 102262306a36Sopenharmony_ci * signifying that the ca8210 has a message to send upstream to us. Starts the 102362306a36Sopenharmony_ci * asynchronous spi read. 102462306a36Sopenharmony_ci * 102562306a36Sopenharmony_ci * Return: irq return code 102662306a36Sopenharmony_ci */ 102762306a36Sopenharmony_cistatic irqreturn_t ca8210_interrupt_handler(int irq, void *dev_id) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct ca8210_priv *priv = dev_id; 103062306a36Sopenharmony_ci int status; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "irq: Interrupt occurred\n"); 103362306a36Sopenharmony_ci do { 103462306a36Sopenharmony_ci status = ca8210_spi_transfer(priv->spi, NULL, 0); 103562306a36Sopenharmony_ci if (status && (status != -EBUSY)) { 103662306a36Sopenharmony_ci dev_warn( 103762306a36Sopenharmony_ci &priv->spi->dev, 103862306a36Sopenharmony_ci "spi read failed, returned %d\n", 103962306a36Sopenharmony_ci status 104062306a36Sopenharmony_ci ); 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci } while (status == -EBUSY); 104362306a36Sopenharmony_ci return IRQ_HANDLED; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic int (*cascoda_api_downstream)( 104762306a36Sopenharmony_ci const u8 *buf, 104862306a36Sopenharmony_ci size_t len, 104962306a36Sopenharmony_ci u8 *response, 105062306a36Sopenharmony_ci void *device_ref 105162306a36Sopenharmony_ci) = ca8210_spi_exchange; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* Cascoda API / 15.4 SAP Primitives */ 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/** 105662306a36Sopenharmony_ci * tdme_setsfr_request_sync() - TDME_SETSFR_request/confirm according to API 105762306a36Sopenharmony_ci * @sfr_page: SFR Page 105862306a36Sopenharmony_ci * @sfr_address: SFR Address 105962306a36Sopenharmony_ci * @sfr_value: SFR Value 106062306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 106162306a36Sopenharmony_ci * 106262306a36Sopenharmony_ci * Return: 802.15.4 status code of TDME-SETSFR.confirm 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_cistatic u8 tdme_setsfr_request_sync( 106562306a36Sopenharmony_ci u8 sfr_page, 106662306a36Sopenharmony_ci u8 sfr_address, 106762306a36Sopenharmony_ci u8 sfr_value, 106862306a36Sopenharmony_ci void *device_ref 106962306a36Sopenharmony_ci) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci int ret; 107262306a36Sopenharmony_ci struct mac_message command, response; 107362306a36Sopenharmony_ci struct spi_device *spi = device_ref; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci command.command_id = SPI_TDME_SETSFR_REQUEST; 107662306a36Sopenharmony_ci command.length = 3; 107762306a36Sopenharmony_ci command.pdata.tdme_set_sfr_req.sfr_page = sfr_page; 107862306a36Sopenharmony_ci command.pdata.tdme_set_sfr_req.sfr_address = sfr_address; 107962306a36Sopenharmony_ci command.pdata.tdme_set_sfr_req.sfr_value = sfr_value; 108062306a36Sopenharmony_ci response.command_id = SPI_IDLE; 108162306a36Sopenharmony_ci ret = cascoda_api_downstream( 108262306a36Sopenharmony_ci &command.command_id, 108362306a36Sopenharmony_ci command.length + 2, 108462306a36Sopenharmony_ci &response.command_id, 108562306a36Sopenharmony_ci device_ref 108662306a36Sopenharmony_ci ); 108762306a36Sopenharmony_ci if (ret) { 108862306a36Sopenharmony_ci dev_crit(&spi->dev, "cascoda_api_downstream returned %d", ret); 108962306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (response.command_id != SPI_TDME_SETSFR_CONFIRM) { 109362306a36Sopenharmony_ci dev_crit( 109462306a36Sopenharmony_ci &spi->dev, 109562306a36Sopenharmony_ci "sync response to SPI_TDME_SETSFR_REQUEST was not SPI_TDME_SETSFR_CONFIRM, it was %d\n", 109662306a36Sopenharmony_ci response.command_id 109762306a36Sopenharmony_ci ); 109862306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return response.pdata.tdme_set_sfr_cnf.status; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/** 110562306a36Sopenharmony_ci * tdme_chipinit() - TDME Chip Register Default Initialisation Macro 110662306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 110762306a36Sopenharmony_ci * 110862306a36Sopenharmony_ci * Return: 802.15.4 status code of API calls 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_cistatic u8 tdme_chipinit(void *device_ref) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci u8 status = IEEE802154_SUCCESS; 111362306a36Sopenharmony_ci u8 sfr_address; 111462306a36Sopenharmony_ci struct spi_device *spi = device_ref; 111562306a36Sopenharmony_ci struct preamble_cfg_sfr pre_cfg_value = { 111662306a36Sopenharmony_ci .timeout_symbols = 3, 111762306a36Sopenharmony_ci .acquisition_symbols = 3, 111862306a36Sopenharmony_ci .search_symbols = 1, 111962306a36Sopenharmony_ci }; 112062306a36Sopenharmony_ci /* LNA Gain Settings */ 112162306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 112262306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX40), 112362306a36Sopenharmony_ci LNAGX40_DEFAULT_GAIN, device_ref); 112462306a36Sopenharmony_ci if (status) 112562306a36Sopenharmony_ci goto finish; 112662306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 112762306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX41), 112862306a36Sopenharmony_ci LNAGX41_DEFAULT_GAIN, device_ref); 112962306a36Sopenharmony_ci if (status) 113062306a36Sopenharmony_ci goto finish; 113162306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 113262306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX42), 113362306a36Sopenharmony_ci LNAGX42_DEFAULT_GAIN, device_ref); 113462306a36Sopenharmony_ci if (status) 113562306a36Sopenharmony_ci goto finish; 113662306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 113762306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX43), 113862306a36Sopenharmony_ci LNAGX43_DEFAULT_GAIN, device_ref); 113962306a36Sopenharmony_ci if (status) 114062306a36Sopenharmony_ci goto finish; 114162306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 114262306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX44), 114362306a36Sopenharmony_ci LNAGX44_DEFAULT_GAIN, device_ref); 114462306a36Sopenharmony_ci if (status) 114562306a36Sopenharmony_ci goto finish; 114662306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 114762306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX45), 114862306a36Sopenharmony_ci LNAGX45_DEFAULT_GAIN, device_ref); 114962306a36Sopenharmony_ci if (status) 115062306a36Sopenharmony_ci goto finish; 115162306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 115262306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX46), 115362306a36Sopenharmony_ci LNAGX46_DEFAULT_GAIN, device_ref); 115462306a36Sopenharmony_ci if (status) 115562306a36Sopenharmony_ci goto finish; 115662306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 115762306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_LNAGX47), 115862306a36Sopenharmony_ci LNAGX47_DEFAULT_GAIN, device_ref); 115962306a36Sopenharmony_ci if (status) 116062306a36Sopenharmony_ci goto finish; 116162306a36Sopenharmony_ci /* Preamble Timing Config */ 116262306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 116362306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_PRECFG), 116462306a36Sopenharmony_ci *((u8 *)&pre_cfg_value), device_ref); 116562306a36Sopenharmony_ci if (status) 116662306a36Sopenharmony_ci goto finish; 116762306a36Sopenharmony_ci /* Preamble Threshold High */ 116862306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 116962306a36Sopenharmony_ci 1, (sfr_address = CA8210_SFR_PTHRH), 117062306a36Sopenharmony_ci PTHRH_DEFAULT_THRESHOLD, device_ref); 117162306a36Sopenharmony_ci if (status) 117262306a36Sopenharmony_ci goto finish; 117362306a36Sopenharmony_ci /* Tx Output Power 8 dBm */ 117462306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 117562306a36Sopenharmony_ci 0, (sfr_address = CA8210_SFR_PACFGIB), 117662306a36Sopenharmony_ci PACFGIB_DEFAULT_CURRENT, device_ref); 117762306a36Sopenharmony_ci if (status) 117862306a36Sopenharmony_ci goto finish; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cifinish: 118162306a36Sopenharmony_ci if (status != IEEE802154_SUCCESS) { 118262306a36Sopenharmony_ci dev_err( 118362306a36Sopenharmony_ci &spi->dev, 118462306a36Sopenharmony_ci "failed to set sfr at %#03x, status = %#03x\n", 118562306a36Sopenharmony_ci sfr_address, 118662306a36Sopenharmony_ci status 118762306a36Sopenharmony_ci ); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci return status; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci/** 119362306a36Sopenharmony_ci * tdme_channelinit() - TDME Channel Register Default Initialisation Macro (Tx) 119462306a36Sopenharmony_ci * @channel: 802.15.4 channel to initialise chip for 119562306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 119662306a36Sopenharmony_ci * 119762306a36Sopenharmony_ci * Return: 802.15.4 status code of API calls 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_cistatic u8 tdme_channelinit(u8 channel, void *device_ref) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci /* Transceiver front-end local oscillator tx two-point calibration 120262306a36Sopenharmony_ci * value. Tuned for the hardware. 120362306a36Sopenharmony_ci */ 120462306a36Sopenharmony_ci u8 txcalval; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (channel >= 25) 120762306a36Sopenharmony_ci txcalval = 0xA7; 120862306a36Sopenharmony_ci else if (channel >= 23) 120962306a36Sopenharmony_ci txcalval = 0xA8; 121062306a36Sopenharmony_ci else if (channel >= 22) 121162306a36Sopenharmony_ci txcalval = 0xA9; 121262306a36Sopenharmony_ci else if (channel >= 20) 121362306a36Sopenharmony_ci txcalval = 0xAA; 121462306a36Sopenharmony_ci else if (channel >= 17) 121562306a36Sopenharmony_ci txcalval = 0xAB; 121662306a36Sopenharmony_ci else if (channel >= 16) 121762306a36Sopenharmony_ci txcalval = 0xAC; 121862306a36Sopenharmony_ci else if (channel >= 14) 121962306a36Sopenharmony_ci txcalval = 0xAD; 122062306a36Sopenharmony_ci else if (channel >= 12) 122162306a36Sopenharmony_ci txcalval = 0xAE; 122262306a36Sopenharmony_ci else 122362306a36Sopenharmony_ci txcalval = 0xAF; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci return tdme_setsfr_request_sync( 122662306a36Sopenharmony_ci 1, 122762306a36Sopenharmony_ci CA8210_SFR_LOTXCAL, 122862306a36Sopenharmony_ci txcalval, 122962306a36Sopenharmony_ci device_ref 123062306a36Sopenharmony_ci ); /* LO Tx Cal */ 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci/** 123462306a36Sopenharmony_ci * tdme_checkpibattribute() - Checks Attribute Values that are not checked in 123562306a36Sopenharmony_ci * MAC 123662306a36Sopenharmony_ci * @pib_attribute: Attribute Number 123762306a36Sopenharmony_ci * @pib_attribute_length: Attribute length 123862306a36Sopenharmony_ci * @pib_attribute_value: Pointer to Attribute Value 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci * Return: 802.15.4 status code of checks 124162306a36Sopenharmony_ci */ 124262306a36Sopenharmony_cistatic u8 tdme_checkpibattribute( 124362306a36Sopenharmony_ci u8 pib_attribute, 124462306a36Sopenharmony_ci u8 pib_attribute_length, 124562306a36Sopenharmony_ci const void *pib_attribute_value 124662306a36Sopenharmony_ci) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci u8 status = IEEE802154_SUCCESS; 124962306a36Sopenharmony_ci u8 value; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci value = *((u8 *)pib_attribute_value); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci switch (pib_attribute) { 125462306a36Sopenharmony_ci /* PHY */ 125562306a36Sopenharmony_ci case PHY_TRANSMIT_POWER: 125662306a36Sopenharmony_ci if (value > 0x3F) 125762306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci case PHY_CCA_MODE: 126062306a36Sopenharmony_ci if (value > 0x03) 126162306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci /* MAC */ 126462306a36Sopenharmony_ci case MAC_BATT_LIFE_EXT_PERIODS: 126562306a36Sopenharmony_ci if (value < 6 || value > 41) 126662306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 126762306a36Sopenharmony_ci break; 126862306a36Sopenharmony_ci case MAC_BEACON_PAYLOAD: 126962306a36Sopenharmony_ci if (pib_attribute_length > MAX_BEACON_PAYLOAD_LENGTH) 127062306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 127162306a36Sopenharmony_ci break; 127262306a36Sopenharmony_ci case MAC_BEACON_PAYLOAD_LENGTH: 127362306a36Sopenharmony_ci if (value > MAX_BEACON_PAYLOAD_LENGTH) 127462306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci case MAC_BEACON_ORDER: 127762306a36Sopenharmony_ci if (value > 15) 127862306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 127962306a36Sopenharmony_ci break; 128062306a36Sopenharmony_ci case MAC_MAX_BE: 128162306a36Sopenharmony_ci if (value < 3 || value > 8) 128262306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci case MAC_MAX_CSMA_BACKOFFS: 128562306a36Sopenharmony_ci if (value > 5) 128662306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 128762306a36Sopenharmony_ci break; 128862306a36Sopenharmony_ci case MAC_MAX_FRAME_RETRIES: 128962306a36Sopenharmony_ci if (value > 7) 129062306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 129162306a36Sopenharmony_ci break; 129262306a36Sopenharmony_ci case MAC_MIN_BE: 129362306a36Sopenharmony_ci if (value > 8) 129462306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 129562306a36Sopenharmony_ci break; 129662306a36Sopenharmony_ci case MAC_RESPONSE_WAIT_TIME: 129762306a36Sopenharmony_ci if (value < 2 || value > 64) 129862306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci case MAC_SUPERFRAME_ORDER: 130162306a36Sopenharmony_ci if (value > 15) 130262306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 130362306a36Sopenharmony_ci break; 130462306a36Sopenharmony_ci /* boolean */ 130562306a36Sopenharmony_ci case MAC_ASSOCIATED_PAN_COORD: 130662306a36Sopenharmony_ci case MAC_ASSOCIATION_PERMIT: 130762306a36Sopenharmony_ci case MAC_AUTO_REQUEST: 130862306a36Sopenharmony_ci case MAC_BATT_LIFE_EXT: 130962306a36Sopenharmony_ci case MAC_GTS_PERMIT: 131062306a36Sopenharmony_ci case MAC_PROMISCUOUS_MODE: 131162306a36Sopenharmony_ci case MAC_RX_ON_WHEN_IDLE: 131262306a36Sopenharmony_ci case MAC_SECURITY_ENABLED: 131362306a36Sopenharmony_ci if (value > 1) 131462306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 131562306a36Sopenharmony_ci break; 131662306a36Sopenharmony_ci /* MAC SEC */ 131762306a36Sopenharmony_ci case MAC_AUTO_REQUEST_SECURITY_LEVEL: 131862306a36Sopenharmony_ci if (value > 7) 131962306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci case MAC_AUTO_REQUEST_KEY_ID_MODE: 132262306a36Sopenharmony_ci if (value > 3) 132362306a36Sopenharmony_ci status = IEEE802154_INVALID_PARAMETER; 132462306a36Sopenharmony_ci break; 132562306a36Sopenharmony_ci default: 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return status; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci/** 133362306a36Sopenharmony_ci * tdme_settxpower() - Sets the tx power for MLME_SET phyTransmitPower 133462306a36Sopenharmony_ci * @txp: Transmit Power 133562306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 133662306a36Sopenharmony_ci * 133762306a36Sopenharmony_ci * Normalised to 802.15.4 Definition (6-bit, signed): 133862306a36Sopenharmony_ci * Bit 7-6: not used 133962306a36Sopenharmony_ci * Bit 5-0: tx power (-32 - +31 dB) 134062306a36Sopenharmony_ci * 134162306a36Sopenharmony_ci * Return: 802.15.4 status code of api calls 134262306a36Sopenharmony_ci */ 134362306a36Sopenharmony_cistatic u8 tdme_settxpower(u8 txp, void *device_ref) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci u8 status; 134662306a36Sopenharmony_ci s8 txp_val; 134762306a36Sopenharmony_ci u8 txp_ext; 134862306a36Sopenharmony_ci union pa_cfg_sfr pa_cfg_val; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* extend from 6 to 8 bit */ 135162306a36Sopenharmony_ci txp_ext = 0x3F & txp; 135262306a36Sopenharmony_ci if (txp_ext & 0x20) 135362306a36Sopenharmony_ci txp_ext += 0xC0; 135462306a36Sopenharmony_ci txp_val = (s8)txp_ext; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (CA8210_MAC_MPW) { 135762306a36Sopenharmony_ci if (txp_val > 0) { 135862306a36Sopenharmony_ci /* 8 dBm: ptrim = 5, itrim = +3 => +4 dBm */ 135962306a36Sopenharmony_ci pa_cfg_val.bias_current_trim = 3; 136062306a36Sopenharmony_ci pa_cfg_val.buffer_capacitor_trim = 5; 136162306a36Sopenharmony_ci pa_cfg_val.boost = 1; 136262306a36Sopenharmony_ci } else { 136362306a36Sopenharmony_ci /* 0 dBm: ptrim = 7, itrim = +3 => -6 dBm */ 136462306a36Sopenharmony_ci pa_cfg_val.bias_current_trim = 3; 136562306a36Sopenharmony_ci pa_cfg_val.buffer_capacitor_trim = 7; 136662306a36Sopenharmony_ci pa_cfg_val.boost = 0; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci /* write PACFG */ 136962306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 137062306a36Sopenharmony_ci 0, 137162306a36Sopenharmony_ci CA8210_SFR_PACFG, 137262306a36Sopenharmony_ci pa_cfg_val.paib, 137362306a36Sopenharmony_ci device_ref 137462306a36Sopenharmony_ci ); 137562306a36Sopenharmony_ci } else { 137662306a36Sopenharmony_ci /* Look-Up Table for Setting Current and Frequency Trim values 137762306a36Sopenharmony_ci * for desired Output Power 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_ci if (txp_val > 8) { 138062306a36Sopenharmony_ci pa_cfg_val.paib = 0x3F; 138162306a36Sopenharmony_ci } else if (txp_val == 8) { 138262306a36Sopenharmony_ci pa_cfg_val.paib = 0x32; 138362306a36Sopenharmony_ci } else if (txp_val == 7) { 138462306a36Sopenharmony_ci pa_cfg_val.paib = 0x22; 138562306a36Sopenharmony_ci } else if (txp_val == 6) { 138662306a36Sopenharmony_ci pa_cfg_val.paib = 0x18; 138762306a36Sopenharmony_ci } else if (txp_val == 5) { 138862306a36Sopenharmony_ci pa_cfg_val.paib = 0x10; 138962306a36Sopenharmony_ci } else if (txp_val == 4) { 139062306a36Sopenharmony_ci pa_cfg_val.paib = 0x0C; 139162306a36Sopenharmony_ci } else if (txp_val == 3) { 139262306a36Sopenharmony_ci pa_cfg_val.paib = 0x08; 139362306a36Sopenharmony_ci } else if (txp_val == 2) { 139462306a36Sopenharmony_ci pa_cfg_val.paib = 0x05; 139562306a36Sopenharmony_ci } else if (txp_val == 1) { 139662306a36Sopenharmony_ci pa_cfg_val.paib = 0x03; 139762306a36Sopenharmony_ci } else if (txp_val == 0) { 139862306a36Sopenharmony_ci pa_cfg_val.paib = 0x01; 139962306a36Sopenharmony_ci } else { /* < 0 */ 140062306a36Sopenharmony_ci pa_cfg_val.paib = 0x00; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci /* write PACFGIB */ 140362306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 140462306a36Sopenharmony_ci 0, 140562306a36Sopenharmony_ci CA8210_SFR_PACFGIB, 140662306a36Sopenharmony_ci pa_cfg_val.paib, 140762306a36Sopenharmony_ci device_ref 140862306a36Sopenharmony_ci ); 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci return status; 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci/** 141562306a36Sopenharmony_ci * mcps_data_request() - mcps_data_request (Send Data) according to API Spec 141662306a36Sopenharmony_ci * @src_addr_mode: Source Addressing Mode 141762306a36Sopenharmony_ci * @dst_address_mode: Destination Addressing Mode 141862306a36Sopenharmony_ci * @dst_pan_id: Destination PAN ID 141962306a36Sopenharmony_ci * @dst_addr: Pointer to Destination Address 142062306a36Sopenharmony_ci * @msdu_length: length of Data 142162306a36Sopenharmony_ci * @msdu: Pointer to Data 142262306a36Sopenharmony_ci * @msdu_handle: Handle of Data 142362306a36Sopenharmony_ci * @tx_options: Tx Options Bit Field 142462306a36Sopenharmony_ci * @security: Pointer to Security Structure or NULL 142562306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 142662306a36Sopenharmony_ci * 142762306a36Sopenharmony_ci * Return: 802.15.4 status code of action 142862306a36Sopenharmony_ci */ 142962306a36Sopenharmony_cistatic u8 mcps_data_request( 143062306a36Sopenharmony_ci u8 src_addr_mode, 143162306a36Sopenharmony_ci u8 dst_address_mode, 143262306a36Sopenharmony_ci u16 dst_pan_id, 143362306a36Sopenharmony_ci union macaddr *dst_addr, 143462306a36Sopenharmony_ci u8 msdu_length, 143562306a36Sopenharmony_ci u8 *msdu, 143662306a36Sopenharmony_ci u8 msdu_handle, 143762306a36Sopenharmony_ci u8 tx_options, 143862306a36Sopenharmony_ci struct secspec *security, 143962306a36Sopenharmony_ci void *device_ref 144062306a36Sopenharmony_ci) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci struct secspec *psec; 144362306a36Sopenharmony_ci struct mac_message command; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci command.command_id = SPI_MCPS_DATA_REQUEST; 144662306a36Sopenharmony_ci command.pdata.data_req.src_addr_mode = src_addr_mode; 144762306a36Sopenharmony_ci command.pdata.data_req.dst.mode = dst_address_mode; 144862306a36Sopenharmony_ci if (dst_address_mode != MAC_MODE_NO_ADDR) { 144962306a36Sopenharmony_ci command.pdata.data_req.dst.pan_id[0] = LS_BYTE(dst_pan_id); 145062306a36Sopenharmony_ci command.pdata.data_req.dst.pan_id[1] = MS_BYTE(dst_pan_id); 145162306a36Sopenharmony_ci if (dst_address_mode == MAC_MODE_SHORT_ADDR) { 145262306a36Sopenharmony_ci command.pdata.data_req.dst.address[0] = LS_BYTE( 145362306a36Sopenharmony_ci dst_addr->short_address 145462306a36Sopenharmony_ci ); 145562306a36Sopenharmony_ci command.pdata.data_req.dst.address[1] = MS_BYTE( 145662306a36Sopenharmony_ci dst_addr->short_address 145762306a36Sopenharmony_ci ); 145862306a36Sopenharmony_ci } else { /* MAC_MODE_LONG_ADDR*/ 145962306a36Sopenharmony_ci memcpy( 146062306a36Sopenharmony_ci command.pdata.data_req.dst.address, 146162306a36Sopenharmony_ci dst_addr->ieee_address, 146262306a36Sopenharmony_ci 8 146362306a36Sopenharmony_ci ); 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci command.pdata.data_req.msdu_length = msdu_length; 146762306a36Sopenharmony_ci command.pdata.data_req.msdu_handle = msdu_handle; 146862306a36Sopenharmony_ci command.pdata.data_req.tx_options = tx_options; 146962306a36Sopenharmony_ci memcpy(command.pdata.data_req.msdu, msdu, msdu_length); 147062306a36Sopenharmony_ci psec = (struct secspec *)(command.pdata.data_req.msdu + msdu_length); 147162306a36Sopenharmony_ci command.length = sizeof(struct mcps_data_request_pset) - 147262306a36Sopenharmony_ci MAX_DATA_SIZE + msdu_length; 147362306a36Sopenharmony_ci if (!security || security->security_level == 0) { 147462306a36Sopenharmony_ci psec->security_level = 0; 147562306a36Sopenharmony_ci command.length += 1; 147662306a36Sopenharmony_ci } else { 147762306a36Sopenharmony_ci *psec = *security; 147862306a36Sopenharmony_ci command.length += sizeof(struct secspec); 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (ca8210_spi_transfer(device_ref, &command.command_id, 148262306a36Sopenharmony_ci command.length + 2)) 148362306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci return IEEE802154_SUCCESS; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci/** 148962306a36Sopenharmony_ci * mlme_reset_request_sync() - MLME_RESET_request/confirm according to API Spec 149062306a36Sopenharmony_ci * @set_default_pib: Set defaults in PIB 149162306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 149262306a36Sopenharmony_ci * 149362306a36Sopenharmony_ci * Return: 802.15.4 status code of MLME-RESET.confirm 149462306a36Sopenharmony_ci */ 149562306a36Sopenharmony_cistatic u8 mlme_reset_request_sync( 149662306a36Sopenharmony_ci u8 set_default_pib, 149762306a36Sopenharmony_ci void *device_ref 149862306a36Sopenharmony_ci) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci u8 status; 150162306a36Sopenharmony_ci struct mac_message command, response; 150262306a36Sopenharmony_ci struct spi_device *spi = device_ref; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci command.command_id = SPI_MLME_RESET_REQUEST; 150562306a36Sopenharmony_ci command.length = 1; 150662306a36Sopenharmony_ci command.pdata.u8param = set_default_pib; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (cascoda_api_downstream( 150962306a36Sopenharmony_ci &command.command_id, 151062306a36Sopenharmony_ci command.length + 2, 151162306a36Sopenharmony_ci &response.command_id, 151262306a36Sopenharmony_ci device_ref)) { 151362306a36Sopenharmony_ci dev_err(&spi->dev, "cascoda_api_downstream failed\n"); 151462306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (response.command_id != SPI_MLME_RESET_CONFIRM) 151862306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci status = response.pdata.status; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* reset COORD Bit for Channel Filtering as Coordinator */ 152362306a36Sopenharmony_ci if (CA8210_MAC_WORKAROUNDS && set_default_pib && !status) { 152462306a36Sopenharmony_ci status = tdme_setsfr_request_sync( 152562306a36Sopenharmony_ci 0, 152662306a36Sopenharmony_ci CA8210_SFR_MACCON, 152762306a36Sopenharmony_ci 0, 152862306a36Sopenharmony_ci device_ref 152962306a36Sopenharmony_ci ); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci return status; 153362306a36Sopenharmony_ci} 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci/** 153662306a36Sopenharmony_ci * mlme_set_request_sync() - MLME_SET_request/confirm according to API Spec 153762306a36Sopenharmony_ci * @pib_attribute: Attribute Number 153862306a36Sopenharmony_ci * @pib_attribute_index: Index within Attribute if an Array 153962306a36Sopenharmony_ci * @pib_attribute_length: Attribute length 154062306a36Sopenharmony_ci * @pib_attribute_value: Pointer to Attribute Value 154162306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 154262306a36Sopenharmony_ci * 154362306a36Sopenharmony_ci * Return: 802.15.4 status code of MLME-SET.confirm 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_cistatic u8 mlme_set_request_sync( 154662306a36Sopenharmony_ci u8 pib_attribute, 154762306a36Sopenharmony_ci u8 pib_attribute_index, 154862306a36Sopenharmony_ci u8 pib_attribute_length, 154962306a36Sopenharmony_ci const void *pib_attribute_value, 155062306a36Sopenharmony_ci void *device_ref 155162306a36Sopenharmony_ci) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci u8 status; 155462306a36Sopenharmony_ci struct mac_message command, response; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci /* pre-check the validity of pib_attribute values that are not checked 155762306a36Sopenharmony_ci * in MAC 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci if (tdme_checkpibattribute( 156062306a36Sopenharmony_ci pib_attribute, pib_attribute_length, pib_attribute_value)) { 156162306a36Sopenharmony_ci return IEEE802154_INVALID_PARAMETER; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (pib_attribute == PHY_CURRENT_CHANNEL) { 156562306a36Sopenharmony_ci status = tdme_channelinit( 156662306a36Sopenharmony_ci *((u8 *)pib_attribute_value), 156762306a36Sopenharmony_ci device_ref 156862306a36Sopenharmony_ci ); 156962306a36Sopenharmony_ci if (status) 157062306a36Sopenharmony_ci return status; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (pib_attribute == PHY_TRANSMIT_POWER) { 157462306a36Sopenharmony_ci return tdme_settxpower( 157562306a36Sopenharmony_ci *((u8 *)pib_attribute_value), 157662306a36Sopenharmony_ci device_ref 157762306a36Sopenharmony_ci ); 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci command.command_id = SPI_MLME_SET_REQUEST; 158162306a36Sopenharmony_ci command.length = sizeof(struct mlme_set_request_pset) - 158262306a36Sopenharmony_ci MAX_ATTRIBUTE_SIZE + pib_attribute_length; 158362306a36Sopenharmony_ci command.pdata.set_req.pib_attribute = pib_attribute; 158462306a36Sopenharmony_ci command.pdata.set_req.pib_attribute_index = pib_attribute_index; 158562306a36Sopenharmony_ci command.pdata.set_req.pib_attribute_length = pib_attribute_length; 158662306a36Sopenharmony_ci memcpy( 158762306a36Sopenharmony_ci command.pdata.set_req.pib_attribute_value, 158862306a36Sopenharmony_ci pib_attribute_value, 158962306a36Sopenharmony_ci pib_attribute_length 159062306a36Sopenharmony_ci ); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (cascoda_api_downstream( 159362306a36Sopenharmony_ci &command.command_id, 159462306a36Sopenharmony_ci command.length + 2, 159562306a36Sopenharmony_ci &response.command_id, 159662306a36Sopenharmony_ci device_ref)) { 159762306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci if (response.command_id != SPI_MLME_SET_CONFIRM) 160162306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci return response.pdata.status; 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci/** 160762306a36Sopenharmony_ci * hwme_set_request_sync() - HWME_SET_request/confirm according to API Spec 160862306a36Sopenharmony_ci * @hw_attribute: Attribute Number 160962306a36Sopenharmony_ci * @hw_attribute_length: Attribute length 161062306a36Sopenharmony_ci * @hw_attribute_value: Pointer to Attribute Value 161162306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 161262306a36Sopenharmony_ci * 161362306a36Sopenharmony_ci * Return: 802.15.4 status code of HWME-SET.confirm 161462306a36Sopenharmony_ci */ 161562306a36Sopenharmony_cistatic u8 hwme_set_request_sync( 161662306a36Sopenharmony_ci u8 hw_attribute, 161762306a36Sopenharmony_ci u8 hw_attribute_length, 161862306a36Sopenharmony_ci u8 *hw_attribute_value, 161962306a36Sopenharmony_ci void *device_ref 162062306a36Sopenharmony_ci) 162162306a36Sopenharmony_ci{ 162262306a36Sopenharmony_ci struct mac_message command, response; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci command.command_id = SPI_HWME_SET_REQUEST; 162562306a36Sopenharmony_ci command.length = 2 + hw_attribute_length; 162662306a36Sopenharmony_ci command.pdata.hwme_set_req.hw_attribute = hw_attribute; 162762306a36Sopenharmony_ci command.pdata.hwme_set_req.hw_attribute_length = hw_attribute_length; 162862306a36Sopenharmony_ci memcpy( 162962306a36Sopenharmony_ci command.pdata.hwme_set_req.hw_attribute_value, 163062306a36Sopenharmony_ci hw_attribute_value, 163162306a36Sopenharmony_ci hw_attribute_length 163262306a36Sopenharmony_ci ); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (cascoda_api_downstream( 163562306a36Sopenharmony_ci &command.command_id, 163662306a36Sopenharmony_ci command.length + 2, 163762306a36Sopenharmony_ci &response.command_id, 163862306a36Sopenharmony_ci device_ref)) { 163962306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci if (response.command_id != SPI_HWME_SET_CONFIRM) 164362306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return response.pdata.hwme_set_cnf.status; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/** 164962306a36Sopenharmony_ci * hwme_get_request_sync() - HWME_GET_request/confirm according to API Spec 165062306a36Sopenharmony_ci * @hw_attribute: Attribute Number 165162306a36Sopenharmony_ci * @hw_attribute_length: Attribute length 165262306a36Sopenharmony_ci * @hw_attribute_value: Pointer to Attribute Value 165362306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 165462306a36Sopenharmony_ci * 165562306a36Sopenharmony_ci * Return: 802.15.4 status code of HWME-GET.confirm 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_cistatic u8 hwme_get_request_sync( 165862306a36Sopenharmony_ci u8 hw_attribute, 165962306a36Sopenharmony_ci u8 *hw_attribute_length, 166062306a36Sopenharmony_ci u8 *hw_attribute_value, 166162306a36Sopenharmony_ci void *device_ref 166262306a36Sopenharmony_ci) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci struct mac_message command, response; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci command.command_id = SPI_HWME_GET_REQUEST; 166762306a36Sopenharmony_ci command.length = 1; 166862306a36Sopenharmony_ci command.pdata.hwme_get_req.hw_attribute = hw_attribute; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (cascoda_api_downstream( 167162306a36Sopenharmony_ci &command.command_id, 167262306a36Sopenharmony_ci command.length + 2, 167362306a36Sopenharmony_ci &response.command_id, 167462306a36Sopenharmony_ci device_ref)) { 167562306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (response.command_id != SPI_HWME_GET_CONFIRM) 167962306a36Sopenharmony_ci return IEEE802154_SYSTEM_ERROR; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (response.pdata.hwme_get_cnf.status == IEEE802154_SUCCESS) { 168262306a36Sopenharmony_ci *hw_attribute_length = 168362306a36Sopenharmony_ci response.pdata.hwme_get_cnf.hw_attribute_length; 168462306a36Sopenharmony_ci memcpy( 168562306a36Sopenharmony_ci hw_attribute_value, 168662306a36Sopenharmony_ci response.pdata.hwme_get_cnf.hw_attribute_value, 168762306a36Sopenharmony_ci *hw_attribute_length 168862306a36Sopenharmony_ci ); 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci return response.pdata.hwme_get_cnf.status; 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci/* Network driver operation */ 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci/** 169762306a36Sopenharmony_ci * ca8210_async_xmit_complete() - Called to announce that an asynchronous 169862306a36Sopenharmony_ci * transmission has finished 169962306a36Sopenharmony_ci * @hw: ieee802154_hw of ca8210 that has finished exchange 170062306a36Sopenharmony_ci * @msduhandle: Identifier of transmission that has completed 170162306a36Sopenharmony_ci * @status: Returned 802.15.4 status code of the transmission 170262306a36Sopenharmony_ci * 170362306a36Sopenharmony_ci * Return: 0 or linux error code 170462306a36Sopenharmony_ci */ 170562306a36Sopenharmony_cistatic int ca8210_async_xmit_complete( 170662306a36Sopenharmony_ci struct ieee802154_hw *hw, 170762306a36Sopenharmony_ci u8 msduhandle, 170862306a36Sopenharmony_ci u8 status) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (priv->nextmsduhandle != msduhandle) { 171362306a36Sopenharmony_ci dev_err( 171462306a36Sopenharmony_ci &priv->spi->dev, 171562306a36Sopenharmony_ci "Unexpected msdu_handle on data confirm, Expected %d, got %d\n", 171662306a36Sopenharmony_ci priv->nextmsduhandle, 171762306a36Sopenharmony_ci msduhandle 171862306a36Sopenharmony_ci ); 171962306a36Sopenharmony_ci return -EIO; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci priv->async_tx_pending = false; 172362306a36Sopenharmony_ci priv->nextmsduhandle++; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (status) { 172662306a36Sopenharmony_ci dev_err( 172762306a36Sopenharmony_ci &priv->spi->dev, 172862306a36Sopenharmony_ci "Link transmission unsuccessful, status = %d\n", 172962306a36Sopenharmony_ci status 173062306a36Sopenharmony_ci ); 173162306a36Sopenharmony_ci if (status != IEEE802154_TRANSACTION_OVERFLOW) { 173262306a36Sopenharmony_ci ieee802154_xmit_error(priv->hw, priv->tx_skb, status); 173362306a36Sopenharmony_ci return 0; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci ieee802154_xmit_complete(priv->hw, priv->tx_skb, true); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci return 0; 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/** 174262306a36Sopenharmony_ci * ca8210_skb_rx() - Contructs a properly framed socket buffer from a received 174362306a36Sopenharmony_ci * MCPS_DATA_indication 174462306a36Sopenharmony_ci * @hw: ieee802154_hw that MCPS_DATA_indication was received by 174562306a36Sopenharmony_ci * @len: length of MCPS_DATA_indication 174662306a36Sopenharmony_ci * @data_ind: Octet array of MCPS_DATA_indication 174762306a36Sopenharmony_ci * 174862306a36Sopenharmony_ci * Called by the spi driver whenever a SAP command is received, this function 174962306a36Sopenharmony_ci * will ascertain whether the command is of interest to the network driver and 175062306a36Sopenharmony_ci * take necessary action. 175162306a36Sopenharmony_ci * 175262306a36Sopenharmony_ci * Return: 0 or linux error code 175362306a36Sopenharmony_ci */ 175462306a36Sopenharmony_cistatic int ca8210_skb_rx( 175562306a36Sopenharmony_ci struct ieee802154_hw *hw, 175662306a36Sopenharmony_ci size_t len, 175762306a36Sopenharmony_ci u8 *data_ind 175862306a36Sopenharmony_ci) 175962306a36Sopenharmony_ci{ 176062306a36Sopenharmony_ci struct ieee802154_hdr hdr; 176162306a36Sopenharmony_ci int msdulen; 176262306a36Sopenharmony_ci int hlen; 176362306a36Sopenharmony_ci u8 mpdulinkquality = data_ind[23]; 176462306a36Sopenharmony_ci struct sk_buff *skb; 176562306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci /* Allocate mtu size buffer for every rx packet */ 176862306a36Sopenharmony_ci skb = dev_alloc_skb(IEEE802154_MTU + sizeof(hdr)); 176962306a36Sopenharmony_ci if (!skb) 177062306a36Sopenharmony_ci return -ENOMEM; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci skb_reserve(skb, sizeof(hdr)); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci msdulen = data_ind[22]; /* msdu_length */ 177562306a36Sopenharmony_ci if (msdulen > IEEE802154_MTU) { 177662306a36Sopenharmony_ci dev_err( 177762306a36Sopenharmony_ci &priv->spi->dev, 177862306a36Sopenharmony_ci "received erroneously large msdu length!\n" 177962306a36Sopenharmony_ci ); 178062306a36Sopenharmony_ci kfree_skb(skb); 178162306a36Sopenharmony_ci return -EMSGSIZE; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "skb buffer length = %d\n", msdulen); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (priv->promiscuous) 178662306a36Sopenharmony_ci goto copy_payload; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci /* Populate hdr */ 178962306a36Sopenharmony_ci hdr.sec.level = data_ind[29 + msdulen]; 179062306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "security level: %#03x\n", hdr.sec.level); 179162306a36Sopenharmony_ci if (hdr.sec.level > 0) { 179262306a36Sopenharmony_ci hdr.sec.key_id_mode = data_ind[30 + msdulen]; 179362306a36Sopenharmony_ci memcpy(&hdr.sec.extended_src, &data_ind[31 + msdulen], 8); 179462306a36Sopenharmony_ci hdr.sec.key_id = data_ind[39 + msdulen]; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci hdr.source.mode = data_ind[0]; 179762306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "srcAddrMode: %#03x\n", hdr.source.mode); 179862306a36Sopenharmony_ci hdr.source.pan_id = *(u16 *)&data_ind[1]; 179962306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "srcPanId: %#06x\n", hdr.source.pan_id); 180062306a36Sopenharmony_ci memcpy(&hdr.source.extended_addr, &data_ind[3], 8); 180162306a36Sopenharmony_ci hdr.dest.mode = data_ind[11]; 180262306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "dstAddrMode: %#03x\n", hdr.dest.mode); 180362306a36Sopenharmony_ci hdr.dest.pan_id = *(u16 *)&data_ind[12]; 180462306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "dstPanId: %#06x\n", hdr.dest.pan_id); 180562306a36Sopenharmony_ci memcpy(&hdr.dest.extended_addr, &data_ind[14], 8); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci /* Fill in FC implicitly */ 180862306a36Sopenharmony_ci hdr.fc.type = 1; /* Data frame */ 180962306a36Sopenharmony_ci if (hdr.sec.level) 181062306a36Sopenharmony_ci hdr.fc.security_enabled = 1; 181162306a36Sopenharmony_ci else 181262306a36Sopenharmony_ci hdr.fc.security_enabled = 0; 181362306a36Sopenharmony_ci if (data_ind[1] != data_ind[12] || data_ind[2] != data_ind[13]) 181462306a36Sopenharmony_ci hdr.fc.intra_pan = 1; 181562306a36Sopenharmony_ci else 181662306a36Sopenharmony_ci hdr.fc.intra_pan = 0; 181762306a36Sopenharmony_ci hdr.fc.dest_addr_mode = hdr.dest.mode; 181862306a36Sopenharmony_ci hdr.fc.source_addr_mode = hdr.source.mode; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci /* Add hdr to front of buffer */ 182162306a36Sopenharmony_ci hlen = ieee802154_hdr_push(skb, &hdr); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (hlen < 0) { 182462306a36Sopenharmony_ci dev_crit(&priv->spi->dev, "failed to push mac hdr onto skb!\n"); 182562306a36Sopenharmony_ci kfree_skb(skb); 182662306a36Sopenharmony_ci return hlen; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci skb_reset_mac_header(skb); 183062306a36Sopenharmony_ci skb->mac_len = hlen; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cicopy_payload: 183362306a36Sopenharmony_ci /* Add <msdulen> bytes of space to the back of the buffer */ 183462306a36Sopenharmony_ci /* Copy msdu to skb */ 183562306a36Sopenharmony_ci skb_put_data(skb, &data_ind[29], msdulen); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci ieee802154_rx_irqsafe(hw, skb, mpdulinkquality); 183862306a36Sopenharmony_ci return 0; 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci/** 184262306a36Sopenharmony_ci * ca8210_net_rx() - Acts upon received SAP commands relevant to the network 184362306a36Sopenharmony_ci * driver 184462306a36Sopenharmony_ci * @hw: ieee802154_hw that command was received by 184562306a36Sopenharmony_ci * @command: Octet array of received command 184662306a36Sopenharmony_ci * @len: length of the received command 184762306a36Sopenharmony_ci * 184862306a36Sopenharmony_ci * Called by the spi driver whenever a SAP command is received, this function 184962306a36Sopenharmony_ci * will ascertain whether the command is of interest to the network driver and 185062306a36Sopenharmony_ci * take necessary action. 185162306a36Sopenharmony_ci * 185262306a36Sopenharmony_ci * Return: 0 or linux error code 185362306a36Sopenharmony_ci */ 185462306a36Sopenharmony_cistatic int ca8210_net_rx(struct ieee802154_hw *hw, u8 *command, size_t len) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 185762306a36Sopenharmony_ci unsigned long flags; 185862306a36Sopenharmony_ci u8 status; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "%s: CmdID = %d\n", __func__, command[0]); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (command[0] == SPI_MCPS_DATA_INDICATION) { 186362306a36Sopenharmony_ci /* Received data */ 186462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 186562306a36Sopenharmony_ci if (command[26] == priv->last_dsn) { 186662306a36Sopenharmony_ci dev_dbg( 186762306a36Sopenharmony_ci &priv->spi->dev, 186862306a36Sopenharmony_ci "DSN %d resend received, ignoring...\n", 186962306a36Sopenharmony_ci command[26] 187062306a36Sopenharmony_ci ); 187162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 187262306a36Sopenharmony_ci return 0; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci priv->last_dsn = command[26]; 187562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 187662306a36Sopenharmony_ci return ca8210_skb_rx(hw, len - 2, command + 2); 187762306a36Sopenharmony_ci } else if (command[0] == SPI_MCPS_DATA_CONFIRM) { 187862306a36Sopenharmony_ci status = command[3]; 187962306a36Sopenharmony_ci if (priv->async_tx_pending) { 188062306a36Sopenharmony_ci return ca8210_async_xmit_complete( 188162306a36Sopenharmony_ci hw, 188262306a36Sopenharmony_ci command[2], 188362306a36Sopenharmony_ci status 188462306a36Sopenharmony_ci ); 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return 0; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci/** 189262306a36Sopenharmony_ci * ca8210_skb_tx() - Transmits a given socket buffer using the ca8210 189362306a36Sopenharmony_ci * @skb: Socket buffer to transmit 189462306a36Sopenharmony_ci * @msduhandle: Data identifier to pass to the 802.15.4 MAC 189562306a36Sopenharmony_ci * @priv: Pointer to private data section of target ca8210 189662306a36Sopenharmony_ci * 189762306a36Sopenharmony_ci * Return: 0 or linux error code 189862306a36Sopenharmony_ci */ 189962306a36Sopenharmony_cistatic int ca8210_skb_tx( 190062306a36Sopenharmony_ci struct sk_buff *skb, 190162306a36Sopenharmony_ci u8 msduhandle, 190262306a36Sopenharmony_ci struct ca8210_priv *priv 190362306a36Sopenharmony_ci) 190462306a36Sopenharmony_ci{ 190562306a36Sopenharmony_ci struct ieee802154_hdr header = { }; 190662306a36Sopenharmony_ci struct secspec secspec; 190762306a36Sopenharmony_ci int mac_len, status; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "%s called\n", __func__); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* Get addressing info from skb - ieee802154 layer creates a full 191262306a36Sopenharmony_ci * packet 191362306a36Sopenharmony_ci */ 191462306a36Sopenharmony_ci mac_len = ieee802154_hdr_peek_addrs(skb, &header); 191562306a36Sopenharmony_ci if (mac_len < 0) 191662306a36Sopenharmony_ci return mac_len; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci secspec.security_level = header.sec.level; 191962306a36Sopenharmony_ci secspec.key_id_mode = header.sec.key_id_mode; 192062306a36Sopenharmony_ci if (secspec.key_id_mode == 2) 192162306a36Sopenharmony_ci memcpy(secspec.key_source, &header.sec.short_src, 4); 192262306a36Sopenharmony_ci else if (secspec.key_id_mode == 3) 192362306a36Sopenharmony_ci memcpy(secspec.key_source, &header.sec.extended_src, 8); 192462306a36Sopenharmony_ci secspec.key_index = header.sec.key_id; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Pass to Cascoda API */ 192762306a36Sopenharmony_ci status = mcps_data_request( 192862306a36Sopenharmony_ci header.source.mode, 192962306a36Sopenharmony_ci header.dest.mode, 193062306a36Sopenharmony_ci header.dest.pan_id, 193162306a36Sopenharmony_ci (union macaddr *)&header.dest.extended_addr, 193262306a36Sopenharmony_ci skb->len - mac_len, 193362306a36Sopenharmony_ci &skb->data[mac_len], 193462306a36Sopenharmony_ci msduhandle, 193562306a36Sopenharmony_ci header.fc.ack_request, 193662306a36Sopenharmony_ci &secspec, 193762306a36Sopenharmony_ci priv->spi 193862306a36Sopenharmony_ci ); 193962306a36Sopenharmony_ci return link_to_linux_err(status); 194062306a36Sopenharmony_ci} 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci/** 194362306a36Sopenharmony_ci * ca8210_start() - Starts the network driver 194462306a36Sopenharmony_ci * @hw: ieee802154_hw of ca8210 being started 194562306a36Sopenharmony_ci * 194662306a36Sopenharmony_ci * Return: 0 or linux error code 194762306a36Sopenharmony_ci */ 194862306a36Sopenharmony_cistatic int ca8210_start(struct ieee802154_hw *hw) 194962306a36Sopenharmony_ci{ 195062306a36Sopenharmony_ci int status; 195162306a36Sopenharmony_ci u8 rx_on_when_idle; 195262306a36Sopenharmony_ci u8 lqi_threshold = 0; 195362306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci priv->last_dsn = -1; 195662306a36Sopenharmony_ci /* Turn receiver on when idle for now just to test rx */ 195762306a36Sopenharmony_ci rx_on_when_idle = 1; 195862306a36Sopenharmony_ci status = mlme_set_request_sync( 195962306a36Sopenharmony_ci MAC_RX_ON_WHEN_IDLE, 196062306a36Sopenharmony_ci 0, 196162306a36Sopenharmony_ci 1, 196262306a36Sopenharmony_ci &rx_on_when_idle, 196362306a36Sopenharmony_ci priv->spi 196462306a36Sopenharmony_ci ); 196562306a36Sopenharmony_ci if (status) { 196662306a36Sopenharmony_ci dev_crit( 196762306a36Sopenharmony_ci &priv->spi->dev, 196862306a36Sopenharmony_ci "Setting rx_on_when_idle failed, status = %d\n", 196962306a36Sopenharmony_ci status 197062306a36Sopenharmony_ci ); 197162306a36Sopenharmony_ci return link_to_linux_err(status); 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci status = hwme_set_request_sync( 197462306a36Sopenharmony_ci HWME_LQILIMIT, 197562306a36Sopenharmony_ci 1, 197662306a36Sopenharmony_ci &lqi_threshold, 197762306a36Sopenharmony_ci priv->spi 197862306a36Sopenharmony_ci ); 197962306a36Sopenharmony_ci if (status) { 198062306a36Sopenharmony_ci dev_crit( 198162306a36Sopenharmony_ci &priv->spi->dev, 198262306a36Sopenharmony_ci "Setting lqilimit failed, status = %d\n", 198362306a36Sopenharmony_ci status 198462306a36Sopenharmony_ci ); 198562306a36Sopenharmony_ci return link_to_linux_err(status); 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci return 0; 198962306a36Sopenharmony_ci} 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci/** 199262306a36Sopenharmony_ci * ca8210_stop() - Stops the network driver 199362306a36Sopenharmony_ci * @hw: ieee802154_hw of ca8210 being stopped 199462306a36Sopenharmony_ci * 199562306a36Sopenharmony_ci * Return: 0 or linux error code 199662306a36Sopenharmony_ci */ 199762306a36Sopenharmony_cistatic void ca8210_stop(struct ieee802154_hw *hw) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci/** 200262306a36Sopenharmony_ci * ca8210_xmit_async() - Asynchronously transmits a given socket buffer using 200362306a36Sopenharmony_ci * the ca8210 200462306a36Sopenharmony_ci * @hw: ieee802154_hw of ca8210 to transmit from 200562306a36Sopenharmony_ci * @skb: Socket buffer to transmit 200662306a36Sopenharmony_ci * 200762306a36Sopenharmony_ci * Return: 0 or linux error code 200862306a36Sopenharmony_ci */ 200962306a36Sopenharmony_cistatic int ca8210_xmit_async(struct ieee802154_hw *hw, struct sk_buff *skb) 201062306a36Sopenharmony_ci{ 201162306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 201262306a36Sopenharmony_ci int status; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "calling %s\n", __func__); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci priv->tx_skb = skb; 201762306a36Sopenharmony_ci priv->async_tx_pending = true; 201862306a36Sopenharmony_ci status = ca8210_skb_tx(skb, priv->nextmsduhandle, priv); 201962306a36Sopenharmony_ci return status; 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci/** 202362306a36Sopenharmony_ci * ca8210_get_ed() - Returns the measured energy on the current channel at this 202462306a36Sopenharmony_ci * instant in time 202562306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 202662306a36Sopenharmony_ci * @level: Measured Energy Detect level 202762306a36Sopenharmony_ci * 202862306a36Sopenharmony_ci * Return: 0 or linux error code 202962306a36Sopenharmony_ci */ 203062306a36Sopenharmony_cistatic int ca8210_get_ed(struct ieee802154_hw *hw, u8 *level) 203162306a36Sopenharmony_ci{ 203262306a36Sopenharmony_ci u8 lenvar; 203362306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci return link_to_linux_err( 203662306a36Sopenharmony_ci hwme_get_request_sync(HWME_EDVALUE, &lenvar, level, priv->spi) 203762306a36Sopenharmony_ci ); 203862306a36Sopenharmony_ci} 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci/** 204162306a36Sopenharmony_ci * ca8210_set_channel() - Sets the current operating 802.15.4 channel of the 204262306a36Sopenharmony_ci * ca8210 204362306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 204462306a36Sopenharmony_ci * @page: Channel page to set 204562306a36Sopenharmony_ci * @channel: Channel number to set 204662306a36Sopenharmony_ci * 204762306a36Sopenharmony_ci * Return: 0 or linux error code 204862306a36Sopenharmony_ci */ 204962306a36Sopenharmony_cistatic int ca8210_set_channel( 205062306a36Sopenharmony_ci struct ieee802154_hw *hw, 205162306a36Sopenharmony_ci u8 page, 205262306a36Sopenharmony_ci u8 channel 205362306a36Sopenharmony_ci) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci u8 status; 205662306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci status = mlme_set_request_sync( 205962306a36Sopenharmony_ci PHY_CURRENT_CHANNEL, 206062306a36Sopenharmony_ci 0, 206162306a36Sopenharmony_ci 1, 206262306a36Sopenharmony_ci &channel, 206362306a36Sopenharmony_ci priv->spi 206462306a36Sopenharmony_ci ); 206562306a36Sopenharmony_ci if (status) { 206662306a36Sopenharmony_ci dev_err( 206762306a36Sopenharmony_ci &priv->spi->dev, 206862306a36Sopenharmony_ci "error setting channel, MLME-SET.confirm status = %d\n", 206962306a36Sopenharmony_ci status 207062306a36Sopenharmony_ci ); 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci return link_to_linux_err(status); 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci/** 207662306a36Sopenharmony_ci * ca8210_set_hw_addr_filt() - Sets the address filtering parameters of the 207762306a36Sopenharmony_ci * ca8210 207862306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 207962306a36Sopenharmony_ci * @filt: Filtering parameters 208062306a36Sopenharmony_ci * @changed: Bitmap representing which parameters to change 208162306a36Sopenharmony_ci * 208262306a36Sopenharmony_ci * Effectively just sets the actual addressing information identifying this node 208362306a36Sopenharmony_ci * as all filtering is performed by the ca8210 as detailed in the IEEE 802.15.4 208462306a36Sopenharmony_ci * 2006 specification. 208562306a36Sopenharmony_ci * 208662306a36Sopenharmony_ci * Return: 0 or linux error code 208762306a36Sopenharmony_ci */ 208862306a36Sopenharmony_cistatic int ca8210_set_hw_addr_filt( 208962306a36Sopenharmony_ci struct ieee802154_hw *hw, 209062306a36Sopenharmony_ci struct ieee802154_hw_addr_filt *filt, 209162306a36Sopenharmony_ci unsigned long changed 209262306a36Sopenharmony_ci) 209362306a36Sopenharmony_ci{ 209462306a36Sopenharmony_ci u8 status = 0; 209562306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_PANID_CHANGED) { 209862306a36Sopenharmony_ci status = mlme_set_request_sync( 209962306a36Sopenharmony_ci MAC_PAN_ID, 210062306a36Sopenharmony_ci 0, 210162306a36Sopenharmony_ci 2, 210262306a36Sopenharmony_ci &filt->pan_id, priv->spi 210362306a36Sopenharmony_ci ); 210462306a36Sopenharmony_ci if (status) { 210562306a36Sopenharmony_ci dev_err( 210662306a36Sopenharmony_ci &priv->spi->dev, 210762306a36Sopenharmony_ci "error setting pan id, MLME-SET.confirm status = %d", 210862306a36Sopenharmony_ci status 210962306a36Sopenharmony_ci ); 211062306a36Sopenharmony_ci return link_to_linux_err(status); 211162306a36Sopenharmony_ci } 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_SADDR_CHANGED) { 211462306a36Sopenharmony_ci status = mlme_set_request_sync( 211562306a36Sopenharmony_ci MAC_SHORT_ADDRESS, 211662306a36Sopenharmony_ci 0, 211762306a36Sopenharmony_ci 2, 211862306a36Sopenharmony_ci &filt->short_addr, priv->spi 211962306a36Sopenharmony_ci ); 212062306a36Sopenharmony_ci if (status) { 212162306a36Sopenharmony_ci dev_err( 212262306a36Sopenharmony_ci &priv->spi->dev, 212362306a36Sopenharmony_ci "error setting short address, MLME-SET.confirm status = %d", 212462306a36Sopenharmony_ci status 212562306a36Sopenharmony_ci ); 212662306a36Sopenharmony_ci return link_to_linux_err(status); 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci } 212962306a36Sopenharmony_ci if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { 213062306a36Sopenharmony_ci status = mlme_set_request_sync( 213162306a36Sopenharmony_ci NS_IEEE_ADDRESS, 213262306a36Sopenharmony_ci 0, 213362306a36Sopenharmony_ci 8, 213462306a36Sopenharmony_ci &filt->ieee_addr, 213562306a36Sopenharmony_ci priv->spi 213662306a36Sopenharmony_ci ); 213762306a36Sopenharmony_ci if (status) { 213862306a36Sopenharmony_ci dev_err( 213962306a36Sopenharmony_ci &priv->spi->dev, 214062306a36Sopenharmony_ci "error setting ieee address, MLME-SET.confirm status = %d", 214162306a36Sopenharmony_ci status 214262306a36Sopenharmony_ci ); 214362306a36Sopenharmony_ci return link_to_linux_err(status); 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci /* TODO: Should use MLME_START to set coord bit? */ 214762306a36Sopenharmony_ci return 0; 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci/** 215162306a36Sopenharmony_ci * ca8210_set_tx_power() - Sets the transmit power of the ca8210 215262306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 215362306a36Sopenharmony_ci * @mbm: Transmit power in mBm (dBm*100) 215462306a36Sopenharmony_ci * 215562306a36Sopenharmony_ci * Return: 0 or linux error code 215662306a36Sopenharmony_ci */ 215762306a36Sopenharmony_cistatic int ca8210_set_tx_power(struct ieee802154_hw *hw, s32 mbm) 215862306a36Sopenharmony_ci{ 215962306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci mbm /= 100; 216262306a36Sopenharmony_ci return link_to_linux_err( 216362306a36Sopenharmony_ci mlme_set_request_sync(PHY_TRANSMIT_POWER, 0, 1, &mbm, priv->spi) 216462306a36Sopenharmony_ci ); 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci/** 216862306a36Sopenharmony_ci * ca8210_set_cca_mode() - Sets the clear channel assessment mode of the ca8210 216962306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 217062306a36Sopenharmony_ci * @cca: CCA mode to set 217162306a36Sopenharmony_ci * 217262306a36Sopenharmony_ci * Return: 0 or linux error code 217362306a36Sopenharmony_ci */ 217462306a36Sopenharmony_cistatic int ca8210_set_cca_mode( 217562306a36Sopenharmony_ci struct ieee802154_hw *hw, 217662306a36Sopenharmony_ci const struct wpan_phy_cca *cca 217762306a36Sopenharmony_ci) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci u8 status; 218062306a36Sopenharmony_ci u8 cca_mode; 218162306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci cca_mode = cca->mode & 3; 218462306a36Sopenharmony_ci if (cca_mode == 3 && cca->opt == NL802154_CCA_OPT_ENERGY_CARRIER_OR) { 218562306a36Sopenharmony_ci /* cca_mode 0 == CS OR ED, 3 == CS AND ED */ 218662306a36Sopenharmony_ci cca_mode = 0; 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci status = mlme_set_request_sync( 218962306a36Sopenharmony_ci PHY_CCA_MODE, 219062306a36Sopenharmony_ci 0, 219162306a36Sopenharmony_ci 1, 219262306a36Sopenharmony_ci &cca_mode, 219362306a36Sopenharmony_ci priv->spi 219462306a36Sopenharmony_ci ); 219562306a36Sopenharmony_ci if (status) { 219662306a36Sopenharmony_ci dev_err( 219762306a36Sopenharmony_ci &priv->spi->dev, 219862306a36Sopenharmony_ci "error setting cca mode, MLME-SET.confirm status = %d", 219962306a36Sopenharmony_ci status 220062306a36Sopenharmony_ci ); 220162306a36Sopenharmony_ci } 220262306a36Sopenharmony_ci return link_to_linux_err(status); 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci/** 220662306a36Sopenharmony_ci * ca8210_set_cca_ed_level() - Sets the CCA ED level of the ca8210 220762306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 220862306a36Sopenharmony_ci * @level: ED level to set (in mbm) 220962306a36Sopenharmony_ci * 221062306a36Sopenharmony_ci * Sets the minimum threshold of measured energy above which the ca8210 will 221162306a36Sopenharmony_ci * back off and retry a transmission. 221262306a36Sopenharmony_ci * 221362306a36Sopenharmony_ci * Return: 0 or linux error code 221462306a36Sopenharmony_ci */ 221562306a36Sopenharmony_cistatic int ca8210_set_cca_ed_level(struct ieee802154_hw *hw, s32 level) 221662306a36Sopenharmony_ci{ 221762306a36Sopenharmony_ci u8 status; 221862306a36Sopenharmony_ci u8 ed_threshold = (level / 100) * 2 + 256; 221962306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci status = hwme_set_request_sync( 222262306a36Sopenharmony_ci HWME_EDTHRESHOLD, 222362306a36Sopenharmony_ci 1, 222462306a36Sopenharmony_ci &ed_threshold, 222562306a36Sopenharmony_ci priv->spi 222662306a36Sopenharmony_ci ); 222762306a36Sopenharmony_ci if (status) { 222862306a36Sopenharmony_ci dev_err( 222962306a36Sopenharmony_ci &priv->spi->dev, 223062306a36Sopenharmony_ci "error setting ed threshold, HWME-SET.confirm status = %d", 223162306a36Sopenharmony_ci status 223262306a36Sopenharmony_ci ); 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci return link_to_linux_err(status); 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci/** 223862306a36Sopenharmony_ci * ca8210_set_csma_params() - Sets the CSMA parameters of the ca8210 223962306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 224062306a36Sopenharmony_ci * @min_be: Minimum backoff exponent when backing off a transmission 224162306a36Sopenharmony_ci * @max_be: Maximum backoff exponent when backing off a transmission 224262306a36Sopenharmony_ci * @retries: Number of times to retry after backing off 224362306a36Sopenharmony_ci * 224462306a36Sopenharmony_ci * Return: 0 or linux error code 224562306a36Sopenharmony_ci */ 224662306a36Sopenharmony_cistatic int ca8210_set_csma_params( 224762306a36Sopenharmony_ci struct ieee802154_hw *hw, 224862306a36Sopenharmony_ci u8 min_be, 224962306a36Sopenharmony_ci u8 max_be, 225062306a36Sopenharmony_ci u8 retries 225162306a36Sopenharmony_ci) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci u8 status; 225462306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci status = mlme_set_request_sync(MAC_MIN_BE, 0, 1, &min_be, priv->spi); 225762306a36Sopenharmony_ci if (status) { 225862306a36Sopenharmony_ci dev_err( 225962306a36Sopenharmony_ci &priv->spi->dev, 226062306a36Sopenharmony_ci "error setting min be, MLME-SET.confirm status = %d", 226162306a36Sopenharmony_ci status 226262306a36Sopenharmony_ci ); 226362306a36Sopenharmony_ci return link_to_linux_err(status); 226462306a36Sopenharmony_ci } 226562306a36Sopenharmony_ci status = mlme_set_request_sync(MAC_MAX_BE, 0, 1, &max_be, priv->spi); 226662306a36Sopenharmony_ci if (status) { 226762306a36Sopenharmony_ci dev_err( 226862306a36Sopenharmony_ci &priv->spi->dev, 226962306a36Sopenharmony_ci "error setting max be, MLME-SET.confirm status = %d", 227062306a36Sopenharmony_ci status 227162306a36Sopenharmony_ci ); 227262306a36Sopenharmony_ci return link_to_linux_err(status); 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci status = mlme_set_request_sync( 227562306a36Sopenharmony_ci MAC_MAX_CSMA_BACKOFFS, 227662306a36Sopenharmony_ci 0, 227762306a36Sopenharmony_ci 1, 227862306a36Sopenharmony_ci &retries, 227962306a36Sopenharmony_ci priv->spi 228062306a36Sopenharmony_ci ); 228162306a36Sopenharmony_ci if (status) { 228262306a36Sopenharmony_ci dev_err( 228362306a36Sopenharmony_ci &priv->spi->dev, 228462306a36Sopenharmony_ci "error setting max csma backoffs, MLME-SET.confirm status = %d", 228562306a36Sopenharmony_ci status 228662306a36Sopenharmony_ci ); 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci return link_to_linux_err(status); 228962306a36Sopenharmony_ci} 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci/** 229262306a36Sopenharmony_ci * ca8210_set_frame_retries() - Sets the maximum frame retries of the ca8210 229362306a36Sopenharmony_ci * @hw: ieee802154_hw of target ca8210 229462306a36Sopenharmony_ci * @retries: Number of retries 229562306a36Sopenharmony_ci * 229662306a36Sopenharmony_ci * Sets the number of times to retry a transmission if no acknowledgment was 229762306a36Sopenharmony_ci * received from the other end when one was requested. 229862306a36Sopenharmony_ci * 229962306a36Sopenharmony_ci * Return: 0 or linux error code 230062306a36Sopenharmony_ci */ 230162306a36Sopenharmony_cistatic int ca8210_set_frame_retries(struct ieee802154_hw *hw, s8 retries) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci u8 status; 230462306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci status = mlme_set_request_sync( 230762306a36Sopenharmony_ci MAC_MAX_FRAME_RETRIES, 230862306a36Sopenharmony_ci 0, 230962306a36Sopenharmony_ci 1, 231062306a36Sopenharmony_ci &retries, 231162306a36Sopenharmony_ci priv->spi 231262306a36Sopenharmony_ci ); 231362306a36Sopenharmony_ci if (status) { 231462306a36Sopenharmony_ci dev_err( 231562306a36Sopenharmony_ci &priv->spi->dev, 231662306a36Sopenharmony_ci "error setting frame retries, MLME-SET.confirm status = %d", 231762306a36Sopenharmony_ci status 231862306a36Sopenharmony_ci ); 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci return link_to_linux_err(status); 232162306a36Sopenharmony_ci} 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_cistatic int ca8210_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) 232462306a36Sopenharmony_ci{ 232562306a36Sopenharmony_ci u8 status; 232662306a36Sopenharmony_ci struct ca8210_priv *priv = hw->priv; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci status = mlme_set_request_sync( 232962306a36Sopenharmony_ci MAC_PROMISCUOUS_MODE, 233062306a36Sopenharmony_ci 0, 233162306a36Sopenharmony_ci 1, 233262306a36Sopenharmony_ci (const void *)&on, 233362306a36Sopenharmony_ci priv->spi 233462306a36Sopenharmony_ci ); 233562306a36Sopenharmony_ci if (status) { 233662306a36Sopenharmony_ci dev_err( 233762306a36Sopenharmony_ci &priv->spi->dev, 233862306a36Sopenharmony_ci "error setting promiscuous mode, MLME-SET.confirm status = %d", 233962306a36Sopenharmony_ci status 234062306a36Sopenharmony_ci ); 234162306a36Sopenharmony_ci } else { 234262306a36Sopenharmony_ci priv->promiscuous = on; 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci return link_to_linux_err(status); 234562306a36Sopenharmony_ci} 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_cistatic const struct ieee802154_ops ca8210_phy_ops = { 234862306a36Sopenharmony_ci .start = ca8210_start, 234962306a36Sopenharmony_ci .stop = ca8210_stop, 235062306a36Sopenharmony_ci .xmit_async = ca8210_xmit_async, 235162306a36Sopenharmony_ci .ed = ca8210_get_ed, 235262306a36Sopenharmony_ci .set_channel = ca8210_set_channel, 235362306a36Sopenharmony_ci .set_hw_addr_filt = ca8210_set_hw_addr_filt, 235462306a36Sopenharmony_ci .set_txpower = ca8210_set_tx_power, 235562306a36Sopenharmony_ci .set_cca_mode = ca8210_set_cca_mode, 235662306a36Sopenharmony_ci .set_cca_ed_level = ca8210_set_cca_ed_level, 235762306a36Sopenharmony_ci .set_csma_params = ca8210_set_csma_params, 235862306a36Sopenharmony_ci .set_frame_retries = ca8210_set_frame_retries, 235962306a36Sopenharmony_ci .set_promiscuous_mode = ca8210_set_promiscuous_mode 236062306a36Sopenharmony_ci}; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci/* Test/EVBME Interface */ 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci/** 236562306a36Sopenharmony_ci * ca8210_test_int_open() - Opens the test interface to the userspace 236662306a36Sopenharmony_ci * @inodp: inode representation of file interface 236762306a36Sopenharmony_ci * @filp: file interface 236862306a36Sopenharmony_ci * 236962306a36Sopenharmony_ci * Return: 0 or linux error code 237062306a36Sopenharmony_ci */ 237162306a36Sopenharmony_cistatic int ca8210_test_int_open(struct inode *inodp, struct file *filp) 237262306a36Sopenharmony_ci{ 237362306a36Sopenharmony_ci struct ca8210_priv *priv = inodp->i_private; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci filp->private_data = priv; 237662306a36Sopenharmony_ci return 0; 237762306a36Sopenharmony_ci} 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci/** 238062306a36Sopenharmony_ci * ca8210_test_check_upstream() - Checks a command received from the upstream 238162306a36Sopenharmony_ci * testing interface for required action 238262306a36Sopenharmony_ci * @buf: Buffer containing command to check 238362306a36Sopenharmony_ci * @device_ref: Nondescript pointer to target device 238462306a36Sopenharmony_ci * 238562306a36Sopenharmony_ci * Return: 0 or linux error code 238662306a36Sopenharmony_ci */ 238762306a36Sopenharmony_cistatic int ca8210_test_check_upstream(u8 *buf, void *device_ref) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci int ret; 239062306a36Sopenharmony_ci u8 response[CA8210_SPI_BUF_SIZE]; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci if (buf[0] == SPI_MLME_SET_REQUEST) { 239362306a36Sopenharmony_ci ret = tdme_checkpibattribute(buf[2], buf[4], buf + 5); 239462306a36Sopenharmony_ci if (ret) { 239562306a36Sopenharmony_ci response[0] = SPI_MLME_SET_CONFIRM; 239662306a36Sopenharmony_ci response[1] = 3; 239762306a36Sopenharmony_ci response[2] = IEEE802154_INVALID_PARAMETER; 239862306a36Sopenharmony_ci response[3] = buf[2]; 239962306a36Sopenharmony_ci response[4] = buf[3]; 240062306a36Sopenharmony_ci if (cascoda_api_upstream) 240162306a36Sopenharmony_ci cascoda_api_upstream(response, 5, device_ref); 240262306a36Sopenharmony_ci return ret; 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci if (buf[0] == SPI_MLME_ASSOCIATE_REQUEST) { 240662306a36Sopenharmony_ci return tdme_channelinit(buf[2], device_ref); 240762306a36Sopenharmony_ci } else if (buf[0] == SPI_MLME_START_REQUEST) { 240862306a36Sopenharmony_ci return tdme_channelinit(buf[4], device_ref); 240962306a36Sopenharmony_ci } else if ( 241062306a36Sopenharmony_ci (buf[0] == SPI_MLME_SET_REQUEST) && 241162306a36Sopenharmony_ci (buf[2] == PHY_CURRENT_CHANNEL) 241262306a36Sopenharmony_ci ) { 241362306a36Sopenharmony_ci return tdme_channelinit(buf[5], device_ref); 241462306a36Sopenharmony_ci } else if ( 241562306a36Sopenharmony_ci (buf[0] == SPI_TDME_SET_REQUEST) && 241662306a36Sopenharmony_ci (buf[2] == TDME_CHANNEL) 241762306a36Sopenharmony_ci ) { 241862306a36Sopenharmony_ci return tdme_channelinit(buf[4], device_ref); 241962306a36Sopenharmony_ci } else if ( 242062306a36Sopenharmony_ci (CA8210_MAC_WORKAROUNDS) && 242162306a36Sopenharmony_ci (buf[0] == SPI_MLME_RESET_REQUEST) && 242262306a36Sopenharmony_ci (buf[2] == 1) 242362306a36Sopenharmony_ci ) { 242462306a36Sopenharmony_ci /* reset COORD Bit for Channel Filtering as Coordinator */ 242562306a36Sopenharmony_ci return tdme_setsfr_request_sync( 242662306a36Sopenharmony_ci 0, 242762306a36Sopenharmony_ci CA8210_SFR_MACCON, 242862306a36Sopenharmony_ci 0, 242962306a36Sopenharmony_ci device_ref 243062306a36Sopenharmony_ci ); 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci return 0; 243362306a36Sopenharmony_ci} /* End of EVBMECheckSerialCommand() */ 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci/** 243662306a36Sopenharmony_ci * ca8210_test_int_user_write() - Called by a process in userspace to send a 243762306a36Sopenharmony_ci * message to the ca8210 drivers 243862306a36Sopenharmony_ci * @filp: file interface 243962306a36Sopenharmony_ci * @in_buf: Buffer containing message to write 244062306a36Sopenharmony_ci * @len: length of message 244162306a36Sopenharmony_ci * @off: file offset 244262306a36Sopenharmony_ci * 244362306a36Sopenharmony_ci * Return: 0 or linux error code 244462306a36Sopenharmony_ci */ 244562306a36Sopenharmony_cistatic ssize_t ca8210_test_int_user_write( 244662306a36Sopenharmony_ci struct file *filp, 244762306a36Sopenharmony_ci const char __user *in_buf, 244862306a36Sopenharmony_ci size_t len, 244962306a36Sopenharmony_ci loff_t *off 245062306a36Sopenharmony_ci) 245162306a36Sopenharmony_ci{ 245262306a36Sopenharmony_ci int ret; 245362306a36Sopenharmony_ci struct ca8210_priv *priv = filp->private_data; 245462306a36Sopenharmony_ci u8 command[CA8210_SPI_BUF_SIZE]; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci memset(command, SPI_IDLE, 6); 245762306a36Sopenharmony_ci if (len > CA8210_SPI_BUF_SIZE || len < 2) { 245862306a36Sopenharmony_ci dev_warn( 245962306a36Sopenharmony_ci &priv->spi->dev, 246062306a36Sopenharmony_ci "userspace requested erroneous write length (%zu)\n", 246162306a36Sopenharmony_ci len 246262306a36Sopenharmony_ci ); 246362306a36Sopenharmony_ci return -EBADE; 246462306a36Sopenharmony_ci } 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci ret = copy_from_user(command, in_buf, len); 246762306a36Sopenharmony_ci if (ret) { 246862306a36Sopenharmony_ci dev_err( 246962306a36Sopenharmony_ci &priv->spi->dev, 247062306a36Sopenharmony_ci "%d bytes could not be copied from userspace\n", 247162306a36Sopenharmony_ci ret 247262306a36Sopenharmony_ci ); 247362306a36Sopenharmony_ci return -EIO; 247462306a36Sopenharmony_ci } 247562306a36Sopenharmony_ci if (len != command[1] + 2) { 247662306a36Sopenharmony_ci dev_err( 247762306a36Sopenharmony_ci &priv->spi->dev, 247862306a36Sopenharmony_ci "write len does not match packet length field\n" 247962306a36Sopenharmony_ci ); 248062306a36Sopenharmony_ci return -EBADE; 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci ret = ca8210_test_check_upstream(command, priv->spi); 248462306a36Sopenharmony_ci if (ret == 0) { 248562306a36Sopenharmony_ci ret = ca8210_spi_exchange( 248662306a36Sopenharmony_ci command, 248762306a36Sopenharmony_ci command[1] + 2, 248862306a36Sopenharmony_ci NULL, 248962306a36Sopenharmony_ci priv->spi 249062306a36Sopenharmony_ci ); 249162306a36Sopenharmony_ci if (ret < 0) { 249262306a36Sopenharmony_ci /* effectively 0 bytes were written successfully */ 249362306a36Sopenharmony_ci dev_err( 249462306a36Sopenharmony_ci &priv->spi->dev, 249562306a36Sopenharmony_ci "spi exchange failed\n" 249662306a36Sopenharmony_ci ); 249762306a36Sopenharmony_ci return ret; 249862306a36Sopenharmony_ci } 249962306a36Sopenharmony_ci if (command[0] & SPI_SYN) 250062306a36Sopenharmony_ci priv->sync_down++; 250162306a36Sopenharmony_ci } 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci return len; 250462306a36Sopenharmony_ci} 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci/** 250762306a36Sopenharmony_ci * ca8210_test_int_user_read() - Called by a process in userspace to read a 250862306a36Sopenharmony_ci * message from the ca8210 drivers 250962306a36Sopenharmony_ci * @filp: file interface 251062306a36Sopenharmony_ci * @buf: Buffer to write message to 251162306a36Sopenharmony_ci * @len: length of message to read (ignored) 251262306a36Sopenharmony_ci * @offp: file offset 251362306a36Sopenharmony_ci * 251462306a36Sopenharmony_ci * If the O_NONBLOCK flag was set when opening the file then this function will 251562306a36Sopenharmony_ci * not block, i.e. it will return if the fifo is empty. Otherwise the function 251662306a36Sopenharmony_ci * will block, i.e. wait until new data arrives. 251762306a36Sopenharmony_ci * 251862306a36Sopenharmony_ci * Return: number of bytes read 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_cistatic ssize_t ca8210_test_int_user_read( 252162306a36Sopenharmony_ci struct file *filp, 252262306a36Sopenharmony_ci char __user *buf, 252362306a36Sopenharmony_ci size_t len, 252462306a36Sopenharmony_ci loff_t *offp 252562306a36Sopenharmony_ci) 252662306a36Sopenharmony_ci{ 252762306a36Sopenharmony_ci int i, cmdlen; 252862306a36Sopenharmony_ci struct ca8210_priv *priv = filp->private_data; 252962306a36Sopenharmony_ci unsigned char *fifo_buffer; 253062306a36Sopenharmony_ci unsigned long bytes_not_copied; 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) { 253362306a36Sopenharmony_ci /* Non-blocking mode */ 253462306a36Sopenharmony_ci if (kfifo_is_empty(&priv->test.up_fifo)) 253562306a36Sopenharmony_ci return 0; 253662306a36Sopenharmony_ci } else { 253762306a36Sopenharmony_ci /* Blocking mode */ 253862306a36Sopenharmony_ci wait_event_interruptible( 253962306a36Sopenharmony_ci priv->test.readq, 254062306a36Sopenharmony_ci !kfifo_is_empty(&priv->test.up_fifo) 254162306a36Sopenharmony_ci ); 254262306a36Sopenharmony_ci } 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) { 254562306a36Sopenharmony_ci dev_err( 254662306a36Sopenharmony_ci &priv->spi->dev, 254762306a36Sopenharmony_ci "test_interface: Wrong number of elements popped from upstream fifo\n" 254862306a36Sopenharmony_ci ); 254962306a36Sopenharmony_ci return 0; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci cmdlen = fifo_buffer[1]; 255262306a36Sopenharmony_ci bytes_not_copied = cmdlen + 2; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci bytes_not_copied = copy_to_user(buf, fifo_buffer, bytes_not_copied); 255562306a36Sopenharmony_ci if (bytes_not_copied > 0) { 255662306a36Sopenharmony_ci dev_err( 255762306a36Sopenharmony_ci &priv->spi->dev, 255862306a36Sopenharmony_ci "%lu bytes could not be copied to user space!\n", 255962306a36Sopenharmony_ci bytes_not_copied 256062306a36Sopenharmony_ci ); 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "test_interface: Cmd len = %d\n", cmdlen); 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "test_interface: Read\n"); 256662306a36Sopenharmony_ci for (i = 0; i < cmdlen + 2; i++) 256762306a36Sopenharmony_ci dev_dbg(&priv->spi->dev, "%#03x\n", fifo_buffer[i]); 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci kfree(fifo_buffer); 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci return cmdlen + 2; 257262306a36Sopenharmony_ci} 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci/** 257562306a36Sopenharmony_ci * ca8210_test_int_ioctl() - Called by a process in userspace to enact an 257662306a36Sopenharmony_ci * arbitrary action 257762306a36Sopenharmony_ci * @filp: file interface 257862306a36Sopenharmony_ci * @ioctl_num: which action to enact 257962306a36Sopenharmony_ci * @ioctl_param: arbitrary parameter for the action 258062306a36Sopenharmony_ci * 258162306a36Sopenharmony_ci * Return: status 258262306a36Sopenharmony_ci */ 258362306a36Sopenharmony_cistatic long ca8210_test_int_ioctl( 258462306a36Sopenharmony_ci struct file *filp, 258562306a36Sopenharmony_ci unsigned int ioctl_num, 258662306a36Sopenharmony_ci unsigned long ioctl_param 258762306a36Sopenharmony_ci) 258862306a36Sopenharmony_ci{ 258962306a36Sopenharmony_ci struct ca8210_priv *priv = filp->private_data; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci switch (ioctl_num) { 259262306a36Sopenharmony_ci case CA8210_IOCTL_HARD_RESET: 259362306a36Sopenharmony_ci ca8210_reset_send(priv->spi, ioctl_param); 259462306a36Sopenharmony_ci break; 259562306a36Sopenharmony_ci default: 259662306a36Sopenharmony_ci break; 259762306a36Sopenharmony_ci } 259862306a36Sopenharmony_ci return 0; 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci/** 260262306a36Sopenharmony_ci * ca8210_test_int_poll() - Called by a process in userspace to determine which 260362306a36Sopenharmony_ci * actions are currently possible for the file 260462306a36Sopenharmony_ci * @filp: file interface 260562306a36Sopenharmony_ci * @ptable: poll table 260662306a36Sopenharmony_ci * 260762306a36Sopenharmony_ci * Return: set of poll return flags 260862306a36Sopenharmony_ci */ 260962306a36Sopenharmony_cistatic __poll_t ca8210_test_int_poll( 261062306a36Sopenharmony_ci struct file *filp, 261162306a36Sopenharmony_ci struct poll_table_struct *ptable 261262306a36Sopenharmony_ci) 261362306a36Sopenharmony_ci{ 261462306a36Sopenharmony_ci __poll_t return_flags = 0; 261562306a36Sopenharmony_ci struct ca8210_priv *priv = filp->private_data; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci poll_wait(filp, &priv->test.readq, ptable); 261862306a36Sopenharmony_ci if (!kfifo_is_empty(&priv->test.up_fifo)) 261962306a36Sopenharmony_ci return_flags |= (EPOLLIN | EPOLLRDNORM); 262062306a36Sopenharmony_ci if (wait_event_interruptible( 262162306a36Sopenharmony_ci priv->test.readq, 262262306a36Sopenharmony_ci !kfifo_is_empty(&priv->test.up_fifo))) { 262362306a36Sopenharmony_ci return EPOLLERR; 262462306a36Sopenharmony_ci } 262562306a36Sopenharmony_ci return return_flags; 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_cistatic const struct file_operations test_int_fops = { 262962306a36Sopenharmony_ci .read = ca8210_test_int_user_read, 263062306a36Sopenharmony_ci .write = ca8210_test_int_user_write, 263162306a36Sopenharmony_ci .open = ca8210_test_int_open, 263262306a36Sopenharmony_ci .release = NULL, 263362306a36Sopenharmony_ci .unlocked_ioctl = ca8210_test_int_ioctl, 263462306a36Sopenharmony_ci .poll = ca8210_test_int_poll 263562306a36Sopenharmony_ci}; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci/* Init/Deinit */ 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci/** 264062306a36Sopenharmony_ci * ca8210_get_platform_data() - Populate a ca8210_platform_data object 264162306a36Sopenharmony_ci * @spi_device: Pointer to ca8210 spi device object to get data for 264262306a36Sopenharmony_ci * @pdata: Pointer to ca8210_platform_data object to populate 264362306a36Sopenharmony_ci * 264462306a36Sopenharmony_ci * Return: 0 or linux error code 264562306a36Sopenharmony_ci */ 264662306a36Sopenharmony_cistatic int ca8210_get_platform_data( 264762306a36Sopenharmony_ci struct spi_device *spi_device, 264862306a36Sopenharmony_ci struct ca8210_platform_data *pdata 264962306a36Sopenharmony_ci) 265062306a36Sopenharmony_ci{ 265162306a36Sopenharmony_ci int ret = 0; 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (!spi_device->dev.of_node) 265462306a36Sopenharmony_ci return -EINVAL; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci pdata->extclockenable = of_property_read_bool( 265762306a36Sopenharmony_ci spi_device->dev.of_node, 265862306a36Sopenharmony_ci "extclock-enable" 265962306a36Sopenharmony_ci ); 266062306a36Sopenharmony_ci if (pdata->extclockenable) { 266162306a36Sopenharmony_ci ret = of_property_read_u32( 266262306a36Sopenharmony_ci spi_device->dev.of_node, 266362306a36Sopenharmony_ci "extclock-freq", 266462306a36Sopenharmony_ci &pdata->extclockfreq 266562306a36Sopenharmony_ci ); 266662306a36Sopenharmony_ci if (ret < 0) 266762306a36Sopenharmony_ci return ret; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci ret = of_property_read_u32( 267062306a36Sopenharmony_ci spi_device->dev.of_node, 267162306a36Sopenharmony_ci "extclock-gpio", 267262306a36Sopenharmony_ci &pdata->extclockgpio 267362306a36Sopenharmony_ci ); 267462306a36Sopenharmony_ci } 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci return ret; 267762306a36Sopenharmony_ci} 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci/** 268062306a36Sopenharmony_ci * ca8210_config_extern_clk() - Configure the external clock provided by the 268162306a36Sopenharmony_ci * ca8210 268262306a36Sopenharmony_ci * @pdata: Pointer to ca8210_platform_data containing clock parameters 268362306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 268462306a36Sopenharmony_ci * @on: True to turn the clock on, false to turn off 268562306a36Sopenharmony_ci * 268662306a36Sopenharmony_ci * The external clock is configured with a frequency and output pin taken from 268762306a36Sopenharmony_ci * the platform data. 268862306a36Sopenharmony_ci * 268962306a36Sopenharmony_ci * Return: 0 or linux error code 269062306a36Sopenharmony_ci */ 269162306a36Sopenharmony_cistatic int ca8210_config_extern_clk( 269262306a36Sopenharmony_ci struct ca8210_platform_data *pdata, 269362306a36Sopenharmony_ci struct spi_device *spi, 269462306a36Sopenharmony_ci bool on 269562306a36Sopenharmony_ci) 269662306a36Sopenharmony_ci{ 269762306a36Sopenharmony_ci u8 clkparam[2]; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci if (on) { 270062306a36Sopenharmony_ci dev_info(&spi->dev, "Switching external clock on\n"); 270162306a36Sopenharmony_ci switch (pdata->extclockfreq) { 270262306a36Sopenharmony_ci case SIXTEEN_MHZ: 270362306a36Sopenharmony_ci clkparam[0] = 1; 270462306a36Sopenharmony_ci break; 270562306a36Sopenharmony_ci case EIGHT_MHZ: 270662306a36Sopenharmony_ci clkparam[0] = 2; 270762306a36Sopenharmony_ci break; 270862306a36Sopenharmony_ci case FOUR_MHZ: 270962306a36Sopenharmony_ci clkparam[0] = 3; 271062306a36Sopenharmony_ci break; 271162306a36Sopenharmony_ci case TWO_MHZ: 271262306a36Sopenharmony_ci clkparam[0] = 4; 271362306a36Sopenharmony_ci break; 271462306a36Sopenharmony_ci case ONE_MHZ: 271562306a36Sopenharmony_ci clkparam[0] = 5; 271662306a36Sopenharmony_ci break; 271762306a36Sopenharmony_ci default: 271862306a36Sopenharmony_ci dev_crit(&spi->dev, "Invalid extclock-freq\n"); 271962306a36Sopenharmony_ci return -EINVAL; 272062306a36Sopenharmony_ci } 272162306a36Sopenharmony_ci clkparam[1] = pdata->extclockgpio; 272262306a36Sopenharmony_ci } else { 272362306a36Sopenharmony_ci dev_info(&spi->dev, "Switching external clock off\n"); 272462306a36Sopenharmony_ci clkparam[0] = 0; /* off */ 272562306a36Sopenharmony_ci clkparam[1] = 0; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci return link_to_linux_err( 272862306a36Sopenharmony_ci hwme_set_request_sync(HWME_SYSCLKOUT, 2, clkparam, spi) 272962306a36Sopenharmony_ci ); 273062306a36Sopenharmony_ci} 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci/** 273362306a36Sopenharmony_ci * ca8210_register_ext_clock() - Register ca8210's external clock with kernel 273462306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 273562306a36Sopenharmony_ci * 273662306a36Sopenharmony_ci * Return: 0 or linux error code 273762306a36Sopenharmony_ci */ 273862306a36Sopenharmony_cistatic int ca8210_register_ext_clock(struct spi_device *spi) 273962306a36Sopenharmony_ci{ 274062306a36Sopenharmony_ci struct device_node *np = spi->dev.of_node; 274162306a36Sopenharmony_ci struct ca8210_priv *priv = spi_get_drvdata(spi); 274262306a36Sopenharmony_ci struct ca8210_platform_data *pdata = spi->dev.platform_data; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci if (!np) 274562306a36Sopenharmony_ci return -EFAULT; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci priv->clk = clk_register_fixed_rate( 274862306a36Sopenharmony_ci &spi->dev, 274962306a36Sopenharmony_ci np->name, 275062306a36Sopenharmony_ci NULL, 275162306a36Sopenharmony_ci 0, 275262306a36Sopenharmony_ci pdata->extclockfreq 275362306a36Sopenharmony_ci ); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci if (IS_ERR(priv->clk)) { 275662306a36Sopenharmony_ci dev_crit(&spi->dev, "Failed to register external clk\n"); 275762306a36Sopenharmony_ci return PTR_ERR(priv->clk); 275862306a36Sopenharmony_ci } 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci return of_clk_add_provider(np, of_clk_src_simple_get, priv->clk); 276162306a36Sopenharmony_ci} 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci/** 276462306a36Sopenharmony_ci * ca8210_unregister_ext_clock() - Unregister ca8210's external clock with 276562306a36Sopenharmony_ci * kernel 276662306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 276762306a36Sopenharmony_ci */ 276862306a36Sopenharmony_cistatic void ca8210_unregister_ext_clock(struct spi_device *spi) 276962306a36Sopenharmony_ci{ 277062306a36Sopenharmony_ci struct ca8210_priv *priv = spi_get_drvdata(spi); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(priv->clk)) 277362306a36Sopenharmony_ci return; 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci of_clk_del_provider(spi->dev.of_node); 277662306a36Sopenharmony_ci clk_unregister(priv->clk); 277762306a36Sopenharmony_ci dev_info(&spi->dev, "External clock unregistered\n"); 277862306a36Sopenharmony_ci} 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci/** 278162306a36Sopenharmony_ci * ca8210_reset_init() - Initialise the reset input to the ca8210 278262306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 278362306a36Sopenharmony_ci * 278462306a36Sopenharmony_ci * Return: 0 or linux error code 278562306a36Sopenharmony_ci */ 278662306a36Sopenharmony_cistatic int ca8210_reset_init(struct spi_device *spi) 278762306a36Sopenharmony_ci{ 278862306a36Sopenharmony_ci int ret; 278962306a36Sopenharmony_ci struct ca8210_platform_data *pdata = spi->dev.platform_data; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci pdata->gpio_reset = of_get_named_gpio( 279262306a36Sopenharmony_ci spi->dev.of_node, 279362306a36Sopenharmony_ci "reset-gpio", 279462306a36Sopenharmony_ci 0 279562306a36Sopenharmony_ci ); 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci ret = gpio_direction_output(pdata->gpio_reset, 1); 279862306a36Sopenharmony_ci if (ret < 0) { 279962306a36Sopenharmony_ci dev_crit( 280062306a36Sopenharmony_ci &spi->dev, 280162306a36Sopenharmony_ci "Reset GPIO %d did not set to output mode\n", 280262306a36Sopenharmony_ci pdata->gpio_reset 280362306a36Sopenharmony_ci ); 280462306a36Sopenharmony_ci } 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci return ret; 280762306a36Sopenharmony_ci} 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci/** 281062306a36Sopenharmony_ci * ca8210_interrupt_init() - Initialise the irq output from the ca8210 281162306a36Sopenharmony_ci * @spi: Pointer to target ca8210 spi device 281262306a36Sopenharmony_ci * 281362306a36Sopenharmony_ci * Return: 0 or linux error code 281462306a36Sopenharmony_ci */ 281562306a36Sopenharmony_cistatic int ca8210_interrupt_init(struct spi_device *spi) 281662306a36Sopenharmony_ci{ 281762306a36Sopenharmony_ci int ret; 281862306a36Sopenharmony_ci struct ca8210_platform_data *pdata = spi->dev.platform_data; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci pdata->gpio_irq = of_get_named_gpio( 282162306a36Sopenharmony_ci spi->dev.of_node, 282262306a36Sopenharmony_ci "irq-gpio", 282362306a36Sopenharmony_ci 0 282462306a36Sopenharmony_ci ); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci pdata->irq_id = gpio_to_irq(pdata->gpio_irq); 282762306a36Sopenharmony_ci if (pdata->irq_id < 0) { 282862306a36Sopenharmony_ci dev_crit( 282962306a36Sopenharmony_ci &spi->dev, 283062306a36Sopenharmony_ci "Could not get irq for gpio pin %d\n", 283162306a36Sopenharmony_ci pdata->gpio_irq 283262306a36Sopenharmony_ci ); 283362306a36Sopenharmony_ci gpio_free(pdata->gpio_irq); 283462306a36Sopenharmony_ci return pdata->irq_id; 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci ret = request_irq( 283862306a36Sopenharmony_ci pdata->irq_id, 283962306a36Sopenharmony_ci ca8210_interrupt_handler, 284062306a36Sopenharmony_ci IRQF_TRIGGER_FALLING, 284162306a36Sopenharmony_ci "ca8210-irq", 284262306a36Sopenharmony_ci spi_get_drvdata(spi) 284362306a36Sopenharmony_ci ); 284462306a36Sopenharmony_ci if (ret) { 284562306a36Sopenharmony_ci dev_crit(&spi->dev, "request_irq %d failed\n", pdata->irq_id); 284662306a36Sopenharmony_ci gpio_free(pdata->gpio_irq); 284762306a36Sopenharmony_ci } 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci return ret; 285062306a36Sopenharmony_ci} 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci/** 285362306a36Sopenharmony_ci * ca8210_dev_com_init() - Initialise the spi communication component 285462306a36Sopenharmony_ci * @priv: Pointer to private data structure 285562306a36Sopenharmony_ci * 285662306a36Sopenharmony_ci * Return: 0 or linux error code 285762306a36Sopenharmony_ci */ 285862306a36Sopenharmony_cistatic int ca8210_dev_com_init(struct ca8210_priv *priv) 285962306a36Sopenharmony_ci{ 286062306a36Sopenharmony_ci priv->mlme_workqueue = alloc_ordered_workqueue( 286162306a36Sopenharmony_ci "MLME work queue", 286262306a36Sopenharmony_ci WQ_UNBOUND 286362306a36Sopenharmony_ci ); 286462306a36Sopenharmony_ci if (!priv->mlme_workqueue) { 286562306a36Sopenharmony_ci dev_crit(&priv->spi->dev, "alloc of mlme_workqueue failed!\n"); 286662306a36Sopenharmony_ci return -ENOMEM; 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci priv->irq_workqueue = alloc_ordered_workqueue( 287062306a36Sopenharmony_ci "ca8210 irq worker", 287162306a36Sopenharmony_ci WQ_UNBOUND 287262306a36Sopenharmony_ci ); 287362306a36Sopenharmony_ci if (!priv->irq_workqueue) { 287462306a36Sopenharmony_ci dev_crit(&priv->spi->dev, "alloc of irq_workqueue failed!\n"); 287562306a36Sopenharmony_ci destroy_workqueue(priv->mlme_workqueue); 287662306a36Sopenharmony_ci return -ENOMEM; 287762306a36Sopenharmony_ci } 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci return 0; 288062306a36Sopenharmony_ci} 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci/** 288362306a36Sopenharmony_ci * ca8210_dev_com_clear() - Deinitialise the spi communication component 288462306a36Sopenharmony_ci * @priv: Pointer to private data structure 288562306a36Sopenharmony_ci */ 288662306a36Sopenharmony_cistatic void ca8210_dev_com_clear(struct ca8210_priv *priv) 288762306a36Sopenharmony_ci{ 288862306a36Sopenharmony_ci destroy_workqueue(priv->mlme_workqueue); 288962306a36Sopenharmony_ci destroy_workqueue(priv->irq_workqueue); 289062306a36Sopenharmony_ci} 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci#define CA8210_MAX_TX_POWERS (9) 289362306a36Sopenharmony_cistatic const s32 ca8210_tx_powers[CA8210_MAX_TX_POWERS] = { 289462306a36Sopenharmony_ci 800, 700, 600, 500, 400, 300, 200, 100, 0 289562306a36Sopenharmony_ci}; 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci#define CA8210_MAX_ED_LEVELS (21) 289862306a36Sopenharmony_cistatic const s32 ca8210_ed_levels[CA8210_MAX_ED_LEVELS] = { 289962306a36Sopenharmony_ci -10300, -10250, -10200, -10150, -10100, -10050, -10000, -9950, -9900, 290062306a36Sopenharmony_ci -9850, -9800, -9750, -9700, -9650, -9600, -9550, -9500, -9450, -9400, 290162306a36Sopenharmony_ci -9350, -9300 290262306a36Sopenharmony_ci}; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci/** 290562306a36Sopenharmony_ci * ca8210_hw_setup() - Populate the ieee802154_hw phy attributes with the 290662306a36Sopenharmony_ci * ca8210's defaults 290762306a36Sopenharmony_ci * @ca8210_hw: Pointer to ieee802154_hw to populate 290862306a36Sopenharmony_ci */ 290962306a36Sopenharmony_cistatic void ca8210_hw_setup(struct ieee802154_hw *ca8210_hw) 291062306a36Sopenharmony_ci{ 291162306a36Sopenharmony_ci /* Support channels 11-26 */ 291262306a36Sopenharmony_ci ca8210_hw->phy->supported.channels[0] = CA8210_VALID_CHANNELS; 291362306a36Sopenharmony_ci ca8210_hw->phy->supported.tx_powers_size = CA8210_MAX_TX_POWERS; 291462306a36Sopenharmony_ci ca8210_hw->phy->supported.tx_powers = ca8210_tx_powers; 291562306a36Sopenharmony_ci ca8210_hw->phy->supported.cca_ed_levels_size = CA8210_MAX_ED_LEVELS; 291662306a36Sopenharmony_ci ca8210_hw->phy->supported.cca_ed_levels = ca8210_ed_levels; 291762306a36Sopenharmony_ci ca8210_hw->phy->current_channel = 18; 291862306a36Sopenharmony_ci ca8210_hw->phy->current_page = 0; 291962306a36Sopenharmony_ci ca8210_hw->phy->transmit_power = 800; 292062306a36Sopenharmony_ci ca8210_hw->phy->cca.mode = NL802154_CCA_ENERGY_CARRIER; 292162306a36Sopenharmony_ci ca8210_hw->phy->cca.opt = NL802154_CCA_OPT_ENERGY_CARRIER_AND; 292262306a36Sopenharmony_ci ca8210_hw->phy->cca_ed_level = -9800; 292362306a36Sopenharmony_ci ca8210_hw->phy->symbol_duration = 16; 292462306a36Sopenharmony_ci ca8210_hw->phy->lifs_period = 40 * ca8210_hw->phy->symbol_duration; 292562306a36Sopenharmony_ci ca8210_hw->phy->sifs_period = 12 * ca8210_hw->phy->symbol_duration; 292662306a36Sopenharmony_ci ca8210_hw->flags = 292762306a36Sopenharmony_ci IEEE802154_HW_AFILT | 292862306a36Sopenharmony_ci IEEE802154_HW_OMIT_CKSUM | 292962306a36Sopenharmony_ci IEEE802154_HW_FRAME_RETRIES | 293062306a36Sopenharmony_ci IEEE802154_HW_PROMISCUOUS | 293162306a36Sopenharmony_ci IEEE802154_HW_CSMA_PARAMS; 293262306a36Sopenharmony_ci ca8210_hw->phy->flags = 293362306a36Sopenharmony_ci WPAN_PHY_FLAG_TXPOWER | 293462306a36Sopenharmony_ci WPAN_PHY_FLAG_CCA_ED_LEVEL | 293562306a36Sopenharmony_ci WPAN_PHY_FLAG_CCA_MODE | 293662306a36Sopenharmony_ci WPAN_PHY_FLAG_DATAGRAMS_ONLY; 293762306a36Sopenharmony_ci} 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci/** 294062306a36Sopenharmony_ci * ca8210_test_interface_init() - Initialise the test file interface 294162306a36Sopenharmony_ci * @priv: Pointer to private data structure 294262306a36Sopenharmony_ci * 294362306a36Sopenharmony_ci * Provided as an alternative to the standard linux network interface, the test 294462306a36Sopenharmony_ci * interface exposes a file in the filesystem (ca8210_test) that allows 294562306a36Sopenharmony_ci * 802.15.4 SAP Commands and Cascoda EVBME commands to be sent directly to 294662306a36Sopenharmony_ci * the stack. 294762306a36Sopenharmony_ci * 294862306a36Sopenharmony_ci * Return: 0 or linux error code 294962306a36Sopenharmony_ci */ 295062306a36Sopenharmony_cistatic int ca8210_test_interface_init(struct ca8210_priv *priv) 295162306a36Sopenharmony_ci{ 295262306a36Sopenharmony_ci struct ca8210_test *test = &priv->test; 295362306a36Sopenharmony_ci char node_name[32]; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci snprintf( 295662306a36Sopenharmony_ci node_name, 295762306a36Sopenharmony_ci sizeof(node_name), 295862306a36Sopenharmony_ci "ca8210@%d_%d", 295962306a36Sopenharmony_ci priv->spi->master->bus_num, 296062306a36Sopenharmony_ci spi_get_chipselect(priv->spi, 0) 296162306a36Sopenharmony_ci ); 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci test->ca8210_dfs_spi_int = debugfs_create_file( 296462306a36Sopenharmony_ci node_name, 296562306a36Sopenharmony_ci 0600, /* S_IRUSR | S_IWUSR */ 296662306a36Sopenharmony_ci NULL, 296762306a36Sopenharmony_ci priv, 296862306a36Sopenharmony_ci &test_int_fops 296962306a36Sopenharmony_ci ); 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci debugfs_create_symlink("ca8210", NULL, node_name); 297262306a36Sopenharmony_ci init_waitqueue_head(&test->readq); 297362306a36Sopenharmony_ci return kfifo_alloc( 297462306a36Sopenharmony_ci &test->up_fifo, 297562306a36Sopenharmony_ci CA8210_TEST_INT_FIFO_SIZE, 297662306a36Sopenharmony_ci GFP_KERNEL 297762306a36Sopenharmony_ci ); 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci/** 298162306a36Sopenharmony_ci * ca8210_test_interface_clear() - Deinitialise the test file interface 298262306a36Sopenharmony_ci * @priv: Pointer to private data structure 298362306a36Sopenharmony_ci */ 298462306a36Sopenharmony_cistatic void ca8210_test_interface_clear(struct ca8210_priv *priv) 298562306a36Sopenharmony_ci{ 298662306a36Sopenharmony_ci struct ca8210_test *test = &priv->test; 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci debugfs_remove(test->ca8210_dfs_spi_int); 298962306a36Sopenharmony_ci kfifo_free(&test->up_fifo); 299062306a36Sopenharmony_ci dev_info(&priv->spi->dev, "Test interface removed\n"); 299162306a36Sopenharmony_ci} 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci/** 299462306a36Sopenharmony_ci * ca8210_remove() - Shut down a ca8210 upon being disconnected 299562306a36Sopenharmony_ci * @spi_device: Pointer to spi device data structure 299662306a36Sopenharmony_ci * 299762306a36Sopenharmony_ci * Return: 0 or linux error code 299862306a36Sopenharmony_ci */ 299962306a36Sopenharmony_cistatic void ca8210_remove(struct spi_device *spi_device) 300062306a36Sopenharmony_ci{ 300162306a36Sopenharmony_ci struct ca8210_priv *priv; 300262306a36Sopenharmony_ci struct ca8210_platform_data *pdata; 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci dev_info(&spi_device->dev, "Removing ca8210\n"); 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci pdata = spi_device->dev.platform_data; 300762306a36Sopenharmony_ci if (pdata) { 300862306a36Sopenharmony_ci if (pdata->extclockenable) { 300962306a36Sopenharmony_ci ca8210_unregister_ext_clock(spi_device); 301062306a36Sopenharmony_ci ca8210_config_extern_clk(pdata, spi_device, 0); 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci free_irq(pdata->irq_id, spi_device->dev.driver_data); 301362306a36Sopenharmony_ci kfree(pdata); 301462306a36Sopenharmony_ci spi_device->dev.platform_data = NULL; 301562306a36Sopenharmony_ci } 301662306a36Sopenharmony_ci /* get spi_device private data */ 301762306a36Sopenharmony_ci priv = spi_get_drvdata(spi_device); 301862306a36Sopenharmony_ci if (priv) { 301962306a36Sopenharmony_ci dev_info( 302062306a36Sopenharmony_ci &spi_device->dev, 302162306a36Sopenharmony_ci "sync_down = %d, sync_up = %d\n", 302262306a36Sopenharmony_ci priv->sync_down, 302362306a36Sopenharmony_ci priv->sync_up 302462306a36Sopenharmony_ci ); 302562306a36Sopenharmony_ci ca8210_dev_com_clear(spi_device->dev.driver_data); 302662306a36Sopenharmony_ci if (priv->hw) { 302762306a36Sopenharmony_ci if (priv->hw_registered) 302862306a36Sopenharmony_ci ieee802154_unregister_hw(priv->hw); 302962306a36Sopenharmony_ci ieee802154_free_hw(priv->hw); 303062306a36Sopenharmony_ci priv->hw = NULL; 303162306a36Sopenharmony_ci dev_info( 303262306a36Sopenharmony_ci &spi_device->dev, 303362306a36Sopenharmony_ci "Unregistered & freed ieee802154_hw.\n" 303462306a36Sopenharmony_ci ); 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_IEEE802154_CA8210_DEBUGFS)) 303762306a36Sopenharmony_ci ca8210_test_interface_clear(priv); 303862306a36Sopenharmony_ci } 303962306a36Sopenharmony_ci} 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci/** 304262306a36Sopenharmony_ci * ca8210_probe() - Set up a connected ca8210 upon being detected by the system 304362306a36Sopenharmony_ci * @spi_device: Pointer to spi device data structure 304462306a36Sopenharmony_ci * 304562306a36Sopenharmony_ci * Return: 0 or linux error code 304662306a36Sopenharmony_ci */ 304762306a36Sopenharmony_cistatic int ca8210_probe(struct spi_device *spi_device) 304862306a36Sopenharmony_ci{ 304962306a36Sopenharmony_ci struct ca8210_priv *priv; 305062306a36Sopenharmony_ci struct ieee802154_hw *hw; 305162306a36Sopenharmony_ci struct ca8210_platform_data *pdata; 305262306a36Sopenharmony_ci int ret; 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci dev_info(&spi_device->dev, "Inserting ca8210\n"); 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci /* allocate ieee802154_hw and private data */ 305762306a36Sopenharmony_ci hw = ieee802154_alloc_hw(sizeof(struct ca8210_priv), &ca8210_phy_ops); 305862306a36Sopenharmony_ci if (!hw) { 305962306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ieee802154_alloc_hw failed\n"); 306062306a36Sopenharmony_ci ret = -ENOMEM; 306162306a36Sopenharmony_ci goto error; 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci priv = hw->priv; 306562306a36Sopenharmony_ci priv->hw = hw; 306662306a36Sopenharmony_ci priv->spi = spi_device; 306762306a36Sopenharmony_ci hw->parent = &spi_device->dev; 306862306a36Sopenharmony_ci spin_lock_init(&priv->lock); 306962306a36Sopenharmony_ci priv->async_tx_pending = false; 307062306a36Sopenharmony_ci priv->hw_registered = false; 307162306a36Sopenharmony_ci priv->sync_up = 0; 307262306a36Sopenharmony_ci priv->sync_down = 0; 307362306a36Sopenharmony_ci priv->promiscuous = false; 307462306a36Sopenharmony_ci priv->retries = 0; 307562306a36Sopenharmony_ci init_completion(&priv->ca8210_is_awake); 307662306a36Sopenharmony_ci init_completion(&priv->spi_transfer_complete); 307762306a36Sopenharmony_ci init_completion(&priv->sync_exchange_complete); 307862306a36Sopenharmony_ci spi_set_drvdata(priv->spi, priv); 307962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_IEEE802154_CA8210_DEBUGFS)) { 308062306a36Sopenharmony_ci cascoda_api_upstream = ca8210_test_int_driver_write; 308162306a36Sopenharmony_ci ca8210_test_interface_init(priv); 308262306a36Sopenharmony_ci } else { 308362306a36Sopenharmony_ci cascoda_api_upstream = NULL; 308462306a36Sopenharmony_ci } 308562306a36Sopenharmony_ci ca8210_hw_setup(hw); 308662306a36Sopenharmony_ci ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci pdata = kmalloc(sizeof(*pdata), GFP_KERNEL); 308962306a36Sopenharmony_ci if (!pdata) { 309062306a36Sopenharmony_ci ret = -ENOMEM; 309162306a36Sopenharmony_ci goto error; 309262306a36Sopenharmony_ci } 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci priv->spi->dev.platform_data = pdata; 309562306a36Sopenharmony_ci ret = ca8210_get_platform_data(priv->spi, pdata); 309662306a36Sopenharmony_ci if (ret) { 309762306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ca8210_get_platform_data failed\n"); 309862306a36Sopenharmony_ci goto error; 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci ret = ca8210_dev_com_init(priv); 310262306a36Sopenharmony_ci if (ret) { 310362306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ca8210_dev_com_init failed\n"); 310462306a36Sopenharmony_ci goto error; 310562306a36Sopenharmony_ci } 310662306a36Sopenharmony_ci ret = ca8210_reset_init(priv->spi); 310762306a36Sopenharmony_ci if (ret) { 310862306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ca8210_reset_init failed\n"); 310962306a36Sopenharmony_ci goto error; 311062306a36Sopenharmony_ci } 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci ret = ca8210_interrupt_init(priv->spi); 311362306a36Sopenharmony_ci if (ret) { 311462306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ca8210_interrupt_init failed\n"); 311562306a36Sopenharmony_ci goto error; 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci msleep(100); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci ca8210_reset_send(priv->spi, 1); 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci ret = tdme_chipinit(priv->spi); 312362306a36Sopenharmony_ci if (ret) { 312462306a36Sopenharmony_ci dev_crit(&spi_device->dev, "tdme_chipinit failed\n"); 312562306a36Sopenharmony_ci goto error; 312662306a36Sopenharmony_ci } 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci if (pdata->extclockenable) { 312962306a36Sopenharmony_ci ret = ca8210_config_extern_clk(pdata, priv->spi, 1); 313062306a36Sopenharmony_ci if (ret) { 313162306a36Sopenharmony_ci dev_crit( 313262306a36Sopenharmony_ci &spi_device->dev, 313362306a36Sopenharmony_ci "ca8210_config_extern_clk failed\n" 313462306a36Sopenharmony_ci ); 313562306a36Sopenharmony_ci goto error; 313662306a36Sopenharmony_ci } 313762306a36Sopenharmony_ci ret = ca8210_register_ext_clock(priv->spi); 313862306a36Sopenharmony_ci if (ret) { 313962306a36Sopenharmony_ci dev_crit( 314062306a36Sopenharmony_ci &spi_device->dev, 314162306a36Sopenharmony_ci "ca8210_register_ext_clock failed\n" 314262306a36Sopenharmony_ci ); 314362306a36Sopenharmony_ci goto error; 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci } 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci ret = ieee802154_register_hw(hw); 314862306a36Sopenharmony_ci if (ret) { 314962306a36Sopenharmony_ci dev_crit(&spi_device->dev, "ieee802154_register_hw failed\n"); 315062306a36Sopenharmony_ci goto error; 315162306a36Sopenharmony_ci } 315262306a36Sopenharmony_ci priv->hw_registered = true; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci return 0; 315562306a36Sopenharmony_cierror: 315662306a36Sopenharmony_ci msleep(100); /* wait for pending spi transfers to complete */ 315762306a36Sopenharmony_ci ca8210_remove(spi_device); 315862306a36Sopenharmony_ci return link_to_linux_err(ret); 315962306a36Sopenharmony_ci} 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_cistatic const struct of_device_id ca8210_of_ids[] = { 316262306a36Sopenharmony_ci {.compatible = "cascoda,ca8210", }, 316362306a36Sopenharmony_ci {}, 316462306a36Sopenharmony_ci}; 316562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ca8210_of_ids); 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_cistatic struct spi_driver ca8210_spi_driver = { 316862306a36Sopenharmony_ci .driver = { 316962306a36Sopenharmony_ci .name = DRIVER_NAME, 317062306a36Sopenharmony_ci .of_match_table = ca8210_of_ids, 317162306a36Sopenharmony_ci }, 317262306a36Sopenharmony_ci .probe = ca8210_probe, 317362306a36Sopenharmony_ci .remove = ca8210_remove 317462306a36Sopenharmony_ci}; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_cimodule_spi_driver(ca8210_spi_driver); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ciMODULE_AUTHOR("Harry Morris <h.morris@cascoda.com>"); 317962306a36Sopenharmony_ciMODULE_DESCRIPTION("CA-8210 SoftMAC driver"); 318062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 318162306a36Sopenharmony_ciMODULE_VERSION("1.0"); 3182