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