18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 48c2ecf20Sopenharmony_ci * Author: Daniel Martensson 58c2ecf20Sopenharmony_ci * Dmitry.Tarnyagin / dmitry.tarnyagin@lockless.no 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 208c2ecf20Sopenharmony_ci#include <linux/timer.h> 218c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 228c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h> 238c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h> 248c2ecf20Sopenharmony_ci#include <net/caif/caif_hsi.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Martensson"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CAIF HSI driver"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Returns the number of padding bytes for alignment. */ 318c2ecf20Sopenharmony_ci#define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\ 328c2ecf20Sopenharmony_ci (((pow)-((x)&((pow)-1))))) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct cfhsi_config hsi_default_config = { 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* Inactivity timeout on HSI, ms */ 378c2ecf20Sopenharmony_ci .inactivity_timeout = HZ, 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Aggregation timeout (ms) of zero means no aggregation is done*/ 408c2ecf20Sopenharmony_ci .aggregation_timeout = 1, 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * HSI link layer flow-control thresholds. 448c2ecf20Sopenharmony_ci * Threshold values for the HSI packet queue. Flow-control will be 458c2ecf20Sopenharmony_ci * asserted when the number of packets exceeds q_high_mark. It will 468c2ecf20Sopenharmony_ci * not be de-asserted before the number of packets drops below 478c2ecf20Sopenharmony_ci * q_low_mark. 488c2ecf20Sopenharmony_ci * Warning: A high threshold value might increase throughput but it 498c2ecf20Sopenharmony_ci * will at the same time prevent channel prioritization and increase 508c2ecf20Sopenharmony_ci * the risk of flooding the modem. The high threshold should be above 518c2ecf20Sopenharmony_ci * the low. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci .q_high_mark = 100, 548c2ecf20Sopenharmony_ci .q_low_mark = 50, 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * HSI padding options. 588c2ecf20Sopenharmony_ci * Warning: must be a base of 2 (& operation used) and can not be zero ! 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci .head_align = 4, 618c2ecf20Sopenharmony_ci .tail_align = 4, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define ON 1 658c2ecf20Sopenharmony_ci#define OFF 0 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic LIST_HEAD(cfhsi_list); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void cfhsi_inactivity_tout(struct timer_list *t) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = from_timer(cfhsi, t, inactivity_timer); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 748c2ecf20Sopenharmony_ci __func__); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Schedule power down work queue. */ 778c2ecf20Sopenharmony_ci if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 788c2ecf20Sopenharmony_ci queue_work(cfhsi->wq, &cfhsi->wake_down_work); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void cfhsi_update_aggregation_stats(struct cfhsi *cfhsi, 828c2ecf20Sopenharmony_ci const struct sk_buff *skb, 838c2ecf20Sopenharmony_ci int direction) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct caif_payload_info *info; 868c2ecf20Sopenharmony_ci int hpad, tpad, len; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci info = (struct caif_payload_info *)&skb->cb; 898c2ecf20Sopenharmony_ci hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); 908c2ecf20Sopenharmony_ci tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); 918c2ecf20Sopenharmony_ci len = skb->len + hpad + tpad; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (direction > 0) 948c2ecf20Sopenharmony_ci cfhsi->aggregation_len += len; 958c2ecf20Sopenharmony_ci else if (direction < 0) 968c2ecf20Sopenharmony_ci cfhsi->aggregation_len -= len; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic bool cfhsi_can_send_aggregate(struct cfhsi *cfhsi) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (cfhsi->cfg.aggregation_timeout == 0) 1048c2ecf20Sopenharmony_ci return true; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < CFHSI_PRIO_BEBK; ++i) { 1078c2ecf20Sopenharmony_ci if (cfhsi->qhead[i].qlen) 1088c2ecf20Sopenharmony_ci return true; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* TODO: Use aggregation_len instead */ 1128c2ecf20Sopenharmony_ci if (cfhsi->qhead[CFHSI_PRIO_BEBK].qlen >= CFHSI_MAX_PKTS) 1138c2ecf20Sopenharmony_ci return true; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return false; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct sk_buff *cfhsi_dequeue(struct cfhsi *cfhsi) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct sk_buff *skb; 1218c2ecf20Sopenharmony_ci int i; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci for (i = 0; i < CFHSI_PRIO_LAST; ++i) { 1248c2ecf20Sopenharmony_ci skb = skb_dequeue(&cfhsi->qhead[i]); 1258c2ecf20Sopenharmony_ci if (skb) 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return skb; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int cfhsi_tx_queue_len(struct cfhsi *cfhsi) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int i, len = 0; 1358c2ecf20Sopenharmony_ci for (i = 0; i < CFHSI_PRIO_LAST; ++i) 1368c2ecf20Sopenharmony_ci len += skb_queue_len(&cfhsi->qhead[i]); 1378c2ecf20Sopenharmony_ci return len; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void cfhsi_abort_tx(struct cfhsi *cfhsi) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (;;) { 1458c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 1468c2ecf20Sopenharmony_ci skb = cfhsi_dequeue(cfhsi); 1478c2ecf20Sopenharmony_ci if (!skb) 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_errors++; 1518c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_dropped++; 1528c2ecf20Sopenharmony_ci cfhsi_update_aggregation_stats(cfhsi, skb, -1); 1538c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 1548c2ecf20Sopenharmony_ci kfree_skb(skb); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci cfhsi->tx_state = CFHSI_TX_STATE_IDLE; 1578c2ecf20Sopenharmony_ci if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 1588c2ecf20Sopenharmony_ci mod_timer(&cfhsi->inactivity_timer, 1598c2ecf20Sopenharmony_ci jiffies + cfhsi->cfg.inactivity_timeout); 1608c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int cfhsi_flush_fifo(struct cfhsi *cfhsi) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci char buffer[32]; /* Any reasonable value */ 1668c2ecf20Sopenharmony_ci size_t fifo_occupancy; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 1708c2ecf20Sopenharmony_ci __func__); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci do { 1738c2ecf20Sopenharmony_ci ret = cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, 1748c2ecf20Sopenharmony_ci &fifo_occupancy); 1758c2ecf20Sopenharmony_ci if (ret) { 1768c2ecf20Sopenharmony_ci netdev_warn(cfhsi->ndev, 1778c2ecf20Sopenharmony_ci "%s: can't get FIFO occupancy: %d.\n", 1788c2ecf20Sopenharmony_ci __func__, ret); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } else if (!fifo_occupancy) 1818c2ecf20Sopenharmony_ci /* No more data, exitting normally */ 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci fifo_occupancy = min(sizeof(buffer), fifo_occupancy); 1858c2ecf20Sopenharmony_ci set_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); 1868c2ecf20Sopenharmony_ci ret = cfhsi->ops->cfhsi_rx(buffer, fifo_occupancy, 1878c2ecf20Sopenharmony_ci cfhsi->ops); 1888c2ecf20Sopenharmony_ci if (ret) { 1898c2ecf20Sopenharmony_ci clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); 1908c2ecf20Sopenharmony_ci netdev_warn(cfhsi->ndev, 1918c2ecf20Sopenharmony_ci "%s: can't read data: %d.\n", 1928c2ecf20Sopenharmony_ci __func__, ret); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = 5 * HZ; 1978c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(cfhsi->flush_fifo_wait, 1988c2ecf20Sopenharmony_ci !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (ret < 0) { 2018c2ecf20Sopenharmony_ci netdev_warn(cfhsi->ndev, 2028c2ecf20Sopenharmony_ci "%s: can't wait for flush complete: %d.\n", 2038c2ecf20Sopenharmony_ci __func__, ret); 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci } else if (!ret) { 2068c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2078c2ecf20Sopenharmony_ci netdev_warn(cfhsi->ndev, 2088c2ecf20Sopenharmony_ci "%s: timeout waiting for flush complete.\n", 2098c2ecf20Sopenharmony_ci __func__); 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } while (1); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int nfrms = 0; 2208c2ecf20Sopenharmony_ci int pld_len = 0; 2218c2ecf20Sopenharmony_ci struct sk_buff *skb; 2228c2ecf20Sopenharmony_ci u8 *pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci skb = cfhsi_dequeue(cfhsi); 2258c2ecf20Sopenharmony_ci if (!skb) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Clear offset. */ 2298c2ecf20Sopenharmony_ci desc->offset = 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Check if we can embed a CAIF frame. */ 2328c2ecf20Sopenharmony_ci if (skb->len < CFHSI_MAX_EMB_FRM_SZ) { 2338c2ecf20Sopenharmony_ci struct caif_payload_info *info; 2348c2ecf20Sopenharmony_ci int hpad; 2358c2ecf20Sopenharmony_ci int tpad; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Calculate needed head alignment and tail alignment. */ 2388c2ecf20Sopenharmony_ci info = (struct caif_payload_info *)&skb->cb; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); 2418c2ecf20Sopenharmony_ci tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Check if frame still fits with added alignment. */ 2448c2ecf20Sopenharmony_ci if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) { 2458c2ecf20Sopenharmony_ci u8 *pemb = desc->emb_frm; 2468c2ecf20Sopenharmony_ci desc->offset = CFHSI_DESC_SHORT_SZ; 2478c2ecf20Sopenharmony_ci *pemb = (u8)(hpad - 1); 2488c2ecf20Sopenharmony_ci pemb += hpad; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Update network statistics. */ 2518c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 2528c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_packets++; 2538c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_bytes += skb->len; 2548c2ecf20Sopenharmony_ci cfhsi_update_aggregation_stats(cfhsi, skb, -1); 2558c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Copy in embedded CAIF frame. */ 2588c2ecf20Sopenharmony_ci skb_copy_bits(skb, 0, pemb, skb->len); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Consume the SKB */ 2618c2ecf20Sopenharmony_ci consume_skb(skb); 2628c2ecf20Sopenharmony_ci skb = NULL; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Create payload CAIF frames. */ 2678c2ecf20Sopenharmony_ci while (nfrms < CFHSI_MAX_PKTS) { 2688c2ecf20Sopenharmony_ci struct caif_payload_info *info; 2698c2ecf20Sopenharmony_ci int hpad; 2708c2ecf20Sopenharmony_ci int tpad; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!skb) 2738c2ecf20Sopenharmony_ci skb = cfhsi_dequeue(cfhsi); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!skb) 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Calculate needed head alignment and tail alignment. */ 2798c2ecf20Sopenharmony_ci info = (struct caif_payload_info *)&skb->cb; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); 2828c2ecf20Sopenharmony_ci tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Fill in CAIF frame length in descriptor. */ 2858c2ecf20Sopenharmony_ci desc->cffrm_len[nfrms] = hpad + skb->len + tpad; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Fill head padding information. */ 2888c2ecf20Sopenharmony_ci *pfrm = (u8)(hpad - 1); 2898c2ecf20Sopenharmony_ci pfrm += hpad; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Update network statistics. */ 2928c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 2938c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_packets++; 2948c2ecf20Sopenharmony_ci cfhsi->ndev->stats.tx_bytes += skb->len; 2958c2ecf20Sopenharmony_ci cfhsi_update_aggregation_stats(cfhsi, skb, -1); 2968c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Copy in CAIF frame. */ 2998c2ecf20Sopenharmony_ci skb_copy_bits(skb, 0, pfrm, skb->len); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Update payload length. */ 3028c2ecf20Sopenharmony_ci pld_len += desc->cffrm_len[nfrms]; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Update frame pointer. */ 3058c2ecf20Sopenharmony_ci pfrm += skb->len + tpad; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Consume the SKB */ 3088c2ecf20Sopenharmony_ci consume_skb(skb); 3098c2ecf20Sopenharmony_ci skb = NULL; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Update number of frames. */ 3128c2ecf20Sopenharmony_ci nfrms++; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Unused length fields should be zero-filled (according to SPEC). */ 3168c2ecf20Sopenharmony_ci while (nfrms < CFHSI_MAX_PKTS) { 3178c2ecf20Sopenharmony_ci desc->cffrm_len[nfrms] = 0x0000; 3188c2ecf20Sopenharmony_ci nfrms++; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Check if we can piggy-back another descriptor. */ 3228c2ecf20Sopenharmony_ci if (cfhsi_can_send_aggregate(cfhsi)) 3238c2ecf20Sopenharmony_ci desc->header |= CFHSI_PIGGY_DESC; 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci desc->header &= ~CFHSI_PIGGY_DESC; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return CFHSI_DESC_SZ + pld_len; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void cfhsi_start_tx(struct cfhsi *cfhsi) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf; 3338c2ecf20Sopenharmony_ci int len, res; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", __func__); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci do { 3418c2ecf20Sopenharmony_ci /* Create HSI frame. */ 3428c2ecf20Sopenharmony_ci len = cfhsi_tx_frm(desc, cfhsi); 3438c2ecf20Sopenharmony_ci if (!len) { 3448c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 3458c2ecf20Sopenharmony_ci if (unlikely(cfhsi_tx_queue_len(cfhsi))) { 3468c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 3478c2ecf20Sopenharmony_ci res = -EAGAIN; 3488c2ecf20Sopenharmony_ci continue; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci cfhsi->tx_state = CFHSI_TX_STATE_IDLE; 3518c2ecf20Sopenharmony_ci /* Start inactivity timer. */ 3528c2ecf20Sopenharmony_ci mod_timer(&cfhsi->inactivity_timer, 3538c2ecf20Sopenharmony_ci jiffies + cfhsi->cfg.inactivity_timeout); 3548c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Set up new transfer. */ 3598c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); 3608c2ecf20Sopenharmony_ci if (WARN_ON(res < 0)) 3618c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: TX error %d.\n", 3628c2ecf20Sopenharmony_ci __func__, res); 3638c2ecf20Sopenharmony_ci } while (res < 0); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void cfhsi_tx_done(struct cfhsi *cfhsi) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", __func__); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * Send flow on if flow off has been previously signalled 3758c2ecf20Sopenharmony_ci * and number of packets is below low water mark. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 3788c2ecf20Sopenharmony_ci if (cfhsi->flow_off_sent && 3798c2ecf20Sopenharmony_ci cfhsi_tx_queue_len(cfhsi) <= cfhsi->cfg.q_low_mark && 3808c2ecf20Sopenharmony_ci cfhsi->cfdev.flowctrl) { 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci cfhsi->flow_off_sent = 0; 3838c2ecf20Sopenharmony_ci cfhsi->cfdev.flowctrl(cfhsi->ndev, ON); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (cfhsi_can_send_aggregate(cfhsi)) { 3878c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 3888c2ecf20Sopenharmony_ci cfhsi_start_tx(cfhsi); 3898c2ecf20Sopenharmony_ci } else { 3908c2ecf20Sopenharmony_ci mod_timer(&cfhsi->aggregation_timer, 3918c2ecf20Sopenharmony_ci jiffies + cfhsi->cfg.aggregation_timeout); 3928c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void cfhsi_tx_done_cb(struct cfhsi_cb_ops *cb_ops) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct cfhsi *cfhsi; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); 4038c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 4048c2ecf20Sopenharmony_ci __func__); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci cfhsi_tx_done(cfhsi); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int xfer_sz = 0; 4148c2ecf20Sopenharmony_ci int nfrms = 0; 4158c2ecf20Sopenharmony_ci u16 *plen = NULL; 4168c2ecf20Sopenharmony_ci u8 *pfrm = NULL; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if ((desc->header & ~CFHSI_PIGGY_DESC) || 4198c2ecf20Sopenharmony_ci (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { 4208c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Invalid descriptor.\n", 4218c2ecf20Sopenharmony_ci __func__); 4228c2ecf20Sopenharmony_ci return -EPROTO; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Check for embedded CAIF frame. */ 4268c2ecf20Sopenharmony_ci if (desc->offset) { 4278c2ecf20Sopenharmony_ci struct sk_buff *skb; 4288c2ecf20Sopenharmony_ci int len = 0; 4298c2ecf20Sopenharmony_ci pfrm = ((u8 *)desc) + desc->offset; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Remove offset padding. */ 4328c2ecf20Sopenharmony_ci pfrm += *pfrm + 1; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Read length of CAIF frame (little endian). */ 4358c2ecf20Sopenharmony_ci len = *pfrm; 4368c2ecf20Sopenharmony_ci len |= ((*(pfrm+1)) << 8) & 0xFF00; 4378c2ecf20Sopenharmony_ci len += 2; /* Add FCS fields. */ 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* Sanity check length of CAIF frame. */ 4408c2ecf20Sopenharmony_ci if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { 4418c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Invalid length.\n", 4428c2ecf20Sopenharmony_ci __func__); 4438c2ecf20Sopenharmony_ci return -EPROTO; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Allocate SKB (OK even in IRQ context). */ 4478c2ecf20Sopenharmony_ci skb = alloc_skb(len + 1, GFP_ATOMIC); 4488c2ecf20Sopenharmony_ci if (!skb) { 4498c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Out of memory !\n", 4508c2ecf20Sopenharmony_ci __func__); 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci caif_assert(skb != NULL); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci skb_put_data(skb, pfrm, len); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 4588c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 4598c2ecf20Sopenharmony_ci skb->dev = cfhsi->ndev; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci netif_rx_any_context(skb); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Update network statistics. */ 4648c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_packets++; 4658c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_bytes += len; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Calculate transfer length. */ 4698c2ecf20Sopenharmony_ci plen = desc->cffrm_len; 4708c2ecf20Sopenharmony_ci while (nfrms < CFHSI_MAX_PKTS && *plen) { 4718c2ecf20Sopenharmony_ci xfer_sz += *plen; 4728c2ecf20Sopenharmony_ci plen++; 4738c2ecf20Sopenharmony_ci nfrms++; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Check for piggy-backed descriptor. */ 4778c2ecf20Sopenharmony_ci if (desc->header & CFHSI_PIGGY_DESC) 4788c2ecf20Sopenharmony_ci xfer_sz += CFHSI_DESC_SZ; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) { 4818c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, 4828c2ecf20Sopenharmony_ci "%s: Invalid payload len: %d, ignored.\n", 4838c2ecf20Sopenharmony_ci __func__, xfer_sz); 4848c2ecf20Sopenharmony_ci return -EPROTO; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci return xfer_sz; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int cfhsi_rx_desc_len(struct cfhsi_desc *desc) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int xfer_sz = 0; 4928c2ecf20Sopenharmony_ci int nfrms = 0; 4938c2ecf20Sopenharmony_ci u16 *plen; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if ((desc->header & ~CFHSI_PIGGY_DESC) || 4968c2ecf20Sopenharmony_ci (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci pr_err("Invalid descriptor. %x %x\n", desc->header, 4998c2ecf20Sopenharmony_ci desc->offset); 5008c2ecf20Sopenharmony_ci return -EPROTO; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Calculate transfer length. */ 5048c2ecf20Sopenharmony_ci plen = desc->cffrm_len; 5058c2ecf20Sopenharmony_ci while (nfrms < CFHSI_MAX_PKTS && *plen) { 5068c2ecf20Sopenharmony_ci xfer_sz += *plen; 5078c2ecf20Sopenharmony_ci plen++; 5088c2ecf20Sopenharmony_ci nfrms++; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (xfer_sz % 4) { 5128c2ecf20Sopenharmony_ci pr_err("Invalid payload len: %d, ignored.\n", xfer_sz); 5138c2ecf20Sopenharmony_ci return -EPROTO; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci return xfer_sz; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int rx_sz = 0; 5218c2ecf20Sopenharmony_ci int nfrms = 0; 5228c2ecf20Sopenharmony_ci u16 *plen = NULL; 5238c2ecf20Sopenharmony_ci u8 *pfrm = NULL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Sanity check header and offset. */ 5268c2ecf20Sopenharmony_ci if (WARN_ON((desc->header & ~CFHSI_PIGGY_DESC) || 5278c2ecf20Sopenharmony_ci (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { 5288c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Invalid descriptor.\n", 5298c2ecf20Sopenharmony_ci __func__); 5308c2ecf20Sopenharmony_ci return -EPROTO; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Set frame pointer to start of payload. */ 5348c2ecf20Sopenharmony_ci pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; 5358c2ecf20Sopenharmony_ci plen = desc->cffrm_len; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Skip already processed frames. */ 5388c2ecf20Sopenharmony_ci while (nfrms < cfhsi->rx_state.nfrms) { 5398c2ecf20Sopenharmony_ci pfrm += *plen; 5408c2ecf20Sopenharmony_ci rx_sz += *plen; 5418c2ecf20Sopenharmony_ci plen++; 5428c2ecf20Sopenharmony_ci nfrms++; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Parse payload. */ 5468c2ecf20Sopenharmony_ci while (nfrms < CFHSI_MAX_PKTS && *plen) { 5478c2ecf20Sopenharmony_ci struct sk_buff *skb; 5488c2ecf20Sopenharmony_ci u8 *pcffrm = NULL; 5498c2ecf20Sopenharmony_ci int len; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* CAIF frame starts after head padding. */ 5528c2ecf20Sopenharmony_ci pcffrm = pfrm + *pfrm + 1; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Read length of CAIF frame (little endian). */ 5558c2ecf20Sopenharmony_ci len = *pcffrm; 5568c2ecf20Sopenharmony_ci len |= ((*(pcffrm + 1)) << 8) & 0xFF00; 5578c2ecf20Sopenharmony_ci len += 2; /* Add FCS fields. */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Sanity check length of CAIF frames. */ 5608c2ecf20Sopenharmony_ci if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { 5618c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Invalid length.\n", 5628c2ecf20Sopenharmony_ci __func__); 5638c2ecf20Sopenharmony_ci return -EPROTO; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Allocate SKB (OK even in IRQ context). */ 5678c2ecf20Sopenharmony_ci skb = alloc_skb(len + 1, GFP_ATOMIC); 5688c2ecf20Sopenharmony_ci if (!skb) { 5698c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Out of memory !\n", 5708c2ecf20Sopenharmony_ci __func__); 5718c2ecf20Sopenharmony_ci cfhsi->rx_state.nfrms = nfrms; 5728c2ecf20Sopenharmony_ci return -ENOMEM; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci caif_assert(skb != NULL); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci skb_put_data(skb, pcffrm, len); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 5798c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 5808c2ecf20Sopenharmony_ci skb->dev = cfhsi->ndev; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci netif_rx_any_context(skb); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Update network statistics. */ 5858c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_packets++; 5868c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_bytes += len; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci pfrm += *plen; 5898c2ecf20Sopenharmony_ci rx_sz += *plen; 5908c2ecf20Sopenharmony_ci plen++; 5918c2ecf20Sopenharmony_ci nfrms++; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return rx_sz; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void cfhsi_rx_done(struct cfhsi *cfhsi) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci int res; 6008c2ecf20Sopenharmony_ci int desc_pld_len = 0, rx_len, rx_state; 6018c2ecf20Sopenharmony_ci struct cfhsi_desc *desc = NULL; 6028c2ecf20Sopenharmony_ci u8 *rx_ptr, *rx_buf; 6038c2ecf20Sopenharmony_ci struct cfhsi_desc *piggy_desc = NULL; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci desc = (struct cfhsi_desc *)cfhsi->rx_buf; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s\n", __func__); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Update inactivity timer if pending. */ 6138c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 6148c2ecf20Sopenharmony_ci mod_timer_pending(&cfhsi->inactivity_timer, 6158c2ecf20Sopenharmony_ci jiffies + cfhsi->cfg.inactivity_timeout); 6168c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { 6198c2ecf20Sopenharmony_ci desc_pld_len = cfhsi_rx_desc_len(desc); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (desc_pld_len < 0) 6228c2ecf20Sopenharmony_ci goto out_of_sync; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci rx_buf = cfhsi->rx_buf; 6258c2ecf20Sopenharmony_ci rx_len = desc_pld_len; 6268c2ecf20Sopenharmony_ci if (desc_pld_len > 0 && (desc->header & CFHSI_PIGGY_DESC)) 6278c2ecf20Sopenharmony_ci rx_len += CFHSI_DESC_SZ; 6288c2ecf20Sopenharmony_ci if (desc_pld_len == 0) 6298c2ecf20Sopenharmony_ci rx_buf = cfhsi->rx_flip_buf; 6308c2ecf20Sopenharmony_ci } else { 6318c2ecf20Sopenharmony_ci rx_buf = cfhsi->rx_flip_buf; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci rx_len = CFHSI_DESC_SZ; 6348c2ecf20Sopenharmony_ci if (cfhsi->rx_state.pld_len > 0 && 6358c2ecf20Sopenharmony_ci (desc->header & CFHSI_PIGGY_DESC)) { 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci piggy_desc = (struct cfhsi_desc *) 6388c2ecf20Sopenharmony_ci (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ + 6398c2ecf20Sopenharmony_ci cfhsi->rx_state.pld_len); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci cfhsi->rx_state.piggy_desc = true; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Extract payload len from piggy-backed descriptor. */ 6448c2ecf20Sopenharmony_ci desc_pld_len = cfhsi_rx_desc_len(piggy_desc); 6458c2ecf20Sopenharmony_ci if (desc_pld_len < 0) 6468c2ecf20Sopenharmony_ci goto out_of_sync; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (desc_pld_len > 0) { 6498c2ecf20Sopenharmony_ci rx_len = desc_pld_len; 6508c2ecf20Sopenharmony_ci if (piggy_desc->header & CFHSI_PIGGY_DESC) 6518c2ecf20Sopenharmony_ci rx_len += CFHSI_DESC_SZ; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Copy needed information from the piggy-backed 6568c2ecf20Sopenharmony_ci * descriptor to the descriptor in the start. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci memcpy(rx_buf, (u8 *)piggy_desc, 6598c2ecf20Sopenharmony_ci CFHSI_DESC_SHORT_SZ); 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (desc_pld_len) { 6648c2ecf20Sopenharmony_ci rx_state = CFHSI_RX_STATE_PAYLOAD; 6658c2ecf20Sopenharmony_ci rx_ptr = rx_buf + CFHSI_DESC_SZ; 6668c2ecf20Sopenharmony_ci } else { 6678c2ecf20Sopenharmony_ci rx_state = CFHSI_RX_STATE_DESC; 6688c2ecf20Sopenharmony_ci rx_ptr = rx_buf; 6698c2ecf20Sopenharmony_ci rx_len = CFHSI_DESC_SZ; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Initiate next read */ 6738c2ecf20Sopenharmony_ci if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) { 6748c2ecf20Sopenharmony_ci /* Set up new transfer. */ 6758c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", 6768c2ecf20Sopenharmony_ci __func__); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_rx(rx_ptr, rx_len, 6798c2ecf20Sopenharmony_ci cfhsi->ops); 6808c2ecf20Sopenharmony_ci if (WARN_ON(res < 0)) { 6818c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: RX error %d.\n", 6828c2ecf20Sopenharmony_ci __func__, res); 6838c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_errors++; 6848c2ecf20Sopenharmony_ci cfhsi->ndev->stats.rx_dropped++; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { 6898c2ecf20Sopenharmony_ci /* Extract payload from descriptor */ 6908c2ecf20Sopenharmony_ci if (cfhsi_rx_desc(desc, cfhsi) < 0) 6918c2ecf20Sopenharmony_ci goto out_of_sync; 6928c2ecf20Sopenharmony_ci } else { 6938c2ecf20Sopenharmony_ci /* Extract payload */ 6948c2ecf20Sopenharmony_ci if (cfhsi_rx_pld(desc, cfhsi) < 0) 6958c2ecf20Sopenharmony_ci goto out_of_sync; 6968c2ecf20Sopenharmony_ci if (piggy_desc) { 6978c2ecf20Sopenharmony_ci /* Extract any payload in piggyback descriptor. */ 6988c2ecf20Sopenharmony_ci if (cfhsi_rx_desc(piggy_desc, cfhsi) < 0) 6998c2ecf20Sopenharmony_ci goto out_of_sync; 7008c2ecf20Sopenharmony_ci /* Mark no embedded frame after extracting it */ 7018c2ecf20Sopenharmony_ci piggy_desc->offset = 0; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Update state info */ 7068c2ecf20Sopenharmony_ci memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); 7078c2ecf20Sopenharmony_ci cfhsi->rx_state.state = rx_state; 7088c2ecf20Sopenharmony_ci cfhsi->rx_ptr = rx_ptr; 7098c2ecf20Sopenharmony_ci cfhsi->rx_len = rx_len; 7108c2ecf20Sopenharmony_ci cfhsi->rx_state.pld_len = desc_pld_len; 7118c2ecf20Sopenharmony_ci cfhsi->rx_state.piggy_desc = desc->header & CFHSI_PIGGY_DESC; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (rx_buf != cfhsi->rx_buf) 7148c2ecf20Sopenharmony_ci swap(cfhsi->rx_buf, cfhsi->rx_flip_buf); 7158c2ecf20Sopenharmony_ci return; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ciout_of_sync: 7188c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Out of sync.\n", __func__); 7198c2ecf20Sopenharmony_ci print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, 7208c2ecf20Sopenharmony_ci cfhsi->rx_buf, CFHSI_DESC_SZ); 7218c2ecf20Sopenharmony_ci schedule_work(&cfhsi->out_of_sync_work); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void cfhsi_rx_slowpath(struct timer_list *t) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = from_timer(cfhsi, t, rx_slowpath_timer); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 7298c2ecf20Sopenharmony_ci __func__); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci cfhsi_rx_done(cfhsi); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic void cfhsi_rx_done_cb(struct cfhsi_cb_ops *cb_ops) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct cfhsi *cfhsi; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); 7398c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 7408c2ecf20Sopenharmony_ci __func__); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 7438c2ecf20Sopenharmony_ci return; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits)) 7468c2ecf20Sopenharmony_ci wake_up_interruptible(&cfhsi->flush_fifo_wait); 7478c2ecf20Sopenharmony_ci else 7488c2ecf20Sopenharmony_ci cfhsi_rx_done(cfhsi); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void cfhsi_wake_up(struct work_struct *work) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 7548c2ecf20Sopenharmony_ci int res; 7558c2ecf20Sopenharmony_ci int len; 7568c2ecf20Sopenharmony_ci long ret; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci cfhsi = container_of(work, struct cfhsi, wake_up_work); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 7618c2ecf20Sopenharmony_ci return; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (unlikely(test_bit(CFHSI_AWAKE, &cfhsi->bits))) { 7648c2ecf20Sopenharmony_ci /* It happenes when wakeup is requested by 7658c2ecf20Sopenharmony_ci * both ends at the same time. */ 7668c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); 7678c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); 7688c2ecf20Sopenharmony_ci return; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* Activate wake line. */ 7728c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_wake_up(cfhsi->ops); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Start waiting.\n", 7758c2ecf20Sopenharmony_ci __func__); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Wait for acknowledge. */ 7788c2ecf20Sopenharmony_ci ret = CFHSI_WAKE_TOUT; 7798c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(cfhsi->wake_up_wait, 7808c2ecf20Sopenharmony_ci test_and_clear_bit(CFHSI_WAKE_UP_ACK, 7818c2ecf20Sopenharmony_ci &cfhsi->bits), ret); 7828c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 7838c2ecf20Sopenharmony_ci /* Interrupted by signal. */ 7848c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Signalled: %ld.\n", 7858c2ecf20Sopenharmony_ci __func__, ret); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); 7888c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_wake_down(cfhsi->ops); 7898c2ecf20Sopenharmony_ci return; 7908c2ecf20Sopenharmony_ci } else if (!ret) { 7918c2ecf20Sopenharmony_ci bool ca_wake = false; 7928c2ecf20Sopenharmony_ci size_t fifo_occupancy = 0; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Wakeup timeout */ 7958c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Timeout.\n", 7968c2ecf20Sopenharmony_ci __func__); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Check FIFO to check if modem has sent something. */ 7998c2ecf20Sopenharmony_ci WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, 8008c2ecf20Sopenharmony_ci &fifo_occupancy)); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Bytes in FIFO: %u.\n", 8038c2ecf20Sopenharmony_ci __func__, (unsigned) fifo_occupancy); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Check if we misssed the interrupt. */ 8068c2ecf20Sopenharmony_ci WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, 8078c2ecf20Sopenharmony_ci &ca_wake)); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (ca_wake) { 8108c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", 8118c2ecf20Sopenharmony_ci __func__); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* Clear the CFHSI_WAKE_UP_ACK bit to prevent race. */ 8148c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* Continue execution. */ 8178c2ecf20Sopenharmony_ci goto wake_ack; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); 8218c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_wake_down(cfhsi->ops); 8228c2ecf20Sopenharmony_ci return; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ciwake_ack: 8258c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Woken.\n", 8268c2ecf20Sopenharmony_ci __func__); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Clear power up bit. */ 8298c2ecf20Sopenharmony_ci set_bit(CFHSI_AWAKE, &cfhsi->bits); 8308c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* Resume read operation. */ 8338c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); 8348c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->ops); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (WARN_ON(res < 0)) 8378c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: RX err %d.\n", __func__, res); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* Clear power up acknowledment. */ 8408c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Resume transmit if queues are not empty. */ 8458c2ecf20Sopenharmony_ci if (!cfhsi_tx_queue_len(cfhsi)) { 8468c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Peer wake, start timer.\n", 8478c2ecf20Sopenharmony_ci __func__); 8488c2ecf20Sopenharmony_ci /* Start inactivity timer. */ 8498c2ecf20Sopenharmony_ci mod_timer(&cfhsi->inactivity_timer, 8508c2ecf20Sopenharmony_ci jiffies + cfhsi->cfg.inactivity_timeout); 8518c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 8528c2ecf20Sopenharmony_ci return; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s: Host wake.\n", 8568c2ecf20Sopenharmony_ci __func__); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* Create HSI frame. */ 8618c2ecf20Sopenharmony_ci len = cfhsi_tx_frm((struct cfhsi_desc *)cfhsi->tx_buf, cfhsi); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (likely(len > 0)) { 8648c2ecf20Sopenharmony_ci /* Set up new transfer. */ 8658c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); 8668c2ecf20Sopenharmony_ci if (WARN_ON(res < 0)) { 8678c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: TX error %d.\n", 8688c2ecf20Sopenharmony_ci __func__, res); 8698c2ecf20Sopenharmony_ci cfhsi_abort_tx(cfhsi); 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci } else { 8728c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, 8738c2ecf20Sopenharmony_ci "%s: Failed to create HSI frame: %d.\n", 8748c2ecf20Sopenharmony_ci __func__, len); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic void cfhsi_wake_down(struct work_struct *work) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci long ret; 8818c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 8828c2ecf20Sopenharmony_ci size_t fifo_occupancy = 0; 8838c2ecf20Sopenharmony_ci int retry = CFHSI_WAKE_TOUT; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci cfhsi = container_of(work, struct cfhsi, wake_down_work); 8868c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", __func__); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 8898c2ecf20Sopenharmony_ci return; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Deactivate wake line. */ 8928c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_wake_down(cfhsi->ops); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Wait for acknowledge. */ 8958c2ecf20Sopenharmony_ci ret = CFHSI_WAKE_TOUT; 8968c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait, 8978c2ecf20Sopenharmony_ci test_and_clear_bit(CFHSI_WAKE_DOWN_ACK, 8988c2ecf20Sopenharmony_ci &cfhsi->bits), ret); 8998c2ecf20Sopenharmony_ci if (ret < 0) { 9008c2ecf20Sopenharmony_ci /* Interrupted by signal. */ 9018c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Signalled: %ld.\n", 9028c2ecf20Sopenharmony_ci __func__, ret); 9038c2ecf20Sopenharmony_ci return; 9048c2ecf20Sopenharmony_ci } else if (!ret) { 9058c2ecf20Sopenharmony_ci bool ca_wake = true; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* Timeout */ 9088c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Timeout.\n", __func__); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Check if we misssed the interrupt. */ 9118c2ecf20Sopenharmony_ci WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, 9128c2ecf20Sopenharmony_ci &ca_wake)); 9138c2ecf20Sopenharmony_ci if (!ca_wake) 9148c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", 9158c2ecf20Sopenharmony_ci __func__); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* Check FIFO occupancy. */ 9198c2ecf20Sopenharmony_ci while (retry) { 9208c2ecf20Sopenharmony_ci WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, 9218c2ecf20Sopenharmony_ci &fifo_occupancy)); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (!fifo_occupancy) 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 9278c2ecf20Sopenharmony_ci schedule_timeout(1); 9288c2ecf20Sopenharmony_ci retry--; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (!retry) 9328c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: FIFO Timeout.\n", __func__); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Clear AWAKE condition. */ 9358c2ecf20Sopenharmony_ci clear_bit(CFHSI_AWAKE, &cfhsi->bits); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* Cancel pending RX requests. */ 9388c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic void cfhsi_out_of_sync(struct work_struct *work) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci cfhsi = container_of(work, struct cfhsi, out_of_sync_work); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci rtnl_lock(); 9488c2ecf20Sopenharmony_ci dev_close(cfhsi->ndev); 9498c2ecf20Sopenharmony_ci rtnl_unlock(); 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic void cfhsi_wake_up_cb(struct cfhsi_cb_ops *cb_ops) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); 9578c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 9588c2ecf20Sopenharmony_ci __func__); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci set_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); 9618c2ecf20Sopenharmony_ci wake_up_interruptible(&cfhsi->wake_up_wait); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) 9648c2ecf20Sopenharmony_ci return; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Schedule wake up work queue if the peer initiates. */ 9678c2ecf20Sopenharmony_ci if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) 9688c2ecf20Sopenharmony_ci queue_work(cfhsi->wq, &cfhsi->wake_up_work); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic void cfhsi_wake_down_cb(struct cfhsi_cb_ops *cb_ops) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); 9768c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 9778c2ecf20Sopenharmony_ci __func__); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* Initiating low power is only permitted by the host (us). */ 9808c2ecf20Sopenharmony_ci set_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); 9818c2ecf20Sopenharmony_ci wake_up_interruptible(&cfhsi->wake_down_wait); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic void cfhsi_aggregation_tout(struct timer_list *t) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = from_timer(cfhsi, t, aggregation_timer); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci netdev_dbg(cfhsi->ndev, "%s.\n", 9898c2ecf20Sopenharmony_ci __func__); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci cfhsi_start_tx(cfhsi); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic netdev_tx_t cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 9978c2ecf20Sopenharmony_ci int start_xfer = 0; 9988c2ecf20Sopenharmony_ci int timer_active; 9998c2ecf20Sopenharmony_ci int prio; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (!dev) 10028c2ecf20Sopenharmony_ci return -EINVAL; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci cfhsi = netdev_priv(dev); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci switch (skb->priority) { 10078c2ecf20Sopenharmony_ci case TC_PRIO_BESTEFFORT: 10088c2ecf20Sopenharmony_ci case TC_PRIO_FILLER: 10098c2ecf20Sopenharmony_ci case TC_PRIO_BULK: 10108c2ecf20Sopenharmony_ci prio = CFHSI_PRIO_BEBK; 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci case TC_PRIO_INTERACTIVE_BULK: 10138c2ecf20Sopenharmony_ci prio = CFHSI_PRIO_VI; 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci case TC_PRIO_INTERACTIVE: 10168c2ecf20Sopenharmony_ci prio = CFHSI_PRIO_VO; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case TC_PRIO_CONTROL: 10198c2ecf20Sopenharmony_ci default: 10208c2ecf20Sopenharmony_ci prio = CFHSI_PRIO_CTL; 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci spin_lock_bh(&cfhsi->lock); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Update aggregation statistics */ 10278c2ecf20Sopenharmony_ci cfhsi_update_aggregation_stats(cfhsi, skb, 1); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* Queue the SKB */ 10308c2ecf20Sopenharmony_ci skb_queue_tail(&cfhsi->qhead[prio], skb); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* Sanity check; xmit should not be called after unregister_netdev */ 10338c2ecf20Sopenharmony_ci if (WARN_ON(test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))) { 10348c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 10358c2ecf20Sopenharmony_ci cfhsi_abort_tx(cfhsi); 10368c2ecf20Sopenharmony_ci return -EINVAL; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Send flow off if number of packets is above high water mark. */ 10408c2ecf20Sopenharmony_ci if (!cfhsi->flow_off_sent && 10418c2ecf20Sopenharmony_ci cfhsi_tx_queue_len(cfhsi) > cfhsi->cfg.q_high_mark && 10428c2ecf20Sopenharmony_ci cfhsi->cfdev.flowctrl) { 10438c2ecf20Sopenharmony_ci cfhsi->flow_off_sent = 1; 10448c2ecf20Sopenharmony_ci cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (cfhsi->tx_state == CFHSI_TX_STATE_IDLE) { 10488c2ecf20Sopenharmony_ci cfhsi->tx_state = CFHSI_TX_STATE_XFER; 10498c2ecf20Sopenharmony_ci start_xfer = 1; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!start_xfer) { 10538c2ecf20Sopenharmony_ci /* Send aggregate if it is possible */ 10548c2ecf20Sopenharmony_ci bool aggregate_ready = 10558c2ecf20Sopenharmony_ci cfhsi_can_send_aggregate(cfhsi) && 10568c2ecf20Sopenharmony_ci del_timer(&cfhsi->aggregation_timer) > 0; 10578c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 10588c2ecf20Sopenharmony_ci if (aggregate_ready) 10598c2ecf20Sopenharmony_ci cfhsi_start_tx(cfhsi); 10608c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* Delete inactivity timer if started. */ 10648c2ecf20Sopenharmony_ci timer_active = del_timer_sync(&cfhsi->inactivity_timer); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci spin_unlock_bh(&cfhsi->lock); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (timer_active) { 10698c2ecf20Sopenharmony_ci struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf; 10708c2ecf20Sopenharmony_ci int len; 10718c2ecf20Sopenharmony_ci int res; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* Create HSI frame. */ 10748c2ecf20Sopenharmony_ci len = cfhsi_tx_frm(desc, cfhsi); 10758c2ecf20Sopenharmony_ci WARN_ON(!len); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* Set up new transfer. */ 10788c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); 10798c2ecf20Sopenharmony_ci if (WARN_ON(res < 0)) { 10808c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: TX error %d.\n", 10818c2ecf20Sopenharmony_ci __func__, res); 10828c2ecf20Sopenharmony_ci cfhsi_abort_tx(cfhsi); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } else { 10858c2ecf20Sopenharmony_ci /* Schedule wake up work queue if the we initiate. */ 10868c2ecf20Sopenharmony_ci if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) 10878c2ecf20Sopenharmony_ci queue_work(cfhsi->wq, &cfhsi->wake_up_work); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic const struct net_device_ops cfhsi_netdevops; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic void cfhsi_setup(struct net_device *dev) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci int i; 10988c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = netdev_priv(dev); 10998c2ecf20Sopenharmony_ci dev->features = 0; 11008c2ecf20Sopenharmony_ci dev->type = ARPHRD_CAIF; 11018c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 11028c2ecf20Sopenharmony_ci dev->mtu = CFHSI_MAX_CAIF_FRAME_SZ; 11038c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; 11048c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 11058c2ecf20Sopenharmony_ci dev->netdev_ops = &cfhsi_netdevops; 11068c2ecf20Sopenharmony_ci for (i = 0; i < CFHSI_PRIO_LAST; ++i) 11078c2ecf20Sopenharmony_ci skb_queue_head_init(&cfhsi->qhead[i]); 11088c2ecf20Sopenharmony_ci cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW; 11098c2ecf20Sopenharmony_ci cfhsi->cfdev.use_frag = false; 11108c2ecf20Sopenharmony_ci cfhsi->cfdev.use_stx = false; 11118c2ecf20Sopenharmony_ci cfhsi->cfdev.use_fcs = false; 11128c2ecf20Sopenharmony_ci cfhsi->ndev = dev; 11138c2ecf20Sopenharmony_ci cfhsi->cfg = hsi_default_config; 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic int cfhsi_open(struct net_device *ndev) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = netdev_priv(ndev); 11198c2ecf20Sopenharmony_ci int res; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci clear_bit(CFHSI_SHUTDOWN, &cfhsi->bits); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* Initialize state vaiables. */ 11248c2ecf20Sopenharmony_ci cfhsi->tx_state = CFHSI_TX_STATE_IDLE; 11258c2ecf20Sopenharmony_ci cfhsi->rx_state.state = CFHSI_RX_STATE_DESC; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Set flow info */ 11288c2ecf20Sopenharmony_ci cfhsi->flow_off_sent = 0; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* 11318c2ecf20Sopenharmony_ci * Allocate a TX buffer with the size of a HSI packet descriptors 11328c2ecf20Sopenharmony_ci * and the necessary room for CAIF payload frames. 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_ci cfhsi->tx_buf = kzalloc(CFHSI_BUF_SZ_TX, GFP_KERNEL); 11358c2ecf20Sopenharmony_ci if (!cfhsi->tx_buf) { 11368c2ecf20Sopenharmony_ci res = -ENODEV; 11378c2ecf20Sopenharmony_ci goto err_alloc_tx; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* 11418c2ecf20Sopenharmony_ci * Allocate a RX buffer with the size of two HSI packet descriptors and 11428c2ecf20Sopenharmony_ci * the necessary room for CAIF payload frames. 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci cfhsi->rx_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL); 11458c2ecf20Sopenharmony_ci if (!cfhsi->rx_buf) { 11468c2ecf20Sopenharmony_ci res = -ENODEV; 11478c2ecf20Sopenharmony_ci goto err_alloc_rx; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci cfhsi->rx_flip_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL); 11518c2ecf20Sopenharmony_ci if (!cfhsi->rx_flip_buf) { 11528c2ecf20Sopenharmony_ci res = -ENODEV; 11538c2ecf20Sopenharmony_ci goto err_alloc_rx_flip; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* Initialize aggregation timeout */ 11578c2ecf20Sopenharmony_ci cfhsi->cfg.aggregation_timeout = hsi_default_config.aggregation_timeout; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci /* Initialize recieve vaiables. */ 11608c2ecf20Sopenharmony_ci cfhsi->rx_ptr = cfhsi->rx_buf; 11618c2ecf20Sopenharmony_ci cfhsi->rx_len = CFHSI_DESC_SZ; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* Initialize spin locks. */ 11648c2ecf20Sopenharmony_ci spin_lock_init(&cfhsi->lock); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Set up the driver. */ 11678c2ecf20Sopenharmony_ci cfhsi->cb_ops.tx_done_cb = cfhsi_tx_done_cb; 11688c2ecf20Sopenharmony_ci cfhsi->cb_ops.rx_done_cb = cfhsi_rx_done_cb; 11698c2ecf20Sopenharmony_ci cfhsi->cb_ops.wake_up_cb = cfhsi_wake_up_cb; 11708c2ecf20Sopenharmony_ci cfhsi->cb_ops.wake_down_cb = cfhsi_wake_down_cb; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* Initialize the work queues. */ 11738c2ecf20Sopenharmony_ci INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); 11748c2ecf20Sopenharmony_ci INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); 11758c2ecf20Sopenharmony_ci INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* Clear all bit fields. */ 11788c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); 11798c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits); 11808c2ecf20Sopenharmony_ci clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); 11818c2ecf20Sopenharmony_ci clear_bit(CFHSI_AWAKE, &cfhsi->bits); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Create work thread. */ 11848c2ecf20Sopenharmony_ci cfhsi->wq = alloc_ordered_workqueue(cfhsi->ndev->name, WQ_MEM_RECLAIM); 11858c2ecf20Sopenharmony_ci if (!cfhsi->wq) { 11868c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Failed to create work queue.\n", 11878c2ecf20Sopenharmony_ci __func__); 11888c2ecf20Sopenharmony_ci res = -ENODEV; 11898c2ecf20Sopenharmony_ci goto err_create_wq; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* Initialize wait queues. */ 11938c2ecf20Sopenharmony_ci init_waitqueue_head(&cfhsi->wake_up_wait); 11948c2ecf20Sopenharmony_ci init_waitqueue_head(&cfhsi->wake_down_wait); 11958c2ecf20Sopenharmony_ci init_waitqueue_head(&cfhsi->flush_fifo_wait); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* Setup the inactivity timer. */ 11988c2ecf20Sopenharmony_ci timer_setup(&cfhsi->inactivity_timer, cfhsi_inactivity_tout, 0); 11998c2ecf20Sopenharmony_ci /* Setup the slowpath RX timer. */ 12008c2ecf20Sopenharmony_ci timer_setup(&cfhsi->rx_slowpath_timer, cfhsi_rx_slowpath, 0); 12018c2ecf20Sopenharmony_ci /* Setup the aggregation timer. */ 12028c2ecf20Sopenharmony_ci timer_setup(&cfhsi->aggregation_timer, cfhsi_aggregation_tout, 0); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* Activate HSI interface. */ 12058c2ecf20Sopenharmony_ci res = cfhsi->ops->cfhsi_up(cfhsi->ops); 12068c2ecf20Sopenharmony_ci if (res) { 12078c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, 12088c2ecf20Sopenharmony_ci "%s: can't activate HSI interface: %d.\n", 12098c2ecf20Sopenharmony_ci __func__, res); 12108c2ecf20Sopenharmony_ci goto err_activate; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Flush FIFO */ 12148c2ecf20Sopenharmony_ci res = cfhsi_flush_fifo(cfhsi); 12158c2ecf20Sopenharmony_ci if (res) { 12168c2ecf20Sopenharmony_ci netdev_err(cfhsi->ndev, "%s: Can't flush FIFO: %d.\n", 12178c2ecf20Sopenharmony_ci __func__, res); 12188c2ecf20Sopenharmony_ci goto err_net_reg; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci return res; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci err_net_reg: 12238c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_down(cfhsi->ops); 12248c2ecf20Sopenharmony_ci err_activate: 12258c2ecf20Sopenharmony_ci destroy_workqueue(cfhsi->wq); 12268c2ecf20Sopenharmony_ci err_create_wq: 12278c2ecf20Sopenharmony_ci kfree(cfhsi->rx_flip_buf); 12288c2ecf20Sopenharmony_ci err_alloc_rx_flip: 12298c2ecf20Sopenharmony_ci kfree(cfhsi->rx_buf); 12308c2ecf20Sopenharmony_ci err_alloc_rx: 12318c2ecf20Sopenharmony_ci kfree(cfhsi->tx_buf); 12328c2ecf20Sopenharmony_ci err_alloc_tx: 12338c2ecf20Sopenharmony_ci return res; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int cfhsi_close(struct net_device *ndev) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = netdev_priv(ndev); 12398c2ecf20Sopenharmony_ci u8 *tx_buf, *rx_buf, *flip_buf; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* going to shutdown driver */ 12428c2ecf20Sopenharmony_ci set_bit(CFHSI_SHUTDOWN, &cfhsi->bits); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* Delete timers if pending */ 12458c2ecf20Sopenharmony_ci del_timer_sync(&cfhsi->inactivity_timer); 12468c2ecf20Sopenharmony_ci del_timer_sync(&cfhsi->rx_slowpath_timer); 12478c2ecf20Sopenharmony_ci del_timer_sync(&cfhsi->aggregation_timer); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* Cancel pending RX request (if any) */ 12508c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* Destroy workqueue */ 12538c2ecf20Sopenharmony_ci destroy_workqueue(cfhsi->wq); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* Store bufferes: will be freed later. */ 12568c2ecf20Sopenharmony_ci tx_buf = cfhsi->tx_buf; 12578c2ecf20Sopenharmony_ci rx_buf = cfhsi->rx_buf; 12588c2ecf20Sopenharmony_ci flip_buf = cfhsi->rx_flip_buf; 12598c2ecf20Sopenharmony_ci /* Flush transmit queues. */ 12608c2ecf20Sopenharmony_ci cfhsi_abort_tx(cfhsi); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Deactivate interface */ 12638c2ecf20Sopenharmony_ci cfhsi->ops->cfhsi_down(cfhsi->ops); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* Free buffers. */ 12668c2ecf20Sopenharmony_ci kfree(tx_buf); 12678c2ecf20Sopenharmony_ci kfree(rx_buf); 12688c2ecf20Sopenharmony_ci kfree(flip_buf); 12698c2ecf20Sopenharmony_ci return 0; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic void cfhsi_uninit(struct net_device *dev) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = netdev_priv(dev); 12758c2ecf20Sopenharmony_ci ASSERT_RTNL(); 12768c2ecf20Sopenharmony_ci symbol_put(cfhsi_get_device); 12778c2ecf20Sopenharmony_ci list_del(&cfhsi->list); 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic const struct net_device_ops cfhsi_netdevops = { 12818c2ecf20Sopenharmony_ci .ndo_uninit = cfhsi_uninit, 12828c2ecf20Sopenharmony_ci .ndo_open = cfhsi_open, 12838c2ecf20Sopenharmony_ci .ndo_stop = cfhsi_close, 12848c2ecf20Sopenharmony_ci .ndo_start_xmit = cfhsi_xmit 12858c2ecf20Sopenharmony_ci}; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci int i; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (!data) { 12928c2ecf20Sopenharmony_ci pr_debug("no params data found\n"); 12938c2ecf20Sopenharmony_ci return; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; 12978c2ecf20Sopenharmony_ci /* 12988c2ecf20Sopenharmony_ci * Inactivity timeout in millisecs. Lowest possible value is 1, 12998c2ecf20Sopenharmony_ci * and highest possible is NEXT_TIMER_MAX_DELTA. 13008c2ecf20Sopenharmony_ci */ 13018c2ecf20Sopenharmony_ci if (data[i]) { 13028c2ecf20Sopenharmony_ci u32 inactivity_timeout = nla_get_u32(data[i]); 13038c2ecf20Sopenharmony_ci /* Pre-calculate inactivity timeout. */ 13048c2ecf20Sopenharmony_ci cfhsi->cfg.inactivity_timeout = inactivity_timeout * HZ / 1000; 13058c2ecf20Sopenharmony_ci if (cfhsi->cfg.inactivity_timeout == 0) 13068c2ecf20Sopenharmony_ci cfhsi->cfg.inactivity_timeout = 1; 13078c2ecf20Sopenharmony_ci else if (cfhsi->cfg.inactivity_timeout > NEXT_TIMER_MAX_DELTA) 13088c2ecf20Sopenharmony_ci cfhsi->cfg.inactivity_timeout = NEXT_TIMER_MAX_DELTA; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; 13128c2ecf20Sopenharmony_ci if (data[i]) 13138c2ecf20Sopenharmony_ci cfhsi->cfg.aggregation_timeout = nla_get_u32(data[i]); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_HEAD_ALIGN; 13168c2ecf20Sopenharmony_ci if (data[i]) 13178c2ecf20Sopenharmony_ci cfhsi->cfg.head_align = nla_get_u32(data[i]); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_TAIL_ALIGN; 13208c2ecf20Sopenharmony_ci if (data[i]) 13218c2ecf20Sopenharmony_ci cfhsi->cfg.tail_align = nla_get_u32(data[i]); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; 13248c2ecf20Sopenharmony_ci if (data[i]) 13258c2ecf20Sopenharmony_ci cfhsi->cfg.q_high_mark = nla_get_u32(data[i]); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci i = __IFLA_CAIF_HSI_QLOW_WATERMARK; 13288c2ecf20Sopenharmony_ci if (data[i]) 13298c2ecf20Sopenharmony_ci cfhsi->cfg.q_low_mark = nla_get_u32(data[i]); 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], 13338c2ecf20Sopenharmony_ci struct nlattr *data[], 13348c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci cfhsi_netlink_parms(data, netdev_priv(dev)); 13378c2ecf20Sopenharmony_ci netdev_state_change(dev); 13388c2ecf20Sopenharmony_ci return 0; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic const struct nla_policy caif_hsi_policy[__IFLA_CAIF_HSI_MAX + 1] = { 13428c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_INACTIVITY_TOUT] = { .type = NLA_U32, .len = 4 }, 13438c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_AGGREGATION_TOUT] = { .type = NLA_U32, .len = 4 }, 13448c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_HEAD_ALIGN] = { .type = NLA_U32, .len = 4 }, 13458c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_TAIL_ALIGN] = { .type = NLA_U32, .len = 4 }, 13468c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_QHIGH_WATERMARK] = { .type = NLA_U32, .len = 4 }, 13478c2ecf20Sopenharmony_ci [__IFLA_CAIF_HSI_QLOW_WATERMARK] = { .type = NLA_U32, .len = 4 }, 13488c2ecf20Sopenharmony_ci}; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic size_t caif_hsi_get_size(const struct net_device *dev) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci int i; 13538c2ecf20Sopenharmony_ci size_t s = 0; 13548c2ecf20Sopenharmony_ci for (i = __IFLA_CAIF_HSI_UNSPEC + 1; i < __IFLA_CAIF_HSI_MAX; i++) 13558c2ecf20Sopenharmony_ci s += nla_total_size(caif_hsi_policy[i].len); 13568c2ecf20Sopenharmony_ci return s; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = netdev_priv(dev); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, 13648c2ecf20Sopenharmony_ci cfhsi->cfg.inactivity_timeout) || 13658c2ecf20Sopenharmony_ci nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, 13668c2ecf20Sopenharmony_ci cfhsi->cfg.aggregation_timeout) || 13678c2ecf20Sopenharmony_ci nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, 13688c2ecf20Sopenharmony_ci cfhsi->cfg.head_align) || 13698c2ecf20Sopenharmony_ci nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, 13708c2ecf20Sopenharmony_ci cfhsi->cfg.tail_align) || 13718c2ecf20Sopenharmony_ci nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, 13728c2ecf20Sopenharmony_ci cfhsi->cfg.q_high_mark) || 13738c2ecf20Sopenharmony_ci nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, 13748c2ecf20Sopenharmony_ci cfhsi->cfg.q_low_mark)) 13758c2ecf20Sopenharmony_ci return -EMSGSIZE; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return 0; 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic int caif_hsi_newlink(struct net *src_net, struct net_device *dev, 13818c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 13828c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci struct cfhsi *cfhsi = NULL; 13858c2ecf20Sopenharmony_ci struct cfhsi_ops *(*get_ops)(void); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci ASSERT_RTNL(); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci cfhsi = netdev_priv(dev); 13908c2ecf20Sopenharmony_ci cfhsi_netlink_parms(data, cfhsi); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci get_ops = symbol_get(cfhsi_get_ops); 13938c2ecf20Sopenharmony_ci if (!get_ops) { 13948c2ecf20Sopenharmony_ci pr_err("%s: failed to get the cfhsi_ops\n", __func__); 13958c2ecf20Sopenharmony_ci return -ENODEV; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* Assign the HSI device. */ 13998c2ecf20Sopenharmony_ci cfhsi->ops = (*get_ops)(); 14008c2ecf20Sopenharmony_ci if (!cfhsi->ops) { 14018c2ecf20Sopenharmony_ci pr_err("%s: failed to get the cfhsi_ops\n", __func__); 14028c2ecf20Sopenharmony_ci goto err; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* Assign the driver to this HSI device. */ 14068c2ecf20Sopenharmony_ci cfhsi->ops->cb_ops = &cfhsi->cb_ops; 14078c2ecf20Sopenharmony_ci if (register_netdevice(dev)) { 14088c2ecf20Sopenharmony_ci pr_warn("%s: caif_hsi device registration failed\n", __func__); 14098c2ecf20Sopenharmony_ci goto err; 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci /* Add CAIF HSI device to list. */ 14128c2ecf20Sopenharmony_ci list_add_tail(&cfhsi->list, &cfhsi_list); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return 0; 14158c2ecf20Sopenharmony_cierr: 14168c2ecf20Sopenharmony_ci symbol_put(cfhsi_get_ops); 14178c2ecf20Sopenharmony_ci return -ENODEV; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic struct rtnl_link_ops caif_hsi_link_ops __read_mostly = { 14218c2ecf20Sopenharmony_ci .kind = "cfhsi", 14228c2ecf20Sopenharmony_ci .priv_size = sizeof(struct cfhsi), 14238c2ecf20Sopenharmony_ci .setup = cfhsi_setup, 14248c2ecf20Sopenharmony_ci .maxtype = __IFLA_CAIF_HSI_MAX, 14258c2ecf20Sopenharmony_ci .policy = caif_hsi_policy, 14268c2ecf20Sopenharmony_ci .newlink = caif_hsi_newlink, 14278c2ecf20Sopenharmony_ci .changelink = caif_hsi_changelink, 14288c2ecf20Sopenharmony_ci .get_size = caif_hsi_get_size, 14298c2ecf20Sopenharmony_ci .fill_info = caif_hsi_fill_info, 14308c2ecf20Sopenharmony_ci}; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic void __exit cfhsi_exit_module(void) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci struct list_head *list_node; 14358c2ecf20Sopenharmony_ci struct list_head *n; 14368c2ecf20Sopenharmony_ci struct cfhsi *cfhsi; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci rtnl_link_unregister(&caif_hsi_link_ops); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci rtnl_lock(); 14418c2ecf20Sopenharmony_ci list_for_each_safe(list_node, n, &cfhsi_list) { 14428c2ecf20Sopenharmony_ci cfhsi = list_entry(list_node, struct cfhsi, list); 14438c2ecf20Sopenharmony_ci unregister_netdevice(cfhsi->ndev); 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci rtnl_unlock(); 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_cistatic int __init cfhsi_init_module(void) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci return rtnl_link_register(&caif_hsi_link_ops); 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cimodule_init(cfhsi_init_module); 14548c2ecf20Sopenharmony_cimodule_exit(cfhsi_exit_module); 1455