1ab9953ffSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 24a71df50SFrank Blaschka /* 3bbcfcdc8SFrank Blaschka * Copyright IBM Corp. 2007, 2009 44a71df50SFrank Blaschka * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, 54a71df50SFrank Blaschka * Frank Pavlic <fpavlic@de.ibm.com>, 64a71df50SFrank Blaschka * Thomas Spatzier <tspat@de.ibm.com>, 74a71df50SFrank Blaschka * Frank Blaschka <frank.blaschka@de.ibm.com> 84a71df50SFrank Blaschka */ 94a71df50SFrank Blaschka 1074eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth" 1174eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1274eacdb9SFrank Blaschka 130d55303cSDeepa Dinamani #include <linux/compat.h> 144a71df50SFrank Blaschka #include <linux/module.h> 154a71df50SFrank Blaschka #include <linux/moduleparam.h> 164a71df50SFrank Blaschka #include <linux/string.h> 174a71df50SFrank Blaschka #include <linux/errno.h> 184a71df50SFrank Blaschka #include <linux/kernel.h> 194a71df50SFrank Blaschka #include <linux/ip.h> 204a71df50SFrank Blaschka #include <linux/tcp.h> 214a71df50SFrank Blaschka #include <linux/mii.h> 224a71df50SFrank Blaschka #include <linux/kthread.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 246d69b1f1SJulian Wiedmann #include <linux/if_vlan.h> 256d69b1f1SJulian Wiedmann #include <linux/netdevice.h> 266d69b1f1SJulian Wiedmann #include <linux/netdev_features.h> 276d69b1f1SJulian Wiedmann #include <linux/skbuff.h> 28aec45e85SWenjia Zhang #include <linux/vmalloc.h> 296d69b1f1SJulian Wiedmann 30b3332930SFrank Blaschka #include <net/iucv/af_iucv.h> 31290b8348SStefan Raspl #include <net/dsfield.h> 324a71df50SFrank Blaschka 33ab4227cbSMartin Schwidefsky #include <asm/ebcdic.h> 342bf29df7SSebastian Ott #include <asm/chpid.h> 35ab4227cbSMartin Schwidefsky #include <asm/io.h> 361da74b1cSFrank Blaschka #include <asm/sysinfo.h> 37ec61bd2fSJulian Wiedmann #include <asm/diag.h> 38ec61bd2fSJulian Wiedmann #include <asm/cio.h> 39ec61bd2fSJulian Wiedmann #include <asm/ccwdev.h> 40615dff22SJulian Wiedmann #include <asm/cpcmd.h> 414a71df50SFrank Blaschka 424a71df50SFrank Blaschka #include "qeth_core.h" 434a71df50SFrank Blaschka 44d11ba0c4SPeter Tiedemann struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { 45d11ba0c4SPeter Tiedemann /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */ 46d11ba0c4SPeter Tiedemann /* N P A M L V H */ 47d11ba0c4SPeter Tiedemann [QETH_DBF_SETUP] = {"qeth_setup", 48d11ba0c4SPeter Tiedemann 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, 49f7e1e65dSSebastian Ott [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, 50f7e1e65dSSebastian Ott &debug_sprintf_view, NULL}, 51d11ba0c4SPeter Tiedemann [QETH_DBF_CTRL] = {"qeth_control", 52d11ba0c4SPeter Tiedemann 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, 53d11ba0c4SPeter Tiedemann }; 54d11ba0c4SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf); 554a71df50SFrank Blaschka 564a71df50SFrank Blaschka struct qeth_card_list_struct qeth_core_card_list; 574a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_card_list); 58683d718aSFrank Blaschka struct kmem_cache *qeth_core_header_cache; 59683d718aSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_header_cache); 600da9581dSEinar Lueck static struct kmem_cache *qeth_qdio_outbuf_cache; 614a71df50SFrank Blaschka 624a71df50SFrank Blaschka static struct device *qeth_core_root_dev; 634a71df50SFrank Blaschka static struct lock_class_key qdio_out_skb_queue_key; 642022e00cSFrank Blaschka static struct mutex qeth_mod_mutex; 654a71df50SFrank Blaschka 664a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *, 674a71df50SFrank Blaschka struct qeth_cmd_buffer *); 684a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *); 694a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *); 704a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *); 710da9581dSEinar Lueck static void qeth_free_qdio_buffers(struct qeth_card *); 72b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *queue, 73b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 74b3332930SFrank Blaschka enum iucv_tx_notify notification); 75b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); 7672861ae7SEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); 774a71df50SFrank Blaschka 78b4d72c08SEugene Crosser struct workqueue_struct *qeth_wq; 79c044dc21SEugene Crosser EXPORT_SYMBOL_GPL(qeth_wq); 800f54761dSStefan Raspl 81511c2445SEugene Crosser int qeth_card_hw_is_reachable(struct qeth_card *card) 82511c2445SEugene Crosser { 83511c2445SEugene Crosser return (card->state == CARD_STATE_SOFTSETUP) || 84511c2445SEugene Crosser (card->state == CARD_STATE_UP); 85511c2445SEugene Crosser } 86511c2445SEugene Crosser EXPORT_SYMBOL_GPL(qeth_card_hw_is_reachable); 87511c2445SEugene Crosser 880f54761dSStefan Raspl static void qeth_close_dev_handler(struct work_struct *work) 890f54761dSStefan Raspl { 900f54761dSStefan Raspl struct qeth_card *card; 910f54761dSStefan Raspl 920f54761dSStefan Raspl card = container_of(work, struct qeth_card, close_dev_work); 930f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevhdl"); 940f54761dSStefan Raspl rtnl_lock(); 950f54761dSStefan Raspl dev_close(card->dev); 960f54761dSStefan Raspl rtnl_unlock(); 970f54761dSStefan Raspl ccwgroup_set_offline(card->gdev); 980f54761dSStefan Raspl } 990f54761dSStefan Raspl 1000f54761dSStefan Raspl void qeth_close_dev(struct qeth_card *card) 1010f54761dSStefan Raspl { 1020f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevsubm"); 1030f54761dSStefan Raspl queue_work(qeth_wq, &card->close_dev_work); 1040f54761dSStefan Raspl } 1050f54761dSStefan Raspl EXPORT_SYMBOL_GPL(qeth_close_dev); 1060f54761dSStefan Raspl 107cef6ff22SJulian Wiedmann static const char *qeth_get_cardname(struct qeth_card *card) 1084a71df50SFrank Blaschka { 1094a71df50SFrank Blaschka if (card->info.guestlan) { 1104a71df50SFrank Blaschka switch (card->info.type) { 1115113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1127096b187SStefan Raspl return " Virtual NIC QDIO"; 1134a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1147096b187SStefan Raspl return " Virtual NIC Hiper"; 1155113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1167096b187SStefan Raspl return " Virtual NIC QDIO - OSM"; 1175113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1187096b187SStefan Raspl return " Virtual NIC QDIO - OSX"; 1194a71df50SFrank Blaschka default: 1204a71df50SFrank Blaschka return " unknown"; 1214a71df50SFrank Blaschka } 1224a71df50SFrank Blaschka } else { 1234a71df50SFrank Blaschka switch (card->info.type) { 1245113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1254a71df50SFrank Blaschka return " OSD Express"; 1264a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1274a71df50SFrank Blaschka return " HiperSockets"; 1284a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1294a71df50SFrank Blaschka return " OSN QDIO"; 1305113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1315113fec0SUrsula Braun return " OSM QDIO"; 1325113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1335113fec0SUrsula Braun return " OSX QDIO"; 1344a71df50SFrank Blaschka default: 1354a71df50SFrank Blaschka return " unknown"; 1364a71df50SFrank Blaschka } 1374a71df50SFrank Blaschka } 1384a71df50SFrank Blaschka return " n/a"; 1394a71df50SFrank Blaschka } 1404a71df50SFrank Blaschka 1414a71df50SFrank Blaschka /* max length to be returned: 14 */ 1424a71df50SFrank Blaschka const char *qeth_get_cardname_short(struct qeth_card *card) 1434a71df50SFrank Blaschka { 1444a71df50SFrank Blaschka if (card->info.guestlan) { 1454a71df50SFrank Blaschka switch (card->info.type) { 1465113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1477096b187SStefan Raspl return "Virt.NIC QDIO"; 1484a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1497096b187SStefan Raspl return "Virt.NIC Hiper"; 1505113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1517096b187SStefan Raspl return "Virt.NIC OSM"; 1525113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1537096b187SStefan Raspl return "Virt.NIC OSX"; 1544a71df50SFrank Blaschka default: 1554a71df50SFrank Blaschka return "unknown"; 1564a71df50SFrank Blaschka } 1574a71df50SFrank Blaschka } else { 1584a71df50SFrank Blaschka switch (card->info.type) { 1595113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1604a71df50SFrank Blaschka switch (card->info.link_type) { 1614a71df50SFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 1624a71df50SFrank Blaschka return "OSD_100"; 1634a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 1644a71df50SFrank Blaschka return "HSTR"; 1654a71df50SFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 1664a71df50SFrank Blaschka return "OSD_1000"; 1674a71df50SFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 1684a71df50SFrank Blaschka return "OSD_10GIG"; 1694a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 1704a71df50SFrank Blaschka return "OSD_FE_LANE"; 1714a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_TR: 1724a71df50SFrank Blaschka return "OSD_TR_LANE"; 1734a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 1744a71df50SFrank Blaschka return "OSD_GbE_LANE"; 1754a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE: 1764a71df50SFrank Blaschka return "OSD_ATM_LANE"; 1774a71df50SFrank Blaschka default: 1784a71df50SFrank Blaschka return "OSD_Express"; 1794a71df50SFrank Blaschka } 1804a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1814a71df50SFrank Blaschka return "HiperSockets"; 1824a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1834a71df50SFrank Blaschka return "OSN"; 1845113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1855113fec0SUrsula Braun return "OSM_1000"; 1865113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1875113fec0SUrsula Braun return "OSX_10GIG"; 1884a71df50SFrank Blaschka default: 1894a71df50SFrank Blaschka return "unknown"; 1904a71df50SFrank Blaschka } 1914a71df50SFrank Blaschka } 1924a71df50SFrank Blaschka return "n/a"; 1934a71df50SFrank Blaschka } 1944a71df50SFrank Blaschka 19565d8013cSStefan Raspl void qeth_set_recovery_task(struct qeth_card *card) 19665d8013cSStefan Raspl { 19765d8013cSStefan Raspl card->recovery_task = current; 19865d8013cSStefan Raspl } 19965d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_set_recovery_task); 20065d8013cSStefan Raspl 20165d8013cSStefan Raspl void qeth_clear_recovery_task(struct qeth_card *card) 20265d8013cSStefan Raspl { 20365d8013cSStefan Raspl card->recovery_task = NULL; 20465d8013cSStefan Raspl } 20565d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_clear_recovery_task); 20665d8013cSStefan Raspl 20765d8013cSStefan Raspl static bool qeth_is_recovery_task(const struct qeth_card *card) 20865d8013cSStefan Raspl { 20965d8013cSStefan Raspl return card->recovery_task == current; 21065d8013cSStefan Raspl } 21165d8013cSStefan Raspl 2124a71df50SFrank Blaschka void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads, 2134a71df50SFrank Blaschka int clear_start_mask) 2144a71df50SFrank Blaschka { 2154a71df50SFrank Blaschka unsigned long flags; 2164a71df50SFrank Blaschka 2174a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2184a71df50SFrank Blaschka card->thread_allowed_mask = threads; 2194a71df50SFrank Blaschka if (clear_start_mask) 2204a71df50SFrank Blaschka card->thread_start_mask &= threads; 2214a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2224a71df50SFrank Blaschka wake_up(&card->wait_q); 2234a71df50SFrank Blaschka } 2244a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_set_allowed_threads); 2254a71df50SFrank Blaschka 2264a71df50SFrank Blaschka int qeth_threads_running(struct qeth_card *card, unsigned long threads) 2274a71df50SFrank Blaschka { 2284a71df50SFrank Blaschka unsigned long flags; 2294a71df50SFrank Blaschka int rc = 0; 2304a71df50SFrank Blaschka 2314a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2324a71df50SFrank Blaschka rc = (card->thread_running_mask & threads); 2334a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2344a71df50SFrank Blaschka return rc; 2354a71df50SFrank Blaschka } 2364a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_threads_running); 2374a71df50SFrank Blaschka 2384a71df50SFrank Blaschka int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) 2394a71df50SFrank Blaschka { 24065d8013cSStefan Raspl if (qeth_is_recovery_task(card)) 24165d8013cSStefan Raspl return 0; 2424a71df50SFrank Blaschka return wait_event_interruptible(card->wait_q, 2434a71df50SFrank Blaschka qeth_threads_running(card, threads) == 0); 2444a71df50SFrank Blaschka } 2454a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_threads); 2464a71df50SFrank Blaschka 2474a71df50SFrank Blaschka void qeth_clear_working_pool_list(struct qeth_card *card) 2484a71df50SFrank Blaschka { 2494a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 2504a71df50SFrank Blaschka 251847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "clwrklst"); 2524a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 2534a71df50SFrank Blaschka &card->qdio.in_buf_pool.entry_list, list){ 2544a71df50SFrank Blaschka list_del(&pool_entry->list); 2554a71df50SFrank Blaschka } 2564a71df50SFrank Blaschka } 2574a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_working_pool_list); 2584a71df50SFrank Blaschka 2594a71df50SFrank Blaschka static int qeth_alloc_buffer_pool(struct qeth_card *card) 2604a71df50SFrank Blaschka { 2614a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 2624a71df50SFrank Blaschka void *ptr; 2634a71df50SFrank Blaschka int i, j; 2644a71df50SFrank Blaschka 265847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "alocpool"); 2664a71df50SFrank Blaschka for (i = 0; i < card->qdio.init_pool.buf_count; ++i) { 267b3332930SFrank Blaschka pool_entry = kzalloc(sizeof(*pool_entry), GFP_KERNEL); 2684a71df50SFrank Blaschka if (!pool_entry) { 2694a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2704a71df50SFrank Blaschka return -ENOMEM; 2714a71df50SFrank Blaschka } 2724a71df50SFrank Blaschka for (j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j) { 273508b3c4fSUrsula Braun ptr = (void *) __get_free_page(GFP_KERNEL); 2744a71df50SFrank Blaschka if (!ptr) { 2754a71df50SFrank Blaschka while (j > 0) 2764a71df50SFrank Blaschka free_page((unsigned long) 2774a71df50SFrank Blaschka pool_entry->elements[--j]); 2784a71df50SFrank Blaschka kfree(pool_entry); 2794a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2804a71df50SFrank Blaschka return -ENOMEM; 2814a71df50SFrank Blaschka } 2824a71df50SFrank Blaschka pool_entry->elements[j] = ptr; 2834a71df50SFrank Blaschka } 2844a71df50SFrank Blaschka list_add(&pool_entry->init_list, 2854a71df50SFrank Blaschka &card->qdio.init_pool.entry_list); 2864a71df50SFrank Blaschka } 2874a71df50SFrank Blaschka return 0; 2884a71df50SFrank Blaschka } 2894a71df50SFrank Blaschka 2904a71df50SFrank Blaschka int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt) 2914a71df50SFrank Blaschka { 292847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "realcbp"); 2934a71df50SFrank Blaschka 2944a71df50SFrank Blaschka if ((card->state != CARD_STATE_DOWN) && 2954a71df50SFrank Blaschka (card->state != CARD_STATE_RECOVER)) 2964a71df50SFrank Blaschka return -EPERM; 2974a71df50SFrank Blaschka 2984a71df50SFrank Blaschka /* TODO: steel/add buffers from/to a running card's buffer pool (?) */ 2994a71df50SFrank Blaschka qeth_clear_working_pool_list(card); 3004a71df50SFrank Blaschka qeth_free_buffer_pool(card); 3014a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = bufcnt; 3024a71df50SFrank Blaschka card->qdio.init_pool.buf_count = bufcnt; 3034a71df50SFrank Blaschka return qeth_alloc_buffer_pool(card); 3044a71df50SFrank Blaschka } 30576b11f8eSUrsula Braun EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool); 3064a71df50SFrank Blaschka 3074601ba6cSSebastian Ott static void qeth_free_qdio_queue(struct qeth_qdio_q *q) 3084601ba6cSSebastian Ott { 3096d284bdeSSebastian Ott if (!q) 3106d284bdeSSebastian Ott return; 3116d284bdeSSebastian Ott 3126d284bdeSSebastian Ott qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); 3134601ba6cSSebastian Ott kfree(q); 3144601ba6cSSebastian Ott } 3154601ba6cSSebastian Ott 3164601ba6cSSebastian Ott static struct qeth_qdio_q *qeth_alloc_qdio_queue(void) 3174601ba6cSSebastian Ott { 3184601ba6cSSebastian Ott struct qeth_qdio_q *q = kzalloc(sizeof(*q), GFP_KERNEL); 3194601ba6cSSebastian Ott int i; 3204601ba6cSSebastian Ott 3214601ba6cSSebastian Ott if (!q) 3224601ba6cSSebastian Ott return NULL; 3234601ba6cSSebastian Ott 3246d284bdeSSebastian Ott if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) { 3256d284bdeSSebastian Ott kfree(q); 3266d284bdeSSebastian Ott return NULL; 3276d284bdeSSebastian Ott } 3286d284bdeSSebastian Ott 3294601ba6cSSebastian Ott for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) 3306d284bdeSSebastian Ott q->bufs[i].buffer = q->qdio_bufs[i]; 3314601ba6cSSebastian Ott 3324601ba6cSSebastian Ott QETH_DBF_HEX(SETUP, 2, &q, sizeof(void *)); 3334601ba6cSSebastian Ott return q; 3344601ba6cSSebastian Ott } 3354601ba6cSSebastian Ott 336cef6ff22SJulian Wiedmann static int qeth_cq_init(struct qeth_card *card) 3370da9581dSEinar Lueck { 3380da9581dSEinar Lueck int rc; 3390da9581dSEinar Lueck 3400da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 3410da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqinit"); 3426d284bdeSSebastian Ott qdio_reset_buffers(card->qdio.c_q->qdio_bufs, 3436d284bdeSSebastian Ott QDIO_MAX_BUFFERS_PER_Q); 3440da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = 127; 3450da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 3460da9581dSEinar Lueck card->qdio.no_in_queues - 1, 0, 3470da9581dSEinar Lueck 127); 3480da9581dSEinar Lueck if (rc) { 3490da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 3500da9581dSEinar Lueck goto out; 3510da9581dSEinar Lueck } 3520da9581dSEinar Lueck } 3530da9581dSEinar Lueck rc = 0; 3540da9581dSEinar Lueck out: 3550da9581dSEinar Lueck return rc; 3560da9581dSEinar Lueck } 3570da9581dSEinar Lueck 358cef6ff22SJulian Wiedmann static int qeth_alloc_cq(struct qeth_card *card) 3590da9581dSEinar Lueck { 3600da9581dSEinar Lueck int rc; 3610da9581dSEinar Lueck 3620da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 3630da9581dSEinar Lueck int i; 3640da9581dSEinar Lueck struct qdio_outbuf_state *outbuf_states; 3650da9581dSEinar Lueck 3660da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqon"); 3674601ba6cSSebastian Ott card->qdio.c_q = qeth_alloc_qdio_queue(); 3680da9581dSEinar Lueck if (!card->qdio.c_q) { 3690da9581dSEinar Lueck rc = -1; 3700da9581dSEinar Lueck goto kmsg_out; 3710da9581dSEinar Lueck } 3720da9581dSEinar Lueck card->qdio.no_in_queues = 2; 3734a912f98SZhang Yanfei card->qdio.out_bufstates = 3746396bb22SKees Cook kcalloc(card->qdio.no_out_queues * 3756396bb22SKees Cook QDIO_MAX_BUFFERS_PER_Q, 3766396bb22SKees Cook sizeof(struct qdio_outbuf_state), 3776396bb22SKees Cook GFP_KERNEL); 3780da9581dSEinar Lueck outbuf_states = card->qdio.out_bufstates; 3790da9581dSEinar Lueck if (outbuf_states == NULL) { 3800da9581dSEinar Lueck rc = -1; 3810da9581dSEinar Lueck goto free_cq_out; 3820da9581dSEinar Lueck } 3830da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 3840da9581dSEinar Lueck card->qdio.out_qs[i]->bufstates = outbuf_states; 3850da9581dSEinar Lueck outbuf_states += QDIO_MAX_BUFFERS_PER_Q; 3860da9581dSEinar Lueck } 3870da9581dSEinar Lueck } else { 3880da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "nocq"); 3890da9581dSEinar Lueck card->qdio.c_q = NULL; 3900da9581dSEinar Lueck card->qdio.no_in_queues = 1; 3910da9581dSEinar Lueck } 3920da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "iqc%d", card->qdio.no_in_queues); 3930da9581dSEinar Lueck rc = 0; 3940da9581dSEinar Lueck out: 3950da9581dSEinar Lueck return rc; 3960da9581dSEinar Lueck free_cq_out: 3974601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.c_q); 3980da9581dSEinar Lueck card->qdio.c_q = NULL; 3990da9581dSEinar Lueck kmsg_out: 4000da9581dSEinar Lueck dev_err(&card->gdev->dev, "Failed to create completion queue\n"); 4010da9581dSEinar Lueck goto out; 4020da9581dSEinar Lueck } 4030da9581dSEinar Lueck 404cef6ff22SJulian Wiedmann static void qeth_free_cq(struct qeth_card *card) 4050da9581dSEinar Lueck { 4060da9581dSEinar Lueck if (card->qdio.c_q) { 4070da9581dSEinar Lueck --card->qdio.no_in_queues; 4084601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.c_q); 4090da9581dSEinar Lueck card->qdio.c_q = NULL; 4100da9581dSEinar Lueck } 4110da9581dSEinar Lueck kfree(card->qdio.out_bufstates); 4120da9581dSEinar Lueck card->qdio.out_bufstates = NULL; 4130da9581dSEinar Lueck } 4140da9581dSEinar Lueck 415cef6ff22SJulian Wiedmann static enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, 416cef6ff22SJulian Wiedmann int delayed) 417cef6ff22SJulian Wiedmann { 418b3332930SFrank Blaschka enum iucv_tx_notify n; 419b3332930SFrank Blaschka 420b3332930SFrank Blaschka switch (sbalf15) { 421b3332930SFrank Blaschka case 0: 422b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK; 423b3332930SFrank Blaschka break; 424b3332930SFrank Blaschka case 4: 425b3332930SFrank Blaschka case 16: 426b3332930SFrank Blaschka case 17: 427b3332930SFrank Blaschka case 18: 428b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE : 429b3332930SFrank Blaschka TX_NOTIFY_UNREACHABLE; 430b3332930SFrank Blaschka break; 431b3332930SFrank Blaschka default: 432b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR : 433b3332930SFrank Blaschka TX_NOTIFY_GENERALERROR; 434b3332930SFrank Blaschka break; 435b3332930SFrank Blaschka } 436b3332930SFrank Blaschka 437b3332930SFrank Blaschka return n; 438b3332930SFrank Blaschka } 439b3332930SFrank Blaschka 440cef6ff22SJulian Wiedmann static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx, 441cef6ff22SJulian Wiedmann int forced_cleanup) 4420da9581dSEinar Lueck { 44372861ae7SEinar Lueck if (q->card->options.cq != QETH_CQ_ENABLED) 44472861ae7SEinar Lueck return; 44572861ae7SEinar Lueck 4460da9581dSEinar Lueck if (q->bufs[bidx]->next_pending != NULL) { 4470da9581dSEinar Lueck struct qeth_qdio_out_buffer *head = q->bufs[bidx]; 4480da9581dSEinar Lueck struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending; 4490da9581dSEinar Lueck 4500da9581dSEinar Lueck while (c) { 4510da9581dSEinar Lueck if (forced_cleanup || 4520da9581dSEinar Lueck atomic_read(&c->state) == 4530da9581dSEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED) { 4540da9581dSEinar Lueck struct qeth_qdio_out_buffer *f = c; 4550da9581dSEinar Lueck QETH_CARD_TEXT(f->q->card, 5, "fp"); 4560da9581dSEinar Lueck QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f); 457b3332930SFrank Blaschka /* release here to avoid interleaving between 458b3332930SFrank Blaschka outbound tasklet and inbound tasklet 459b3332930SFrank Blaschka regarding notifications and lifecycle */ 460b3332930SFrank Blaschka qeth_release_skbs(c); 461b3332930SFrank Blaschka 4620da9581dSEinar Lueck c = f->next_pending; 46318af5c17SStefan Raspl WARN_ON_ONCE(head->next_pending != f); 4640da9581dSEinar Lueck head->next_pending = c; 4650da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, f); 4660da9581dSEinar Lueck } else { 4670da9581dSEinar Lueck head = c; 4680da9581dSEinar Lueck c = c->next_pending; 4690da9581dSEinar Lueck } 4700da9581dSEinar Lueck 4710da9581dSEinar Lueck } 4720da9581dSEinar Lueck } 47372861ae7SEinar Lueck if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == 47472861ae7SEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED)) { 47572861ae7SEinar Lueck /* for recovery situations */ 47672861ae7SEinar Lueck qeth_init_qdio_out_buf(q, bidx); 47772861ae7SEinar Lueck QETH_CARD_TEXT(q->card, 2, "clprecov"); 47872861ae7SEinar Lueck } 4790da9581dSEinar Lueck } 4800da9581dSEinar Lueck 4810da9581dSEinar Lueck 482cef6ff22SJulian Wiedmann static void qeth_qdio_handle_aob(struct qeth_card *card, 483cef6ff22SJulian Wiedmann unsigned long phys_aob_addr) 484cef6ff22SJulian Wiedmann { 4850da9581dSEinar Lueck struct qaob *aob; 4860da9581dSEinar Lueck struct qeth_qdio_out_buffer *buffer; 487b3332930SFrank Blaschka enum iucv_tx_notify notification; 488ce28867fSJulian Wiedmann unsigned int i; 4890da9581dSEinar Lueck 4900da9581dSEinar Lueck aob = (struct qaob *) phys_to_virt(phys_aob_addr); 4910da9581dSEinar Lueck QETH_CARD_TEXT(card, 5, "haob"); 4920da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr); 4930da9581dSEinar Lueck buffer = (struct qeth_qdio_out_buffer *) aob->user1; 4940da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); 4950da9581dSEinar Lueck 496b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, 497b3332930SFrank Blaschka QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) { 498b3332930SFrank Blaschka notification = TX_NOTIFY_OK; 499b3332930SFrank Blaschka } else { 50018af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buffer->state) != 50118af5c17SStefan Raspl QETH_QDIO_BUF_PENDING); 502b3332930SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); 503b3332930SFrank Blaschka notification = TX_NOTIFY_DELAYED_OK; 504b3332930SFrank Blaschka } 505b3332930SFrank Blaschka 506b3332930SFrank Blaschka if (aob->aorc != 0) { 507b3332930SFrank Blaschka QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc); 508b3332930SFrank Blaschka notification = qeth_compute_cq_notification(aob->aorc, 1); 509b3332930SFrank Blaschka } 510b3332930SFrank Blaschka qeth_notify_skbs(buffer->q, buffer, notification); 511b3332930SFrank Blaschka 512ce28867fSJulian Wiedmann /* Free dangling allocations. The attached skbs are handled by 513ce28867fSJulian Wiedmann * qeth_cleanup_handled_pending(). 514ce28867fSJulian Wiedmann */ 515ce28867fSJulian Wiedmann for (i = 0; 516ce28867fSJulian Wiedmann i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card); 517ce28867fSJulian Wiedmann i++) { 518ce28867fSJulian Wiedmann if (aob->sba[i] && buffer->is_header[i]) 519ce28867fSJulian Wiedmann kmem_cache_free(qeth_core_header_cache, 520ce28867fSJulian Wiedmann (void *) aob->sba[i]); 521ce28867fSJulian Wiedmann } 522ce28867fSJulian Wiedmann atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED); 52372861ae7SEinar Lueck 5240da9581dSEinar Lueck qdio_release_aob(aob); 5250da9581dSEinar Lueck } 5260da9581dSEinar Lueck 5270da9581dSEinar Lueck static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue) 5280da9581dSEinar Lueck { 5290da9581dSEinar Lueck return card->options.cq == QETH_CQ_ENABLED && 5300da9581dSEinar Lueck card->qdio.c_q != NULL && 5310da9581dSEinar Lueck queue != 0 && 5320da9581dSEinar Lueck queue == card->qdio.no_in_queues - 1; 5330da9581dSEinar Lueck } 5340da9581dSEinar Lueck 53545ca2fd6SJulian Wiedmann static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u32 len, void *data) 53645ca2fd6SJulian Wiedmann { 53745ca2fd6SJulian Wiedmann ccw->cmd_code = cmd_code; 53845ca2fd6SJulian Wiedmann ccw->flags = CCW_FLAG_SLI; 53945ca2fd6SJulian Wiedmann ccw->count = len; 54045ca2fd6SJulian Wiedmann ccw->cda = (__u32) __pa(data); 54145ca2fd6SJulian Wiedmann } 54245ca2fd6SJulian Wiedmann 54317bf8c9bSJulian Wiedmann static int __qeth_issue_next_read(struct qeth_card *card) 5444a71df50SFrank Blaschka { 545750b1625SJulian Wiedmann struct qeth_channel *channel = &card->read; 5464a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 547750b1625SJulian Wiedmann int rc; 5484a71df50SFrank Blaschka 549847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "issnxrd"); 550750b1625SJulian Wiedmann if (channel->state != CH_STATE_UP) 5514a71df50SFrank Blaschka return -EIO; 552750b1625SJulian Wiedmann iob = qeth_get_buffer(channel); 5534a71df50SFrank Blaschka if (!iob) { 55474eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver " 55574eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 55674eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob " 55774eacdb9SFrank Blaschka "available\n", dev_name(&card->gdev->dev)); 5584a71df50SFrank Blaschka return -ENOMEM; 5594a71df50SFrank Blaschka } 560f15cdaf2SJulian Wiedmann qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data); 561847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 562f15cdaf2SJulian Wiedmann rc = ccw_device_start(channel->ccwdev, channel->ccw, 5634a71df50SFrank Blaschka (addr_t) iob, 0, 0); 5644a71df50SFrank Blaschka if (rc) { 56574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! " 56674eacdb9SFrank Blaschka "rc=%i\n", dev_name(&card->gdev->dev), rc); 567750b1625SJulian Wiedmann atomic_set(&channel->irq_pending, 0); 568908abbb5SUrsula Braun card->read_or_write_problem = 1; 5694a71df50SFrank Blaschka qeth_schedule_recovery(card); 5704a71df50SFrank Blaschka wake_up(&card->wait_q); 5714a71df50SFrank Blaschka } 5724a71df50SFrank Blaschka return rc; 5734a71df50SFrank Blaschka } 5744a71df50SFrank Blaschka 57517bf8c9bSJulian Wiedmann static int qeth_issue_next_read(struct qeth_card *card) 57617bf8c9bSJulian Wiedmann { 57717bf8c9bSJulian Wiedmann int ret; 57817bf8c9bSJulian Wiedmann 57917bf8c9bSJulian Wiedmann spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card))); 58017bf8c9bSJulian Wiedmann ret = __qeth_issue_next_read(card); 58117bf8c9bSJulian Wiedmann spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card))); 58217bf8c9bSJulian Wiedmann 58317bf8c9bSJulian Wiedmann return ret; 58417bf8c9bSJulian Wiedmann } 58517bf8c9bSJulian Wiedmann 5864a71df50SFrank Blaschka static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card) 5874a71df50SFrank Blaschka { 5884a71df50SFrank Blaschka struct qeth_reply *reply; 5894a71df50SFrank Blaschka 5904a71df50SFrank Blaschka reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC); 5914a71df50SFrank Blaschka if (reply) { 592ae695927SElena Reshetova refcount_set(&reply->refcnt, 1); 5934a71df50SFrank Blaschka atomic_set(&reply->received, 0); 5944a71df50SFrank Blaschka reply->card = card; 5956531084cSPeter Senna Tschudin } 5964a71df50SFrank Blaschka return reply; 5974a71df50SFrank Blaschka } 5984a71df50SFrank Blaschka 5994a71df50SFrank Blaschka static void qeth_get_reply(struct qeth_reply *reply) 6004a71df50SFrank Blaschka { 601ae695927SElena Reshetova refcount_inc(&reply->refcnt); 6024a71df50SFrank Blaschka } 6034a71df50SFrank Blaschka 6044a71df50SFrank Blaschka static void qeth_put_reply(struct qeth_reply *reply) 6054a71df50SFrank Blaschka { 606ae695927SElena Reshetova if (refcount_dec_and_test(&reply->refcnt)) 6074a71df50SFrank Blaschka kfree(reply); 6084a71df50SFrank Blaschka } 6094a71df50SFrank Blaschka 610d11ba0c4SPeter Tiedemann static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, 6114a71df50SFrank Blaschka struct qeth_card *card) 6124a71df50SFrank Blaschka { 613048a7f8bSJean Delvare const char *ipa_name; 614d11ba0c4SPeter Tiedemann int com = cmd->hdr.command; 6154a71df50SFrank Blaschka ipa_name = qeth_get_ipa_cmd_name(com); 616d11ba0c4SPeter Tiedemann if (rc) 61770919e23SUrsula Braun QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s/%s returned " 61870919e23SUrsula Braun "x%X \"%s\"\n", 61970919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 62070919e23SUrsula Braun QETH_CARD_IFNAME(card), rc, 62170919e23SUrsula Braun qeth_get_ipa_msg(rc)); 622d11ba0c4SPeter Tiedemann else 62370919e23SUrsula Braun QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s/%s succeeded\n", 62470919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 62570919e23SUrsula Braun QETH_CARD_IFNAME(card)); 6264a71df50SFrank Blaschka } 6274a71df50SFrank Blaschka 6284a71df50SFrank Blaschka static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, 6294a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 6304a71df50SFrank Blaschka { 6314a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd = NULL; 6324a71df50SFrank Blaschka 633847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "chkipad"); 6344a71df50SFrank Blaschka if (IS_IPA(iob->data)) { 6354a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data); 6364a71df50SFrank Blaschka if (IS_IPA_REPLY(cmd)) { 63776b11f8eSUrsula Braun if (cmd->hdr.command != IPA_CMD_SETCCID && 63876b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_DELCCID && 63976b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_MODCCID && 64076b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_SET_DIAG_ASS) 641d11ba0c4SPeter Tiedemann qeth_issue_ipa_msg(cmd, 642d11ba0c4SPeter Tiedemann cmd->hdr.return_code, card); 6434a71df50SFrank Blaschka return cmd; 6444a71df50SFrank Blaschka } else { 6454a71df50SFrank Blaschka switch (cmd->hdr.command) { 6464a71df50SFrank Blaschka case IPA_CMD_STOPLAN: 6470f54761dSStefan Raspl if (cmd->hdr.return_code == 6480f54761dSStefan Raspl IPA_RC_VEPA_TO_VEB_TRANSITION) { 6490f54761dSStefan Raspl dev_err(&card->gdev->dev, 6500f54761dSStefan Raspl "Interface %s is down because the " 6510f54761dSStefan Raspl "adjacent port is no longer in " 6520f54761dSStefan Raspl "reflective relay mode\n", 6530f54761dSStefan Raspl QETH_CARD_IFNAME(card)); 6540f54761dSStefan Raspl qeth_close_dev(card); 6550f54761dSStefan Raspl } else { 65674eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, 65774eacdb9SFrank Blaschka "The link for interface %s on CHPID" 65874eacdb9SFrank Blaschka " 0x%X failed\n", 6594a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 6604a71df50SFrank Blaschka card->info.chpid); 6610f54761dSStefan Raspl qeth_issue_ipa_msg(cmd, 6620f54761dSStefan Raspl cmd->hdr.return_code, card); 6630f54761dSStefan Raspl } 6644a71df50SFrank Blaschka card->lan_online = 0; 6654a71df50SFrank Blaschka netif_carrier_off(card->dev); 6664a71df50SFrank Blaschka return NULL; 6674a71df50SFrank Blaschka case IPA_CMD_STARTLAN: 66874eacdb9SFrank Blaschka dev_info(&card->gdev->dev, 66974eacdb9SFrank Blaschka "The link for %s on CHPID 0x%X has" 67074eacdb9SFrank Blaschka " been restored\n", 6714a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 6724a71df50SFrank Blaschka card->info.chpid); 6734a71df50SFrank Blaschka netif_carrier_on(card->dev); 674922dc062SUrsula Braun card->lan_online = 1; 6751da74b1cSFrank Blaschka if (card->info.hwtrap) 6761da74b1cSFrank Blaschka card->info.hwtrap = 2; 6774a71df50SFrank Blaschka qeth_schedule_recovery(card); 6784a71df50SFrank Blaschka return NULL; 6799c23f4daSEugene Crosser case IPA_CMD_SETBRIDGEPORT_IQD: 6809c23f4daSEugene Crosser case IPA_CMD_SETBRIDGEPORT_OSA: 6819f48b9dbSEugene Crosser case IPA_CMD_ADDRESS_CHANGE_NOTIF: 682c044dc21SEugene Crosser if (card->discipline->control_event_handler 683c044dc21SEugene Crosser (card, cmd)) 684c044dc21SEugene Crosser return cmd; 685c044dc21SEugene Crosser else 6869f48b9dbSEugene Crosser return NULL; 6874a71df50SFrank Blaschka case IPA_CMD_MODCCID: 6884a71df50SFrank Blaschka return cmd; 6894a71df50SFrank Blaschka case IPA_CMD_REGISTER_LOCAL_ADDR: 690847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "irla"); 6914a71df50SFrank Blaschka break; 6924a71df50SFrank Blaschka case IPA_CMD_UNREGISTER_LOCAL_ADDR: 693847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "urla"); 6944a71df50SFrank Blaschka break; 6954a71df50SFrank Blaschka default: 696c4cef07cSFrank Blaschka QETH_DBF_MESSAGE(2, "Received data is IPA " 6974a71df50SFrank Blaschka "but not a reply!\n"); 6984a71df50SFrank Blaschka break; 6994a71df50SFrank Blaschka } 7004a71df50SFrank Blaschka } 7014a71df50SFrank Blaschka } 7024a71df50SFrank Blaschka return cmd; 7034a71df50SFrank Blaschka } 7044a71df50SFrank Blaschka 7054a71df50SFrank Blaschka void qeth_clear_ipacmd_list(struct qeth_card *card) 7064a71df50SFrank Blaschka { 7074a71df50SFrank Blaschka struct qeth_reply *reply, *r; 7084a71df50SFrank Blaschka unsigned long flags; 7094a71df50SFrank Blaschka 710847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "clipalst"); 7114a71df50SFrank Blaschka 7124a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 7134a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 7144a71df50SFrank Blaschka qeth_get_reply(reply); 7154a71df50SFrank Blaschka reply->rc = -EIO; 7164a71df50SFrank Blaschka atomic_inc(&reply->received); 7174a71df50SFrank Blaschka list_del_init(&reply->list); 7184a71df50SFrank Blaschka wake_up(&reply->wait_q); 7194a71df50SFrank Blaschka qeth_put_reply(reply); 7204a71df50SFrank Blaschka } 7214a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 7224a71df50SFrank Blaschka } 7234a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list); 7244a71df50SFrank Blaschka 7255113fec0SUrsula Braun static int qeth_check_idx_response(struct qeth_card *card, 7265113fec0SUrsula Braun unsigned char *buffer) 7274a71df50SFrank Blaschka { 7284a71df50SFrank Blaschka if (!buffer) 7294a71df50SFrank Blaschka return 0; 7304a71df50SFrank Blaschka 731d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN); 7324a71df50SFrank Blaschka if ((buffer[2] & 0xc0) == 0xc0) { 733d857e111SJulian Wiedmann QETH_DBF_MESSAGE(2, "received an IDX TERMINATE with cause code %#02x\n", 734d857e111SJulian Wiedmann buffer[4]); 735847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckidxres"); 736847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " idxterm"); 737847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 7385113fec0SUrsula Braun if (buffer[4] == 0xf6) { 7395113fec0SUrsula Braun dev_err(&card->gdev->dev, 7405113fec0SUrsula Braun "The qeth device is not configured " 7415113fec0SUrsula Braun "for the OSI layer required by z/VM\n"); 7425113fec0SUrsula Braun return -EPERM; 7435113fec0SUrsula Braun } 7444a71df50SFrank Blaschka return -EIO; 7454a71df50SFrank Blaschka } 7464a71df50SFrank Blaschka return 0; 7474a71df50SFrank Blaschka } 7484a71df50SFrank Blaschka 749bca51650SThomas Richter static struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev) 750bca51650SThomas Richter { 751bca51650SThomas Richter struct qeth_card *card = dev_get_drvdata(&((struct ccwgroup_device *) 752bca51650SThomas Richter dev_get_drvdata(&cdev->dev))->dev); 753bca51650SThomas Richter return card; 754bca51650SThomas Richter } 755bca51650SThomas Richter 7564a71df50SFrank Blaschka static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel) 7574a71df50SFrank Blaschka { 7584a71df50SFrank Blaschka __u8 index; 7594a71df50SFrank Blaschka 760847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "getbuff"); 7614a71df50SFrank Blaschka index = channel->io_buf_no; 7624a71df50SFrank Blaschka do { 7634a71df50SFrank Blaschka if (channel->iob[index].state == BUF_STATE_FREE) { 7644a71df50SFrank Blaschka channel->iob[index].state = BUF_STATE_LOCKED; 7654a71df50SFrank Blaschka channel->io_buf_no = (channel->io_buf_no + 1) % 7664a71df50SFrank Blaschka QETH_CMD_BUFFER_NO; 7674a71df50SFrank Blaschka memset(channel->iob[index].data, 0, QETH_BUFSIZE); 7684a71df50SFrank Blaschka return channel->iob + index; 7694a71df50SFrank Blaschka } 7704a71df50SFrank Blaschka index = (index + 1) % QETH_CMD_BUFFER_NO; 7714a71df50SFrank Blaschka } while (index != channel->io_buf_no); 7724a71df50SFrank Blaschka 7734a71df50SFrank Blaschka return NULL; 7744a71df50SFrank Blaschka } 7754a71df50SFrank Blaschka 7764a71df50SFrank Blaschka void qeth_release_buffer(struct qeth_channel *channel, 7774a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 7784a71df50SFrank Blaschka { 7794a71df50SFrank Blaschka unsigned long flags; 7804a71df50SFrank Blaschka 781847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "relbuff"); 7824a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7834a71df50SFrank Blaschka memset(iob->data, 0, QETH_BUFSIZE); 7844a71df50SFrank Blaschka iob->state = BUF_STATE_FREE; 7854a71df50SFrank Blaschka iob->callback = qeth_send_control_data_cb; 7864a71df50SFrank Blaschka iob->rc = 0; 7874a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 788039055b9SUrsula Braun wake_up(&channel->wait_q); 7894a71df50SFrank Blaschka } 7904a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_release_buffer); 7914a71df50SFrank Blaschka 7924a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *channel) 7934a71df50SFrank Blaschka { 7944a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer = NULL; 7954a71df50SFrank Blaschka unsigned long flags; 7964a71df50SFrank Blaschka 7974a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7984a71df50SFrank Blaschka buffer = __qeth_get_buffer(channel); 7994a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 8004a71df50SFrank Blaschka return buffer; 8014a71df50SFrank Blaschka } 8024a71df50SFrank Blaschka 8034a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *channel) 8044a71df50SFrank Blaschka { 8054a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer; 8064a71df50SFrank Blaschka wait_event(channel->wait_q, 8074a71df50SFrank Blaschka ((buffer = qeth_get_buffer(channel)) != NULL)); 8084a71df50SFrank Blaschka return buffer; 8094a71df50SFrank Blaschka } 8104a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_buffer); 8114a71df50SFrank Blaschka 8124a71df50SFrank Blaschka void qeth_clear_cmd_buffers(struct qeth_channel *channel) 8134a71df50SFrank Blaschka { 8144a71df50SFrank Blaschka int cnt; 8154a71df50SFrank Blaschka 8164a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 8174a71df50SFrank Blaschka qeth_release_buffer(channel, &channel->iob[cnt]); 8184a71df50SFrank Blaschka channel->io_buf_no = 0; 8194a71df50SFrank Blaschka } 8204a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers); 8214a71df50SFrank Blaschka 8224a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *channel, 8234a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 8244a71df50SFrank Blaschka { 8254a71df50SFrank Blaschka struct qeth_card *card; 8264a71df50SFrank Blaschka struct qeth_reply *reply, *r; 8274a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 8284a71df50SFrank Blaschka unsigned long flags; 8294a71df50SFrank Blaschka int keep_reply; 8305113fec0SUrsula Braun int rc = 0; 8314a71df50SFrank Blaschka 8324a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 833847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sndctlcb"); 8345113fec0SUrsula Braun rc = qeth_check_idx_response(card, iob->data); 8355113fec0SUrsula Braun switch (rc) { 8365113fec0SUrsula Braun case 0: 8375113fec0SUrsula Braun break; 8385113fec0SUrsula Braun case -EIO: 8394a71df50SFrank Blaschka qeth_clear_ipacmd_list(card); 8404a71df50SFrank Blaschka qeth_schedule_recovery(card); 84101fc3e86SUrsula Braun /* fall through */ 8425113fec0SUrsula Braun default: 8434a71df50SFrank Blaschka goto out; 8444a71df50SFrank Blaschka } 8454a71df50SFrank Blaschka 8464a71df50SFrank Blaschka cmd = qeth_check_ipa_data(card, iob); 8474a71df50SFrank Blaschka if ((cmd == NULL) && (card->state != CARD_STATE_DOWN)) 8484a71df50SFrank Blaschka goto out; 8494a71df50SFrank Blaschka /*in case of OSN : check if cmd is set */ 8504a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN && 8514a71df50SFrank Blaschka cmd && 8524a71df50SFrank Blaschka cmd->hdr.command != IPA_CMD_STARTLAN && 8534a71df50SFrank Blaschka card->osn_info.assist_cb != NULL) { 8544a71df50SFrank Blaschka card->osn_info.assist_cb(card->dev, cmd); 8554a71df50SFrank Blaschka goto out; 8564a71df50SFrank Blaschka } 8574a71df50SFrank Blaschka 8584a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8594a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 8604a71df50SFrank Blaschka if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) || 8614a71df50SFrank Blaschka ((cmd) && (reply->seqno == cmd->hdr.seqno))) { 8624a71df50SFrank Blaschka qeth_get_reply(reply); 8634a71df50SFrank Blaschka list_del_init(&reply->list); 8644a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8654a71df50SFrank Blaschka keep_reply = 0; 8664a71df50SFrank Blaschka if (reply->callback != NULL) { 8674a71df50SFrank Blaschka if (cmd) { 8684a71df50SFrank Blaschka reply->offset = (__u16)((char *)cmd - 8694a71df50SFrank Blaschka (char *)iob->data); 8704a71df50SFrank Blaschka keep_reply = reply->callback(card, 8714a71df50SFrank Blaschka reply, 8724a71df50SFrank Blaschka (unsigned long)cmd); 8734a71df50SFrank Blaschka } else 8744a71df50SFrank Blaschka keep_reply = reply->callback(card, 8754a71df50SFrank Blaschka reply, 8764a71df50SFrank Blaschka (unsigned long)iob); 8774a71df50SFrank Blaschka } 8784a71df50SFrank Blaschka if (cmd) 8794a71df50SFrank Blaschka reply->rc = (u16) cmd->hdr.return_code; 8804a71df50SFrank Blaschka else if (iob->rc) 8814a71df50SFrank Blaschka reply->rc = iob->rc; 8824a71df50SFrank Blaschka if (keep_reply) { 8834a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8844a71df50SFrank Blaschka list_add_tail(&reply->list, 8854a71df50SFrank Blaschka &card->cmd_waiter_list); 8864a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8874a71df50SFrank Blaschka } else { 8884a71df50SFrank Blaschka atomic_inc(&reply->received); 8894a71df50SFrank Blaschka wake_up(&reply->wait_q); 8904a71df50SFrank Blaschka } 8914a71df50SFrank Blaschka qeth_put_reply(reply); 8924a71df50SFrank Blaschka goto out; 8934a71df50SFrank Blaschka } 8944a71df50SFrank Blaschka } 8954a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8964a71df50SFrank Blaschka out: 8974a71df50SFrank Blaschka memcpy(&card->seqno.pdu_hdr_ack, 8984a71df50SFrank Blaschka QETH_PDU_HEADER_SEQ_NO(iob->data), 8994a71df50SFrank Blaschka QETH_SEQ_NO_LENGTH); 9004a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 9014a71df50SFrank Blaschka } 9024a71df50SFrank Blaschka 90324142fd8SJulian Wiedmann static int qeth_setup_channel(struct qeth_channel *channel, bool alloc_buffers) 9044a71df50SFrank Blaschka { 9054a71df50SFrank Blaschka int cnt; 9064a71df50SFrank Blaschka 907d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupch"); 90824142fd8SJulian Wiedmann 909f15cdaf2SJulian Wiedmann channel->ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); 910f15cdaf2SJulian Wiedmann if (!channel->ccw) 911f15cdaf2SJulian Wiedmann return -ENOMEM; 91224142fd8SJulian Wiedmann channel->state = CH_STATE_DOWN; 91324142fd8SJulian Wiedmann atomic_set(&channel->irq_pending, 0); 91424142fd8SJulian Wiedmann init_waitqueue_head(&channel->wait_q); 91524142fd8SJulian Wiedmann 91624142fd8SJulian Wiedmann if (!alloc_buffers) 91724142fd8SJulian Wiedmann return 0; 91824142fd8SJulian Wiedmann 9194a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) { 920ae57b20aSJulia Lawall channel->iob[cnt].data = 921b3332930SFrank Blaschka kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); 9224a71df50SFrank Blaschka if (channel->iob[cnt].data == NULL) 9234a71df50SFrank Blaschka break; 9244a71df50SFrank Blaschka channel->iob[cnt].state = BUF_STATE_FREE; 9254a71df50SFrank Blaschka channel->iob[cnt].channel = channel; 9264a71df50SFrank Blaschka channel->iob[cnt].callback = qeth_send_control_data_cb; 9274a71df50SFrank Blaschka channel->iob[cnt].rc = 0; 9284a71df50SFrank Blaschka } 9294a71df50SFrank Blaschka if (cnt < QETH_CMD_BUFFER_NO) { 930f15cdaf2SJulian Wiedmann kfree(channel->ccw); 9314a71df50SFrank Blaschka while (cnt-- > 0) 9324a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 9334a71df50SFrank Blaschka return -ENOMEM; 9344a71df50SFrank Blaschka } 9354a71df50SFrank Blaschka channel->io_buf_no = 0; 9364a71df50SFrank Blaschka spin_lock_init(&channel->iob_lock); 9374a71df50SFrank Blaschka 9384a71df50SFrank Blaschka return 0; 9394a71df50SFrank Blaschka } 9404a71df50SFrank Blaschka 9414a71df50SFrank Blaschka static int qeth_set_thread_start_bit(struct qeth_card *card, 9424a71df50SFrank Blaschka unsigned long thread) 9434a71df50SFrank Blaschka { 9444a71df50SFrank Blaschka unsigned long flags; 9454a71df50SFrank Blaschka 9464a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9474a71df50SFrank Blaschka if (!(card->thread_allowed_mask & thread) || 9484a71df50SFrank Blaschka (card->thread_start_mask & thread)) { 9494a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9504a71df50SFrank Blaschka return -EPERM; 9514a71df50SFrank Blaschka } 9524a71df50SFrank Blaschka card->thread_start_mask |= thread; 9534a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9544a71df50SFrank Blaschka return 0; 9554a71df50SFrank Blaschka } 9564a71df50SFrank Blaschka 9574a71df50SFrank Blaschka void qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread) 9584a71df50SFrank Blaschka { 9594a71df50SFrank Blaschka unsigned long flags; 9604a71df50SFrank Blaschka 9614a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9624a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 9634a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9644a71df50SFrank Blaschka wake_up(&card->wait_q); 9654a71df50SFrank Blaschka } 9664a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_start_bit); 9674a71df50SFrank Blaschka 9684a71df50SFrank Blaschka void qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread) 9694a71df50SFrank Blaschka { 9704a71df50SFrank Blaschka unsigned long flags; 9714a71df50SFrank Blaschka 9724a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9734a71df50SFrank Blaschka card->thread_running_mask &= ~thread; 9744a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9751063e432SJulian Wiedmann wake_up_all(&card->wait_q); 9764a71df50SFrank Blaschka } 9774a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit); 9784a71df50SFrank Blaschka 9794a71df50SFrank Blaschka static int __qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9804a71df50SFrank Blaschka { 9814a71df50SFrank Blaschka unsigned long flags; 9824a71df50SFrank Blaschka int rc = 0; 9834a71df50SFrank Blaschka 9844a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9854a71df50SFrank Blaschka if (card->thread_start_mask & thread) { 9864a71df50SFrank Blaschka if ((card->thread_allowed_mask & thread) && 9874a71df50SFrank Blaschka !(card->thread_running_mask & thread)) { 9884a71df50SFrank Blaschka rc = 1; 9894a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 9904a71df50SFrank Blaschka card->thread_running_mask |= thread; 9914a71df50SFrank Blaschka } else 9924a71df50SFrank Blaschka rc = -EPERM; 9934a71df50SFrank Blaschka } 9944a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9954a71df50SFrank Blaschka return rc; 9964a71df50SFrank Blaschka } 9974a71df50SFrank Blaschka 9984a71df50SFrank Blaschka int qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9994a71df50SFrank Blaschka { 10004a71df50SFrank Blaschka int rc = 0; 10014a71df50SFrank Blaschka 10024a71df50SFrank Blaschka wait_event(card->wait_q, 10034a71df50SFrank Blaschka (rc = __qeth_do_run_thread(card, thread)) >= 0); 10044a71df50SFrank Blaschka return rc; 10054a71df50SFrank Blaschka } 10064a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_run_thread); 10074a71df50SFrank Blaschka 10084a71df50SFrank Blaschka void qeth_schedule_recovery(struct qeth_card *card) 10094a71df50SFrank Blaschka { 1010847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "startrec"); 10114a71df50SFrank Blaschka if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0) 10124a71df50SFrank Blaschka schedule_work(&card->kernel_thread_starter); 10134a71df50SFrank Blaschka } 10144a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_schedule_recovery); 10154a71df50SFrank Blaschka 10164a71df50SFrank Blaschka static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb) 10174a71df50SFrank Blaschka { 10184a71df50SFrank Blaschka int dstat, cstat; 10194a71df50SFrank Blaschka char *sense; 1020847a50fdSCarsten Otte struct qeth_card *card; 10214a71df50SFrank Blaschka 10224a71df50SFrank Blaschka sense = (char *) irb->ecw; 102323d805b6SPeter Oberparleiter cstat = irb->scsw.cmd.cstat; 102423d805b6SPeter Oberparleiter dstat = irb->scsw.cmd.dstat; 1025847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 10264a71df50SFrank Blaschka 10274a71df50SFrank Blaschka if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | 10284a71df50SFrank Blaschka SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | 10294a71df50SFrank Blaschka SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) { 1030847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CGENCHK"); 103174eacdb9SFrank Blaschka dev_warn(&cdev->dev, "The qeth device driver " 103274eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 10335113fec0SUrsula Braun QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x\n", 10342a0217d5SKay Sievers dev_name(&cdev->dev), dstat, cstat); 10354a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 10364a71df50SFrank Blaschka 16, 1, irb, 64, 1); 10374a71df50SFrank Blaschka return 1; 10384a71df50SFrank Blaschka } 10394a71df50SFrank Blaschka 10404a71df50SFrank Blaschka if (dstat & DEV_STAT_UNIT_CHECK) { 10414a71df50SFrank Blaschka if (sense[SENSE_RESETTING_EVENT_BYTE] & 10424a71df50SFrank Blaschka SENSE_RESETTING_EVENT_FLAG) { 1043847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "REVIND"); 10444a71df50SFrank Blaschka return 1; 10454a71df50SFrank Blaschka } 10464a71df50SFrank Blaschka if (sense[SENSE_COMMAND_REJECT_BYTE] & 10474a71df50SFrank Blaschka SENSE_COMMAND_REJECT_FLAG) { 1048847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CMDREJi"); 104928a7e4c9SUrsula Braun return 1; 10504a71df50SFrank Blaschka } 10514a71df50SFrank Blaschka if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { 1052847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "AFFE"); 10534a71df50SFrank Blaschka return 1; 10544a71df50SFrank Blaschka } 10554a71df50SFrank Blaschka if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { 1056847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ZEROSEN"); 10574a71df50SFrank Blaschka return 0; 10584a71df50SFrank Blaschka } 1059847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "DGENCHK"); 10604a71df50SFrank Blaschka return 1; 10614a71df50SFrank Blaschka } 10624a71df50SFrank Blaschka return 0; 10634a71df50SFrank Blaschka } 10644a71df50SFrank Blaschka 10654a71df50SFrank Blaschka static long __qeth_check_irb_error(struct ccw_device *cdev, 10664a71df50SFrank Blaschka unsigned long intparm, struct irb *irb) 10674a71df50SFrank Blaschka { 1068847a50fdSCarsten Otte struct qeth_card *card; 1069847a50fdSCarsten Otte 1070847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 1071847a50fdSCarsten Otte 1072e95051ffSRickard Strandqvist if (!card || !IS_ERR(irb)) 10734a71df50SFrank Blaschka return 0; 10744a71df50SFrank Blaschka 10754a71df50SFrank Blaschka switch (PTR_ERR(irb)) { 10764a71df50SFrank Blaschka case -EIO: 107774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s i/o-error on device\n", 107874eacdb9SFrank Blaschka dev_name(&cdev->dev)); 1079847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1080847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 10814a71df50SFrank Blaschka break; 10824a71df50SFrank Blaschka case -ETIMEDOUT: 108374eacdb9SFrank Blaschka dev_warn(&cdev->dev, "A hardware operation timed out" 108474eacdb9SFrank Blaschka " on the device\n"); 1085847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1086847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT); 10874a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 1088e95051ffSRickard Strandqvist if (card->data.ccwdev == cdev) { 10894a71df50SFrank Blaschka card->data.state = CH_STATE_DOWN; 10904a71df50SFrank Blaschka wake_up(&card->wait_q); 10914a71df50SFrank Blaschka } 10924a71df50SFrank Blaschka } 10934a71df50SFrank Blaschka break; 10944a71df50SFrank Blaschka default: 109574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n", 109674eacdb9SFrank Blaschka dev_name(&cdev->dev), PTR_ERR(irb)); 1097847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1098847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " rc???"); 10994a71df50SFrank Blaschka } 11004a71df50SFrank Blaschka return PTR_ERR(irb); 11014a71df50SFrank Blaschka } 11024a71df50SFrank Blaschka 11034a71df50SFrank Blaschka static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, 11044a71df50SFrank Blaschka struct irb *irb) 11054a71df50SFrank Blaschka { 11064a71df50SFrank Blaschka int rc; 11074a71df50SFrank Blaschka int cstat, dstat; 1108db71bbbdSJulian Wiedmann struct qeth_cmd_buffer *iob = NULL; 11094a71df50SFrank Blaschka struct qeth_channel *channel; 11104a71df50SFrank Blaschka struct qeth_card *card; 11114a71df50SFrank Blaschka 11124a71df50SFrank Blaschka card = CARD_FROM_CDEV(cdev); 11134a71df50SFrank Blaschka if (!card) 11144a71df50SFrank Blaschka return; 11154a71df50SFrank Blaschka 1116847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "irq"); 1117847a50fdSCarsten Otte 11184a71df50SFrank Blaschka if (card->read.ccwdev == cdev) { 11194a71df50SFrank Blaschka channel = &card->read; 1120847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "read"); 11214a71df50SFrank Blaschka } else if (card->write.ccwdev == cdev) { 11224a71df50SFrank Blaschka channel = &card->write; 1123847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "write"); 11244a71df50SFrank Blaschka } else { 11254a71df50SFrank Blaschka channel = &card->data; 1126847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "data"); 11274a71df50SFrank Blaschka } 1128db71bbbdSJulian Wiedmann 1129db71bbbdSJulian Wiedmann if (qeth_intparm_is_iob(intparm)) 1130db71bbbdSJulian Wiedmann iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm); 1131db71bbbdSJulian Wiedmann 1132db71bbbdSJulian Wiedmann if (__qeth_check_irb_error(cdev, intparm, irb)) { 1133db71bbbdSJulian Wiedmann /* IO was terminated, free its resources. */ 1134db71bbbdSJulian Wiedmann if (iob) 1135db71bbbdSJulian Wiedmann qeth_release_buffer(iob->channel, iob); 1136db71bbbdSJulian Wiedmann atomic_set(&channel->irq_pending, 0); 1137db71bbbdSJulian Wiedmann wake_up(&card->wait_q); 1138db71bbbdSJulian Wiedmann return; 1139db71bbbdSJulian Wiedmann } 1140db71bbbdSJulian Wiedmann 11414a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 11424a71df50SFrank Blaschka 114323d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) 11444a71df50SFrank Blaschka channel->state = CH_STATE_STOPPED; 11454a71df50SFrank Blaschka 114623d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) 11474a71df50SFrank Blaschka channel->state = CH_STATE_HALTED; 11484a71df50SFrank Blaschka 11494a71df50SFrank Blaschka /*let's wake up immediately on data channel*/ 11504a71df50SFrank Blaschka if ((channel == &card->data) && (intparm != 0) && 11514a71df50SFrank Blaschka (intparm != QETH_RCD_PARM)) 11524a71df50SFrank Blaschka goto out; 11534a71df50SFrank Blaschka 11544a71df50SFrank Blaschka if (intparm == QETH_CLEAR_CHANNEL_PARM) { 1155847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "clrchpar"); 11564a71df50SFrank Blaschka /* we don't have to handle this further */ 11574a71df50SFrank Blaschka intparm = 0; 11584a71df50SFrank Blaschka } 11594a71df50SFrank Blaschka if (intparm == QETH_HALT_CHANNEL_PARM) { 1160847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hltchpar"); 11614a71df50SFrank Blaschka /* we don't have to handle this further */ 11624a71df50SFrank Blaschka intparm = 0; 11634a71df50SFrank Blaschka } 1164db71bbbdSJulian Wiedmann 1165db71bbbdSJulian Wiedmann cstat = irb->scsw.cmd.cstat; 1166db71bbbdSJulian Wiedmann dstat = irb->scsw.cmd.dstat; 1167db71bbbdSJulian Wiedmann 11684a71df50SFrank Blaschka if ((dstat & DEV_STAT_UNIT_EXCEP) || 11694a71df50SFrank Blaschka (dstat & DEV_STAT_UNIT_CHECK) || 11704a71df50SFrank Blaschka (cstat)) { 11714a71df50SFrank Blaschka if (irb->esw.esw0.erw.cons) { 117274eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, 117374eacdb9SFrank Blaschka "The qeth device driver failed to recover " 117474eacdb9SFrank Blaschka "an error on the device\n"); 117574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s sense data available. cstat " 117674eacdb9SFrank Blaschka "0x%X dstat 0x%X\n", 117774eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev), cstat, dstat); 11784a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", 11794a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1); 11804a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: sense data ", 11814a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb->ecw, 32, 1); 11824a71df50SFrank Blaschka } 11834a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11844a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 11854a71df50SFrank Blaschka goto out; 11864a71df50SFrank Blaschka } 11874a71df50SFrank Blaschka rc = qeth_get_problem(cdev, irb); 11884a71df50SFrank Blaschka if (rc) { 1189a6c3d939SJulian Wiedmann card->read_or_write_problem = 1; 119028a7e4c9SUrsula Braun qeth_clear_ipacmd_list(card); 11914a71df50SFrank Blaschka qeth_schedule_recovery(card); 11924a71df50SFrank Blaschka goto out; 11934a71df50SFrank Blaschka } 11944a71df50SFrank Blaschka } 11954a71df50SFrank Blaschka 11964a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11974a71df50SFrank Blaschka channel->state = CH_STATE_RCD_DONE; 11984a71df50SFrank Blaschka goto out; 11994a71df50SFrank Blaschka } 12004a71df50SFrank Blaschka if (channel == &card->data) 12014a71df50SFrank Blaschka return; 12024a71df50SFrank Blaschka if (channel == &card->read && 12034a71df50SFrank Blaschka channel->state == CH_STATE_UP) 120417bf8c9bSJulian Wiedmann __qeth_issue_next_read(card); 12054a71df50SFrank Blaschka 1206db71bbbdSJulian Wiedmann if (iob && iob->callback) 1207901e3f49SJulian Wiedmann iob->callback(iob->channel, iob); 12084a71df50SFrank Blaschka 12094a71df50SFrank Blaschka out: 12104a71df50SFrank Blaschka wake_up(&card->wait_q); 12114a71df50SFrank Blaschka return; 12124a71df50SFrank Blaschka } 12134a71df50SFrank Blaschka 1214b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *q, 1215b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 1216b3332930SFrank Blaschka enum iucv_tx_notify notification) 1217b3332930SFrank Blaschka { 1218b3332930SFrank Blaschka struct sk_buff *skb; 1219b3332930SFrank Blaschka 1220b3332930SFrank Blaschka if (skb_queue_empty(&buf->skb_list)) 1221b3332930SFrank Blaschka goto out; 1222b3332930SFrank Blaschka skb = skb_peek(&buf->skb_list); 1223b3332930SFrank Blaschka while (skb) { 1224b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); 1225b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); 12266bee4e26SHans Wippel if (be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { 1227b3332930SFrank Blaschka if (skb->sk) { 1228b3332930SFrank Blaschka struct iucv_sock *iucv = iucv_sk(skb->sk); 1229b3332930SFrank Blaschka iucv->sk_txnotify(skb, notification); 1230b3332930SFrank Blaschka } 1231b3332930SFrank Blaschka } 1232b3332930SFrank Blaschka if (skb_queue_is_last(&buf->skb_list, skb)) 1233b3332930SFrank Blaschka skb = NULL; 1234b3332930SFrank Blaschka else 1235b3332930SFrank Blaschka skb = skb_queue_next(&buf->skb_list, skb); 1236b3332930SFrank Blaschka } 1237b3332930SFrank Blaschka out: 1238b3332930SFrank Blaschka return; 1239b3332930SFrank Blaschka } 1240b3332930SFrank Blaschka 1241b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) 1242b3332930SFrank Blaschka { 1243b3332930SFrank Blaschka struct sk_buff *skb; 124472861ae7SEinar Lueck struct iucv_sock *iucv; 124572861ae7SEinar Lueck int notify_general_error = 0; 124672861ae7SEinar Lueck 124772861ae7SEinar Lueck if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING) 124872861ae7SEinar Lueck notify_general_error = 1; 124972861ae7SEinar Lueck 125072861ae7SEinar Lueck /* release may never happen from within CQ tasklet scope */ 125118af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); 1252b3332930SFrank Blaschka 1253b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1254b3332930SFrank Blaschka while (skb) { 1255b3332930SFrank Blaschka QETH_CARD_TEXT(buf->q->card, 5, "skbr"); 1256b3332930SFrank Blaschka QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); 12576bee4e26SHans Wippel if (notify_general_error && 12586bee4e26SHans Wippel be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { 125972861ae7SEinar Lueck if (skb->sk) { 126072861ae7SEinar Lueck iucv = iucv_sk(skb->sk); 126172861ae7SEinar Lueck iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); 126272861ae7SEinar Lueck } 126372861ae7SEinar Lueck } 126463354797SReshetova, Elena refcount_dec(&skb->users); 1265b3332930SFrank Blaschka dev_kfree_skb_any(skb); 1266b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1267b3332930SFrank Blaschka } 1268b3332930SFrank Blaschka } 1269b3332930SFrank Blaschka 1270b67d801fSUrsula Braun static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, 12713b346c18SJulian Wiedmann struct qeth_qdio_out_buffer *buf) 12724a71df50SFrank Blaschka { 12734a71df50SFrank Blaschka int i; 12744a71df50SFrank Blaschka 12754a71df50SFrank Blaschka /* is PCI flag set on buffer? */ 12763ec90878SJan Glauber if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) 12774a71df50SFrank Blaschka atomic_dec(&queue->set_pci_flags_count); 12784a71df50SFrank Blaschka 1279b3332930SFrank Blaschka qeth_release_skbs(buf); 12803b346c18SJulian Wiedmann 12814a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { 1282683d718aSFrank Blaschka if (buf->buffer->element[i].addr && buf->is_header[i]) 1283683d718aSFrank Blaschka kmem_cache_free(qeth_core_header_cache, 1284683d718aSFrank Blaschka buf->buffer->element[i].addr); 1285683d718aSFrank Blaschka buf->is_header[i] = 0; 12864a71df50SFrank Blaschka } 12873b346c18SJulian Wiedmann 12883b346c18SJulian Wiedmann qeth_scrub_qdio_buffer(buf->buffer, 12893b346c18SJulian Wiedmann QETH_MAX_BUFFER_ELEMENTS(queue->card)); 12904a71df50SFrank Blaschka buf->next_element_to_fill = 0; 12913b346c18SJulian Wiedmann atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); 12920da9581dSEinar Lueck } 12930da9581dSEinar Lueck 12940da9581dSEinar Lueck static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) 12950da9581dSEinar Lueck { 12960da9581dSEinar Lueck int j; 12970da9581dSEinar Lueck 12980da9581dSEinar Lueck for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 12990da9581dSEinar Lueck if (!q->bufs[j]) 13000da9581dSEinar Lueck continue; 130172861ae7SEinar Lueck qeth_cleanup_handled_pending(q, j, 1); 13023b346c18SJulian Wiedmann qeth_clear_output_buffer(q, q->bufs[j]); 13030da9581dSEinar Lueck if (free) { 13040da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); 13050da9581dSEinar Lueck q->bufs[j] = NULL; 13060da9581dSEinar Lueck } 13070da9581dSEinar Lueck } 13084a71df50SFrank Blaschka } 13094a71df50SFrank Blaschka 13104a71df50SFrank Blaschka void qeth_clear_qdio_buffers(struct qeth_card *card) 13114a71df50SFrank Blaschka { 13120da9581dSEinar Lueck int i; 13134a71df50SFrank Blaschka 1314847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "clearqdbf"); 13154a71df50SFrank Blaschka /* clear outbound buffers to free skbs */ 13160da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 13174a71df50SFrank Blaschka if (card->qdio.out_qs[i]) { 13180da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 0); 13190da9581dSEinar Lueck } 13204a71df50SFrank Blaschka } 13214a71df50SFrank Blaschka } 13224a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_qdio_buffers); 13234a71df50SFrank Blaschka 13244a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *card) 13254a71df50SFrank Blaschka { 13264a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 13274a71df50SFrank Blaschka int i = 0; 13284a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 13294a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list){ 13304a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) 13314a71df50SFrank Blaschka free_page((unsigned long)pool_entry->elements[i]); 13324a71df50SFrank Blaschka list_del(&pool_entry->init_list); 13334a71df50SFrank Blaschka kfree(pool_entry); 13344a71df50SFrank Blaschka } 13354a71df50SFrank Blaschka } 13364a71df50SFrank Blaschka 13374a71df50SFrank Blaschka static void qeth_clean_channel(struct qeth_channel *channel) 13384a71df50SFrank Blaschka { 13394a71df50SFrank Blaschka int cnt; 13404a71df50SFrank Blaschka 1341d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freech"); 13424a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 13434a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 1344f15cdaf2SJulian Wiedmann kfree(channel->ccw); 13454a71df50SFrank Blaschka } 13464a71df50SFrank Blaschka 1347725b9c04SSebastian Ott static void qeth_set_single_write_queues(struct qeth_card *card) 1348725b9c04SSebastian Ott { 1349725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1350725b9c04SSebastian Ott (card->qdio.no_out_queues == 4)) 1351725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1352725b9c04SSebastian Ott 1353725b9c04SSebastian Ott card->qdio.no_out_queues = 1; 1354725b9c04SSebastian Ott if (card->qdio.default_out_queue != 0) 1355725b9c04SSebastian Ott dev_info(&card->gdev->dev, "Priority Queueing not supported\n"); 1356725b9c04SSebastian Ott 1357725b9c04SSebastian Ott card->qdio.default_out_queue = 0; 1358725b9c04SSebastian Ott } 1359725b9c04SSebastian Ott 1360725b9c04SSebastian Ott static void qeth_set_multiple_write_queues(struct qeth_card *card) 1361725b9c04SSebastian Ott { 1362725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1363725b9c04SSebastian Ott (card->qdio.no_out_queues == 1)) { 1364725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1365725b9c04SSebastian Ott card->qdio.default_out_queue = 2; 1366725b9c04SSebastian Ott } 1367725b9c04SSebastian Ott card->qdio.no_out_queues = 4; 1368725b9c04SSebastian Ott } 1369725b9c04SSebastian Ott 1370725b9c04SSebastian Ott static void qeth_update_from_chp_desc(struct qeth_card *card) 13714a71df50SFrank Blaschka { 13724a71df50SFrank Blaschka struct ccw_device *ccwdev; 1373ded27d8dSSebastian Ott struct channel_path_desc_fmt0 *chp_dsc; 13744a71df50SFrank Blaschka 13755113fec0SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "chp_desc"); 13764a71df50SFrank Blaschka 13774a71df50SFrank Blaschka ccwdev = card->data.ccwdev; 1378725b9c04SSebastian Ott chp_dsc = ccw_device_get_chp_desc(ccwdev, 0); 1379725b9c04SSebastian Ott if (!chp_dsc) 1380725b9c04SSebastian Ott goto out; 1381725b9c04SSebastian Ott 1382d0ff1f52SUrsula Braun card->info.func_level = 0x4100 + chp_dsc->desc; 1383725b9c04SSebastian Ott if (card->info.type == QETH_CARD_TYPE_IQD) 1384725b9c04SSebastian Ott goto out; 1385725b9c04SSebastian Ott 1386725b9c04SSebastian Ott /* CHPP field bit 6 == 1 -> single queue */ 1387725b9c04SSebastian Ott if ((chp_dsc->chpp & 0x02) == 0x02) 1388725b9c04SSebastian Ott qeth_set_single_write_queues(card); 1389725b9c04SSebastian Ott else 1390725b9c04SSebastian Ott qeth_set_multiple_write_queues(card); 1391725b9c04SSebastian Ott out: 1392d0ff1f52SUrsula Braun kfree(chp_dsc); 13935113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues); 13945113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); 13954a71df50SFrank Blaschka } 13964a71df50SFrank Blaschka 13974a71df50SFrank Blaschka static void qeth_init_qdio_info(struct qeth_card *card) 13984a71df50SFrank Blaschka { 1399d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 4, "intqdinf"); 14004a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 140195f4d8b7SJulian Wiedmann card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT; 140295f4d8b7SJulian Wiedmann card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; 140395f4d8b7SJulian Wiedmann card->qdio.no_out_queues = QETH_MAX_QUEUES; 140495f4d8b7SJulian Wiedmann 14054a71df50SFrank Blaschka /* inbound */ 1406ed2e93efSJulian Wiedmann card->qdio.no_in_queues = 1; 14074a71df50SFrank Blaschka card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 1408dcf4ae2dSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1409dcf4ae2dSFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT; 1410dcf4ae2dSFrank Blaschka else 14114a71df50SFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; 14124a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count; 14134a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list); 14144a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.init_pool.entry_list); 14154a71df50SFrank Blaschka } 14164a71df50SFrank Blaschka 141795f4d8b7SJulian Wiedmann static void qeth_set_initial_options(struct qeth_card *card) 14184a71df50SFrank Blaschka { 14194a71df50SFrank Blaschka card->options.route4.type = NO_ROUTER; 14204a71df50SFrank Blaschka card->options.route6.type = NO_ROUTER; 14214a71df50SFrank Blaschka card->options.rx_sg_cb = QETH_RX_SG_CB; 1422d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 14230da9581dSEinar Lueck card->options.cq = QETH_CQ_DISABLED; 14244a71df50SFrank Blaschka } 14254a71df50SFrank Blaschka 14264a71df50SFrank Blaschka static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) 14274a71df50SFrank Blaschka { 14284a71df50SFrank Blaschka unsigned long flags; 14294a71df50SFrank Blaschka int rc = 0; 14304a71df50SFrank Blaschka 14314a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 1432847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, " %02x%02x%02x", 14334a71df50SFrank Blaschka (u8) card->thread_start_mask, 14344a71df50SFrank Blaschka (u8) card->thread_allowed_mask, 14354a71df50SFrank Blaschka (u8) card->thread_running_mask); 14364a71df50SFrank Blaschka rc = (card->thread_start_mask & thread); 14374a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 14384a71df50SFrank Blaschka return rc; 14394a71df50SFrank Blaschka } 14404a71df50SFrank Blaschka 14414a71df50SFrank Blaschka static void qeth_start_kernel_thread(struct work_struct *work) 14424a71df50SFrank Blaschka { 14433f36b890SFrank Blaschka struct task_struct *ts; 14444a71df50SFrank Blaschka struct qeth_card *card = container_of(work, struct qeth_card, 14454a71df50SFrank Blaschka kernel_thread_starter); 1446847a50fdSCarsten Otte QETH_CARD_TEXT(card , 2, "strthrd"); 14474a71df50SFrank Blaschka 14484a71df50SFrank Blaschka if (card->read.state != CH_STATE_UP && 14494a71df50SFrank Blaschka card->write.state != CH_STATE_UP) 14504a71df50SFrank Blaschka return; 14513f36b890SFrank Blaschka if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { 1452c041f2d4SSebastian Ott ts = kthread_run(card->discipline->recover, (void *)card, 14534a71df50SFrank Blaschka "qeth_recover"); 14543f36b890SFrank Blaschka if (IS_ERR(ts)) { 14553f36b890SFrank Blaschka qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); 14563f36b890SFrank Blaschka qeth_clear_thread_running_bit(card, 14573f36b890SFrank Blaschka QETH_RECOVER_THREAD); 14583f36b890SFrank Blaschka } 14593f36b890SFrank Blaschka } 14604a71df50SFrank Blaschka } 14614a71df50SFrank Blaschka 1462bca51650SThomas Richter static void qeth_buffer_reclaim_work(struct work_struct *); 146395f4d8b7SJulian Wiedmann static void qeth_setup_card(struct qeth_card *card) 14644a71df50SFrank Blaschka { 1465d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupcrd"); 1466d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 14674a71df50SFrank Blaschka 146895f4d8b7SJulian Wiedmann card->info.type = CARD_RDEV(card)->id.driver_info; 14694a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 14704a71df50SFrank Blaschka spin_lock_init(&card->mclock); 14714a71df50SFrank Blaschka spin_lock_init(&card->lock); 14724a71df50SFrank Blaschka spin_lock_init(&card->ip_lock); 14734a71df50SFrank Blaschka spin_lock_init(&card->thread_mask_lock); 1474c4949f07SFrank Blaschka mutex_init(&card->conf_mutex); 14759dc48cccSUrsula Braun mutex_init(&card->discipline_mutex); 1476d4ac0246SJulian Wiedmann mutex_init(&card->vid_list_mutex); 14774a71df50SFrank Blaschka INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); 14784a71df50SFrank Blaschka INIT_LIST_HEAD(&card->cmd_waiter_list); 14794a71df50SFrank Blaschka init_waitqueue_head(&card->wait_q); 148095f4d8b7SJulian Wiedmann qeth_set_initial_options(card); 14814a71df50SFrank Blaschka /* IP address takeover */ 14824a71df50SFrank Blaschka INIT_LIST_HEAD(&card->ipato.entries); 14834a71df50SFrank Blaschka qeth_init_qdio_info(card); 1484b3332930SFrank Blaschka INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); 14850f54761dSStefan Raspl INIT_WORK(&card->close_dev_work, qeth_close_dev_handler); 14864a71df50SFrank Blaschka } 14874a71df50SFrank Blaschka 14886bcac508SMartin Schwidefsky static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) 14896bcac508SMartin Schwidefsky { 14906bcac508SMartin Schwidefsky struct qeth_card *card = container_of(slr, struct qeth_card, 14916bcac508SMartin Schwidefsky qeth_service_level); 14920d788c7dSKlaus-Dieter Wacker if (card->info.mcl_level[0]) 14930d788c7dSKlaus-Dieter Wacker seq_printf(m, "qeth: %s firmware level %s\n", 14940d788c7dSKlaus-Dieter Wacker CARD_BUS_ID(card), card->info.mcl_level); 14956bcac508SMartin Schwidefsky } 14966bcac508SMartin Schwidefsky 14974a71df50SFrank Blaschka static struct qeth_card *qeth_alloc_card(void) 14984a71df50SFrank Blaschka { 14994a71df50SFrank Blaschka struct qeth_card *card; 15004a71df50SFrank Blaschka 1501d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "alloccrd"); 1502f15cdaf2SJulian Wiedmann card = kzalloc(sizeof(*card), GFP_KERNEL); 15034a71df50SFrank Blaschka if (!card) 150476b11f8eSUrsula Braun goto out; 1505d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 150624142fd8SJulian Wiedmann if (qeth_setup_channel(&card->read, true)) 150776b11f8eSUrsula Braun goto out_ip; 150824142fd8SJulian Wiedmann if (qeth_setup_channel(&card->write, true)) 150976b11f8eSUrsula Braun goto out_channel; 151024142fd8SJulian Wiedmann if (qeth_setup_channel(&card->data, false)) 151124142fd8SJulian Wiedmann goto out_data; 15124a71df50SFrank Blaschka card->options.layer2 = -1; 15136bcac508SMartin Schwidefsky card->qeth_service_level.seq_print = qeth_core_sl_print; 15146bcac508SMartin Schwidefsky register_service_level(&card->qeth_service_level); 15154a71df50SFrank Blaschka return card; 151676b11f8eSUrsula Braun 151724142fd8SJulian Wiedmann out_data: 151824142fd8SJulian Wiedmann qeth_clean_channel(&card->write); 151976b11f8eSUrsula Braun out_channel: 152076b11f8eSUrsula Braun qeth_clean_channel(&card->read); 152176b11f8eSUrsula Braun out_ip: 152276b11f8eSUrsula Braun kfree(card); 152376b11f8eSUrsula Braun out: 152476b11f8eSUrsula Braun return NULL; 15254a71df50SFrank Blaschka } 15264a71df50SFrank Blaschka 15274a71df50SFrank Blaschka static int qeth_clear_channel(struct qeth_channel *channel) 15284a71df50SFrank Blaschka { 15294a71df50SFrank Blaschka unsigned long flags; 15304a71df50SFrank Blaschka struct qeth_card *card; 15314a71df50SFrank Blaschka int rc; 15324a71df50SFrank Blaschka 15334a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1534847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearch"); 15354a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15364a71df50SFrank Blaschka rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); 15374a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15384a71df50SFrank Blaschka 15394a71df50SFrank Blaschka if (rc) 15404a71df50SFrank Blaschka return rc; 15414a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15424a71df50SFrank Blaschka channel->state == CH_STATE_STOPPED, QETH_TIMEOUT); 15434a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15444a71df50SFrank Blaschka return rc; 15454a71df50SFrank Blaschka if (channel->state != CH_STATE_STOPPED) 15464a71df50SFrank Blaschka return -ETIME; 15474a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 15484a71df50SFrank Blaschka return 0; 15494a71df50SFrank Blaschka } 15504a71df50SFrank Blaschka 15514a71df50SFrank Blaschka static int qeth_halt_channel(struct qeth_channel *channel) 15524a71df50SFrank Blaschka { 15534a71df50SFrank Blaschka unsigned long flags; 15544a71df50SFrank Blaschka struct qeth_card *card; 15554a71df50SFrank Blaschka int rc; 15564a71df50SFrank Blaschka 15574a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1558847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltch"); 15594a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15604a71df50SFrank Blaschka rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); 15614a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15624a71df50SFrank Blaschka 15634a71df50SFrank Blaschka if (rc) 15644a71df50SFrank Blaschka return rc; 15654a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15664a71df50SFrank Blaschka channel->state == CH_STATE_HALTED, QETH_TIMEOUT); 15674a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15684a71df50SFrank Blaschka return rc; 15694a71df50SFrank Blaschka if (channel->state != CH_STATE_HALTED) 15704a71df50SFrank Blaschka return -ETIME; 15714a71df50SFrank Blaschka return 0; 15724a71df50SFrank Blaschka } 15734a71df50SFrank Blaschka 15744a71df50SFrank Blaschka static int qeth_halt_channels(struct qeth_card *card) 15754a71df50SFrank Blaschka { 15764a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 15774a71df50SFrank Blaschka 1578847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltchs"); 15794a71df50SFrank Blaschka rc1 = qeth_halt_channel(&card->read); 15804a71df50SFrank Blaschka rc2 = qeth_halt_channel(&card->write); 15814a71df50SFrank Blaschka rc3 = qeth_halt_channel(&card->data); 15824a71df50SFrank Blaschka if (rc1) 15834a71df50SFrank Blaschka return rc1; 15844a71df50SFrank Blaschka if (rc2) 15854a71df50SFrank Blaschka return rc2; 15864a71df50SFrank Blaschka return rc3; 15874a71df50SFrank Blaschka } 15884a71df50SFrank Blaschka 15894a71df50SFrank Blaschka static int qeth_clear_channels(struct qeth_card *card) 15904a71df50SFrank Blaschka { 15914a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 15924a71df50SFrank Blaschka 1593847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearchs"); 15944a71df50SFrank Blaschka rc1 = qeth_clear_channel(&card->read); 15954a71df50SFrank Blaschka rc2 = qeth_clear_channel(&card->write); 15964a71df50SFrank Blaschka rc3 = qeth_clear_channel(&card->data); 15974a71df50SFrank Blaschka if (rc1) 15984a71df50SFrank Blaschka return rc1; 15994a71df50SFrank Blaschka if (rc2) 16004a71df50SFrank Blaschka return rc2; 16014a71df50SFrank Blaschka return rc3; 16024a71df50SFrank Blaschka } 16034a71df50SFrank Blaschka 16044a71df50SFrank Blaschka static int qeth_clear_halt_card(struct qeth_card *card, int halt) 16054a71df50SFrank Blaschka { 16064a71df50SFrank Blaschka int rc = 0; 16074a71df50SFrank Blaschka 1608847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clhacrd"); 16094a71df50SFrank Blaschka 16104a71df50SFrank Blaschka if (halt) 16114a71df50SFrank Blaschka rc = qeth_halt_channels(card); 16124a71df50SFrank Blaschka if (rc) 16134a71df50SFrank Blaschka return rc; 16144a71df50SFrank Blaschka return qeth_clear_channels(card); 16154a71df50SFrank Blaschka } 16164a71df50SFrank Blaschka 16174a71df50SFrank Blaschka int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) 16184a71df50SFrank Blaschka { 16194a71df50SFrank Blaschka int rc = 0; 16204a71df50SFrank Blaschka 1621847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "qdioclr"); 16224a71df50SFrank Blaschka switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED, 16234a71df50SFrank Blaschka QETH_QDIO_CLEANING)) { 16244a71df50SFrank Blaschka case QETH_QDIO_ESTABLISHED: 16254a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1626cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16274a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_HALT); 16284a71df50SFrank Blaschka else 1629cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16304a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_CLEAR); 16314a71df50SFrank Blaschka if (rc) 1632847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "1err%d", rc); 16334a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 16344a71df50SFrank Blaschka break; 16354a71df50SFrank Blaschka case QETH_QDIO_CLEANING: 16364a71df50SFrank Blaschka return rc; 16374a71df50SFrank Blaschka default: 16384a71df50SFrank Blaschka break; 16394a71df50SFrank Blaschka } 16404a71df50SFrank Blaschka rc = qeth_clear_halt_card(card, use_halt); 16414a71df50SFrank Blaschka if (rc) 1642847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "2err%d", rc); 16434a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 16444a71df50SFrank Blaschka return rc; 16454a71df50SFrank Blaschka } 16464a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_clear_card); 16474a71df50SFrank Blaschka 16484a71df50SFrank Blaschka static int qeth_read_conf_data(struct qeth_card *card, void **buffer, 16494a71df50SFrank Blaschka int *length) 16504a71df50SFrank Blaschka { 16514a71df50SFrank Blaschka struct ciw *ciw; 16524a71df50SFrank Blaschka char *rcd_buf; 16534a71df50SFrank Blaschka int ret; 16544a71df50SFrank Blaschka struct qeth_channel *channel = &card->data; 16554a71df50SFrank Blaschka unsigned long flags; 16564a71df50SFrank Blaschka 16574a71df50SFrank Blaschka /* 16584a71df50SFrank Blaschka * scan for RCD command in extended SenseID data 16594a71df50SFrank Blaschka */ 16604a71df50SFrank Blaschka ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD); 16614a71df50SFrank Blaschka if (!ciw || ciw->cmd == 0) 16624a71df50SFrank Blaschka return -EOPNOTSUPP; 16634a71df50SFrank Blaschka rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); 16644a71df50SFrank Blaschka if (!rcd_buf) 16654a71df50SFrank Blaschka return -ENOMEM; 16664a71df50SFrank Blaschka 1667f15cdaf2SJulian Wiedmann qeth_setup_ccw(channel->ccw, ciw->cmd, ciw->count, rcd_buf); 16684a71df50SFrank Blaschka channel->state = CH_STATE_RCD; 16694a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 1670f15cdaf2SJulian Wiedmann ret = ccw_device_start_timeout(channel->ccwdev, channel->ccw, 16714a71df50SFrank Blaschka QETH_RCD_PARM, LPM_ANYPATH, 0, 16724a71df50SFrank Blaschka QETH_RCD_TIMEOUT); 16734a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 16744a71df50SFrank Blaschka if (!ret) 16754a71df50SFrank Blaschka wait_event(card->wait_q, 16764a71df50SFrank Blaschka (channel->state == CH_STATE_RCD_DONE || 16774a71df50SFrank Blaschka channel->state == CH_STATE_DOWN)); 16784a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) 16794a71df50SFrank Blaschka ret = -EIO; 16804a71df50SFrank Blaschka else 16814a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 16824a71df50SFrank Blaschka if (ret) { 16834a71df50SFrank Blaschka kfree(rcd_buf); 16844a71df50SFrank Blaschka *buffer = NULL; 16854a71df50SFrank Blaschka *length = 0; 16864a71df50SFrank Blaschka } else { 16874a71df50SFrank Blaschka *length = ciw->count; 16884a71df50SFrank Blaschka *buffer = rcd_buf; 16894a71df50SFrank Blaschka } 16904a71df50SFrank Blaschka return ret; 16914a71df50SFrank Blaschka } 16924a71df50SFrank Blaschka 1693a60389abSEinar Lueck static void qeth_configure_unitaddr(struct qeth_card *card, char *prcd) 16944a71df50SFrank Blaschka { 1695a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgunit"); 16964a71df50SFrank Blaschka card->info.chpid = prcd[30]; 16974a71df50SFrank Blaschka card->info.unit_addr2 = prcd[31]; 16984a71df50SFrank Blaschka card->info.cula = prcd[63]; 16994a71df50SFrank Blaschka card->info.guestlan = ((prcd[0x10] == _ascebc['V']) && 17004a71df50SFrank Blaschka (prcd[0x11] == _ascebc['M'])); 1701a60389abSEinar Lueck } 1702a60389abSEinar Lueck 1703615dff22SJulian Wiedmann static enum qeth_discipline_id qeth_vm_detect_layer(struct qeth_card *card) 1704615dff22SJulian Wiedmann { 1705615dff22SJulian Wiedmann enum qeth_discipline_id disc = QETH_DISCIPLINE_UNDETERMINED; 1706615dff22SJulian Wiedmann struct diag26c_vnic_resp *response = NULL; 1707615dff22SJulian Wiedmann struct diag26c_vnic_req *request = NULL; 1708615dff22SJulian Wiedmann struct ccw_dev_id id; 1709615dff22SJulian Wiedmann char userid[80]; 1710615dff22SJulian Wiedmann int rc = 0; 1711615dff22SJulian Wiedmann 1712615dff22SJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "vmlayer"); 1713615dff22SJulian Wiedmann 1714615dff22SJulian Wiedmann cpcmd("QUERY USERID", userid, sizeof(userid), &rc); 1715615dff22SJulian Wiedmann if (rc) 1716615dff22SJulian Wiedmann goto out; 1717615dff22SJulian Wiedmann 1718615dff22SJulian Wiedmann request = kzalloc(sizeof(*request), GFP_KERNEL | GFP_DMA); 1719615dff22SJulian Wiedmann response = kzalloc(sizeof(*response), GFP_KERNEL | GFP_DMA); 1720615dff22SJulian Wiedmann if (!request || !response) { 1721615dff22SJulian Wiedmann rc = -ENOMEM; 1722615dff22SJulian Wiedmann goto out; 1723615dff22SJulian Wiedmann } 1724615dff22SJulian Wiedmann 1725615dff22SJulian Wiedmann ccw_device_get_id(CARD_RDEV(card), &id); 1726615dff22SJulian Wiedmann request->resp_buf_len = sizeof(*response); 1727615dff22SJulian Wiedmann request->resp_version = DIAG26C_VERSION6_VM65918; 1728615dff22SJulian Wiedmann request->req_format = DIAG26C_VNIC_INFO; 1729615dff22SJulian Wiedmann ASCEBC(userid, 8); 1730615dff22SJulian Wiedmann memcpy(&request->sys_name, userid, 8); 1731615dff22SJulian Wiedmann request->devno = id.devno; 1732615dff22SJulian Wiedmann 1733615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, request, sizeof(*request)); 1734615dff22SJulian Wiedmann rc = diag26c(request, response, DIAG26C_PORT_VNIC); 1735615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, request, sizeof(*request)); 1736615dff22SJulian Wiedmann if (rc) 1737615dff22SJulian Wiedmann goto out; 1738615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, response, sizeof(*response)); 1739615dff22SJulian Wiedmann 1740615dff22SJulian Wiedmann if (request->resp_buf_len < sizeof(*response) || 1741615dff22SJulian Wiedmann response->version != request->resp_version) { 1742615dff22SJulian Wiedmann rc = -EIO; 1743615dff22SJulian Wiedmann goto out; 1744615dff22SJulian Wiedmann } 1745615dff22SJulian Wiedmann 1746615dff22SJulian Wiedmann if (response->protocol == VNIC_INFO_PROT_L2) 1747615dff22SJulian Wiedmann disc = QETH_DISCIPLINE_LAYER2; 1748615dff22SJulian Wiedmann else if (response->protocol == VNIC_INFO_PROT_L3) 1749615dff22SJulian Wiedmann disc = QETH_DISCIPLINE_LAYER3; 1750615dff22SJulian Wiedmann 1751615dff22SJulian Wiedmann out: 1752615dff22SJulian Wiedmann kfree(response); 1753615dff22SJulian Wiedmann kfree(request); 1754615dff22SJulian Wiedmann if (rc) 1755615dff22SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "err%x", rc); 1756615dff22SJulian Wiedmann return disc; 1757615dff22SJulian Wiedmann } 1758615dff22SJulian Wiedmann 1759c70eb09dSJulian Wiedmann /* Determine whether the device requires a specific layer discipline */ 1760c70eb09dSJulian Wiedmann static enum qeth_discipline_id qeth_enforce_discipline(struct qeth_card *card) 1761c70eb09dSJulian Wiedmann { 1762615dff22SJulian Wiedmann enum qeth_discipline_id disc = QETH_DISCIPLINE_UNDETERMINED; 1763615dff22SJulian Wiedmann 1764c70eb09dSJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_OSM || 1765615dff22SJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSN) 1766615dff22SJulian Wiedmann disc = QETH_DISCIPLINE_LAYER2; 1767615dff22SJulian Wiedmann else if (card->info.guestlan) 1768615dff22SJulian Wiedmann disc = (card->info.type == QETH_CARD_TYPE_IQD) ? 1769615dff22SJulian Wiedmann QETH_DISCIPLINE_LAYER3 : 1770615dff22SJulian Wiedmann qeth_vm_detect_layer(card); 1771615dff22SJulian Wiedmann 1772615dff22SJulian Wiedmann switch (disc) { 1773615dff22SJulian Wiedmann case QETH_DISCIPLINE_LAYER2: 1774c70eb09dSJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force l2"); 1775615dff22SJulian Wiedmann break; 1776615dff22SJulian Wiedmann case QETH_DISCIPLINE_LAYER3: 1777c70eb09dSJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force l3"); 1778615dff22SJulian Wiedmann break; 1779615dff22SJulian Wiedmann default: 1780615dff22SJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force no"); 1781c70eb09dSJulian Wiedmann } 1782c70eb09dSJulian Wiedmann 1783615dff22SJulian Wiedmann return disc; 1784c70eb09dSJulian Wiedmann } 1785c70eb09dSJulian Wiedmann 1786a60389abSEinar Lueck static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd) 1787a60389abSEinar Lueck { 1788a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); 1789a60389abSEinar Lueck 1790e6e056baSStefan Raspl if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && 1791a0c98523SUrsula Braun prcd[76] >= 0xF1 && prcd[76] <= 0xF4) { 1792a60389abSEinar Lueck card->info.blkt.time_total = 0; 1793a60389abSEinar Lueck card->info.blkt.inter_packet = 0; 1794a60389abSEinar Lueck card->info.blkt.inter_packet_jumbo = 0; 1795a0c98523SUrsula Braun } else { 1796a0c98523SUrsula Braun card->info.blkt.time_total = 250; 1797a0c98523SUrsula Braun card->info.blkt.inter_packet = 5; 1798a0c98523SUrsula Braun card->info.blkt.inter_packet_jumbo = 15; 1799a60389abSEinar Lueck } 18004a71df50SFrank Blaschka } 18014a71df50SFrank Blaschka 18024a71df50SFrank Blaschka static void qeth_init_tokens(struct qeth_card *card) 18034a71df50SFrank Blaschka { 18044a71df50SFrank Blaschka card->token.issuer_rm_w = 0x00010103UL; 18054a71df50SFrank Blaschka card->token.cm_filter_w = 0x00010108UL; 18064a71df50SFrank Blaschka card->token.cm_connection_w = 0x0001010aUL; 18074a71df50SFrank Blaschka card->token.ulp_filter_w = 0x0001010bUL; 18084a71df50SFrank Blaschka card->token.ulp_connection_w = 0x0001010dUL; 18094a71df50SFrank Blaschka } 18104a71df50SFrank Blaschka 18114a71df50SFrank Blaschka static void qeth_init_func_level(struct qeth_card *card) 18124a71df50SFrank Blaschka { 18135113fec0SUrsula Braun switch (card->info.type) { 18145113fec0SUrsula Braun case QETH_CARD_TYPE_IQD: 18156298263aSKlaus-Dieter Wacker card->info.func_level = QETH_IDX_FUNC_LEVEL_IQD; 18165113fec0SUrsula Braun break; 18175113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 18180132951eSUrsula Braun case QETH_CARD_TYPE_OSN: 18195113fec0SUrsula Braun card->info.func_level = QETH_IDX_FUNC_LEVEL_OSD; 18205113fec0SUrsula Braun break; 18215113fec0SUrsula Braun default: 18225113fec0SUrsula Braun break; 18234a71df50SFrank Blaschka } 18244a71df50SFrank Blaschka } 18254a71df50SFrank Blaschka 18264a71df50SFrank Blaschka static int qeth_idx_activate_get_answer(struct qeth_channel *channel, 18274a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 18284a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 18294a71df50SFrank Blaschka { 18304a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 18314a71df50SFrank Blaschka unsigned long flags; 18324a71df50SFrank Blaschka int rc; 18334a71df50SFrank Blaschka struct qeth_card *card; 18344a71df50SFrank Blaschka 1835d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxanswr"); 18364a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 18374a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 18381aec42bcSThomas Richter if (!iob) 18391aec42bcSThomas Richter return -ENOMEM; 18404a71df50SFrank Blaschka iob->callback = idx_reply_cb; 1841f15cdaf2SJulian Wiedmann qeth_setup_ccw(channel->ccw, CCW_CMD_READ, QETH_BUFSIZE, iob->data); 18424a71df50SFrank Blaschka 18434a71df50SFrank Blaschka wait_event(card->wait_q, 18444a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1845d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 18464a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 1847f15cdaf2SJulian Wiedmann rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw, 1848db71bbbdSJulian Wiedmann (addr_t) iob, 0, 0, QETH_TIMEOUT); 18494a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 18504a71df50SFrank Blaschka 18514a71df50SFrank Blaschka if (rc) { 185214cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc); 1853d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 18544a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 18554a71df50SFrank Blaschka wake_up(&card->wait_q); 18564a71df50SFrank Blaschka return rc; 18574a71df50SFrank Blaschka } 18584a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 18594a71df50SFrank Blaschka channel->state == CH_STATE_UP, QETH_TIMEOUT); 18604a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 18614a71df50SFrank Blaschka return rc; 18624a71df50SFrank Blaschka if (channel->state != CH_STATE_UP) { 18634a71df50SFrank Blaschka rc = -ETIME; 1864d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 18654a71df50SFrank Blaschka } else 18664a71df50SFrank Blaschka rc = 0; 18674a71df50SFrank Blaschka return rc; 18684a71df50SFrank Blaschka } 18694a71df50SFrank Blaschka 18704a71df50SFrank Blaschka static int qeth_idx_activate_channel(struct qeth_channel *channel, 18714a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 18724a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 18734a71df50SFrank Blaschka { 18744a71df50SFrank Blaschka struct qeth_card *card; 18754a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 18764a71df50SFrank Blaschka unsigned long flags; 18774a71df50SFrank Blaschka __u16 temp; 18784a71df50SFrank Blaschka __u8 tmp; 18794a71df50SFrank Blaschka int rc; 1880f06f6f32SCornelia Huck struct ccw_dev_id temp_devid; 18814a71df50SFrank Blaschka 18824a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 18834a71df50SFrank Blaschka 1884d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxactch"); 18854a71df50SFrank Blaschka 18864a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 18871aec42bcSThomas Richter if (!iob) 18881aec42bcSThomas Richter return -ENOMEM; 18894a71df50SFrank Blaschka iob->callback = idx_reply_cb; 1890f15cdaf2SJulian Wiedmann qeth_setup_ccw(channel->ccw, CCW_CMD_WRITE, IDX_ACTIVATE_SIZE, 189145ca2fd6SJulian Wiedmann iob->data); 18924a71df50SFrank Blaschka if (channel == &card->write) { 18934a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE); 18944a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 18954a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 18964a71df50SFrank Blaschka card->seqno.trans_hdr++; 18974a71df50SFrank Blaschka } else { 18984a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE); 18994a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 19004a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 19014a71df50SFrank Blaschka } 190292d27209SJulian Wiedmann tmp = ((u8)card->dev->dev_port) | 0x80; 19034a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1); 19044a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 19054a71df50SFrank Blaschka &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); 19064a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data), 19074a71df50SFrank Blaschka &card->info.func_level, sizeof(__u16)); 1908f06f6f32SCornelia Huck ccw_device_get_id(CARD_DDEV(card), &temp_devid); 1909f06f6f32SCornelia Huck memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 2); 19104a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 19114a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2); 19124a71df50SFrank Blaschka 19134a71df50SFrank Blaschka wait_event(card->wait_q, 19144a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1915d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 19164a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 1917f15cdaf2SJulian Wiedmann rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw, 1918db71bbbdSJulian Wiedmann (addr_t) iob, 0, 0, QETH_TIMEOUT); 19194a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 19204a71df50SFrank Blaschka 19214a71df50SFrank Blaschka if (rc) { 192214cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error1 in activating channel. rc=%d\n", 192314cc21b6SFrank Blaschka rc); 1924d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 19254a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 19264a71df50SFrank Blaschka wake_up(&card->wait_q); 19274a71df50SFrank Blaschka return rc; 19284a71df50SFrank Blaschka } 19294a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 19304a71df50SFrank Blaschka channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT); 19314a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 19324a71df50SFrank Blaschka return rc; 19334a71df50SFrank Blaschka if (channel->state != CH_STATE_ACTIVATING) { 193474eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, "The qeth device driver" 193574eacdb9SFrank Blaschka " failed to recover an error on the device\n"); 193674eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n", 193774eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev)); 1938d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME); 19394a71df50SFrank Blaschka return -ETIME; 19404a71df50SFrank Blaschka } 19414a71df50SFrank Blaschka return qeth_idx_activate_get_answer(channel, idx_reply_cb); 19424a71df50SFrank Blaschka } 19434a71df50SFrank Blaschka 19444a71df50SFrank Blaschka static int qeth_peer_func_level(int level) 19454a71df50SFrank Blaschka { 19464a71df50SFrank Blaschka if ((level & 0xff) == 8) 19474a71df50SFrank Blaschka return (level & 0xff) + 0x400; 19484a71df50SFrank Blaschka if (((level >> 8) & 3) == 1) 19494a71df50SFrank Blaschka return (level & 0xff) + 0x200; 19504a71df50SFrank Blaschka return level; 19514a71df50SFrank Blaschka } 19524a71df50SFrank Blaschka 19534a71df50SFrank Blaschka static void qeth_idx_write_cb(struct qeth_channel *channel, 19544a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 19554a71df50SFrank Blaschka { 19564a71df50SFrank Blaschka struct qeth_card *card; 19574a71df50SFrank Blaschka __u16 temp; 19584a71df50SFrank Blaschka 1959d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxwrcb"); 19604a71df50SFrank Blaschka 19614a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19624a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 19634a71df50SFrank Blaschka goto out; 19644a71df50SFrank Blaschka } 19654a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 19664a71df50SFrank Blaschka 19674a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 19685113fec0SUrsula Braun if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == QETH_IDX_ACT_ERR_EXCL) 1969750b1625SJulian Wiedmann dev_err(&channel->ccwdev->dev, 197074eacdb9SFrank Blaschka "The adapter is used exclusively by another " 197174eacdb9SFrank Blaschka "host\n"); 19724a71df50SFrank Blaschka else 197374eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:" 197474eacdb9SFrank Blaschka " negative reply\n", 1975750b1625SJulian Wiedmann dev_name(&channel->ccwdev->dev)); 19764a71df50SFrank Blaschka goto out; 19774a71df50SFrank Blaschka } 19784a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 19794a71df50SFrank Blaschka if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { 198074eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: " 198174eacdb9SFrank Blaschka "function level mismatch (sent: 0x%x, received: " 1982750b1625SJulian Wiedmann "0x%x)\n", dev_name(&channel->ccwdev->dev), 198374eacdb9SFrank Blaschka card->info.func_level, temp); 19844a71df50SFrank Blaschka goto out; 19854a71df50SFrank Blaschka } 19864a71df50SFrank Blaschka channel->state = CH_STATE_UP; 19874a71df50SFrank Blaschka out: 19884a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 19894a71df50SFrank Blaschka } 19904a71df50SFrank Blaschka 19914a71df50SFrank Blaschka static void qeth_idx_read_cb(struct qeth_channel *channel, 19924a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 19934a71df50SFrank Blaschka { 19944a71df50SFrank Blaschka struct qeth_card *card; 19954a71df50SFrank Blaschka __u16 temp; 19964a71df50SFrank Blaschka 1997d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxrdcb"); 19984a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19994a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 20004a71df50SFrank Blaschka goto out; 20014a71df50SFrank Blaschka } 20024a71df50SFrank Blaschka 20034a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 20045113fec0SUrsula Braun if (qeth_check_idx_response(card, iob->data)) 20054a71df50SFrank Blaschka goto out; 20064a71df50SFrank Blaschka 20074a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 20085113fec0SUrsula Braun switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) { 20095113fec0SUrsula Braun case QETH_IDX_ACT_ERR_EXCL: 2010750b1625SJulian Wiedmann dev_err(&channel->ccwdev->dev, 201174eacdb9SFrank Blaschka "The adapter is used exclusively by another " 201274eacdb9SFrank Blaschka "host\n"); 20135113fec0SUrsula Braun break; 20145113fec0SUrsula Braun case QETH_IDX_ACT_ERR_AUTH: 201501fc3e86SUrsula Braun case QETH_IDX_ACT_ERR_AUTH_USER: 2016750b1625SJulian Wiedmann dev_err(&channel->ccwdev->dev, 20175113fec0SUrsula Braun "Setting the device online failed because of " 201801fc3e86SUrsula Braun "insufficient authorization\n"); 20195113fec0SUrsula Braun break; 20205113fec0SUrsula Braun default: 202174eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:" 202274eacdb9SFrank Blaschka " negative reply\n", 2023750b1625SJulian Wiedmann dev_name(&channel->ccwdev->dev)); 20245113fec0SUrsula Braun } 202501fc3e86SUrsula Braun QETH_CARD_TEXT_(card, 2, "idxread%c", 202601fc3e86SUrsula Braun QETH_IDX_ACT_CAUSE_CODE(iob->data)); 20274a71df50SFrank Blaschka goto out; 20284a71df50SFrank Blaschka } 20294a71df50SFrank Blaschka 20304a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 20314a71df50SFrank Blaschka if (temp != qeth_peer_func_level(card->info.func_level)) { 203274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " 20334a71df50SFrank Blaschka "level mismatch (sent: 0x%x, received: 0x%x)\n", 2034750b1625SJulian Wiedmann dev_name(&channel->ccwdev->dev), 203574eacdb9SFrank Blaschka card->info.func_level, temp); 20364a71df50SFrank Blaschka goto out; 20374a71df50SFrank Blaschka } 20384a71df50SFrank Blaschka memcpy(&card->token.issuer_rm_r, 20394a71df50SFrank Blaschka QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 20404a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 20414a71df50SFrank Blaschka memcpy(&card->info.mcl_level[0], 20424a71df50SFrank Blaschka QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH); 20434a71df50SFrank Blaschka channel->state = CH_STATE_UP; 20444a71df50SFrank Blaschka out: 20454a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 20464a71df50SFrank Blaschka } 20474a71df50SFrank Blaschka 20484a71df50SFrank Blaschka void qeth_prepare_control_data(struct qeth_card *card, int len, 20494a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 20504a71df50SFrank Blaschka { 2051f15cdaf2SJulian Wiedmann qeth_setup_ccw(iob->channel->ccw, CCW_CMD_WRITE, len, iob->data); 20524a71df50SFrank Blaschka iob->callback = qeth_release_buffer; 20534a71df50SFrank Blaschka 20544a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 20554a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 20564a71df50SFrank Blaschka card->seqno.trans_hdr++; 20574a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data), 20584a71df50SFrank Blaschka &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH); 20594a71df50SFrank Blaschka card->seqno.pdu_hdr++; 20604a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data), 20614a71df50SFrank Blaschka &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH); 2062d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN); 20634a71df50SFrank Blaschka } 20644a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_control_data); 20654a71df50SFrank Blaschka 2066efbbc1d5SEugene Crosser /** 2067efbbc1d5SEugene Crosser * qeth_send_control_data() - send control command to the card 2068efbbc1d5SEugene Crosser * @card: qeth_card structure pointer 2069efbbc1d5SEugene Crosser * @len: size of the command buffer 2070efbbc1d5SEugene Crosser * @iob: qeth_cmd_buffer pointer 2071efbbc1d5SEugene Crosser * @reply_cb: callback function pointer 2072efbbc1d5SEugene Crosser * @cb_card: pointer to the qeth_card structure 2073efbbc1d5SEugene Crosser * @cb_reply: pointer to the qeth_reply structure 2074efbbc1d5SEugene Crosser * @cb_cmd: pointer to the original iob for non-IPA 2075efbbc1d5SEugene Crosser * commands, or to the qeth_ipa_cmd structure 2076efbbc1d5SEugene Crosser * for the IPA commands. 2077efbbc1d5SEugene Crosser * @reply_param: private pointer passed to the callback 2078efbbc1d5SEugene Crosser * 2079efbbc1d5SEugene Crosser * Returns the value of the `return_code' field of the response 2080efbbc1d5SEugene Crosser * block returned from the hardware, or other error indication. 2081efbbc1d5SEugene Crosser * Value of zero indicates successful execution of the command. 2082efbbc1d5SEugene Crosser * 2083efbbc1d5SEugene Crosser * Callback function gets called one or more times, with cb_cmd 2084efbbc1d5SEugene Crosser * pointing to the response returned by the hardware. Callback 2085efbbc1d5SEugene Crosser * function must return non-zero if more reply blocks are expected, 2086efbbc1d5SEugene Crosser * and zero if the last or only reply block is received. Callback 2087efbbc1d5SEugene Crosser * function can get the value of the reply_param pointer from the 2088efbbc1d5SEugene Crosser * field 'param' of the structure qeth_reply. 2089efbbc1d5SEugene Crosser */ 2090efbbc1d5SEugene Crosser 20914a71df50SFrank Blaschka int qeth_send_control_data(struct qeth_card *card, int len, 20924a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, 2093efbbc1d5SEugene Crosser int (*reply_cb)(struct qeth_card *cb_card, 2094efbbc1d5SEugene Crosser struct qeth_reply *cb_reply, 2095efbbc1d5SEugene Crosser unsigned long cb_cmd), 20964a71df50SFrank Blaschka void *reply_param) 20974a71df50SFrank Blaschka { 2098750b1625SJulian Wiedmann struct qeth_channel *channel = iob->channel; 20994a71df50SFrank Blaschka int rc; 21004a71df50SFrank Blaschka unsigned long flags; 21014a71df50SFrank Blaschka struct qeth_reply *reply = NULL; 21027834cd5aSHeiko Carstens unsigned long timeout, event_timeout; 21031c5b2216SJulian Wiedmann struct qeth_ipa_cmd *cmd = NULL; 21044a71df50SFrank Blaschka 2105847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "sendctl"); 21064a71df50SFrank Blaschka 2107908abbb5SUrsula Braun if (card->read_or_write_problem) { 2108750b1625SJulian Wiedmann qeth_release_buffer(channel, iob); 2109908abbb5SUrsula Braun return -EIO; 2110908abbb5SUrsula Braun } 21114a71df50SFrank Blaschka reply = qeth_alloc_reply(card); 21124a71df50SFrank Blaschka if (!reply) { 21134a71df50SFrank Blaschka return -ENOMEM; 21144a71df50SFrank Blaschka } 21154a71df50SFrank Blaschka reply->callback = reply_cb; 21164a71df50SFrank Blaschka reply->param = reply_param; 2117d22ffb5aSJulian Wiedmann 21184a71df50SFrank Blaschka init_waitqueue_head(&reply->wait_q); 21194a71df50SFrank Blaschka 2120750b1625SJulian Wiedmann while (atomic_cmpxchg(&channel->irq_pending, 0, 1)) ; 21214a71df50SFrank Blaschka 21221c5b2216SJulian Wiedmann if (IS_IPA(iob->data)) { 21231c5b2216SJulian Wiedmann cmd = __ipa_cmd(iob); 2124d22ffb5aSJulian Wiedmann cmd->hdr.seqno = card->seqno.ipa++; 2125d22ffb5aSJulian Wiedmann reply->seqno = cmd->hdr.seqno; 21267834cd5aSHeiko Carstens event_timeout = QETH_IPA_TIMEOUT; 21271c5b2216SJulian Wiedmann } else { 2128d22ffb5aSJulian Wiedmann reply->seqno = QETH_IDX_COMMAND_SEQNO; 21297834cd5aSHeiko Carstens event_timeout = QETH_TIMEOUT; 21301c5b2216SJulian Wiedmann } 2131d22ffb5aSJulian Wiedmann qeth_prepare_control_data(card, len, iob); 2132d22ffb5aSJulian Wiedmann 2133d22ffb5aSJulian Wiedmann spin_lock_irqsave(&card->lock, flags); 2134d22ffb5aSJulian Wiedmann list_add_tail(&reply->list, &card->cmd_waiter_list); 2135d22ffb5aSJulian Wiedmann spin_unlock_irqrestore(&card->lock, flags); 21361c5b2216SJulian Wiedmann 21377834cd5aSHeiko Carstens timeout = jiffies + event_timeout; 21384a71df50SFrank Blaschka 2139847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 2140750b1625SJulian Wiedmann spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 2141f15cdaf2SJulian Wiedmann rc = ccw_device_start_timeout(channel->ccwdev, channel->ccw, 2142db71bbbdSJulian Wiedmann (addr_t) iob, 0, 0, event_timeout); 2143750b1625SJulian Wiedmann spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 21444a71df50SFrank Blaschka if (rc) { 214574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: " 214674eacdb9SFrank Blaschka "ccw_device_start rc = %i\n", 2147750b1625SJulian Wiedmann dev_name(&channel->ccwdev->dev), rc); 2148847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " err%d", rc); 21494a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 21504a71df50SFrank Blaschka list_del_init(&reply->list); 21514a71df50SFrank Blaschka qeth_put_reply(reply); 21524a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 2153750b1625SJulian Wiedmann qeth_release_buffer(channel, iob); 2154750b1625SJulian Wiedmann atomic_set(&channel->irq_pending, 0); 21554a71df50SFrank Blaschka wake_up(&card->wait_q); 21564a71df50SFrank Blaschka return rc; 21574a71df50SFrank Blaschka } 21585b54e16fSFrank Blaschka 21595b54e16fSFrank Blaschka /* we have only one long running ipassist, since we can ensure 21605b54e16fSFrank Blaschka process context of this command we can sleep */ 21611c5b2216SJulian Wiedmann if (cmd && cmd->hdr.command == IPA_CMD_SETIP && 21621c5b2216SJulian Wiedmann cmd->hdr.prot_version == QETH_PROT_IPV4) { 21635b54e16fSFrank Blaschka if (!wait_event_timeout(reply->wait_q, 21647834cd5aSHeiko Carstens atomic_read(&reply->received), event_timeout)) 21655b54e16fSFrank Blaschka goto time_err; 21665b54e16fSFrank Blaschka } else { 21674a71df50SFrank Blaschka while (!atomic_read(&reply->received)) { 21685b54e16fSFrank Blaschka if (time_after(jiffies, timeout)) 21695b54e16fSFrank Blaschka goto time_err; 21705b54e16fSFrank Blaschka cpu_relax(); 21716531084cSPeter Senna Tschudin } 21725b54e16fSFrank Blaschka } 21735b54e16fSFrank Blaschka 21745b54e16fSFrank Blaschka rc = reply->rc; 21755b54e16fSFrank Blaschka qeth_put_reply(reply); 21765b54e16fSFrank Blaschka return rc; 21775b54e16fSFrank Blaschka 21785b54e16fSFrank Blaschka time_err: 217970919e23SUrsula Braun reply->rc = -ETIME; 21804a71df50SFrank Blaschka spin_lock_irqsave(&reply->card->lock, flags); 21814a71df50SFrank Blaschka list_del_init(&reply->list); 21824a71df50SFrank Blaschka spin_unlock_irqrestore(&reply->card->lock, flags); 21834a71df50SFrank Blaschka atomic_inc(&reply->received); 21844a71df50SFrank Blaschka rc = reply->rc; 21854a71df50SFrank Blaschka qeth_put_reply(reply); 21864a71df50SFrank Blaschka return rc; 21874a71df50SFrank Blaschka } 21884a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_control_data); 21894a71df50SFrank Blaschka 21904a71df50SFrank Blaschka static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 21914a71df50SFrank Blaschka unsigned long data) 21924a71df50SFrank Blaschka { 21934a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21944a71df50SFrank Blaschka 2195d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenblcb"); 21964a71df50SFrank Blaschka 21974a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 21984a71df50SFrank Blaschka memcpy(&card->token.cm_filter_r, 21994a71df50SFrank Blaschka QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data), 22004a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2201d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 22024a71df50SFrank Blaschka return 0; 22034a71df50SFrank Blaschka } 22044a71df50SFrank Blaschka 22054a71df50SFrank Blaschka static int qeth_cm_enable(struct qeth_card *card) 22064a71df50SFrank Blaschka { 22074a71df50SFrank Blaschka int rc; 22084a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 22094a71df50SFrank Blaschka 2210d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenable"); 22114a71df50SFrank Blaschka 22124a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 22134a71df50SFrank Blaschka memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE); 22144a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data), 22154a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 22164a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data), 22174a71df50SFrank Blaschka &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH); 22184a71df50SFrank Blaschka 22194a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob, 22204a71df50SFrank Blaschka qeth_cm_enable_cb, NULL); 22214a71df50SFrank Blaschka return rc; 22224a71df50SFrank Blaschka } 22234a71df50SFrank Blaschka 22244a71df50SFrank Blaschka static int qeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 22254a71df50SFrank Blaschka unsigned long data) 22264a71df50SFrank Blaschka { 22274a71df50SFrank Blaschka 22284a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 22294a71df50SFrank Blaschka 2230d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetpcb"); 22314a71df50SFrank Blaschka 22324a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 22334a71df50SFrank Blaschka memcpy(&card->token.cm_connection_r, 22344a71df50SFrank Blaschka QETH_CM_SETUP_RESP_DEST_ADDR(iob->data), 22354a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2236d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 22374a71df50SFrank Blaschka return 0; 22384a71df50SFrank Blaschka } 22394a71df50SFrank Blaschka 22404a71df50SFrank Blaschka static int qeth_cm_setup(struct qeth_card *card) 22414a71df50SFrank Blaschka { 22424a71df50SFrank Blaschka int rc; 22434a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 22444a71df50SFrank Blaschka 2245d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetup"); 22464a71df50SFrank Blaschka 22474a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 22484a71df50SFrank Blaschka memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE); 22494a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data), 22504a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 22514a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data), 22524a71df50SFrank Blaschka &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH); 22534a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data), 22544a71df50SFrank Blaschka &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH); 22554a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob, 22564a71df50SFrank Blaschka qeth_cm_setup_cb, NULL); 22574a71df50SFrank Blaschka return rc; 22584a71df50SFrank Blaschka 22594a71df50SFrank Blaschka } 22604a71df50SFrank Blaschka 22618ce7a9e0SJulian Wiedmann static int qeth_update_max_mtu(struct qeth_card *card, unsigned int max_mtu) 22624a71df50SFrank Blaschka { 22638ce7a9e0SJulian Wiedmann struct net_device *dev = card->dev; 22648ce7a9e0SJulian Wiedmann unsigned int new_mtu; 22658ce7a9e0SJulian Wiedmann 22668ce7a9e0SJulian Wiedmann if (!max_mtu) { 22678ce7a9e0SJulian Wiedmann /* IQD needs accurate max MTU to set up its RX buffers: */ 22688ce7a9e0SJulian Wiedmann if (IS_IQD(card)) 22698ce7a9e0SJulian Wiedmann return -EINVAL; 22708ce7a9e0SJulian Wiedmann /* tolerate quirky HW: */ 22718ce7a9e0SJulian Wiedmann max_mtu = ETH_MAX_MTU; 22724a71df50SFrank Blaschka } 22738ce7a9e0SJulian Wiedmann 22748ce7a9e0SJulian Wiedmann rtnl_lock(); 22758ce7a9e0SJulian Wiedmann if (IS_IQD(card)) { 22768ce7a9e0SJulian Wiedmann /* move any device with default MTU to new max MTU: */ 22778ce7a9e0SJulian Wiedmann new_mtu = (dev->mtu == dev->max_mtu) ? max_mtu : dev->mtu; 22788ce7a9e0SJulian Wiedmann 22798ce7a9e0SJulian Wiedmann /* adjust RX buffer size to new max MTU: */ 22808ce7a9e0SJulian Wiedmann card->qdio.in_buf_size = max_mtu + 2 * PAGE_SIZE; 22818ce7a9e0SJulian Wiedmann if (dev->max_mtu && dev->max_mtu != max_mtu) 22828ce7a9e0SJulian Wiedmann qeth_free_qdio_buffers(card); 22838ce7a9e0SJulian Wiedmann } else { 22848ce7a9e0SJulian Wiedmann if (dev->mtu) 22858ce7a9e0SJulian Wiedmann new_mtu = dev->mtu; 22868ce7a9e0SJulian Wiedmann /* default MTUs for first setup: */ 22878ce7a9e0SJulian Wiedmann else if (card->options.layer2) 22888ce7a9e0SJulian Wiedmann new_mtu = ETH_DATA_LEN; 22898ce7a9e0SJulian Wiedmann else 22908ce7a9e0SJulian Wiedmann new_mtu = ETH_DATA_LEN - 8; /* allow for LLC + SNAP */ 22918ce7a9e0SJulian Wiedmann } 22928ce7a9e0SJulian Wiedmann 22938ce7a9e0SJulian Wiedmann dev->max_mtu = max_mtu; 22948ce7a9e0SJulian Wiedmann dev->mtu = min(new_mtu, max_mtu); 22958ce7a9e0SJulian Wiedmann rtnl_unlock(); 22968ce7a9e0SJulian Wiedmann return 0; 22974a71df50SFrank Blaschka } 22984a71df50SFrank Blaschka 2299cef6ff22SJulian Wiedmann static int qeth_get_mtu_outof_framesize(int framesize) 23004a71df50SFrank Blaschka { 23014a71df50SFrank Blaschka switch (framesize) { 23024a71df50SFrank Blaschka case 0x4000: 23034a71df50SFrank Blaschka return 8192; 23044a71df50SFrank Blaschka case 0x6000: 23054a71df50SFrank Blaschka return 16384; 23064a71df50SFrank Blaschka case 0xa000: 23074a71df50SFrank Blaschka return 32768; 23084a71df50SFrank Blaschka case 0xffff: 23094a71df50SFrank Blaschka return 57344; 23104a71df50SFrank Blaschka default: 23114a71df50SFrank Blaschka return 0; 23124a71df50SFrank Blaschka } 23134a71df50SFrank Blaschka } 23144a71df50SFrank Blaschka 23154a71df50SFrank Blaschka static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 23164a71df50SFrank Blaschka unsigned long data) 23174a71df50SFrank Blaschka { 23184a71df50SFrank Blaschka 23194a71df50SFrank Blaschka __u16 mtu, framesize; 23204a71df50SFrank Blaschka __u16 len; 23214a71df50SFrank Blaschka __u8 link_type; 23224a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23234a71df50SFrank Blaschka 2324d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenacb"); 23254a71df50SFrank Blaschka 23264a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 23274a71df50SFrank Blaschka memcpy(&card->token.ulp_filter_r, 23284a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data), 23294a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 23309853b97bSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) { 23314a71df50SFrank Blaschka memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); 23324a71df50SFrank Blaschka mtu = qeth_get_mtu_outof_framesize(framesize); 23334a71df50SFrank Blaschka } else { 23348ce7a9e0SJulian Wiedmann mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data); 23354a71df50SFrank Blaschka } 23368ce7a9e0SJulian Wiedmann *(u16 *)reply->param = mtu; 23374a71df50SFrank Blaschka 23384a71df50SFrank Blaschka memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); 23394a71df50SFrank Blaschka if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { 23404a71df50SFrank Blaschka memcpy(&link_type, 23414a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1); 23424a71df50SFrank Blaschka card->info.link_type = link_type; 23434a71df50SFrank Blaschka } else 23444a71df50SFrank Blaschka card->info.link_type = 0; 234501fc3e86SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "link%d", card->info.link_type); 2346d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 23474a71df50SFrank Blaschka return 0; 23484a71df50SFrank Blaschka } 23494a71df50SFrank Blaschka 235073657a3eSJulian Wiedmann static u8 qeth_mpc_select_prot_type(struct qeth_card *card) 235173657a3eSJulian Wiedmann { 235273657a3eSJulian Wiedmann if (IS_OSN(card)) 235373657a3eSJulian Wiedmann return QETH_PROT_OSN2; 235473657a3eSJulian Wiedmann return (card->options.layer2 == 1) ? QETH_PROT_LAYER2 : QETH_PROT_TCPIP; 235573657a3eSJulian Wiedmann } 235673657a3eSJulian Wiedmann 23574a71df50SFrank Blaschka static int qeth_ulp_enable(struct qeth_card *card) 23584a71df50SFrank Blaschka { 235973657a3eSJulian Wiedmann u8 prot_type = qeth_mpc_select_prot_type(card); 23604a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23618ce7a9e0SJulian Wiedmann u16 max_mtu; 236273657a3eSJulian Wiedmann int rc; 23634a71df50SFrank Blaschka 23644a71df50SFrank Blaschka /*FIXME: trace view callbacks*/ 2365d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenabl"); 23664a71df50SFrank Blaschka 23674a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 23684a71df50SFrank Blaschka memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); 23694a71df50SFrank Blaschka 237092d27209SJulian Wiedmann *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = (u8) card->dev->dev_port; 23714a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data), &prot_type, 1); 23724a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data), 23734a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 23744a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), 23754a71df50SFrank Blaschka &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); 23764a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, 23778ce7a9e0SJulian Wiedmann qeth_ulp_enable_cb, &max_mtu); 23788ce7a9e0SJulian Wiedmann if (rc) 23794a71df50SFrank Blaschka return rc; 23808ce7a9e0SJulian Wiedmann return qeth_update_max_mtu(card, max_mtu); 23814a71df50SFrank Blaschka } 23824a71df50SFrank Blaschka 23834a71df50SFrank Blaschka static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 23844a71df50SFrank Blaschka unsigned long data) 23854a71df50SFrank Blaschka { 23864a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23874a71df50SFrank Blaschka 2388d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpstpcb"); 23894a71df50SFrank Blaschka 23904a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 23914a71df50SFrank Blaschka memcpy(&card->token.ulp_connection_r, 23924a71df50SFrank Blaschka QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 23934a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 239465a1f898SUrsula Braun if (!strncmp("00S", QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 239565a1f898SUrsula Braun 3)) { 239665a1f898SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "olmlimit"); 239765a1f898SUrsula Braun dev_err(&card->gdev->dev, "A connection could not be " 239865a1f898SUrsula Braun "established because of an OLM limit\n"); 2399bbb822a8SUrsula Braun iob->rc = -EMLINK; 240065a1f898SUrsula Braun } 2401d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 24027bf9bcffSStefan Raspl return 0; 24034a71df50SFrank Blaschka } 24044a71df50SFrank Blaschka 24054a71df50SFrank Blaschka static int qeth_ulp_setup(struct qeth_card *card) 24064a71df50SFrank Blaschka { 24074a71df50SFrank Blaschka int rc; 24084a71df50SFrank Blaschka __u16 temp; 24094a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 24104a71df50SFrank Blaschka struct ccw_dev_id dev_id; 24114a71df50SFrank Blaschka 2412d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpsetup"); 24134a71df50SFrank Blaschka 24144a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 24154a71df50SFrank Blaschka memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE); 24164a71df50SFrank Blaschka 24174a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data), 24184a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 24194a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data), 24204a71df50SFrank Blaschka &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH); 24214a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data), 24224a71df50SFrank Blaschka &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH); 24234a71df50SFrank Blaschka 24244a71df50SFrank Blaschka ccw_device_get_id(CARD_DDEV(card), &dev_id); 24254a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2); 24264a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 24274a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2); 24284a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob, 24294a71df50SFrank Blaschka qeth_ulp_setup_cb, NULL); 24304a71df50SFrank Blaschka return rc; 24314a71df50SFrank Blaschka } 24324a71df50SFrank Blaschka 24330da9581dSEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx) 24340da9581dSEinar Lueck { 24350da9581dSEinar Lueck struct qeth_qdio_out_buffer *newbuf; 24360da9581dSEinar Lueck 24370da9581dSEinar Lueck newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC); 24383b346c18SJulian Wiedmann if (!newbuf) 24393b346c18SJulian Wiedmann return -ENOMEM; 24403b346c18SJulian Wiedmann 2441d445a4e2SSebastian Ott newbuf->buffer = q->qdio_bufs[bidx]; 24420da9581dSEinar Lueck skb_queue_head_init(&newbuf->skb_list); 24430da9581dSEinar Lueck lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); 24440da9581dSEinar Lueck newbuf->q = q; 24450da9581dSEinar Lueck newbuf->next_pending = q->bufs[bidx]; 24460da9581dSEinar Lueck atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); 24470da9581dSEinar Lueck q->bufs[bidx] = newbuf; 24483b346c18SJulian Wiedmann return 0; 24490da9581dSEinar Lueck } 24500da9581dSEinar Lueck 2451d445a4e2SSebastian Ott static void qeth_free_qdio_out_buf(struct qeth_qdio_out_q *q) 2452d445a4e2SSebastian Ott { 2453d445a4e2SSebastian Ott if (!q) 2454d445a4e2SSebastian Ott return; 2455d445a4e2SSebastian Ott 2456d445a4e2SSebastian Ott qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); 2457d445a4e2SSebastian Ott kfree(q); 2458d445a4e2SSebastian Ott } 2459d445a4e2SSebastian Ott 2460d445a4e2SSebastian Ott static struct qeth_qdio_out_q *qeth_alloc_qdio_out_buf(void) 2461d445a4e2SSebastian Ott { 2462d445a4e2SSebastian Ott struct qeth_qdio_out_q *q = kzalloc(sizeof(*q), GFP_KERNEL); 2463d445a4e2SSebastian Ott 2464d445a4e2SSebastian Ott if (!q) 2465d445a4e2SSebastian Ott return NULL; 2466d445a4e2SSebastian Ott 2467d445a4e2SSebastian Ott if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) { 2468d445a4e2SSebastian Ott kfree(q); 2469d445a4e2SSebastian Ott return NULL; 2470d445a4e2SSebastian Ott } 2471d445a4e2SSebastian Ott return q; 2472d445a4e2SSebastian Ott } 24730da9581dSEinar Lueck 24744a71df50SFrank Blaschka static int qeth_alloc_qdio_buffers(struct qeth_card *card) 24754a71df50SFrank Blaschka { 24764a71df50SFrank Blaschka int i, j; 24774a71df50SFrank Blaschka 2478d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "allcqdbf"); 24794a71df50SFrank Blaschka 24804a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED, 24814a71df50SFrank Blaschka QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED) 24824a71df50SFrank Blaschka return 0; 24834a71df50SFrank Blaschka 24844601ba6cSSebastian Ott QETH_DBF_TEXT(SETUP, 2, "inq"); 24854601ba6cSSebastian Ott card->qdio.in_q = qeth_alloc_qdio_queue(); 24864a71df50SFrank Blaschka if (!card->qdio.in_q) 24874a71df50SFrank Blaschka goto out_nomem; 24884601ba6cSSebastian Ott 24894a71df50SFrank Blaschka /* inbound buffer pool */ 24904a71df50SFrank Blaschka if (qeth_alloc_buffer_pool(card)) 24914a71df50SFrank Blaschka goto out_freeinq; 24920da9581dSEinar Lueck 24934a71df50SFrank Blaschka /* outbound */ 24944a71df50SFrank Blaschka card->qdio.out_qs = 24956396bb22SKees Cook kcalloc(card->qdio.no_out_queues, 24966396bb22SKees Cook sizeof(struct qeth_qdio_out_q *), 24976396bb22SKees Cook GFP_KERNEL); 24984a71df50SFrank Blaschka if (!card->qdio.out_qs) 24994a71df50SFrank Blaschka goto out_freepool; 25004a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 2501d445a4e2SSebastian Ott card->qdio.out_qs[i] = qeth_alloc_qdio_out_buf(); 25024a71df50SFrank Blaschka if (!card->qdio.out_qs[i]) 25034a71df50SFrank Blaschka goto out_freeoutq; 2504d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "outq %i", i); 2505d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *)); 25064a71df50SFrank Blaschka card->qdio.out_qs[i]->queue_no = i; 25074a71df50SFrank Blaschka /* give outbound qeth_qdio_buffers their qdio_buffers */ 25084a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 250918af5c17SStefan Raspl WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL); 25100da9581dSEinar Lueck if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j)) 25110da9581dSEinar Lueck goto out_freeoutqbufs; 25124a71df50SFrank Blaschka } 25134a71df50SFrank Blaschka } 25140da9581dSEinar Lueck 25150da9581dSEinar Lueck /* completion */ 25160da9581dSEinar Lueck if (qeth_alloc_cq(card)) 25170da9581dSEinar Lueck goto out_freeoutq; 25180da9581dSEinar Lueck 25194a71df50SFrank Blaschka return 0; 25204a71df50SFrank Blaschka 25210da9581dSEinar Lueck out_freeoutqbufs: 25220da9581dSEinar Lueck while (j > 0) { 25230da9581dSEinar Lueck --j; 25240da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, 25250da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]); 25260da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j] = NULL; 25270da9581dSEinar Lueck } 25284a71df50SFrank Blaschka out_freeoutq: 25290da9581dSEinar Lueck while (i > 0) { 2530d445a4e2SSebastian Ott qeth_free_qdio_out_buf(card->qdio.out_qs[--i]); 25310da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 25320da9581dSEinar Lueck } 25334a71df50SFrank Blaschka kfree(card->qdio.out_qs); 25344a71df50SFrank Blaschka card->qdio.out_qs = NULL; 25354a71df50SFrank Blaschka out_freepool: 25364a71df50SFrank Blaschka qeth_free_buffer_pool(card); 25374a71df50SFrank Blaschka out_freeinq: 25384601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.in_q); 25394a71df50SFrank Blaschka card->qdio.in_q = NULL; 25404a71df50SFrank Blaschka out_nomem: 25414a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 25424a71df50SFrank Blaschka return -ENOMEM; 25434a71df50SFrank Blaschka } 25444a71df50SFrank Blaschka 2545d445a4e2SSebastian Ott static void qeth_free_qdio_buffers(struct qeth_card *card) 2546d445a4e2SSebastian Ott { 2547d445a4e2SSebastian Ott int i, j; 2548d445a4e2SSebastian Ott 2549d445a4e2SSebastian Ott if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) == 2550d445a4e2SSebastian Ott QETH_QDIO_UNINITIALIZED) 2551d445a4e2SSebastian Ott return; 2552d445a4e2SSebastian Ott 2553d445a4e2SSebastian Ott qeth_free_cq(card); 2554d445a4e2SSebastian Ott cancel_delayed_work_sync(&card->buffer_reclaim_work); 2555d445a4e2SSebastian Ott for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 2556d445a4e2SSebastian Ott if (card->qdio.in_q->bufs[j].rx_skb) 2557d445a4e2SSebastian Ott dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); 2558d445a4e2SSebastian Ott } 2559d445a4e2SSebastian Ott qeth_free_qdio_queue(card->qdio.in_q); 2560d445a4e2SSebastian Ott card->qdio.in_q = NULL; 2561d445a4e2SSebastian Ott /* inbound buffer pool */ 2562d445a4e2SSebastian Ott qeth_free_buffer_pool(card); 2563d445a4e2SSebastian Ott /* free outbound qdio_qs */ 2564d445a4e2SSebastian Ott if (card->qdio.out_qs) { 2565d445a4e2SSebastian Ott for (i = 0; i < card->qdio.no_out_queues; ++i) { 2566d445a4e2SSebastian Ott qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 2567d445a4e2SSebastian Ott qeth_free_qdio_out_buf(card->qdio.out_qs[i]); 2568d445a4e2SSebastian Ott } 2569d445a4e2SSebastian Ott kfree(card->qdio.out_qs); 2570d445a4e2SSebastian Ott card->qdio.out_qs = NULL; 2571d445a4e2SSebastian Ott } 2572d445a4e2SSebastian Ott } 2573d445a4e2SSebastian Ott 25744a71df50SFrank Blaschka static void qeth_create_qib_param_field(struct qeth_card *card, 25754a71df50SFrank Blaschka char *param_field) 25764a71df50SFrank Blaschka { 25774a71df50SFrank Blaschka 25784a71df50SFrank Blaschka param_field[0] = _ascebc['P']; 25794a71df50SFrank Blaschka param_field[1] = _ascebc['C']; 25804a71df50SFrank Blaschka param_field[2] = _ascebc['I']; 25814a71df50SFrank Blaschka param_field[3] = _ascebc['T']; 25824a71df50SFrank Blaschka *((unsigned int *) (¶m_field[4])) = QETH_PCI_THRESHOLD_A(card); 25834a71df50SFrank Blaschka *((unsigned int *) (¶m_field[8])) = QETH_PCI_THRESHOLD_B(card); 25844a71df50SFrank Blaschka *((unsigned int *) (¶m_field[12])) = QETH_PCI_TIMER_VALUE(card); 25854a71df50SFrank Blaschka } 25864a71df50SFrank Blaschka 25874a71df50SFrank Blaschka static void qeth_create_qib_param_field_blkt(struct qeth_card *card, 25884a71df50SFrank Blaschka char *param_field) 25894a71df50SFrank Blaschka { 25904a71df50SFrank Blaschka param_field[16] = _ascebc['B']; 25914a71df50SFrank Blaschka param_field[17] = _ascebc['L']; 25924a71df50SFrank Blaschka param_field[18] = _ascebc['K']; 25934a71df50SFrank Blaschka param_field[19] = _ascebc['T']; 25944a71df50SFrank Blaschka *((unsigned int *) (¶m_field[20])) = card->info.blkt.time_total; 25954a71df50SFrank Blaschka *((unsigned int *) (¶m_field[24])) = card->info.blkt.inter_packet; 25964a71df50SFrank Blaschka *((unsigned int *) (¶m_field[28])) = 25974a71df50SFrank Blaschka card->info.blkt.inter_packet_jumbo; 25984a71df50SFrank Blaschka } 25994a71df50SFrank Blaschka 26004a71df50SFrank Blaschka static int qeth_qdio_activate(struct qeth_card *card) 26014a71df50SFrank Blaschka { 2602d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 3, "qdioact"); 2603779e6e1cSJan Glauber return qdio_activate(CARD_DDEV(card)); 26044a71df50SFrank Blaschka } 26054a71df50SFrank Blaschka 26064a71df50SFrank Blaschka static int qeth_dm_act(struct qeth_card *card) 26074a71df50SFrank Blaschka { 26084a71df50SFrank Blaschka int rc; 26094a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 26104a71df50SFrank Blaschka 2611d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "dmact"); 26124a71df50SFrank Blaschka 26134a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 26144a71df50SFrank Blaschka memcpy(iob->data, DM_ACT, DM_ACT_SIZE); 26154a71df50SFrank Blaschka 26164a71df50SFrank Blaschka memcpy(QETH_DM_ACT_DEST_ADDR(iob->data), 26174a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 26184a71df50SFrank Blaschka memcpy(QETH_DM_ACT_CONNECTION_TOKEN(iob->data), 26194a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 26204a71df50SFrank Blaschka rc = qeth_send_control_data(card, DM_ACT_SIZE, iob, NULL, NULL); 26214a71df50SFrank Blaschka return rc; 26224a71df50SFrank Blaschka } 26234a71df50SFrank Blaschka 26244a71df50SFrank Blaschka static int qeth_mpc_initialize(struct qeth_card *card) 26254a71df50SFrank Blaschka { 26264a71df50SFrank Blaschka int rc; 26274a71df50SFrank Blaschka 2628d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "mpcinit"); 26294a71df50SFrank Blaschka 26304a71df50SFrank Blaschka rc = qeth_issue_next_read(card); 26314a71df50SFrank Blaschka if (rc) { 2632d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 26334a71df50SFrank Blaschka return rc; 26344a71df50SFrank Blaschka } 26354a71df50SFrank Blaschka rc = qeth_cm_enable(card); 26364a71df50SFrank Blaschka if (rc) { 2637d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 26384a71df50SFrank Blaschka goto out_qdio; 26394a71df50SFrank Blaschka } 26404a71df50SFrank Blaschka rc = qeth_cm_setup(card); 26414a71df50SFrank Blaschka if (rc) { 2642d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 26434a71df50SFrank Blaschka goto out_qdio; 26444a71df50SFrank Blaschka } 26454a71df50SFrank Blaschka rc = qeth_ulp_enable(card); 26464a71df50SFrank Blaschka if (rc) { 2647d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 26484a71df50SFrank Blaschka goto out_qdio; 26494a71df50SFrank Blaschka } 26504a71df50SFrank Blaschka rc = qeth_ulp_setup(card); 26514a71df50SFrank Blaschka if (rc) { 2652d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 26534a71df50SFrank Blaschka goto out_qdio; 26544a71df50SFrank Blaschka } 26554a71df50SFrank Blaschka rc = qeth_alloc_qdio_buffers(card); 26564a71df50SFrank Blaschka if (rc) { 2657d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 26584a71df50SFrank Blaschka goto out_qdio; 26594a71df50SFrank Blaschka } 26604a71df50SFrank Blaschka rc = qeth_qdio_establish(card); 26614a71df50SFrank Blaschka if (rc) { 2662d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 26634a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 26644a71df50SFrank Blaschka goto out_qdio; 26654a71df50SFrank Blaschka } 26664a71df50SFrank Blaschka rc = qeth_qdio_activate(card); 26674a71df50SFrank Blaschka if (rc) { 2668d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); 26694a71df50SFrank Blaschka goto out_qdio; 26704a71df50SFrank Blaschka } 26714a71df50SFrank Blaschka rc = qeth_dm_act(card); 26724a71df50SFrank Blaschka if (rc) { 2673d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); 26744a71df50SFrank Blaschka goto out_qdio; 26754a71df50SFrank Blaschka } 26764a71df50SFrank Blaschka 26774a71df50SFrank Blaschka return 0; 26784a71df50SFrank Blaschka out_qdio: 26794a71df50SFrank Blaschka qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 268022ae2790SUrsula Braun qdio_free(CARD_DDEV(card)); 26814a71df50SFrank Blaschka return rc; 26824a71df50SFrank Blaschka } 26834a71df50SFrank Blaschka 26844a71df50SFrank Blaschka void qeth_print_status_message(struct qeth_card *card) 26854a71df50SFrank Blaschka { 26864a71df50SFrank Blaschka switch (card->info.type) { 26875113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 26885113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 26895113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 26904a71df50SFrank Blaschka /* VM will use a non-zero first character 26914a71df50SFrank Blaschka * to indicate a HiperSockets like reporting 26924a71df50SFrank Blaschka * of the level OSA sets the first character to zero 26934a71df50SFrank Blaschka * */ 26944a71df50SFrank Blaschka if (!card->info.mcl_level[0]) { 26954a71df50SFrank Blaschka sprintf(card->info.mcl_level, "%02x%02x", 26964a71df50SFrank Blaschka card->info.mcl_level[2], 26974a71df50SFrank Blaschka card->info.mcl_level[3]); 26984a71df50SFrank Blaschka break; 26994a71df50SFrank Blaschka } 27004a71df50SFrank Blaschka /* fallthrough */ 27014a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 2702906f1f07SKlaus-Dieter Wacker if ((card->info.guestlan) || 2703906f1f07SKlaus-Dieter Wacker (card->info.mcl_level[0] & 0x80)) { 27044a71df50SFrank Blaschka card->info.mcl_level[0] = (char) _ebcasc[(__u8) 27054a71df50SFrank Blaschka card->info.mcl_level[0]]; 27064a71df50SFrank Blaschka card->info.mcl_level[1] = (char) _ebcasc[(__u8) 27074a71df50SFrank Blaschka card->info.mcl_level[1]]; 27084a71df50SFrank Blaschka card->info.mcl_level[2] = (char) _ebcasc[(__u8) 27094a71df50SFrank Blaschka card->info.mcl_level[2]]; 27104a71df50SFrank Blaschka card->info.mcl_level[3] = (char) _ebcasc[(__u8) 27114a71df50SFrank Blaschka card->info.mcl_level[3]]; 27124a71df50SFrank Blaschka card->info.mcl_level[QETH_MCL_LENGTH] = 0; 27134a71df50SFrank Blaschka } 27144a71df50SFrank Blaschka break; 27154a71df50SFrank Blaschka default: 27164a71df50SFrank Blaschka memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); 27174a71df50SFrank Blaschka } 2718239ff408SUrsula Braun dev_info(&card->gdev->dev, 2719239ff408SUrsula Braun "Device is a%s card%s%s%s\nwith link type %s.\n", 2720239ff408SUrsula Braun qeth_get_cardname(card), 2721239ff408SUrsula Braun (card->info.mcl_level[0]) ? " (level: " : "", 2722239ff408SUrsula Braun (card->info.mcl_level[0]) ? card->info.mcl_level : "", 2723239ff408SUrsula Braun (card->info.mcl_level[0]) ? ")" : "", 2724239ff408SUrsula Braun qeth_get_cardname_short(card)); 27254a71df50SFrank Blaschka } 27264a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_print_status_message); 27274a71df50SFrank Blaschka 27284a71df50SFrank Blaschka static void qeth_initialize_working_pool_list(struct qeth_card *card) 27294a71df50SFrank Blaschka { 27304a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 27314a71df50SFrank Blaschka 2732847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "inwrklst"); 27334a71df50SFrank Blaschka 27344a71df50SFrank Blaschka list_for_each_entry(entry, 27354a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list) { 27364a71df50SFrank Blaschka qeth_put_buffer_pool_entry(card, entry); 27374a71df50SFrank Blaschka } 27384a71df50SFrank Blaschka } 27394a71df50SFrank Blaschka 2740cef6ff22SJulian Wiedmann static struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry( 27414a71df50SFrank Blaschka struct qeth_card *card) 27424a71df50SFrank Blaschka { 27434a71df50SFrank Blaschka struct list_head *plh; 27444a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 27454a71df50SFrank Blaschka int i, free; 27464a71df50SFrank Blaschka struct page *page; 27474a71df50SFrank Blaschka 27484a71df50SFrank Blaschka if (list_empty(&card->qdio.in_buf_pool.entry_list)) 27494a71df50SFrank Blaschka return NULL; 27504a71df50SFrank Blaschka 27514a71df50SFrank Blaschka list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { 27524a71df50SFrank Blaschka entry = list_entry(plh, struct qeth_buffer_pool_entry, list); 27534a71df50SFrank Blaschka free = 1; 27544a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27554a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 27564a71df50SFrank Blaschka free = 0; 27574a71df50SFrank Blaschka break; 27584a71df50SFrank Blaschka } 27594a71df50SFrank Blaschka } 27604a71df50SFrank Blaschka if (free) { 27614a71df50SFrank Blaschka list_del_init(&entry->list); 27624a71df50SFrank Blaschka return entry; 27634a71df50SFrank Blaschka } 27644a71df50SFrank Blaschka } 27654a71df50SFrank Blaschka 27664a71df50SFrank Blaschka /* no free buffer in pool so take first one and swap pages */ 27674a71df50SFrank Blaschka entry = list_entry(card->qdio.in_buf_pool.entry_list.next, 27684a71df50SFrank Blaschka struct qeth_buffer_pool_entry, list); 27694a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27704a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 2771508b3c4fSUrsula Braun page = alloc_page(GFP_ATOMIC); 27724a71df50SFrank Blaschka if (!page) { 27734a71df50SFrank Blaschka return NULL; 27744a71df50SFrank Blaschka } else { 27754a71df50SFrank Blaschka free_page((unsigned long)entry->elements[i]); 27764a71df50SFrank Blaschka entry->elements[i] = page_address(page); 27774a71df50SFrank Blaschka if (card->options.performance_stats) 27784a71df50SFrank Blaschka card->perf_stats.sg_alloc_page_rx++; 27794a71df50SFrank Blaschka } 27804a71df50SFrank Blaschka } 27814a71df50SFrank Blaschka } 27824a71df50SFrank Blaschka list_del_init(&entry->list); 27834a71df50SFrank Blaschka return entry; 27844a71df50SFrank Blaschka } 27854a71df50SFrank Blaschka 27864a71df50SFrank Blaschka static int qeth_init_input_buffer(struct qeth_card *card, 27874a71df50SFrank Blaschka struct qeth_qdio_buffer *buf) 27884a71df50SFrank Blaschka { 27894a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 27904a71df50SFrank Blaschka int i; 27914a71df50SFrank Blaschka 2792b3332930SFrank Blaschka if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) { 279337cf05d2SJulian Wiedmann buf->rx_skb = netdev_alloc_skb(card->dev, 279437cf05d2SJulian Wiedmann QETH_RX_PULL_LEN + ETH_HLEN); 2795b3332930SFrank Blaschka if (!buf->rx_skb) 2796b3332930SFrank Blaschka return 1; 2797b3332930SFrank Blaschka } 2798b3332930SFrank Blaschka 27994a71df50SFrank Blaschka pool_entry = qeth_find_free_buffer_pool_entry(card); 28004a71df50SFrank Blaschka if (!pool_entry) 28014a71df50SFrank Blaschka return 1; 28024a71df50SFrank Blaschka 28034a71df50SFrank Blaschka /* 28044a71df50SFrank Blaschka * since the buffer is accessed only from the input_tasklet 28054a71df50SFrank Blaschka * there shouldn't be a need to synchronize; also, since we use 28064a71df50SFrank Blaschka * the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off 28074a71df50SFrank Blaschka * buffers 28084a71df50SFrank Blaschka */ 28094a71df50SFrank Blaschka 28104a71df50SFrank Blaschka buf->pool_entry = pool_entry; 28114a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 28124a71df50SFrank Blaschka buf->buffer->element[i].length = PAGE_SIZE; 28134a71df50SFrank Blaschka buf->buffer->element[i].addr = pool_entry->elements[i]; 28144a71df50SFrank Blaschka if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1) 28153ec90878SJan Glauber buf->buffer->element[i].eflags = SBAL_EFLAGS_LAST_ENTRY; 28164a71df50SFrank Blaschka else 28173ec90878SJan Glauber buf->buffer->element[i].eflags = 0; 28183ec90878SJan Glauber buf->buffer->element[i].sflags = 0; 28194a71df50SFrank Blaschka } 28204a71df50SFrank Blaschka return 0; 28214a71df50SFrank Blaschka } 28224a71df50SFrank Blaschka 28234a71df50SFrank Blaschka int qeth_init_qdio_queues(struct qeth_card *card) 28244a71df50SFrank Blaschka { 28254a71df50SFrank Blaschka int i, j; 28264a71df50SFrank Blaschka int rc; 28274a71df50SFrank Blaschka 2828d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "initqdqs"); 28294a71df50SFrank Blaschka 28304a71df50SFrank Blaschka /* inbound queue */ 28311b45c80bSJulian Wiedmann qdio_reset_buffers(card->qdio.in_q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); 28321b45c80bSJulian Wiedmann memset(&card->rx, 0, sizeof(struct qeth_rx)); 28334a71df50SFrank Blaschka qeth_initialize_working_pool_list(card); 28344a71df50SFrank Blaschka /*give only as many buffers to hardware as we have buffer pool entries*/ 28354a71df50SFrank Blaschka for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++i) 28364a71df50SFrank Blaschka qeth_init_input_buffer(card, &card->qdio.in_q->bufs[i]); 28374a71df50SFrank Blaschka card->qdio.in_q->next_buf_to_init = 28384a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 1; 28394a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, 2840779e6e1cSJan Glauber card->qdio.in_buf_pool.buf_count - 1); 28414a71df50SFrank Blaschka if (rc) { 2842d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 28434a71df50SFrank Blaschka return rc; 28444a71df50SFrank Blaschka } 28450da9581dSEinar Lueck 28460da9581dSEinar Lueck /* completion */ 28470da9581dSEinar Lueck rc = qeth_cq_init(card); 28480da9581dSEinar Lueck if (rc) { 28490da9581dSEinar Lueck return rc; 28500da9581dSEinar Lueck } 28510da9581dSEinar Lueck 28524a71df50SFrank Blaschka /* outbound queue */ 28534a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 2854d445a4e2SSebastian Ott qdio_reset_buffers(card->qdio.out_qs[i]->qdio_bufs, 2855d445a4e2SSebastian Ott QDIO_MAX_BUFFERS_PER_Q); 28564a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 28574a71df50SFrank Blaschka qeth_clear_output_buffer(card->qdio.out_qs[i], 28583b346c18SJulian Wiedmann card->qdio.out_qs[i]->bufs[j]); 28594a71df50SFrank Blaschka } 28604a71df50SFrank Blaschka card->qdio.out_qs[i]->card = card; 28614a71df50SFrank Blaschka card->qdio.out_qs[i]->next_buf_to_fill = 0; 28624a71df50SFrank Blaschka card->qdio.out_qs[i]->do_pack = 0; 28634a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->used_buffers, 0); 28644a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->set_pci_flags_count, 0); 28654a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->state, 28664a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 28674a71df50SFrank Blaschka } 28684a71df50SFrank Blaschka return 0; 28694a71df50SFrank Blaschka } 28704a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_init_qdio_queues); 28714a71df50SFrank Blaschka 2872cef6ff22SJulian Wiedmann static __u8 qeth_get_ipa_adp_type(enum qeth_link_types link_type) 28734a71df50SFrank Blaschka { 28744a71df50SFrank Blaschka switch (link_type) { 28754a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 28764a71df50SFrank Blaschka return 2; 28774a71df50SFrank Blaschka default: 28784a71df50SFrank Blaschka return 1; 28794a71df50SFrank Blaschka } 28804a71df50SFrank Blaschka } 28814a71df50SFrank Blaschka 28824a71df50SFrank Blaschka static void qeth_fill_ipacmd_header(struct qeth_card *card, 28834a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd, __u8 command, 28844a71df50SFrank Blaschka enum qeth_prot_versions prot) 28854a71df50SFrank Blaschka { 28864a71df50SFrank Blaschka memset(cmd, 0, sizeof(struct qeth_ipa_cmd)); 28874a71df50SFrank Blaschka cmd->hdr.command = command; 28884a71df50SFrank Blaschka cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; 2889d22ffb5aSJulian Wiedmann /* cmd->hdr.seqno is set by qeth_send_control_data() */ 28904a71df50SFrank Blaschka cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); 289192d27209SJulian Wiedmann cmd->hdr.rel_adapter_no = (u8) card->dev->dev_port; 28924a71df50SFrank Blaschka if (card->options.layer2) 28934a71df50SFrank Blaschka cmd->hdr.prim_version_no = 2; 28944a71df50SFrank Blaschka else 28954a71df50SFrank Blaschka cmd->hdr.prim_version_no = 1; 28964a71df50SFrank Blaschka cmd->hdr.param_count = 1; 28974a71df50SFrank Blaschka cmd->hdr.prot_version = prot; 28984a71df50SFrank Blaschka cmd->hdr.ipa_supported = 0; 28994a71df50SFrank Blaschka cmd->hdr.ipa_enabled = 0; 29004a71df50SFrank Blaschka } 29014a71df50SFrank Blaschka 29024a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card, 29034a71df50SFrank Blaschka enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot) 29044a71df50SFrank Blaschka { 29054a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29064a71df50SFrank Blaschka 29071aec42bcSThomas Richter iob = qeth_get_buffer(&card->write); 29081aec42bcSThomas Richter if (iob) { 2909ff5caa7aSJulian Wiedmann qeth_fill_ipacmd_header(card, __ipa_cmd(iob), ipacmd, prot); 29101aec42bcSThomas Richter } else { 29111aec42bcSThomas Richter dev_warn(&card->gdev->dev, 29121aec42bcSThomas Richter "The qeth driver ran out of channel command buffers\n"); 29131aec42bcSThomas Richter QETH_DBF_MESSAGE(1, "%s The qeth driver ran out of channel command buffers", 29141aec42bcSThomas Richter dev_name(&card->gdev->dev)); 29151aec42bcSThomas Richter } 29164a71df50SFrank Blaschka 29174a71df50SFrank Blaschka return iob; 29184a71df50SFrank Blaschka } 29194a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer); 29204a71df50SFrank Blaschka 292173657a3eSJulian Wiedmann void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob) 29224a71df50SFrank Blaschka { 292373657a3eSJulian Wiedmann u8 prot_type = qeth_mpc_select_prot_type(card); 292473657a3eSJulian Wiedmann 29254a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 29264a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1); 29274a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 29284a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 29294a71df50SFrank Blaschka } 29304a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd); 29314a71df50SFrank Blaschka 2932efbbc1d5SEugene Crosser /** 2933efbbc1d5SEugene Crosser * qeth_send_ipa_cmd() - send an IPA command 2934efbbc1d5SEugene Crosser * 2935efbbc1d5SEugene Crosser * See qeth_send_control_data() for explanation of the arguments. 2936efbbc1d5SEugene Crosser */ 2937efbbc1d5SEugene Crosser 29384a71df50SFrank Blaschka int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, 29394a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply*, 29404a71df50SFrank Blaschka unsigned long), 29414a71df50SFrank Blaschka void *reply_param) 29424a71df50SFrank Blaschka { 29434a71df50SFrank Blaschka int rc; 29444a71df50SFrank Blaschka 2945847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendipa"); 294673657a3eSJulian Wiedmann qeth_prepare_ipa_cmd(card, iob); 2947d11ba0c4SPeter Tiedemann rc = qeth_send_control_data(card, IPA_CMD_LENGTH, 2948d11ba0c4SPeter Tiedemann iob, reply_cb, reply_param); 2949908abbb5SUrsula Braun if (rc == -ETIME) { 2950908abbb5SUrsula Braun qeth_clear_ipacmd_list(card); 2951908abbb5SUrsula Braun qeth_schedule_recovery(card); 2952908abbb5SUrsula Braun } 29534a71df50SFrank Blaschka return rc; 29544a71df50SFrank Blaschka } 29554a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd); 29564a71df50SFrank Blaschka 295710340510SJulian Wiedmann static int qeth_send_startlan(struct qeth_card *card) 29584a71df50SFrank Blaschka { 29594a71df50SFrank Blaschka int rc; 29604a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29614a71df50SFrank Blaschka 2962d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "strtlan"); 29634a71df50SFrank Blaschka 296470919e23SUrsula Braun iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0); 29651aec42bcSThomas Richter if (!iob) 29661aec42bcSThomas Richter return -ENOMEM; 296770919e23SUrsula Braun rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); 29684a71df50SFrank Blaschka return rc; 29694a71df50SFrank Blaschka } 29704a71df50SFrank Blaschka 2971686c97eeSJulian Wiedmann static int qeth_setadpparms_inspect_rc(struct qeth_ipa_cmd *cmd) 29724a71df50SFrank Blaschka { 2973686c97eeSJulian Wiedmann if (!cmd->hdr.return_code) 29744a71df50SFrank Blaschka cmd->hdr.return_code = 29754a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 2976686c97eeSJulian Wiedmann return cmd->hdr.return_code; 29774a71df50SFrank Blaschka } 29784a71df50SFrank Blaschka 29794a71df50SFrank Blaschka static int qeth_query_setadapterparms_cb(struct qeth_card *card, 29804a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 29814a71df50SFrank Blaschka { 2982686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 29834a71df50SFrank Blaschka 2984847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "quyadpcb"); 2985686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) 2986686c97eeSJulian Wiedmann return 0; 29874a71df50SFrank Blaschka 29885113fec0SUrsula Braun if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) { 29894a71df50SFrank Blaschka card->info.link_type = 29904a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.lan_type; 29915113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lnk %d", card->info.link_type); 29925113fec0SUrsula Braun } 29934a71df50SFrank Blaschka card->options.adp.supported_funcs = 29944a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; 2995686c97eeSJulian Wiedmann return 0; 29964a71df50SFrank Blaschka } 29974a71df50SFrank Blaschka 2998eb3fb0baSStefan Raspl static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, 29994a71df50SFrank Blaschka __u32 command, __u32 cmdlen) 30004a71df50SFrank Blaschka { 30014a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 30024a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 30034a71df50SFrank Blaschka 30044a71df50SFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETADAPTERPARMS, 30054a71df50SFrank Blaschka QETH_PROT_IPV4); 30061aec42bcSThomas Richter if (iob) { 3007ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 30084a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.cmdlength = cmdlen; 30094a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.command_code = command; 30104a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total = 1; 30114a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no = 1; 30121aec42bcSThomas Richter } 30134a71df50SFrank Blaschka 30144a71df50SFrank Blaschka return iob; 30154a71df50SFrank Blaschka } 30164a71df50SFrank Blaschka 301709960b3aSJulian Wiedmann static int qeth_query_setadapterparms(struct qeth_card *card) 30184a71df50SFrank Blaschka { 30194a71df50SFrank Blaschka int rc; 30204a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 30214a71df50SFrank Blaschka 3022847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "queryadp"); 30234a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED, 30244a71df50SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms)); 30251aec42bcSThomas Richter if (!iob) 30261aec42bcSThomas Richter return -ENOMEM; 30274a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); 30284a71df50SFrank Blaschka return rc; 30294a71df50SFrank Blaschka } 30304a71df50SFrank Blaschka 30311da74b1cSFrank Blaschka static int qeth_query_ipassists_cb(struct qeth_card *card, 30321da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 30331da74b1cSFrank Blaschka { 30341da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 30351da74b1cSFrank Blaschka 30361da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qipasscb"); 30371da74b1cSFrank Blaschka 30381da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 3039a134884aSStefan Raspl 3040a134884aSStefan Raspl switch (cmd->hdr.return_code) { 3041a134884aSStefan Raspl case IPA_RC_NOTSUPP: 3042a134884aSStefan Raspl case IPA_RC_L2_UNSUPPORTED_CMD: 3043a134884aSStefan Raspl QETH_DBF_TEXT(SETUP, 2, "ipaunsup"); 3044a134884aSStefan Raspl card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; 3045a134884aSStefan Raspl card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; 3046a134884aSStefan Raspl return -0; 3047a134884aSStefan Raspl default: 3048a134884aSStefan Raspl if (cmd->hdr.return_code) { 3049a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled " 3050a134884aSStefan Raspl "rc=%d\n", 3051a134884aSStefan Raspl dev_name(&card->gdev->dev), 3052a134884aSStefan Raspl cmd->hdr.return_code); 3053a134884aSStefan Raspl return 0; 3054a134884aSStefan Raspl } 3055a134884aSStefan Raspl } 3056a134884aSStefan Raspl 30571da74b1cSFrank Blaschka if (cmd->hdr.prot_version == QETH_PROT_IPV4) { 30581da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; 30591da74b1cSFrank Blaschka card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; 3060a134884aSStefan Raspl } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { 30611da74b1cSFrank Blaschka card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; 30621da74b1cSFrank Blaschka card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; 3063a134884aSStefan Raspl } else 3064a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected" 3065a134884aSStefan Raspl "\n", dev_name(&card->gdev->dev)); 30661da74b1cSFrank Blaschka return 0; 30671da74b1cSFrank Blaschka } 30681da74b1cSFrank Blaschka 306909960b3aSJulian Wiedmann static int qeth_query_ipassists(struct qeth_card *card, 307009960b3aSJulian Wiedmann enum qeth_prot_versions prot) 30711da74b1cSFrank Blaschka { 30721da74b1cSFrank Blaschka int rc; 30731da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 30741da74b1cSFrank Blaschka 30751da74b1cSFrank Blaschka QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); 30761da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); 30771aec42bcSThomas Richter if (!iob) 30781aec42bcSThomas Richter return -ENOMEM; 30791da74b1cSFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); 30801da74b1cSFrank Blaschka return rc; 30811da74b1cSFrank Blaschka } 30821da74b1cSFrank Blaschka 308345cbb2e4SStefan Raspl static int qeth_query_switch_attributes_cb(struct qeth_card *card, 308445cbb2e4SStefan Raspl struct qeth_reply *reply, unsigned long data) 308545cbb2e4SStefan Raspl { 3086686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 308745cbb2e4SStefan Raspl struct qeth_query_switch_attributes *attrs; 3088686c97eeSJulian Wiedmann struct qeth_switch_info *sw_info; 308945cbb2e4SStefan Raspl 309045cbb2e4SStefan Raspl QETH_CARD_TEXT(card, 2, "qswiatcb"); 3091686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) 3092686c97eeSJulian Wiedmann return 0; 3093686c97eeSJulian Wiedmann 309445cbb2e4SStefan Raspl sw_info = (struct qeth_switch_info *)reply->param; 309545cbb2e4SStefan Raspl attrs = &cmd->data.setadapterparms.data.query_switch_attributes; 309645cbb2e4SStefan Raspl sw_info->capabilities = attrs->capabilities; 309745cbb2e4SStefan Raspl sw_info->settings = attrs->settings; 309845cbb2e4SStefan Raspl QETH_CARD_TEXT_(card, 2, "%04x%04x", sw_info->capabilities, 309945cbb2e4SStefan Raspl sw_info->settings); 310045cbb2e4SStefan Raspl return 0; 310145cbb2e4SStefan Raspl } 310245cbb2e4SStefan Raspl 310345cbb2e4SStefan Raspl int qeth_query_switch_attributes(struct qeth_card *card, 310445cbb2e4SStefan Raspl struct qeth_switch_info *sw_info) 310545cbb2e4SStefan Raspl { 310645cbb2e4SStefan Raspl struct qeth_cmd_buffer *iob; 310745cbb2e4SStefan Raspl 310845cbb2e4SStefan Raspl QETH_CARD_TEXT(card, 2, "qswiattr"); 310945cbb2e4SStefan Raspl if (!qeth_adp_supported(card, IPA_SETADP_QUERY_SWITCH_ATTRIBUTES)) 311045cbb2e4SStefan Raspl return -EOPNOTSUPP; 311145cbb2e4SStefan Raspl if (!netif_carrier_ok(card->dev)) 311245cbb2e4SStefan Raspl return -ENOMEDIUM; 311345cbb2e4SStefan Raspl iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_SWITCH_ATTRIBUTES, 311445cbb2e4SStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr)); 31151aec42bcSThomas Richter if (!iob) 31161aec42bcSThomas Richter return -ENOMEM; 311745cbb2e4SStefan Raspl return qeth_send_ipa_cmd(card, iob, 311845cbb2e4SStefan Raspl qeth_query_switch_attributes_cb, sw_info); 311945cbb2e4SStefan Raspl } 312045cbb2e4SStefan Raspl 31211da74b1cSFrank Blaschka static int qeth_query_setdiagass_cb(struct qeth_card *card, 31221da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 31231da74b1cSFrank Blaschka { 31241da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31251da74b1cSFrank Blaschka __u16 rc; 31261da74b1cSFrank Blaschka 31271da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 31281da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 31291da74b1cSFrank Blaschka if (rc) 31301da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "diagq:%x", rc); 31311da74b1cSFrank Blaschka else 31321da74b1cSFrank Blaschka card->info.diagass_support = cmd->data.diagass.ext; 31331da74b1cSFrank Blaschka return 0; 31341da74b1cSFrank Blaschka } 31351da74b1cSFrank Blaschka 31361da74b1cSFrank Blaschka static int qeth_query_setdiagass(struct qeth_card *card) 31371da74b1cSFrank Blaschka { 31381da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 31391da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31401da74b1cSFrank Blaschka 31411da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qdiagass"); 31421da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 31431aec42bcSThomas Richter if (!iob) 31441aec42bcSThomas Richter return -ENOMEM; 3145ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 31461da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 16; 31471da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY; 31481da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL); 31491da74b1cSFrank Blaschka } 31501da74b1cSFrank Blaschka 31511da74b1cSFrank Blaschka static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) 31521da74b1cSFrank Blaschka { 31531da74b1cSFrank Blaschka unsigned long info = get_zeroed_page(GFP_KERNEL); 31541da74b1cSFrank Blaschka struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 31551da74b1cSFrank Blaschka struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; 31561da74b1cSFrank Blaschka struct ccw_dev_id ccwid; 3157caf757c6SHeiko Carstens int level; 31581da74b1cSFrank Blaschka 31591da74b1cSFrank Blaschka tid->chpid = card->info.chpid; 31601da74b1cSFrank Blaschka ccw_device_get_id(CARD_RDEV(card), &ccwid); 31611da74b1cSFrank Blaschka tid->ssid = ccwid.ssid; 31621da74b1cSFrank Blaschka tid->devno = ccwid.devno; 31631da74b1cSFrank Blaschka if (!info) 31641da74b1cSFrank Blaschka return; 3165caf757c6SHeiko Carstens level = stsi(NULL, 0, 0, 0); 3166caf757c6SHeiko Carstens if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0)) 31671da74b1cSFrank Blaschka tid->lparnr = info222->lpar_number; 3168caf757c6SHeiko Carstens if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) { 31691da74b1cSFrank Blaschka EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); 31701da74b1cSFrank Blaschka memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); 31711da74b1cSFrank Blaschka } 31721da74b1cSFrank Blaschka free_page(info); 31731da74b1cSFrank Blaschka return; 31741da74b1cSFrank Blaschka } 31751da74b1cSFrank Blaschka 31761da74b1cSFrank Blaschka static int qeth_hw_trap_cb(struct qeth_card *card, 31771da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 31781da74b1cSFrank Blaschka { 31791da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31801da74b1cSFrank Blaschka __u16 rc; 31811da74b1cSFrank Blaschka 31821da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 31831da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 31841da74b1cSFrank Blaschka if (rc) 31851da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "trapc:%x", rc); 31861da74b1cSFrank Blaschka return 0; 31871da74b1cSFrank Blaschka } 31881da74b1cSFrank Blaschka 31891da74b1cSFrank Blaschka int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) 31901da74b1cSFrank Blaschka { 31911da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 31921da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31931da74b1cSFrank Blaschka 31941da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "diagtrap"); 31951da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 31961aec42bcSThomas Richter if (!iob) 31971aec42bcSThomas Richter return -ENOMEM; 3198ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 31991da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 80; 32001da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP; 32011da74b1cSFrank Blaschka cmd->data.diagass.type = 1; 32021da74b1cSFrank Blaschka cmd->data.diagass.action = action; 32031da74b1cSFrank Blaschka switch (action) { 32041da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_ARM: 32051da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0003; 32061da74b1cSFrank Blaschka cmd->data.diagass.ext = 0x00010000 + 32071da74b1cSFrank Blaschka sizeof(struct qeth_trap_id); 32081da74b1cSFrank Blaschka qeth_get_trap_id(card, 32091da74b1cSFrank Blaschka (struct qeth_trap_id *)cmd->data.diagass.cdata); 32101da74b1cSFrank Blaschka break; 32111da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_DISARM: 32121da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0001; 32131da74b1cSFrank Blaschka break; 32141da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_CAPTURE: 32151da74b1cSFrank Blaschka break; 32161da74b1cSFrank Blaschka } 32171da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_hw_trap_cb, NULL); 32181da74b1cSFrank Blaschka } 32191da74b1cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hw_trap); 32201da74b1cSFrank Blaschka 3221d73ef324SJulian Wiedmann static int qeth_check_qdio_errors(struct qeth_card *card, 3222d73ef324SJulian Wiedmann struct qdio_buffer *buf, 3223d73ef324SJulian Wiedmann unsigned int qdio_error, 3224d73ef324SJulian Wiedmann const char *dbftext) 32254a71df50SFrank Blaschka { 3226779e6e1cSJan Glauber if (qdio_error) { 3227847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, dbftext); 322838593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F15=%02X", 32293ec90878SJan Glauber buf->element[15].sflags); 323038593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F14=%02X", 32313ec90878SJan Glauber buf->element[14].sflags); 323238593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); 32333ec90878SJan Glauber if ((buf->element[15].sflags) == 0x12) { 323476b11f8eSUrsula Braun card->stats.rx_dropped++; 323576b11f8eSUrsula Braun return 0; 323676b11f8eSUrsula Braun } else 32374a71df50SFrank Blaschka return 1; 32384a71df50SFrank Blaschka } 32394a71df50SFrank Blaschka return 0; 32404a71df50SFrank Blaschka } 32414a71df50SFrank Blaschka 3242d73ef324SJulian Wiedmann static void qeth_queue_input_buffer(struct qeth_card *card, int index) 32434a71df50SFrank Blaschka { 32444a71df50SFrank Blaschka struct qeth_qdio_q *queue = card->qdio.in_q; 3245b3332930SFrank Blaschka struct list_head *lh; 32464a71df50SFrank Blaschka int count; 32474a71df50SFrank Blaschka int i; 32484a71df50SFrank Blaschka int rc; 32494a71df50SFrank Blaschka int newcount = 0; 32504a71df50SFrank Blaschka 32514a71df50SFrank Blaschka count = (index < queue->next_buf_to_init)? 32524a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 32534a71df50SFrank Blaschka (queue->next_buf_to_init - index) : 32544a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 32554a71df50SFrank Blaschka (queue->next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index); 32564a71df50SFrank Blaschka /* only requeue at a certain threshold to avoid SIGAs */ 32574a71df50SFrank Blaschka if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)) { 32584a71df50SFrank Blaschka for (i = queue->next_buf_to_init; 32594a71df50SFrank Blaschka i < queue->next_buf_to_init + count; ++i) { 32604a71df50SFrank Blaschka if (qeth_init_input_buffer(card, 32614a71df50SFrank Blaschka &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { 32624a71df50SFrank Blaschka break; 32634a71df50SFrank Blaschka } else { 32644a71df50SFrank Blaschka newcount++; 32654a71df50SFrank Blaschka } 32664a71df50SFrank Blaschka } 32674a71df50SFrank Blaschka 32684a71df50SFrank Blaschka if (newcount < count) { 32694a71df50SFrank Blaschka /* we are in memory shortage so we switch back to 32704a71df50SFrank Blaschka traditional skb allocation and drop packages */ 32714a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 3); 32724a71df50SFrank Blaschka count = newcount; 32734a71df50SFrank Blaschka } else { 32744a71df50SFrank Blaschka atomic_add_unless(&card->force_alloc_skb, -1, 0); 32754a71df50SFrank Blaschka } 32764a71df50SFrank Blaschka 3277b3332930SFrank Blaschka if (!count) { 3278b3332930SFrank Blaschka i = 0; 3279b3332930SFrank Blaschka list_for_each(lh, &card->qdio.in_buf_pool.entry_list) 3280b3332930SFrank Blaschka i++; 3281b3332930SFrank Blaschka if (i == card->qdio.in_buf_pool.buf_count) { 3282b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "qsarbw"); 3283b3332930SFrank Blaschka card->reclaim_index = index; 3284b3332930SFrank Blaschka schedule_delayed_work( 3285b3332930SFrank Blaschka &card->buffer_reclaim_work, 3286b3332930SFrank Blaschka QETH_RECLAIM_WORK_TIME); 3287b3332930SFrank Blaschka } 3288b3332930SFrank Blaschka return; 3289b3332930SFrank Blaschka } 3290b3332930SFrank Blaschka 32914a71df50SFrank Blaschka /* 32924a71df50SFrank Blaschka * according to old code it should be avoided to requeue all 32934a71df50SFrank Blaschka * 128 buffers in order to benefit from PCI avoidance. 32944a71df50SFrank Blaschka * this function keeps at least one buffer (the buffer at 32954a71df50SFrank Blaschka * 'index') un-requeued -> this buffer is the first buffer that 32964a71df50SFrank Blaschka * will be requeued the next time 32974a71df50SFrank Blaschka */ 32984a71df50SFrank Blaschka if (card->options.performance_stats) { 32994a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_cnt++; 33004a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time = 33014a71df50SFrank Blaschka qeth_get_micros(); 33024a71df50SFrank Blaschka } 3303779e6e1cSJan Glauber rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 3304779e6e1cSJan Glauber queue->next_buf_to_init, count); 33054a71df50SFrank Blaschka if (card->options.performance_stats) 33064a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_time += 33074a71df50SFrank Blaschka qeth_get_micros() - 33084a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time; 33094a71df50SFrank Blaschka if (rc) { 3310847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "qinberr"); 33114a71df50SFrank Blaschka } 33124a71df50SFrank Blaschka queue->next_buf_to_init = (queue->next_buf_to_init + count) % 33134a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 33144a71df50SFrank Blaschka } 33154a71df50SFrank Blaschka } 3316d73ef324SJulian Wiedmann 3317d73ef324SJulian Wiedmann static void qeth_buffer_reclaim_work(struct work_struct *work) 3318d73ef324SJulian Wiedmann { 3319d73ef324SJulian Wiedmann struct qeth_card *card = container_of(work, struct qeth_card, 3320d73ef324SJulian Wiedmann buffer_reclaim_work.work); 3321d73ef324SJulian Wiedmann 3322d73ef324SJulian Wiedmann QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); 3323d73ef324SJulian Wiedmann qeth_queue_input_buffer(card, card->reclaim_index); 3324d73ef324SJulian Wiedmann } 33254a71df50SFrank Blaschka 3326d7a39937SJulian Wiedmann static void qeth_handle_send_error(struct qeth_card *card, 3327779e6e1cSJan Glauber struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) 33284a71df50SFrank Blaschka { 33293ec90878SJan Glauber int sbalf15 = buffer->buffer->element[15].sflags; 33304a71df50SFrank Blaschka 3331847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hdsnderr"); 333258490f18SKlaus-Dieter Wacker if (card->info.type == QETH_CARD_TYPE_IQD) { 333358490f18SKlaus-Dieter Wacker if (sbalf15 == 0) { 333458490f18SKlaus-Dieter Wacker qdio_err = 0; 333558490f18SKlaus-Dieter Wacker } else { 333658490f18SKlaus-Dieter Wacker qdio_err = 1; 333758490f18SKlaus-Dieter Wacker } 333858490f18SKlaus-Dieter Wacker } 333976b11f8eSUrsula Braun qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr"); 3340d303b6fdSJan Glauber 3341d303b6fdSJan Glauber if (!qdio_err) 3342d7a39937SJulian Wiedmann return; 3343d303b6fdSJan Glauber 3344d303b6fdSJan Glauber if ((sbalf15 >= 15) && (sbalf15 <= 31)) 3345d7a39937SJulian Wiedmann return; 3346d303b6fdSJan Glauber 3347847a50fdSCarsten Otte QETH_CARD_TEXT(card, 1, "lnkfail"); 3348847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 1, "%04x %02x", 33494a71df50SFrank Blaschka (u16)qdio_err, (u8)sbalf15); 33504a71df50SFrank Blaschka } 33514a71df50SFrank Blaschka 3352664e42acSJulian Wiedmann /** 3353664e42acSJulian Wiedmann * qeth_prep_flush_pack_buffer - Prepares flushing of a packing buffer. 3354664e42acSJulian Wiedmann * @queue: queue to check for packing buffer 3355664e42acSJulian Wiedmann * 3356664e42acSJulian Wiedmann * Returns number of buffers that were prepared for flush. 3357664e42acSJulian Wiedmann */ 3358664e42acSJulian Wiedmann static int qeth_prep_flush_pack_buffer(struct qeth_qdio_out_q *queue) 3359664e42acSJulian Wiedmann { 3360664e42acSJulian Wiedmann struct qeth_qdio_out_buffer *buffer; 3361664e42acSJulian Wiedmann 3362664e42acSJulian Wiedmann buffer = queue->bufs[queue->next_buf_to_fill]; 3363664e42acSJulian Wiedmann if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) && 3364664e42acSJulian Wiedmann (buffer->next_element_to_fill > 0)) { 3365664e42acSJulian Wiedmann /* it's a packing buffer */ 3366664e42acSJulian Wiedmann atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 3367664e42acSJulian Wiedmann queue->next_buf_to_fill = 3368664e42acSJulian Wiedmann (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q; 3369664e42acSJulian Wiedmann return 1; 3370664e42acSJulian Wiedmann } 3371664e42acSJulian Wiedmann return 0; 3372664e42acSJulian Wiedmann } 3373664e42acSJulian Wiedmann 33744a71df50SFrank Blaschka /* 33754a71df50SFrank Blaschka * Switched to packing state if the number of used buffers on a queue 33764a71df50SFrank Blaschka * reaches a certain limit. 33774a71df50SFrank Blaschka */ 33784a71df50SFrank Blaschka static void qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue) 33794a71df50SFrank Blaschka { 33804a71df50SFrank Blaschka if (!queue->do_pack) { 33814a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 33824a71df50SFrank Blaschka >= QETH_HIGH_WATERMARK_PACK){ 33834a71df50SFrank Blaschka /* switch non-PACKING -> PACKING */ 3384847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "np->pack"); 33854a71df50SFrank Blaschka if (queue->card->options.performance_stats) 33864a71df50SFrank Blaschka queue->card->perf_stats.sc_dp_p++; 33874a71df50SFrank Blaschka queue->do_pack = 1; 33884a71df50SFrank Blaschka } 33894a71df50SFrank Blaschka } 33904a71df50SFrank Blaschka } 33914a71df50SFrank Blaschka 33924a71df50SFrank Blaschka /* 33934a71df50SFrank Blaschka * Switches from packing to non-packing mode. If there is a packing 33944a71df50SFrank Blaschka * buffer on the queue this buffer will be prepared to be flushed. 33954a71df50SFrank Blaschka * In that case 1 is returned to inform the caller. If no buffer 33964a71df50SFrank Blaschka * has to be flushed, zero is returned. 33974a71df50SFrank Blaschka */ 33984a71df50SFrank Blaschka static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue) 33994a71df50SFrank Blaschka { 34004a71df50SFrank Blaschka if (queue->do_pack) { 34014a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 34024a71df50SFrank Blaschka <= QETH_LOW_WATERMARK_PACK) { 34034a71df50SFrank Blaschka /* switch PACKING -> non-PACKING */ 3404847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "pack->np"); 34054a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34064a71df50SFrank Blaschka queue->card->perf_stats.sc_p_dp++; 34074a71df50SFrank Blaschka queue->do_pack = 0; 3408664e42acSJulian Wiedmann return qeth_prep_flush_pack_buffer(queue); 34094a71df50SFrank Blaschka } 34104a71df50SFrank Blaschka } 34114a71df50SFrank Blaschka return 0; 34124a71df50SFrank Blaschka } 34134a71df50SFrank Blaschka 3414779e6e1cSJan Glauber static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, 3415779e6e1cSJan Glauber int count) 34164a71df50SFrank Blaschka { 34174a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buf; 34184a71df50SFrank Blaschka int rc; 34194a71df50SFrank Blaschka int i; 34204a71df50SFrank Blaschka unsigned int qdio_flags; 34214a71df50SFrank Blaschka 34224a71df50SFrank Blaschka for (i = index; i < index + count; ++i) { 34230da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 34240da9581dSEinar Lueck buf = queue->bufs[bidx]; 34253ec90878SJan Glauber buf->buffer->element[buf->next_element_to_fill - 1].eflags |= 34263ec90878SJan Glauber SBAL_EFLAGS_LAST_ENTRY; 34274a71df50SFrank Blaschka 34280da9581dSEinar Lueck if (queue->bufstates) 34290da9581dSEinar Lueck queue->bufstates[bidx].user = buf; 34300da9581dSEinar Lueck 34314a71df50SFrank Blaschka if (queue->card->info.type == QETH_CARD_TYPE_IQD) 34324a71df50SFrank Blaschka continue; 34334a71df50SFrank Blaschka 34344a71df50SFrank Blaschka if (!queue->do_pack) { 34354a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) >= 34364a71df50SFrank Blaschka (QETH_HIGH_WATERMARK_PACK - 34374a71df50SFrank Blaschka QETH_WATERMARK_PACK_FUZZ)) && 34384a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 34394a71df50SFrank Blaschka /* it's likely that we'll go to packing 34404a71df50SFrank Blaschka * mode soon */ 34414a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 34423ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 34434a71df50SFrank Blaschka } 34444a71df50SFrank Blaschka } else { 34454a71df50SFrank Blaschka if (!atomic_read(&queue->set_pci_flags_count)) { 34464a71df50SFrank Blaschka /* 34474a71df50SFrank Blaschka * there's no outstanding PCI any more, so we 34484a71df50SFrank Blaschka * have to request a PCI to be sure the the PCI 34494a71df50SFrank Blaschka * will wake at some time in the future then we 34504a71df50SFrank Blaschka * can flush packed buffers that might still be 34514a71df50SFrank Blaschka * hanging around, which can happen if no 34524a71df50SFrank Blaschka * further send was requested by the stack 34534a71df50SFrank Blaschka */ 34544a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 34553ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 34564a71df50SFrank Blaschka } 34574a71df50SFrank Blaschka } 34584a71df50SFrank Blaschka } 34594a71df50SFrank Blaschka 34603e66bab3SFlorian Westphal netif_trans_update(queue->card->dev); 34614a71df50SFrank Blaschka if (queue->card->options.performance_stats) { 34624a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_cnt++; 34634a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time = 34644a71df50SFrank Blaschka qeth_get_micros(); 34654a71df50SFrank Blaschka } 34664a71df50SFrank Blaschka qdio_flags = QDIO_FLAG_SYNC_OUTPUT; 34674a71df50SFrank Blaschka if (atomic_read(&queue->set_pci_flags_count)) 34684a71df50SFrank Blaschka qdio_flags |= QDIO_FLAG_PCI_OUT; 3469a702349aSJulian Wiedmann atomic_add(count, &queue->used_buffers); 3470a702349aSJulian Wiedmann 34714a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, 3472779e6e1cSJan Glauber queue->queue_no, index, count); 34734a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34744a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_time += 34754a71df50SFrank Blaschka qeth_get_micros() - 34764a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time; 34774a71df50SFrank Blaschka if (rc) { 3478d303b6fdSJan Glauber queue->card->stats.tx_errors += count; 3479d303b6fdSJan Glauber /* ignore temporary SIGA errors without busy condition */ 34801549d13fSJan Glauber if (rc == -ENOBUFS) 3481d303b6fdSJan Glauber return; 3482847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 2, "flushbuf"); 34830da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no); 34840da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " idx%d", index); 34850da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " c%d", count); 3486847a50fdSCarsten Otte QETH_CARD_TEXT_(queue->card, 2, " err%d", rc); 3487d303b6fdSJan Glauber 34884a71df50SFrank Blaschka /* this must not happen under normal circumstances. if it 34894a71df50SFrank Blaschka * happens something is really wrong -> recover */ 34904a71df50SFrank Blaschka qeth_schedule_recovery(queue->card); 34914a71df50SFrank Blaschka return; 34924a71df50SFrank Blaschka } 34934a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34944a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent += count; 34954a71df50SFrank Blaschka } 34964a71df50SFrank Blaschka 34974a71df50SFrank Blaschka static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue) 34984a71df50SFrank Blaschka { 34994a71df50SFrank Blaschka int index; 35004a71df50SFrank Blaschka int flush_cnt = 0; 35014a71df50SFrank Blaschka int q_was_packing = 0; 35024a71df50SFrank Blaschka 35034a71df50SFrank Blaschka /* 35044a71df50SFrank Blaschka * check if weed have to switch to non-packing mode or if 35054a71df50SFrank Blaschka * we have to get a pci flag out on the queue 35064a71df50SFrank Blaschka */ 35074a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) <= QETH_LOW_WATERMARK_PACK) || 35084a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 35094a71df50SFrank Blaschka if (atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH) == 35104a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED) { 35114a71df50SFrank Blaschka /* 35124a71df50SFrank Blaschka * If we get in here, there was no action in 35134a71df50SFrank Blaschka * do_send_packet. So, we check if there is a 35144a71df50SFrank Blaschka * packing buffer to be flushed here. 35154a71df50SFrank Blaschka */ 35164a71df50SFrank Blaschka netif_stop_queue(queue->card->dev); 35174a71df50SFrank Blaschka index = queue->next_buf_to_fill; 35184a71df50SFrank Blaschka q_was_packing = queue->do_pack; 35194a71df50SFrank Blaschka /* queue->do_pack may change */ 35204a71df50SFrank Blaschka barrier(); 35214a71df50SFrank Blaschka flush_cnt += qeth_switch_to_nonpacking_if_needed(queue); 35224a71df50SFrank Blaschka if (!flush_cnt && 35234a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) 3524664e42acSJulian Wiedmann flush_cnt += qeth_prep_flush_pack_buffer(queue); 35254a71df50SFrank Blaschka if (queue->card->options.performance_stats && 35264a71df50SFrank Blaschka q_was_packing) 35274a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += 35284a71df50SFrank Blaschka flush_cnt; 35294a71df50SFrank Blaschka if (flush_cnt) 3530779e6e1cSJan Glauber qeth_flush_buffers(queue, index, flush_cnt); 35314a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 35324a71df50SFrank Blaschka } 35334a71df50SFrank Blaschka } 35344a71df50SFrank Blaschka } 35354a71df50SFrank Blaschka 35367bcd64ebSJulian Wiedmann static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, 3537a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3538a1c3ed4cSFrank Blaschka { 3539a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3540a1c3ed4cSFrank Blaschka 3541d3d1b205SJulian Wiedmann if (card->dev->flags & IFF_UP) 3542a1c3ed4cSFrank Blaschka napi_schedule(&card->napi); 3543a1c3ed4cSFrank Blaschka } 3544a1c3ed4cSFrank Blaschka 35450da9581dSEinar Lueck int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) 35460da9581dSEinar Lueck { 35470da9581dSEinar Lueck int rc; 35480da9581dSEinar Lueck 35490da9581dSEinar Lueck if (card->options.cq == QETH_CQ_NOTAVAILABLE) { 35500da9581dSEinar Lueck rc = -1; 35510da9581dSEinar Lueck goto out; 35520da9581dSEinar Lueck } else { 35530da9581dSEinar Lueck if (card->options.cq == cq) { 35540da9581dSEinar Lueck rc = 0; 35550da9581dSEinar Lueck goto out; 35560da9581dSEinar Lueck } 35570da9581dSEinar Lueck 35580da9581dSEinar Lueck if (card->state != CARD_STATE_DOWN && 35590da9581dSEinar Lueck card->state != CARD_STATE_RECOVER) { 35600da9581dSEinar Lueck rc = -1; 35610da9581dSEinar Lueck goto out; 35620da9581dSEinar Lueck } 35630da9581dSEinar Lueck 35640da9581dSEinar Lueck qeth_free_qdio_buffers(card); 35650da9581dSEinar Lueck card->options.cq = cq; 35660da9581dSEinar Lueck rc = 0; 35670da9581dSEinar Lueck } 35680da9581dSEinar Lueck out: 35690da9581dSEinar Lueck return rc; 35700da9581dSEinar Lueck 35710da9581dSEinar Lueck } 35720da9581dSEinar Lueck EXPORT_SYMBOL_GPL(qeth_configure_cq); 35730da9581dSEinar Lueck 35743b346c18SJulian Wiedmann static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, 35753b346c18SJulian Wiedmann unsigned int queue, int first_element, 35763b346c18SJulian Wiedmann int count) 35773b346c18SJulian Wiedmann { 35780da9581dSEinar Lueck struct qeth_qdio_q *cq = card->qdio.c_q; 35790da9581dSEinar Lueck int i; 35800da9581dSEinar Lueck int rc; 35810da9581dSEinar Lueck 35820da9581dSEinar Lueck if (!qeth_is_cq(card, queue)) 35830da9581dSEinar Lueck goto out; 35840da9581dSEinar Lueck 35850da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element); 35860da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhc%d", count); 35870da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err); 35880da9581dSEinar Lueck 35890da9581dSEinar Lueck if (qdio_err) { 35900da9581dSEinar Lueck netif_stop_queue(card->dev); 35910da9581dSEinar Lueck qeth_schedule_recovery(card); 35920da9581dSEinar Lueck goto out; 35930da9581dSEinar Lueck } 35940da9581dSEinar Lueck 35950da9581dSEinar Lueck if (card->options.performance_stats) { 35960da9581dSEinar Lueck card->perf_stats.cq_cnt++; 35970da9581dSEinar Lueck card->perf_stats.cq_start_time = qeth_get_micros(); 35980da9581dSEinar Lueck } 35990da9581dSEinar Lueck 36000da9581dSEinar Lueck for (i = first_element; i < first_element + count; ++i) { 36010da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 36026d284bdeSSebastian Ott struct qdio_buffer *buffer = cq->qdio_bufs[bidx]; 36033b346c18SJulian Wiedmann int e = 0; 36040da9581dSEinar Lueck 3605903e4853SUrsula Braun while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && 3606903e4853SUrsula Braun buffer->element[e].addr) { 36070da9581dSEinar Lueck unsigned long phys_aob_addr; 36080da9581dSEinar Lueck 36090da9581dSEinar Lueck phys_aob_addr = (unsigned long) buffer->element[e].addr; 36100da9581dSEinar Lueck qeth_qdio_handle_aob(card, phys_aob_addr); 36110da9581dSEinar Lueck ++e; 36120da9581dSEinar Lueck } 36133b346c18SJulian Wiedmann qeth_scrub_qdio_buffer(buffer, QDIO_MAX_ELEMENTS_PER_BUFFER); 36140da9581dSEinar Lueck } 36150da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, 36160da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init, 36170da9581dSEinar Lueck count); 36180da9581dSEinar Lueck if (rc) { 36190da9581dSEinar Lueck dev_warn(&card->gdev->dev, 36200da9581dSEinar Lueck "QDIO reported an error, rc=%i\n", rc); 36210da9581dSEinar Lueck QETH_CARD_TEXT(card, 2, "qcqherr"); 36220da9581dSEinar Lueck } 36230da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init 36240da9581dSEinar Lueck + count) % QDIO_MAX_BUFFERS_PER_Q; 36250da9581dSEinar Lueck 36260da9581dSEinar Lueck netif_wake_queue(card->dev); 36270da9581dSEinar Lueck 36280da9581dSEinar Lueck if (card->options.performance_stats) { 36290da9581dSEinar Lueck int delta_t = qeth_get_micros(); 36300da9581dSEinar Lueck delta_t -= card->perf_stats.cq_start_time; 36310da9581dSEinar Lueck card->perf_stats.cq_time += delta_t; 36320da9581dSEinar Lueck } 36330da9581dSEinar Lueck out: 36340da9581dSEinar Lueck return; 36350da9581dSEinar Lueck } 36360da9581dSEinar Lueck 36377bcd64ebSJulian Wiedmann static void qeth_qdio_input_handler(struct ccw_device *ccwdev, 36387bcd64ebSJulian Wiedmann unsigned int qdio_err, int queue, 36397bcd64ebSJulian Wiedmann int first_elem, int count, 3640a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3641a1c3ed4cSFrank Blaschka { 3642a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3643a1c3ed4cSFrank Blaschka 36440da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qihq%d", queue); 36450da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err); 36460da9581dSEinar Lueck 36470da9581dSEinar Lueck if (qeth_is_cq(card, queue)) 36480da9581dSEinar Lueck qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count); 36490da9581dSEinar Lueck else if (qdio_err) 3650a1c3ed4cSFrank Blaschka qeth_schedule_recovery(card); 3651a1c3ed4cSFrank Blaschka } 3652a1c3ed4cSFrank Blaschka 36537bcd64ebSJulian Wiedmann static void qeth_qdio_output_handler(struct ccw_device *ccwdev, 36547bcd64ebSJulian Wiedmann unsigned int qdio_error, int __queue, 36557bcd64ebSJulian Wiedmann int first_element, int count, 36567bcd64ebSJulian Wiedmann unsigned long card_ptr) 36574a71df50SFrank Blaschka { 36584a71df50SFrank Blaschka struct qeth_card *card = (struct qeth_card *) card_ptr; 36594a71df50SFrank Blaschka struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; 36604a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 36614a71df50SFrank Blaschka int i; 36624a71df50SFrank Blaschka 3663847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "qdouhdl"); 36641549d13fSJan Glauber if (qdio_error & QDIO_ERROR_FATAL) { 3665847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "achkcond"); 36664a71df50SFrank Blaschka netif_stop_queue(card->dev); 36674a71df50SFrank Blaschka qeth_schedule_recovery(card); 36684a71df50SFrank Blaschka return; 36694a71df50SFrank Blaschka } 36704a71df50SFrank Blaschka if (card->options.performance_stats) { 36714a71df50SFrank Blaschka card->perf_stats.outbound_handler_cnt++; 36724a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time = 36734a71df50SFrank Blaschka qeth_get_micros(); 36744a71df50SFrank Blaschka } 36754a71df50SFrank Blaschka for (i = first_element; i < (first_element + count); ++i) { 36760da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 36770da9581dSEinar Lueck buffer = queue->bufs[bidx]; 3678b67d801fSUrsula Braun qeth_handle_send_error(card, buffer, qdio_error); 36790da9581dSEinar Lueck 36800da9581dSEinar Lueck if (queue->bufstates && 36810da9581dSEinar Lueck (queue->bufstates[bidx].flags & 36820da9581dSEinar Lueck QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) { 368318af5c17SStefan Raspl WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); 3684b3332930SFrank Blaschka 3685b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, 3686b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED, 3687b3332930SFrank Blaschka QETH_QDIO_BUF_PENDING) == 3688b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED) { 3689b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, 3690b3332930SFrank Blaschka TX_NOTIFY_PENDING); 3691b3332930SFrank Blaschka } 36920da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx); 3693ce28867fSJulian Wiedmann 3694ce28867fSJulian Wiedmann /* prepare the queue slot for re-use: */ 3695ce28867fSJulian Wiedmann qeth_scrub_qdio_buffer(buffer->buffer, 3696ce28867fSJulian Wiedmann QETH_MAX_BUFFER_ELEMENTS(card)); 3697b3332930SFrank Blaschka if (qeth_init_qdio_out_buf(queue, bidx)) { 3698b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "outofbuf"); 36990da9581dSEinar Lueck qeth_schedule_recovery(card); 3700b3332930SFrank Blaschka } 37010da9581dSEinar Lueck } else { 3702b3332930SFrank Blaschka if (card->options.cq == QETH_CQ_ENABLED) { 3703b3332930SFrank Blaschka enum iucv_tx_notify n; 3704b3332930SFrank Blaschka 3705b3332930SFrank Blaschka n = qeth_compute_cq_notification( 3706b3332930SFrank Blaschka buffer->buffer->element[15].sflags, 0); 3707b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, n); 3708b3332930SFrank Blaschka } 3709b3332930SFrank Blaschka 37103b346c18SJulian Wiedmann qeth_clear_output_buffer(queue, buffer); 37110da9581dSEinar Lueck } 37120da9581dSEinar Lueck qeth_cleanup_handled_pending(queue, bidx, 0); 37134a71df50SFrank Blaschka } 37144a71df50SFrank Blaschka atomic_sub(count, &queue->used_buffers); 37154a71df50SFrank Blaschka /* check if we need to do something on this outbound queue */ 37164a71df50SFrank Blaschka if (card->info.type != QETH_CARD_TYPE_IQD) 37174a71df50SFrank Blaschka qeth_check_outbound_queue(queue); 37184a71df50SFrank Blaschka 37194a71df50SFrank Blaschka netif_wake_queue(queue->card->dev); 37204a71df50SFrank Blaschka if (card->options.performance_stats) 37214a71df50SFrank Blaschka card->perf_stats.outbound_handler_time += qeth_get_micros() - 37224a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time; 37234a71df50SFrank Blaschka } 37244a71df50SFrank Blaschka 372570deb016SHans Wippel /* We cannot use outbound queue 3 for unicast packets on HiperSockets */ 372670deb016SHans Wippel static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num) 372770deb016SHans Wippel { 372870deb016SHans Wippel if ((card->info.type == QETH_CARD_TYPE_IQD) && (queue_num == 3)) 372970deb016SHans Wippel return 2; 373070deb016SHans Wippel return queue_num; 373170deb016SHans Wippel } 373270deb016SHans Wippel 3733290b8348SStefan Raspl /** 3734290b8348SStefan Raspl * Note: Function assumes that we have 4 outbound queues. 3735290b8348SStefan Raspl */ 37364a71df50SFrank Blaschka int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, 373786c0cdb9SJulian Wiedmann int ipv) 37384a71df50SFrank Blaschka { 3739d66cb37eSStefan Raspl __be16 *tci; 3740290b8348SStefan Raspl u8 tos; 3741290b8348SStefan Raspl 3742290b8348SStefan Raspl switch (card->qdio.do_prio_queueing) { 3743290b8348SStefan Raspl case QETH_PRIO_Q_ING_TOS: 3744290b8348SStefan Raspl case QETH_PRIO_Q_ING_PREC: 3745290b8348SStefan Raspl switch (ipv) { 3746290b8348SStefan Raspl case 4: 3747290b8348SStefan Raspl tos = ipv4_get_dsfield(ip_hdr(skb)); 3748290b8348SStefan Raspl break; 3749290b8348SStefan Raspl case 6: 3750290b8348SStefan Raspl tos = ipv6_get_dsfield(ipv6_hdr(skb)); 3751290b8348SStefan Raspl break; 37524a71df50SFrank Blaschka default: 37534a71df50SFrank Blaschka return card->qdio.default_out_queue; 37544a71df50SFrank Blaschka } 3755290b8348SStefan Raspl if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) 375670deb016SHans Wippel return qeth_cut_iqd_prio(card, ~tos >> 6 & 3); 3757290b8348SStefan Raspl if (tos & IPTOS_MINCOST) 375870deb016SHans Wippel return qeth_cut_iqd_prio(card, 3); 3759290b8348SStefan Raspl if (tos & IPTOS_RELIABILITY) 3760290b8348SStefan Raspl return 2; 3761290b8348SStefan Raspl if (tos & IPTOS_THROUGHPUT) 3762290b8348SStefan Raspl return 1; 3763290b8348SStefan Raspl if (tos & IPTOS_LOWDELAY) 3764290b8348SStefan Raspl return 0; 3765d66cb37eSStefan Raspl break; 3766d66cb37eSStefan Raspl case QETH_PRIO_Q_ING_SKB: 3767d66cb37eSStefan Raspl if (skb->priority > 5) 3768d66cb37eSStefan Raspl return 0; 376970deb016SHans Wippel return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3); 3770d66cb37eSStefan Raspl case QETH_PRIO_Q_ING_VLAN: 3771d66cb37eSStefan Raspl tci = &((struct ethhdr *)skb->data)->h_proto; 37726bee4e26SHans Wippel if (be16_to_cpu(*tci) == ETH_P_8021Q) 37736bee4e26SHans Wippel return qeth_cut_iqd_prio(card, 37746bee4e26SHans Wippel ~be16_to_cpu(*(tci + 1)) >> (VLAN_PRIO_SHIFT + 1) & 3); 3775d66cb37eSStefan Raspl break; 3776290b8348SStefan Raspl default: 3777290b8348SStefan Raspl break; 3778290b8348SStefan Raspl } 3779290b8348SStefan Raspl return card->qdio.default_out_queue; 37804a71df50SFrank Blaschka } 37814a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_priority_queue); 37824a71df50SFrank Blaschka 37832863c613SEugene Crosser /** 37842863c613SEugene Crosser * qeth_get_elements_for_frags() - find number of SBALEs for skb frags. 37852863c613SEugene Crosser * @skb: SKB address 37862863c613SEugene Crosser * 37872863c613SEugene Crosser * Returns the number of pages, and thus QDIO buffer elements, needed to cover 37882863c613SEugene Crosser * fragmented part of the SKB. Returns zero for linear SKB. 37892863c613SEugene Crosser */ 3790271648b4SFrank Blaschka int qeth_get_elements_for_frags(struct sk_buff *skb) 3791271648b4SFrank Blaschka { 37922863c613SEugene Crosser int cnt, elements = 0; 3793271648b4SFrank Blaschka 3794271648b4SFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 37952863c613SEugene Crosser struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[cnt]; 37962863c613SEugene Crosser 37972863c613SEugene Crosser elements += qeth_get_elements_for_range( 37982863c613SEugene Crosser (addr_t)skb_frag_address(frag), 37992863c613SEugene Crosser (addr_t)skb_frag_address(frag) + skb_frag_size(frag)); 3800271648b4SFrank Blaschka } 3801271648b4SFrank Blaschka return elements; 3802271648b4SFrank Blaschka } 3803271648b4SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); 3804271648b4SFrank Blaschka 3805ba86ceeeSJulian Wiedmann static unsigned int qeth_count_elements(struct sk_buff *skb, int data_offset) 3806ba86ceeeSJulian Wiedmann { 3807ba86ceeeSJulian Wiedmann unsigned int elements = qeth_get_elements_for_frags(skb); 3808ba86ceeeSJulian Wiedmann addr_t end = (addr_t)skb->data + skb_headlen(skb); 3809ba86ceeeSJulian Wiedmann addr_t start = (addr_t)skb->data + data_offset; 3810ba86ceeeSJulian Wiedmann 3811ba86ceeeSJulian Wiedmann if (start != end) 3812ba86ceeeSJulian Wiedmann elements += qeth_get_elements_for_range(start, end); 3813ba86ceeeSJulian Wiedmann return elements; 3814ba86ceeeSJulian Wiedmann } 3815ba86ceeeSJulian Wiedmann 38162863c613SEugene Crosser /** 38172863c613SEugene Crosser * qeth_get_elements_no() - find number of SBALEs for skb data, inc. frags. 38182863c613SEugene Crosser * @card: qeth card structure, to check max. elems. 38192863c613SEugene Crosser * @skb: SKB address 38202863c613SEugene Crosser * @extra_elems: extra elems needed, to check against max. 38217d969d2eSJulian Wiedmann * @data_offset: range starts at skb->data + data_offset 38222863c613SEugene Crosser * 38232863c613SEugene Crosser * Returns the number of pages, and thus QDIO buffer elements, needed to cover 38242863c613SEugene Crosser * skb data, including linear part and fragments. Checks if the result plus 38252863c613SEugene Crosser * extra_elems fits under the limit for the card. Returns 0 if it does not. 38262863c613SEugene Crosser * Note: extra_elems is not included in the returned result. 38272863c613SEugene Crosser */ 3828065cc782SStefan Raspl int qeth_get_elements_no(struct qeth_card *card, 38297d969d2eSJulian Wiedmann struct sk_buff *skb, int extra_elems, int data_offset) 38304a71df50SFrank Blaschka { 3831ba86ceeeSJulian Wiedmann int elements = qeth_count_elements(skb, data_offset); 38324a71df50SFrank Blaschka 38332863c613SEugene Crosser if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { 383414cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Invalid size of IP packet " 38354a71df50SFrank Blaschka "(Number=%d / Length=%d). Discarded.\n", 38362863c613SEugene Crosser elements + extra_elems, skb->len); 38374a71df50SFrank Blaschka return 0; 38384a71df50SFrank Blaschka } 38392863c613SEugene Crosser return elements; 38404a71df50SFrank Blaschka } 38414a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_no); 38424a71df50SFrank Blaschka 3843d4ae1f5eSStefan Raspl int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) 384451aa165cSFrank Blaschka { 384551aa165cSFrank Blaschka int hroom, inpage, rest; 384651aa165cSFrank Blaschka 384751aa165cSFrank Blaschka if (((unsigned long)skb->data & PAGE_MASK) != 384851aa165cSFrank Blaschka (((unsigned long)skb->data + len - 1) & PAGE_MASK)) { 384951aa165cSFrank Blaschka hroom = skb_headroom(skb); 385051aa165cSFrank Blaschka inpage = PAGE_SIZE - ((unsigned long) skb->data % PAGE_SIZE); 385151aa165cSFrank Blaschka rest = len - inpage; 385251aa165cSFrank Blaschka if (rest > hroom) 385351aa165cSFrank Blaschka return 1; 38542863c613SEugene Crosser memmove(skb->data - rest, skb->data, skb_headlen(skb)); 385551aa165cSFrank Blaschka skb->data -= rest; 3856d4ae1f5eSStefan Raspl skb->tail -= rest; 3857d4ae1f5eSStefan Raspl *hdr = (struct qeth_hdr *)skb->data; 385851aa165cSFrank Blaschka QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest); 385951aa165cSFrank Blaschka } 386051aa165cSFrank Blaschka return 0; 386151aa165cSFrank Blaschka } 386251aa165cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); 386351aa165cSFrank Blaschka 38640d6f02d3SJulian Wiedmann /** 3865ba86ceeeSJulian Wiedmann * qeth_add_hw_header() - add a HW header to an skb. 3866ba86ceeeSJulian Wiedmann * @skb: skb that the HW header should be added to. 38670d6f02d3SJulian Wiedmann * @hdr: double pointer to a qeth_hdr. When returning with >= 0, 38680d6f02d3SJulian Wiedmann * it contains a valid pointer to a qeth_hdr. 3869a7c2f4a3SJulian Wiedmann * @hdr_len: length of the HW header. 3870a7c2f4a3SJulian Wiedmann * @proto_len: length of protocol headers that need to be in same page as the 3871a7c2f4a3SJulian Wiedmann * HW header. 38720d6f02d3SJulian Wiedmann * 38730d6f02d3SJulian Wiedmann * Returns the pushed length. If the header can't be pushed on 38740d6f02d3SJulian Wiedmann * (eg. because it would cross a page boundary), it is allocated from 38750d6f02d3SJulian Wiedmann * the cache instead and 0 is returned. 3876ba86ceeeSJulian Wiedmann * The number of needed buffer elements is returned in @elements. 38770d6f02d3SJulian Wiedmann * Error to create the hdr is indicated by returning with < 0. 38780d6f02d3SJulian Wiedmann */ 3879ba86ceeeSJulian Wiedmann int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, 3880a7c2f4a3SJulian Wiedmann struct qeth_hdr **hdr, unsigned int hdr_len, 3881a7c2f4a3SJulian Wiedmann unsigned int proto_len, unsigned int *elements) 38820d6f02d3SJulian Wiedmann { 3883ba86ceeeSJulian Wiedmann const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card); 3884a7c2f4a3SJulian Wiedmann const unsigned int contiguous = proto_len ? proto_len : 1; 3885ba86ceeeSJulian Wiedmann unsigned int __elements; 3886ba86ceeeSJulian Wiedmann addr_t start, end; 3887ba86ceeeSJulian Wiedmann bool push_ok; 3888ba86ceeeSJulian Wiedmann int rc; 3889ba86ceeeSJulian Wiedmann 3890ba86ceeeSJulian Wiedmann check_layout: 3891a7c2f4a3SJulian Wiedmann start = (addr_t)skb->data - hdr_len; 3892ba86ceeeSJulian Wiedmann end = (addr_t)skb->data; 3893ba86ceeeSJulian Wiedmann 3894a7c2f4a3SJulian Wiedmann if (qeth_get_elements_for_range(start, end + contiguous) == 1) { 3895ba86ceeeSJulian Wiedmann /* Push HW header into same page as first protocol header. */ 3896ba86ceeeSJulian Wiedmann push_ok = true; 3897ba86ceeeSJulian Wiedmann __elements = qeth_count_elements(skb, 0); 3898a7c2f4a3SJulian Wiedmann } else if (!proto_len && qeth_get_elements_for_range(start, end) == 1) { 3899ba86ceeeSJulian Wiedmann /* Push HW header into a new page. */ 3900ba86ceeeSJulian Wiedmann push_ok = true; 3901a7c2f4a3SJulian Wiedmann __elements = 1 + qeth_count_elements(skb, 0); 3902a7c2f4a3SJulian Wiedmann } else { 3903a7c2f4a3SJulian Wiedmann /* Use header cache, copy protocol headers up. */ 3904ba86ceeeSJulian Wiedmann push_ok = false; 3905a7c2f4a3SJulian Wiedmann __elements = 1 + qeth_count_elements(skb, proto_len); 3906ba86ceeeSJulian Wiedmann } 3907ba86ceeeSJulian Wiedmann 3908ba86ceeeSJulian Wiedmann /* Compress skb to fit into one IO buffer: */ 3909ba86ceeeSJulian Wiedmann if (__elements > max_elements) { 3910ba86ceeeSJulian Wiedmann if (!skb_is_nonlinear(skb)) { 3911ba86ceeeSJulian Wiedmann /* Drop it, no easy way of shrinking it further. */ 3912ba86ceeeSJulian Wiedmann QETH_DBF_MESSAGE(2, "Dropped an oversized skb (Max Elements=%u / Actual=%u / Length=%u).\n", 3913ba86ceeeSJulian Wiedmann max_elements, __elements, skb->len); 3914ba86ceeeSJulian Wiedmann return -E2BIG; 3915ba86ceeeSJulian Wiedmann } 3916ba86ceeeSJulian Wiedmann 3917ba86ceeeSJulian Wiedmann rc = skb_linearize(skb); 3918ba86ceeeSJulian Wiedmann if (card->options.performance_stats) { 3919ba86ceeeSJulian Wiedmann if (rc) 3920ba86ceeeSJulian Wiedmann card->perf_stats.tx_linfail++; 3921ba86ceeeSJulian Wiedmann else 3922ba86ceeeSJulian Wiedmann card->perf_stats.tx_lin++; 3923ba86ceeeSJulian Wiedmann } 3924ba86ceeeSJulian Wiedmann if (rc) 3925ba86ceeeSJulian Wiedmann return rc; 3926ba86ceeeSJulian Wiedmann 3927ba86ceeeSJulian Wiedmann /* Linearization changed the layout, re-evaluate: */ 3928ba86ceeeSJulian Wiedmann goto check_layout; 3929ba86ceeeSJulian Wiedmann } 3930ba86ceeeSJulian Wiedmann 3931ba86ceeeSJulian Wiedmann *elements = __elements; 3932ba86ceeeSJulian Wiedmann /* Add the header: */ 3933ba86ceeeSJulian Wiedmann if (push_ok) { 3934a7c2f4a3SJulian Wiedmann *hdr = skb_push(skb, hdr_len); 3935a7c2f4a3SJulian Wiedmann return hdr_len; 39360d6f02d3SJulian Wiedmann } 39370d6f02d3SJulian Wiedmann /* fall back */ 39380d6f02d3SJulian Wiedmann *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); 39390d6f02d3SJulian Wiedmann if (!*hdr) 39400d6f02d3SJulian Wiedmann return -ENOMEM; 3941a7c2f4a3SJulian Wiedmann /* Copy protocol headers behind HW header: */ 3942a7c2f4a3SJulian Wiedmann skb_copy_from_linear_data(skb, ((char *)*hdr) + hdr_len, proto_len); 39430d6f02d3SJulian Wiedmann return 0; 39440d6f02d3SJulian Wiedmann } 3945ba86ceeeSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_add_hw_header); 39460d6f02d3SJulian Wiedmann 3947cef6ff22SJulian Wiedmann static void __qeth_fill_buffer(struct sk_buff *skb, 3948384d2ef1SJulian Wiedmann struct qeth_qdio_out_buffer *buf, 3949cc309f83SJulian Wiedmann bool is_first_elem, unsigned int offset) 39504a71df50SFrank Blaschka { 3951384d2ef1SJulian Wiedmann struct qdio_buffer *buffer = buf->buffer; 3952384d2ef1SJulian Wiedmann int element = buf->next_element_to_fill; 3953cc309f83SJulian Wiedmann int length = skb_headlen(skb) - offset; 3954cc309f83SJulian Wiedmann char *data = skb->data + offset; 3955384d2ef1SJulian Wiedmann int length_here, cnt; 39564a71df50SFrank Blaschka 3957cc309f83SJulian Wiedmann /* map linear part into buffer element(s) */ 39584a71df50SFrank Blaschka while (length > 0) { 39594a71df50SFrank Blaschka /* length_here is the remaining amount of data in this page */ 39604a71df50SFrank Blaschka length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); 39614a71df50SFrank Blaschka if (length < length_here) 39624a71df50SFrank Blaschka length_here = length; 39634a71df50SFrank Blaschka 39644a71df50SFrank Blaschka buffer->element[element].addr = data; 39654a71df50SFrank Blaschka buffer->element[element].length = length_here; 39664a71df50SFrank Blaschka length -= length_here; 3967384d2ef1SJulian Wiedmann if (is_first_elem) { 3968384d2ef1SJulian Wiedmann is_first_elem = false; 39695258830bSJulian Wiedmann if (length || skb_is_nonlinear(skb)) 39705258830bSJulian Wiedmann /* skb needs additional elements */ 39713ec90878SJan Glauber buffer->element[element].eflags = 39723ec90878SJan Glauber SBAL_EFLAGS_FIRST_FRAG; 397351aa165cSFrank Blaschka else 39743ec90878SJan Glauber buffer->element[element].eflags = 0; 39754a71df50SFrank Blaschka } else { 39763ec90878SJan Glauber buffer->element[element].eflags = 39773ec90878SJan Glauber SBAL_EFLAGS_MIDDLE_FRAG; 39784a71df50SFrank Blaschka } 39794a71df50SFrank Blaschka data += length_here; 39804a71df50SFrank Blaschka element++; 39814a71df50SFrank Blaschka } 398251aa165cSFrank Blaschka 3983cc309f83SJulian Wiedmann /* map page frags into buffer element(s) */ 398451aa165cSFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 3985f8eb4930SJulian Wiedmann skb_frag_t *frag = &skb_shinfo(skb)->frags[cnt]; 3986f8eb4930SJulian Wiedmann 3987f8eb4930SJulian Wiedmann data = skb_frag_address(frag); 3988f8eb4930SJulian Wiedmann length = skb_frag_size(frag); 3989271648b4SFrank Blaschka while (length > 0) { 3990271648b4SFrank Blaschka length_here = PAGE_SIZE - 3991271648b4SFrank Blaschka ((unsigned long) data % PAGE_SIZE); 3992271648b4SFrank Blaschka if (length < length_here) 3993271648b4SFrank Blaschka length_here = length; 3994271648b4SFrank Blaschka 3995271648b4SFrank Blaschka buffer->element[element].addr = data; 3996271648b4SFrank Blaschka buffer->element[element].length = length_here; 3997271648b4SFrank Blaschka buffer->element[element].eflags = 3998271648b4SFrank Blaschka SBAL_EFLAGS_MIDDLE_FRAG; 3999271648b4SFrank Blaschka length -= length_here; 4000271648b4SFrank Blaschka data += length_here; 400151aa165cSFrank Blaschka element++; 400251aa165cSFrank Blaschka } 4003271648b4SFrank Blaschka } 400451aa165cSFrank Blaschka 40053ec90878SJan Glauber if (buffer->element[element - 1].eflags) 40063ec90878SJan Glauber buffer->element[element - 1].eflags = SBAL_EFLAGS_LAST_FRAG; 4007384d2ef1SJulian Wiedmann buf->next_element_to_fill = element; 40084a71df50SFrank Blaschka } 40094a71df50SFrank Blaschka 4010eaf3cc08SJulian Wiedmann /** 4011eaf3cc08SJulian Wiedmann * qeth_fill_buffer() - map skb into an output buffer 4012eaf3cc08SJulian Wiedmann * @queue: QDIO queue to submit the buffer on 4013eaf3cc08SJulian Wiedmann * @buf: buffer to transport the skb 4014eaf3cc08SJulian Wiedmann * @skb: skb to map into the buffer 4015eaf3cc08SJulian Wiedmann * @hdr: qeth_hdr for this skb. Either at skb->data, or allocated 4016eaf3cc08SJulian Wiedmann * from qeth_core_header_cache. 4017eaf3cc08SJulian Wiedmann * @offset: when mapping the skb, start at skb->data + offset 4018eaf3cc08SJulian Wiedmann * @hd_len: if > 0, build a dedicated header element of this size 4019eaf3cc08SJulian Wiedmann */ 4020cef6ff22SJulian Wiedmann static int qeth_fill_buffer(struct qeth_qdio_out_q *queue, 4021cc309f83SJulian Wiedmann struct qeth_qdio_out_buffer *buf, 4022cc309f83SJulian Wiedmann struct sk_buff *skb, struct qeth_hdr *hdr, 402313ddacb5SJulian Wiedmann unsigned int offset, unsigned int hd_len) 40244a71df50SFrank Blaschka { 4025eaf3cc08SJulian Wiedmann struct qdio_buffer *buffer = buf->buffer; 4026384d2ef1SJulian Wiedmann bool is_first_elem = true; 402713ddacb5SJulian Wiedmann int flush_cnt = 0; 40284a71df50SFrank Blaschka 402963354797SReshetova, Elena refcount_inc(&skb->users); 40304a71df50SFrank Blaschka skb_queue_tail(&buf->skb_list, skb); 40314a71df50SFrank Blaschka 4032eaf3cc08SJulian Wiedmann /* build dedicated header element */ 4033eaf3cc08SJulian Wiedmann if (hd_len) { 4034683d718aSFrank Blaschka int element = buf->next_element_to_fill; 4035384d2ef1SJulian Wiedmann is_first_elem = false; 4036384d2ef1SJulian Wiedmann 4037683d718aSFrank Blaschka buffer->element[element].addr = hdr; 4038f1588177SJulian Wiedmann buffer->element[element].length = hd_len; 40393ec90878SJan Glauber buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; 4040eaf3cc08SJulian Wiedmann /* remember to free cache-allocated qeth_hdr: */ 4041eaf3cc08SJulian Wiedmann buf->is_header[element] = ((void *)hdr != skb->data); 4042683d718aSFrank Blaschka buf->next_element_to_fill++; 4043683d718aSFrank Blaschka } 4044683d718aSFrank Blaschka 4045384d2ef1SJulian Wiedmann __qeth_fill_buffer(skb, buf, is_first_elem, offset); 40464a71df50SFrank Blaschka 40474a71df50SFrank Blaschka if (!queue->do_pack) { 4048847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfnp"); 40494a71df50SFrank Blaschka /* set state to PRIMED -> will be flushed */ 40504a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 40514a71df50SFrank Blaschka flush_cnt = 1; 40524a71df50SFrank Blaschka } else { 4053847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfpa"); 40544a71df50SFrank Blaschka if (queue->card->options.performance_stats) 40554a71df50SFrank Blaschka queue->card->perf_stats.skbs_sent_pack++; 40564a71df50SFrank Blaschka if (buf->next_element_to_fill >= 40574a71df50SFrank Blaschka QETH_MAX_BUFFER_ELEMENTS(queue->card)) { 40584a71df50SFrank Blaschka /* 40594a71df50SFrank Blaschka * packed buffer if full -> set state PRIMED 40604a71df50SFrank Blaschka * -> will be flushed 40614a71df50SFrank Blaschka */ 40624a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 40634a71df50SFrank Blaschka flush_cnt = 1; 40644a71df50SFrank Blaschka } 40654a71df50SFrank Blaschka } 40664a71df50SFrank Blaschka return flush_cnt; 40674a71df50SFrank Blaschka } 40684a71df50SFrank Blaschka 40697c2e9ba3SJulian Wiedmann int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb, 4070cc309f83SJulian Wiedmann struct qeth_hdr *hdr, unsigned int offset, 407113ddacb5SJulian Wiedmann unsigned int hd_len) 40724a71df50SFrank Blaschka { 40737c2e9ba3SJulian Wiedmann int index = queue->next_buf_to_fill; 40747c2e9ba3SJulian Wiedmann struct qeth_qdio_out_buffer *buffer = queue->bufs[index]; 40754a71df50SFrank Blaschka 40764a71df50SFrank Blaschka /* 40774a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 40784a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 40794a71df50SFrank Blaschka */ 40804a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) 40817c2e9ba3SJulian Wiedmann return -EBUSY; 40827c2e9ba3SJulian Wiedmann queue->next_buf_to_fill = (index + 1) % QDIO_MAX_BUFFERS_PER_Q; 4083683d718aSFrank Blaschka qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); 4084779e6e1cSJan Glauber qeth_flush_buffers(queue, index, 1); 40854a71df50SFrank Blaschka return 0; 40864a71df50SFrank Blaschka } 40874a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast); 40884a71df50SFrank Blaschka 40894a71df50SFrank Blaschka int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, 40904a71df50SFrank Blaschka struct sk_buff *skb, struct qeth_hdr *hdr, 40919c3bfda9SJulian Wiedmann unsigned int offset, unsigned int hd_len, 40929c3bfda9SJulian Wiedmann int elements_needed) 40934a71df50SFrank Blaschka { 40944a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 40954a71df50SFrank Blaschka int start_index; 40964a71df50SFrank Blaschka int flush_count = 0; 40974a71df50SFrank Blaschka int do_pack = 0; 40984a71df50SFrank Blaschka int tmp; 40994a71df50SFrank Blaschka int rc = 0; 41004a71df50SFrank Blaschka 41014a71df50SFrank Blaschka /* spin until we get the queue ... */ 41024a71df50SFrank Blaschka while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, 41034a71df50SFrank Blaschka QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); 41044a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 41050da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 41064a71df50SFrank Blaschka /* 41074a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 41084a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 41094a71df50SFrank Blaschka */ 41104a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { 41114a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 41124a71df50SFrank Blaschka return -EBUSY; 41134a71df50SFrank Blaschka } 41144a71df50SFrank Blaschka /* check if we need to switch packing state of this queue */ 41154a71df50SFrank Blaschka qeth_switch_to_packing_if_needed(queue); 41164a71df50SFrank Blaschka if (queue->do_pack) { 41174a71df50SFrank Blaschka do_pack = 1; 41184a71df50SFrank Blaschka /* does packet fit in current buffer? */ 41194a71df50SFrank Blaschka if ((QETH_MAX_BUFFER_ELEMENTS(card) - 41204a71df50SFrank Blaschka buffer->next_element_to_fill) < elements_needed) { 41214a71df50SFrank Blaschka /* ... no -> set state PRIMED */ 412264ef8957SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 41234a71df50SFrank Blaschka flush_count++; 41244a71df50SFrank Blaschka queue->next_buf_to_fill = 41254a71df50SFrank Blaschka (queue->next_buf_to_fill + 1) % 41264a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 41270da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 41284a71df50SFrank Blaschka /* we did a step forward, so check buffer state 41294a71df50SFrank Blaschka * again */ 41304a71df50SFrank Blaschka if (atomic_read(&buffer->state) != 41314a71df50SFrank Blaschka QETH_QDIO_BUF_EMPTY) { 4132779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, 4133779e6e1cSJan Glauber flush_count); 41344a71df50SFrank Blaschka atomic_set(&queue->state, 41354a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 41363cdc8a25SJulian Wiedmann rc = -EBUSY; 41373cdc8a25SJulian Wiedmann goto out; 41384a71df50SFrank Blaschka } 41394a71df50SFrank Blaschka } 41404a71df50SFrank Blaschka } 41419c3bfda9SJulian Wiedmann tmp = qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); 41424a71df50SFrank Blaschka queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) % 41434a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 41444a71df50SFrank Blaschka flush_count += tmp; 41454a71df50SFrank Blaschka if (flush_count) 4146779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, flush_count); 41474a71df50SFrank Blaschka else if (!atomic_read(&queue->set_pci_flags_count)) 41484a71df50SFrank Blaschka atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH); 41494a71df50SFrank Blaschka /* 41504a71df50SFrank Blaschka * queue->state will go from LOCKED -> UNLOCKED or from 41514a71df50SFrank Blaschka * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us 41524a71df50SFrank Blaschka * (switch packing state or flush buffer to get another pci flag out). 41534a71df50SFrank Blaschka * In that case we will enter this loop 41544a71df50SFrank Blaschka */ 41554a71df50SFrank Blaschka while (atomic_dec_return(&queue->state)) { 41564a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 41574a71df50SFrank Blaschka /* check if we can go back to non-packing state */ 41583cdc8a25SJulian Wiedmann tmp = qeth_switch_to_nonpacking_if_needed(queue); 41594a71df50SFrank Blaschka /* 41604a71df50SFrank Blaschka * check if we need to flush a packing buffer to get a pci 41614a71df50SFrank Blaschka * flag out on the queue 41624a71df50SFrank Blaschka */ 41633cdc8a25SJulian Wiedmann if (!tmp && !atomic_read(&queue->set_pci_flags_count)) 41643cdc8a25SJulian Wiedmann tmp = qeth_prep_flush_pack_buffer(queue); 41653cdc8a25SJulian Wiedmann if (tmp) { 41663cdc8a25SJulian Wiedmann qeth_flush_buffers(queue, start_index, tmp); 41673cdc8a25SJulian Wiedmann flush_count += tmp; 41684a71df50SFrank Blaschka } 41693cdc8a25SJulian Wiedmann } 41703cdc8a25SJulian Wiedmann out: 41714a71df50SFrank Blaschka /* at this point the queue is UNLOCKED again */ 41724a71df50SFrank Blaschka if (queue->card->options.performance_stats && do_pack) 41734a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += flush_count; 41744a71df50SFrank Blaschka 41754a71df50SFrank Blaschka return rc; 41764a71df50SFrank Blaschka } 41774a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet); 41784a71df50SFrank Blaschka 41794a71df50SFrank Blaschka static int qeth_setadp_promisc_mode_cb(struct qeth_card *card, 41804a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 41814a71df50SFrank Blaschka { 4182686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 41834a71df50SFrank Blaschka struct qeth_ipacmd_setadpparms *setparms; 41844a71df50SFrank Blaschka 4185847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "prmadpcb"); 41864a71df50SFrank Blaschka 41874a71df50SFrank Blaschka setparms = &(cmd->data.setadapterparms); 4188686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) { 41898a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "prmrc%x", cmd->hdr.return_code); 41904a71df50SFrank Blaschka setparms->data.mode = SET_PROMISC_MODE_OFF; 41914a71df50SFrank Blaschka } 41924a71df50SFrank Blaschka card->info.promisc_mode = setparms->data.mode; 41934a71df50SFrank Blaschka return 0; 41944a71df50SFrank Blaschka } 41954a71df50SFrank Blaschka 41964a71df50SFrank Blaschka void qeth_setadp_promisc_mode(struct qeth_card *card) 41974a71df50SFrank Blaschka { 41984a71df50SFrank Blaschka enum qeth_ipa_promisc_modes mode; 41994a71df50SFrank Blaschka struct net_device *dev = card->dev; 42004a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 42014a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 42024a71df50SFrank Blaschka 4203847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setprom"); 42044a71df50SFrank Blaschka 42054a71df50SFrank Blaschka if (((dev->flags & IFF_PROMISC) && 42064a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_ON)) || 42074a71df50SFrank Blaschka (!(dev->flags & IFF_PROMISC) && 42084a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_OFF))) 42094a71df50SFrank Blaschka return; 42104a71df50SFrank Blaschka mode = SET_PROMISC_MODE_OFF; 42114a71df50SFrank Blaschka if (dev->flags & IFF_PROMISC) 42124a71df50SFrank Blaschka mode = SET_PROMISC_MODE_ON; 4213847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "mode:%x", mode); 42144a71df50SFrank Blaschka 42154a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE, 4216ca5b20acSStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr) + 8); 42171aec42bcSThomas Richter if (!iob) 42181aec42bcSThomas Richter return; 4219ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 42204a71df50SFrank Blaschka cmd->data.setadapterparms.data.mode = mode; 42214a71df50SFrank Blaschka qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL); 42224a71df50SFrank Blaschka } 42234a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode); 42244a71df50SFrank Blaschka 42254a71df50SFrank Blaschka struct net_device_stats *qeth_get_stats(struct net_device *dev) 42264a71df50SFrank Blaschka { 42274a71df50SFrank Blaschka struct qeth_card *card; 42284a71df50SFrank Blaschka 4229509e2562SHeiko Carstens card = dev->ml_priv; 42304a71df50SFrank Blaschka 4231847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "getstat"); 42324a71df50SFrank Blaschka 42334a71df50SFrank Blaschka return &card->stats; 42344a71df50SFrank Blaschka } 42354a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_stats); 42364a71df50SFrank Blaschka 42374a71df50SFrank Blaschka static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card, 42384a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 42394a71df50SFrank Blaschka { 4240686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 42414a71df50SFrank Blaschka 4242847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmaccb"); 4243686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) 4244686c97eeSJulian Wiedmann return 0; 42454a71df50SFrank Blaschka 42464a71df50SFrank Blaschka if (!card->options.layer2 || 42474a71df50SFrank Blaschka !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) { 424899f0b85dSJulian Wiedmann ether_addr_copy(card->dev->dev_addr, 424999f0b85dSJulian Wiedmann cmd->data.setadapterparms.data.change_addr.addr); 42504a71df50SFrank Blaschka card->info.mac_bits |= QETH_LAYER2_MAC_READ; 42514a71df50SFrank Blaschka } 42524a71df50SFrank Blaschka return 0; 42534a71df50SFrank Blaschka } 42544a71df50SFrank Blaschka 42554a71df50SFrank Blaschka int qeth_setadpparms_change_macaddr(struct qeth_card *card) 42564a71df50SFrank Blaschka { 42574a71df50SFrank Blaschka int rc; 42584a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 42594a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 42604a71df50SFrank Blaschka 4261847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmac"); 42624a71df50SFrank Blaschka 42634a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_ALTER_MAC_ADDRESS, 4264ca5b20acSStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4265ca5b20acSStefan Raspl sizeof(struct qeth_change_addr)); 42661aec42bcSThomas Richter if (!iob) 42671aec42bcSThomas Richter return -ENOMEM; 4268ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 42694a71df50SFrank Blaschka cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; 427099f0b85dSJulian Wiedmann cmd->data.setadapterparms.data.change_addr.addr_size = ETH_ALEN; 427199f0b85dSJulian Wiedmann ether_addr_copy(cmd->data.setadapterparms.data.change_addr.addr, 427299f0b85dSJulian Wiedmann card->dev->dev_addr); 42734a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_change_macaddr_cb, 42744a71df50SFrank Blaschka NULL); 42754a71df50SFrank Blaschka return rc; 42764a71df50SFrank Blaschka } 42774a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr); 42784a71df50SFrank Blaschka 4279d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, 4280d64ecc22SEinar Lueck struct qeth_reply *reply, unsigned long data) 4281d64ecc22SEinar Lueck { 4282686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 4283d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 42840f54761dSStefan Raspl int fallback = *(int *)reply->param; 4285d64ecc22SEinar Lueck 4286847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setaccb"); 4287686c97eeSJulian Wiedmann if (cmd->hdr.return_code) 4288686c97eeSJulian Wiedmann return 0; 4289686c97eeSJulian Wiedmann qeth_setadpparms_inspect_rc(cmd); 4290d64ecc22SEinar Lueck 4291d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4292d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setaccb"); 4293d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4294d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", 4295d64ecc22SEinar Lueck cmd->data.setadapterparms.hdr.return_code); 42960f54761dSStefan Raspl if (cmd->data.setadapterparms.hdr.return_code != 42970f54761dSStefan Raspl SET_ACCESS_CTRL_RC_SUCCESS) 42980f54761dSStefan Raspl QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", 42990f54761dSStefan Raspl card->gdev->dev.kobj.name, 43000f54761dSStefan Raspl access_ctrl_req->subcmd_code, 43010f54761dSStefan Raspl cmd->data.setadapterparms.hdr.return_code); 4302d64ecc22SEinar Lueck switch (cmd->data.setadapterparms.hdr.return_code) { 4303d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_SUCCESS: 4304d64ecc22SEinar Lueck if (card->options.isolation == ISOLATION_MODE_NONE) { 4305d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4306d64ecc22SEinar Lueck "QDIO data connection isolation is deactivated\n"); 4307d64ecc22SEinar Lueck } else { 4308d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4309d64ecc22SEinar Lueck "QDIO data connection isolation is activated\n"); 4310d64ecc22SEinar Lueck } 4311d64ecc22SEinar Lueck break; 43120f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: 43130f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already " 43140f54761dSStefan Raspl "deactivated\n", dev_name(&card->gdev->dev)); 43150f54761dSStefan Raspl if (fallback) 43160f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43170f54761dSStefan Raspl break; 43180f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: 43190f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already" 43200f54761dSStefan Raspl " activated\n", dev_name(&card->gdev->dev)); 43210f54761dSStefan Raspl if (fallback) 43220f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43230f54761dSStefan Raspl break; 4324d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: 4325d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4326d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4327d64ecc22SEinar Lueck break; 4328d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: 4329d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4330d64ecc22SEinar Lueck "Adapter is dedicated. " 4331d64ecc22SEinar Lueck "QDIO data connection isolation not supported\n"); 43320f54761dSStefan Raspl if (fallback) 43330f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4334d64ecc22SEinar Lueck break; 4335d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: 4336d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4337d64ecc22SEinar Lueck "TSO does not permit QDIO data connection isolation\n"); 43380f54761dSStefan Raspl if (fallback) 43390f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4340d64ecc22SEinar Lueck break; 43410f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED: 43420f54761dSStefan Raspl dev_err(&card->gdev->dev, "The adjacent switch port does not " 43430f54761dSStefan Raspl "support reflective relay mode\n"); 43440f54761dSStefan Raspl if (fallback) 43450f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43460f54761dSStefan Raspl break; 43470f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_FAILED: 43480f54761dSStefan Raspl dev_err(&card->gdev->dev, "The reflective relay mode cannot be " 43490f54761dSStefan Raspl "enabled at the adjacent switch port"); 43500f54761dSStefan Raspl if (fallback) 43510f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43520f54761dSStefan Raspl break; 43530f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED: 43540f54761dSStefan Raspl dev_warn(&card->gdev->dev, "Turning off reflective relay mode " 43550f54761dSStefan Raspl "at the adjacent switch failed\n"); 43560f54761dSStefan Raspl break; 4357d64ecc22SEinar Lueck default: 4358d64ecc22SEinar Lueck /* this should never happen */ 43590f54761dSStefan Raspl if (fallback) 43600f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4361d64ecc22SEinar Lueck break; 4362d64ecc22SEinar Lueck } 4363bbb822a8SUrsula Braun return 0; 4364d64ecc22SEinar Lueck } 4365d64ecc22SEinar Lueck 4366d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, 43670f54761dSStefan Raspl enum qeth_ipa_isolation_modes isolation, int fallback) 4368d64ecc22SEinar Lueck { 4369d64ecc22SEinar Lueck int rc; 4370d64ecc22SEinar Lueck struct qeth_cmd_buffer *iob; 4371d64ecc22SEinar Lueck struct qeth_ipa_cmd *cmd; 4372d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 4373d64ecc22SEinar Lueck 4374847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setacctl"); 4375d64ecc22SEinar Lueck 4376d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setacctl"); 4377d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4378d64ecc22SEinar Lueck 4379d64ecc22SEinar Lueck iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, 4380d64ecc22SEinar Lueck sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4381d64ecc22SEinar Lueck sizeof(struct qeth_set_access_ctrl)); 43821aec42bcSThomas Richter if (!iob) 43831aec42bcSThomas Richter return -ENOMEM; 4384ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 4385d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4386d64ecc22SEinar Lueck access_ctrl_req->subcmd_code = isolation; 4387d64ecc22SEinar Lueck 4388d64ecc22SEinar Lueck rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, 43890f54761dSStefan Raspl &fallback); 4390d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); 4391d64ecc22SEinar Lueck return rc; 4392d64ecc22SEinar Lueck } 4393d64ecc22SEinar Lueck 43940f54761dSStefan Raspl int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) 4395d64ecc22SEinar Lueck { 4396d64ecc22SEinar Lueck int rc = 0; 4397d64ecc22SEinar Lueck 4398847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setactlo"); 4399d64ecc22SEinar Lueck 44005113fec0SUrsula Braun if ((card->info.type == QETH_CARD_TYPE_OSD || 44015113fec0SUrsula Braun card->info.type == QETH_CARD_TYPE_OSX) && 4402d64ecc22SEinar Lueck qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { 4403d64ecc22SEinar Lueck rc = qeth_setadpparms_set_access_ctrl(card, 44040f54761dSStefan Raspl card->options.isolation, fallback); 4405d64ecc22SEinar Lueck if (rc) { 4406d64ecc22SEinar Lueck QETH_DBF_MESSAGE(3, 44075113fec0SUrsula Braun "IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n", 4408d64ecc22SEinar Lueck card->gdev->dev.kobj.name, 4409d64ecc22SEinar Lueck rc); 44100f54761dSStefan Raspl rc = -EOPNOTSUPP; 4411d64ecc22SEinar Lueck } 4412d64ecc22SEinar Lueck } else if (card->options.isolation != ISOLATION_MODE_NONE) { 4413d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 4414d64ecc22SEinar Lueck 4415d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4416d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4417d64ecc22SEinar Lueck rc = -EOPNOTSUPP; 4418d64ecc22SEinar Lueck } 4419d64ecc22SEinar Lueck return rc; 4420d64ecc22SEinar Lueck } 4421d64ecc22SEinar Lueck EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); 4422d64ecc22SEinar Lueck 44234a71df50SFrank Blaschka void qeth_tx_timeout(struct net_device *dev) 44244a71df50SFrank Blaschka { 44254a71df50SFrank Blaschka struct qeth_card *card; 44264a71df50SFrank Blaschka 4427509e2562SHeiko Carstens card = dev->ml_priv; 4428847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "txtimeo"); 44294a71df50SFrank Blaschka card->stats.tx_errors++; 44304a71df50SFrank Blaschka qeth_schedule_recovery(card); 44314a71df50SFrank Blaschka } 44324a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_tx_timeout); 44334a71df50SFrank Blaschka 4434942d6984SJulian Wiedmann static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) 44354a71df50SFrank Blaschka { 4436509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 44374a71df50SFrank Blaschka int rc = 0; 44384a71df50SFrank Blaschka 44394a71df50SFrank Blaschka switch (regnum) { 44404a71df50SFrank Blaschka case MII_BMCR: /* Basic mode control register */ 44414a71df50SFrank Blaschka rc = BMCR_FULLDPLX; 44424a71df50SFrank Blaschka if ((card->info.link_type != QETH_LINK_TYPE_GBIT_ETH) && 44434a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_OSN) && 44444a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_10GBIT_ETH)) 44454a71df50SFrank Blaschka rc |= BMCR_SPEED100; 44464a71df50SFrank Blaschka break; 44474a71df50SFrank Blaschka case MII_BMSR: /* Basic mode status register */ 44484a71df50SFrank Blaschka rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS | 44494a71df50SFrank Blaschka BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL | 44504a71df50SFrank Blaschka BMSR_100BASE4; 44514a71df50SFrank Blaschka break; 44524a71df50SFrank Blaschka case MII_PHYSID1: /* PHYS ID 1 */ 44534a71df50SFrank Blaschka rc = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 8) | 44544a71df50SFrank Blaschka dev->dev_addr[2]; 44554a71df50SFrank Blaschka rc = (rc >> 5) & 0xFFFF; 44564a71df50SFrank Blaschka break; 44574a71df50SFrank Blaschka case MII_PHYSID2: /* PHYS ID 2 */ 44584a71df50SFrank Blaschka rc = (dev->dev_addr[2] << 10) & 0xFFFF; 44594a71df50SFrank Blaschka break; 44604a71df50SFrank Blaschka case MII_ADVERTISE: /* Advertisement control reg */ 44614a71df50SFrank Blaschka rc = ADVERTISE_ALL; 44624a71df50SFrank Blaschka break; 44634a71df50SFrank Blaschka case MII_LPA: /* Link partner ability reg */ 44644a71df50SFrank Blaschka rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL | 44654a71df50SFrank Blaschka LPA_100BASE4 | LPA_LPACK; 44664a71df50SFrank Blaschka break; 44674a71df50SFrank Blaschka case MII_EXPANSION: /* Expansion register */ 44684a71df50SFrank Blaschka break; 44694a71df50SFrank Blaschka case MII_DCOUNTER: /* disconnect counter */ 44704a71df50SFrank Blaschka break; 44714a71df50SFrank Blaschka case MII_FCSCOUNTER: /* false carrier counter */ 44724a71df50SFrank Blaschka break; 44734a71df50SFrank Blaschka case MII_NWAYTEST: /* N-way auto-neg test register */ 44744a71df50SFrank Blaschka break; 44754a71df50SFrank Blaschka case MII_RERRCOUNTER: /* rx error counter */ 44764a71df50SFrank Blaschka rc = card->stats.rx_errors; 44774a71df50SFrank Blaschka break; 44784a71df50SFrank Blaschka case MII_SREVISION: /* silicon revision */ 44794a71df50SFrank Blaschka break; 44804a71df50SFrank Blaschka case MII_RESV1: /* reserved 1 */ 44814a71df50SFrank Blaschka break; 44824a71df50SFrank Blaschka case MII_LBRERROR: /* loopback, rx, bypass error */ 44834a71df50SFrank Blaschka break; 44844a71df50SFrank Blaschka case MII_PHYADDR: /* physical address */ 44854a71df50SFrank Blaschka break; 44864a71df50SFrank Blaschka case MII_RESV2: /* reserved 2 */ 44874a71df50SFrank Blaschka break; 44884a71df50SFrank Blaschka case MII_TPISTATUS: /* TPI status for 10mbps */ 44894a71df50SFrank Blaschka break; 44904a71df50SFrank Blaschka case MII_NCONFIG: /* network interface config */ 44914a71df50SFrank Blaschka break; 44924a71df50SFrank Blaschka default: 44934a71df50SFrank Blaschka break; 44944a71df50SFrank Blaschka } 44954a71df50SFrank Blaschka return rc; 44964a71df50SFrank Blaschka } 44974a71df50SFrank Blaschka 44984a71df50SFrank Blaschka static int qeth_send_ipa_snmp_cmd(struct qeth_card *card, 44994a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, int len, 45004a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply *, 45014a71df50SFrank Blaschka unsigned long), 45024a71df50SFrank Blaschka void *reply_param) 45034a71df50SFrank Blaschka { 45044a71df50SFrank Blaschka u16 s1, s2; 45054a71df50SFrank Blaschka 4506847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendsnmp"); 45074a71df50SFrank Blaschka 45084a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 45094a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 45104a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 45114a71df50SFrank Blaschka /* adjust PDU length fields in IPA_PDU_HEADER */ 45124a71df50SFrank Blaschka s1 = (u32) IPA_PDU_HEADER_SIZE + len; 45134a71df50SFrank Blaschka s2 = (u32) len; 45144a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2); 45154a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2); 45164a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2); 45174a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2); 45184a71df50SFrank Blaschka return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob, 45194a71df50SFrank Blaschka reply_cb, reply_param); 45204a71df50SFrank Blaschka } 45214a71df50SFrank Blaschka 45224a71df50SFrank Blaschka static int qeth_snmp_command_cb(struct qeth_card *card, 45234a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long sdata) 45244a71df50SFrank Blaschka { 45254a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 45264a71df50SFrank Blaschka struct qeth_arp_query_info *qinfo; 45274a71df50SFrank Blaschka struct qeth_snmp_cmd *snmp; 45284a71df50SFrank Blaschka unsigned char *data; 45294a71df50SFrank Blaschka __u16 data_len; 45304a71df50SFrank Blaschka 4531847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snpcmdcb"); 45324a71df50SFrank Blaschka 45334a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) sdata; 45344a71df50SFrank Blaschka data = (unsigned char *)((char *)cmd - reply->offset); 45354a71df50SFrank Blaschka qinfo = (struct qeth_arp_query_info *) reply->param; 45364a71df50SFrank Blaschka snmp = &cmd->data.setadapterparms.data.snmp; 45374a71df50SFrank Blaschka 45384a71df50SFrank Blaschka if (cmd->hdr.return_code) { 45398a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "scer1%x", cmd->hdr.return_code); 45404a71df50SFrank Blaschka return 0; 45414a71df50SFrank Blaschka } 45424a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.return_code) { 45434a71df50SFrank Blaschka cmd->hdr.return_code = 45444a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 45458a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "scer2%x", cmd->hdr.return_code); 45464a71df50SFrank Blaschka return 0; 45474a71df50SFrank Blaschka } 45484a71df50SFrank Blaschka data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data)); 45494a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) 45504a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->data - (char *)cmd); 45514a71df50SFrank Blaschka else 45524a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->request - (char *)cmd); 45534a71df50SFrank Blaschka 45544a71df50SFrank Blaschka /* check if there is enough room in userspace */ 45554a71df50SFrank Blaschka if ((qinfo->udata_len - qinfo->udata_offset) < data_len) { 4556847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM); 4557e0a8114cSUrsula Braun cmd->hdr.return_code = IPA_RC_ENOMEM; 45584a71df50SFrank Blaschka return 0; 45594a71df50SFrank Blaschka } 4560847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "snore%i", 45614a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4562847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "sseqn%i", 45634a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 45644a71df50SFrank Blaschka /*copy entries to user buffer*/ 45654a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) { 45664a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 45674a71df50SFrank Blaschka (char *)snmp, 45684a71df50SFrank Blaschka data_len + offsetof(struct qeth_snmp_cmd, data)); 45694a71df50SFrank Blaschka qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data); 45704a71df50SFrank Blaschka } else { 45714a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 45724a71df50SFrank Blaschka (char *)&snmp->request, data_len); 45734a71df50SFrank Blaschka } 45744a71df50SFrank Blaschka qinfo->udata_offset += data_len; 45754a71df50SFrank Blaschka /* check if all replies received ... */ 4576847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srtot%i", 45774a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4578847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srseq%i", 45794a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 45804a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 45814a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 45824a71df50SFrank Blaschka return 1; 45834a71df50SFrank Blaschka return 0; 45844a71df50SFrank Blaschka } 45854a71df50SFrank Blaschka 4586942d6984SJulian Wiedmann static int qeth_snmp_command(struct qeth_card *card, char __user *udata) 45874a71df50SFrank Blaschka { 45884a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 45894a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 45904a71df50SFrank Blaschka struct qeth_snmp_ureq *ureq; 45916fb392b1SUrsula Braun unsigned int req_len; 45924a71df50SFrank Blaschka struct qeth_arp_query_info qinfo = {0, }; 45934a71df50SFrank Blaschka int rc = 0; 45944a71df50SFrank Blaschka 4595847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snmpcmd"); 45964a71df50SFrank Blaschka 45974a71df50SFrank Blaschka if (card->info.guestlan) 45984a71df50SFrank Blaschka return -EOPNOTSUPP; 45994a71df50SFrank Blaschka 46004a71df50SFrank Blaschka if ((!qeth_adp_supported(card, IPA_SETADP_SET_SNMP_CONTROL)) && 46014a71df50SFrank Blaschka (!card->options.layer2)) { 46024a71df50SFrank Blaschka return -EOPNOTSUPP; 46034a71df50SFrank Blaschka } 46044a71df50SFrank Blaschka /* skip 4 bytes (data_len struct member) to get req_len */ 46054a71df50SFrank Blaschka if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int))) 46064a71df50SFrank Blaschka return -EFAULT; 46076fb392b1SUrsula Braun if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - 46086fb392b1SUrsula Braun sizeof(struct qeth_ipacmd_hdr) - 46096fb392b1SUrsula Braun sizeof(struct qeth_ipacmd_setadpparms_hdr))) 46106fb392b1SUrsula Braun return -EINVAL; 46114986f3f0SJulia Lawall ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr)); 46124986f3f0SJulia Lawall if (IS_ERR(ureq)) { 4613847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "snmpnome"); 46144986f3f0SJulia Lawall return PTR_ERR(ureq); 46154a71df50SFrank Blaschka } 46164a71df50SFrank Blaschka qinfo.udata_len = ureq->hdr.data_len; 46174a71df50SFrank Blaschka qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); 46184a71df50SFrank Blaschka if (!qinfo.udata) { 46194a71df50SFrank Blaschka kfree(ureq); 46204a71df50SFrank Blaschka return -ENOMEM; 46214a71df50SFrank Blaschka } 46224a71df50SFrank Blaschka qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr); 46234a71df50SFrank Blaschka 46244a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, 46254a71df50SFrank Blaschka QETH_SNMP_SETADP_CMDLENGTH + req_len); 46261aec42bcSThomas Richter if (!iob) { 46271aec42bcSThomas Richter rc = -ENOMEM; 46281aec42bcSThomas Richter goto out; 46291aec42bcSThomas Richter } 4630ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 46314a71df50SFrank Blaschka memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len); 46324a71df50SFrank Blaschka rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len, 46334a71df50SFrank Blaschka qeth_snmp_command_cb, (void *)&qinfo); 46344a71df50SFrank Blaschka if (rc) 463514cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "SNMP command failed on %s: (0x%x)\n", 46364a71df50SFrank Blaschka QETH_CARD_IFNAME(card), rc); 46374a71df50SFrank Blaschka else { 46384a71df50SFrank Blaschka if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) 46394a71df50SFrank Blaschka rc = -EFAULT; 46404a71df50SFrank Blaschka } 46411aec42bcSThomas Richter out: 46424a71df50SFrank Blaschka kfree(ureq); 46434a71df50SFrank Blaschka kfree(qinfo.udata); 46444a71df50SFrank Blaschka return rc; 46454a71df50SFrank Blaschka } 46464a71df50SFrank Blaschka 4647c3ab96f3SFrank Blaschka static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, 4648c3ab96f3SFrank Blaschka struct qeth_reply *reply, unsigned long data) 4649c3ab96f3SFrank Blaschka { 4650686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; 4651c3ab96f3SFrank Blaschka struct qeth_qoat_priv *priv; 4652c3ab96f3SFrank Blaschka char *resdata; 4653c3ab96f3SFrank Blaschka int resdatalen; 4654c3ab96f3SFrank Blaschka 4655c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcb"); 4656686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) 4657686c97eeSJulian Wiedmann return 0; 4658c3ab96f3SFrank Blaschka 4659c3ab96f3SFrank Blaschka priv = (struct qeth_qoat_priv *)reply->param; 4660c3ab96f3SFrank Blaschka resdatalen = cmd->data.setadapterparms.hdr.cmdlength; 4661c3ab96f3SFrank Blaschka resdata = (char *)data + 28; 4662c3ab96f3SFrank Blaschka 4663c3ab96f3SFrank Blaschka if (resdatalen > (priv->buffer_len - priv->response_len)) { 4664c3ab96f3SFrank Blaschka cmd->hdr.return_code = IPA_RC_FFFF; 4665c3ab96f3SFrank Blaschka return 0; 4666c3ab96f3SFrank Blaschka } 4667c3ab96f3SFrank Blaschka 4668c3ab96f3SFrank Blaschka memcpy((priv->buffer + priv->response_len), resdata, 4669c3ab96f3SFrank Blaschka resdatalen); 4670c3ab96f3SFrank Blaschka priv->response_len += resdatalen; 4671c3ab96f3SFrank Blaschka 4672c3ab96f3SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 4673c3ab96f3SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 4674c3ab96f3SFrank Blaschka return 1; 4675c3ab96f3SFrank Blaschka return 0; 4676c3ab96f3SFrank Blaschka } 4677c3ab96f3SFrank Blaschka 4678942d6984SJulian Wiedmann static int qeth_query_oat_command(struct qeth_card *card, char __user *udata) 4679c3ab96f3SFrank Blaschka { 4680c3ab96f3SFrank Blaschka int rc = 0; 4681c3ab96f3SFrank Blaschka struct qeth_cmd_buffer *iob; 4682c3ab96f3SFrank Blaschka struct qeth_ipa_cmd *cmd; 4683c3ab96f3SFrank Blaschka struct qeth_query_oat *oat_req; 4684c3ab96f3SFrank Blaschka struct qeth_query_oat_data oat_data; 4685c3ab96f3SFrank Blaschka struct qeth_qoat_priv priv; 4686c3ab96f3SFrank Blaschka void __user *tmp; 4687c3ab96f3SFrank Blaschka 4688c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcmd"); 4689c3ab96f3SFrank Blaschka 4690c3ab96f3SFrank Blaschka if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { 4691c3ab96f3SFrank Blaschka rc = -EOPNOTSUPP; 4692c3ab96f3SFrank Blaschka goto out; 4693c3ab96f3SFrank Blaschka } 4694c3ab96f3SFrank Blaschka 4695c3ab96f3SFrank Blaschka if (copy_from_user(&oat_data, udata, 4696c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) { 4697c3ab96f3SFrank Blaschka rc = -EFAULT; 4698c3ab96f3SFrank Blaschka goto out; 4699c3ab96f3SFrank Blaschka } 4700c3ab96f3SFrank Blaschka 4701c3ab96f3SFrank Blaschka priv.buffer_len = oat_data.buffer_len; 4702c3ab96f3SFrank Blaschka priv.response_len = 0; 4703aec45e85SWenjia Zhang priv.buffer = vzalloc(oat_data.buffer_len); 4704c3ab96f3SFrank Blaschka if (!priv.buffer) { 4705c3ab96f3SFrank Blaschka rc = -ENOMEM; 4706c3ab96f3SFrank Blaschka goto out; 4707c3ab96f3SFrank Blaschka } 4708c3ab96f3SFrank Blaschka 4709c3ab96f3SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, 4710c3ab96f3SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4711c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat)); 47121aec42bcSThomas Richter if (!iob) { 47131aec42bcSThomas Richter rc = -ENOMEM; 47141aec42bcSThomas Richter goto out_free; 47151aec42bcSThomas Richter } 4716ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 4717c3ab96f3SFrank Blaschka oat_req = &cmd->data.setadapterparms.data.query_oat; 4718c3ab96f3SFrank Blaschka oat_req->subcmd_code = oat_data.command; 4719c3ab96f3SFrank Blaschka 4720c3ab96f3SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb, 4721c3ab96f3SFrank Blaschka &priv); 4722c3ab96f3SFrank Blaschka if (!rc) { 4723c3ab96f3SFrank Blaschka if (is_compat_task()) 4724c3ab96f3SFrank Blaschka tmp = compat_ptr(oat_data.ptr); 4725c3ab96f3SFrank Blaschka else 4726c3ab96f3SFrank Blaschka tmp = (void __user *)(unsigned long)oat_data.ptr; 4727c3ab96f3SFrank Blaschka 4728c3ab96f3SFrank Blaschka if (copy_to_user(tmp, priv.buffer, 4729c3ab96f3SFrank Blaschka priv.response_len)) { 4730c3ab96f3SFrank Blaschka rc = -EFAULT; 4731c3ab96f3SFrank Blaschka goto out_free; 4732c3ab96f3SFrank Blaschka } 4733c3ab96f3SFrank Blaschka 4734c3ab96f3SFrank Blaschka oat_data.response_len = priv.response_len; 4735c3ab96f3SFrank Blaschka 4736c3ab96f3SFrank Blaschka if (copy_to_user(udata, &oat_data, 4737c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) 4738c3ab96f3SFrank Blaschka rc = -EFAULT; 4739c3ab96f3SFrank Blaschka } else 4740c3ab96f3SFrank Blaschka if (rc == IPA_RC_FFFF) 4741c3ab96f3SFrank Blaschka rc = -EFAULT; 4742c3ab96f3SFrank Blaschka 4743c3ab96f3SFrank Blaschka out_free: 4744aec45e85SWenjia Zhang vfree(priv.buffer); 4745c3ab96f3SFrank Blaschka out: 4746c3ab96f3SFrank Blaschka return rc; 4747c3ab96f3SFrank Blaschka } 4748c3ab96f3SFrank Blaschka 4749e71e4072SHeiko Carstens static int qeth_query_card_info_cb(struct qeth_card *card, 475002d5cb5bSEugene Crosser struct qeth_reply *reply, unsigned long data) 475102d5cb5bSEugene Crosser { 4752686c97eeSJulian Wiedmann struct carrier_info *carrier_info = (struct carrier_info *)reply->param; 4753686c97eeSJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; 475402d5cb5bSEugene Crosser struct qeth_query_card_info *card_info; 475502d5cb5bSEugene Crosser 475602d5cb5bSEugene Crosser QETH_CARD_TEXT(card, 2, "qcrdincb"); 4757686c97eeSJulian Wiedmann if (qeth_setadpparms_inspect_rc(cmd)) 4758686c97eeSJulian Wiedmann return 0; 4759686c97eeSJulian Wiedmann 476002d5cb5bSEugene Crosser card_info = &cmd->data.setadapterparms.data.card_info; 476102d5cb5bSEugene Crosser carrier_info->card_type = card_info->card_type; 476202d5cb5bSEugene Crosser carrier_info->port_mode = card_info->port_mode; 476302d5cb5bSEugene Crosser carrier_info->port_speed = card_info->port_speed; 476402d5cb5bSEugene Crosser return 0; 476502d5cb5bSEugene Crosser } 476602d5cb5bSEugene Crosser 4767bca51650SThomas Richter static int qeth_query_card_info(struct qeth_card *card, 476802d5cb5bSEugene Crosser struct carrier_info *carrier_info) 476902d5cb5bSEugene Crosser { 477002d5cb5bSEugene Crosser struct qeth_cmd_buffer *iob; 477102d5cb5bSEugene Crosser 477202d5cb5bSEugene Crosser QETH_CARD_TEXT(card, 2, "qcrdinfo"); 477302d5cb5bSEugene Crosser if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) 477402d5cb5bSEugene Crosser return -EOPNOTSUPP; 477502d5cb5bSEugene Crosser iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 477602d5cb5bSEugene Crosser sizeof(struct qeth_ipacmd_setadpparms_hdr)); 47771aec42bcSThomas Richter if (!iob) 47781aec42bcSThomas Richter return -ENOMEM; 477902d5cb5bSEugene Crosser return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, 478002d5cb5bSEugene Crosser (void *)carrier_info); 478102d5cb5bSEugene Crosser } 478202d5cb5bSEugene Crosser 4783ec61bd2fSJulian Wiedmann /** 4784ec61bd2fSJulian Wiedmann * qeth_vm_request_mac() - Request a hypervisor-managed MAC address 4785ec61bd2fSJulian Wiedmann * @card: pointer to a qeth_card 4786ec61bd2fSJulian Wiedmann * 4787ec61bd2fSJulian Wiedmann * Returns 4788ec61bd2fSJulian Wiedmann * 0, if a MAC address has been set for the card's netdevice 4789ec61bd2fSJulian Wiedmann * a return code, for various error conditions 4790ec61bd2fSJulian Wiedmann */ 4791ec61bd2fSJulian Wiedmann int qeth_vm_request_mac(struct qeth_card *card) 4792ec61bd2fSJulian Wiedmann { 4793ec61bd2fSJulian Wiedmann struct diag26c_mac_resp *response; 4794ec61bd2fSJulian Wiedmann struct diag26c_mac_req *request; 4795ec61bd2fSJulian Wiedmann struct ccw_dev_id id; 4796ec61bd2fSJulian Wiedmann int rc; 4797ec61bd2fSJulian Wiedmann 4798ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "vmreqmac"); 4799ec61bd2fSJulian Wiedmann 4800ec61bd2fSJulian Wiedmann request = kzalloc(sizeof(*request), GFP_KERNEL | GFP_DMA); 4801ec61bd2fSJulian Wiedmann response = kzalloc(sizeof(*response), GFP_KERNEL | GFP_DMA); 4802ec61bd2fSJulian Wiedmann if (!request || !response) { 4803ec61bd2fSJulian Wiedmann rc = -ENOMEM; 4804ec61bd2fSJulian Wiedmann goto out; 4805ec61bd2fSJulian Wiedmann } 4806ec61bd2fSJulian Wiedmann 480746646105SJulian Wiedmann ccw_device_get_id(CARD_DDEV(card), &id); 4808ec61bd2fSJulian Wiedmann request->resp_buf_len = sizeof(*response); 4809ec61bd2fSJulian Wiedmann request->resp_version = DIAG26C_VERSION2; 4810ec61bd2fSJulian Wiedmann request->op_code = DIAG26C_GET_MAC; 4811ec61bd2fSJulian Wiedmann request->devno = id.devno; 4812ec61bd2fSJulian Wiedmann 4813615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, request, sizeof(*request)); 4814ec61bd2fSJulian Wiedmann rc = diag26c(request, response, DIAG26C_MAC_SERVICES); 4815615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, request, sizeof(*request)); 4816ec61bd2fSJulian Wiedmann if (rc) 4817ec61bd2fSJulian Wiedmann goto out; 4818615dff22SJulian Wiedmann QETH_DBF_HEX(CTRL, 2, response, sizeof(*response)); 4819ec61bd2fSJulian Wiedmann 4820ec61bd2fSJulian Wiedmann if (request->resp_buf_len < sizeof(*response) || 4821ec61bd2fSJulian Wiedmann response->version != request->resp_version) { 4822ec61bd2fSJulian Wiedmann rc = -EIO; 4823ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "badresp"); 4824ec61bd2fSJulian Wiedmann QETH_DBF_HEX(SETUP, 2, &request->resp_buf_len, 4825ec61bd2fSJulian Wiedmann sizeof(request->resp_buf_len)); 4826ec61bd2fSJulian Wiedmann } else if (!is_valid_ether_addr(response->mac)) { 4827ec61bd2fSJulian Wiedmann rc = -EINVAL; 4828ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "badmac"); 4829ec61bd2fSJulian Wiedmann QETH_DBF_HEX(SETUP, 2, response->mac, ETH_ALEN); 4830ec61bd2fSJulian Wiedmann } else { 4831ec61bd2fSJulian Wiedmann ether_addr_copy(card->dev->dev_addr, response->mac); 4832ec61bd2fSJulian Wiedmann } 4833ec61bd2fSJulian Wiedmann 4834ec61bd2fSJulian Wiedmann out: 4835ec61bd2fSJulian Wiedmann kfree(response); 4836ec61bd2fSJulian Wiedmann kfree(request); 4837ec61bd2fSJulian Wiedmann return rc; 4838ec61bd2fSJulian Wiedmann } 4839ec61bd2fSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_vm_request_mac); 4840ec61bd2fSJulian Wiedmann 4841cef6ff22SJulian Wiedmann static int qeth_get_qdio_q_format(struct qeth_card *card) 48424a71df50SFrank Blaschka { 4843aa59004bSJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_IQD) 4844aa59004bSJulian Wiedmann return QDIO_IQDIO_QFMT; 4845aa59004bSJulian Wiedmann else 4846aa59004bSJulian Wiedmann return QDIO_QETH_QFMT; 48474a71df50SFrank Blaschka } 48484a71df50SFrank Blaschka 4849d0ff1f52SUrsula Braun static void qeth_determine_capabilities(struct qeth_card *card) 4850d0ff1f52SUrsula Braun { 4851d0ff1f52SUrsula Braun int rc; 4852d0ff1f52SUrsula Braun int length; 4853d0ff1f52SUrsula Braun char *prcd; 4854d0ff1f52SUrsula Braun struct ccw_device *ddev; 4855d0ff1f52SUrsula Braun int ddev_offline = 0; 4856d0ff1f52SUrsula Braun 4857d0ff1f52SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "detcapab"); 4858d0ff1f52SUrsula Braun ddev = CARD_DDEV(card); 4859d0ff1f52SUrsula Braun if (!ddev->online) { 4860d0ff1f52SUrsula Braun ddev_offline = 1; 4861d0ff1f52SUrsula Braun rc = ccw_device_set_online(ddev); 4862d0ff1f52SUrsula Braun if (rc) { 4863d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 4864d0ff1f52SUrsula Braun goto out; 4865d0ff1f52SUrsula Braun } 4866d0ff1f52SUrsula Braun } 4867d0ff1f52SUrsula Braun 4868d0ff1f52SUrsula Braun rc = qeth_read_conf_data(card, (void **) &prcd, &length); 4869d0ff1f52SUrsula Braun if (rc) { 4870d0ff1f52SUrsula Braun QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", 4871d0ff1f52SUrsula Braun dev_name(&card->gdev->dev), rc); 4872d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 4873d0ff1f52SUrsula Braun goto out_offline; 4874d0ff1f52SUrsula Braun } 4875d0ff1f52SUrsula Braun qeth_configure_unitaddr(card, prcd); 48767e665afbSUrsula Braun if (ddev_offline) 4877d0ff1f52SUrsula Braun qeth_configure_blkt_default(card, prcd); 4878d0ff1f52SUrsula Braun kfree(prcd); 4879d0ff1f52SUrsula Braun 4880d0ff1f52SUrsula Braun rc = qdio_get_ssqd_desc(ddev, &card->ssqd); 4881d0ff1f52SUrsula Braun if (rc) 4882d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 4883d0ff1f52SUrsula Braun 48840da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt); 4885bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac1:%02x", card->ssqd.qdioac1); 4886bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac2:%04x", card->ssqd.qdioac2); 4887bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac3:%04x", card->ssqd.qdioac3); 48880da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt); 48890da9581dSEinar Lueck if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) || 48900da9581dSEinar Lueck ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) || 48910da9581dSEinar Lueck ((card->ssqd.qdioac3 & CHSC_AC3_FORMAT2_CQ_AVAILABLE) == 0))) { 48920da9581dSEinar Lueck dev_info(&card->gdev->dev, 48930da9581dSEinar Lueck "Completion Queueing supported\n"); 48940da9581dSEinar Lueck } else { 48950da9581dSEinar Lueck card->options.cq = QETH_CQ_NOTAVAILABLE; 48960da9581dSEinar Lueck } 48970da9581dSEinar Lueck 48980da9581dSEinar Lueck 4899d0ff1f52SUrsula Braun out_offline: 4900d0ff1f52SUrsula Braun if (ddev_offline == 1) 4901d0ff1f52SUrsula Braun ccw_device_set_offline(ddev); 4902d0ff1f52SUrsula Braun out: 4903d0ff1f52SUrsula Braun return; 4904d0ff1f52SUrsula Braun } 4905d0ff1f52SUrsula Braun 4906cef6ff22SJulian Wiedmann static void qeth_qdio_establish_cq(struct qeth_card *card, 49070da9581dSEinar Lueck struct qdio_buffer **in_sbal_ptrs, 4908cef6ff22SJulian Wiedmann void (**queue_start_poll) 4909cef6ff22SJulian Wiedmann (struct ccw_device *, int, 4910cef6ff22SJulian Wiedmann unsigned long)) 4911cef6ff22SJulian Wiedmann { 49120da9581dSEinar Lueck int i; 49130da9581dSEinar Lueck 49140da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 49150da9581dSEinar Lueck int offset = QDIO_MAX_BUFFERS_PER_Q * 49160da9581dSEinar Lueck (card->qdio.no_in_queues - 1); 49170da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 49180da9581dSEinar Lueck in_sbal_ptrs[offset + i] = (struct qdio_buffer *) 49190da9581dSEinar Lueck virt_to_phys(card->qdio.c_q->bufs[i].buffer); 49200da9581dSEinar Lueck } 49210da9581dSEinar Lueck 49220da9581dSEinar Lueck queue_start_poll[card->qdio.no_in_queues - 1] = NULL; 49230da9581dSEinar Lueck } 49240da9581dSEinar Lueck } 49250da9581dSEinar Lueck 49264a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *card) 49274a71df50SFrank Blaschka { 49284a71df50SFrank Blaschka struct qdio_initialize init_data; 49294a71df50SFrank Blaschka char *qib_param_field; 49304a71df50SFrank Blaschka struct qdio_buffer **in_sbal_ptrs; 4931104ea556Sfrank.blaschka@de.ibm.com void (**queue_start_poll) (struct ccw_device *, int, unsigned long); 49324a71df50SFrank Blaschka struct qdio_buffer **out_sbal_ptrs; 49334a71df50SFrank Blaschka int i, j, k; 49344a71df50SFrank Blaschka int rc = 0; 49354a71df50SFrank Blaschka 4936d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "qdioest"); 49374a71df50SFrank Blaschka 49386396bb22SKees Cook qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q, 49394a71df50SFrank Blaschka GFP_KERNEL); 4940104ea556Sfrank.blaschka@de.ibm.com if (!qib_param_field) { 4941104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4942104ea556Sfrank.blaschka@de.ibm.com goto out_free_nothing; 4943104ea556Sfrank.blaschka@de.ibm.com } 49444a71df50SFrank Blaschka 49454a71df50SFrank Blaschka qeth_create_qib_param_field(card, qib_param_field); 49464a71df50SFrank Blaschka qeth_create_qib_param_field_blkt(card, qib_param_field); 49474a71df50SFrank Blaschka 49486396bb22SKees Cook in_sbal_ptrs = kcalloc(card->qdio.no_in_queues * QDIO_MAX_BUFFERS_PER_Q, 49496396bb22SKees Cook sizeof(void *), 49504a71df50SFrank Blaschka GFP_KERNEL); 49514a71df50SFrank Blaschka if (!in_sbal_ptrs) { 4952104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4953104ea556Sfrank.blaschka@de.ibm.com goto out_free_qib_param; 49544a71df50SFrank Blaschka } 49550da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 49564a71df50SFrank Blaschka in_sbal_ptrs[i] = (struct qdio_buffer *) 49574a71df50SFrank Blaschka virt_to_phys(card->qdio.in_q->bufs[i].buffer); 49580da9581dSEinar Lueck } 49594a71df50SFrank Blaschka 49606396bb22SKees Cook queue_start_poll = kcalloc(card->qdio.no_in_queues, sizeof(void *), 49610da9581dSEinar Lueck GFP_KERNEL); 4962104ea556Sfrank.blaschka@de.ibm.com if (!queue_start_poll) { 4963104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4964104ea556Sfrank.blaschka@de.ibm.com goto out_free_in_sbals; 4965104ea556Sfrank.blaschka@de.ibm.com } 49660da9581dSEinar Lueck for (i = 0; i < card->qdio.no_in_queues; ++i) 49677bcd64ebSJulian Wiedmann queue_start_poll[i] = qeth_qdio_start_poll; 49680da9581dSEinar Lueck 49690da9581dSEinar Lueck qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); 4970104ea556Sfrank.blaschka@de.ibm.com 49714a71df50SFrank Blaschka out_sbal_ptrs = 49726396bb22SKees Cook kcalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q, 49736396bb22SKees Cook sizeof(void *), 49746396bb22SKees Cook GFP_KERNEL); 49754a71df50SFrank Blaschka if (!out_sbal_ptrs) { 4976104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4977104ea556Sfrank.blaschka@de.ibm.com goto out_free_queue_start_poll; 49784a71df50SFrank Blaschka } 49794a71df50SFrank Blaschka for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i) 49804a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k) { 49814a71df50SFrank Blaschka out_sbal_ptrs[k] = (struct qdio_buffer *)virt_to_phys( 49820da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]->buffer); 49834a71df50SFrank Blaschka } 49844a71df50SFrank Blaschka 49854a71df50SFrank Blaschka memset(&init_data, 0, sizeof(struct qdio_initialize)); 49864a71df50SFrank Blaschka init_data.cdev = CARD_DDEV(card); 49874a71df50SFrank Blaschka init_data.q_format = qeth_get_qdio_q_format(card); 49884a71df50SFrank Blaschka init_data.qib_param_field_format = 0; 49894a71df50SFrank Blaschka init_data.qib_param_field = qib_param_field; 49900da9581dSEinar Lueck init_data.no_input_qs = card->qdio.no_in_queues; 49914a71df50SFrank Blaschka init_data.no_output_qs = card->qdio.no_out_queues; 49927bcd64ebSJulian Wiedmann init_data.input_handler = qeth_qdio_input_handler; 49937bcd64ebSJulian Wiedmann init_data.output_handler = qeth_qdio_output_handler; 4994e58b0d90SSteffen Maier init_data.queue_start_poll_array = queue_start_poll; 49954a71df50SFrank Blaschka init_data.int_parm = (unsigned long) card; 49964a71df50SFrank Blaschka init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; 49974a71df50SFrank Blaschka init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; 49980da9581dSEinar Lueck init_data.output_sbal_state_array = card->qdio.out_bufstates; 49993d6c76ffSJan Glauber init_data.scan_threshold = 50000fa81cd4SStefan Raspl (card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32; 50014a71df50SFrank Blaschka 50024a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, 50034a71df50SFrank Blaschka QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { 5004cc961d40SJan Glauber rc = qdio_allocate(&init_data); 5005cc961d40SJan Glauber if (rc) { 50064a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 5007cc961d40SJan Glauber goto out; 50084a71df50SFrank Blaschka } 5009cc961d40SJan Glauber rc = qdio_establish(&init_data); 5010cc961d40SJan Glauber if (rc) { 5011cc961d40SJan Glauber atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 5012cc961d40SJan Glauber qdio_free(CARD_DDEV(card)); 5013cc961d40SJan Glauber } 5014cc961d40SJan Glauber } 50150da9581dSEinar Lueck 50160da9581dSEinar Lueck switch (card->options.cq) { 50170da9581dSEinar Lueck case QETH_CQ_ENABLED: 50180da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support enabled"); 50190da9581dSEinar Lueck break; 50200da9581dSEinar Lueck case QETH_CQ_DISABLED: 50210da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support disabled"); 50220da9581dSEinar Lueck break; 50230da9581dSEinar Lueck default: 50240da9581dSEinar Lueck break; 50250da9581dSEinar Lueck } 5026cc961d40SJan Glauber out: 50274a71df50SFrank Blaschka kfree(out_sbal_ptrs); 5028104ea556Sfrank.blaschka@de.ibm.com out_free_queue_start_poll: 5029104ea556Sfrank.blaschka@de.ibm.com kfree(queue_start_poll); 5030104ea556Sfrank.blaschka@de.ibm.com out_free_in_sbals: 50314a71df50SFrank Blaschka kfree(in_sbal_ptrs); 5032104ea556Sfrank.blaschka@de.ibm.com out_free_qib_param: 50334a71df50SFrank Blaschka kfree(qib_param_field); 5034104ea556Sfrank.blaschka@de.ibm.com out_free_nothing: 50354a71df50SFrank Blaschka return rc; 50364a71df50SFrank Blaschka } 50374a71df50SFrank Blaschka 50384a71df50SFrank Blaschka static void qeth_core_free_card(struct qeth_card *card) 50394a71df50SFrank Blaschka { 5040d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freecrd"); 5041d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 50424a71df50SFrank Blaschka qeth_clean_channel(&card->read); 50434a71df50SFrank Blaschka qeth_clean_channel(&card->write); 5044f15cdaf2SJulian Wiedmann qeth_clean_channel(&card->data); 50454a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 50466bcac508SMartin Schwidefsky unregister_service_level(&card->qeth_service_level); 50474a71df50SFrank Blaschka kfree(card); 50484a71df50SFrank Blaschka } 50494a71df50SFrank Blaschka 5050395672e0SStefan Raspl void qeth_trace_features(struct qeth_card *card) 5051395672e0SStefan Raspl { 5052395672e0SStefan Raspl QETH_CARD_TEXT(card, 2, "features"); 50534d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4)); 50544d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6)); 50554d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp)); 50564d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->info.diagass_support, 50574d7def2aSThomas Richter sizeof(card->info.diagass_support)); 5058395672e0SStefan Raspl } 5059395672e0SStefan Raspl EXPORT_SYMBOL_GPL(qeth_trace_features); 5060395672e0SStefan Raspl 50614a71df50SFrank Blaschka static struct ccw_device_id qeth_ids[] = { 50625113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01), 50635113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSD}, 50645113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x05, 0x1732, 0x05), 50655113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_IQD}, 50665113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x06, 0x1732, 0x06), 50675113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSN}, 50685113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x03), 50695113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSM}, 50705113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x02), 50715113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSX}, 50724a71df50SFrank Blaschka {}, 50734a71df50SFrank Blaschka }; 50744a71df50SFrank Blaschka MODULE_DEVICE_TABLE(ccw, qeth_ids); 50754a71df50SFrank Blaschka 50764a71df50SFrank Blaschka static struct ccw_driver qeth_ccw_driver = { 50773bda058bSSebastian Ott .driver = { 50783e70b3b8SSebastian Ott .owner = THIS_MODULE, 50794a71df50SFrank Blaschka .name = "qeth", 50803bda058bSSebastian Ott }, 50814a71df50SFrank Blaschka .ids = qeth_ids, 50824a71df50SFrank Blaschka .probe = ccwgroup_probe_ccwdev, 50834a71df50SFrank Blaschka .remove = ccwgroup_remove_ccwdev, 50844a71df50SFrank Blaschka }; 50854a71df50SFrank Blaschka 50864a71df50SFrank Blaschka int qeth_core_hardsetup_card(struct qeth_card *card) 50874a71df50SFrank Blaschka { 50886ebb7f8dSStefan Raspl int retries = 3; 50894a71df50SFrank Blaschka int rc; 50904a71df50SFrank Blaschka 5091d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); 50924a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 0); 5093725b9c04SSebastian Ott qeth_update_from_chp_desc(card); 50944a71df50SFrank Blaschka retry: 50956ebb7f8dSStefan Raspl if (retries < 3) 509674eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", 509774eacdb9SFrank Blaschka dev_name(&card->gdev->dev)); 509822ae2790SUrsula Braun rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 50994a71df50SFrank Blaschka ccw_device_set_offline(CARD_DDEV(card)); 51004a71df50SFrank Blaschka ccw_device_set_offline(CARD_WDEV(card)); 51014a71df50SFrank Blaschka ccw_device_set_offline(CARD_RDEV(card)); 510222ae2790SUrsula Braun qdio_free(CARD_DDEV(card)); 5103aa909224SUrsula Braun rc = ccw_device_set_online(CARD_RDEV(card)); 5104aa909224SUrsula Braun if (rc) 5105aa909224SUrsula Braun goto retriable; 5106aa909224SUrsula Braun rc = ccw_device_set_online(CARD_WDEV(card)); 5107aa909224SUrsula Braun if (rc) 5108aa909224SUrsula Braun goto retriable; 5109aa909224SUrsula Braun rc = ccw_device_set_online(CARD_DDEV(card)); 5110aa909224SUrsula Braun if (rc) 5111aa909224SUrsula Braun goto retriable; 5112aa909224SUrsula Braun retriable: 51134a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5114d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break1"); 51154a71df50SFrank Blaschka return rc; 51164a71df50SFrank Blaschka } else if (rc) { 5117d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 51186ebb7f8dSStefan Raspl if (--retries < 0) 51194a71df50SFrank Blaschka goto out; 51204a71df50SFrank Blaschka else 51214a71df50SFrank Blaschka goto retry; 51224a71df50SFrank Blaschka } 5123d0ff1f52SUrsula Braun qeth_determine_capabilities(card); 51244a71df50SFrank Blaschka qeth_init_tokens(card); 51254a71df50SFrank Blaschka qeth_init_func_level(card); 51264a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); 51274a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5128d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break2"); 51294a71df50SFrank Blaschka return rc; 51304a71df50SFrank Blaschka } else if (rc) { 5131d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 51324a71df50SFrank Blaschka if (--retries < 0) 51334a71df50SFrank Blaschka goto out; 51344a71df50SFrank Blaschka else 51354a71df50SFrank Blaschka goto retry; 51364a71df50SFrank Blaschka } 51374a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->write, qeth_idx_write_cb); 51384a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5139d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break3"); 51404a71df50SFrank Blaschka return rc; 51414a71df50SFrank Blaschka } else if (rc) { 5142d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 51434a71df50SFrank Blaschka if (--retries < 0) 51444a71df50SFrank Blaschka goto out; 51454a71df50SFrank Blaschka else 51464a71df50SFrank Blaschka goto retry; 51474a71df50SFrank Blaschka } 5148908abbb5SUrsula Braun card->read_or_write_problem = 0; 51494a71df50SFrank Blaschka rc = qeth_mpc_initialize(card); 51504a71df50SFrank Blaschka if (rc) { 5151d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 51524a71df50SFrank Blaschka goto out; 51534a71df50SFrank Blaschka } 51541da74b1cSFrank Blaschka 515510340510SJulian Wiedmann rc = qeth_send_startlan(card); 515610340510SJulian Wiedmann if (rc) { 515710340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 515810340510SJulian Wiedmann if (rc == IPA_RC_LAN_OFFLINE) { 515910340510SJulian Wiedmann dev_warn(&card->gdev->dev, 516010340510SJulian Wiedmann "The LAN is offline\n"); 516110340510SJulian Wiedmann card->lan_online = 0; 516210340510SJulian Wiedmann } else { 516310340510SJulian Wiedmann rc = -ENODEV; 516410340510SJulian Wiedmann goto out; 516510340510SJulian Wiedmann } 516610340510SJulian Wiedmann } else 516710340510SJulian Wiedmann card->lan_online = 1; 516810340510SJulian Wiedmann 51691da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = 0; 51704d7def2aSThomas Richter card->options.ipa6.supported_funcs = 0; 51711da74b1cSFrank Blaschka card->options.adp.supported_funcs = 0; 5172b4d72c08SEugene Crosser card->options.sbp.supported_funcs = 0; 51731da74b1cSFrank Blaschka card->info.diagass_support = 0; 51741aec42bcSThomas Richter rc = qeth_query_ipassists(card, QETH_PROT_IPV4); 51751aec42bcSThomas Richter if (rc == -ENOMEM) 51761aec42bcSThomas Richter goto out; 5177ee75fb86SKittipon Meesompop if (qeth_is_supported(card, IPA_IPV6)) { 5178ee75fb86SKittipon Meesompop rc = qeth_query_ipassists(card, QETH_PROT_IPV6); 5179ee75fb86SKittipon Meesompop if (rc == -ENOMEM) 5180ee75fb86SKittipon Meesompop goto out; 5181ee75fb86SKittipon Meesompop } 51821aec42bcSThomas Richter if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { 51831aec42bcSThomas Richter rc = qeth_query_setadapterparms(card); 51841aec42bcSThomas Richter if (rc < 0) { 518510340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); 51861aec42bcSThomas Richter goto out; 51871aec42bcSThomas Richter } 51881aec42bcSThomas Richter } 51891aec42bcSThomas Richter if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { 51901aec42bcSThomas Richter rc = qeth_query_setdiagass(card); 51911aec42bcSThomas Richter if (rc < 0) { 519210340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); 51931aec42bcSThomas Richter goto out; 51941aec42bcSThomas Richter } 51951aec42bcSThomas Richter } 51964a71df50SFrank Blaschka return 0; 51974a71df50SFrank Blaschka out: 519874eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " 519974eacdb9SFrank Blaschka "an error on the device\n"); 520074eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n", 520174eacdb9SFrank Blaschka dev_name(&card->gdev->dev), rc); 52024a71df50SFrank Blaschka return rc; 52034a71df50SFrank Blaschka } 52044a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); 52054a71df50SFrank Blaschka 52068d68af6aSJulian Wiedmann static void qeth_create_skb_frag(struct qdio_buffer_element *element, 52078d68af6aSJulian Wiedmann struct sk_buff *skb, int offset, int data_len) 52084a71df50SFrank Blaschka { 52094a71df50SFrank Blaschka struct page *page = virt_to_page(element->addr); 5210b6f72f96SJulian Wiedmann unsigned int next_frag; 5211b6f72f96SJulian Wiedmann 52128d68af6aSJulian Wiedmann /* first fill the linear space */ 52138d68af6aSJulian Wiedmann if (!skb->len) { 52148d68af6aSJulian Wiedmann unsigned int linear = min(data_len, skb_tailroom(skb)); 5215b3332930SFrank Blaschka 52168d68af6aSJulian Wiedmann skb_put_data(skb, element->addr + offset, linear); 52178d68af6aSJulian Wiedmann data_len -= linear; 52188d68af6aSJulian Wiedmann if (!data_len) 52198d68af6aSJulian Wiedmann return; 52208d68af6aSJulian Wiedmann offset += linear; 5221b6f72f96SJulian Wiedmann /* fall through to add page frag for remaining data */ 52224a71df50SFrank Blaschka } 52234a71df50SFrank Blaschka 52248d68af6aSJulian Wiedmann next_frag = skb_shinfo(skb)->nr_frags; 52254a71df50SFrank Blaschka get_page(page); 52268d68af6aSJulian Wiedmann skb_add_rx_frag(skb, next_frag, page, offset, data_len, data_len); 52274a71df50SFrank Blaschka } 52284a71df50SFrank Blaschka 5229bca51650SThomas Richter static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) 5230bca51650SThomas Richter { 5231bca51650SThomas Richter return (sbale->eflags & SBAL_EFLAGS_LAST_ENTRY); 5232bca51650SThomas Richter } 5233bca51650SThomas Richter 52344a71df50SFrank Blaschka struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, 5235b3332930SFrank Blaschka struct qeth_qdio_buffer *qethbuffer, 52364a71df50SFrank Blaschka struct qdio_buffer_element **__element, int *__offset, 52374a71df50SFrank Blaschka struct qeth_hdr **hdr) 52384a71df50SFrank Blaschka { 52394a71df50SFrank Blaschka struct qdio_buffer_element *element = *__element; 5240b3332930SFrank Blaschka struct qdio_buffer *buffer = qethbuffer->buffer; 52414a71df50SFrank Blaschka int offset = *__offset; 52428d68af6aSJulian Wiedmann struct sk_buff *skb; 524376b11f8eSUrsula Braun int skb_len = 0; 52444a71df50SFrank Blaschka void *data_ptr; 52454a71df50SFrank Blaschka int data_len; 52464a71df50SFrank Blaschka int headroom = 0; 52474a71df50SFrank Blaschka int use_rx_sg = 0; 52484a71df50SFrank Blaschka 52494a71df50SFrank Blaschka /* qeth_hdr must not cross element boundaries */ 5250864c17c3SJulian Wiedmann while (element->length < offset + sizeof(struct qeth_hdr)) { 52514a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) 52524a71df50SFrank Blaschka return NULL; 52534a71df50SFrank Blaschka element++; 52544a71df50SFrank Blaschka offset = 0; 52554a71df50SFrank Blaschka } 52564a71df50SFrank Blaschka *hdr = element->addr + offset; 52574a71df50SFrank Blaschka 52584a71df50SFrank Blaschka offset += sizeof(struct qeth_hdr); 525976b11f8eSUrsula Braun switch ((*hdr)->hdr.l2.id) { 526076b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER2: 52614a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l2.pkt_length; 526276b11f8eSUrsula Braun break; 526376b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER3: 52644a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l3.length; 5265b403e685SFrank Blaschka headroom = ETH_HLEN; 526676b11f8eSUrsula Braun break; 526776b11f8eSUrsula Braun case QETH_HEADER_TYPE_OSN: 526876b11f8eSUrsula Braun skb_len = (*hdr)->hdr.osn.pdu_length; 526976b11f8eSUrsula Braun headroom = sizeof(struct qeth_hdr); 527076b11f8eSUrsula Braun break; 527176b11f8eSUrsula Braun default: 527276b11f8eSUrsula Braun break; 52734a71df50SFrank Blaschka } 52744a71df50SFrank Blaschka 52754a71df50SFrank Blaschka if (!skb_len) 52764a71df50SFrank Blaschka return NULL; 52774a71df50SFrank Blaschka 5278b3332930SFrank Blaschka if (((skb_len >= card->options.rx_sg_cb) && 52794a71df50SFrank Blaschka (!(card->info.type == QETH_CARD_TYPE_OSN)) && 5280b3332930SFrank Blaschka (!atomic_read(&card->force_alloc_skb))) || 52818d68af6aSJulian Wiedmann (card->options.cq == QETH_CQ_ENABLED)) 52824a71df50SFrank Blaschka use_rx_sg = 1; 52838d68af6aSJulian Wiedmann 52848d68af6aSJulian Wiedmann if (use_rx_sg && qethbuffer->rx_skb) { 52858d68af6aSJulian Wiedmann /* QETH_CQ_ENABLED only: */ 52868d68af6aSJulian Wiedmann skb = qethbuffer->rx_skb; 52878d68af6aSJulian Wiedmann qethbuffer->rx_skb = NULL; 52884a71df50SFrank Blaschka } else { 52898d68af6aSJulian Wiedmann unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len; 52908d68af6aSJulian Wiedmann 529137cf05d2SJulian Wiedmann skb = napi_alloc_skb(&card->napi, linear + headroom); 52928d68af6aSJulian Wiedmann } 52934a71df50SFrank Blaschka if (!skb) 52944a71df50SFrank Blaschka goto no_mem; 52954a71df50SFrank Blaschka if (headroom) 52964a71df50SFrank Blaschka skb_reserve(skb, headroom); 52974a71df50SFrank Blaschka 52984a71df50SFrank Blaschka data_ptr = element->addr + offset; 52994a71df50SFrank Blaschka while (skb_len) { 53004a71df50SFrank Blaschka data_len = min(skb_len, (int)(element->length - offset)); 53014a71df50SFrank Blaschka if (data_len) { 53028d68af6aSJulian Wiedmann if (use_rx_sg) 53038d68af6aSJulian Wiedmann qeth_create_skb_frag(element, skb, offset, 53048d68af6aSJulian Wiedmann data_len); 53058d68af6aSJulian Wiedmann else 530659ae1d12SJohannes Berg skb_put_data(skb, data_ptr, data_len); 53074a71df50SFrank Blaschka } 53084a71df50SFrank Blaschka skb_len -= data_len; 53094a71df50SFrank Blaschka if (skb_len) { 53104a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) { 5311847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "unexeob"); 5312efd5d9a4SCarsten Otte QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); 53134a71df50SFrank Blaschka dev_kfree_skb_any(skb); 53144a71df50SFrank Blaschka card->stats.rx_errors++; 53154a71df50SFrank Blaschka return NULL; 53164a71df50SFrank Blaschka } 53174a71df50SFrank Blaschka element++; 53184a71df50SFrank Blaschka offset = 0; 53194a71df50SFrank Blaschka data_ptr = element->addr; 53204a71df50SFrank Blaschka } else { 53214a71df50SFrank Blaschka offset += data_len; 53224a71df50SFrank Blaschka } 53234a71df50SFrank Blaschka } 53244a71df50SFrank Blaschka *__element = element; 53254a71df50SFrank Blaschka *__offset = offset; 53264a71df50SFrank Blaschka if (use_rx_sg && card->options.performance_stats) { 53274a71df50SFrank Blaschka card->perf_stats.sg_skbs_rx++; 53284a71df50SFrank Blaschka card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; 53294a71df50SFrank Blaschka } 53304a71df50SFrank Blaschka return skb; 53314a71df50SFrank Blaschka no_mem: 53324a71df50SFrank Blaschka if (net_ratelimit()) { 5333847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "noskbmem"); 53344a71df50SFrank Blaschka } 53354a71df50SFrank Blaschka card->stats.rx_dropped++; 53364a71df50SFrank Blaschka return NULL; 53374a71df50SFrank Blaschka } 53384a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); 53394a71df50SFrank Blaschka 5340d73ef324SJulian Wiedmann int qeth_poll(struct napi_struct *napi, int budget) 5341d73ef324SJulian Wiedmann { 5342d73ef324SJulian Wiedmann struct qeth_card *card = container_of(napi, struct qeth_card, napi); 5343d73ef324SJulian Wiedmann int work_done = 0; 5344d73ef324SJulian Wiedmann struct qeth_qdio_buffer *buffer; 5345d73ef324SJulian Wiedmann int done; 5346d73ef324SJulian Wiedmann int new_budget = budget; 5347d73ef324SJulian Wiedmann 5348d73ef324SJulian Wiedmann if (card->options.performance_stats) { 5349d73ef324SJulian Wiedmann card->perf_stats.inbound_cnt++; 5350d73ef324SJulian Wiedmann card->perf_stats.inbound_start_time = qeth_get_micros(); 5351d73ef324SJulian Wiedmann } 5352d73ef324SJulian Wiedmann 5353d73ef324SJulian Wiedmann while (1) { 5354d73ef324SJulian Wiedmann if (!card->rx.b_count) { 5355d73ef324SJulian Wiedmann card->rx.qdio_err = 0; 5356d73ef324SJulian Wiedmann card->rx.b_count = qdio_get_next_buffers( 5357d73ef324SJulian Wiedmann card->data.ccwdev, 0, &card->rx.b_index, 5358d73ef324SJulian Wiedmann &card->rx.qdio_err); 5359d73ef324SJulian Wiedmann if (card->rx.b_count <= 0) { 5360d73ef324SJulian Wiedmann card->rx.b_count = 0; 5361d73ef324SJulian Wiedmann break; 5362d73ef324SJulian Wiedmann } 5363d73ef324SJulian Wiedmann card->rx.b_element = 5364d73ef324SJulian Wiedmann &card->qdio.in_q->bufs[card->rx.b_index] 5365d73ef324SJulian Wiedmann .buffer->element[0]; 5366d73ef324SJulian Wiedmann card->rx.e_offset = 0; 5367d73ef324SJulian Wiedmann } 5368d73ef324SJulian Wiedmann 5369d73ef324SJulian Wiedmann while (card->rx.b_count) { 5370d73ef324SJulian Wiedmann buffer = &card->qdio.in_q->bufs[card->rx.b_index]; 5371d73ef324SJulian Wiedmann if (!(card->rx.qdio_err && 5372d73ef324SJulian Wiedmann qeth_check_qdio_errors(card, buffer->buffer, 5373d73ef324SJulian Wiedmann card->rx.qdio_err, "qinerr"))) 5374d73ef324SJulian Wiedmann work_done += 5375d73ef324SJulian Wiedmann card->discipline->process_rx_buffer( 5376d73ef324SJulian Wiedmann card, new_budget, &done); 5377d73ef324SJulian Wiedmann else 5378d73ef324SJulian Wiedmann done = 1; 5379d73ef324SJulian Wiedmann 5380d73ef324SJulian Wiedmann if (done) { 5381d73ef324SJulian Wiedmann if (card->options.performance_stats) 5382d73ef324SJulian Wiedmann card->perf_stats.bufs_rec++; 5383d73ef324SJulian Wiedmann qeth_put_buffer_pool_entry(card, 5384d73ef324SJulian Wiedmann buffer->pool_entry); 5385d73ef324SJulian Wiedmann qeth_queue_input_buffer(card, card->rx.b_index); 5386d73ef324SJulian Wiedmann card->rx.b_count--; 5387d73ef324SJulian Wiedmann if (card->rx.b_count) { 5388d73ef324SJulian Wiedmann card->rx.b_index = 5389d73ef324SJulian Wiedmann (card->rx.b_index + 1) % 5390d73ef324SJulian Wiedmann QDIO_MAX_BUFFERS_PER_Q; 5391d73ef324SJulian Wiedmann card->rx.b_element = 5392d73ef324SJulian Wiedmann &card->qdio.in_q 5393d73ef324SJulian Wiedmann ->bufs[card->rx.b_index] 5394d73ef324SJulian Wiedmann .buffer->element[0]; 5395d73ef324SJulian Wiedmann card->rx.e_offset = 0; 5396d73ef324SJulian Wiedmann } 5397d73ef324SJulian Wiedmann } 5398d73ef324SJulian Wiedmann 5399d73ef324SJulian Wiedmann if (work_done >= budget) 5400d73ef324SJulian Wiedmann goto out; 5401d73ef324SJulian Wiedmann else 5402d73ef324SJulian Wiedmann new_budget = budget - work_done; 5403d73ef324SJulian Wiedmann } 5404d73ef324SJulian Wiedmann } 5405d73ef324SJulian Wiedmann 5406978759e8SJulian Wiedmann napi_complete_done(napi, work_done); 5407d73ef324SJulian Wiedmann if (qdio_start_irq(card->data.ccwdev, 0)) 5408d73ef324SJulian Wiedmann napi_schedule(&card->napi); 5409d73ef324SJulian Wiedmann out: 5410d73ef324SJulian Wiedmann if (card->options.performance_stats) 5411d73ef324SJulian Wiedmann card->perf_stats.inbound_time += qeth_get_micros() - 5412d73ef324SJulian Wiedmann card->perf_stats.inbound_start_time; 5413d73ef324SJulian Wiedmann return work_done; 5414d73ef324SJulian Wiedmann } 5415d73ef324SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_poll); 5416d73ef324SJulian Wiedmann 5417ad3cbf61SJulian Wiedmann static int qeth_setassparms_inspect_rc(struct qeth_ipa_cmd *cmd) 5418ad3cbf61SJulian Wiedmann { 5419ad3cbf61SJulian Wiedmann if (!cmd->hdr.return_code) 5420ad3cbf61SJulian Wiedmann cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; 5421ad3cbf61SJulian Wiedmann return cmd->hdr.return_code; 5422ad3cbf61SJulian Wiedmann } 5423ad3cbf61SJulian Wiedmann 54248f43fb00SThomas Richter int qeth_setassparms_cb(struct qeth_card *card, 54254d7def2aSThomas Richter struct qeth_reply *reply, unsigned long data) 54264d7def2aSThomas Richter { 54274d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 54284d7def2aSThomas Richter 54294d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "defadpcb"); 54304d7def2aSThomas Richter 54314d7def2aSThomas Richter cmd = (struct qeth_ipa_cmd *) data; 54324d7def2aSThomas Richter if (cmd->hdr.return_code == 0) { 54334d7def2aSThomas Richter cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; 54344d7def2aSThomas Richter if (cmd->hdr.prot_version == QETH_PROT_IPV4) 54354d7def2aSThomas Richter card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; 54364d7def2aSThomas Richter if (cmd->hdr.prot_version == QETH_PROT_IPV6) 54374d7def2aSThomas Richter card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; 54384d7def2aSThomas Richter } 54394d7def2aSThomas Richter return 0; 54404d7def2aSThomas Richter } 54418f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_setassparms_cb); 54424d7def2aSThomas Richter 5443b475e316SThomas Richter struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, 54444d7def2aSThomas Richter enum qeth_ipa_funcs ipa_func, 54454d7def2aSThomas Richter __u16 cmd_code, __u16 len, 54464d7def2aSThomas Richter enum qeth_prot_versions prot) 54474d7def2aSThomas Richter { 54484d7def2aSThomas Richter struct qeth_cmd_buffer *iob; 54494d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 54504d7def2aSThomas Richter 54514d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "getasscm"); 54524d7def2aSThomas Richter iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); 54534d7def2aSThomas Richter 54544d7def2aSThomas Richter if (iob) { 5455ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 54564d7def2aSThomas Richter cmd->data.setassparms.hdr.assist_no = ipa_func; 54574d7def2aSThomas Richter cmd->data.setassparms.hdr.length = 8 + len; 54584d7def2aSThomas Richter cmd->data.setassparms.hdr.command_code = cmd_code; 54594d7def2aSThomas Richter cmd->data.setassparms.hdr.return_code = 0; 54604d7def2aSThomas Richter cmd->data.setassparms.hdr.seq_no = 0; 54614d7def2aSThomas Richter } 54624d7def2aSThomas Richter 54634d7def2aSThomas Richter return iob; 54644d7def2aSThomas Richter } 5465b475e316SThomas Richter EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd); 54664d7def2aSThomas Richter 54674d7def2aSThomas Richter int qeth_send_setassparms(struct qeth_card *card, 54684d7def2aSThomas Richter struct qeth_cmd_buffer *iob, __u16 len, long data, 54694d7def2aSThomas Richter int (*reply_cb)(struct qeth_card *, 54704d7def2aSThomas Richter struct qeth_reply *, unsigned long), 54714d7def2aSThomas Richter void *reply_param) 54724d7def2aSThomas Richter { 54734d7def2aSThomas Richter int rc; 54744d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 54754d7def2aSThomas Richter 54764d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "sendassp"); 54774d7def2aSThomas Richter 5478ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob); 54794d7def2aSThomas Richter if (len <= sizeof(__u32)) 54804d7def2aSThomas Richter cmd->data.setassparms.data.flags_32bit = (__u32) data; 54814d7def2aSThomas Richter else /* (len > sizeof(__u32)) */ 54824d7def2aSThomas Richter memcpy(&cmd->data.setassparms.data, (void *) data, len); 54834d7def2aSThomas Richter 54844d7def2aSThomas Richter rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); 54854d7def2aSThomas Richter return rc; 54864d7def2aSThomas Richter } 54874d7def2aSThomas Richter EXPORT_SYMBOL_GPL(qeth_send_setassparms); 54884d7def2aSThomas Richter 5489a8155b00SKittipon Meesompop int qeth_send_simple_setassparms_prot(struct qeth_card *card, 54904d7def2aSThomas Richter enum qeth_ipa_funcs ipa_func, 5491a8155b00SKittipon Meesompop u16 cmd_code, long data, 5492a8155b00SKittipon Meesompop enum qeth_prot_versions prot) 54934d7def2aSThomas Richter { 54944d7def2aSThomas Richter int rc; 54954d7def2aSThomas Richter int length = 0; 54964d7def2aSThomas Richter struct qeth_cmd_buffer *iob; 54974d7def2aSThomas Richter 5498a8155b00SKittipon Meesompop QETH_CARD_TEXT_(card, 4, "simassp%i", prot); 54994d7def2aSThomas Richter if (data) 55004d7def2aSThomas Richter length = sizeof(__u32); 5501a8155b00SKittipon Meesompop iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, length, prot); 55024d7def2aSThomas Richter if (!iob) 55034d7def2aSThomas Richter return -ENOMEM; 55044d7def2aSThomas Richter rc = qeth_send_setassparms(card, iob, length, data, 55054d7def2aSThomas Richter qeth_setassparms_cb, NULL); 55064d7def2aSThomas Richter return rc; 55074d7def2aSThomas Richter } 5508a8155b00SKittipon Meesompop EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms_prot); 55094d7def2aSThomas Richter 55104a71df50SFrank Blaschka static void qeth_unregister_dbf_views(void) 55114a71df50SFrank Blaschka { 5512d11ba0c4SPeter Tiedemann int x; 5513d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5514d11ba0c4SPeter Tiedemann debug_unregister(qeth_dbf[x].id); 5515d11ba0c4SPeter Tiedemann qeth_dbf[x].id = NULL; 5516d11ba0c4SPeter Tiedemann } 55174a71df50SFrank Blaschka } 55184a71df50SFrank Blaschka 55198e96c51cSCarsten Otte void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...) 5520cd023216SPeter Tiedemann { 5521cd023216SPeter Tiedemann char dbf_txt_buf[32]; 5522345aa66eSPeter Tiedemann va_list args; 5523cd023216SPeter Tiedemann 55248e6a8285SHendrik Brueckner if (!debug_level_enabled(id, level)) 5525cd023216SPeter Tiedemann return; 5526345aa66eSPeter Tiedemann va_start(args, fmt); 5527345aa66eSPeter Tiedemann vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); 5528345aa66eSPeter Tiedemann va_end(args); 55298e96c51cSCarsten Otte debug_text_event(id, level, dbf_txt_buf); 5530cd023216SPeter Tiedemann } 5531cd023216SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf_longtext); 5532cd023216SPeter Tiedemann 55334a71df50SFrank Blaschka static int qeth_register_dbf_views(void) 55344a71df50SFrank Blaschka { 5535d11ba0c4SPeter Tiedemann int ret; 5536d11ba0c4SPeter Tiedemann int x; 55374a71df50SFrank Blaschka 5538d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5539d11ba0c4SPeter Tiedemann /* register the areas */ 5540d11ba0c4SPeter Tiedemann qeth_dbf[x].id = debug_register(qeth_dbf[x].name, 5541d11ba0c4SPeter Tiedemann qeth_dbf[x].pages, 5542d11ba0c4SPeter Tiedemann qeth_dbf[x].areas, 5543d11ba0c4SPeter Tiedemann qeth_dbf[x].len); 5544d11ba0c4SPeter Tiedemann if (qeth_dbf[x].id == NULL) { 55454a71df50SFrank Blaschka qeth_unregister_dbf_views(); 55464a71df50SFrank Blaschka return -ENOMEM; 55474a71df50SFrank Blaschka } 55484a71df50SFrank Blaschka 5549d11ba0c4SPeter Tiedemann /* register a view */ 5550d11ba0c4SPeter Tiedemann ret = debug_register_view(qeth_dbf[x].id, qeth_dbf[x].view); 5551d11ba0c4SPeter Tiedemann if (ret) { 5552d11ba0c4SPeter Tiedemann qeth_unregister_dbf_views(); 5553d11ba0c4SPeter Tiedemann return ret; 5554d11ba0c4SPeter Tiedemann } 55554a71df50SFrank Blaschka 5556d11ba0c4SPeter Tiedemann /* set a passing level */ 5557d11ba0c4SPeter Tiedemann debug_set_level(qeth_dbf[x].id, qeth_dbf[x].level); 5558d11ba0c4SPeter Tiedemann } 55594a71df50SFrank Blaschka 55604a71df50SFrank Blaschka return 0; 55614a71df50SFrank Blaschka } 55624a71df50SFrank Blaschka 55634a71df50SFrank Blaschka int qeth_core_load_discipline(struct qeth_card *card, 55644a71df50SFrank Blaschka enum qeth_discipline_id discipline) 55654a71df50SFrank Blaschka { 55664a71df50SFrank Blaschka int rc = 0; 5567c70eb09dSJulian Wiedmann 55682022e00cSFrank Blaschka mutex_lock(&qeth_mod_mutex); 55694a71df50SFrank Blaschka switch (discipline) { 55704a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER3: 5571c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5572c041f2d4SSebastian Ott symbol_get(qeth_l3_discipline), "qeth_l3"); 55734a71df50SFrank Blaschka break; 55744a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER2: 5575c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5576c041f2d4SSebastian Ott symbol_get(qeth_l2_discipline), "qeth_l2"); 55774a71df50SFrank Blaschka break; 5578c70eb09dSJulian Wiedmann default: 5579c70eb09dSJulian Wiedmann break; 55804a71df50SFrank Blaschka } 5581c70eb09dSJulian Wiedmann 5582c041f2d4SSebastian Ott if (!card->discipline) { 558374eacdb9SFrank Blaschka dev_err(&card->gdev->dev, "There is no kernel module to " 558474eacdb9SFrank Blaschka "support discipline %d\n", discipline); 55854a71df50SFrank Blaschka rc = -EINVAL; 55864a71df50SFrank Blaschka } 55872022e00cSFrank Blaschka mutex_unlock(&qeth_mod_mutex); 55884a71df50SFrank Blaschka return rc; 55894a71df50SFrank Blaschka } 55904a71df50SFrank Blaschka 55914a71df50SFrank Blaschka void qeth_core_free_discipline(struct qeth_card *card) 55924a71df50SFrank Blaschka { 55934a71df50SFrank Blaschka if (card->options.layer2) 5594c041f2d4SSebastian Ott symbol_put(qeth_l2_discipline); 55954a71df50SFrank Blaschka else 5596c041f2d4SSebastian Ott symbol_put(qeth_l3_discipline); 5597c041f2d4SSebastian Ott card->discipline = NULL; 55984a71df50SFrank Blaschka } 55994a71df50SFrank Blaschka 56002d2ebb3eSJulian Wiedmann const struct device_type qeth_generic_devtype = { 5601b7169c51SSebastian Ott .name = "qeth_generic", 5602b7169c51SSebastian Ott .groups = qeth_generic_attr_groups, 5603b7169c51SSebastian Ott }; 56042d2ebb3eSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_generic_devtype); 56052d2ebb3eSJulian Wiedmann 5606b7169c51SSebastian Ott static const struct device_type qeth_osn_devtype = { 5607b7169c51SSebastian Ott .name = "qeth_osn", 5608b7169c51SSebastian Ott .groups = qeth_osn_attr_groups, 5609b7169c51SSebastian Ott }; 5610b7169c51SSebastian Ott 5611819dc537SStefan Raspl #define DBF_NAME_LEN 20 5612819dc537SStefan Raspl 5613819dc537SStefan Raspl struct qeth_dbf_entry { 5614819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 5615819dc537SStefan Raspl debug_info_t *dbf_info; 5616819dc537SStefan Raspl struct list_head dbf_list; 5617819dc537SStefan Raspl }; 5618819dc537SStefan Raspl 5619819dc537SStefan Raspl static LIST_HEAD(qeth_dbf_list); 5620819dc537SStefan Raspl static DEFINE_MUTEX(qeth_dbf_list_mutex); 5621819dc537SStefan Raspl 5622819dc537SStefan Raspl static debug_info_t *qeth_get_dbf_entry(char *name) 5623819dc537SStefan Raspl { 5624819dc537SStefan Raspl struct qeth_dbf_entry *entry; 5625819dc537SStefan Raspl debug_info_t *rc = NULL; 5626819dc537SStefan Raspl 5627819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5628819dc537SStefan Raspl list_for_each_entry(entry, &qeth_dbf_list, dbf_list) { 5629819dc537SStefan Raspl if (strcmp(entry->dbf_name, name) == 0) { 5630819dc537SStefan Raspl rc = entry->dbf_info; 5631819dc537SStefan Raspl break; 5632819dc537SStefan Raspl } 5633819dc537SStefan Raspl } 5634819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5635819dc537SStefan Raspl return rc; 5636819dc537SStefan Raspl } 5637819dc537SStefan Raspl 5638819dc537SStefan Raspl static int qeth_add_dbf_entry(struct qeth_card *card, char *name) 5639819dc537SStefan Raspl { 5640819dc537SStefan Raspl struct qeth_dbf_entry *new_entry; 5641819dc537SStefan Raspl 5642819dc537SStefan Raspl card->debug = debug_register(name, 2, 1, 8); 5643819dc537SStefan Raspl if (!card->debug) { 5644819dc537SStefan Raspl QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); 5645819dc537SStefan Raspl goto err; 5646819dc537SStefan Raspl } 5647819dc537SStefan Raspl if (debug_register_view(card->debug, &debug_hex_ascii_view)) 5648819dc537SStefan Raspl goto err_dbg; 5649819dc537SStefan Raspl new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL); 5650819dc537SStefan Raspl if (!new_entry) 5651819dc537SStefan Raspl goto err_dbg; 5652819dc537SStefan Raspl strncpy(new_entry->dbf_name, name, DBF_NAME_LEN); 5653819dc537SStefan Raspl new_entry->dbf_info = card->debug; 5654819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5655819dc537SStefan Raspl list_add(&new_entry->dbf_list, &qeth_dbf_list); 5656819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5657819dc537SStefan Raspl 5658819dc537SStefan Raspl return 0; 5659819dc537SStefan Raspl 5660819dc537SStefan Raspl err_dbg: 5661819dc537SStefan Raspl debug_unregister(card->debug); 5662819dc537SStefan Raspl err: 5663819dc537SStefan Raspl return -ENOMEM; 5664819dc537SStefan Raspl } 5665819dc537SStefan Raspl 5666819dc537SStefan Raspl static void qeth_clear_dbf_list(void) 5667819dc537SStefan Raspl { 5668819dc537SStefan Raspl struct qeth_dbf_entry *entry, *tmp; 5669819dc537SStefan Raspl 5670819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5671819dc537SStefan Raspl list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) { 5672819dc537SStefan Raspl list_del(&entry->dbf_list); 5673819dc537SStefan Raspl debug_unregister(entry->dbf_info); 5674819dc537SStefan Raspl kfree(entry); 5675819dc537SStefan Raspl } 5676819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5677819dc537SStefan Raspl } 5678819dc537SStefan Raspl 5679d3d1b205SJulian Wiedmann static struct net_device *qeth_alloc_netdev(struct qeth_card *card) 5680d3d1b205SJulian Wiedmann { 5681d3d1b205SJulian Wiedmann struct net_device *dev; 5682d3d1b205SJulian Wiedmann 5683d3d1b205SJulian Wiedmann switch (card->info.type) { 5684d3d1b205SJulian Wiedmann case QETH_CARD_TYPE_IQD: 5685d3d1b205SJulian Wiedmann dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, ether_setup); 5686d3d1b205SJulian Wiedmann break; 5687d3d1b205SJulian Wiedmann case QETH_CARD_TYPE_OSN: 5688d3d1b205SJulian Wiedmann dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup); 5689d3d1b205SJulian Wiedmann break; 5690d3d1b205SJulian Wiedmann default: 5691d3d1b205SJulian Wiedmann dev = alloc_etherdev(0); 5692d3d1b205SJulian Wiedmann } 5693d3d1b205SJulian Wiedmann 5694d3d1b205SJulian Wiedmann if (!dev) 5695d3d1b205SJulian Wiedmann return NULL; 5696d3d1b205SJulian Wiedmann 5697d3d1b205SJulian Wiedmann dev->ml_priv = card; 5698d3d1b205SJulian Wiedmann dev->watchdog_timeo = QETH_TX_TIMEOUT; 569972f219daSJulian Wiedmann dev->min_mtu = IS_OSN(card) ? 64 : 576; 57008ce7a9e0SJulian Wiedmann /* initialized when device first goes online: */ 57018ce7a9e0SJulian Wiedmann dev->max_mtu = 0; 57028ce7a9e0SJulian Wiedmann dev->mtu = 0; 5703d3d1b205SJulian Wiedmann SET_NETDEV_DEV(dev, &card->gdev->dev); 5704d3d1b205SJulian Wiedmann netif_carrier_off(dev); 57055f89eca5SJulian Wiedmann 57065f89eca5SJulian Wiedmann if (!IS_OSN(card)) { 57075f89eca5SJulian Wiedmann dev->priv_flags &= ~IFF_TX_SKB_SHARING; 57085f89eca5SJulian Wiedmann dev->hw_features |= NETIF_F_SG; 57095f89eca5SJulian Wiedmann dev->vlan_features |= NETIF_F_SG; 571004db741dSJulian Wiedmann if (IS_IQD(card)) 571104db741dSJulian Wiedmann dev->features |= NETIF_F_SG; 57125f89eca5SJulian Wiedmann } 57135f89eca5SJulian Wiedmann 5714d3d1b205SJulian Wiedmann return dev; 5715d3d1b205SJulian Wiedmann } 5716d3d1b205SJulian Wiedmann 5717d3d1b205SJulian Wiedmann struct net_device *qeth_clone_netdev(struct net_device *orig) 5718d3d1b205SJulian Wiedmann { 5719d3d1b205SJulian Wiedmann struct net_device *clone = qeth_alloc_netdev(orig->ml_priv); 5720d3d1b205SJulian Wiedmann 5721d3d1b205SJulian Wiedmann if (!clone) 5722d3d1b205SJulian Wiedmann return NULL; 5723d3d1b205SJulian Wiedmann 5724d3d1b205SJulian Wiedmann clone->dev_port = orig->dev_port; 5725d3d1b205SJulian Wiedmann return clone; 5726d3d1b205SJulian Wiedmann } 5727d3d1b205SJulian Wiedmann 57284a71df50SFrank Blaschka static int qeth_core_probe_device(struct ccwgroup_device *gdev) 57294a71df50SFrank Blaschka { 57304a71df50SFrank Blaschka struct qeth_card *card; 57314a71df50SFrank Blaschka struct device *dev; 57324a71df50SFrank Blaschka int rc; 5733c70eb09dSJulian Wiedmann enum qeth_discipline_id enforced_disc; 57344a71df50SFrank Blaschka unsigned long flags; 5735819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 57364a71df50SFrank Blaschka 5737d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "probedev"); 57384a71df50SFrank Blaschka 57394a71df50SFrank Blaschka dev = &gdev->dev; 57404a71df50SFrank Blaschka if (!get_device(dev)) 57414a71df50SFrank Blaschka return -ENODEV; 57424a71df50SFrank Blaschka 57432a0217d5SKay Sievers QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev)); 57444a71df50SFrank Blaschka 57454a71df50SFrank Blaschka card = qeth_alloc_card(); 57464a71df50SFrank Blaschka if (!card) { 5747d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", -ENOMEM); 57484a71df50SFrank Blaschka rc = -ENOMEM; 57494a71df50SFrank Blaschka goto err_dev; 57504a71df50SFrank Blaschka } 5751af039068SCarsten Otte 5752af039068SCarsten Otte snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s", 5753af039068SCarsten Otte dev_name(&gdev->dev)); 5754819dc537SStefan Raspl card->debug = qeth_get_dbf_entry(dbf_name); 5755af039068SCarsten Otte if (!card->debug) { 5756819dc537SStefan Raspl rc = qeth_add_dbf_entry(card, dbf_name); 5757819dc537SStefan Raspl if (rc) 5758af039068SCarsten Otte goto err_card; 5759af039068SCarsten Otte } 5760af039068SCarsten Otte 57614a71df50SFrank Blaschka card->read.ccwdev = gdev->cdev[0]; 57624a71df50SFrank Blaschka card->write.ccwdev = gdev->cdev[1]; 57634a71df50SFrank Blaschka card->data.ccwdev = gdev->cdev[2]; 57644a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, card); 57654a71df50SFrank Blaschka card->gdev = gdev; 57664a71df50SFrank Blaschka gdev->cdev[0]->handler = qeth_irq; 57674a71df50SFrank Blaschka gdev->cdev[1]->handler = qeth_irq; 57684a71df50SFrank Blaschka gdev->cdev[2]->handler = qeth_irq; 57694a71df50SFrank Blaschka 577095f4d8b7SJulian Wiedmann qeth_setup_card(card); 577195f4d8b7SJulian Wiedmann qeth_update_from_chp_desc(card); 57724a71df50SFrank Blaschka 5773d3d1b205SJulian Wiedmann card->dev = qeth_alloc_netdev(card); 5774778b1ac7SJulian Wiedmann if (!card->dev) { 5775778b1ac7SJulian Wiedmann rc = -ENOMEM; 5776d3d1b205SJulian Wiedmann goto err_card; 5777778b1ac7SJulian Wiedmann } 5778d3d1b205SJulian Wiedmann 5779c70eb09dSJulian Wiedmann qeth_determine_capabilities(card); 5780c70eb09dSJulian Wiedmann enforced_disc = qeth_enforce_discipline(card); 5781c70eb09dSJulian Wiedmann switch (enforced_disc) { 5782c70eb09dSJulian Wiedmann case QETH_DISCIPLINE_UNDETERMINED: 5783c70eb09dSJulian Wiedmann gdev->dev.type = &qeth_generic_devtype; 5784c70eb09dSJulian Wiedmann break; 5785c70eb09dSJulian Wiedmann default: 5786c70eb09dSJulian Wiedmann card->info.layer_enforced = true; 5787c70eb09dSJulian Wiedmann rc = qeth_core_load_discipline(card, enforced_disc); 57885113fec0SUrsula Braun if (rc) 5789d3d1b205SJulian Wiedmann goto err_load; 57902d2ebb3eSJulian Wiedmann 57912d2ebb3eSJulian Wiedmann gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) 57922d2ebb3eSJulian Wiedmann ? card->discipline->devtype 57932d2ebb3eSJulian Wiedmann : &qeth_osn_devtype; 5794c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 57955113fec0SUrsula Braun if (rc) 57965113fec0SUrsula Braun goto err_disc; 57972d2ebb3eSJulian Wiedmann break; 57984a71df50SFrank Blaschka } 57994a71df50SFrank Blaschka 58004a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 58014a71df50SFrank Blaschka list_add_tail(&card->list, &qeth_core_card_list.list); 58024a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 58034a71df50SFrank Blaschka return 0; 58044a71df50SFrank Blaschka 58055113fec0SUrsula Braun err_disc: 58065113fec0SUrsula Braun qeth_core_free_discipline(card); 5807d3d1b205SJulian Wiedmann err_load: 5808d3d1b205SJulian Wiedmann free_netdev(card->dev); 58094a71df50SFrank Blaschka err_card: 58104a71df50SFrank Blaschka qeth_core_free_card(card); 58114a71df50SFrank Blaschka err_dev: 58124a71df50SFrank Blaschka put_device(dev); 58134a71df50SFrank Blaschka return rc; 58144a71df50SFrank Blaschka } 58154a71df50SFrank Blaschka 58164a71df50SFrank Blaschka static void qeth_core_remove_device(struct ccwgroup_device *gdev) 58174a71df50SFrank Blaschka { 58184a71df50SFrank Blaschka unsigned long flags; 58194a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 58204a71df50SFrank Blaschka 582128a7e4c9SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "removedv"); 58224a71df50SFrank Blaschka 5823c041f2d4SSebastian Ott if (card->discipline) { 5824c041f2d4SSebastian Ott card->discipline->remove(gdev); 58259dc48cccSUrsula Braun qeth_core_free_discipline(card); 58269dc48cccSUrsula Braun } 58279dc48cccSUrsula Braun 58284a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 58294a71df50SFrank Blaschka list_del(&card->list); 58304a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 5831d3d1b205SJulian Wiedmann free_netdev(card->dev); 58324a71df50SFrank Blaschka qeth_core_free_card(card); 58334a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, NULL); 58344a71df50SFrank Blaschka put_device(&gdev->dev); 58354a71df50SFrank Blaschka } 58364a71df50SFrank Blaschka 58374a71df50SFrank Blaschka static int qeth_core_set_online(struct ccwgroup_device *gdev) 58384a71df50SFrank Blaschka { 58394a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 58404a71df50SFrank Blaschka int rc = 0; 5841c70eb09dSJulian Wiedmann enum qeth_discipline_id def_discipline; 58424a71df50SFrank Blaschka 5843c041f2d4SSebastian Ott if (!card->discipline) { 58444a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 58454a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER3; 58464a71df50SFrank Blaschka else 58474a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER2; 58484a71df50SFrank Blaschka rc = qeth_core_load_discipline(card, def_discipline); 58494a71df50SFrank Blaschka if (rc) 58504a71df50SFrank Blaschka goto err; 5851c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 58529111e788SUrsula Braun if (rc) { 58539111e788SUrsula Braun qeth_core_free_discipline(card); 58544a71df50SFrank Blaschka goto err; 58554a71df50SFrank Blaschka } 58569111e788SUrsula Braun } 5857c041f2d4SSebastian Ott rc = card->discipline->set_online(gdev); 58584a71df50SFrank Blaschka err: 58594a71df50SFrank Blaschka return rc; 58604a71df50SFrank Blaschka } 58614a71df50SFrank Blaschka 58624a71df50SFrank Blaschka static int qeth_core_set_offline(struct ccwgroup_device *gdev) 58634a71df50SFrank Blaschka { 58644a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5865c041f2d4SSebastian Ott return card->discipline->set_offline(gdev); 58664a71df50SFrank Blaschka } 58674a71df50SFrank Blaschka 58684a71df50SFrank Blaschka static void qeth_core_shutdown(struct ccwgroup_device *gdev) 58694a71df50SFrank Blaschka { 58704a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 587196d1bb53SJulian Wiedmann qeth_set_allowed_threads(card, 0, 1); 587296d1bb53SJulian Wiedmann if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) 587396d1bb53SJulian Wiedmann qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); 587496d1bb53SJulian Wiedmann qeth_qdio_clear_card(card, 0); 587596d1bb53SJulian Wiedmann qeth_clear_qdio_buffers(card); 587696d1bb53SJulian Wiedmann qdio_free(CARD_DDEV(card)); 58774a71df50SFrank Blaschka } 58784a71df50SFrank Blaschka 5879bbcfcdc8SFrank Blaschka static int qeth_core_freeze(struct ccwgroup_device *gdev) 5880bbcfcdc8SFrank Blaschka { 5881bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5882c041f2d4SSebastian Ott if (card->discipline && card->discipline->freeze) 5883c041f2d4SSebastian Ott return card->discipline->freeze(gdev); 5884bbcfcdc8SFrank Blaschka return 0; 5885bbcfcdc8SFrank Blaschka } 5886bbcfcdc8SFrank Blaschka 5887bbcfcdc8SFrank Blaschka static int qeth_core_thaw(struct ccwgroup_device *gdev) 5888bbcfcdc8SFrank Blaschka { 5889bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5890c041f2d4SSebastian Ott if (card->discipline && card->discipline->thaw) 5891c041f2d4SSebastian Ott return card->discipline->thaw(gdev); 5892bbcfcdc8SFrank Blaschka return 0; 5893bbcfcdc8SFrank Blaschka } 5894bbcfcdc8SFrank Blaschka 5895bbcfcdc8SFrank Blaschka static int qeth_core_restore(struct ccwgroup_device *gdev) 5896bbcfcdc8SFrank Blaschka { 5897bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5898c041f2d4SSebastian Ott if (card->discipline && card->discipline->restore) 5899c041f2d4SSebastian Ott return card->discipline->restore(gdev); 5900bbcfcdc8SFrank Blaschka return 0; 5901bbcfcdc8SFrank Blaschka } 5902bbcfcdc8SFrank Blaschka 590336369569SGreg Kroah-Hartman static ssize_t group_store(struct device_driver *ddrv, const char *buf, 590436369569SGreg Kroah-Hartman size_t count) 59054a71df50SFrank Blaschka { 59064a71df50SFrank Blaschka int err; 59074a71df50SFrank Blaschka 59086d8769abSJulian Wiedmann err = ccwgroup_create_dev(qeth_core_root_dev, to_ccwgroupdrv(ddrv), 3, 59096d8769abSJulian Wiedmann buf); 5910b7169c51SSebastian Ott 5911b7169c51SSebastian Ott return err ? err : count; 5912b7169c51SSebastian Ott } 591336369569SGreg Kroah-Hartman static DRIVER_ATTR_WO(group); 59144a71df50SFrank Blaschka 5915f47e2256SSebastian Ott static struct attribute *qeth_drv_attrs[] = { 5916f47e2256SSebastian Ott &driver_attr_group.attr, 5917f47e2256SSebastian Ott NULL, 5918f47e2256SSebastian Ott }; 5919f47e2256SSebastian Ott static struct attribute_group qeth_drv_attr_group = { 5920f47e2256SSebastian Ott .attrs = qeth_drv_attrs, 5921f47e2256SSebastian Ott }; 5922f47e2256SSebastian Ott static const struct attribute_group *qeth_drv_attr_groups[] = { 5923f47e2256SSebastian Ott &qeth_drv_attr_group, 5924f47e2256SSebastian Ott NULL, 5925f47e2256SSebastian Ott }; 5926f47e2256SSebastian Ott 59276d8769abSJulian Wiedmann static struct ccwgroup_driver qeth_core_ccwgroup_driver = { 59286d8769abSJulian Wiedmann .driver = { 59296d8769abSJulian Wiedmann .groups = qeth_drv_attr_groups, 59306d8769abSJulian Wiedmann .owner = THIS_MODULE, 59316d8769abSJulian Wiedmann .name = "qeth", 59326d8769abSJulian Wiedmann }, 59336d8769abSJulian Wiedmann .ccw_driver = &qeth_ccw_driver, 59346d8769abSJulian Wiedmann .setup = qeth_core_probe_device, 59356d8769abSJulian Wiedmann .remove = qeth_core_remove_device, 59366d8769abSJulian Wiedmann .set_online = qeth_core_set_online, 59376d8769abSJulian Wiedmann .set_offline = qeth_core_set_offline, 59386d8769abSJulian Wiedmann .shutdown = qeth_core_shutdown, 59396d8769abSJulian Wiedmann .prepare = NULL, 59406d8769abSJulian Wiedmann .complete = NULL, 59416d8769abSJulian Wiedmann .freeze = qeth_core_freeze, 59426d8769abSJulian Wiedmann .thaw = qeth_core_thaw, 59436d8769abSJulian Wiedmann .restore = qeth_core_restore, 59446d8769abSJulian Wiedmann }; 59456d8769abSJulian Wiedmann 5946942d6984SJulian Wiedmann int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 5947942d6984SJulian Wiedmann { 5948942d6984SJulian Wiedmann struct qeth_card *card = dev->ml_priv; 5949942d6984SJulian Wiedmann struct mii_ioctl_data *mii_data; 5950942d6984SJulian Wiedmann int rc = 0; 5951942d6984SJulian Wiedmann 5952942d6984SJulian Wiedmann if (!card) 5953942d6984SJulian Wiedmann return -ENODEV; 5954942d6984SJulian Wiedmann 5955942d6984SJulian Wiedmann if (!qeth_card_hw_is_reachable(card)) 5956942d6984SJulian Wiedmann return -ENODEV; 5957942d6984SJulian Wiedmann 5958942d6984SJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_OSN) 5959942d6984SJulian Wiedmann return -EPERM; 5960942d6984SJulian Wiedmann 5961942d6984SJulian Wiedmann switch (cmd) { 5962942d6984SJulian Wiedmann case SIOC_QETH_ADP_SET_SNMP_CONTROL: 5963942d6984SJulian Wiedmann rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); 5964942d6984SJulian Wiedmann break; 5965942d6984SJulian Wiedmann case SIOC_QETH_GET_CARD_TYPE: 5966942d6984SJulian Wiedmann if ((card->info.type == QETH_CARD_TYPE_OSD || 5967942d6984SJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSM || 5968942d6984SJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSX) && 5969942d6984SJulian Wiedmann !card->info.guestlan) 5970942d6984SJulian Wiedmann return 1; 5971942d6984SJulian Wiedmann else 5972942d6984SJulian Wiedmann return 0; 5973942d6984SJulian Wiedmann case SIOCGMIIPHY: 5974942d6984SJulian Wiedmann mii_data = if_mii(rq); 5975942d6984SJulian Wiedmann mii_data->phy_id = 0; 5976942d6984SJulian Wiedmann break; 5977942d6984SJulian Wiedmann case SIOCGMIIREG: 5978942d6984SJulian Wiedmann mii_data = if_mii(rq); 5979942d6984SJulian Wiedmann if (mii_data->phy_id != 0) 5980942d6984SJulian Wiedmann rc = -EINVAL; 5981942d6984SJulian Wiedmann else 5982942d6984SJulian Wiedmann mii_data->val_out = qeth_mdio_read(dev, 5983942d6984SJulian Wiedmann mii_data->phy_id, mii_data->reg_num); 5984942d6984SJulian Wiedmann break; 5985942d6984SJulian Wiedmann case SIOC_QETH_QUERY_OAT: 5986942d6984SJulian Wiedmann rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); 5987942d6984SJulian Wiedmann break; 5988942d6984SJulian Wiedmann default: 5989942d6984SJulian Wiedmann if (card->discipline->do_ioctl) 5990942d6984SJulian Wiedmann rc = card->discipline->do_ioctl(dev, rq, cmd); 5991942d6984SJulian Wiedmann else 5992942d6984SJulian Wiedmann rc = -EOPNOTSUPP; 5993942d6984SJulian Wiedmann } 5994942d6984SJulian Wiedmann if (rc) 5995942d6984SJulian Wiedmann QETH_CARD_TEXT_(card, 2, "ioce%x", rc); 5996942d6984SJulian Wiedmann return rc; 5997942d6984SJulian Wiedmann } 5998942d6984SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_do_ioctl); 5999942d6984SJulian Wiedmann 60004a71df50SFrank Blaschka static struct { 60014a71df50SFrank Blaschka const char str[ETH_GSTRING_LEN]; 60024a71df50SFrank Blaschka } qeth_ethtool_stats_keys[] = { 60034a71df50SFrank Blaschka /* 0 */{"rx skbs"}, 60044a71df50SFrank Blaschka {"rx buffers"}, 60054a71df50SFrank Blaschka {"tx skbs"}, 60064a71df50SFrank Blaschka {"tx buffers"}, 60074a71df50SFrank Blaschka {"tx skbs no packing"}, 60084a71df50SFrank Blaschka {"tx buffers no packing"}, 60094a71df50SFrank Blaschka {"tx skbs packing"}, 60104a71df50SFrank Blaschka {"tx buffers packing"}, 60114a71df50SFrank Blaschka {"tx sg skbs"}, 6012d2a274b2SJulian Wiedmann {"tx buffer elements"}, 60134a71df50SFrank Blaschka /* 10 */{"rx sg skbs"}, 60144a71df50SFrank Blaschka {"rx sg frags"}, 60154a71df50SFrank Blaschka {"rx sg page allocs"}, 60164a71df50SFrank Blaschka {"tx large kbytes"}, 60174a71df50SFrank Blaschka {"tx large count"}, 60184a71df50SFrank Blaschka {"tx pk state ch n->p"}, 60194a71df50SFrank Blaschka {"tx pk state ch p->n"}, 60204a71df50SFrank Blaschka {"tx pk watermark low"}, 60214a71df50SFrank Blaschka {"tx pk watermark high"}, 60224a71df50SFrank Blaschka {"queue 0 buffer usage"}, 60234a71df50SFrank Blaschka /* 20 */{"queue 1 buffer usage"}, 60244a71df50SFrank Blaschka {"queue 2 buffer usage"}, 60254a71df50SFrank Blaschka {"queue 3 buffer usage"}, 6026a1c3ed4cSFrank Blaschka {"rx poll time"}, 6027a1c3ed4cSFrank Blaschka {"rx poll count"}, 60284a71df50SFrank Blaschka {"rx do_QDIO time"}, 60294a71df50SFrank Blaschka {"rx do_QDIO count"}, 60304a71df50SFrank Blaschka {"tx handler time"}, 60314a71df50SFrank Blaschka {"tx handler count"}, 60324a71df50SFrank Blaschka {"tx time"}, 60334a71df50SFrank Blaschka /* 30 */{"tx count"}, 60344a71df50SFrank Blaschka {"tx do_QDIO time"}, 60354a71df50SFrank Blaschka {"tx do_QDIO count"}, 6036f61a0d05SFrank Blaschka {"tx csum"}, 6037c3b4a740SFrank Blaschka {"tx lin"}, 60386059c905SEugene Crosser {"tx linfail"}, 60390da9581dSEinar Lueck {"cq handler count"}, 60403aade31bSKittipon Meesompop {"cq handler time"}, 60413aade31bSKittipon Meesompop {"rx csum"} 60424a71df50SFrank Blaschka }; 60434a71df50SFrank Blaschka 6044df8b4ec8SBen Hutchings int qeth_core_get_sset_count(struct net_device *dev, int stringset) 60454a71df50SFrank Blaschka { 6046df8b4ec8SBen Hutchings switch (stringset) { 6047df8b4ec8SBen Hutchings case ETH_SS_STATS: 60484a71df50SFrank Blaschka return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); 6049df8b4ec8SBen Hutchings default: 6050df8b4ec8SBen Hutchings return -EINVAL; 60514a71df50SFrank Blaschka } 6052df8b4ec8SBen Hutchings } 6053df8b4ec8SBen Hutchings EXPORT_SYMBOL_GPL(qeth_core_get_sset_count); 60544a71df50SFrank Blaschka 60554a71df50SFrank Blaschka void qeth_core_get_ethtool_stats(struct net_device *dev, 60564a71df50SFrank Blaschka struct ethtool_stats *stats, u64 *data) 60574a71df50SFrank Blaschka { 6058509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 60594a71df50SFrank Blaschka data[0] = card->stats.rx_packets - 60604a71df50SFrank Blaschka card->perf_stats.initial_rx_packets; 60614a71df50SFrank Blaschka data[1] = card->perf_stats.bufs_rec; 60624a71df50SFrank Blaschka data[2] = card->stats.tx_packets - 60634a71df50SFrank Blaschka card->perf_stats.initial_tx_packets; 60644a71df50SFrank Blaschka data[3] = card->perf_stats.bufs_sent; 60654a71df50SFrank Blaschka data[4] = card->stats.tx_packets - card->perf_stats.initial_tx_packets 60664a71df50SFrank Blaschka - card->perf_stats.skbs_sent_pack; 60674a71df50SFrank Blaschka data[5] = card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack; 60684a71df50SFrank Blaschka data[6] = card->perf_stats.skbs_sent_pack; 60694a71df50SFrank Blaschka data[7] = card->perf_stats.bufs_sent_pack; 60704a71df50SFrank Blaschka data[8] = card->perf_stats.sg_skbs_sent; 6071d2a274b2SJulian Wiedmann data[9] = card->perf_stats.buf_elements_sent; 60724a71df50SFrank Blaschka data[10] = card->perf_stats.sg_skbs_rx; 60734a71df50SFrank Blaschka data[11] = card->perf_stats.sg_frags_rx; 60744a71df50SFrank Blaschka data[12] = card->perf_stats.sg_alloc_page_rx; 60754a71df50SFrank Blaschka data[13] = (card->perf_stats.large_send_bytes >> 10); 60764a71df50SFrank Blaschka data[14] = card->perf_stats.large_send_cnt; 60774a71df50SFrank Blaschka data[15] = card->perf_stats.sc_dp_p; 60784a71df50SFrank Blaschka data[16] = card->perf_stats.sc_p_dp; 60794a71df50SFrank Blaschka data[17] = QETH_LOW_WATERMARK_PACK; 60804a71df50SFrank Blaschka data[18] = QETH_HIGH_WATERMARK_PACK; 60814a71df50SFrank Blaschka data[19] = atomic_read(&card->qdio.out_qs[0]->used_buffers); 60824a71df50SFrank Blaschka data[20] = (card->qdio.no_out_queues > 1) ? 60834a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[1]->used_buffers) : 0; 60844a71df50SFrank Blaschka data[21] = (card->qdio.no_out_queues > 2) ? 60854a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[2]->used_buffers) : 0; 60864a71df50SFrank Blaschka data[22] = (card->qdio.no_out_queues > 3) ? 60874a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[3]->used_buffers) : 0; 60884a71df50SFrank Blaschka data[23] = card->perf_stats.inbound_time; 60894a71df50SFrank Blaschka data[24] = card->perf_stats.inbound_cnt; 60904a71df50SFrank Blaschka data[25] = card->perf_stats.inbound_do_qdio_time; 60914a71df50SFrank Blaschka data[26] = card->perf_stats.inbound_do_qdio_cnt; 60924a71df50SFrank Blaschka data[27] = card->perf_stats.outbound_handler_time; 60934a71df50SFrank Blaschka data[28] = card->perf_stats.outbound_handler_cnt; 60944a71df50SFrank Blaschka data[29] = card->perf_stats.outbound_time; 60954a71df50SFrank Blaschka data[30] = card->perf_stats.outbound_cnt; 60964a71df50SFrank Blaschka data[31] = card->perf_stats.outbound_do_qdio_time; 60974a71df50SFrank Blaschka data[32] = card->perf_stats.outbound_do_qdio_cnt; 6098f61a0d05SFrank Blaschka data[33] = card->perf_stats.tx_csum; 6099c3b4a740SFrank Blaschka data[34] = card->perf_stats.tx_lin; 61006059c905SEugene Crosser data[35] = card->perf_stats.tx_linfail; 61016059c905SEugene Crosser data[36] = card->perf_stats.cq_cnt; 61026059c905SEugene Crosser data[37] = card->perf_stats.cq_time; 61033aade31bSKittipon Meesompop data[38] = card->perf_stats.rx_csum; 61044a71df50SFrank Blaschka } 61054a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); 61064a71df50SFrank Blaschka 61074a71df50SFrank Blaschka void qeth_core_get_strings(struct net_device *dev, u32 stringset, u8 *data) 61084a71df50SFrank Blaschka { 61094a71df50SFrank Blaschka switch (stringset) { 61104a71df50SFrank Blaschka case ETH_SS_STATS: 61114a71df50SFrank Blaschka memcpy(data, &qeth_ethtool_stats_keys, 61124a71df50SFrank Blaschka sizeof(qeth_ethtool_stats_keys)); 61134a71df50SFrank Blaschka break; 61144a71df50SFrank Blaschka default: 61154a71df50SFrank Blaschka WARN_ON(1); 61164a71df50SFrank Blaschka break; 61174a71df50SFrank Blaschka } 61184a71df50SFrank Blaschka } 61194a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_strings); 61204a71df50SFrank Blaschka 61214a71df50SFrank Blaschka void qeth_core_get_drvinfo(struct net_device *dev, 61224a71df50SFrank Blaschka struct ethtool_drvinfo *info) 61234a71df50SFrank Blaschka { 6124509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 61254a71df50SFrank Blaschka 61267826d43fSJiri Pirko strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", 61277826d43fSJiri Pirko sizeof(info->driver)); 61287826d43fSJiri Pirko strlcpy(info->version, "1.0", sizeof(info->version)); 61297826d43fSJiri Pirko strlcpy(info->fw_version, card->info.mcl_level, 61307826d43fSJiri Pirko sizeof(info->fw_version)); 61317826d43fSJiri Pirko snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", 61327826d43fSJiri Pirko CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); 61334a71df50SFrank Blaschka } 61344a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); 61354a71df50SFrank Blaschka 6136774afb8eSJulian Wiedmann /* Helper function to fill 'advertising' and 'supported' which are the same. */ 6137774afb8eSJulian Wiedmann /* Autoneg and full-duplex are supported and advertised unconditionally. */ 6138774afb8eSJulian Wiedmann /* Always advertise and support all speeds up to specified, and only one */ 613902d5cb5bSEugene Crosser /* specified port type. */ 6140993e19c0SJulian Wiedmann static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, 614102d5cb5bSEugene Crosser int maxspeed, int porttype) 614202d5cb5bSEugene Crosser { 614341fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, supported); 614441fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, advertising); 614541fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); 6146774afb8eSJulian Wiedmann 614741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); 614841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); 614902d5cb5bSEugene Crosser 615002d5cb5bSEugene Crosser switch (porttype) { 615102d5cb5bSEugene Crosser case PORT_TP: 615241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 615341fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 615402d5cb5bSEugene Crosser break; 615502d5cb5bSEugene Crosser case PORT_FIBRE: 615641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); 615741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); 615802d5cb5bSEugene Crosser break; 615902d5cb5bSEugene Crosser default: 616041fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 616141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 616202d5cb5bSEugene Crosser WARN_ON_ONCE(1); 616302d5cb5bSEugene Crosser } 616402d5cb5bSEugene Crosser 6165774afb8eSJulian Wiedmann /* fallthrough from high to low, to select all legal speeds: */ 616602d5cb5bSEugene Crosser switch (maxspeed) { 616702d5cb5bSEugene Crosser case SPEED_10000: 616841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 616941fc3b65SJulian Wiedmann 10000baseT_Full); 617041fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 617141fc3b65SJulian Wiedmann 10000baseT_Full); 617202d5cb5bSEugene Crosser case SPEED_1000: 617341fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 617441fc3b65SJulian Wiedmann 1000baseT_Full); 617541fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 617641fc3b65SJulian Wiedmann 1000baseT_Full); 617741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 617841fc3b65SJulian Wiedmann 1000baseT_Half); 617941fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 618041fc3b65SJulian Wiedmann 1000baseT_Half); 618102d5cb5bSEugene Crosser case SPEED_100: 618241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 618341fc3b65SJulian Wiedmann 100baseT_Full); 618441fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 618541fc3b65SJulian Wiedmann 100baseT_Full); 618641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 618741fc3b65SJulian Wiedmann 100baseT_Half); 618841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 618941fc3b65SJulian Wiedmann 100baseT_Half); 619002d5cb5bSEugene Crosser case SPEED_10: 619141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 619241fc3b65SJulian Wiedmann 10baseT_Full); 619341fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 619441fc3b65SJulian Wiedmann 10baseT_Full); 619541fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 619641fc3b65SJulian Wiedmann 10baseT_Half); 619741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 619841fc3b65SJulian Wiedmann 10baseT_Half); 6199774afb8eSJulian Wiedmann /* end fallthrough */ 620002d5cb5bSEugene Crosser break; 620102d5cb5bSEugene Crosser default: 620241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 620341fc3b65SJulian Wiedmann 10baseT_Full); 620441fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 620541fc3b65SJulian Wiedmann 10baseT_Full); 620641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 620741fc3b65SJulian Wiedmann 10baseT_Half); 620841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 620941fc3b65SJulian Wiedmann 10baseT_Half); 621002d5cb5bSEugene Crosser WARN_ON_ONCE(1); 621102d5cb5bSEugene Crosser } 621202d5cb5bSEugene Crosser } 621302d5cb5bSEugene Crosser 6214993e19c0SJulian Wiedmann int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev, 6215993e19c0SJulian Wiedmann struct ethtool_link_ksettings *cmd) 62163f9975aaSFrank Blaschka { 6217509e2562SHeiko Carstens struct qeth_card *card = netdev->ml_priv; 62183f9975aaSFrank Blaschka enum qeth_link_types link_type; 621902d5cb5bSEugene Crosser struct carrier_info carrier_info; 6220511c2445SEugene Crosser int rc; 62213f9975aaSFrank Blaschka 62223f9975aaSFrank Blaschka if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) 62233f9975aaSFrank Blaschka link_type = QETH_LINK_TYPE_10GBIT_ETH; 62243f9975aaSFrank Blaschka else 62253f9975aaSFrank Blaschka link_type = card->info.link_type; 62263f9975aaSFrank Blaschka 6227993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_FULL; 6228993e19c0SJulian Wiedmann cmd->base.autoneg = AUTONEG_ENABLE; 6229993e19c0SJulian Wiedmann cmd->base.phy_address = 0; 6230993e19c0SJulian Wiedmann cmd->base.mdio_support = 0; 6231993e19c0SJulian Wiedmann cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; 6232993e19c0SJulian Wiedmann cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; 62333f9975aaSFrank Blaschka 62343f9975aaSFrank Blaschka switch (link_type) { 62353f9975aaSFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 62363f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 6237993e19c0SJulian Wiedmann cmd->base.speed = SPEED_100; 6238993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 62393f9975aaSFrank Blaschka break; 62403f9975aaSFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 62413f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 6242993e19c0SJulian Wiedmann cmd->base.speed = SPEED_1000; 6243993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 62443f9975aaSFrank Blaschka break; 62453f9975aaSFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 6246993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10000; 6247993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 62483f9975aaSFrank Blaschka break; 62493f9975aaSFrank Blaschka default: 6250993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10; 6251993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 62523f9975aaSFrank Blaschka } 6253993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port); 62543f9975aaSFrank Blaschka 625502d5cb5bSEugene Crosser /* Check if we can obtain more accurate information. */ 625602d5cb5bSEugene Crosser /* If QUERY_CARD_INFO command is not supported or fails, */ 625702d5cb5bSEugene Crosser /* just return the heuristics that was filled above. */ 6258511c2445SEugene Crosser if (!qeth_card_hw_is_reachable(card)) 6259511c2445SEugene Crosser return -ENODEV; 6260511c2445SEugene Crosser rc = qeth_query_card_info(card, &carrier_info); 6261511c2445SEugene Crosser if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */ 626202d5cb5bSEugene Crosser return 0; 6263511c2445SEugene Crosser if (rc) /* report error from the hardware operation */ 6264511c2445SEugene Crosser return rc; 6265511c2445SEugene Crosser /* on success, fill in the information got from the hardware */ 626602d5cb5bSEugene Crosser 626702d5cb5bSEugene Crosser netdev_dbg(netdev, 626802d5cb5bSEugene Crosser "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", 626902d5cb5bSEugene Crosser carrier_info.card_type, 627002d5cb5bSEugene Crosser carrier_info.port_mode, 627102d5cb5bSEugene Crosser carrier_info.port_speed); 627202d5cb5bSEugene Crosser 627302d5cb5bSEugene Crosser /* Update attributes for which we've obtained more authoritative */ 627402d5cb5bSEugene Crosser /* information, leave the rest the way they where filled above. */ 627502d5cb5bSEugene Crosser switch (carrier_info.card_type) { 627602d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_COPPER_A: 627702d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_COPPER_B: 6278993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 6279993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 628002d5cb5bSEugene Crosser break; 628102d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_FIBRE_A: 628202d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_FIBRE_B: 6283993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 6284993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 628502d5cb5bSEugene Crosser break; 628602d5cb5bSEugene Crosser case CARD_INFO_TYPE_10G_FIBRE_A: 628702d5cb5bSEugene Crosser case CARD_INFO_TYPE_10G_FIBRE_B: 6288993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 6289993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port); 629002d5cb5bSEugene Crosser break; 629102d5cb5bSEugene Crosser } 629202d5cb5bSEugene Crosser 629302d5cb5bSEugene Crosser switch (carrier_info.port_mode) { 629402d5cb5bSEugene Crosser case CARD_INFO_PORTM_FULLDUPLEX: 6295993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_FULL; 629602d5cb5bSEugene Crosser break; 629702d5cb5bSEugene Crosser case CARD_INFO_PORTM_HALFDUPLEX: 6298993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_HALF; 629902d5cb5bSEugene Crosser break; 630002d5cb5bSEugene Crosser } 630102d5cb5bSEugene Crosser 630202d5cb5bSEugene Crosser switch (carrier_info.port_speed) { 630302d5cb5bSEugene Crosser case CARD_INFO_PORTS_10M: 6304993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10; 630502d5cb5bSEugene Crosser break; 630602d5cb5bSEugene Crosser case CARD_INFO_PORTS_100M: 6307993e19c0SJulian Wiedmann cmd->base.speed = SPEED_100; 630802d5cb5bSEugene Crosser break; 630902d5cb5bSEugene Crosser case CARD_INFO_PORTS_1G: 6310993e19c0SJulian Wiedmann cmd->base.speed = SPEED_1000; 631102d5cb5bSEugene Crosser break; 631202d5cb5bSEugene Crosser case CARD_INFO_PORTS_10G: 6313993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10000; 631402d5cb5bSEugene Crosser break; 631502d5cb5bSEugene Crosser } 631602d5cb5bSEugene Crosser 63173f9975aaSFrank Blaschka return 0; 63183f9975aaSFrank Blaschka } 6319993e19c0SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_link_ksettings); 63203f9975aaSFrank Blaschka 6321c9475369SThomas Richter /* Callback to handle checksum offload command reply from OSA card. 6322c9475369SThomas Richter * Verify that required features have been enabled on the card. 6323c9475369SThomas Richter * Return error in hdr->return_code as this value is checked by caller. 6324c9475369SThomas Richter * 6325c9475369SThomas Richter * Always returns zero to indicate no further messages from the OSA card. 6326c9475369SThomas Richter */ 6327c9475369SThomas Richter static int qeth_ipa_checksum_run_cmd_cb(struct qeth_card *card, 6328c9475369SThomas Richter struct qeth_reply *reply, 6329c9475369SThomas Richter unsigned long data) 6330c9475369SThomas Richter { 6331c9475369SThomas Richter struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 6332c9475369SThomas Richter struct qeth_checksum_cmd *chksum_cb = 6333c9475369SThomas Richter (struct qeth_checksum_cmd *)reply->param; 6334c9475369SThomas Richter 6335c9475369SThomas Richter QETH_CARD_TEXT(card, 4, "chkdoccb"); 6336ad3cbf61SJulian Wiedmann if (qeth_setassparms_inspect_rc(cmd)) 6337c9475369SThomas Richter return 0; 6338c9475369SThomas Richter 6339c9475369SThomas Richter memset(chksum_cb, 0, sizeof(*chksum_cb)); 6340c9475369SThomas Richter if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { 6341c9475369SThomas Richter chksum_cb->supported = 6342c9475369SThomas Richter cmd->data.setassparms.data.chksum.supported; 6343c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "strt:%x", chksum_cb->supported); 6344c9475369SThomas Richter } 6345c9475369SThomas Richter if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_ENABLE) { 6346c9475369SThomas Richter chksum_cb->supported = 6347c9475369SThomas Richter cmd->data.setassparms.data.chksum.supported; 6348c9475369SThomas Richter chksum_cb->enabled = 6349c9475369SThomas Richter cmd->data.setassparms.data.chksum.enabled; 6350c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "supp:%x", chksum_cb->supported); 6351c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "enab:%x", chksum_cb->enabled); 6352c9475369SThomas Richter } 6353c9475369SThomas Richter return 0; 6354c9475369SThomas Richter } 6355c9475369SThomas Richter 6356c9475369SThomas Richter /* Send command to OSA card and check results. */ 6357c9475369SThomas Richter static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, 6358c9475369SThomas Richter enum qeth_ipa_funcs ipa_func, 6359c9475369SThomas Richter __u16 cmd_code, long data, 6360a8155b00SKittipon Meesompop struct qeth_checksum_cmd *chksum_cb, 6361a8155b00SKittipon Meesompop enum qeth_prot_versions prot) 6362c9475369SThomas Richter { 6363c9475369SThomas Richter struct qeth_cmd_buffer *iob; 6364c9475369SThomas Richter int rc = -ENOMEM; 6365c9475369SThomas Richter 6366c9475369SThomas Richter QETH_CARD_TEXT(card, 4, "chkdocmd"); 6367c9475369SThomas Richter iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, 6368a8155b00SKittipon Meesompop sizeof(__u32), prot); 6369c9475369SThomas Richter if (iob) 6370c9475369SThomas Richter rc = qeth_send_setassparms(card, iob, sizeof(__u32), data, 6371c9475369SThomas Richter qeth_ipa_checksum_run_cmd_cb, 6372c9475369SThomas Richter chksum_cb); 6373c9475369SThomas Richter return rc; 6374c9475369SThomas Richter } 6375c9475369SThomas Richter 6376a8155b00SKittipon Meesompop static int qeth_send_checksum_on(struct qeth_card *card, int cstype, 6377a8155b00SKittipon Meesompop enum qeth_prot_versions prot) 63784d7def2aSThomas Richter { 6379571f9dd8SKittipon Meesompop u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP; 6380c9475369SThomas Richter struct qeth_checksum_cmd chksum_cb; 63814d7def2aSThomas Richter int rc; 63824d7def2aSThomas Richter 6383571f9dd8SKittipon Meesompop if (prot == QETH_PROT_IPV4) 6384571f9dd8SKittipon Meesompop required_features |= QETH_IPA_CHECKSUM_IP_HDR; 6385c9475369SThomas Richter rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, 6386a8155b00SKittipon Meesompop &chksum_cb, prot); 6387f9d8e6dcSThomas Richter if (!rc) { 6388f9d8e6dcSThomas Richter if ((required_features & chksum_cb.supported) != 6389f9d8e6dcSThomas Richter required_features) 6390f9d8e6dcSThomas Richter rc = -EIO; 6391dae84c8eSThomas Richter else if (!(QETH_IPA_CHECKSUM_LP2LP & chksum_cb.supported) && 6392dae84c8eSThomas Richter cstype == IPA_INBOUND_CHECKSUM) 6393dae84c8eSThomas Richter dev_warn(&card->gdev->dev, 6394dae84c8eSThomas Richter "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n", 6395dae84c8eSThomas Richter QETH_CARD_IFNAME(card)); 6396f9d8e6dcSThomas Richter } 63974d7def2aSThomas Richter if (rc) { 6398a8155b00SKittipon Meesompop qeth_send_simple_setassparms_prot(card, cstype, 6399a8155b00SKittipon Meesompop IPA_CMD_ASS_STOP, 0, prot); 64008f43fb00SThomas Richter dev_warn(&card->gdev->dev, 6401a8155b00SKittipon Meesompop "Starting HW IPv%d checksumming for %s failed, using SW checksumming\n", 6402a8155b00SKittipon Meesompop prot, QETH_CARD_IFNAME(card)); 64034d7def2aSThomas Richter return rc; 64044d7def2aSThomas Richter } 6405c9475369SThomas Richter rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE, 6406a8155b00SKittipon Meesompop chksum_cb.supported, &chksum_cb, 6407a8155b00SKittipon Meesompop prot); 6408f9d8e6dcSThomas Richter if (!rc) { 6409f9d8e6dcSThomas Richter if ((required_features & chksum_cb.enabled) != 6410f9d8e6dcSThomas Richter required_features) 6411f9d8e6dcSThomas Richter rc = -EIO; 6412f9d8e6dcSThomas Richter } 64134d7def2aSThomas Richter if (rc) { 6414a8155b00SKittipon Meesompop qeth_send_simple_setassparms_prot(card, cstype, 6415a8155b00SKittipon Meesompop IPA_CMD_ASS_STOP, 0, prot); 64168f43fb00SThomas Richter dev_warn(&card->gdev->dev, 6417a8155b00SKittipon Meesompop "Enabling HW IPv%d checksumming for %s failed, using SW checksumming\n", 6418a8155b00SKittipon Meesompop prot, QETH_CARD_IFNAME(card)); 64194d7def2aSThomas Richter return rc; 64204d7def2aSThomas Richter } 64218f43fb00SThomas Richter 6422a8155b00SKittipon Meesompop dev_info(&card->gdev->dev, "HW Checksumming (%sbound IPv%d) enabled\n", 6423a8155b00SKittipon Meesompop cstype == IPA_INBOUND_CHECKSUM ? "in" : "out", prot); 64244d7def2aSThomas Richter return 0; 64254d7def2aSThomas Richter } 64264d7def2aSThomas Richter 6427a8155b00SKittipon Meesompop static int qeth_set_ipa_csum(struct qeth_card *card, bool on, int cstype, 6428a8155b00SKittipon Meesompop enum qeth_prot_versions prot) 64294d7def2aSThomas Richter { 6430a8155b00SKittipon Meesompop int rc = (on) ? qeth_send_checksum_on(card, cstype, prot) 6431a8155b00SKittipon Meesompop : qeth_send_simple_setassparms_prot(card, cstype, 6432a8155b00SKittipon Meesompop IPA_CMD_ASS_STOP, 0, 6433a8155b00SKittipon Meesompop prot); 6434c9475369SThomas Richter return rc ? -EIO : 0; 64354d7def2aSThomas Richter } 64364d7def2aSThomas Richter 64378f43fb00SThomas Richter static int qeth_set_ipa_tso(struct qeth_card *card, int on) 64384d7def2aSThomas Richter { 64398f43fb00SThomas Richter int rc; 64404d7def2aSThomas Richter 64418f43fb00SThomas Richter QETH_CARD_TEXT(card, 3, "sttso"); 64428f43fb00SThomas Richter 64438f43fb00SThomas Richter if (on) { 64448f43fb00SThomas Richter rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, 64454d7def2aSThomas Richter IPA_CMD_ASS_START, 0); 64468f43fb00SThomas Richter if (rc) { 64478f43fb00SThomas Richter dev_warn(&card->gdev->dev, 64488f43fb00SThomas Richter "Starting outbound TCP segmentation offload for %s failed\n", 64498f43fb00SThomas Richter QETH_CARD_IFNAME(card)); 64508f43fb00SThomas Richter return -EIO; 64518f43fb00SThomas Richter } 64528f43fb00SThomas Richter dev_info(&card->gdev->dev, "Outbound TSO enabled\n"); 64538f43fb00SThomas Richter } else { 64548f43fb00SThomas Richter rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, 64558f43fb00SThomas Richter IPA_CMD_ASS_STOP, 0); 64568f43fb00SThomas Richter } 64574d7def2aSThomas Richter return rc; 64584d7def2aSThomas Richter } 64598f43fb00SThomas Richter 6460d7e6ed97SKittipon Meesompop static int qeth_set_ipa_rx_csum(struct qeth_card *card, bool on) 6461d7e6ed97SKittipon Meesompop { 6462d7e6ed97SKittipon Meesompop int rc_ipv4 = (on) ? -EOPNOTSUPP : 0; 6463d7e6ed97SKittipon Meesompop int rc_ipv6; 6464ce344356SJulian Wiedmann 6465d7e6ed97SKittipon Meesompop if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) 6466d7e6ed97SKittipon Meesompop rc_ipv4 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, 6467d7e6ed97SKittipon Meesompop QETH_PROT_IPV4); 6468d7e6ed97SKittipon Meesompop if (!qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) 6469d7e6ed97SKittipon Meesompop /* no/one Offload Assist available, so the rc is trivial */ 6470d7e6ed97SKittipon Meesompop return rc_ipv4; 6471d7e6ed97SKittipon Meesompop 6472d7e6ed97SKittipon Meesompop rc_ipv6 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, 6473d7e6ed97SKittipon Meesompop QETH_PROT_IPV6); 6474d7e6ed97SKittipon Meesompop 6475d7e6ed97SKittipon Meesompop if (on) 6476d7e6ed97SKittipon Meesompop /* enable: success if any Assist is active */ 6477d7e6ed97SKittipon Meesompop return (rc_ipv6) ? rc_ipv4 : 0; 6478d7e6ed97SKittipon Meesompop 6479d7e6ed97SKittipon Meesompop /* disable: failure if any Assist is still active */ 6480d7e6ed97SKittipon Meesompop return (rc_ipv6) ? rc_ipv6 : rc_ipv4; 6481d7e6ed97SKittipon Meesompop } 6482d7e6ed97SKittipon Meesompop 6483571f9dd8SKittipon Meesompop #define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO | \ 6484571f9dd8SKittipon Meesompop NETIF_F_IPV6_CSUM) 6485ce344356SJulian Wiedmann /** 6486d025da9eSJulian Wiedmann * qeth_enable_hw_features() - (Re-)Enable HW functions for device features 6487d025da9eSJulian Wiedmann * @dev: a net_device 6488ce344356SJulian Wiedmann */ 6489d025da9eSJulian Wiedmann void qeth_enable_hw_features(struct net_device *dev) 6490e830baa9SHans Wippel { 6491e830baa9SHans Wippel struct qeth_card *card = dev->ml_priv; 6492d025da9eSJulian Wiedmann netdev_features_t features; 6493e830baa9SHans Wippel 6494d025da9eSJulian Wiedmann rtnl_lock(); 6495d025da9eSJulian Wiedmann features = dev->features; 6496ce344356SJulian Wiedmann /* force-off any feature that needs an IPA sequence. 6497ce344356SJulian Wiedmann * netdev_update_features() will restart them. 6498ce344356SJulian Wiedmann */ 6499ce344356SJulian Wiedmann dev->features &= ~QETH_HW_FEATURES; 6500ce344356SJulian Wiedmann netdev_update_features(dev); 6501d025da9eSJulian Wiedmann if (features != dev->features) 6502e830baa9SHans Wippel dev_warn(&card->gdev->dev, 6503e830baa9SHans Wippel "Device recovery failed to restore all offload features\n"); 6504d025da9eSJulian Wiedmann rtnl_unlock(); 6505e830baa9SHans Wippel } 6506d025da9eSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_enable_hw_features); 6507e830baa9SHans Wippel 65088f43fb00SThomas Richter int qeth_set_features(struct net_device *dev, netdev_features_t features) 65098f43fb00SThomas Richter { 65108f43fb00SThomas Richter struct qeth_card *card = dev->ml_priv; 65116c7cd712SHans Wippel netdev_features_t changed = dev->features ^ features; 65128f43fb00SThomas Richter int rc = 0; 65138f43fb00SThomas Richter 65148f43fb00SThomas Richter QETH_DBF_TEXT(SETUP, 2, "setfeat"); 65158f43fb00SThomas Richter QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); 65168f43fb00SThomas Richter 65176c7cd712SHans Wippel if ((changed & NETIF_F_IP_CSUM)) { 6518a8155b00SKittipon Meesompop rc = qeth_set_ipa_csum(card, features & NETIF_F_IP_CSUM, 6519a8155b00SKittipon Meesompop IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4); 65206c7cd712SHans Wippel if (rc) 65216c7cd712SHans Wippel changed ^= NETIF_F_IP_CSUM; 65226c7cd712SHans Wippel } 6523571f9dd8SKittipon Meesompop if (changed & NETIF_F_IPV6_CSUM) { 6524571f9dd8SKittipon Meesompop rc = qeth_set_ipa_csum(card, features & NETIF_F_IPV6_CSUM, 6525571f9dd8SKittipon Meesompop IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6); 6526571f9dd8SKittipon Meesompop if (rc) 6527571f9dd8SKittipon Meesompop changed ^= NETIF_F_IPV6_CSUM; 6528571f9dd8SKittipon Meesompop } 6529d7e6ed97SKittipon Meesompop if (changed & NETIF_F_RXCSUM) { 6530d7e6ed97SKittipon Meesompop rc = qeth_set_ipa_rx_csum(card, features & NETIF_F_RXCSUM); 65316c7cd712SHans Wippel if (rc) 65326c7cd712SHans Wippel changed ^= NETIF_F_RXCSUM; 65336c7cd712SHans Wippel } 65346c7cd712SHans Wippel if ((changed & NETIF_F_TSO)) { 65356c7cd712SHans Wippel rc = qeth_set_ipa_tso(card, features & NETIF_F_TSO ? 1 : 0); 65366c7cd712SHans Wippel if (rc) 65376c7cd712SHans Wippel changed ^= NETIF_F_TSO; 65386c7cd712SHans Wippel } 65396c7cd712SHans Wippel 65406c7cd712SHans Wippel /* everything changed successfully? */ 65416c7cd712SHans Wippel if ((dev->features ^ features) == changed) 65426c7cd712SHans Wippel return 0; 65436c7cd712SHans Wippel /* something went wrong. save changed features and return error */ 65446c7cd712SHans Wippel dev->features ^= changed; 65456c7cd712SHans Wippel return -EIO; 65468f43fb00SThomas Richter } 65478f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_set_features); 65488f43fb00SThomas Richter 65498f43fb00SThomas Richter netdev_features_t qeth_fix_features(struct net_device *dev, 65508f43fb00SThomas Richter netdev_features_t features) 65518f43fb00SThomas Richter { 65528f43fb00SThomas Richter struct qeth_card *card = dev->ml_priv; 65538f43fb00SThomas Richter 65548f43fb00SThomas Richter QETH_DBF_TEXT(SETUP, 2, "fixfeat"); 65558f43fb00SThomas Richter if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) 65568f43fb00SThomas Richter features &= ~NETIF_F_IP_CSUM; 6557571f9dd8SKittipon Meesompop if (!qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) 6558571f9dd8SKittipon Meesompop features &= ~NETIF_F_IPV6_CSUM; 6559d7e6ed97SKittipon Meesompop if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM) && 6560d7e6ed97SKittipon Meesompop !qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) 65618f43fb00SThomas Richter features &= ~NETIF_F_RXCSUM; 6562cf536ffeSJulian Wiedmann if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) 65638f43fb00SThomas Richter features &= ~NETIF_F_TSO; 65646c7cd712SHans Wippel /* if the card isn't up, remove features that require hw changes */ 65656c7cd712SHans Wippel if (card->state == CARD_STATE_DOWN || 65666c7cd712SHans Wippel card->state == CARD_STATE_RECOVER) 6567ce344356SJulian Wiedmann features &= ~QETH_HW_FEATURES; 65688f43fb00SThomas Richter QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); 65698f43fb00SThomas Richter return features; 65708f43fb00SThomas Richter } 65718f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_fix_features); 65724d7def2aSThomas Richter 65736d69b1f1SJulian Wiedmann netdev_features_t qeth_features_check(struct sk_buff *skb, 65746d69b1f1SJulian Wiedmann struct net_device *dev, 65756d69b1f1SJulian Wiedmann netdev_features_t features) 65766d69b1f1SJulian Wiedmann { 65776d69b1f1SJulian Wiedmann /* GSO segmentation builds skbs with 65786d69b1f1SJulian Wiedmann * a (small) linear part for the headers, and 65796d69b1f1SJulian Wiedmann * page frags for the data. 65806d69b1f1SJulian Wiedmann * Compared to a linear skb, the header-only part consumes an 65816d69b1f1SJulian Wiedmann * additional buffer element. This reduces buffer utilization, and 65826d69b1f1SJulian Wiedmann * hurts throughput. So compress small segments into one element. 65836d69b1f1SJulian Wiedmann */ 65846d69b1f1SJulian Wiedmann if (netif_needs_gso(skb, features)) { 65856d69b1f1SJulian Wiedmann /* match skb_segment(): */ 65866d69b1f1SJulian Wiedmann unsigned int doffset = skb->data - skb_mac_header(skb); 65876d69b1f1SJulian Wiedmann unsigned int hsize = skb_shinfo(skb)->gso_size; 65886d69b1f1SJulian Wiedmann unsigned int hroom = skb_headroom(skb); 65896d69b1f1SJulian Wiedmann 65906d69b1f1SJulian Wiedmann /* linearize only if resulting skb allocations are order-0: */ 65916d69b1f1SJulian Wiedmann if (SKB_DATA_ALIGN(hroom + doffset + hsize) <= SKB_MAX_HEAD(0)) 65926d69b1f1SJulian Wiedmann features &= ~NETIF_F_SG; 65936d69b1f1SJulian Wiedmann } 65946d69b1f1SJulian Wiedmann 65956d69b1f1SJulian Wiedmann return vlan_features_check(skb, features); 65966d69b1f1SJulian Wiedmann } 65976d69b1f1SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_features_check); 65986d69b1f1SJulian Wiedmann 65994a71df50SFrank Blaschka static int __init qeth_core_init(void) 66004a71df50SFrank Blaschka { 66014a71df50SFrank Blaschka int rc; 66024a71df50SFrank Blaschka 660374eacdb9SFrank Blaschka pr_info("loading core functions\n"); 66044a71df50SFrank Blaschka INIT_LIST_HEAD(&qeth_core_card_list.list); 6605819dc537SStefan Raspl INIT_LIST_HEAD(&qeth_dbf_list); 66064a71df50SFrank Blaschka rwlock_init(&qeth_core_card_list.rwlock); 66072022e00cSFrank Blaschka mutex_init(&qeth_mod_mutex); 66084a71df50SFrank Blaschka 66090f54761dSStefan Raspl qeth_wq = create_singlethread_workqueue("qeth_wq"); 6610a936b1efSJulian Wiedmann if (!qeth_wq) { 6611a936b1efSJulian Wiedmann rc = -ENOMEM; 6612a936b1efSJulian Wiedmann goto out_err; 6613a936b1efSJulian Wiedmann } 66140f54761dSStefan Raspl 66154a71df50SFrank Blaschka rc = qeth_register_dbf_views(); 66164a71df50SFrank Blaschka if (rc) 6617a936b1efSJulian Wiedmann goto dbf_err; 6618035da16fSMark McLoughlin qeth_core_root_dev = root_device_register("qeth"); 66199262c6c2SDuan Jiong rc = PTR_ERR_OR_ZERO(qeth_core_root_dev); 66204a71df50SFrank Blaschka if (rc) 66214a71df50SFrank Blaschka goto register_err; 6622683d718aSFrank Blaschka qeth_core_header_cache = kmem_cache_create("qeth_hdr", 6623683d718aSFrank Blaschka sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL); 6624683d718aSFrank Blaschka if (!qeth_core_header_cache) { 6625683d718aSFrank Blaschka rc = -ENOMEM; 6626683d718aSFrank Blaschka goto slab_err; 6627683d718aSFrank Blaschka } 66280da9581dSEinar Lueck qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf", 66290da9581dSEinar Lueck sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL); 66300da9581dSEinar Lueck if (!qeth_qdio_outbuf_cache) { 66310da9581dSEinar Lueck rc = -ENOMEM; 66320da9581dSEinar Lueck goto cqslab_err; 66330da9581dSEinar Lueck } 66344a71df50SFrank Blaschka rc = ccw_driver_register(&qeth_ccw_driver); 66354a71df50SFrank Blaschka if (rc) 66364a71df50SFrank Blaschka goto ccw_err; 66374a71df50SFrank Blaschka rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); 66384a71df50SFrank Blaschka if (rc) 66394a71df50SFrank Blaschka goto ccwgroup_err; 66400da9581dSEinar Lueck 6641683d718aSFrank Blaschka return 0; 6642afb6ac59SSebastian Ott 6643afb6ac59SSebastian Ott ccwgroup_err: 6644afb6ac59SSebastian Ott ccw_driver_unregister(&qeth_ccw_driver); 6645afb6ac59SSebastian Ott ccw_err: 6646afb6ac59SSebastian Ott kmem_cache_destroy(qeth_qdio_outbuf_cache); 66470da9581dSEinar Lueck cqslab_err: 66480da9581dSEinar Lueck kmem_cache_destroy(qeth_core_header_cache); 6649683d718aSFrank Blaschka slab_err: 6650035da16fSMark McLoughlin root_device_unregister(qeth_core_root_dev); 66514a71df50SFrank Blaschka register_err: 66524a71df50SFrank Blaschka qeth_unregister_dbf_views(); 6653a936b1efSJulian Wiedmann dbf_err: 6654a936b1efSJulian Wiedmann destroy_workqueue(qeth_wq); 66554a71df50SFrank Blaschka out_err: 665674eacdb9SFrank Blaschka pr_err("Initializing the qeth device driver failed\n"); 66574a71df50SFrank Blaschka return rc; 66584a71df50SFrank Blaschka } 66594a71df50SFrank Blaschka 66604a71df50SFrank Blaschka static void __exit qeth_core_exit(void) 66614a71df50SFrank Blaschka { 6662819dc537SStefan Raspl qeth_clear_dbf_list(); 66630f54761dSStefan Raspl destroy_workqueue(qeth_wq); 66644a71df50SFrank Blaschka ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); 66654a71df50SFrank Blaschka ccw_driver_unregister(&qeth_ccw_driver); 66660da9581dSEinar Lueck kmem_cache_destroy(qeth_qdio_outbuf_cache); 6667683d718aSFrank Blaschka kmem_cache_destroy(qeth_core_header_cache); 6668afb6ac59SSebastian Ott root_device_unregister(qeth_core_root_dev); 66694a71df50SFrank Blaschka qeth_unregister_dbf_views(); 667074eacdb9SFrank Blaschka pr_info("core functions removed\n"); 66714a71df50SFrank Blaschka } 66724a71df50SFrank Blaschka 66734a71df50SFrank Blaschka module_init(qeth_core_init); 66744a71df50SFrank Blaschka module_exit(qeth_core_exit); 66754a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 66764a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth core functions"); 66774a71df50SFrank Blaschka MODULE_LICENSE("GPL"); 6678