18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ssi_protocol.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Implementation of the SSI McSAAB improved protocol. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2013 Sebastian Reichel <sre@kernel.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Contact: Carlos Chinea <carlos.chinea@nokia.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/atomic.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 198c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 208c2ecf20Sopenharmony_ci#include <linux/if_phonet.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/notifier.h> 278c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 288c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 318c2ecf20Sopenharmony_ci#include <linux/timer.h> 328c2ecf20Sopenharmony_ci#include <linux/hsi/hsi.h> 338c2ecf20Sopenharmony_ci#include <linux/hsi/ssi_protocol.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid ssi_waketest(struct hsi_client *cl, unsigned int enable); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define SSIP_TXQUEUE_LEN 100 388c2ecf20Sopenharmony_ci#define SSIP_MAX_MTU 65535 398c2ecf20Sopenharmony_ci#define SSIP_DEFAULT_MTU 4000 408c2ecf20Sopenharmony_ci#define PN_MEDIA_SOS 21 418c2ecf20Sopenharmony_ci#define SSIP_MIN_PN_HDR 6 /* FIXME: Revisit */ 428c2ecf20Sopenharmony_ci#define SSIP_WDTOUT 2000 /* FIXME: has to be 500 msecs */ 438c2ecf20Sopenharmony_ci#define SSIP_KATOUT 15 /* 15 msecs */ 448c2ecf20Sopenharmony_ci#define SSIP_MAX_CMDS 5 /* Number of pre-allocated commands buffers */ 458c2ecf20Sopenharmony_ci#define SSIP_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1) 468c2ecf20Sopenharmony_ci#define SSIP_CMT_LOADER_SYNC 0x11223344 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * SSI protocol command definitions 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define SSIP_COMMAND(data) ((data) >> 28) 518c2ecf20Sopenharmony_ci#define SSIP_PAYLOAD(data) ((data) & 0xfffffff) 528c2ecf20Sopenharmony_ci/* Commands */ 538c2ecf20Sopenharmony_ci#define SSIP_SW_BREAK 0 548c2ecf20Sopenharmony_ci#define SSIP_BOOTINFO_REQ 1 558c2ecf20Sopenharmony_ci#define SSIP_BOOTINFO_RESP 2 568c2ecf20Sopenharmony_ci#define SSIP_WAKETEST_RESULT 3 578c2ecf20Sopenharmony_ci#define SSIP_START_TRANS 4 588c2ecf20Sopenharmony_ci#define SSIP_READY 5 598c2ecf20Sopenharmony_ci/* Payloads */ 608c2ecf20Sopenharmony_ci#define SSIP_DATA_VERSION(data) ((data) & 0xff) 618c2ecf20Sopenharmony_ci#define SSIP_LOCAL_VERID 1 628c2ecf20Sopenharmony_ci#define SSIP_WAKETEST_OK 0 638c2ecf20Sopenharmony_ci#define SSIP_WAKETEST_FAILED 1 648c2ecf20Sopenharmony_ci#define SSIP_PDU_LENGTH(data) (((data) >> 8) & 0xffff) 658c2ecf20Sopenharmony_ci#define SSIP_MSG_ID(data) ((data) & 0xff) 668c2ecf20Sopenharmony_ci/* Generic Command */ 678c2ecf20Sopenharmony_ci#define SSIP_CMD(cmd, payload) (((cmd) << 28) | ((payload) & 0xfffffff)) 688c2ecf20Sopenharmony_ci/* Commands for the control channel */ 698c2ecf20Sopenharmony_ci#define SSIP_BOOTINFO_REQ_CMD(ver) \ 708c2ecf20Sopenharmony_ci SSIP_CMD(SSIP_BOOTINFO_REQ, SSIP_DATA_VERSION(ver)) 718c2ecf20Sopenharmony_ci#define SSIP_BOOTINFO_RESP_CMD(ver) \ 728c2ecf20Sopenharmony_ci SSIP_CMD(SSIP_BOOTINFO_RESP, SSIP_DATA_VERSION(ver)) 738c2ecf20Sopenharmony_ci#define SSIP_START_TRANS_CMD(pdulen, id) \ 748c2ecf20Sopenharmony_ci SSIP_CMD(SSIP_START_TRANS, (((pdulen) << 8) | SSIP_MSG_ID(id))) 758c2ecf20Sopenharmony_ci#define SSIP_READY_CMD SSIP_CMD(SSIP_READY, 0) 768c2ecf20Sopenharmony_ci#define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define SSIP_WAKETEST_FLAG 0 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Main state machine states */ 818c2ecf20Sopenharmony_cienum { 828c2ecf20Sopenharmony_ci INIT, 838c2ecf20Sopenharmony_ci HANDSHAKE, 848c2ecf20Sopenharmony_ci ACTIVE, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Send state machine states */ 888c2ecf20Sopenharmony_cienum { 898c2ecf20Sopenharmony_ci SEND_IDLE, 908c2ecf20Sopenharmony_ci WAIT4READY, 918c2ecf20Sopenharmony_ci SEND_READY, 928c2ecf20Sopenharmony_ci SENDING, 938c2ecf20Sopenharmony_ci SENDING_SWBREAK, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Receive state machine states */ 978c2ecf20Sopenharmony_cienum { 988c2ecf20Sopenharmony_ci RECV_IDLE, 998c2ecf20Sopenharmony_ci RECV_READY, 1008c2ecf20Sopenharmony_ci RECEIVING, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * struct ssi_protocol - SSI protocol (McSAAB) data 1058c2ecf20Sopenharmony_ci * @main_state: Main state machine 1068c2ecf20Sopenharmony_ci * @send_state: TX state machine 1078c2ecf20Sopenharmony_ci * @recv_state: RX state machine 1088c2ecf20Sopenharmony_ci * @flags: Flags, currently only used to follow wake line test 1098c2ecf20Sopenharmony_ci * @rxid: RX data id 1108c2ecf20Sopenharmony_ci * @txid: TX data id 1118c2ecf20Sopenharmony_ci * @txqueue_len: TX queue length 1128c2ecf20Sopenharmony_ci * @tx_wd: TX watchdog 1138c2ecf20Sopenharmony_ci * @rx_wd: RX watchdog 1148c2ecf20Sopenharmony_ci * @keep_alive: Workaround for SSI HW bug 1158c2ecf20Sopenharmony_ci * @lock: To serialize access to this struct 1168c2ecf20Sopenharmony_ci * @netdev: Phonet network device 1178c2ecf20Sopenharmony_ci * @txqueue: TX data queue 1188c2ecf20Sopenharmony_ci * @cmdqueue: Queue of free commands 1198c2ecf20Sopenharmony_ci * @cl: HSI client own reference 1208c2ecf20Sopenharmony_ci * @link: Link for ssip_list 1218c2ecf20Sopenharmony_ci * @tx_usecount: Refcount to keep track the slaves that use the wake line 1228c2ecf20Sopenharmony_ci * @channel_id_cmd: HSI channel id for command stream 1238c2ecf20Sopenharmony_ci * @channel_id_data: HSI channel id for data stream 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistruct ssi_protocol { 1268c2ecf20Sopenharmony_ci unsigned int main_state; 1278c2ecf20Sopenharmony_ci unsigned int send_state; 1288c2ecf20Sopenharmony_ci unsigned int recv_state; 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci u8 rxid; 1318c2ecf20Sopenharmony_ci u8 txid; 1328c2ecf20Sopenharmony_ci unsigned int txqueue_len; 1338c2ecf20Sopenharmony_ci struct timer_list tx_wd; 1348c2ecf20Sopenharmony_ci struct timer_list rx_wd; 1358c2ecf20Sopenharmony_ci struct timer_list keep_alive; /* wake-up workaround */ 1368c2ecf20Sopenharmony_ci spinlock_t lock; 1378c2ecf20Sopenharmony_ci struct net_device *netdev; 1388c2ecf20Sopenharmony_ci struct list_head txqueue; 1398c2ecf20Sopenharmony_ci struct list_head cmdqueue; 1408c2ecf20Sopenharmony_ci struct work_struct work; 1418c2ecf20Sopenharmony_ci struct hsi_client *cl; 1428c2ecf20Sopenharmony_ci struct list_head link; 1438c2ecf20Sopenharmony_ci atomic_t tx_usecnt; 1448c2ecf20Sopenharmony_ci int channel_id_cmd; 1458c2ecf20Sopenharmony_ci int channel_id_data; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* List of ssi protocol instances */ 1498c2ecf20Sopenharmony_cistatic LIST_HEAD(ssip_list); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void ssip_rxcmd_complete(struct hsi_msg *msg); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic inline void ssip_set_cmd(struct hsi_msg *msg, u32 cmd) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci u32 *data; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci data = sg_virt(msg->sgt.sgl); 1588c2ecf20Sopenharmony_ci *data = cmd; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic inline u32 ssip_get_cmd(struct hsi_msg *msg) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u32 *data; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci data = sg_virt(msg->sgt.sgl); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return *data; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void ssip_skb_to_msg(struct sk_buff *skb, struct hsi_msg *msg) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci skb_frag_t *frag; 1738c2ecf20Sopenharmony_ci struct scatterlist *sg; 1748c2ecf20Sopenharmony_ci int i; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci BUG_ON(msg->sgt.nents != (unsigned int)(skb_shinfo(skb)->nr_frags + 1)); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci sg = msg->sgt.sgl; 1798c2ecf20Sopenharmony_ci sg_set_buf(sg, skb->data, skb_headlen(skb)); 1808c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 1818c2ecf20Sopenharmony_ci sg = sg_next(sg); 1828c2ecf20Sopenharmony_ci BUG_ON(!sg); 1838c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 1848c2ecf20Sopenharmony_ci sg_set_page(sg, skb_frag_page(frag), skb_frag_size(frag), 1858c2ecf20Sopenharmony_ci skb_frag_off(frag)); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void ssip_free_data(struct hsi_msg *msg) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct sk_buff *skb; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci skb = msg->context; 1948c2ecf20Sopenharmony_ci pr_debug("free data: msg %p context %p skb %p\n", msg, msg->context, 1958c2ecf20Sopenharmony_ci skb); 1968c2ecf20Sopenharmony_ci msg->destructor = NULL; 1978c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1988c2ecf20Sopenharmony_ci hsi_free_msg(msg); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic struct hsi_msg *ssip_alloc_data(struct ssi_protocol *ssi, 2028c2ecf20Sopenharmony_ci struct sk_buff *skb, gfp_t flags) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct hsi_msg *msg; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci msg = hsi_alloc_msg(skb_shinfo(skb)->nr_frags + 1, flags); 2078c2ecf20Sopenharmony_ci if (!msg) 2088c2ecf20Sopenharmony_ci return NULL; 2098c2ecf20Sopenharmony_ci ssip_skb_to_msg(skb, msg); 2108c2ecf20Sopenharmony_ci msg->destructor = ssip_free_data; 2118c2ecf20Sopenharmony_ci msg->channel = ssi->channel_id_data; 2128c2ecf20Sopenharmony_ci msg->context = skb; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return msg; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic inline void ssip_release_cmd(struct hsi_msg *msg) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(msg->cl); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci dev_dbg(&msg->cl->device, "Release cmd 0x%08x\n", ssip_get_cmd(msg)); 2228c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 2238c2ecf20Sopenharmony_ci list_add_tail(&msg->link, &ssi->cmdqueue); 2248c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic struct hsi_msg *ssip_claim_cmd(struct ssi_protocol *ssi) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct hsi_msg *msg; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci BUG_ON(list_empty(&ssi->cmdqueue)); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 2348c2ecf20Sopenharmony_ci msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link); 2358c2ecf20Sopenharmony_ci list_del(&msg->link); 2368c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 2378c2ecf20Sopenharmony_ci msg->destructor = ssip_release_cmd; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return msg; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void ssip_free_cmds(struct ssi_protocol *ssi) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct hsi_msg *msg, *tmp; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) { 2478c2ecf20Sopenharmony_ci list_del(&msg->link); 2488c2ecf20Sopenharmony_ci msg->destructor = NULL; 2498c2ecf20Sopenharmony_ci kfree(sg_virt(msg->sgt.sgl)); 2508c2ecf20Sopenharmony_ci hsi_free_msg(msg); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int ssip_alloc_cmds(struct ssi_protocol *ssi) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct hsi_msg *msg; 2578c2ecf20Sopenharmony_ci u32 *buf; 2588c2ecf20Sopenharmony_ci unsigned int i; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci for (i = 0; i < SSIP_MAX_CMDS; i++) { 2618c2ecf20Sopenharmony_ci msg = hsi_alloc_msg(1, GFP_KERNEL); 2628c2ecf20Sopenharmony_ci if (!msg) 2638c2ecf20Sopenharmony_ci goto out; 2648c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_KERNEL); 2658c2ecf20Sopenharmony_ci if (!buf) { 2668c2ecf20Sopenharmony_ci hsi_free_msg(msg); 2678c2ecf20Sopenharmony_ci goto out; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci sg_init_one(msg->sgt.sgl, buf, sizeof(*buf)); 2708c2ecf20Sopenharmony_ci msg->channel = ssi->channel_id_cmd; 2718c2ecf20Sopenharmony_ci list_add_tail(&msg->link, &ssi->cmdqueue); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ciout: 2768c2ecf20Sopenharmony_ci ssip_free_cmds(ssi); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return -ENOMEM; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void ssip_set_rxstate(struct ssi_protocol *ssi, unsigned int state) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci ssi->recv_state = state; 2848c2ecf20Sopenharmony_ci switch (state) { 2858c2ecf20Sopenharmony_ci case RECV_IDLE: 2868c2ecf20Sopenharmony_ci del_timer(&ssi->rx_wd); 2878c2ecf20Sopenharmony_ci if (ssi->send_state == SEND_IDLE) 2888c2ecf20Sopenharmony_ci del_timer(&ssi->keep_alive); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case RECV_READY: 2918c2ecf20Sopenharmony_ci /* CMT speech workaround */ 2928c2ecf20Sopenharmony_ci if (atomic_read(&ssi->tx_usecnt)) 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci fallthrough; 2958c2ecf20Sopenharmony_ci case RECEIVING: 2968c2ecf20Sopenharmony_ci mod_timer(&ssi->keep_alive, jiffies + 2978c2ecf20Sopenharmony_ci msecs_to_jiffies(SSIP_KATOUT)); 2988c2ecf20Sopenharmony_ci mod_timer(&ssi->rx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void ssip_set_txstate(struct ssi_protocol *ssi, unsigned int state) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci ssi->send_state = state; 3088c2ecf20Sopenharmony_ci switch (state) { 3098c2ecf20Sopenharmony_ci case SEND_IDLE: 3108c2ecf20Sopenharmony_ci case SEND_READY: 3118c2ecf20Sopenharmony_ci del_timer(&ssi->tx_wd); 3128c2ecf20Sopenharmony_ci if (ssi->recv_state == RECV_IDLE) 3138c2ecf20Sopenharmony_ci del_timer(&ssi->keep_alive); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case WAIT4READY: 3168c2ecf20Sopenharmony_ci case SENDING: 3178c2ecf20Sopenharmony_ci case SENDING_SWBREAK: 3188c2ecf20Sopenharmony_ci mod_timer(&ssi->keep_alive, 3198c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(SSIP_KATOUT)); 3208c2ecf20Sopenharmony_ci mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci default: 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistruct hsi_client *ssip_slave_get_master(struct hsi_client *slave) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct hsi_client *master = ERR_PTR(-ENODEV); 3308c2ecf20Sopenharmony_ci struct ssi_protocol *ssi; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci list_for_each_entry(ssi, &ssip_list, link) 3338c2ecf20Sopenharmony_ci if (slave->device.parent == ssi->cl->device.parent) { 3348c2ecf20Sopenharmony_ci master = ssi->cl; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return master; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ssip_slave_get_master); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciint ssip_slave_start_tx(struct hsi_client *master) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(master); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dev_dbg(&master->device, "start TX %d\n", atomic_read(&ssi->tx_usecnt)); 3478c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 3488c2ecf20Sopenharmony_ci if (ssi->send_state == SEND_IDLE) { 3498c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, WAIT4READY); 3508c2ecf20Sopenharmony_ci hsi_start_tx(master); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 3538c2ecf20Sopenharmony_ci atomic_inc(&ssi->tx_usecnt); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ssip_slave_start_tx); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciint ssip_slave_stop_tx(struct hsi_client *master) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(master); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci WARN_ON_ONCE(atomic_read(&ssi->tx_usecnt) == 0); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&ssi->tx_usecnt)) { 3668c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 3678c2ecf20Sopenharmony_ci if ((ssi->send_state == SEND_READY) || 3688c2ecf20Sopenharmony_ci (ssi->send_state == WAIT4READY)) { 3698c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SEND_IDLE); 3708c2ecf20Sopenharmony_ci hsi_stop_tx(master); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci dev_dbg(&master->device, "stop TX %d\n", atomic_read(&ssi->tx_usecnt)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ssip_slave_stop_tx); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint ssip_slave_running(struct hsi_client *master) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(master); 3838c2ecf20Sopenharmony_ci return netif_running(ssi->netdev); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ssip_slave_running); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void ssip_reset(struct hsi_client *cl) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 3908c2ecf20Sopenharmony_ci struct list_head *head, *tmp; 3918c2ecf20Sopenharmony_ci struct hsi_msg *msg; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (netif_running(ssi->netdev)) 3948c2ecf20Sopenharmony_ci netif_carrier_off(ssi->netdev); 3958c2ecf20Sopenharmony_ci hsi_flush(cl); 3968c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 3978c2ecf20Sopenharmony_ci if (ssi->send_state != SEND_IDLE) 3988c2ecf20Sopenharmony_ci hsi_stop_tx(cl); 3998c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 4008c2ecf20Sopenharmony_ci if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) 4018c2ecf20Sopenharmony_ci ssi_waketest(cl, 0); /* FIXME: To be removed */ 4028c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 4038c2ecf20Sopenharmony_ci del_timer(&ssi->rx_wd); 4048c2ecf20Sopenharmony_ci del_timer(&ssi->tx_wd); 4058c2ecf20Sopenharmony_ci del_timer(&ssi->keep_alive); 4068c2ecf20Sopenharmony_ci ssi->main_state = 0; 4078c2ecf20Sopenharmony_ci ssi->send_state = 0; 4088c2ecf20Sopenharmony_ci ssi->recv_state = 0; 4098c2ecf20Sopenharmony_ci ssi->flags = 0; 4108c2ecf20Sopenharmony_ci ssi->rxid = 0; 4118c2ecf20Sopenharmony_ci ssi->txid = 0; 4128c2ecf20Sopenharmony_ci list_for_each_safe(head, tmp, &ssi->txqueue) { 4138c2ecf20Sopenharmony_ci msg = list_entry(head, struct hsi_msg, link); 4148c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Pending TX data\n"); 4158c2ecf20Sopenharmony_ci list_del(head); 4168c2ecf20Sopenharmony_ci ssip_free_data(msg); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci ssi->txqueue_len = 0; 4198c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void ssip_dump_state(struct hsi_client *cl) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 4258c2ecf20Sopenharmony_ci struct hsi_msg *msg; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 4288c2ecf20Sopenharmony_ci dev_err(&cl->device, "Main state: %d\n", ssi->main_state); 4298c2ecf20Sopenharmony_ci dev_err(&cl->device, "Recv state: %d\n", ssi->recv_state); 4308c2ecf20Sopenharmony_ci dev_err(&cl->device, "Send state: %d\n", ssi->send_state); 4318c2ecf20Sopenharmony_ci dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ? 4328c2ecf20Sopenharmony_ci "Online" : "Offline"); 4338c2ecf20Sopenharmony_ci dev_err(&cl->device, "Wake test %d\n", 4348c2ecf20Sopenharmony_ci test_bit(SSIP_WAKETEST_FLAG, &ssi->flags)); 4358c2ecf20Sopenharmony_ci dev_err(&cl->device, "Data RX id: %d\n", ssi->rxid); 4368c2ecf20Sopenharmony_ci dev_err(&cl->device, "Data TX id: %d\n", ssi->txid); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci list_for_each_entry(msg, &ssi->txqueue, link) 4398c2ecf20Sopenharmony_ci dev_err(&cl->device, "pending TX data (%p)\n", msg); 4408c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void ssip_error(struct hsi_client *cl) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 4468c2ecf20Sopenharmony_ci struct hsi_msg *msg; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ssip_dump_state(cl); 4498c2ecf20Sopenharmony_ci ssip_reset(cl); 4508c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 4518c2ecf20Sopenharmony_ci msg->complete = ssip_rxcmd_complete; 4528c2ecf20Sopenharmony_ci hsi_async_read(cl, msg); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void ssip_keep_alive(struct timer_list *t) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = from_timer(ssi, t, keep_alive); 4588c2ecf20Sopenharmony_ci struct hsi_client *cl = ssi->cl; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Keep alive kick in: m(%d) r(%d) s(%d)\n", 4618c2ecf20Sopenharmony_ci ssi->main_state, ssi->recv_state, ssi->send_state); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci spin_lock(&ssi->lock); 4648c2ecf20Sopenharmony_ci if (ssi->recv_state == RECV_IDLE) 4658c2ecf20Sopenharmony_ci switch (ssi->send_state) { 4668c2ecf20Sopenharmony_ci case SEND_READY: 4678c2ecf20Sopenharmony_ci if (atomic_read(&ssi->tx_usecnt) == 0) 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci fallthrough; 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Workaround for cmt-speech in that case 4728c2ecf20Sopenharmony_ci * we relay on audio timers. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci case SEND_IDLE: 4758c2ecf20Sopenharmony_ci spin_unlock(&ssi->lock); 4768c2ecf20Sopenharmony_ci return; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci mod_timer(&ssi->keep_alive, jiffies + msecs_to_jiffies(SSIP_KATOUT)); 4798c2ecf20Sopenharmony_ci spin_unlock(&ssi->lock); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void ssip_rx_wd(struct timer_list *t) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = from_timer(ssi, t, rx_wd); 4858c2ecf20Sopenharmony_ci struct hsi_client *cl = ssi->cl; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci dev_err(&cl->device, "Watchdog triggered\n"); 4888c2ecf20Sopenharmony_ci ssip_error(cl); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void ssip_tx_wd(struct timer_list *t) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = from_timer(ssi, t, tx_wd); 4948c2ecf20Sopenharmony_ci struct hsi_client *cl = ssi->cl; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci dev_err(&cl->device, "Watchdog triggered\n"); 4978c2ecf20Sopenharmony_ci ssip_error(cl); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic void ssip_send_bootinfo_req_cmd(struct hsi_client *cl) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 5038c2ecf20Sopenharmony_ci struct hsi_msg *msg; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Issuing BOOT INFO REQ command\n"); 5068c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 5078c2ecf20Sopenharmony_ci ssip_set_cmd(msg, SSIP_BOOTINFO_REQ_CMD(SSIP_LOCAL_VERID)); 5088c2ecf20Sopenharmony_ci msg->complete = ssip_release_cmd; 5098c2ecf20Sopenharmony_ci hsi_async_write(cl, msg); 5108c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Issuing RX command\n"); 5118c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 5128c2ecf20Sopenharmony_ci msg->complete = ssip_rxcmd_complete; 5138c2ecf20Sopenharmony_ci hsi_async_read(cl, msg); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void ssip_start_rx(struct hsi_client *cl) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 5198c2ecf20Sopenharmony_ci struct hsi_msg *msg; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state, 5228c2ecf20Sopenharmony_ci ssi->recv_state); 5238c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 5248c2ecf20Sopenharmony_ci /* 5258c2ecf20Sopenharmony_ci * We can have two UP events in a row due to a short low 5268c2ecf20Sopenharmony_ci * high transition. Therefore we need to ignore the sencond UP event. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) { 5298c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5308c2ecf20Sopenharmony_ci return; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci ssip_set_rxstate(ssi, RECV_READY); 5338c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 5368c2ecf20Sopenharmony_ci ssip_set_cmd(msg, SSIP_READY_CMD); 5378c2ecf20Sopenharmony_ci msg->complete = ssip_release_cmd; 5388c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Send READY\n"); 5398c2ecf20Sopenharmony_ci hsi_async_write(cl, msg); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void ssip_stop_rx(struct hsi_client *cl) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state); 5478c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 5488c2ecf20Sopenharmony_ci if (likely(ssi->main_state == ACTIVE)) 5498c2ecf20Sopenharmony_ci ssip_set_rxstate(ssi, RECV_IDLE); 5508c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic void ssip_free_strans(struct hsi_msg *msg) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci ssip_free_data(msg->context); 5568c2ecf20Sopenharmony_ci ssip_release_cmd(msg); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic void ssip_strans_complete(struct hsi_msg *msg) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct hsi_client *cl = msg->cl; 5628c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 5638c2ecf20Sopenharmony_ci struct hsi_msg *data; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci data = msg->context; 5668c2ecf20Sopenharmony_ci ssip_release_cmd(msg); 5678c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 5688c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SENDING); 5698c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5708c2ecf20Sopenharmony_ci hsi_async_write(cl, data); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int ssip_xmit(struct hsi_client *cl) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 5768c2ecf20Sopenharmony_ci struct hsi_msg *msg, *dmsg; 5778c2ecf20Sopenharmony_ci struct sk_buff *skb; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 5808c2ecf20Sopenharmony_ci if (list_empty(&ssi->txqueue)) { 5818c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci dmsg = list_first_entry(&ssi->txqueue, struct hsi_msg, link); 5858c2ecf20Sopenharmony_ci list_del(&dmsg->link); 5868c2ecf20Sopenharmony_ci ssi->txqueue_len--; 5878c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 5908c2ecf20Sopenharmony_ci skb = dmsg->context; 5918c2ecf20Sopenharmony_ci msg->context = dmsg; 5928c2ecf20Sopenharmony_ci msg->complete = ssip_strans_complete; 5938c2ecf20Sopenharmony_ci msg->destructor = ssip_free_strans; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 5968c2ecf20Sopenharmony_ci ssip_set_cmd(msg, SSIP_START_TRANS_CMD(SSIP_BYTES_TO_FRAMES(skb->len), 5978c2ecf20Sopenharmony_ci ssi->txid)); 5988c2ecf20Sopenharmony_ci ssi->txid++; 5998c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SENDING); 6008c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Send STRANS (%d frames)\n", 6038c2ecf20Sopenharmony_ci SSIP_BYTES_TO_FRAMES(skb->len)); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return hsi_async_write(cl, msg); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* In soft IRQ context */ 6098c2ecf20Sopenharmony_cistatic void ssip_pn_rx(struct sk_buff *skb) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (unlikely(!netif_running(dev))) { 6148c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Drop RX packet\n"); 6158c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 6168c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6178c2ecf20Sopenharmony_ci return; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, SSIP_MIN_PN_HDR))) { 6208c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Error drop RX packet\n"); 6218c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 6228c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 6238c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6248c2ecf20Sopenharmony_ci return; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 6278c2ecf20Sopenharmony_ci dev->stats.rx_bytes += skb->len; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* length field is exchanged in network byte order */ 6308c2ecf20Sopenharmony_ci ((u16 *)skb->data)[2] = ntohs(((u16 *)skb->data)[2]); 6318c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RX length fixed (%04x -> %u)\n", 6328c2ecf20Sopenharmony_ci ((u16 *)skb->data)[2], ntohs(((u16 *)skb->data)[2])); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_PHONET); 6358c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 6368c2ecf20Sopenharmony_ci __skb_pull(skb, 1); 6378c2ecf20Sopenharmony_ci netif_rx(skb); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic void ssip_rx_data_complete(struct hsi_msg *msg) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct hsi_client *cl = msg->cl; 6438c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 6448c2ecf20Sopenharmony_ci struct sk_buff *skb; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (msg->status == HSI_STATUS_ERROR) { 6478c2ecf20Sopenharmony_ci dev_err(&cl->device, "RX data error\n"); 6488c2ecf20Sopenharmony_ci ssip_free_data(msg); 6498c2ecf20Sopenharmony_ci ssip_error(cl); 6508c2ecf20Sopenharmony_ci return; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci del_timer(&ssi->rx_wd); /* FIXME: Revisit */ 6538c2ecf20Sopenharmony_ci skb = msg->context; 6548c2ecf20Sopenharmony_ci ssip_pn_rx(skb); 6558c2ecf20Sopenharmony_ci hsi_free_msg(msg); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 6618c2ecf20Sopenharmony_ci struct hsi_msg *msg; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Workaroud: Ignore CMT Loader message leftover */ 6648c2ecf20Sopenharmony_ci if (cmd == SSIP_CMT_LOADER_SYNC) 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci switch (ssi->main_state) { 6688c2ecf20Sopenharmony_ci case ACTIVE: 6698c2ecf20Sopenharmony_ci dev_err(&cl->device, "Boot info req on active state\n"); 6708c2ecf20Sopenharmony_ci ssip_error(cl); 6718c2ecf20Sopenharmony_ci fallthrough; 6728c2ecf20Sopenharmony_ci case INIT: 6738c2ecf20Sopenharmony_ci case HANDSHAKE: 6748c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 6758c2ecf20Sopenharmony_ci ssi->main_state = HANDSHAKE; 6768c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) 6798c2ecf20Sopenharmony_ci ssi_waketest(cl, 1); /* FIXME: To be removed */ 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 6828c2ecf20Sopenharmony_ci /* Start boot handshake watchdog */ 6838c2ecf20Sopenharmony_ci mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); 6848c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 6858c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Send BOOTINFO_RESP\n"); 6868c2ecf20Sopenharmony_ci if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) 6878c2ecf20Sopenharmony_ci dev_warn(&cl->device, "boot info req verid mismatch\n"); 6888c2ecf20Sopenharmony_ci msg = ssip_claim_cmd(ssi); 6898c2ecf20Sopenharmony_ci ssip_set_cmd(msg, SSIP_BOOTINFO_RESP_CMD(SSIP_LOCAL_VERID)); 6908c2ecf20Sopenharmony_ci msg->complete = ssip_release_cmd; 6918c2ecf20Sopenharmony_ci hsi_async_write(cl, msg); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci default: 6948c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Wrong state M(%d)\n", ssi->main_state); 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void ssip_rx_bootinforesp(struct hsi_client *cl, u32 cmd) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) 7048c2ecf20Sopenharmony_ci dev_warn(&cl->device, "boot info resp verid mismatch\n"); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 7078c2ecf20Sopenharmony_ci if (ssi->main_state != ACTIVE) 7088c2ecf20Sopenharmony_ci /* Use tx_wd as a boot watchdog in non ACTIVE state */ 7098c2ecf20Sopenharmony_ci mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); 7108c2ecf20Sopenharmony_ci else 7118c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "boot info resp ignored M(%d)\n", 7128c2ecf20Sopenharmony_ci ssi->main_state); 7138c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 7198c2ecf20Sopenharmony_ci unsigned int wkres = SSIP_PAYLOAD(cmd); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 7228c2ecf20Sopenharmony_ci if (ssi->main_state != HANDSHAKE) { 7238c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "wake lines test ignored M(%d)\n", 7248c2ecf20Sopenharmony_ci ssi->main_state); 7258c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7268c2ecf20Sopenharmony_ci return; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) 7318c2ecf20Sopenharmony_ci ssi_waketest(cl, 0); /* FIXME: To be removed */ 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 7348c2ecf20Sopenharmony_ci ssi->main_state = ACTIVE; 7358c2ecf20Sopenharmony_ci del_timer(&ssi->tx_wd); /* Stop boot handshake timer */ 7368c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci dev_notice(&cl->device, "WAKELINES TEST %s\n", 7398c2ecf20Sopenharmony_ci wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK"); 7408c2ecf20Sopenharmony_ci if (wkres & SSIP_WAKETEST_FAILED) { 7418c2ecf20Sopenharmony_ci ssip_error(cl); 7428c2ecf20Sopenharmony_ci return; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "CMT is ONLINE\n"); 7458c2ecf20Sopenharmony_ci netif_wake_queue(ssi->netdev); 7468c2ecf20Sopenharmony_ci netif_carrier_on(ssi->netdev); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic void ssip_rx_ready(struct hsi_client *cl) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 7548c2ecf20Sopenharmony_ci if (unlikely(ssi->main_state != ACTIVE)) { 7558c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n", 7568c2ecf20Sopenharmony_ci ssi->send_state, ssi->main_state); 7578c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7588c2ecf20Sopenharmony_ci return; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci if (ssi->send_state != WAIT4READY) { 7618c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Ignore spurious READY command\n"); 7628c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7638c2ecf20Sopenharmony_ci return; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SEND_READY); 7668c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7678c2ecf20Sopenharmony_ci ssip_xmit(cl); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic void ssip_rx_strans(struct hsi_client *cl, u32 cmd) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 7738c2ecf20Sopenharmony_ci struct sk_buff *skb; 7748c2ecf20Sopenharmony_ci struct hsi_msg *msg; 7758c2ecf20Sopenharmony_ci int len = SSIP_PDU_LENGTH(cmd); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "RX strans: %d frames\n", len); 7788c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 7798c2ecf20Sopenharmony_ci if (unlikely(ssi->main_state != ACTIVE)) { 7808c2ecf20Sopenharmony_ci dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n", 7818c2ecf20Sopenharmony_ci ssi->send_state, ssi->main_state); 7828c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7838c2ecf20Sopenharmony_ci return; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci ssip_set_rxstate(ssi, RECEIVING); 7868c2ecf20Sopenharmony_ci if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) { 7878c2ecf20Sopenharmony_ci dev_err(&cl->device, "START TRANS id %d expected %d\n", 7888c2ecf20Sopenharmony_ci SSIP_MSG_ID(cmd), ssi->rxid); 7898c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7908c2ecf20Sopenharmony_ci goto out1; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci ssi->rxid++; 7938c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 7948c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ssi->netdev, len * 4); 7958c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 7968c2ecf20Sopenharmony_ci dev_err(&cl->device, "No memory for rx skb\n"); 7978c2ecf20Sopenharmony_ci goto out1; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci skb->dev = ssi->netdev; 8008c2ecf20Sopenharmony_ci skb_put(skb, len * 4); 8018c2ecf20Sopenharmony_ci msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC); 8028c2ecf20Sopenharmony_ci if (unlikely(!msg)) { 8038c2ecf20Sopenharmony_ci dev_err(&cl->device, "No memory for RX data msg\n"); 8048c2ecf20Sopenharmony_ci goto out2; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci msg->complete = ssip_rx_data_complete; 8078c2ecf20Sopenharmony_ci hsi_async_read(cl, msg); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return; 8108c2ecf20Sopenharmony_ciout2: 8118c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8128c2ecf20Sopenharmony_ciout1: 8138c2ecf20Sopenharmony_ci ssip_error(cl); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic void ssip_rxcmd_complete(struct hsi_msg *msg) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct hsi_client *cl = msg->cl; 8198c2ecf20Sopenharmony_ci u32 cmd = ssip_get_cmd(msg); 8208c2ecf20Sopenharmony_ci unsigned int cmdid = SSIP_COMMAND(cmd); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (msg->status == HSI_STATUS_ERROR) { 8238c2ecf20Sopenharmony_ci dev_err(&cl->device, "RX error detected\n"); 8248c2ecf20Sopenharmony_ci ssip_release_cmd(msg); 8258c2ecf20Sopenharmony_ci ssip_error(cl); 8268c2ecf20Sopenharmony_ci return; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci hsi_async_read(cl, msg); 8298c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "RX cmd: 0x%08x\n", cmd); 8308c2ecf20Sopenharmony_ci switch (cmdid) { 8318c2ecf20Sopenharmony_ci case SSIP_SW_BREAK: 8328c2ecf20Sopenharmony_ci /* Ignored */ 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci case SSIP_BOOTINFO_REQ: 8358c2ecf20Sopenharmony_ci ssip_rx_bootinforeq(cl, cmd); 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case SSIP_BOOTINFO_RESP: 8388c2ecf20Sopenharmony_ci ssip_rx_bootinforesp(cl, cmd); 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci case SSIP_WAKETEST_RESULT: 8418c2ecf20Sopenharmony_ci ssip_rx_waketest(cl, cmd); 8428c2ecf20Sopenharmony_ci break; 8438c2ecf20Sopenharmony_ci case SSIP_START_TRANS: 8448c2ecf20Sopenharmony_ci ssip_rx_strans(cl, cmd); 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci case SSIP_READY: 8478c2ecf20Sopenharmony_ci ssip_rx_ready(cl); 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci default: 8508c2ecf20Sopenharmony_ci dev_warn(&cl->device, "command 0x%08x not supported\n", cmd); 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic void ssip_swbreak_complete(struct hsi_msg *msg) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct hsi_client *cl = msg->cl; 8588c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci ssip_release_cmd(msg); 8618c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 8628c2ecf20Sopenharmony_ci if (list_empty(&ssi->txqueue)) { 8638c2ecf20Sopenharmony_ci if (atomic_read(&ssi->tx_usecnt)) { 8648c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SEND_READY); 8658c2ecf20Sopenharmony_ci } else { 8668c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SEND_IDLE); 8678c2ecf20Sopenharmony_ci hsi_stop_tx(cl); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 8708c2ecf20Sopenharmony_ci } else { 8718c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 8728c2ecf20Sopenharmony_ci ssip_xmit(cl); 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci netif_wake_queue(ssi->netdev); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic void ssip_tx_data_complete(struct hsi_msg *msg) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct hsi_client *cl = msg->cl; 8808c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 8818c2ecf20Sopenharmony_ci struct hsi_msg *cmsg; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (msg->status == HSI_STATUS_ERROR) { 8848c2ecf20Sopenharmony_ci dev_err(&cl->device, "TX data error\n"); 8858c2ecf20Sopenharmony_ci ssip_error(cl); 8868c2ecf20Sopenharmony_ci goto out; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 8898c2ecf20Sopenharmony_ci if (list_empty(&ssi->txqueue)) { 8908c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, SENDING_SWBREAK); 8918c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 8928c2ecf20Sopenharmony_ci cmsg = ssip_claim_cmd(ssi); 8938c2ecf20Sopenharmony_ci ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD); 8948c2ecf20Sopenharmony_ci cmsg->complete = ssip_swbreak_complete; 8958c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Send SWBREAK\n"); 8968c2ecf20Sopenharmony_ci hsi_async_write(cl, cmsg); 8978c2ecf20Sopenharmony_ci } else { 8988c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 8998c2ecf20Sopenharmony_ci ssip_xmit(cl); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ciout: 9028c2ecf20Sopenharmony_ci ssip_free_data(msg); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic void ssip_port_event(struct hsi_client *cl, unsigned long event) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci switch (event) { 9088c2ecf20Sopenharmony_ci case HSI_EVENT_START_RX: 9098c2ecf20Sopenharmony_ci ssip_start_rx(cl); 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case HSI_EVENT_STOP_RX: 9128c2ecf20Sopenharmony_ci ssip_stop_rx(cl); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci default: 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int ssip_pn_open(struct net_device *dev) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev->dev.parent); 9228c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 9238c2ecf20Sopenharmony_ci int err; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci err = hsi_claim_port(cl, 1); 9268c2ecf20Sopenharmony_ci if (err < 0) { 9278c2ecf20Sopenharmony_ci dev_err(&cl->device, "SSI port already claimed\n"); 9288c2ecf20Sopenharmony_ci return err; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci err = hsi_register_port_event(cl, ssip_port_event); 9318c2ecf20Sopenharmony_ci if (err < 0) { 9328c2ecf20Sopenharmony_ci dev_err(&cl->device, "Register HSI port event failed (%d)\n", 9338c2ecf20Sopenharmony_ci err); 9348c2ecf20Sopenharmony_ci return err; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Configuring SSI port\n"); 9378c2ecf20Sopenharmony_ci hsi_setup(cl); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) 9408c2ecf20Sopenharmony_ci ssi_waketest(cl, 1); /* FIXME: To be removed */ 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 9438c2ecf20Sopenharmony_ci ssi->main_state = HANDSHAKE; 9448c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ssip_send_bootinfo_req_cmd(cl); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int ssip_pn_stop(struct net_device *dev) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev->dev.parent); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ssip_reset(cl); 9568c2ecf20Sopenharmony_ci hsi_unregister_port_event(cl); 9578c2ecf20Sopenharmony_ci hsi_release_port(cl); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return 0; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic void ssip_xmit_work(struct work_struct *work) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = 9658c2ecf20Sopenharmony_ci container_of(work, struct ssi_protocol, work); 9668c2ecf20Sopenharmony_ci struct hsi_client *cl = ssi->cl; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci ssip_xmit(cl); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev->dev.parent); 9748c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 9758c2ecf20Sopenharmony_ci struct hsi_msg *msg; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if ((skb->protocol != htons(ETH_P_PHONET)) || 9788c2ecf20Sopenharmony_ci (skb->len < SSIP_MIN_PN_HDR)) 9798c2ecf20Sopenharmony_ci goto drop; 9808c2ecf20Sopenharmony_ci /* Pad to 32-bits - FIXME: Revisit*/ 9818c2ecf20Sopenharmony_ci if ((skb->len & 3) && skb_pad(skb, 4 - (skb->len & 3))) 9828c2ecf20Sopenharmony_ci goto inc_dropped; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* 9858c2ecf20Sopenharmony_ci * Modem sends Phonet messages over SSI with its own endianness. 9868c2ecf20Sopenharmony_ci * Assume that modem has the same endianness as we do. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ci if (skb_cow_head(skb, 0)) 9898c2ecf20Sopenharmony_ci goto drop; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* length field is exchanged in network byte order */ 9928c2ecf20Sopenharmony_ci ((u16 *)skb->data)[2] = htons(((u16 *)skb->data)[2]); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC); 9958c2ecf20Sopenharmony_ci if (!msg) { 9968c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Dropping tx data: No memory\n"); 9978c2ecf20Sopenharmony_ci goto drop; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci msg->complete = ssip_tx_data_complete; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci spin_lock_bh(&ssi->lock); 10028c2ecf20Sopenharmony_ci if (unlikely(ssi->main_state != ACTIVE)) { 10038c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 10048c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Dropping tx data: CMT is OFFLINE\n"); 10058c2ecf20Sopenharmony_ci goto drop2; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci list_add_tail(&msg->link, &ssi->txqueue); 10088c2ecf20Sopenharmony_ci ssi->txqueue_len++; 10098c2ecf20Sopenharmony_ci if (dev->tx_queue_len < ssi->txqueue_len) { 10108c2ecf20Sopenharmony_ci dev_info(&cl->device, "TX queue full %d\n", ssi->txqueue_len); 10118c2ecf20Sopenharmony_ci netif_stop_queue(dev); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci if (ssi->send_state == SEND_IDLE) { 10148c2ecf20Sopenharmony_ci ssip_set_txstate(ssi, WAIT4READY); 10158c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 10168c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Start TX qlen %d\n", ssi->txqueue_len); 10178c2ecf20Sopenharmony_ci hsi_start_tx(cl); 10188c2ecf20Sopenharmony_ci } else if (ssi->send_state == SEND_READY) { 10198c2ecf20Sopenharmony_ci /* Needed for cmt-speech workaround */ 10208c2ecf20Sopenharmony_ci dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n", 10218c2ecf20Sopenharmony_ci ssi->txqueue_len); 10228c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 10238c2ecf20Sopenharmony_ci schedule_work(&ssi->work); 10248c2ecf20Sopenharmony_ci } else { 10258c2ecf20Sopenharmony_ci spin_unlock_bh(&ssi->lock); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 10288c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci return 0; 10318c2ecf20Sopenharmony_cidrop2: 10328c2ecf20Sopenharmony_ci hsi_free_msg(msg); 10338c2ecf20Sopenharmony_cidrop: 10348c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10358c2ecf20Sopenharmony_ciinc_dropped: 10368c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return 0; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci/* CMT reset event handler */ 10428c2ecf20Sopenharmony_civoid ssip_reset_event(struct hsi_client *master) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(master); 10458c2ecf20Sopenharmony_ci dev_err(&ssi->cl->device, "CMT reset detected!\n"); 10468c2ecf20Sopenharmony_ci ssip_error(ssi->cl); 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ssip_reset_event); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic const struct net_device_ops ssip_pn_ops = { 10518c2ecf20Sopenharmony_ci .ndo_open = ssip_pn_open, 10528c2ecf20Sopenharmony_ci .ndo_stop = ssip_pn_stop, 10538c2ecf20Sopenharmony_ci .ndo_start_xmit = ssip_pn_xmit, 10548c2ecf20Sopenharmony_ci}; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void ssip_pn_setup(struct net_device *dev) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci dev->features = 0; 10598c2ecf20Sopenharmony_ci dev->netdev_ops = &ssip_pn_ops; 10608c2ecf20Sopenharmony_ci dev->type = ARPHRD_PHONET; 10618c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 10628c2ecf20Sopenharmony_ci dev->mtu = SSIP_DEFAULT_MTU; 10638c2ecf20Sopenharmony_ci dev->hard_header_len = 1; 10648c2ecf20Sopenharmony_ci dev->dev_addr[0] = PN_MEDIA_SOS; 10658c2ecf20Sopenharmony_ci dev->addr_len = 1; 10668c2ecf20Sopenharmony_ci dev->tx_queue_len = SSIP_TXQUEUE_LEN; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 10698c2ecf20Sopenharmony_ci dev->header_ops = &phonet_header_ops; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int ssi_protocol_probe(struct device *dev) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci static const char ifname[] = "phonet%d"; 10758c2ecf20Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev); 10768c2ecf20Sopenharmony_ci struct ssi_protocol *ssi; 10778c2ecf20Sopenharmony_ci int err; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); 10808c2ecf20Sopenharmony_ci if (!ssi) 10818c2ecf20Sopenharmony_ci return -ENOMEM; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci spin_lock_init(&ssi->lock); 10848c2ecf20Sopenharmony_ci timer_setup(&ssi->rx_wd, ssip_rx_wd, TIMER_DEFERRABLE); 10858c2ecf20Sopenharmony_ci timer_setup(&ssi->tx_wd, ssip_tx_wd, TIMER_DEFERRABLE); 10868c2ecf20Sopenharmony_ci timer_setup(&ssi->keep_alive, ssip_keep_alive, 0); 10878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ssi->txqueue); 10888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ssi->cmdqueue); 10898c2ecf20Sopenharmony_ci atomic_set(&ssi->tx_usecnt, 0); 10908c2ecf20Sopenharmony_ci hsi_client_set_drvdata(cl, ssi); 10918c2ecf20Sopenharmony_ci ssi->cl = cl; 10928c2ecf20Sopenharmony_ci INIT_WORK(&ssi->work, ssip_xmit_work); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control"); 10958c2ecf20Sopenharmony_ci if (ssi->channel_id_cmd < 0) { 10968c2ecf20Sopenharmony_ci err = ssi->channel_id_cmd; 10978c2ecf20Sopenharmony_ci dev_err(dev, "Could not get cmd channel (%d)\n", err); 10988c2ecf20Sopenharmony_ci goto out; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci ssi->channel_id_data = hsi_get_channel_id_by_name(cl, "mcsaab-data"); 11028c2ecf20Sopenharmony_ci if (ssi->channel_id_data < 0) { 11038c2ecf20Sopenharmony_ci err = ssi->channel_id_data; 11048c2ecf20Sopenharmony_ci dev_err(dev, "Could not get data channel (%d)\n", err); 11058c2ecf20Sopenharmony_ci goto out; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci err = ssip_alloc_cmds(ssi); 11098c2ecf20Sopenharmony_ci if (err < 0) { 11108c2ecf20Sopenharmony_ci dev_err(dev, "No memory for commands\n"); 11118c2ecf20Sopenharmony_ci goto out; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci ssi->netdev = alloc_netdev(0, ifname, NET_NAME_UNKNOWN, ssip_pn_setup); 11158c2ecf20Sopenharmony_ci if (!ssi->netdev) { 11168c2ecf20Sopenharmony_ci dev_err(dev, "No memory for netdev\n"); 11178c2ecf20Sopenharmony_ci err = -ENOMEM; 11188c2ecf20Sopenharmony_ci goto out1; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* MTU range: 6 - 65535 */ 11228c2ecf20Sopenharmony_ci ssi->netdev->min_mtu = PHONET_MIN_MTU; 11238c2ecf20Sopenharmony_ci ssi->netdev->max_mtu = SSIP_MAX_MTU; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ssi->netdev, dev); 11268c2ecf20Sopenharmony_ci netif_carrier_off(ssi->netdev); 11278c2ecf20Sopenharmony_ci err = register_netdev(ssi->netdev); 11288c2ecf20Sopenharmony_ci if (err < 0) { 11298c2ecf20Sopenharmony_ci dev_err(dev, "Register netdev failed (%d)\n", err); 11308c2ecf20Sopenharmony_ci goto out2; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci list_add(&ssi->link, &ssip_list); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci dev_dbg(dev, "channel configuration: cmd=%d, data=%d\n", 11368c2ecf20Sopenharmony_ci ssi->channel_id_cmd, ssi->channel_id_data); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci return 0; 11398c2ecf20Sopenharmony_ciout2: 11408c2ecf20Sopenharmony_ci free_netdev(ssi->netdev); 11418c2ecf20Sopenharmony_ciout1: 11428c2ecf20Sopenharmony_ci ssip_free_cmds(ssi); 11438c2ecf20Sopenharmony_ciout: 11448c2ecf20Sopenharmony_ci kfree(ssi); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return err; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic int ssi_protocol_remove(struct device *dev) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct hsi_client *cl = to_hsi_client(dev); 11528c2ecf20Sopenharmony_ci struct ssi_protocol *ssi = hsi_client_drvdata(cl); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci list_del(&ssi->link); 11558c2ecf20Sopenharmony_ci unregister_netdev(ssi->netdev); 11568c2ecf20Sopenharmony_ci ssip_free_cmds(ssi); 11578c2ecf20Sopenharmony_ci hsi_client_set_drvdata(cl, NULL); 11588c2ecf20Sopenharmony_ci kfree(ssi); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic struct hsi_client_driver ssip_driver = { 11648c2ecf20Sopenharmony_ci .driver = { 11658c2ecf20Sopenharmony_ci .name = "ssi-protocol", 11668c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11678c2ecf20Sopenharmony_ci .probe = ssi_protocol_probe, 11688c2ecf20Sopenharmony_ci .remove = ssi_protocol_remove, 11698c2ecf20Sopenharmony_ci }, 11708c2ecf20Sopenharmony_ci}; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic int __init ssip_init(void) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci pr_info("SSI protocol aka McSAAB added\n"); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return hsi_register_client_driver(&ssip_driver); 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_cimodule_init(ssip_init); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic void __exit ssip_exit(void) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci hsi_unregister_client_driver(&ssip_driver); 11838c2ecf20Sopenharmony_ci pr_info("SSI protocol driver removed\n"); 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_cimodule_exit(ssip_exit); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ciMODULE_ALIAS("hsi:ssi-protocol"); 11888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>"); 11898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Remi Denis-Courmont <remi.denis-courmont@nokia.com>"); 11908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SSI protocol improved aka McSAAB"); 11918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1192