14a71df50SFrank Blaschka /* 2bbcfcdc8SFrank Blaschka * Copyright IBM Corp. 2007, 2009 34a71df50SFrank Blaschka * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, 44a71df50SFrank Blaschka * Frank Pavlic <fpavlic@de.ibm.com>, 54a71df50SFrank Blaschka * Thomas Spatzier <tspat@de.ibm.com>, 64a71df50SFrank Blaschka * Frank Blaschka <frank.blaschka@de.ibm.com> 74a71df50SFrank Blaschka */ 84a71df50SFrank Blaschka 974eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth" 1074eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1174eacdb9SFrank Blaschka 124a71df50SFrank Blaschka #include <linux/module.h> 134a71df50SFrank Blaschka #include <linux/moduleparam.h> 144a71df50SFrank Blaschka #include <linux/string.h> 154a71df50SFrank Blaschka #include <linux/errno.h> 164a71df50SFrank Blaschka #include <linux/kernel.h> 174a71df50SFrank Blaschka #include <linux/ip.h> 184a71df50SFrank Blaschka #include <linux/tcp.h> 194a71df50SFrank Blaschka #include <linux/mii.h> 204a71df50SFrank Blaschka #include <linux/kthread.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 22b3332930SFrank Blaschka #include <net/iucv/af_iucv.h> 23290b8348SStefan Raspl #include <net/dsfield.h> 244a71df50SFrank Blaschka 25ab4227cbSMartin Schwidefsky #include <asm/ebcdic.h> 262bf29df7SSebastian Ott #include <asm/chpid.h> 27ab4227cbSMartin Schwidefsky #include <asm/io.h> 281da74b1cSFrank Blaschka #include <asm/sysinfo.h> 29c3ab96f3SFrank Blaschka #include <asm/compat.h> 30ec61bd2fSJulian Wiedmann #include <asm/diag.h> 31ec61bd2fSJulian Wiedmann #include <asm/cio.h> 32ec61bd2fSJulian Wiedmann #include <asm/ccwdev.h> 334a71df50SFrank Blaschka 344a71df50SFrank Blaschka #include "qeth_core.h" 354a71df50SFrank Blaschka 36d11ba0c4SPeter Tiedemann struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { 37d11ba0c4SPeter Tiedemann /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */ 38d11ba0c4SPeter Tiedemann /* N P A M L V H */ 39d11ba0c4SPeter Tiedemann [QETH_DBF_SETUP] = {"qeth_setup", 40d11ba0c4SPeter Tiedemann 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, 41f7e1e65dSSebastian Ott [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, 42f7e1e65dSSebastian Ott &debug_sprintf_view, NULL}, 43d11ba0c4SPeter Tiedemann [QETH_DBF_CTRL] = {"qeth_control", 44d11ba0c4SPeter Tiedemann 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, 45d11ba0c4SPeter Tiedemann }; 46d11ba0c4SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf); 474a71df50SFrank Blaschka 484a71df50SFrank Blaschka struct qeth_card_list_struct qeth_core_card_list; 494a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_card_list); 50683d718aSFrank Blaschka struct kmem_cache *qeth_core_header_cache; 51683d718aSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_header_cache); 520da9581dSEinar Lueck static struct kmem_cache *qeth_qdio_outbuf_cache; 534a71df50SFrank Blaschka 544a71df50SFrank Blaschka static struct device *qeth_core_root_dev; 554a71df50SFrank Blaschka static struct lock_class_key qdio_out_skb_queue_key; 562022e00cSFrank Blaschka static struct mutex qeth_mod_mutex; 574a71df50SFrank Blaschka 584a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *, 594a71df50SFrank Blaschka struct qeth_cmd_buffer *); 604a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *); 614a71df50SFrank Blaschka static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32); 624a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *); 634a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *); 640da9581dSEinar Lueck static void qeth_free_qdio_buffers(struct qeth_card *); 65b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *queue, 66b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 67b3332930SFrank Blaschka enum iucv_tx_notify notification); 68b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); 690da9581dSEinar Lueck static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, 700da9581dSEinar Lueck struct qeth_qdio_out_buffer *buf, 710da9581dSEinar Lueck enum qeth_qdio_buffer_states newbufstate); 7272861ae7SEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); 734a71df50SFrank Blaschka 74b4d72c08SEugene Crosser struct workqueue_struct *qeth_wq; 75c044dc21SEugene Crosser EXPORT_SYMBOL_GPL(qeth_wq); 760f54761dSStefan Raspl 77511c2445SEugene Crosser int qeth_card_hw_is_reachable(struct qeth_card *card) 78511c2445SEugene Crosser { 79511c2445SEugene Crosser return (card->state == CARD_STATE_SOFTSETUP) || 80511c2445SEugene Crosser (card->state == CARD_STATE_UP); 81511c2445SEugene Crosser } 82511c2445SEugene Crosser EXPORT_SYMBOL_GPL(qeth_card_hw_is_reachable); 83511c2445SEugene Crosser 840f54761dSStefan Raspl static void qeth_close_dev_handler(struct work_struct *work) 850f54761dSStefan Raspl { 860f54761dSStefan Raspl struct qeth_card *card; 870f54761dSStefan Raspl 880f54761dSStefan Raspl card = container_of(work, struct qeth_card, close_dev_work); 890f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevhdl"); 900f54761dSStefan Raspl rtnl_lock(); 910f54761dSStefan Raspl dev_close(card->dev); 920f54761dSStefan Raspl rtnl_unlock(); 930f54761dSStefan Raspl ccwgroup_set_offline(card->gdev); 940f54761dSStefan Raspl } 950f54761dSStefan Raspl 960f54761dSStefan Raspl void qeth_close_dev(struct qeth_card *card) 970f54761dSStefan Raspl { 980f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevsubm"); 990f54761dSStefan Raspl queue_work(qeth_wq, &card->close_dev_work); 1000f54761dSStefan Raspl } 1010f54761dSStefan Raspl EXPORT_SYMBOL_GPL(qeth_close_dev); 1020f54761dSStefan Raspl 103cef6ff22SJulian Wiedmann static const char *qeth_get_cardname(struct qeth_card *card) 1044a71df50SFrank Blaschka { 1054a71df50SFrank Blaschka if (card->info.guestlan) { 1064a71df50SFrank Blaschka switch (card->info.type) { 1075113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1087096b187SStefan Raspl return " Virtual NIC QDIO"; 1094a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1107096b187SStefan Raspl return " Virtual NIC Hiper"; 1115113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1127096b187SStefan Raspl return " Virtual NIC QDIO - OSM"; 1135113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1147096b187SStefan Raspl return " Virtual NIC QDIO - OSX"; 1154a71df50SFrank Blaschka default: 1164a71df50SFrank Blaschka return " unknown"; 1174a71df50SFrank Blaschka } 1184a71df50SFrank Blaschka } else { 1194a71df50SFrank Blaschka switch (card->info.type) { 1205113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1214a71df50SFrank Blaschka return " OSD Express"; 1224a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1234a71df50SFrank Blaschka return " HiperSockets"; 1244a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1254a71df50SFrank Blaschka return " OSN QDIO"; 1265113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1275113fec0SUrsula Braun return " OSM QDIO"; 1285113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1295113fec0SUrsula Braun return " OSX QDIO"; 1304a71df50SFrank Blaschka default: 1314a71df50SFrank Blaschka return " unknown"; 1324a71df50SFrank Blaschka } 1334a71df50SFrank Blaschka } 1344a71df50SFrank Blaschka return " n/a"; 1354a71df50SFrank Blaschka } 1364a71df50SFrank Blaschka 1374a71df50SFrank Blaschka /* max length to be returned: 14 */ 1384a71df50SFrank Blaschka const char *qeth_get_cardname_short(struct qeth_card *card) 1394a71df50SFrank Blaschka { 1404a71df50SFrank Blaschka if (card->info.guestlan) { 1414a71df50SFrank Blaschka switch (card->info.type) { 1425113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1437096b187SStefan Raspl return "Virt.NIC QDIO"; 1444a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1457096b187SStefan Raspl return "Virt.NIC Hiper"; 1465113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1477096b187SStefan Raspl return "Virt.NIC OSM"; 1485113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1497096b187SStefan Raspl return "Virt.NIC OSX"; 1504a71df50SFrank Blaschka default: 1514a71df50SFrank Blaschka return "unknown"; 1524a71df50SFrank Blaschka } 1534a71df50SFrank Blaschka } else { 1544a71df50SFrank Blaschka switch (card->info.type) { 1555113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1564a71df50SFrank Blaschka switch (card->info.link_type) { 1574a71df50SFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 1584a71df50SFrank Blaschka return "OSD_100"; 1594a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 1604a71df50SFrank Blaschka return "HSTR"; 1614a71df50SFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 1624a71df50SFrank Blaschka return "OSD_1000"; 1634a71df50SFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 1644a71df50SFrank Blaschka return "OSD_10GIG"; 1654a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 1664a71df50SFrank Blaschka return "OSD_FE_LANE"; 1674a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_TR: 1684a71df50SFrank Blaschka return "OSD_TR_LANE"; 1694a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 1704a71df50SFrank Blaschka return "OSD_GbE_LANE"; 1714a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE: 1724a71df50SFrank Blaschka return "OSD_ATM_LANE"; 1734a71df50SFrank Blaschka default: 1744a71df50SFrank Blaschka return "OSD_Express"; 1754a71df50SFrank Blaschka } 1764a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1774a71df50SFrank Blaschka return "HiperSockets"; 1784a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1794a71df50SFrank Blaschka return "OSN"; 1805113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1815113fec0SUrsula Braun return "OSM_1000"; 1825113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1835113fec0SUrsula Braun return "OSX_10GIG"; 1844a71df50SFrank Blaschka default: 1854a71df50SFrank Blaschka return "unknown"; 1864a71df50SFrank Blaschka } 1874a71df50SFrank Blaschka } 1884a71df50SFrank Blaschka return "n/a"; 1894a71df50SFrank Blaschka } 1904a71df50SFrank Blaschka 19165d8013cSStefan Raspl void qeth_set_recovery_task(struct qeth_card *card) 19265d8013cSStefan Raspl { 19365d8013cSStefan Raspl card->recovery_task = current; 19465d8013cSStefan Raspl } 19565d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_set_recovery_task); 19665d8013cSStefan Raspl 19765d8013cSStefan Raspl void qeth_clear_recovery_task(struct qeth_card *card) 19865d8013cSStefan Raspl { 19965d8013cSStefan Raspl card->recovery_task = NULL; 20065d8013cSStefan Raspl } 20165d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_clear_recovery_task); 20265d8013cSStefan Raspl 20365d8013cSStefan Raspl static bool qeth_is_recovery_task(const struct qeth_card *card) 20465d8013cSStefan Raspl { 20565d8013cSStefan Raspl return card->recovery_task == current; 20665d8013cSStefan Raspl } 20765d8013cSStefan Raspl 2084a71df50SFrank Blaschka void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads, 2094a71df50SFrank Blaschka int clear_start_mask) 2104a71df50SFrank Blaschka { 2114a71df50SFrank Blaschka unsigned long flags; 2124a71df50SFrank Blaschka 2134a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2144a71df50SFrank Blaschka card->thread_allowed_mask = threads; 2154a71df50SFrank Blaschka if (clear_start_mask) 2164a71df50SFrank Blaschka card->thread_start_mask &= threads; 2174a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2184a71df50SFrank Blaschka wake_up(&card->wait_q); 2194a71df50SFrank Blaschka } 2204a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_set_allowed_threads); 2214a71df50SFrank Blaschka 2224a71df50SFrank Blaschka int qeth_threads_running(struct qeth_card *card, unsigned long threads) 2234a71df50SFrank Blaschka { 2244a71df50SFrank Blaschka unsigned long flags; 2254a71df50SFrank Blaschka int rc = 0; 2264a71df50SFrank Blaschka 2274a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2284a71df50SFrank Blaschka rc = (card->thread_running_mask & threads); 2294a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2304a71df50SFrank Blaschka return rc; 2314a71df50SFrank Blaschka } 2324a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_threads_running); 2334a71df50SFrank Blaschka 2344a71df50SFrank Blaschka int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) 2354a71df50SFrank Blaschka { 23665d8013cSStefan Raspl if (qeth_is_recovery_task(card)) 23765d8013cSStefan Raspl return 0; 2384a71df50SFrank Blaschka return wait_event_interruptible(card->wait_q, 2394a71df50SFrank Blaschka qeth_threads_running(card, threads) == 0); 2404a71df50SFrank Blaschka } 2414a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_threads); 2424a71df50SFrank Blaschka 2434a71df50SFrank Blaschka void qeth_clear_working_pool_list(struct qeth_card *card) 2444a71df50SFrank Blaschka { 2454a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 2464a71df50SFrank Blaschka 247847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "clwrklst"); 2484a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 2494a71df50SFrank Blaschka &card->qdio.in_buf_pool.entry_list, list){ 2504a71df50SFrank Blaschka list_del(&pool_entry->list); 2514a71df50SFrank Blaschka } 2524a71df50SFrank Blaschka } 2534a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_working_pool_list); 2544a71df50SFrank Blaschka 2554a71df50SFrank Blaschka static int qeth_alloc_buffer_pool(struct qeth_card *card) 2564a71df50SFrank Blaschka { 2574a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 2584a71df50SFrank Blaschka void *ptr; 2594a71df50SFrank Blaschka int i, j; 2604a71df50SFrank Blaschka 261847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "alocpool"); 2624a71df50SFrank Blaschka for (i = 0; i < card->qdio.init_pool.buf_count; ++i) { 263b3332930SFrank Blaschka pool_entry = kzalloc(sizeof(*pool_entry), GFP_KERNEL); 2644a71df50SFrank Blaschka if (!pool_entry) { 2654a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2664a71df50SFrank Blaschka return -ENOMEM; 2674a71df50SFrank Blaschka } 2684a71df50SFrank Blaschka for (j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j) { 269508b3c4fSUrsula Braun ptr = (void *) __get_free_page(GFP_KERNEL); 2704a71df50SFrank Blaschka if (!ptr) { 2714a71df50SFrank Blaschka while (j > 0) 2724a71df50SFrank Blaschka free_page((unsigned long) 2734a71df50SFrank Blaschka pool_entry->elements[--j]); 2744a71df50SFrank Blaschka kfree(pool_entry); 2754a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2764a71df50SFrank Blaschka return -ENOMEM; 2774a71df50SFrank Blaschka } 2784a71df50SFrank Blaschka pool_entry->elements[j] = ptr; 2794a71df50SFrank Blaschka } 2804a71df50SFrank Blaschka list_add(&pool_entry->init_list, 2814a71df50SFrank Blaschka &card->qdio.init_pool.entry_list); 2824a71df50SFrank Blaschka } 2834a71df50SFrank Blaschka return 0; 2844a71df50SFrank Blaschka } 2854a71df50SFrank Blaschka 2864a71df50SFrank Blaschka int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt) 2874a71df50SFrank Blaschka { 288847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "realcbp"); 2894a71df50SFrank Blaschka 2904a71df50SFrank Blaschka if ((card->state != CARD_STATE_DOWN) && 2914a71df50SFrank Blaschka (card->state != CARD_STATE_RECOVER)) 2924a71df50SFrank Blaschka return -EPERM; 2934a71df50SFrank Blaschka 2944a71df50SFrank Blaschka /* TODO: steel/add buffers from/to a running card's buffer pool (?) */ 2954a71df50SFrank Blaschka qeth_clear_working_pool_list(card); 2964a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2974a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = bufcnt; 2984a71df50SFrank Blaschka card->qdio.init_pool.buf_count = bufcnt; 2994a71df50SFrank Blaschka return qeth_alloc_buffer_pool(card); 3004a71df50SFrank Blaschka } 30176b11f8eSUrsula Braun EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool); 3024a71df50SFrank Blaschka 3034601ba6cSSebastian Ott static void qeth_free_qdio_queue(struct qeth_qdio_q *q) 3044601ba6cSSebastian Ott { 3056d284bdeSSebastian Ott if (!q) 3066d284bdeSSebastian Ott return; 3076d284bdeSSebastian Ott 3086d284bdeSSebastian Ott qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); 3094601ba6cSSebastian Ott kfree(q); 3104601ba6cSSebastian Ott } 3114601ba6cSSebastian Ott 3124601ba6cSSebastian Ott static struct qeth_qdio_q *qeth_alloc_qdio_queue(void) 3134601ba6cSSebastian Ott { 3144601ba6cSSebastian Ott struct qeth_qdio_q *q = kzalloc(sizeof(*q), GFP_KERNEL); 3154601ba6cSSebastian Ott int i; 3164601ba6cSSebastian Ott 3174601ba6cSSebastian Ott if (!q) 3184601ba6cSSebastian Ott return NULL; 3194601ba6cSSebastian Ott 3206d284bdeSSebastian Ott if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) { 3216d284bdeSSebastian Ott kfree(q); 3226d284bdeSSebastian Ott return NULL; 3236d284bdeSSebastian Ott } 3246d284bdeSSebastian Ott 3254601ba6cSSebastian Ott for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) 3266d284bdeSSebastian Ott q->bufs[i].buffer = q->qdio_bufs[i]; 3274601ba6cSSebastian Ott 3284601ba6cSSebastian Ott QETH_DBF_HEX(SETUP, 2, &q, sizeof(void *)); 3294601ba6cSSebastian Ott return q; 3304601ba6cSSebastian Ott } 3314601ba6cSSebastian Ott 332cef6ff22SJulian Wiedmann static int qeth_cq_init(struct qeth_card *card) 3330da9581dSEinar Lueck { 3340da9581dSEinar Lueck int rc; 3350da9581dSEinar Lueck 3360da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 3370da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqinit"); 3386d284bdeSSebastian Ott qdio_reset_buffers(card->qdio.c_q->qdio_bufs, 3396d284bdeSSebastian Ott QDIO_MAX_BUFFERS_PER_Q); 3400da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = 127; 3410da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 3420da9581dSEinar Lueck card->qdio.no_in_queues - 1, 0, 3430da9581dSEinar Lueck 127); 3440da9581dSEinar Lueck if (rc) { 3450da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 3460da9581dSEinar Lueck goto out; 3470da9581dSEinar Lueck } 3480da9581dSEinar Lueck } 3490da9581dSEinar Lueck rc = 0; 3500da9581dSEinar Lueck out: 3510da9581dSEinar Lueck return rc; 3520da9581dSEinar Lueck } 3530da9581dSEinar Lueck 354cef6ff22SJulian Wiedmann static int qeth_alloc_cq(struct qeth_card *card) 3550da9581dSEinar Lueck { 3560da9581dSEinar Lueck int rc; 3570da9581dSEinar Lueck 3580da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 3590da9581dSEinar Lueck int i; 3600da9581dSEinar Lueck struct qdio_outbuf_state *outbuf_states; 3610da9581dSEinar Lueck 3620da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqon"); 3634601ba6cSSebastian Ott card->qdio.c_q = qeth_alloc_qdio_queue(); 3640da9581dSEinar Lueck if (!card->qdio.c_q) { 3650da9581dSEinar Lueck rc = -1; 3660da9581dSEinar Lueck goto kmsg_out; 3670da9581dSEinar Lueck } 3680da9581dSEinar Lueck card->qdio.no_in_queues = 2; 3694a912f98SZhang Yanfei card->qdio.out_bufstates = 3700da9581dSEinar Lueck kzalloc(card->qdio.no_out_queues * 3710da9581dSEinar Lueck QDIO_MAX_BUFFERS_PER_Q * 3720da9581dSEinar Lueck sizeof(struct qdio_outbuf_state), GFP_KERNEL); 3730da9581dSEinar Lueck outbuf_states = card->qdio.out_bufstates; 3740da9581dSEinar Lueck if (outbuf_states == NULL) { 3750da9581dSEinar Lueck rc = -1; 3760da9581dSEinar Lueck goto free_cq_out; 3770da9581dSEinar Lueck } 3780da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 3790da9581dSEinar Lueck card->qdio.out_qs[i]->bufstates = outbuf_states; 3800da9581dSEinar Lueck outbuf_states += QDIO_MAX_BUFFERS_PER_Q; 3810da9581dSEinar Lueck } 3820da9581dSEinar Lueck } else { 3830da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "nocq"); 3840da9581dSEinar Lueck card->qdio.c_q = NULL; 3850da9581dSEinar Lueck card->qdio.no_in_queues = 1; 3860da9581dSEinar Lueck } 3870da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "iqc%d", card->qdio.no_in_queues); 3880da9581dSEinar Lueck rc = 0; 3890da9581dSEinar Lueck out: 3900da9581dSEinar Lueck return rc; 3910da9581dSEinar Lueck free_cq_out: 3924601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.c_q); 3930da9581dSEinar Lueck card->qdio.c_q = NULL; 3940da9581dSEinar Lueck kmsg_out: 3950da9581dSEinar Lueck dev_err(&card->gdev->dev, "Failed to create completion queue\n"); 3960da9581dSEinar Lueck goto out; 3970da9581dSEinar Lueck } 3980da9581dSEinar Lueck 399cef6ff22SJulian Wiedmann static void qeth_free_cq(struct qeth_card *card) 4000da9581dSEinar Lueck { 4010da9581dSEinar Lueck if (card->qdio.c_q) { 4020da9581dSEinar Lueck --card->qdio.no_in_queues; 4034601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.c_q); 4040da9581dSEinar Lueck card->qdio.c_q = NULL; 4050da9581dSEinar Lueck } 4060da9581dSEinar Lueck kfree(card->qdio.out_bufstates); 4070da9581dSEinar Lueck card->qdio.out_bufstates = NULL; 4080da9581dSEinar Lueck } 4090da9581dSEinar Lueck 410cef6ff22SJulian Wiedmann static enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, 411cef6ff22SJulian Wiedmann int delayed) 412cef6ff22SJulian Wiedmann { 413b3332930SFrank Blaschka enum iucv_tx_notify n; 414b3332930SFrank Blaschka 415b3332930SFrank Blaschka switch (sbalf15) { 416b3332930SFrank Blaschka case 0: 417b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK; 418b3332930SFrank Blaschka break; 419b3332930SFrank Blaschka case 4: 420b3332930SFrank Blaschka case 16: 421b3332930SFrank Blaschka case 17: 422b3332930SFrank Blaschka case 18: 423b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE : 424b3332930SFrank Blaschka TX_NOTIFY_UNREACHABLE; 425b3332930SFrank Blaschka break; 426b3332930SFrank Blaschka default: 427b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR : 428b3332930SFrank Blaschka TX_NOTIFY_GENERALERROR; 429b3332930SFrank Blaschka break; 430b3332930SFrank Blaschka } 431b3332930SFrank Blaschka 432b3332930SFrank Blaschka return n; 433b3332930SFrank Blaschka } 434b3332930SFrank Blaschka 435cef6ff22SJulian Wiedmann static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx, 436cef6ff22SJulian Wiedmann int forced_cleanup) 4370da9581dSEinar Lueck { 43872861ae7SEinar Lueck if (q->card->options.cq != QETH_CQ_ENABLED) 43972861ae7SEinar Lueck return; 44072861ae7SEinar Lueck 4410da9581dSEinar Lueck if (q->bufs[bidx]->next_pending != NULL) { 4420da9581dSEinar Lueck struct qeth_qdio_out_buffer *head = q->bufs[bidx]; 4430da9581dSEinar Lueck struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending; 4440da9581dSEinar Lueck 4450da9581dSEinar Lueck while (c) { 4460da9581dSEinar Lueck if (forced_cleanup || 4470da9581dSEinar Lueck atomic_read(&c->state) == 4480da9581dSEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED) { 4490da9581dSEinar Lueck struct qeth_qdio_out_buffer *f = c; 4500da9581dSEinar Lueck QETH_CARD_TEXT(f->q->card, 5, "fp"); 4510da9581dSEinar Lueck QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f); 452b3332930SFrank Blaschka /* release here to avoid interleaving between 453b3332930SFrank Blaschka outbound tasklet and inbound tasklet 454b3332930SFrank Blaschka regarding notifications and lifecycle */ 455b3332930SFrank Blaschka qeth_release_skbs(c); 456b3332930SFrank Blaschka 4570da9581dSEinar Lueck c = f->next_pending; 45818af5c17SStefan Raspl WARN_ON_ONCE(head->next_pending != f); 4590da9581dSEinar Lueck head->next_pending = c; 4600da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, f); 4610da9581dSEinar Lueck } else { 4620da9581dSEinar Lueck head = c; 4630da9581dSEinar Lueck c = c->next_pending; 4640da9581dSEinar Lueck } 4650da9581dSEinar Lueck 4660da9581dSEinar Lueck } 4670da9581dSEinar Lueck } 46872861ae7SEinar Lueck if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == 46972861ae7SEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED)) { 47072861ae7SEinar Lueck /* for recovery situations */ 47172861ae7SEinar Lueck q->bufs[bidx]->aob = q->bufstates[bidx].aob; 47272861ae7SEinar Lueck qeth_init_qdio_out_buf(q, bidx); 47372861ae7SEinar Lueck QETH_CARD_TEXT(q->card, 2, "clprecov"); 47472861ae7SEinar Lueck } 4750da9581dSEinar Lueck } 4760da9581dSEinar Lueck 4770da9581dSEinar Lueck 478cef6ff22SJulian Wiedmann static void qeth_qdio_handle_aob(struct qeth_card *card, 479cef6ff22SJulian Wiedmann unsigned long phys_aob_addr) 480cef6ff22SJulian Wiedmann { 4810da9581dSEinar Lueck struct qaob *aob; 4820da9581dSEinar Lueck struct qeth_qdio_out_buffer *buffer; 483b3332930SFrank Blaschka enum iucv_tx_notify notification; 4840da9581dSEinar Lueck 4850da9581dSEinar Lueck aob = (struct qaob *) phys_to_virt(phys_aob_addr); 4860da9581dSEinar Lueck QETH_CARD_TEXT(card, 5, "haob"); 4870da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr); 4880da9581dSEinar Lueck buffer = (struct qeth_qdio_out_buffer *) aob->user1; 4890da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); 4900da9581dSEinar Lueck 491b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, 492b3332930SFrank Blaschka QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) { 493b3332930SFrank Blaschka notification = TX_NOTIFY_OK; 494b3332930SFrank Blaschka } else { 49518af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buffer->state) != 49618af5c17SStefan Raspl QETH_QDIO_BUF_PENDING); 497b3332930SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); 498b3332930SFrank Blaschka notification = TX_NOTIFY_DELAYED_OK; 499b3332930SFrank Blaschka } 500b3332930SFrank Blaschka 501b3332930SFrank Blaschka if (aob->aorc != 0) { 502b3332930SFrank Blaschka QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc); 503b3332930SFrank Blaschka notification = qeth_compute_cq_notification(aob->aorc, 1); 504b3332930SFrank Blaschka } 505b3332930SFrank Blaschka qeth_notify_skbs(buffer->q, buffer, notification); 506b3332930SFrank Blaschka 5070da9581dSEinar Lueck buffer->aob = NULL; 5080da9581dSEinar Lueck qeth_clear_output_buffer(buffer->q, buffer, 5090da9581dSEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED); 51072861ae7SEinar Lueck 5110da9581dSEinar Lueck /* from here on: do not touch buffer anymore */ 5120da9581dSEinar Lueck qdio_release_aob(aob); 5130da9581dSEinar Lueck } 5140da9581dSEinar Lueck 5150da9581dSEinar Lueck static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue) 5160da9581dSEinar Lueck { 5170da9581dSEinar Lueck return card->options.cq == QETH_CQ_ENABLED && 5180da9581dSEinar Lueck card->qdio.c_q != NULL && 5190da9581dSEinar Lueck queue != 0 && 5200da9581dSEinar Lueck queue == card->qdio.no_in_queues - 1; 5210da9581dSEinar Lueck } 5220da9581dSEinar Lueck 5230da9581dSEinar Lueck 5244a71df50SFrank Blaschka static int qeth_issue_next_read(struct qeth_card *card) 5254a71df50SFrank Blaschka { 5264a71df50SFrank Blaschka int rc; 5274a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 5284a71df50SFrank Blaschka 529847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "issnxrd"); 5304a71df50SFrank Blaschka if (card->read.state != CH_STATE_UP) 5314a71df50SFrank Blaschka return -EIO; 5324a71df50SFrank Blaschka iob = qeth_get_buffer(&card->read); 5334a71df50SFrank Blaschka if (!iob) { 53474eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver " 53574eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 53674eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob " 53774eacdb9SFrank Blaschka "available\n", dev_name(&card->gdev->dev)); 5384a71df50SFrank Blaschka return -ENOMEM; 5394a71df50SFrank Blaschka } 5404a71df50SFrank Blaschka qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE); 541847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 5424a71df50SFrank Blaschka rc = ccw_device_start(card->read.ccwdev, &card->read.ccw, 5434a71df50SFrank Blaschka (addr_t) iob, 0, 0); 5444a71df50SFrank Blaschka if (rc) { 54574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! " 54674eacdb9SFrank Blaschka "rc=%i\n", dev_name(&card->gdev->dev), rc); 5474a71df50SFrank Blaschka atomic_set(&card->read.irq_pending, 0); 548908abbb5SUrsula Braun card->read_or_write_problem = 1; 5494a71df50SFrank Blaschka qeth_schedule_recovery(card); 5504a71df50SFrank Blaschka wake_up(&card->wait_q); 5514a71df50SFrank Blaschka } 5524a71df50SFrank Blaschka return rc; 5534a71df50SFrank Blaschka } 5544a71df50SFrank Blaschka 5554a71df50SFrank Blaschka static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card) 5564a71df50SFrank Blaschka { 5574a71df50SFrank Blaschka struct qeth_reply *reply; 5584a71df50SFrank Blaschka 5594a71df50SFrank Blaschka reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC); 5604a71df50SFrank Blaschka if (reply) { 5614a71df50SFrank Blaschka atomic_set(&reply->refcnt, 1); 5624a71df50SFrank Blaschka atomic_set(&reply->received, 0); 5634a71df50SFrank Blaschka reply->card = card; 5646531084cSPeter Senna Tschudin } 5654a71df50SFrank Blaschka return reply; 5664a71df50SFrank Blaschka } 5674a71df50SFrank Blaschka 5684a71df50SFrank Blaschka static void qeth_get_reply(struct qeth_reply *reply) 5694a71df50SFrank Blaschka { 5704a71df50SFrank Blaschka WARN_ON(atomic_read(&reply->refcnt) <= 0); 5714a71df50SFrank Blaschka atomic_inc(&reply->refcnt); 5724a71df50SFrank Blaschka } 5734a71df50SFrank Blaschka 5744a71df50SFrank Blaschka static void qeth_put_reply(struct qeth_reply *reply) 5754a71df50SFrank Blaschka { 5764a71df50SFrank Blaschka WARN_ON(atomic_read(&reply->refcnt) <= 0); 5774a71df50SFrank Blaschka if (atomic_dec_and_test(&reply->refcnt)) 5784a71df50SFrank Blaschka kfree(reply); 5794a71df50SFrank Blaschka } 5804a71df50SFrank Blaschka 581d11ba0c4SPeter Tiedemann static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, 5824a71df50SFrank Blaschka struct qeth_card *card) 5834a71df50SFrank Blaschka { 5844a71df50SFrank Blaschka char *ipa_name; 585d11ba0c4SPeter Tiedemann int com = cmd->hdr.command; 5864a71df50SFrank Blaschka ipa_name = qeth_get_ipa_cmd_name(com); 587d11ba0c4SPeter Tiedemann if (rc) 58870919e23SUrsula Braun QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s/%s returned " 58970919e23SUrsula Braun "x%X \"%s\"\n", 59070919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 59170919e23SUrsula Braun QETH_CARD_IFNAME(card), rc, 59270919e23SUrsula Braun qeth_get_ipa_msg(rc)); 593d11ba0c4SPeter Tiedemann else 59470919e23SUrsula Braun QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s/%s succeeded\n", 59570919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 59670919e23SUrsula Braun QETH_CARD_IFNAME(card)); 5974a71df50SFrank Blaschka } 5984a71df50SFrank Blaschka 5994a71df50SFrank Blaschka static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, 6004a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 6014a71df50SFrank Blaschka { 6024a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd = NULL; 6034a71df50SFrank Blaschka 604847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "chkipad"); 6054a71df50SFrank Blaschka if (IS_IPA(iob->data)) { 6064a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data); 6074a71df50SFrank Blaschka if (IS_IPA_REPLY(cmd)) { 60876b11f8eSUrsula Braun if (cmd->hdr.command != IPA_CMD_SETCCID && 60976b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_DELCCID && 61076b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_MODCCID && 61176b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_SET_DIAG_ASS) 612d11ba0c4SPeter Tiedemann qeth_issue_ipa_msg(cmd, 613d11ba0c4SPeter Tiedemann cmd->hdr.return_code, card); 6144a71df50SFrank Blaschka return cmd; 6154a71df50SFrank Blaschka } else { 6164a71df50SFrank Blaschka switch (cmd->hdr.command) { 6174a71df50SFrank Blaschka case IPA_CMD_STOPLAN: 6180f54761dSStefan Raspl if (cmd->hdr.return_code == 6190f54761dSStefan Raspl IPA_RC_VEPA_TO_VEB_TRANSITION) { 6200f54761dSStefan Raspl dev_err(&card->gdev->dev, 6210f54761dSStefan Raspl "Interface %s is down because the " 6220f54761dSStefan Raspl "adjacent port is no longer in " 6230f54761dSStefan Raspl "reflective relay mode\n", 6240f54761dSStefan Raspl QETH_CARD_IFNAME(card)); 6250f54761dSStefan Raspl qeth_close_dev(card); 6260f54761dSStefan Raspl } else { 62774eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, 62874eacdb9SFrank Blaschka "The link for interface %s on CHPID" 62974eacdb9SFrank Blaschka " 0x%X failed\n", 6304a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 6314a71df50SFrank Blaschka card->info.chpid); 6320f54761dSStefan Raspl qeth_issue_ipa_msg(cmd, 6330f54761dSStefan Raspl cmd->hdr.return_code, card); 6340f54761dSStefan Raspl } 6354a71df50SFrank Blaschka card->lan_online = 0; 6364a71df50SFrank Blaschka if (card->dev && netif_carrier_ok(card->dev)) 6374a71df50SFrank Blaschka netif_carrier_off(card->dev); 6384a71df50SFrank Blaschka return NULL; 6394a71df50SFrank Blaschka case IPA_CMD_STARTLAN: 64074eacdb9SFrank Blaschka dev_info(&card->gdev->dev, 64174eacdb9SFrank Blaschka "The link for %s on CHPID 0x%X has" 64274eacdb9SFrank Blaschka " been restored\n", 6434a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 6444a71df50SFrank Blaschka card->info.chpid); 6454a71df50SFrank Blaschka netif_carrier_on(card->dev); 646922dc062SUrsula Braun card->lan_online = 1; 6471da74b1cSFrank Blaschka if (card->info.hwtrap) 6481da74b1cSFrank Blaschka card->info.hwtrap = 2; 6494a71df50SFrank Blaschka qeth_schedule_recovery(card); 6504a71df50SFrank Blaschka return NULL; 6519c23f4daSEugene Crosser case IPA_CMD_SETBRIDGEPORT_IQD: 6529c23f4daSEugene Crosser case IPA_CMD_SETBRIDGEPORT_OSA: 6539f48b9dbSEugene Crosser case IPA_CMD_ADDRESS_CHANGE_NOTIF: 654c044dc21SEugene Crosser if (card->discipline->control_event_handler 655c044dc21SEugene Crosser (card, cmd)) 656c044dc21SEugene Crosser return cmd; 657c044dc21SEugene Crosser else 6589f48b9dbSEugene Crosser return NULL; 6594a71df50SFrank Blaschka case IPA_CMD_MODCCID: 6604a71df50SFrank Blaschka return cmd; 6614a71df50SFrank Blaschka case IPA_CMD_REGISTER_LOCAL_ADDR: 662847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "irla"); 6634a71df50SFrank Blaschka break; 6644a71df50SFrank Blaschka case IPA_CMD_UNREGISTER_LOCAL_ADDR: 665847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "urla"); 6664a71df50SFrank Blaschka break; 6674a71df50SFrank Blaschka default: 668c4cef07cSFrank Blaschka QETH_DBF_MESSAGE(2, "Received data is IPA " 6694a71df50SFrank Blaschka "but not a reply!\n"); 6704a71df50SFrank Blaschka break; 6714a71df50SFrank Blaschka } 6724a71df50SFrank Blaschka } 6734a71df50SFrank Blaschka } 6744a71df50SFrank Blaschka return cmd; 6754a71df50SFrank Blaschka } 6764a71df50SFrank Blaschka 6774a71df50SFrank Blaschka void qeth_clear_ipacmd_list(struct qeth_card *card) 6784a71df50SFrank Blaschka { 6794a71df50SFrank Blaschka struct qeth_reply *reply, *r; 6804a71df50SFrank Blaschka unsigned long flags; 6814a71df50SFrank Blaschka 682847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "clipalst"); 6834a71df50SFrank Blaschka 6844a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 6854a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 6864a71df50SFrank Blaschka qeth_get_reply(reply); 6874a71df50SFrank Blaschka reply->rc = -EIO; 6884a71df50SFrank Blaschka atomic_inc(&reply->received); 6894a71df50SFrank Blaschka list_del_init(&reply->list); 6904a71df50SFrank Blaschka wake_up(&reply->wait_q); 6914a71df50SFrank Blaschka qeth_put_reply(reply); 6924a71df50SFrank Blaschka } 6934a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 694908abbb5SUrsula Braun atomic_set(&card->write.irq_pending, 0); 6954a71df50SFrank Blaschka } 6964a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list); 6974a71df50SFrank Blaschka 6985113fec0SUrsula Braun static int qeth_check_idx_response(struct qeth_card *card, 6995113fec0SUrsula Braun unsigned char *buffer) 7004a71df50SFrank Blaschka { 7014a71df50SFrank Blaschka if (!buffer) 7024a71df50SFrank Blaschka return 0; 7034a71df50SFrank Blaschka 704d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN); 7054a71df50SFrank Blaschka if ((buffer[2] & 0xc0) == 0xc0) { 70674eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "received an IDX TERMINATE " 7074a71df50SFrank Blaschka "with cause code 0x%02x%s\n", 7084a71df50SFrank Blaschka buffer[4], 7094a71df50SFrank Blaschka ((buffer[4] == 0x22) ? 7104a71df50SFrank Blaschka " -- try another portname" : "")); 711847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckidxres"); 712847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " idxterm"); 713847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 7145113fec0SUrsula Braun if (buffer[4] == 0xf6) { 7155113fec0SUrsula Braun dev_err(&card->gdev->dev, 7165113fec0SUrsula Braun "The qeth device is not configured " 7175113fec0SUrsula Braun "for the OSI layer required by z/VM\n"); 7185113fec0SUrsula Braun return -EPERM; 7195113fec0SUrsula Braun } 7204a71df50SFrank Blaschka return -EIO; 7214a71df50SFrank Blaschka } 7224a71df50SFrank Blaschka return 0; 7234a71df50SFrank Blaschka } 7244a71df50SFrank Blaschka 725bca51650SThomas Richter static struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev) 726bca51650SThomas Richter { 727bca51650SThomas Richter struct qeth_card *card = dev_get_drvdata(&((struct ccwgroup_device *) 728bca51650SThomas Richter dev_get_drvdata(&cdev->dev))->dev); 729bca51650SThomas Richter return card; 730bca51650SThomas Richter } 731bca51650SThomas Richter 7324a71df50SFrank Blaschka static void qeth_setup_ccw(struct qeth_channel *channel, unsigned char *iob, 7334a71df50SFrank Blaschka __u32 len) 7344a71df50SFrank Blaschka { 7354a71df50SFrank Blaschka struct qeth_card *card; 7364a71df50SFrank Blaschka 7374a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 738847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setupccw"); 7394a71df50SFrank Blaschka if (channel == &card->read) 7404a71df50SFrank Blaschka memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); 7414a71df50SFrank Blaschka else 7424a71df50SFrank Blaschka memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); 7434a71df50SFrank Blaschka channel->ccw.count = len; 7444a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob); 7454a71df50SFrank Blaschka } 7464a71df50SFrank Blaschka 7474a71df50SFrank Blaschka static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel) 7484a71df50SFrank Blaschka { 7494a71df50SFrank Blaschka __u8 index; 7504a71df50SFrank Blaschka 751847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "getbuff"); 7524a71df50SFrank Blaschka index = channel->io_buf_no; 7534a71df50SFrank Blaschka do { 7544a71df50SFrank Blaschka if (channel->iob[index].state == BUF_STATE_FREE) { 7554a71df50SFrank Blaschka channel->iob[index].state = BUF_STATE_LOCKED; 7564a71df50SFrank Blaschka channel->io_buf_no = (channel->io_buf_no + 1) % 7574a71df50SFrank Blaschka QETH_CMD_BUFFER_NO; 7584a71df50SFrank Blaschka memset(channel->iob[index].data, 0, QETH_BUFSIZE); 7594a71df50SFrank Blaschka return channel->iob + index; 7604a71df50SFrank Blaschka } 7614a71df50SFrank Blaschka index = (index + 1) % QETH_CMD_BUFFER_NO; 7624a71df50SFrank Blaschka } while (index != channel->io_buf_no); 7634a71df50SFrank Blaschka 7644a71df50SFrank Blaschka return NULL; 7654a71df50SFrank Blaschka } 7664a71df50SFrank Blaschka 7674a71df50SFrank Blaschka void qeth_release_buffer(struct qeth_channel *channel, 7684a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 7694a71df50SFrank Blaschka { 7704a71df50SFrank Blaschka unsigned long flags; 7714a71df50SFrank Blaschka 772847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "relbuff"); 7734a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7744a71df50SFrank Blaschka memset(iob->data, 0, QETH_BUFSIZE); 7754a71df50SFrank Blaschka iob->state = BUF_STATE_FREE; 7764a71df50SFrank Blaschka iob->callback = qeth_send_control_data_cb; 7774a71df50SFrank Blaschka iob->rc = 0; 7784a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 779039055b9SUrsula Braun wake_up(&channel->wait_q); 7804a71df50SFrank Blaschka } 7814a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_release_buffer); 7824a71df50SFrank Blaschka 7834a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *channel) 7844a71df50SFrank Blaschka { 7854a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer = NULL; 7864a71df50SFrank Blaschka unsigned long flags; 7874a71df50SFrank Blaschka 7884a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7894a71df50SFrank Blaschka buffer = __qeth_get_buffer(channel); 7904a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 7914a71df50SFrank Blaschka return buffer; 7924a71df50SFrank Blaschka } 7934a71df50SFrank Blaschka 7944a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *channel) 7954a71df50SFrank Blaschka { 7964a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer; 7974a71df50SFrank Blaschka wait_event(channel->wait_q, 7984a71df50SFrank Blaschka ((buffer = qeth_get_buffer(channel)) != NULL)); 7994a71df50SFrank Blaschka return buffer; 8004a71df50SFrank Blaschka } 8014a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_buffer); 8024a71df50SFrank Blaschka 8034a71df50SFrank Blaschka void qeth_clear_cmd_buffers(struct qeth_channel *channel) 8044a71df50SFrank Blaschka { 8054a71df50SFrank Blaschka int cnt; 8064a71df50SFrank Blaschka 8074a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 8084a71df50SFrank Blaschka qeth_release_buffer(channel, &channel->iob[cnt]); 8094a71df50SFrank Blaschka channel->buf_no = 0; 8104a71df50SFrank Blaschka channel->io_buf_no = 0; 8114a71df50SFrank Blaschka } 8124a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers); 8134a71df50SFrank Blaschka 8144a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *channel, 8154a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 8164a71df50SFrank Blaschka { 8174a71df50SFrank Blaschka struct qeth_card *card; 8184a71df50SFrank Blaschka struct qeth_reply *reply, *r; 8194a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 8204a71df50SFrank Blaschka unsigned long flags; 8214a71df50SFrank Blaschka int keep_reply; 8225113fec0SUrsula Braun int rc = 0; 8234a71df50SFrank Blaschka 8244a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 825847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sndctlcb"); 8265113fec0SUrsula Braun rc = qeth_check_idx_response(card, iob->data); 8275113fec0SUrsula Braun switch (rc) { 8285113fec0SUrsula Braun case 0: 8295113fec0SUrsula Braun break; 8305113fec0SUrsula Braun case -EIO: 8314a71df50SFrank Blaschka qeth_clear_ipacmd_list(card); 8324a71df50SFrank Blaschka qeth_schedule_recovery(card); 83301fc3e86SUrsula Braun /* fall through */ 8345113fec0SUrsula Braun default: 8354a71df50SFrank Blaschka goto out; 8364a71df50SFrank Blaschka } 8374a71df50SFrank Blaschka 8384a71df50SFrank Blaschka cmd = qeth_check_ipa_data(card, iob); 8394a71df50SFrank Blaschka if ((cmd == NULL) && (card->state != CARD_STATE_DOWN)) 8404a71df50SFrank Blaschka goto out; 8414a71df50SFrank Blaschka /*in case of OSN : check if cmd is set */ 8424a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN && 8434a71df50SFrank Blaschka cmd && 8444a71df50SFrank Blaschka cmd->hdr.command != IPA_CMD_STARTLAN && 8454a71df50SFrank Blaschka card->osn_info.assist_cb != NULL) { 8464a71df50SFrank Blaschka card->osn_info.assist_cb(card->dev, cmd); 8474a71df50SFrank Blaschka goto out; 8484a71df50SFrank Blaschka } 8494a71df50SFrank Blaschka 8504a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8514a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 8524a71df50SFrank Blaschka if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) || 8534a71df50SFrank Blaschka ((cmd) && (reply->seqno == cmd->hdr.seqno))) { 8544a71df50SFrank Blaschka qeth_get_reply(reply); 8554a71df50SFrank Blaschka list_del_init(&reply->list); 8564a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8574a71df50SFrank Blaschka keep_reply = 0; 8584a71df50SFrank Blaschka if (reply->callback != NULL) { 8594a71df50SFrank Blaschka if (cmd) { 8604a71df50SFrank Blaschka reply->offset = (__u16)((char *)cmd - 8614a71df50SFrank Blaschka (char *)iob->data); 8624a71df50SFrank Blaschka keep_reply = reply->callback(card, 8634a71df50SFrank Blaschka reply, 8644a71df50SFrank Blaschka (unsigned long)cmd); 8654a71df50SFrank Blaschka } else 8664a71df50SFrank Blaschka keep_reply = reply->callback(card, 8674a71df50SFrank Blaschka reply, 8684a71df50SFrank Blaschka (unsigned long)iob); 8694a71df50SFrank Blaschka } 8704a71df50SFrank Blaschka if (cmd) 8714a71df50SFrank Blaschka reply->rc = (u16) cmd->hdr.return_code; 8724a71df50SFrank Blaschka else if (iob->rc) 8734a71df50SFrank Blaschka reply->rc = iob->rc; 8744a71df50SFrank Blaschka if (keep_reply) { 8754a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8764a71df50SFrank Blaschka list_add_tail(&reply->list, 8774a71df50SFrank Blaschka &card->cmd_waiter_list); 8784a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8794a71df50SFrank Blaschka } else { 8804a71df50SFrank Blaschka atomic_inc(&reply->received); 8814a71df50SFrank Blaschka wake_up(&reply->wait_q); 8824a71df50SFrank Blaschka } 8834a71df50SFrank Blaschka qeth_put_reply(reply); 8844a71df50SFrank Blaschka goto out; 8854a71df50SFrank Blaschka } 8864a71df50SFrank Blaschka } 8874a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8884a71df50SFrank Blaschka out: 8894a71df50SFrank Blaschka memcpy(&card->seqno.pdu_hdr_ack, 8904a71df50SFrank Blaschka QETH_PDU_HEADER_SEQ_NO(iob->data), 8914a71df50SFrank Blaschka QETH_SEQ_NO_LENGTH); 8924a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 8934a71df50SFrank Blaschka } 8944a71df50SFrank Blaschka 8954a71df50SFrank Blaschka static int qeth_setup_channel(struct qeth_channel *channel) 8964a71df50SFrank Blaschka { 8974a71df50SFrank Blaschka int cnt; 8984a71df50SFrank Blaschka 899d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupch"); 9004a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) { 901ae57b20aSJulia Lawall channel->iob[cnt].data = 902b3332930SFrank Blaschka kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); 9034a71df50SFrank Blaschka if (channel->iob[cnt].data == NULL) 9044a71df50SFrank Blaschka break; 9054a71df50SFrank Blaschka channel->iob[cnt].state = BUF_STATE_FREE; 9064a71df50SFrank Blaschka channel->iob[cnt].channel = channel; 9074a71df50SFrank Blaschka channel->iob[cnt].callback = qeth_send_control_data_cb; 9084a71df50SFrank Blaschka channel->iob[cnt].rc = 0; 9094a71df50SFrank Blaschka } 9104a71df50SFrank Blaschka if (cnt < QETH_CMD_BUFFER_NO) { 9114a71df50SFrank Blaschka while (cnt-- > 0) 9124a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 9134a71df50SFrank Blaschka return -ENOMEM; 9144a71df50SFrank Blaschka } 9154a71df50SFrank Blaschka channel->buf_no = 0; 9164a71df50SFrank Blaschka channel->io_buf_no = 0; 9174a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 9184a71df50SFrank Blaschka spin_lock_init(&channel->iob_lock); 9194a71df50SFrank Blaschka 9204a71df50SFrank Blaschka init_waitqueue_head(&channel->wait_q); 9214a71df50SFrank Blaschka return 0; 9224a71df50SFrank Blaschka } 9234a71df50SFrank Blaschka 9244a71df50SFrank Blaschka static int qeth_set_thread_start_bit(struct qeth_card *card, 9254a71df50SFrank Blaschka unsigned long thread) 9264a71df50SFrank Blaschka { 9274a71df50SFrank Blaschka unsigned long flags; 9284a71df50SFrank Blaschka 9294a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9304a71df50SFrank Blaschka if (!(card->thread_allowed_mask & thread) || 9314a71df50SFrank Blaschka (card->thread_start_mask & thread)) { 9324a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9334a71df50SFrank Blaschka return -EPERM; 9344a71df50SFrank Blaschka } 9354a71df50SFrank Blaschka card->thread_start_mask |= thread; 9364a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9374a71df50SFrank Blaschka return 0; 9384a71df50SFrank Blaschka } 9394a71df50SFrank Blaschka 9404a71df50SFrank Blaschka void qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread) 9414a71df50SFrank Blaschka { 9424a71df50SFrank Blaschka unsigned long flags; 9434a71df50SFrank Blaschka 9444a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9454a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 9464a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9474a71df50SFrank Blaschka wake_up(&card->wait_q); 9484a71df50SFrank Blaschka } 9494a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_start_bit); 9504a71df50SFrank Blaschka 9514a71df50SFrank Blaschka void qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread) 9524a71df50SFrank Blaschka { 9534a71df50SFrank Blaschka unsigned long flags; 9544a71df50SFrank Blaschka 9554a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9564a71df50SFrank Blaschka card->thread_running_mask &= ~thread; 9574a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9584a71df50SFrank Blaschka wake_up(&card->wait_q); 9594a71df50SFrank Blaschka } 9604a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit); 9614a71df50SFrank Blaschka 9624a71df50SFrank Blaschka static int __qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9634a71df50SFrank Blaschka { 9644a71df50SFrank Blaschka unsigned long flags; 9654a71df50SFrank Blaschka int rc = 0; 9664a71df50SFrank Blaschka 9674a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9684a71df50SFrank Blaschka if (card->thread_start_mask & thread) { 9694a71df50SFrank Blaschka if ((card->thread_allowed_mask & thread) && 9704a71df50SFrank Blaschka !(card->thread_running_mask & thread)) { 9714a71df50SFrank Blaschka rc = 1; 9724a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 9734a71df50SFrank Blaschka card->thread_running_mask |= thread; 9744a71df50SFrank Blaschka } else 9754a71df50SFrank Blaschka rc = -EPERM; 9764a71df50SFrank Blaschka } 9774a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9784a71df50SFrank Blaschka return rc; 9794a71df50SFrank Blaschka } 9804a71df50SFrank Blaschka 9814a71df50SFrank Blaschka int qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9824a71df50SFrank Blaschka { 9834a71df50SFrank Blaschka int rc = 0; 9844a71df50SFrank Blaschka 9854a71df50SFrank Blaschka wait_event(card->wait_q, 9864a71df50SFrank Blaschka (rc = __qeth_do_run_thread(card, thread)) >= 0); 9874a71df50SFrank Blaschka return rc; 9884a71df50SFrank Blaschka } 9894a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_run_thread); 9904a71df50SFrank Blaschka 9914a71df50SFrank Blaschka void qeth_schedule_recovery(struct qeth_card *card) 9924a71df50SFrank Blaschka { 993847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "startrec"); 9944a71df50SFrank Blaschka if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0) 9954a71df50SFrank Blaschka schedule_work(&card->kernel_thread_starter); 9964a71df50SFrank Blaschka } 9974a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_schedule_recovery); 9984a71df50SFrank Blaschka 9994a71df50SFrank Blaschka static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb) 10004a71df50SFrank Blaschka { 10014a71df50SFrank Blaschka int dstat, cstat; 10024a71df50SFrank Blaschka char *sense; 1003847a50fdSCarsten Otte struct qeth_card *card; 10044a71df50SFrank Blaschka 10054a71df50SFrank Blaschka sense = (char *) irb->ecw; 100623d805b6SPeter Oberparleiter cstat = irb->scsw.cmd.cstat; 100723d805b6SPeter Oberparleiter dstat = irb->scsw.cmd.dstat; 1008847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 10094a71df50SFrank Blaschka 10104a71df50SFrank Blaschka if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | 10114a71df50SFrank Blaschka SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | 10124a71df50SFrank Blaschka SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) { 1013847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CGENCHK"); 101474eacdb9SFrank Blaschka dev_warn(&cdev->dev, "The qeth device driver " 101574eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 10165113fec0SUrsula Braun QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x\n", 10172a0217d5SKay Sievers dev_name(&cdev->dev), dstat, cstat); 10184a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 10194a71df50SFrank Blaschka 16, 1, irb, 64, 1); 10204a71df50SFrank Blaschka return 1; 10214a71df50SFrank Blaschka } 10224a71df50SFrank Blaschka 10234a71df50SFrank Blaschka if (dstat & DEV_STAT_UNIT_CHECK) { 10244a71df50SFrank Blaschka if (sense[SENSE_RESETTING_EVENT_BYTE] & 10254a71df50SFrank Blaschka SENSE_RESETTING_EVENT_FLAG) { 1026847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "REVIND"); 10274a71df50SFrank Blaschka return 1; 10284a71df50SFrank Blaschka } 10294a71df50SFrank Blaschka if (sense[SENSE_COMMAND_REJECT_BYTE] & 10304a71df50SFrank Blaschka SENSE_COMMAND_REJECT_FLAG) { 1031847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CMDREJi"); 103228a7e4c9SUrsula Braun return 1; 10334a71df50SFrank Blaschka } 10344a71df50SFrank Blaschka if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { 1035847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "AFFE"); 10364a71df50SFrank Blaschka return 1; 10374a71df50SFrank Blaschka } 10384a71df50SFrank Blaschka if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { 1039847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ZEROSEN"); 10404a71df50SFrank Blaschka return 0; 10414a71df50SFrank Blaschka } 1042847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "DGENCHK"); 10434a71df50SFrank Blaschka return 1; 10444a71df50SFrank Blaschka } 10454a71df50SFrank Blaschka return 0; 10464a71df50SFrank Blaschka } 10474a71df50SFrank Blaschka 10484a71df50SFrank Blaschka static long __qeth_check_irb_error(struct ccw_device *cdev, 10494a71df50SFrank Blaschka unsigned long intparm, struct irb *irb) 10504a71df50SFrank Blaschka { 1051847a50fdSCarsten Otte struct qeth_card *card; 1052847a50fdSCarsten Otte 1053847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 1054847a50fdSCarsten Otte 1055e95051ffSRickard Strandqvist if (!card || !IS_ERR(irb)) 10564a71df50SFrank Blaschka return 0; 10574a71df50SFrank Blaschka 10584a71df50SFrank Blaschka switch (PTR_ERR(irb)) { 10594a71df50SFrank Blaschka case -EIO: 106074eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s i/o-error on device\n", 106174eacdb9SFrank Blaschka dev_name(&cdev->dev)); 1062847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1063847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 10644a71df50SFrank Blaschka break; 10654a71df50SFrank Blaschka case -ETIMEDOUT: 106674eacdb9SFrank Blaschka dev_warn(&cdev->dev, "A hardware operation timed out" 106774eacdb9SFrank Blaschka " on the device\n"); 1068847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1069847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT); 10704a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 1071e95051ffSRickard Strandqvist if (card->data.ccwdev == cdev) { 10724a71df50SFrank Blaschka card->data.state = CH_STATE_DOWN; 10734a71df50SFrank Blaschka wake_up(&card->wait_q); 10744a71df50SFrank Blaschka } 10754a71df50SFrank Blaschka } 10764a71df50SFrank Blaschka break; 10774a71df50SFrank Blaschka default: 107874eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n", 107974eacdb9SFrank Blaschka dev_name(&cdev->dev), PTR_ERR(irb)); 1080847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1081847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " rc???"); 10824a71df50SFrank Blaschka } 10834a71df50SFrank Blaschka return PTR_ERR(irb); 10844a71df50SFrank Blaschka } 10854a71df50SFrank Blaschka 10864a71df50SFrank Blaschka static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, 10874a71df50SFrank Blaschka struct irb *irb) 10884a71df50SFrank Blaschka { 10894a71df50SFrank Blaschka int rc; 10904a71df50SFrank Blaschka int cstat, dstat; 10914a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer; 10924a71df50SFrank Blaschka struct qeth_channel *channel; 10934a71df50SFrank Blaschka struct qeth_card *card; 10944a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 10954a71df50SFrank Blaschka __u8 index; 10964a71df50SFrank Blaschka 10974a71df50SFrank Blaschka if (__qeth_check_irb_error(cdev, intparm, irb)) 10984a71df50SFrank Blaschka return; 109923d805b6SPeter Oberparleiter cstat = irb->scsw.cmd.cstat; 110023d805b6SPeter Oberparleiter dstat = irb->scsw.cmd.dstat; 11014a71df50SFrank Blaschka 11024a71df50SFrank Blaschka card = CARD_FROM_CDEV(cdev); 11034a71df50SFrank Blaschka if (!card) 11044a71df50SFrank Blaschka return; 11054a71df50SFrank Blaschka 1106847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "irq"); 1107847a50fdSCarsten Otte 11084a71df50SFrank Blaschka if (card->read.ccwdev == cdev) { 11094a71df50SFrank Blaschka channel = &card->read; 1110847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "read"); 11114a71df50SFrank Blaschka } else if (card->write.ccwdev == cdev) { 11124a71df50SFrank Blaschka channel = &card->write; 1113847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "write"); 11144a71df50SFrank Blaschka } else { 11154a71df50SFrank Blaschka channel = &card->data; 1116847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "data"); 11174a71df50SFrank Blaschka } 11184a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 11194a71df50SFrank Blaschka 112023d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) 11214a71df50SFrank Blaschka channel->state = CH_STATE_STOPPED; 11224a71df50SFrank Blaschka 112323d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) 11244a71df50SFrank Blaschka channel->state = CH_STATE_HALTED; 11254a71df50SFrank Blaschka 11264a71df50SFrank Blaschka /*let's wake up immediately on data channel*/ 11274a71df50SFrank Blaschka if ((channel == &card->data) && (intparm != 0) && 11284a71df50SFrank Blaschka (intparm != QETH_RCD_PARM)) 11294a71df50SFrank Blaschka goto out; 11304a71df50SFrank Blaschka 11314a71df50SFrank Blaschka if (intparm == QETH_CLEAR_CHANNEL_PARM) { 1132847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "clrchpar"); 11334a71df50SFrank Blaschka /* we don't have to handle this further */ 11344a71df50SFrank Blaschka intparm = 0; 11354a71df50SFrank Blaschka } 11364a71df50SFrank Blaschka if (intparm == QETH_HALT_CHANNEL_PARM) { 1137847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hltchpar"); 11384a71df50SFrank Blaschka /* we don't have to handle this further */ 11394a71df50SFrank Blaschka intparm = 0; 11404a71df50SFrank Blaschka } 11414a71df50SFrank Blaschka if ((dstat & DEV_STAT_UNIT_EXCEP) || 11424a71df50SFrank Blaschka (dstat & DEV_STAT_UNIT_CHECK) || 11434a71df50SFrank Blaschka (cstat)) { 11444a71df50SFrank Blaschka if (irb->esw.esw0.erw.cons) { 114574eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, 114674eacdb9SFrank Blaschka "The qeth device driver failed to recover " 114774eacdb9SFrank Blaschka "an error on the device\n"); 114874eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s sense data available. cstat " 114974eacdb9SFrank Blaschka "0x%X dstat 0x%X\n", 115074eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev), cstat, dstat); 11514a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", 11524a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1); 11534a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: sense data ", 11544a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb->ecw, 32, 1); 11554a71df50SFrank Blaschka } 11564a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11574a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 11584a71df50SFrank Blaschka goto out; 11594a71df50SFrank Blaschka } 11604a71df50SFrank Blaschka rc = qeth_get_problem(cdev, irb); 11614a71df50SFrank Blaschka if (rc) { 116228a7e4c9SUrsula Braun qeth_clear_ipacmd_list(card); 11634a71df50SFrank Blaschka qeth_schedule_recovery(card); 11644a71df50SFrank Blaschka goto out; 11654a71df50SFrank Blaschka } 11664a71df50SFrank Blaschka } 11674a71df50SFrank Blaschka 11684a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11694a71df50SFrank Blaschka channel->state = CH_STATE_RCD_DONE; 11704a71df50SFrank Blaschka goto out; 11714a71df50SFrank Blaschka } 11724a71df50SFrank Blaschka if (intparm) { 11734a71df50SFrank Blaschka buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm); 11744a71df50SFrank Blaschka buffer->state = BUF_STATE_PROCESSED; 11754a71df50SFrank Blaschka } 11764a71df50SFrank Blaschka if (channel == &card->data) 11774a71df50SFrank Blaschka return; 11784a71df50SFrank Blaschka if (channel == &card->read && 11794a71df50SFrank Blaschka channel->state == CH_STATE_UP) 11804a71df50SFrank Blaschka qeth_issue_next_read(card); 11814a71df50SFrank Blaschka 11824a71df50SFrank Blaschka iob = channel->iob; 11834a71df50SFrank Blaschka index = channel->buf_no; 11844a71df50SFrank Blaschka while (iob[index].state == BUF_STATE_PROCESSED) { 11854a71df50SFrank Blaschka if (iob[index].callback != NULL) 11864a71df50SFrank Blaschka iob[index].callback(channel, iob + index); 11874a71df50SFrank Blaschka 11884a71df50SFrank Blaschka index = (index + 1) % QETH_CMD_BUFFER_NO; 11894a71df50SFrank Blaschka } 11904a71df50SFrank Blaschka channel->buf_no = index; 11914a71df50SFrank Blaschka out: 11924a71df50SFrank Blaschka wake_up(&card->wait_q); 11934a71df50SFrank Blaschka return; 11944a71df50SFrank Blaschka } 11954a71df50SFrank Blaschka 1196b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *q, 1197b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 1198b3332930SFrank Blaschka enum iucv_tx_notify notification) 1199b3332930SFrank Blaschka { 1200b3332930SFrank Blaschka struct sk_buff *skb; 1201b3332930SFrank Blaschka 1202b3332930SFrank Blaschka if (skb_queue_empty(&buf->skb_list)) 1203b3332930SFrank Blaschka goto out; 1204b3332930SFrank Blaschka skb = skb_peek(&buf->skb_list); 1205b3332930SFrank Blaschka while (skb) { 1206b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); 1207b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); 12086bee4e26SHans Wippel if (be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { 1209b3332930SFrank Blaschka if (skb->sk) { 1210b3332930SFrank Blaschka struct iucv_sock *iucv = iucv_sk(skb->sk); 1211b3332930SFrank Blaschka iucv->sk_txnotify(skb, notification); 1212b3332930SFrank Blaschka } 1213b3332930SFrank Blaschka } 1214b3332930SFrank Blaschka if (skb_queue_is_last(&buf->skb_list, skb)) 1215b3332930SFrank Blaschka skb = NULL; 1216b3332930SFrank Blaschka else 1217b3332930SFrank Blaschka skb = skb_queue_next(&buf->skb_list, skb); 1218b3332930SFrank Blaschka } 1219b3332930SFrank Blaschka out: 1220b3332930SFrank Blaschka return; 1221b3332930SFrank Blaschka } 1222b3332930SFrank Blaschka 1223b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) 1224b3332930SFrank Blaschka { 1225b3332930SFrank Blaschka struct sk_buff *skb; 122672861ae7SEinar Lueck struct iucv_sock *iucv; 122772861ae7SEinar Lueck int notify_general_error = 0; 122872861ae7SEinar Lueck 122972861ae7SEinar Lueck if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING) 123072861ae7SEinar Lueck notify_general_error = 1; 123172861ae7SEinar Lueck 123272861ae7SEinar Lueck /* release may never happen from within CQ tasklet scope */ 123318af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); 1234b3332930SFrank Blaschka 1235b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1236b3332930SFrank Blaschka while (skb) { 1237b3332930SFrank Blaschka QETH_CARD_TEXT(buf->q->card, 5, "skbr"); 1238b3332930SFrank Blaschka QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); 12396bee4e26SHans Wippel if (notify_general_error && 12406bee4e26SHans Wippel be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { 124172861ae7SEinar Lueck if (skb->sk) { 124272861ae7SEinar Lueck iucv = iucv_sk(skb->sk); 124372861ae7SEinar Lueck iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); 124472861ae7SEinar Lueck } 124572861ae7SEinar Lueck } 124663354797SReshetova, Elena refcount_dec(&skb->users); 1247b3332930SFrank Blaschka dev_kfree_skb_any(skb); 1248b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1249b3332930SFrank Blaschka } 1250b3332930SFrank Blaschka } 1251b3332930SFrank Blaschka 1252b67d801fSUrsula Braun static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, 12530da9581dSEinar Lueck struct qeth_qdio_out_buffer *buf, 12540da9581dSEinar Lueck enum qeth_qdio_buffer_states newbufstate) 12554a71df50SFrank Blaschka { 12564a71df50SFrank Blaschka int i; 12574a71df50SFrank Blaschka 12584a71df50SFrank Blaschka /* is PCI flag set on buffer? */ 12593ec90878SJan Glauber if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) 12604a71df50SFrank Blaschka atomic_dec(&queue->set_pci_flags_count); 12614a71df50SFrank Blaschka 1262b3332930SFrank Blaschka if (newbufstate == QETH_QDIO_BUF_EMPTY) { 1263b3332930SFrank Blaschka qeth_release_skbs(buf); 12644a71df50SFrank Blaschka } 12654a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { 1266683d718aSFrank Blaschka if (buf->buffer->element[i].addr && buf->is_header[i]) 1267683d718aSFrank Blaschka kmem_cache_free(qeth_core_header_cache, 1268683d718aSFrank Blaschka buf->buffer->element[i].addr); 1269683d718aSFrank Blaschka buf->is_header[i] = 0; 12704a71df50SFrank Blaschka buf->buffer->element[i].length = 0; 12714a71df50SFrank Blaschka buf->buffer->element[i].addr = NULL; 12723ec90878SJan Glauber buf->buffer->element[i].eflags = 0; 12733ec90878SJan Glauber buf->buffer->element[i].sflags = 0; 12744a71df50SFrank Blaschka } 12753ec90878SJan Glauber buf->buffer->element[15].eflags = 0; 12763ec90878SJan Glauber buf->buffer->element[15].sflags = 0; 12774a71df50SFrank Blaschka buf->next_element_to_fill = 0; 12780da9581dSEinar Lueck atomic_set(&buf->state, newbufstate); 12790da9581dSEinar Lueck } 12800da9581dSEinar Lueck 12810da9581dSEinar Lueck static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) 12820da9581dSEinar Lueck { 12830da9581dSEinar Lueck int j; 12840da9581dSEinar Lueck 12850da9581dSEinar Lueck for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 12860da9581dSEinar Lueck if (!q->bufs[j]) 12870da9581dSEinar Lueck continue; 128872861ae7SEinar Lueck qeth_cleanup_handled_pending(q, j, 1); 12890da9581dSEinar Lueck qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY); 12900da9581dSEinar Lueck if (free) { 12910da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); 12920da9581dSEinar Lueck q->bufs[j] = NULL; 12930da9581dSEinar Lueck } 12940da9581dSEinar Lueck } 12954a71df50SFrank Blaschka } 12964a71df50SFrank Blaschka 12974a71df50SFrank Blaschka void qeth_clear_qdio_buffers(struct qeth_card *card) 12984a71df50SFrank Blaschka { 12990da9581dSEinar Lueck int i; 13004a71df50SFrank Blaschka 1301847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "clearqdbf"); 13024a71df50SFrank Blaschka /* clear outbound buffers to free skbs */ 13030da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 13044a71df50SFrank Blaschka if (card->qdio.out_qs[i]) { 13050da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 0); 13060da9581dSEinar Lueck } 13074a71df50SFrank Blaschka } 13084a71df50SFrank Blaschka } 13094a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_qdio_buffers); 13104a71df50SFrank Blaschka 13114a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *card) 13124a71df50SFrank Blaschka { 13134a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 13144a71df50SFrank Blaschka int i = 0; 13154a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 13164a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list){ 13174a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) 13184a71df50SFrank Blaschka free_page((unsigned long)pool_entry->elements[i]); 13194a71df50SFrank Blaschka list_del(&pool_entry->init_list); 13204a71df50SFrank Blaschka kfree(pool_entry); 13214a71df50SFrank Blaschka } 13224a71df50SFrank Blaschka } 13234a71df50SFrank Blaschka 13244a71df50SFrank Blaschka static void qeth_clean_channel(struct qeth_channel *channel) 13254a71df50SFrank Blaschka { 13264a71df50SFrank Blaschka int cnt; 13274a71df50SFrank Blaschka 1328d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freech"); 13294a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 13304a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 13314a71df50SFrank Blaschka } 13324a71df50SFrank Blaschka 1333725b9c04SSebastian Ott static void qeth_set_single_write_queues(struct qeth_card *card) 1334725b9c04SSebastian Ott { 1335725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1336725b9c04SSebastian Ott (card->qdio.no_out_queues == 4)) 1337725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1338725b9c04SSebastian Ott 1339725b9c04SSebastian Ott card->qdio.no_out_queues = 1; 1340725b9c04SSebastian Ott if (card->qdio.default_out_queue != 0) 1341725b9c04SSebastian Ott dev_info(&card->gdev->dev, "Priority Queueing not supported\n"); 1342725b9c04SSebastian Ott 1343725b9c04SSebastian Ott card->qdio.default_out_queue = 0; 1344725b9c04SSebastian Ott } 1345725b9c04SSebastian Ott 1346725b9c04SSebastian Ott static void qeth_set_multiple_write_queues(struct qeth_card *card) 1347725b9c04SSebastian Ott { 1348725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1349725b9c04SSebastian Ott (card->qdio.no_out_queues == 1)) { 1350725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1351725b9c04SSebastian Ott card->qdio.default_out_queue = 2; 1352725b9c04SSebastian Ott } 1353725b9c04SSebastian Ott card->qdio.no_out_queues = 4; 1354725b9c04SSebastian Ott } 1355725b9c04SSebastian Ott 1356725b9c04SSebastian Ott static void qeth_update_from_chp_desc(struct qeth_card *card) 13574a71df50SFrank Blaschka { 13584a71df50SFrank Blaschka struct ccw_device *ccwdev; 13592bf29df7SSebastian Ott struct channel_path_desc *chp_dsc; 13604a71df50SFrank Blaschka 13615113fec0SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "chp_desc"); 13624a71df50SFrank Blaschka 13634a71df50SFrank Blaschka ccwdev = card->data.ccwdev; 1364725b9c04SSebastian Ott chp_dsc = ccw_device_get_chp_desc(ccwdev, 0); 1365725b9c04SSebastian Ott if (!chp_dsc) 1366725b9c04SSebastian Ott goto out; 1367725b9c04SSebastian Ott 1368d0ff1f52SUrsula Braun card->info.func_level = 0x4100 + chp_dsc->desc; 1369725b9c04SSebastian Ott if (card->info.type == QETH_CARD_TYPE_IQD) 1370725b9c04SSebastian Ott goto out; 1371725b9c04SSebastian Ott 1372725b9c04SSebastian Ott /* CHPP field bit 6 == 1 -> single queue */ 1373725b9c04SSebastian Ott if ((chp_dsc->chpp & 0x02) == 0x02) 1374725b9c04SSebastian Ott qeth_set_single_write_queues(card); 1375725b9c04SSebastian Ott else 1376725b9c04SSebastian Ott qeth_set_multiple_write_queues(card); 1377725b9c04SSebastian Ott out: 1378d0ff1f52SUrsula Braun kfree(chp_dsc); 13795113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues); 13805113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); 13814a71df50SFrank Blaschka } 13824a71df50SFrank Blaschka 13834a71df50SFrank Blaschka static void qeth_init_qdio_info(struct qeth_card *card) 13844a71df50SFrank Blaschka { 1385d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 4, "intqdinf"); 13864a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 13874a71df50SFrank Blaschka /* inbound */ 1388ed2e93efSJulian Wiedmann card->qdio.no_in_queues = 1; 13894a71df50SFrank Blaschka card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 1390dcf4ae2dSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1391dcf4ae2dSFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT; 1392dcf4ae2dSFrank Blaschka else 13934a71df50SFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; 13944a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count; 13954a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list); 13964a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.init_pool.entry_list); 13974a71df50SFrank Blaschka } 13984a71df50SFrank Blaschka 13994a71df50SFrank Blaschka static void qeth_set_intial_options(struct qeth_card *card) 14004a71df50SFrank Blaschka { 14014a71df50SFrank Blaschka card->options.route4.type = NO_ROUTER; 14024a71df50SFrank Blaschka card->options.route6.type = NO_ROUTER; 14034a71df50SFrank Blaschka card->options.fake_broadcast = 0; 14044a71df50SFrank Blaschka card->options.performance_stats = 0; 14054a71df50SFrank Blaschka card->options.rx_sg_cb = QETH_RX_SG_CB; 1406d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 14070da9581dSEinar Lueck card->options.cq = QETH_CQ_DISABLED; 14084a71df50SFrank Blaschka } 14094a71df50SFrank Blaschka 14104a71df50SFrank Blaschka static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) 14114a71df50SFrank Blaschka { 14124a71df50SFrank Blaschka unsigned long flags; 14134a71df50SFrank Blaschka int rc = 0; 14144a71df50SFrank Blaschka 14154a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 1416847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, " %02x%02x%02x", 14174a71df50SFrank Blaschka (u8) card->thread_start_mask, 14184a71df50SFrank Blaschka (u8) card->thread_allowed_mask, 14194a71df50SFrank Blaschka (u8) card->thread_running_mask); 14204a71df50SFrank Blaschka rc = (card->thread_start_mask & thread); 14214a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 14224a71df50SFrank Blaschka return rc; 14234a71df50SFrank Blaschka } 14244a71df50SFrank Blaschka 14254a71df50SFrank Blaschka static void qeth_start_kernel_thread(struct work_struct *work) 14264a71df50SFrank Blaschka { 14273f36b890SFrank Blaschka struct task_struct *ts; 14284a71df50SFrank Blaschka struct qeth_card *card = container_of(work, struct qeth_card, 14294a71df50SFrank Blaschka kernel_thread_starter); 1430847a50fdSCarsten Otte QETH_CARD_TEXT(card , 2, "strthrd"); 14314a71df50SFrank Blaschka 14324a71df50SFrank Blaschka if (card->read.state != CH_STATE_UP && 14334a71df50SFrank Blaschka card->write.state != CH_STATE_UP) 14344a71df50SFrank Blaschka return; 14353f36b890SFrank Blaschka if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { 1436c041f2d4SSebastian Ott ts = kthread_run(card->discipline->recover, (void *)card, 14374a71df50SFrank Blaschka "qeth_recover"); 14383f36b890SFrank Blaschka if (IS_ERR(ts)) { 14393f36b890SFrank Blaschka qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); 14403f36b890SFrank Blaschka qeth_clear_thread_running_bit(card, 14413f36b890SFrank Blaschka QETH_RECOVER_THREAD); 14423f36b890SFrank Blaschka } 14433f36b890SFrank Blaschka } 14444a71df50SFrank Blaschka } 14454a71df50SFrank Blaschka 1446bca51650SThomas Richter static void qeth_buffer_reclaim_work(struct work_struct *); 14474a71df50SFrank Blaschka static int qeth_setup_card(struct qeth_card *card) 14484a71df50SFrank Blaschka { 14494a71df50SFrank Blaschka 1450d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupcrd"); 1451d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 14524a71df50SFrank Blaschka 14534a71df50SFrank Blaschka card->read.state = CH_STATE_DOWN; 14544a71df50SFrank Blaschka card->write.state = CH_STATE_DOWN; 14554a71df50SFrank Blaschka card->data.state = CH_STATE_DOWN; 14564a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 14574a71df50SFrank Blaschka card->lan_online = 0; 1458908abbb5SUrsula Braun card->read_or_write_problem = 0; 14594a71df50SFrank Blaschka card->dev = NULL; 14604a71df50SFrank Blaschka spin_lock_init(&card->vlanlock); 14614a71df50SFrank Blaschka spin_lock_init(&card->mclock); 14624a71df50SFrank Blaschka spin_lock_init(&card->lock); 14634a71df50SFrank Blaschka spin_lock_init(&card->ip_lock); 14644a71df50SFrank Blaschka spin_lock_init(&card->thread_mask_lock); 1465c4949f07SFrank Blaschka mutex_init(&card->conf_mutex); 14669dc48cccSUrsula Braun mutex_init(&card->discipline_mutex); 14674a71df50SFrank Blaschka card->thread_start_mask = 0; 14684a71df50SFrank Blaschka card->thread_allowed_mask = 0; 14694a71df50SFrank Blaschka card->thread_running_mask = 0; 14704a71df50SFrank Blaschka INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); 14714a71df50SFrank Blaschka INIT_LIST_HEAD(&card->cmd_waiter_list); 14724a71df50SFrank Blaschka init_waitqueue_head(&card->wait_q); 147325985edcSLucas De Marchi /* initial options */ 14744a71df50SFrank Blaschka qeth_set_intial_options(card); 14754a71df50SFrank Blaschka /* IP address takeover */ 14764a71df50SFrank Blaschka INIT_LIST_HEAD(&card->ipato.entries); 14774a71df50SFrank Blaschka card->ipato.enabled = 0; 14784a71df50SFrank Blaschka card->ipato.invert4 = 0; 14794a71df50SFrank Blaschka card->ipato.invert6 = 0; 14804a71df50SFrank Blaschka /* init QDIO stuff */ 14814a71df50SFrank Blaschka qeth_init_qdio_info(card); 1482b3332930SFrank Blaschka INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); 14830f54761dSStefan Raspl INIT_WORK(&card->close_dev_work, qeth_close_dev_handler); 14844a71df50SFrank Blaschka return 0; 14854a71df50SFrank Blaschka } 14864a71df50SFrank Blaschka 14876bcac508SMartin Schwidefsky static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) 14886bcac508SMartin Schwidefsky { 14896bcac508SMartin Schwidefsky struct qeth_card *card = container_of(slr, struct qeth_card, 14906bcac508SMartin Schwidefsky qeth_service_level); 14910d788c7dSKlaus-Dieter Wacker if (card->info.mcl_level[0]) 14920d788c7dSKlaus-Dieter Wacker seq_printf(m, "qeth: %s firmware level %s\n", 14930d788c7dSKlaus-Dieter Wacker CARD_BUS_ID(card), card->info.mcl_level); 14946bcac508SMartin Schwidefsky } 14956bcac508SMartin Schwidefsky 14964a71df50SFrank Blaschka static struct qeth_card *qeth_alloc_card(void) 14974a71df50SFrank Blaschka { 14984a71df50SFrank Blaschka struct qeth_card *card; 14994a71df50SFrank Blaschka 1500d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "alloccrd"); 15014a71df50SFrank Blaschka card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL); 15024a71df50SFrank Blaschka if (!card) 150376b11f8eSUrsula Braun goto out; 1504d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 150576b11f8eSUrsula Braun if (qeth_setup_channel(&card->read)) 150676b11f8eSUrsula Braun goto out_ip; 150776b11f8eSUrsula Braun if (qeth_setup_channel(&card->write)) 150876b11f8eSUrsula Braun goto out_channel; 15094a71df50SFrank Blaschka card->options.layer2 = -1; 15106bcac508SMartin Schwidefsky card->qeth_service_level.seq_print = qeth_core_sl_print; 15116bcac508SMartin Schwidefsky register_service_level(&card->qeth_service_level); 15124a71df50SFrank Blaschka return card; 151376b11f8eSUrsula Braun 151476b11f8eSUrsula Braun out_channel: 151576b11f8eSUrsula Braun qeth_clean_channel(&card->read); 151676b11f8eSUrsula Braun out_ip: 151776b11f8eSUrsula Braun kfree(card); 151876b11f8eSUrsula Braun out: 151976b11f8eSUrsula Braun return NULL; 15204a71df50SFrank Blaschka } 15214a71df50SFrank Blaschka 1522ed2e93efSJulian Wiedmann static void qeth_determine_card_type(struct qeth_card *card) 15234a71df50SFrank Blaschka { 1524d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "detcdtyp"); 15254a71df50SFrank Blaschka 15264a71df50SFrank Blaschka card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT; 15274a71df50SFrank Blaschka card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; 1528ed2e93efSJulian Wiedmann card->info.type = CARD_RDEV(card)->id.driver_info; 1529ed2e93efSJulian Wiedmann card->qdio.no_out_queues = QETH_MAX_QUEUES; 1530ed2e93efSJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_IQD) 1531ed2e93efSJulian Wiedmann card->info.is_multicast_different = 0x0103; 1532725b9c04SSebastian Ott qeth_update_from_chp_desc(card); 15334a71df50SFrank Blaschka } 15344a71df50SFrank Blaschka 15354a71df50SFrank Blaschka static int qeth_clear_channel(struct qeth_channel *channel) 15364a71df50SFrank Blaschka { 15374a71df50SFrank Blaschka unsigned long flags; 15384a71df50SFrank Blaschka struct qeth_card *card; 15394a71df50SFrank Blaschka int rc; 15404a71df50SFrank Blaschka 15414a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1542847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearch"); 15434a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15444a71df50SFrank Blaschka rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); 15454a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15464a71df50SFrank Blaschka 15474a71df50SFrank Blaschka if (rc) 15484a71df50SFrank Blaschka return rc; 15494a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15504a71df50SFrank Blaschka channel->state == CH_STATE_STOPPED, QETH_TIMEOUT); 15514a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15524a71df50SFrank Blaschka return rc; 15534a71df50SFrank Blaschka if (channel->state != CH_STATE_STOPPED) 15544a71df50SFrank Blaschka return -ETIME; 15554a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 15564a71df50SFrank Blaschka return 0; 15574a71df50SFrank Blaschka } 15584a71df50SFrank Blaschka 15594a71df50SFrank Blaschka static int qeth_halt_channel(struct qeth_channel *channel) 15604a71df50SFrank Blaschka { 15614a71df50SFrank Blaschka unsigned long flags; 15624a71df50SFrank Blaschka struct qeth_card *card; 15634a71df50SFrank Blaschka int rc; 15644a71df50SFrank Blaschka 15654a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1566847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltch"); 15674a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15684a71df50SFrank Blaschka rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); 15694a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15704a71df50SFrank Blaschka 15714a71df50SFrank Blaschka if (rc) 15724a71df50SFrank Blaschka return rc; 15734a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15744a71df50SFrank Blaschka channel->state == CH_STATE_HALTED, QETH_TIMEOUT); 15754a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15764a71df50SFrank Blaschka return rc; 15774a71df50SFrank Blaschka if (channel->state != CH_STATE_HALTED) 15784a71df50SFrank Blaschka return -ETIME; 15794a71df50SFrank Blaschka return 0; 15804a71df50SFrank Blaschka } 15814a71df50SFrank Blaschka 15824a71df50SFrank Blaschka static int qeth_halt_channels(struct qeth_card *card) 15834a71df50SFrank Blaschka { 15844a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 15854a71df50SFrank Blaschka 1586847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltchs"); 15874a71df50SFrank Blaschka rc1 = qeth_halt_channel(&card->read); 15884a71df50SFrank Blaschka rc2 = qeth_halt_channel(&card->write); 15894a71df50SFrank Blaschka rc3 = qeth_halt_channel(&card->data); 15904a71df50SFrank Blaschka if (rc1) 15914a71df50SFrank Blaschka return rc1; 15924a71df50SFrank Blaschka if (rc2) 15934a71df50SFrank Blaschka return rc2; 15944a71df50SFrank Blaschka return rc3; 15954a71df50SFrank Blaschka } 15964a71df50SFrank Blaschka 15974a71df50SFrank Blaschka static int qeth_clear_channels(struct qeth_card *card) 15984a71df50SFrank Blaschka { 15994a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 16004a71df50SFrank Blaschka 1601847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearchs"); 16024a71df50SFrank Blaschka rc1 = qeth_clear_channel(&card->read); 16034a71df50SFrank Blaschka rc2 = qeth_clear_channel(&card->write); 16044a71df50SFrank Blaschka rc3 = qeth_clear_channel(&card->data); 16054a71df50SFrank Blaschka if (rc1) 16064a71df50SFrank Blaschka return rc1; 16074a71df50SFrank Blaschka if (rc2) 16084a71df50SFrank Blaschka return rc2; 16094a71df50SFrank Blaschka return rc3; 16104a71df50SFrank Blaschka } 16114a71df50SFrank Blaschka 16124a71df50SFrank Blaschka static int qeth_clear_halt_card(struct qeth_card *card, int halt) 16134a71df50SFrank Blaschka { 16144a71df50SFrank Blaschka int rc = 0; 16154a71df50SFrank Blaschka 1616847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clhacrd"); 16174a71df50SFrank Blaschka 16184a71df50SFrank Blaschka if (halt) 16194a71df50SFrank Blaschka rc = qeth_halt_channels(card); 16204a71df50SFrank Blaschka if (rc) 16214a71df50SFrank Blaschka return rc; 16224a71df50SFrank Blaschka return qeth_clear_channels(card); 16234a71df50SFrank Blaschka } 16244a71df50SFrank Blaschka 16254a71df50SFrank Blaschka int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) 16264a71df50SFrank Blaschka { 16274a71df50SFrank Blaschka int rc = 0; 16284a71df50SFrank Blaschka 1629847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "qdioclr"); 16304a71df50SFrank Blaschka switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED, 16314a71df50SFrank Blaschka QETH_QDIO_CLEANING)) { 16324a71df50SFrank Blaschka case QETH_QDIO_ESTABLISHED: 16334a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1634cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16354a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_HALT); 16364a71df50SFrank Blaschka else 1637cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16384a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_CLEAR); 16394a71df50SFrank Blaschka if (rc) 1640847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "1err%d", rc); 16414a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 16424a71df50SFrank Blaschka break; 16434a71df50SFrank Blaschka case QETH_QDIO_CLEANING: 16444a71df50SFrank Blaschka return rc; 16454a71df50SFrank Blaschka default: 16464a71df50SFrank Blaschka break; 16474a71df50SFrank Blaschka } 16484a71df50SFrank Blaschka rc = qeth_clear_halt_card(card, use_halt); 16494a71df50SFrank Blaschka if (rc) 1650847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "2err%d", rc); 16514a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 16524a71df50SFrank Blaschka return rc; 16534a71df50SFrank Blaschka } 16544a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_clear_card); 16554a71df50SFrank Blaschka 16564a71df50SFrank Blaschka static int qeth_read_conf_data(struct qeth_card *card, void **buffer, 16574a71df50SFrank Blaschka int *length) 16584a71df50SFrank Blaschka { 16594a71df50SFrank Blaschka struct ciw *ciw; 16604a71df50SFrank Blaschka char *rcd_buf; 16614a71df50SFrank Blaschka int ret; 16624a71df50SFrank Blaschka struct qeth_channel *channel = &card->data; 16634a71df50SFrank Blaschka unsigned long flags; 16644a71df50SFrank Blaschka 16654a71df50SFrank Blaschka /* 16664a71df50SFrank Blaschka * scan for RCD command in extended SenseID data 16674a71df50SFrank Blaschka */ 16684a71df50SFrank Blaschka ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD); 16694a71df50SFrank Blaschka if (!ciw || ciw->cmd == 0) 16704a71df50SFrank Blaschka return -EOPNOTSUPP; 16714a71df50SFrank Blaschka rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); 16724a71df50SFrank Blaschka if (!rcd_buf) 16734a71df50SFrank Blaschka return -ENOMEM; 16744a71df50SFrank Blaschka 16754a71df50SFrank Blaschka channel->ccw.cmd_code = ciw->cmd; 16764a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(rcd_buf); 16774a71df50SFrank Blaschka channel->ccw.count = ciw->count; 16784a71df50SFrank Blaschka channel->ccw.flags = CCW_FLAG_SLI; 16794a71df50SFrank Blaschka channel->state = CH_STATE_RCD; 16804a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 16814a71df50SFrank Blaschka ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw, 16824a71df50SFrank Blaschka QETH_RCD_PARM, LPM_ANYPATH, 0, 16834a71df50SFrank Blaschka QETH_RCD_TIMEOUT); 16844a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 16854a71df50SFrank Blaschka if (!ret) 16864a71df50SFrank Blaschka wait_event(card->wait_q, 16874a71df50SFrank Blaschka (channel->state == CH_STATE_RCD_DONE || 16884a71df50SFrank Blaschka channel->state == CH_STATE_DOWN)); 16894a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) 16904a71df50SFrank Blaschka ret = -EIO; 16914a71df50SFrank Blaschka else 16924a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 16934a71df50SFrank Blaschka if (ret) { 16944a71df50SFrank Blaschka kfree(rcd_buf); 16954a71df50SFrank Blaschka *buffer = NULL; 16964a71df50SFrank Blaschka *length = 0; 16974a71df50SFrank Blaschka } else { 16984a71df50SFrank Blaschka *length = ciw->count; 16994a71df50SFrank Blaschka *buffer = rcd_buf; 17004a71df50SFrank Blaschka } 17014a71df50SFrank Blaschka return ret; 17024a71df50SFrank Blaschka } 17034a71df50SFrank Blaschka 1704a60389abSEinar Lueck static void qeth_configure_unitaddr(struct qeth_card *card, char *prcd) 17054a71df50SFrank Blaschka { 1706a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgunit"); 17074a71df50SFrank Blaschka card->info.chpid = prcd[30]; 17084a71df50SFrank Blaschka card->info.unit_addr2 = prcd[31]; 17094a71df50SFrank Blaschka card->info.cula = prcd[63]; 17104a71df50SFrank Blaschka card->info.guestlan = ((prcd[0x10] == _ascebc['V']) && 17114a71df50SFrank Blaschka (prcd[0x11] == _ascebc['M'])); 1712a60389abSEinar Lueck } 1713a60389abSEinar Lueck 1714c70eb09dSJulian Wiedmann /* Determine whether the device requires a specific layer discipline */ 1715c70eb09dSJulian Wiedmann static enum qeth_discipline_id qeth_enforce_discipline(struct qeth_card *card) 1716c70eb09dSJulian Wiedmann { 1717c70eb09dSJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_OSM || 1718c70eb09dSJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSN) { 1719c70eb09dSJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force l2"); 1720c70eb09dSJulian Wiedmann return QETH_DISCIPLINE_LAYER2; 1721c70eb09dSJulian Wiedmann } 1722c70eb09dSJulian Wiedmann 1723c70eb09dSJulian Wiedmann /* virtual HiperSocket is L3 only: */ 1724c70eb09dSJulian Wiedmann if (card->info.guestlan && card->info.type == QETH_CARD_TYPE_IQD) { 1725c70eb09dSJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force l3"); 1726c70eb09dSJulian Wiedmann return QETH_DISCIPLINE_LAYER3; 1727c70eb09dSJulian Wiedmann } 1728c70eb09dSJulian Wiedmann 1729c70eb09dSJulian Wiedmann QETH_DBF_TEXT(SETUP, 3, "force no"); 1730c70eb09dSJulian Wiedmann return QETH_DISCIPLINE_UNDETERMINED; 1731c70eb09dSJulian Wiedmann } 1732c70eb09dSJulian Wiedmann 1733a60389abSEinar Lueck static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd) 1734a60389abSEinar Lueck { 1735a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); 1736a60389abSEinar Lueck 1737e6e056baSStefan Raspl if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && 1738a0c98523SUrsula Braun prcd[76] >= 0xF1 && prcd[76] <= 0xF4) { 1739a60389abSEinar Lueck card->info.blkt.time_total = 0; 1740a60389abSEinar Lueck card->info.blkt.inter_packet = 0; 1741a60389abSEinar Lueck card->info.blkt.inter_packet_jumbo = 0; 1742a0c98523SUrsula Braun } else { 1743a0c98523SUrsula Braun card->info.blkt.time_total = 250; 1744a0c98523SUrsula Braun card->info.blkt.inter_packet = 5; 1745a0c98523SUrsula Braun card->info.blkt.inter_packet_jumbo = 15; 1746a60389abSEinar Lueck } 17474a71df50SFrank Blaschka } 17484a71df50SFrank Blaschka 17494a71df50SFrank Blaschka static void qeth_init_tokens(struct qeth_card *card) 17504a71df50SFrank Blaschka { 17514a71df50SFrank Blaschka card->token.issuer_rm_w = 0x00010103UL; 17524a71df50SFrank Blaschka card->token.cm_filter_w = 0x00010108UL; 17534a71df50SFrank Blaschka card->token.cm_connection_w = 0x0001010aUL; 17544a71df50SFrank Blaschka card->token.ulp_filter_w = 0x0001010bUL; 17554a71df50SFrank Blaschka card->token.ulp_connection_w = 0x0001010dUL; 17564a71df50SFrank Blaschka } 17574a71df50SFrank Blaschka 17584a71df50SFrank Blaschka static void qeth_init_func_level(struct qeth_card *card) 17594a71df50SFrank Blaschka { 17605113fec0SUrsula Braun switch (card->info.type) { 17615113fec0SUrsula Braun case QETH_CARD_TYPE_IQD: 17626298263aSKlaus-Dieter Wacker card->info.func_level = QETH_IDX_FUNC_LEVEL_IQD; 17635113fec0SUrsula Braun break; 17645113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 17650132951eSUrsula Braun case QETH_CARD_TYPE_OSN: 17665113fec0SUrsula Braun card->info.func_level = QETH_IDX_FUNC_LEVEL_OSD; 17675113fec0SUrsula Braun break; 17685113fec0SUrsula Braun default: 17695113fec0SUrsula Braun break; 17704a71df50SFrank Blaschka } 17714a71df50SFrank Blaschka } 17724a71df50SFrank Blaschka 17734a71df50SFrank Blaschka static int qeth_idx_activate_get_answer(struct qeth_channel *channel, 17744a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 17754a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 17764a71df50SFrank Blaschka { 17774a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 17784a71df50SFrank Blaschka unsigned long flags; 17794a71df50SFrank Blaschka int rc; 17804a71df50SFrank Blaschka struct qeth_card *card; 17814a71df50SFrank Blaschka 1782d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxanswr"); 17834a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 17844a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 17851aec42bcSThomas Richter if (!iob) 17861aec42bcSThomas Richter return -ENOMEM; 17874a71df50SFrank Blaschka iob->callback = idx_reply_cb; 17884a71df50SFrank Blaschka memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); 17894a71df50SFrank Blaschka channel->ccw.count = QETH_BUFSIZE; 17904a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob->data); 17914a71df50SFrank Blaschka 17924a71df50SFrank Blaschka wait_event(card->wait_q, 17934a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1794d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 17954a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 17964a71df50SFrank Blaschka rc = ccw_device_start(channel->ccwdev, 17974a71df50SFrank Blaschka &channel->ccw, (addr_t) iob, 0, 0); 17984a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 17994a71df50SFrank Blaschka 18004a71df50SFrank Blaschka if (rc) { 180114cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc); 1802d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 18034a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 18044a71df50SFrank Blaschka wake_up(&card->wait_q); 18054a71df50SFrank Blaschka return rc; 18064a71df50SFrank Blaschka } 18074a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 18084a71df50SFrank Blaschka channel->state == CH_STATE_UP, QETH_TIMEOUT); 18094a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 18104a71df50SFrank Blaschka return rc; 18114a71df50SFrank Blaschka if (channel->state != CH_STATE_UP) { 18124a71df50SFrank Blaschka rc = -ETIME; 1813d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 18144a71df50SFrank Blaschka qeth_clear_cmd_buffers(channel); 18154a71df50SFrank Blaschka } else 18164a71df50SFrank Blaschka rc = 0; 18174a71df50SFrank Blaschka return rc; 18184a71df50SFrank Blaschka } 18194a71df50SFrank Blaschka 18204a71df50SFrank Blaschka static int qeth_idx_activate_channel(struct qeth_channel *channel, 18214a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 18224a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 18234a71df50SFrank Blaschka { 18244a71df50SFrank Blaschka struct qeth_card *card; 18254a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 18264a71df50SFrank Blaschka unsigned long flags; 18274a71df50SFrank Blaschka __u16 temp; 18284a71df50SFrank Blaschka __u8 tmp; 18294a71df50SFrank Blaschka int rc; 1830f06f6f32SCornelia Huck struct ccw_dev_id temp_devid; 18314a71df50SFrank Blaschka 18324a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 18334a71df50SFrank Blaschka 1834d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxactch"); 18354a71df50SFrank Blaschka 18364a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 18371aec42bcSThomas Richter if (!iob) 18381aec42bcSThomas Richter return -ENOMEM; 18394a71df50SFrank Blaschka iob->callback = idx_reply_cb; 18404a71df50SFrank Blaschka memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); 18414a71df50SFrank Blaschka channel->ccw.count = IDX_ACTIVATE_SIZE; 18424a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob->data); 18434a71df50SFrank Blaschka if (channel == &card->write) { 18444a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE); 18454a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 18464a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 18474a71df50SFrank Blaschka card->seqno.trans_hdr++; 18484a71df50SFrank Blaschka } else { 18494a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE); 18504a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 18514a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 18524a71df50SFrank Blaschka } 18534a71df50SFrank Blaschka tmp = ((__u8)card->info.portno) | 0x80; 18544a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1); 18554a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 18564a71df50SFrank Blaschka &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); 18574a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data), 18584a71df50SFrank Blaschka &card->info.func_level, sizeof(__u16)); 1859f06f6f32SCornelia Huck ccw_device_get_id(CARD_DDEV(card), &temp_devid); 1860f06f6f32SCornelia Huck memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 2); 18614a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 18624a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2); 18634a71df50SFrank Blaschka 18644a71df50SFrank Blaschka wait_event(card->wait_q, 18654a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1866d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 18674a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 18684a71df50SFrank Blaschka rc = ccw_device_start(channel->ccwdev, 18694a71df50SFrank Blaschka &channel->ccw, (addr_t) iob, 0, 0); 18704a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 18714a71df50SFrank Blaschka 18724a71df50SFrank Blaschka if (rc) { 187314cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error1 in activating channel. rc=%d\n", 187414cc21b6SFrank Blaschka rc); 1875d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 18764a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 18774a71df50SFrank Blaschka wake_up(&card->wait_q); 18784a71df50SFrank Blaschka return rc; 18794a71df50SFrank Blaschka } 18804a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 18814a71df50SFrank Blaschka channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT); 18824a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 18834a71df50SFrank Blaschka return rc; 18844a71df50SFrank Blaschka if (channel->state != CH_STATE_ACTIVATING) { 188574eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, "The qeth device driver" 188674eacdb9SFrank Blaschka " failed to recover an error on the device\n"); 188774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n", 188874eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev)); 1889d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME); 18904a71df50SFrank Blaschka qeth_clear_cmd_buffers(channel); 18914a71df50SFrank Blaschka return -ETIME; 18924a71df50SFrank Blaschka } 18934a71df50SFrank Blaschka return qeth_idx_activate_get_answer(channel, idx_reply_cb); 18944a71df50SFrank Blaschka } 18954a71df50SFrank Blaschka 18964a71df50SFrank Blaschka static int qeth_peer_func_level(int level) 18974a71df50SFrank Blaschka { 18984a71df50SFrank Blaschka if ((level & 0xff) == 8) 18994a71df50SFrank Blaschka return (level & 0xff) + 0x400; 19004a71df50SFrank Blaschka if (((level >> 8) & 3) == 1) 19014a71df50SFrank Blaschka return (level & 0xff) + 0x200; 19024a71df50SFrank Blaschka return level; 19034a71df50SFrank Blaschka } 19044a71df50SFrank Blaschka 19054a71df50SFrank Blaschka static void qeth_idx_write_cb(struct qeth_channel *channel, 19064a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 19074a71df50SFrank Blaschka { 19084a71df50SFrank Blaschka struct qeth_card *card; 19094a71df50SFrank Blaschka __u16 temp; 19104a71df50SFrank Blaschka 1911d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxwrcb"); 19124a71df50SFrank Blaschka 19134a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19144a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 19154a71df50SFrank Blaschka goto out; 19164a71df50SFrank Blaschka } 19174a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 19184a71df50SFrank Blaschka 19194a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 19205113fec0SUrsula Braun if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == QETH_IDX_ACT_ERR_EXCL) 192174eacdb9SFrank Blaschka dev_err(&card->write.ccwdev->dev, 192274eacdb9SFrank Blaschka "The adapter is used exclusively by another " 192374eacdb9SFrank Blaschka "host\n"); 19244a71df50SFrank Blaschka else 192574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:" 192674eacdb9SFrank Blaschka " negative reply\n", 192774eacdb9SFrank Blaschka dev_name(&card->write.ccwdev->dev)); 19284a71df50SFrank Blaschka goto out; 19294a71df50SFrank Blaschka } 19304a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 19314a71df50SFrank Blaschka if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { 193274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: " 193374eacdb9SFrank Blaschka "function level mismatch (sent: 0x%x, received: " 193474eacdb9SFrank Blaschka "0x%x)\n", dev_name(&card->write.ccwdev->dev), 193574eacdb9SFrank Blaschka card->info.func_level, temp); 19364a71df50SFrank Blaschka goto out; 19374a71df50SFrank Blaschka } 19384a71df50SFrank Blaschka channel->state = CH_STATE_UP; 19394a71df50SFrank Blaschka out: 19404a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 19414a71df50SFrank Blaschka } 19424a71df50SFrank Blaschka 19434a71df50SFrank Blaschka static void qeth_idx_read_cb(struct qeth_channel *channel, 19444a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 19454a71df50SFrank Blaschka { 19464a71df50SFrank Blaschka struct qeth_card *card; 19474a71df50SFrank Blaschka __u16 temp; 19484a71df50SFrank Blaschka 1949d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxrdcb"); 19504a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19514a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 19524a71df50SFrank Blaschka goto out; 19534a71df50SFrank Blaschka } 19544a71df50SFrank Blaschka 19554a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 19565113fec0SUrsula Braun if (qeth_check_idx_response(card, iob->data)) 19574a71df50SFrank Blaschka goto out; 19584a71df50SFrank Blaschka 19594a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 19605113fec0SUrsula Braun switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) { 19615113fec0SUrsula Braun case QETH_IDX_ACT_ERR_EXCL: 196274eacdb9SFrank Blaschka dev_err(&card->write.ccwdev->dev, 196374eacdb9SFrank Blaschka "The adapter is used exclusively by another " 196474eacdb9SFrank Blaschka "host\n"); 19655113fec0SUrsula Braun break; 19665113fec0SUrsula Braun case QETH_IDX_ACT_ERR_AUTH: 196701fc3e86SUrsula Braun case QETH_IDX_ACT_ERR_AUTH_USER: 19685113fec0SUrsula Braun dev_err(&card->read.ccwdev->dev, 19695113fec0SUrsula Braun "Setting the device online failed because of " 197001fc3e86SUrsula Braun "insufficient authorization\n"); 19715113fec0SUrsula Braun break; 19725113fec0SUrsula Braun default: 197374eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:" 197474eacdb9SFrank Blaschka " negative reply\n", 197574eacdb9SFrank Blaschka dev_name(&card->read.ccwdev->dev)); 19765113fec0SUrsula Braun } 197701fc3e86SUrsula Braun QETH_CARD_TEXT_(card, 2, "idxread%c", 197801fc3e86SUrsula Braun QETH_IDX_ACT_CAUSE_CODE(iob->data)); 19794a71df50SFrank Blaschka goto out; 19804a71df50SFrank Blaschka } 19814a71df50SFrank Blaschka 19824a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 19834a71df50SFrank Blaschka if (temp != qeth_peer_func_level(card->info.func_level)) { 198474eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " 19854a71df50SFrank Blaschka "level mismatch (sent: 0x%x, received: 0x%x)\n", 198674eacdb9SFrank Blaschka dev_name(&card->read.ccwdev->dev), 198774eacdb9SFrank Blaschka card->info.func_level, temp); 19884a71df50SFrank Blaschka goto out; 19894a71df50SFrank Blaschka } 19904a71df50SFrank Blaschka memcpy(&card->token.issuer_rm_r, 19914a71df50SFrank Blaschka QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 19924a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 19934a71df50SFrank Blaschka memcpy(&card->info.mcl_level[0], 19944a71df50SFrank Blaschka QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH); 19954a71df50SFrank Blaschka channel->state = CH_STATE_UP; 19964a71df50SFrank Blaschka out: 19974a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 19984a71df50SFrank Blaschka } 19994a71df50SFrank Blaschka 20004a71df50SFrank Blaschka void qeth_prepare_control_data(struct qeth_card *card, int len, 20014a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 20024a71df50SFrank Blaschka { 20034a71df50SFrank Blaschka qeth_setup_ccw(&card->write, iob->data, len); 20044a71df50SFrank Blaschka iob->callback = qeth_release_buffer; 20054a71df50SFrank Blaschka 20064a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 20074a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 20084a71df50SFrank Blaschka card->seqno.trans_hdr++; 20094a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data), 20104a71df50SFrank Blaschka &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH); 20114a71df50SFrank Blaschka card->seqno.pdu_hdr++; 20124a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data), 20134a71df50SFrank Blaschka &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH); 2014d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN); 20154a71df50SFrank Blaschka } 20164a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_control_data); 20174a71df50SFrank Blaschka 2018efbbc1d5SEugene Crosser /** 2019efbbc1d5SEugene Crosser * qeth_send_control_data() - send control command to the card 2020efbbc1d5SEugene Crosser * @card: qeth_card structure pointer 2021efbbc1d5SEugene Crosser * @len: size of the command buffer 2022efbbc1d5SEugene Crosser * @iob: qeth_cmd_buffer pointer 2023efbbc1d5SEugene Crosser * @reply_cb: callback function pointer 2024efbbc1d5SEugene Crosser * @cb_card: pointer to the qeth_card structure 2025efbbc1d5SEugene Crosser * @cb_reply: pointer to the qeth_reply structure 2026efbbc1d5SEugene Crosser * @cb_cmd: pointer to the original iob for non-IPA 2027efbbc1d5SEugene Crosser * commands, or to the qeth_ipa_cmd structure 2028efbbc1d5SEugene Crosser * for the IPA commands. 2029efbbc1d5SEugene Crosser * @reply_param: private pointer passed to the callback 2030efbbc1d5SEugene Crosser * 2031efbbc1d5SEugene Crosser * Returns the value of the `return_code' field of the response 2032efbbc1d5SEugene Crosser * block returned from the hardware, or other error indication. 2033efbbc1d5SEugene Crosser * Value of zero indicates successful execution of the command. 2034efbbc1d5SEugene Crosser * 2035efbbc1d5SEugene Crosser * Callback function gets called one or more times, with cb_cmd 2036efbbc1d5SEugene Crosser * pointing to the response returned by the hardware. Callback 2037efbbc1d5SEugene Crosser * function must return non-zero if more reply blocks are expected, 2038efbbc1d5SEugene Crosser * and zero if the last or only reply block is received. Callback 2039efbbc1d5SEugene Crosser * function can get the value of the reply_param pointer from the 2040efbbc1d5SEugene Crosser * field 'param' of the structure qeth_reply. 2041efbbc1d5SEugene Crosser */ 2042efbbc1d5SEugene Crosser 20434a71df50SFrank Blaschka int qeth_send_control_data(struct qeth_card *card, int len, 20444a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, 2045efbbc1d5SEugene Crosser int (*reply_cb)(struct qeth_card *cb_card, 2046efbbc1d5SEugene Crosser struct qeth_reply *cb_reply, 2047efbbc1d5SEugene Crosser unsigned long cb_cmd), 20484a71df50SFrank Blaschka void *reply_param) 20494a71df50SFrank Blaschka { 20504a71df50SFrank Blaschka int rc; 20514a71df50SFrank Blaschka unsigned long flags; 20524a71df50SFrank Blaschka struct qeth_reply *reply = NULL; 20537834cd5aSHeiko Carstens unsigned long timeout, event_timeout; 20545b54e16fSFrank Blaschka struct qeth_ipa_cmd *cmd; 20554a71df50SFrank Blaschka 2056847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "sendctl"); 20574a71df50SFrank Blaschka 2058908abbb5SUrsula Braun if (card->read_or_write_problem) { 2059908abbb5SUrsula Braun qeth_release_buffer(iob->channel, iob); 2060908abbb5SUrsula Braun return -EIO; 2061908abbb5SUrsula Braun } 20624a71df50SFrank Blaschka reply = qeth_alloc_reply(card); 20634a71df50SFrank Blaschka if (!reply) { 20644a71df50SFrank Blaschka return -ENOMEM; 20654a71df50SFrank Blaschka } 20664a71df50SFrank Blaschka reply->callback = reply_cb; 20674a71df50SFrank Blaschka reply->param = reply_param; 20684a71df50SFrank Blaschka if (card->state == CARD_STATE_DOWN) 20694a71df50SFrank Blaschka reply->seqno = QETH_IDX_COMMAND_SEQNO; 20704a71df50SFrank Blaschka else 20714a71df50SFrank Blaschka reply->seqno = card->seqno.ipa++; 20724a71df50SFrank Blaschka init_waitqueue_head(&reply->wait_q); 20734a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 20744a71df50SFrank Blaschka list_add_tail(&reply->list, &card->cmd_waiter_list); 20754a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 2076d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN); 20774a71df50SFrank Blaschka 20784a71df50SFrank Blaschka while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ; 20794a71df50SFrank Blaschka qeth_prepare_control_data(card, len, iob); 20804a71df50SFrank Blaschka 20814a71df50SFrank Blaschka if (IS_IPA(iob->data)) 20827834cd5aSHeiko Carstens event_timeout = QETH_IPA_TIMEOUT; 20834a71df50SFrank Blaschka else 20847834cd5aSHeiko Carstens event_timeout = QETH_TIMEOUT; 20857834cd5aSHeiko Carstens timeout = jiffies + event_timeout; 20864a71df50SFrank Blaschka 2087847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 20884a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags); 20894a71df50SFrank Blaschka rc = ccw_device_start(card->write.ccwdev, &card->write.ccw, 20904a71df50SFrank Blaschka (addr_t) iob, 0, 0); 20914a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags); 20924a71df50SFrank Blaschka if (rc) { 209374eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: " 209474eacdb9SFrank Blaschka "ccw_device_start rc = %i\n", 209574eacdb9SFrank Blaschka dev_name(&card->write.ccwdev->dev), rc); 2096847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " err%d", rc); 20974a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 20984a71df50SFrank Blaschka list_del_init(&reply->list); 20994a71df50SFrank Blaschka qeth_put_reply(reply); 21004a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 21014a71df50SFrank Blaschka qeth_release_buffer(iob->channel, iob); 21024a71df50SFrank Blaschka atomic_set(&card->write.irq_pending, 0); 21034a71df50SFrank Blaschka wake_up(&card->wait_q); 21044a71df50SFrank Blaschka return rc; 21054a71df50SFrank Blaschka } 21065b54e16fSFrank Blaschka 21075b54e16fSFrank Blaschka /* we have only one long running ipassist, since we can ensure 21085b54e16fSFrank Blaschka process context of this command we can sleep */ 21095b54e16fSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 21105b54e16fSFrank Blaschka if ((cmd->hdr.command == IPA_CMD_SETIP) && 21115b54e16fSFrank Blaschka (cmd->hdr.prot_version == QETH_PROT_IPV4)) { 21125b54e16fSFrank Blaschka if (!wait_event_timeout(reply->wait_q, 21137834cd5aSHeiko Carstens atomic_read(&reply->received), event_timeout)) 21145b54e16fSFrank Blaschka goto time_err; 21155b54e16fSFrank Blaschka } else { 21164a71df50SFrank Blaschka while (!atomic_read(&reply->received)) { 21175b54e16fSFrank Blaschka if (time_after(jiffies, timeout)) 21185b54e16fSFrank Blaschka goto time_err; 21195b54e16fSFrank Blaschka cpu_relax(); 21206531084cSPeter Senna Tschudin } 21215b54e16fSFrank Blaschka } 21225b54e16fSFrank Blaschka 212370919e23SUrsula Braun if (reply->rc == -EIO) 212470919e23SUrsula Braun goto error; 21255b54e16fSFrank Blaschka rc = reply->rc; 21265b54e16fSFrank Blaschka qeth_put_reply(reply); 21275b54e16fSFrank Blaschka return rc; 21285b54e16fSFrank Blaschka 21295b54e16fSFrank Blaschka time_err: 213070919e23SUrsula Braun reply->rc = -ETIME; 21314a71df50SFrank Blaschka spin_lock_irqsave(&reply->card->lock, flags); 21324a71df50SFrank Blaschka list_del_init(&reply->list); 21334a71df50SFrank Blaschka spin_unlock_irqrestore(&reply->card->lock, flags); 21344a71df50SFrank Blaschka atomic_inc(&reply->received); 213570919e23SUrsula Braun error: 2136908abbb5SUrsula Braun atomic_set(&card->write.irq_pending, 0); 2137908abbb5SUrsula Braun qeth_release_buffer(iob->channel, iob); 2138908abbb5SUrsula Braun card->write.buf_no = (card->write.buf_no + 1) % QETH_CMD_BUFFER_NO; 21394a71df50SFrank Blaschka rc = reply->rc; 21404a71df50SFrank Blaschka qeth_put_reply(reply); 21414a71df50SFrank Blaschka return rc; 21424a71df50SFrank Blaschka } 21434a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_control_data); 21444a71df50SFrank Blaschka 21454a71df50SFrank Blaschka static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 21464a71df50SFrank Blaschka unsigned long data) 21474a71df50SFrank Blaschka { 21484a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21494a71df50SFrank Blaschka 2150d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenblcb"); 21514a71df50SFrank Blaschka 21524a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 21534a71df50SFrank Blaschka memcpy(&card->token.cm_filter_r, 21544a71df50SFrank Blaschka QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data), 21554a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2156d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 21574a71df50SFrank Blaschka return 0; 21584a71df50SFrank Blaschka } 21594a71df50SFrank Blaschka 21604a71df50SFrank Blaschka static int qeth_cm_enable(struct qeth_card *card) 21614a71df50SFrank Blaschka { 21624a71df50SFrank Blaschka int rc; 21634a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21644a71df50SFrank Blaschka 2165d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenable"); 21664a71df50SFrank Blaschka 21674a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 21684a71df50SFrank Blaschka memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE); 21694a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data), 21704a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 21714a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data), 21724a71df50SFrank Blaschka &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH); 21734a71df50SFrank Blaschka 21744a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob, 21754a71df50SFrank Blaschka qeth_cm_enable_cb, NULL); 21764a71df50SFrank Blaschka return rc; 21774a71df50SFrank Blaschka } 21784a71df50SFrank Blaschka 21794a71df50SFrank Blaschka static int qeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 21804a71df50SFrank Blaschka unsigned long data) 21814a71df50SFrank Blaschka { 21824a71df50SFrank Blaschka 21834a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21844a71df50SFrank Blaschka 2185d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetpcb"); 21864a71df50SFrank Blaschka 21874a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 21884a71df50SFrank Blaschka memcpy(&card->token.cm_connection_r, 21894a71df50SFrank Blaschka QETH_CM_SETUP_RESP_DEST_ADDR(iob->data), 21904a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2191d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 21924a71df50SFrank Blaschka return 0; 21934a71df50SFrank Blaschka } 21944a71df50SFrank Blaschka 21954a71df50SFrank Blaschka static int qeth_cm_setup(struct qeth_card *card) 21964a71df50SFrank Blaschka { 21974a71df50SFrank Blaschka int rc; 21984a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21994a71df50SFrank Blaschka 2200d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetup"); 22014a71df50SFrank Blaschka 22024a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 22034a71df50SFrank Blaschka memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE); 22044a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data), 22054a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 22064a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data), 22074a71df50SFrank Blaschka &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH); 22084a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data), 22094a71df50SFrank Blaschka &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH); 22104a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob, 22114a71df50SFrank Blaschka qeth_cm_setup_cb, NULL); 22124a71df50SFrank Blaschka return rc; 22134a71df50SFrank Blaschka 22144a71df50SFrank Blaschka } 22154a71df50SFrank Blaschka 2216cef6ff22SJulian Wiedmann static int qeth_get_initial_mtu_for_card(struct qeth_card *card) 22174a71df50SFrank Blaschka { 22184a71df50SFrank Blaschka switch (card->info.type) { 22194a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 22204a71df50SFrank Blaschka return card->info.max_mtu; 22215113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 22225113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 22236e6f472dSJulian Wiedmann if (!card->options.layer2) 22246e6f472dSJulian Wiedmann return ETH_DATA_LEN - 8; /* L3: allow for LLC + SNAP */ 22256e6f472dSJulian Wiedmann /* fall through */ 22264a71df50SFrank Blaschka default: 22276e6f472dSJulian Wiedmann return ETH_DATA_LEN; 22284a71df50SFrank Blaschka } 22294a71df50SFrank Blaschka } 22304a71df50SFrank Blaschka 2231cef6ff22SJulian Wiedmann static int qeth_get_mtu_outof_framesize(int framesize) 22324a71df50SFrank Blaschka { 22334a71df50SFrank Blaschka switch (framesize) { 22344a71df50SFrank Blaschka case 0x4000: 22354a71df50SFrank Blaschka return 8192; 22364a71df50SFrank Blaschka case 0x6000: 22374a71df50SFrank Blaschka return 16384; 22384a71df50SFrank Blaschka case 0xa000: 22394a71df50SFrank Blaschka return 32768; 22404a71df50SFrank Blaschka case 0xffff: 22414a71df50SFrank Blaschka return 57344; 22424a71df50SFrank Blaschka default: 22434a71df50SFrank Blaschka return 0; 22444a71df50SFrank Blaschka } 22454a71df50SFrank Blaschka } 22464a71df50SFrank Blaschka 2247cef6ff22SJulian Wiedmann static int qeth_mtu_is_valid(struct qeth_card *card, int mtu) 22484a71df50SFrank Blaschka { 22494a71df50SFrank Blaschka switch (card->info.type) { 22505113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 22515113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 22525113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 22534a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 22544a71df50SFrank Blaschka return ((mtu >= 576) && 22559853b97bSFrank Blaschka (mtu <= card->info.max_mtu)); 22564a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 22574a71df50SFrank Blaschka default: 22584a71df50SFrank Blaschka return 1; 22594a71df50SFrank Blaschka } 22604a71df50SFrank Blaschka } 22614a71df50SFrank Blaschka 22624a71df50SFrank Blaschka static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 22634a71df50SFrank Blaschka unsigned long data) 22644a71df50SFrank Blaschka { 22654a71df50SFrank Blaschka 22664a71df50SFrank Blaschka __u16 mtu, framesize; 22674a71df50SFrank Blaschka __u16 len; 22684a71df50SFrank Blaschka __u8 link_type; 22694a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 22704a71df50SFrank Blaschka 2271d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenacb"); 22724a71df50SFrank Blaschka 22734a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 22744a71df50SFrank Blaschka memcpy(&card->token.ulp_filter_r, 22754a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data), 22764a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 22779853b97bSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) { 22784a71df50SFrank Blaschka memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); 22794a71df50SFrank Blaschka mtu = qeth_get_mtu_outof_framesize(framesize); 22804a71df50SFrank Blaschka if (!mtu) { 22814a71df50SFrank Blaschka iob->rc = -EINVAL; 2282d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 22834a71df50SFrank Blaschka return 0; 22844a71df50SFrank Blaschka } 22858b2e18f6SUrsula Braun if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { 22868b2e18f6SUrsula Braun /* frame size has changed */ 22878b2e18f6SUrsula Braun if (card->dev && 22888b2e18f6SUrsula Braun ((card->dev->mtu == card->info.initial_mtu) || 22898b2e18f6SUrsula Braun (card->dev->mtu > mtu))) 22908b2e18f6SUrsula Braun card->dev->mtu = mtu; 22918b2e18f6SUrsula Braun qeth_free_qdio_buffers(card); 22928b2e18f6SUrsula Braun } 22934a71df50SFrank Blaschka card->info.initial_mtu = mtu; 22948b2e18f6SUrsula Braun card->info.max_mtu = mtu; 22954a71df50SFrank Blaschka card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; 22964a71df50SFrank Blaschka } else { 22979853b97bSFrank Blaschka card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( 22989853b97bSFrank Blaschka iob->data); 2299fe44014aSStefan Raspl card->info.initial_mtu = min(card->info.max_mtu, 2300fe44014aSStefan Raspl qeth_get_initial_mtu_for_card(card)); 23014a71df50SFrank Blaschka card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 23024a71df50SFrank Blaschka } 23034a71df50SFrank Blaschka 23044a71df50SFrank Blaschka memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); 23054a71df50SFrank Blaschka if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { 23064a71df50SFrank Blaschka memcpy(&link_type, 23074a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1); 23084a71df50SFrank Blaschka card->info.link_type = link_type; 23094a71df50SFrank Blaschka } else 23104a71df50SFrank Blaschka card->info.link_type = 0; 231101fc3e86SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "link%d", card->info.link_type); 2312d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 23134a71df50SFrank Blaschka return 0; 23144a71df50SFrank Blaschka } 23154a71df50SFrank Blaschka 23164a71df50SFrank Blaschka static int qeth_ulp_enable(struct qeth_card *card) 23174a71df50SFrank Blaschka { 23184a71df50SFrank Blaschka int rc; 23194a71df50SFrank Blaschka char prot_type; 23204a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23214a71df50SFrank Blaschka 23224a71df50SFrank Blaschka /*FIXME: trace view callbacks*/ 2323d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenabl"); 23244a71df50SFrank Blaschka 23254a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 23264a71df50SFrank Blaschka memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); 23274a71df50SFrank Blaschka 23284a71df50SFrank Blaschka *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = 23294a71df50SFrank Blaschka (__u8) card->info.portno; 23304a71df50SFrank Blaschka if (card->options.layer2) 23314a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN) 23324a71df50SFrank Blaschka prot_type = QETH_PROT_OSN2; 23334a71df50SFrank Blaschka else 23344a71df50SFrank Blaschka prot_type = QETH_PROT_LAYER2; 23354a71df50SFrank Blaschka else 23364a71df50SFrank Blaschka prot_type = QETH_PROT_TCPIP; 23374a71df50SFrank Blaschka 23384a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data), &prot_type, 1); 23394a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data), 23404a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 23414a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), 23424a71df50SFrank Blaschka &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); 23434a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, 23444a71df50SFrank Blaschka qeth_ulp_enable_cb, NULL); 23454a71df50SFrank Blaschka return rc; 23464a71df50SFrank Blaschka 23474a71df50SFrank Blaschka } 23484a71df50SFrank Blaschka 23494a71df50SFrank Blaschka static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 23504a71df50SFrank Blaschka unsigned long data) 23514a71df50SFrank Blaschka { 23524a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23534a71df50SFrank Blaschka 2354d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpstpcb"); 23554a71df50SFrank Blaschka 23564a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 23574a71df50SFrank Blaschka memcpy(&card->token.ulp_connection_r, 23584a71df50SFrank Blaschka QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 23594a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 236065a1f898SUrsula Braun if (!strncmp("00S", QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 236165a1f898SUrsula Braun 3)) { 236265a1f898SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "olmlimit"); 236365a1f898SUrsula Braun dev_err(&card->gdev->dev, "A connection could not be " 236465a1f898SUrsula Braun "established because of an OLM limit\n"); 2365bbb822a8SUrsula Braun iob->rc = -EMLINK; 236665a1f898SUrsula Braun } 2367d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 23687bf9bcffSStefan Raspl return 0; 23694a71df50SFrank Blaschka } 23704a71df50SFrank Blaschka 23714a71df50SFrank Blaschka static int qeth_ulp_setup(struct qeth_card *card) 23724a71df50SFrank Blaschka { 23734a71df50SFrank Blaschka int rc; 23744a71df50SFrank Blaschka __u16 temp; 23754a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23764a71df50SFrank Blaschka struct ccw_dev_id dev_id; 23774a71df50SFrank Blaschka 2378d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpsetup"); 23794a71df50SFrank Blaschka 23804a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 23814a71df50SFrank Blaschka memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE); 23824a71df50SFrank Blaschka 23834a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data), 23844a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 23854a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data), 23864a71df50SFrank Blaschka &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH); 23874a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data), 23884a71df50SFrank Blaschka &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH); 23894a71df50SFrank Blaschka 23904a71df50SFrank Blaschka ccw_device_get_id(CARD_DDEV(card), &dev_id); 23914a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2); 23924a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 23934a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2); 23944a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob, 23954a71df50SFrank Blaschka qeth_ulp_setup_cb, NULL); 23964a71df50SFrank Blaschka return rc; 23974a71df50SFrank Blaschka } 23984a71df50SFrank Blaschka 23990da9581dSEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx) 24000da9581dSEinar Lueck { 24010da9581dSEinar Lueck int rc; 24020da9581dSEinar Lueck struct qeth_qdio_out_buffer *newbuf; 24030da9581dSEinar Lueck 24040da9581dSEinar Lueck rc = 0; 24050da9581dSEinar Lueck newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC); 24060da9581dSEinar Lueck if (!newbuf) { 24070da9581dSEinar Lueck rc = -ENOMEM; 24080da9581dSEinar Lueck goto out; 24090da9581dSEinar Lueck } 2410d445a4e2SSebastian Ott newbuf->buffer = q->qdio_bufs[bidx]; 24110da9581dSEinar Lueck skb_queue_head_init(&newbuf->skb_list); 24120da9581dSEinar Lueck lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); 24130da9581dSEinar Lueck newbuf->q = q; 24140da9581dSEinar Lueck newbuf->aob = NULL; 24150da9581dSEinar Lueck newbuf->next_pending = q->bufs[bidx]; 24160da9581dSEinar Lueck atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); 24170da9581dSEinar Lueck q->bufs[bidx] = newbuf; 24180da9581dSEinar Lueck if (q->bufstates) { 24190da9581dSEinar Lueck q->bufstates[bidx].user = newbuf; 24200da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "nbs%d", bidx); 24210da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "%lx", (long) newbuf); 24220da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "%lx", 24230da9581dSEinar Lueck (long) newbuf->next_pending); 24240da9581dSEinar Lueck } 24250da9581dSEinar Lueck out: 24260da9581dSEinar Lueck return rc; 24270da9581dSEinar Lueck } 24280da9581dSEinar Lueck 2429d445a4e2SSebastian Ott static void qeth_free_qdio_out_buf(struct qeth_qdio_out_q *q) 2430d445a4e2SSebastian Ott { 2431d445a4e2SSebastian Ott if (!q) 2432d445a4e2SSebastian Ott return; 2433d445a4e2SSebastian Ott 2434d445a4e2SSebastian Ott qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q); 2435d445a4e2SSebastian Ott kfree(q); 2436d445a4e2SSebastian Ott } 2437d445a4e2SSebastian Ott 2438d445a4e2SSebastian Ott static struct qeth_qdio_out_q *qeth_alloc_qdio_out_buf(void) 2439d445a4e2SSebastian Ott { 2440d445a4e2SSebastian Ott struct qeth_qdio_out_q *q = kzalloc(sizeof(*q), GFP_KERNEL); 2441d445a4e2SSebastian Ott 2442d445a4e2SSebastian Ott if (!q) 2443d445a4e2SSebastian Ott return NULL; 2444d445a4e2SSebastian Ott 2445d445a4e2SSebastian Ott if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) { 2446d445a4e2SSebastian Ott kfree(q); 2447d445a4e2SSebastian Ott return NULL; 2448d445a4e2SSebastian Ott } 2449d445a4e2SSebastian Ott return q; 2450d445a4e2SSebastian Ott } 24510da9581dSEinar Lueck 24524a71df50SFrank Blaschka static int qeth_alloc_qdio_buffers(struct qeth_card *card) 24534a71df50SFrank Blaschka { 24544a71df50SFrank Blaschka int i, j; 24554a71df50SFrank Blaschka 2456d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "allcqdbf"); 24574a71df50SFrank Blaschka 24584a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED, 24594a71df50SFrank Blaschka QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED) 24604a71df50SFrank Blaschka return 0; 24614a71df50SFrank Blaschka 24624601ba6cSSebastian Ott QETH_DBF_TEXT(SETUP, 2, "inq"); 24634601ba6cSSebastian Ott card->qdio.in_q = qeth_alloc_qdio_queue(); 24644a71df50SFrank Blaschka if (!card->qdio.in_q) 24654a71df50SFrank Blaschka goto out_nomem; 24664601ba6cSSebastian Ott 24674a71df50SFrank Blaschka /* inbound buffer pool */ 24684a71df50SFrank Blaschka if (qeth_alloc_buffer_pool(card)) 24694a71df50SFrank Blaschka goto out_freeinq; 24700da9581dSEinar Lueck 24714a71df50SFrank Blaschka /* outbound */ 24724a71df50SFrank Blaschka card->qdio.out_qs = 2473b3332930SFrank Blaschka kzalloc(card->qdio.no_out_queues * 24744a71df50SFrank Blaschka sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); 24754a71df50SFrank Blaschka if (!card->qdio.out_qs) 24764a71df50SFrank Blaschka goto out_freepool; 24774a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 2478d445a4e2SSebastian Ott card->qdio.out_qs[i] = qeth_alloc_qdio_out_buf(); 24794a71df50SFrank Blaschka if (!card->qdio.out_qs[i]) 24804a71df50SFrank Blaschka goto out_freeoutq; 2481d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "outq %i", i); 2482d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *)); 24834a71df50SFrank Blaschka card->qdio.out_qs[i]->queue_no = i; 24844a71df50SFrank Blaschka /* give outbound qeth_qdio_buffers their qdio_buffers */ 24854a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 248618af5c17SStefan Raspl WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL); 24870da9581dSEinar Lueck if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j)) 24880da9581dSEinar Lueck goto out_freeoutqbufs; 24894a71df50SFrank Blaschka } 24904a71df50SFrank Blaschka } 24910da9581dSEinar Lueck 24920da9581dSEinar Lueck /* completion */ 24930da9581dSEinar Lueck if (qeth_alloc_cq(card)) 24940da9581dSEinar Lueck goto out_freeoutq; 24950da9581dSEinar Lueck 24964a71df50SFrank Blaschka return 0; 24974a71df50SFrank Blaschka 24980da9581dSEinar Lueck out_freeoutqbufs: 24990da9581dSEinar Lueck while (j > 0) { 25000da9581dSEinar Lueck --j; 25010da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, 25020da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]); 25030da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j] = NULL; 25040da9581dSEinar Lueck } 25054a71df50SFrank Blaschka out_freeoutq: 25060da9581dSEinar Lueck while (i > 0) { 2507d445a4e2SSebastian Ott qeth_free_qdio_out_buf(card->qdio.out_qs[--i]); 25080da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 25090da9581dSEinar Lueck } 25104a71df50SFrank Blaschka kfree(card->qdio.out_qs); 25114a71df50SFrank Blaschka card->qdio.out_qs = NULL; 25124a71df50SFrank Blaschka out_freepool: 25134a71df50SFrank Blaschka qeth_free_buffer_pool(card); 25144a71df50SFrank Blaschka out_freeinq: 25154601ba6cSSebastian Ott qeth_free_qdio_queue(card->qdio.in_q); 25164a71df50SFrank Blaschka card->qdio.in_q = NULL; 25174a71df50SFrank Blaschka out_nomem: 25184a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 25194a71df50SFrank Blaschka return -ENOMEM; 25204a71df50SFrank Blaschka } 25214a71df50SFrank Blaschka 2522d445a4e2SSebastian Ott static void qeth_free_qdio_buffers(struct qeth_card *card) 2523d445a4e2SSebastian Ott { 2524d445a4e2SSebastian Ott int i, j; 2525d445a4e2SSebastian Ott 2526d445a4e2SSebastian Ott if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) == 2527d445a4e2SSebastian Ott QETH_QDIO_UNINITIALIZED) 2528d445a4e2SSebastian Ott return; 2529d445a4e2SSebastian Ott 2530d445a4e2SSebastian Ott qeth_free_cq(card); 2531d445a4e2SSebastian Ott cancel_delayed_work_sync(&card->buffer_reclaim_work); 2532d445a4e2SSebastian Ott for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 2533d445a4e2SSebastian Ott if (card->qdio.in_q->bufs[j].rx_skb) 2534d445a4e2SSebastian Ott dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); 2535d445a4e2SSebastian Ott } 2536d445a4e2SSebastian Ott qeth_free_qdio_queue(card->qdio.in_q); 2537d445a4e2SSebastian Ott card->qdio.in_q = NULL; 2538d445a4e2SSebastian Ott /* inbound buffer pool */ 2539d445a4e2SSebastian Ott qeth_free_buffer_pool(card); 2540d445a4e2SSebastian Ott /* free outbound qdio_qs */ 2541d445a4e2SSebastian Ott if (card->qdio.out_qs) { 2542d445a4e2SSebastian Ott for (i = 0; i < card->qdio.no_out_queues; ++i) { 2543d445a4e2SSebastian Ott qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 2544d445a4e2SSebastian Ott qeth_free_qdio_out_buf(card->qdio.out_qs[i]); 2545d445a4e2SSebastian Ott } 2546d445a4e2SSebastian Ott kfree(card->qdio.out_qs); 2547d445a4e2SSebastian Ott card->qdio.out_qs = NULL; 2548d445a4e2SSebastian Ott } 2549d445a4e2SSebastian Ott } 2550d445a4e2SSebastian Ott 25514a71df50SFrank Blaschka static void qeth_create_qib_param_field(struct qeth_card *card, 25524a71df50SFrank Blaschka char *param_field) 25534a71df50SFrank Blaschka { 25544a71df50SFrank Blaschka 25554a71df50SFrank Blaschka param_field[0] = _ascebc['P']; 25564a71df50SFrank Blaschka param_field[1] = _ascebc['C']; 25574a71df50SFrank Blaschka param_field[2] = _ascebc['I']; 25584a71df50SFrank Blaschka param_field[3] = _ascebc['T']; 25594a71df50SFrank Blaschka *((unsigned int *) (¶m_field[4])) = QETH_PCI_THRESHOLD_A(card); 25604a71df50SFrank Blaschka *((unsigned int *) (¶m_field[8])) = QETH_PCI_THRESHOLD_B(card); 25614a71df50SFrank Blaschka *((unsigned int *) (¶m_field[12])) = QETH_PCI_TIMER_VALUE(card); 25624a71df50SFrank Blaschka } 25634a71df50SFrank Blaschka 25644a71df50SFrank Blaschka static void qeth_create_qib_param_field_blkt(struct qeth_card *card, 25654a71df50SFrank Blaschka char *param_field) 25664a71df50SFrank Blaschka { 25674a71df50SFrank Blaschka param_field[16] = _ascebc['B']; 25684a71df50SFrank Blaschka param_field[17] = _ascebc['L']; 25694a71df50SFrank Blaschka param_field[18] = _ascebc['K']; 25704a71df50SFrank Blaschka param_field[19] = _ascebc['T']; 25714a71df50SFrank Blaschka *((unsigned int *) (¶m_field[20])) = card->info.blkt.time_total; 25724a71df50SFrank Blaschka *((unsigned int *) (¶m_field[24])) = card->info.blkt.inter_packet; 25734a71df50SFrank Blaschka *((unsigned int *) (¶m_field[28])) = 25744a71df50SFrank Blaschka card->info.blkt.inter_packet_jumbo; 25754a71df50SFrank Blaschka } 25764a71df50SFrank Blaschka 25774a71df50SFrank Blaschka static int qeth_qdio_activate(struct qeth_card *card) 25784a71df50SFrank Blaschka { 2579d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 3, "qdioact"); 2580779e6e1cSJan Glauber return qdio_activate(CARD_DDEV(card)); 25814a71df50SFrank Blaschka } 25824a71df50SFrank Blaschka 25834a71df50SFrank Blaschka static int qeth_dm_act(struct qeth_card *card) 25844a71df50SFrank Blaschka { 25854a71df50SFrank Blaschka int rc; 25864a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 25874a71df50SFrank Blaschka 2588d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "dmact"); 25894a71df50SFrank Blaschka 25904a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 25914a71df50SFrank Blaschka memcpy(iob->data, DM_ACT, DM_ACT_SIZE); 25924a71df50SFrank Blaschka 25934a71df50SFrank Blaschka memcpy(QETH_DM_ACT_DEST_ADDR(iob->data), 25944a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 25954a71df50SFrank Blaschka memcpy(QETH_DM_ACT_CONNECTION_TOKEN(iob->data), 25964a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 25974a71df50SFrank Blaschka rc = qeth_send_control_data(card, DM_ACT_SIZE, iob, NULL, NULL); 25984a71df50SFrank Blaschka return rc; 25994a71df50SFrank Blaschka } 26004a71df50SFrank Blaschka 26014a71df50SFrank Blaschka static int qeth_mpc_initialize(struct qeth_card *card) 26024a71df50SFrank Blaschka { 26034a71df50SFrank Blaschka int rc; 26044a71df50SFrank Blaschka 2605d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "mpcinit"); 26064a71df50SFrank Blaschka 26074a71df50SFrank Blaschka rc = qeth_issue_next_read(card); 26084a71df50SFrank Blaschka if (rc) { 2609d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 26104a71df50SFrank Blaschka return rc; 26114a71df50SFrank Blaschka } 26124a71df50SFrank Blaschka rc = qeth_cm_enable(card); 26134a71df50SFrank Blaschka if (rc) { 2614d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 26154a71df50SFrank Blaschka goto out_qdio; 26164a71df50SFrank Blaschka } 26174a71df50SFrank Blaschka rc = qeth_cm_setup(card); 26184a71df50SFrank Blaschka if (rc) { 2619d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 26204a71df50SFrank Blaschka goto out_qdio; 26214a71df50SFrank Blaschka } 26224a71df50SFrank Blaschka rc = qeth_ulp_enable(card); 26234a71df50SFrank Blaschka if (rc) { 2624d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 26254a71df50SFrank Blaschka goto out_qdio; 26264a71df50SFrank Blaschka } 26274a71df50SFrank Blaschka rc = qeth_ulp_setup(card); 26284a71df50SFrank Blaschka if (rc) { 2629d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 26304a71df50SFrank Blaschka goto out_qdio; 26314a71df50SFrank Blaschka } 26324a71df50SFrank Blaschka rc = qeth_alloc_qdio_buffers(card); 26334a71df50SFrank Blaschka if (rc) { 2634d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 26354a71df50SFrank Blaschka goto out_qdio; 26364a71df50SFrank Blaschka } 26374a71df50SFrank Blaschka rc = qeth_qdio_establish(card); 26384a71df50SFrank Blaschka if (rc) { 2639d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 26404a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 26414a71df50SFrank Blaschka goto out_qdio; 26424a71df50SFrank Blaschka } 26434a71df50SFrank Blaschka rc = qeth_qdio_activate(card); 26444a71df50SFrank Blaschka if (rc) { 2645d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); 26464a71df50SFrank Blaschka goto out_qdio; 26474a71df50SFrank Blaschka } 26484a71df50SFrank Blaschka rc = qeth_dm_act(card); 26494a71df50SFrank Blaschka if (rc) { 2650d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); 26514a71df50SFrank Blaschka goto out_qdio; 26524a71df50SFrank Blaschka } 26534a71df50SFrank Blaschka 26544a71df50SFrank Blaschka return 0; 26554a71df50SFrank Blaschka out_qdio: 26564a71df50SFrank Blaschka qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 265722ae2790SUrsula Braun qdio_free(CARD_DDEV(card)); 26584a71df50SFrank Blaschka return rc; 26594a71df50SFrank Blaschka } 26604a71df50SFrank Blaschka 26614a71df50SFrank Blaschka void qeth_print_status_message(struct qeth_card *card) 26624a71df50SFrank Blaschka { 26634a71df50SFrank Blaschka switch (card->info.type) { 26645113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 26655113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 26665113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 26674a71df50SFrank Blaschka /* VM will use a non-zero first character 26684a71df50SFrank Blaschka * to indicate a HiperSockets like reporting 26694a71df50SFrank Blaschka * of the level OSA sets the first character to zero 26704a71df50SFrank Blaschka * */ 26714a71df50SFrank Blaschka if (!card->info.mcl_level[0]) { 26724a71df50SFrank Blaschka sprintf(card->info.mcl_level, "%02x%02x", 26734a71df50SFrank Blaschka card->info.mcl_level[2], 26744a71df50SFrank Blaschka card->info.mcl_level[3]); 26754a71df50SFrank Blaschka break; 26764a71df50SFrank Blaschka } 26774a71df50SFrank Blaschka /* fallthrough */ 26784a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 2679906f1f07SKlaus-Dieter Wacker if ((card->info.guestlan) || 2680906f1f07SKlaus-Dieter Wacker (card->info.mcl_level[0] & 0x80)) { 26814a71df50SFrank Blaschka card->info.mcl_level[0] = (char) _ebcasc[(__u8) 26824a71df50SFrank Blaschka card->info.mcl_level[0]]; 26834a71df50SFrank Blaschka card->info.mcl_level[1] = (char) _ebcasc[(__u8) 26844a71df50SFrank Blaschka card->info.mcl_level[1]]; 26854a71df50SFrank Blaschka card->info.mcl_level[2] = (char) _ebcasc[(__u8) 26864a71df50SFrank Blaschka card->info.mcl_level[2]]; 26874a71df50SFrank Blaschka card->info.mcl_level[3] = (char) _ebcasc[(__u8) 26884a71df50SFrank Blaschka card->info.mcl_level[3]]; 26894a71df50SFrank Blaschka card->info.mcl_level[QETH_MCL_LENGTH] = 0; 26904a71df50SFrank Blaschka } 26914a71df50SFrank Blaschka break; 26924a71df50SFrank Blaschka default: 26934a71df50SFrank Blaschka memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); 26944a71df50SFrank Blaschka } 2695239ff408SUrsula Braun dev_info(&card->gdev->dev, 2696239ff408SUrsula Braun "Device is a%s card%s%s%s\nwith link type %s.\n", 2697239ff408SUrsula Braun qeth_get_cardname(card), 2698239ff408SUrsula Braun (card->info.mcl_level[0]) ? " (level: " : "", 2699239ff408SUrsula Braun (card->info.mcl_level[0]) ? card->info.mcl_level : "", 2700239ff408SUrsula Braun (card->info.mcl_level[0]) ? ")" : "", 2701239ff408SUrsula Braun qeth_get_cardname_short(card)); 27024a71df50SFrank Blaschka } 27034a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_print_status_message); 27044a71df50SFrank Blaschka 27054a71df50SFrank Blaschka static void qeth_initialize_working_pool_list(struct qeth_card *card) 27064a71df50SFrank Blaschka { 27074a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 27084a71df50SFrank Blaschka 2709847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "inwrklst"); 27104a71df50SFrank Blaschka 27114a71df50SFrank Blaschka list_for_each_entry(entry, 27124a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list) { 27134a71df50SFrank Blaschka qeth_put_buffer_pool_entry(card, entry); 27144a71df50SFrank Blaschka } 27154a71df50SFrank Blaschka } 27164a71df50SFrank Blaschka 2717cef6ff22SJulian Wiedmann static struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry( 27184a71df50SFrank Blaschka struct qeth_card *card) 27194a71df50SFrank Blaschka { 27204a71df50SFrank Blaschka struct list_head *plh; 27214a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 27224a71df50SFrank Blaschka int i, free; 27234a71df50SFrank Blaschka struct page *page; 27244a71df50SFrank Blaschka 27254a71df50SFrank Blaschka if (list_empty(&card->qdio.in_buf_pool.entry_list)) 27264a71df50SFrank Blaschka return NULL; 27274a71df50SFrank Blaschka 27284a71df50SFrank Blaschka list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { 27294a71df50SFrank Blaschka entry = list_entry(plh, struct qeth_buffer_pool_entry, list); 27304a71df50SFrank Blaschka free = 1; 27314a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27324a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 27334a71df50SFrank Blaschka free = 0; 27344a71df50SFrank Blaschka break; 27354a71df50SFrank Blaschka } 27364a71df50SFrank Blaschka } 27374a71df50SFrank Blaschka if (free) { 27384a71df50SFrank Blaschka list_del_init(&entry->list); 27394a71df50SFrank Blaschka return entry; 27404a71df50SFrank Blaschka } 27414a71df50SFrank Blaschka } 27424a71df50SFrank Blaschka 27434a71df50SFrank Blaschka /* no free buffer in pool so take first one and swap pages */ 27444a71df50SFrank Blaschka entry = list_entry(card->qdio.in_buf_pool.entry_list.next, 27454a71df50SFrank Blaschka struct qeth_buffer_pool_entry, list); 27464a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27474a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 2748508b3c4fSUrsula Braun page = alloc_page(GFP_ATOMIC); 27494a71df50SFrank Blaschka if (!page) { 27504a71df50SFrank Blaschka return NULL; 27514a71df50SFrank Blaschka } else { 27524a71df50SFrank Blaschka free_page((unsigned long)entry->elements[i]); 27534a71df50SFrank Blaschka entry->elements[i] = page_address(page); 27544a71df50SFrank Blaschka if (card->options.performance_stats) 27554a71df50SFrank Blaschka card->perf_stats.sg_alloc_page_rx++; 27564a71df50SFrank Blaschka } 27574a71df50SFrank Blaschka } 27584a71df50SFrank Blaschka } 27594a71df50SFrank Blaschka list_del_init(&entry->list); 27604a71df50SFrank Blaschka return entry; 27614a71df50SFrank Blaschka } 27624a71df50SFrank Blaschka 27634a71df50SFrank Blaschka static int qeth_init_input_buffer(struct qeth_card *card, 27644a71df50SFrank Blaschka struct qeth_qdio_buffer *buf) 27654a71df50SFrank Blaschka { 27664a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 27674a71df50SFrank Blaschka int i; 27684a71df50SFrank Blaschka 2769b3332930SFrank Blaschka if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) { 2770b3332930SFrank Blaschka buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); 2771b3332930SFrank Blaschka if (!buf->rx_skb) 2772b3332930SFrank Blaschka return 1; 2773b3332930SFrank Blaschka } 2774b3332930SFrank Blaschka 27754a71df50SFrank Blaschka pool_entry = qeth_find_free_buffer_pool_entry(card); 27764a71df50SFrank Blaschka if (!pool_entry) 27774a71df50SFrank Blaschka return 1; 27784a71df50SFrank Blaschka 27794a71df50SFrank Blaschka /* 27804a71df50SFrank Blaschka * since the buffer is accessed only from the input_tasklet 27814a71df50SFrank Blaschka * there shouldn't be a need to synchronize; also, since we use 27824a71df50SFrank Blaschka * the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off 27834a71df50SFrank Blaschka * buffers 27844a71df50SFrank Blaschka */ 27854a71df50SFrank Blaschka 27864a71df50SFrank Blaschka buf->pool_entry = pool_entry; 27874a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27884a71df50SFrank Blaschka buf->buffer->element[i].length = PAGE_SIZE; 27894a71df50SFrank Blaschka buf->buffer->element[i].addr = pool_entry->elements[i]; 27904a71df50SFrank Blaschka if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1) 27913ec90878SJan Glauber buf->buffer->element[i].eflags = SBAL_EFLAGS_LAST_ENTRY; 27924a71df50SFrank Blaschka else 27933ec90878SJan Glauber buf->buffer->element[i].eflags = 0; 27943ec90878SJan Glauber buf->buffer->element[i].sflags = 0; 27954a71df50SFrank Blaschka } 27964a71df50SFrank Blaschka return 0; 27974a71df50SFrank Blaschka } 27984a71df50SFrank Blaschka 27994a71df50SFrank Blaschka int qeth_init_qdio_queues(struct qeth_card *card) 28004a71df50SFrank Blaschka { 28014a71df50SFrank Blaschka int i, j; 28024a71df50SFrank Blaschka int rc; 28034a71df50SFrank Blaschka 2804d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "initqdqs"); 28054a71df50SFrank Blaschka 28064a71df50SFrank Blaschka /* inbound queue */ 28076d284bdeSSebastian Ott qdio_reset_buffers(card->qdio.in_q->qdio_bufs, 28086d284bdeSSebastian Ott QDIO_MAX_BUFFERS_PER_Q); 28094a71df50SFrank Blaschka qeth_initialize_working_pool_list(card); 28104a71df50SFrank Blaschka /*give only as many buffers to hardware as we have buffer pool entries*/ 28114a71df50SFrank Blaschka for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++i) 28124a71df50SFrank Blaschka qeth_init_input_buffer(card, &card->qdio.in_q->bufs[i]); 28134a71df50SFrank Blaschka card->qdio.in_q->next_buf_to_init = 28144a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 1; 28154a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, 2816779e6e1cSJan Glauber card->qdio.in_buf_pool.buf_count - 1); 28174a71df50SFrank Blaschka if (rc) { 2818d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 28194a71df50SFrank Blaschka return rc; 28204a71df50SFrank Blaschka } 28210da9581dSEinar Lueck 28220da9581dSEinar Lueck /* completion */ 28230da9581dSEinar Lueck rc = qeth_cq_init(card); 28240da9581dSEinar Lueck if (rc) { 28250da9581dSEinar Lueck return rc; 28260da9581dSEinar Lueck } 28270da9581dSEinar Lueck 28284a71df50SFrank Blaschka /* outbound queue */ 28294a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 2830d445a4e2SSebastian Ott qdio_reset_buffers(card->qdio.out_qs[i]->qdio_bufs, 2831d445a4e2SSebastian Ott QDIO_MAX_BUFFERS_PER_Q); 28324a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 28334a71df50SFrank Blaschka qeth_clear_output_buffer(card->qdio.out_qs[i], 28340da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j], 28350da9581dSEinar Lueck QETH_QDIO_BUF_EMPTY); 28364a71df50SFrank Blaschka } 28374a71df50SFrank Blaschka card->qdio.out_qs[i]->card = card; 28384a71df50SFrank Blaschka card->qdio.out_qs[i]->next_buf_to_fill = 0; 28394a71df50SFrank Blaschka card->qdio.out_qs[i]->do_pack = 0; 28404a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->used_buffers, 0); 28414a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->set_pci_flags_count, 0); 28424a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->state, 28434a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 28444a71df50SFrank Blaschka } 28454a71df50SFrank Blaschka return 0; 28464a71df50SFrank Blaschka } 28474a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_init_qdio_queues); 28484a71df50SFrank Blaschka 2849cef6ff22SJulian Wiedmann static __u8 qeth_get_ipa_adp_type(enum qeth_link_types link_type) 28504a71df50SFrank Blaschka { 28514a71df50SFrank Blaschka switch (link_type) { 28524a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 28534a71df50SFrank Blaschka return 2; 28544a71df50SFrank Blaschka default: 28554a71df50SFrank Blaschka return 1; 28564a71df50SFrank Blaschka } 28574a71df50SFrank Blaschka } 28584a71df50SFrank Blaschka 28594a71df50SFrank Blaschka static void qeth_fill_ipacmd_header(struct qeth_card *card, 28604a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd, __u8 command, 28614a71df50SFrank Blaschka enum qeth_prot_versions prot) 28624a71df50SFrank Blaschka { 28634a71df50SFrank Blaschka memset(cmd, 0, sizeof(struct qeth_ipa_cmd)); 28644a71df50SFrank Blaschka cmd->hdr.command = command; 28654a71df50SFrank Blaschka cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; 28664a71df50SFrank Blaschka cmd->hdr.seqno = card->seqno.ipa; 28674a71df50SFrank Blaschka cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); 28684a71df50SFrank Blaschka cmd->hdr.rel_adapter_no = (__u8) card->info.portno; 28694a71df50SFrank Blaschka if (card->options.layer2) 28704a71df50SFrank Blaschka cmd->hdr.prim_version_no = 2; 28714a71df50SFrank Blaschka else 28724a71df50SFrank Blaschka cmd->hdr.prim_version_no = 1; 28734a71df50SFrank Blaschka cmd->hdr.param_count = 1; 28744a71df50SFrank Blaschka cmd->hdr.prot_version = prot; 28754a71df50SFrank Blaschka cmd->hdr.ipa_supported = 0; 28764a71df50SFrank Blaschka cmd->hdr.ipa_enabled = 0; 28774a71df50SFrank Blaschka } 28784a71df50SFrank Blaschka 28794a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card, 28804a71df50SFrank Blaschka enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot) 28814a71df50SFrank Blaschka { 28824a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 28834a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 28844a71df50SFrank Blaschka 28851aec42bcSThomas Richter iob = qeth_get_buffer(&card->write); 28861aec42bcSThomas Richter if (iob) { 28874a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 28884a71df50SFrank Blaschka qeth_fill_ipacmd_header(card, cmd, ipacmd, prot); 28891aec42bcSThomas Richter } else { 28901aec42bcSThomas Richter dev_warn(&card->gdev->dev, 28911aec42bcSThomas Richter "The qeth driver ran out of channel command buffers\n"); 28921aec42bcSThomas Richter QETH_DBF_MESSAGE(1, "%s The qeth driver ran out of channel command buffers", 28931aec42bcSThomas Richter dev_name(&card->gdev->dev)); 28941aec42bcSThomas Richter } 28954a71df50SFrank Blaschka 28964a71df50SFrank Blaschka return iob; 28974a71df50SFrank Blaschka } 28984a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer); 28994a71df50SFrank Blaschka 29004a71df50SFrank Blaschka void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, 29014a71df50SFrank Blaschka char prot_type) 29024a71df50SFrank Blaschka { 29034a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 29044a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1); 29054a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 29064a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 29074a71df50SFrank Blaschka } 29084a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd); 29094a71df50SFrank Blaschka 2910efbbc1d5SEugene Crosser /** 2911efbbc1d5SEugene Crosser * qeth_send_ipa_cmd() - send an IPA command 2912efbbc1d5SEugene Crosser * 2913efbbc1d5SEugene Crosser * See qeth_send_control_data() for explanation of the arguments. 2914efbbc1d5SEugene Crosser */ 2915efbbc1d5SEugene Crosser 29164a71df50SFrank Blaschka int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, 29174a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply*, 29184a71df50SFrank Blaschka unsigned long), 29194a71df50SFrank Blaschka void *reply_param) 29204a71df50SFrank Blaschka { 29214a71df50SFrank Blaschka int rc; 29224a71df50SFrank Blaschka char prot_type; 29234a71df50SFrank Blaschka 2924847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendipa"); 29254a71df50SFrank Blaschka 29264a71df50SFrank Blaschka if (card->options.layer2) 29274a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN) 29284a71df50SFrank Blaschka prot_type = QETH_PROT_OSN2; 29294a71df50SFrank Blaschka else 29304a71df50SFrank Blaschka prot_type = QETH_PROT_LAYER2; 29314a71df50SFrank Blaschka else 29324a71df50SFrank Blaschka prot_type = QETH_PROT_TCPIP; 29334a71df50SFrank Blaschka qeth_prepare_ipa_cmd(card, iob, prot_type); 2934d11ba0c4SPeter Tiedemann rc = qeth_send_control_data(card, IPA_CMD_LENGTH, 2935d11ba0c4SPeter Tiedemann iob, reply_cb, reply_param); 2936908abbb5SUrsula Braun if (rc == -ETIME) { 2937908abbb5SUrsula Braun qeth_clear_ipacmd_list(card); 2938908abbb5SUrsula Braun qeth_schedule_recovery(card); 2939908abbb5SUrsula Braun } 29404a71df50SFrank Blaschka return rc; 29414a71df50SFrank Blaschka } 29424a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd); 29434a71df50SFrank Blaschka 294410340510SJulian Wiedmann static int qeth_send_startlan(struct qeth_card *card) 29454a71df50SFrank Blaschka { 29464a71df50SFrank Blaschka int rc; 29474a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29484a71df50SFrank Blaschka 2949d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "strtlan"); 29504a71df50SFrank Blaschka 295170919e23SUrsula Braun iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0); 29521aec42bcSThomas Richter if (!iob) 29531aec42bcSThomas Richter return -ENOMEM; 295470919e23SUrsula Braun rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); 29554a71df50SFrank Blaschka return rc; 29564a71df50SFrank Blaschka } 29574a71df50SFrank Blaschka 2958eb3fb0baSStefan Raspl static int qeth_default_setadapterparms_cb(struct qeth_card *card, 29594a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 29604a71df50SFrank Blaschka { 29614a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29624a71df50SFrank Blaschka 2963847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "defadpcb"); 29644a71df50SFrank Blaschka 29654a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 29664a71df50SFrank Blaschka if (cmd->hdr.return_code == 0) 29674a71df50SFrank Blaschka cmd->hdr.return_code = 29684a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 29694a71df50SFrank Blaschka return 0; 29704a71df50SFrank Blaschka } 29714a71df50SFrank Blaschka 29724a71df50SFrank Blaschka static int qeth_query_setadapterparms_cb(struct qeth_card *card, 29734a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 29744a71df50SFrank Blaschka { 29754a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29764a71df50SFrank Blaschka 2977847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "quyadpcb"); 29784a71df50SFrank Blaschka 29794a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 29805113fec0SUrsula Braun if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) { 29814a71df50SFrank Blaschka card->info.link_type = 29824a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.lan_type; 29835113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lnk %d", card->info.link_type); 29845113fec0SUrsula Braun } 29854a71df50SFrank Blaschka card->options.adp.supported_funcs = 29864a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; 29874a71df50SFrank Blaschka return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); 29884a71df50SFrank Blaschka } 29894a71df50SFrank Blaschka 2990eb3fb0baSStefan Raspl static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, 29914a71df50SFrank Blaschka __u32 command, __u32 cmdlen) 29924a71df50SFrank Blaschka { 29934a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29944a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29954a71df50SFrank Blaschka 29964a71df50SFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETADAPTERPARMS, 29974a71df50SFrank Blaschka QETH_PROT_IPV4); 29981aec42bcSThomas Richter if (iob) { 29994a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 30004a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.cmdlength = cmdlen; 30014a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.command_code = command; 30024a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total = 1; 30034a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no = 1; 30041aec42bcSThomas Richter } 30054a71df50SFrank Blaschka 30064a71df50SFrank Blaschka return iob; 30074a71df50SFrank Blaschka } 30084a71df50SFrank Blaschka 30094a71df50SFrank Blaschka int qeth_query_setadapterparms(struct qeth_card *card) 30104a71df50SFrank Blaschka { 30114a71df50SFrank Blaschka int rc; 30124a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 30134a71df50SFrank Blaschka 3014847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "queryadp"); 30154a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED, 30164a71df50SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms)); 30171aec42bcSThomas Richter if (!iob) 30181aec42bcSThomas Richter return -ENOMEM; 30194a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); 30204a71df50SFrank Blaschka return rc; 30214a71df50SFrank Blaschka } 30224a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_query_setadapterparms); 30234a71df50SFrank Blaschka 30241da74b1cSFrank Blaschka static int qeth_query_ipassists_cb(struct qeth_card *card, 30251da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 30261da74b1cSFrank Blaschka { 30271da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 30281da74b1cSFrank Blaschka 30291da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qipasscb"); 30301da74b1cSFrank Blaschka 30311da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 3032a134884aSStefan Raspl 3033a134884aSStefan Raspl switch (cmd->hdr.return_code) { 3034a134884aSStefan Raspl case IPA_RC_NOTSUPP: 3035a134884aSStefan Raspl case IPA_RC_L2_UNSUPPORTED_CMD: 3036a134884aSStefan Raspl QETH_DBF_TEXT(SETUP, 2, "ipaunsup"); 3037a134884aSStefan Raspl card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; 3038a134884aSStefan Raspl card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; 3039a134884aSStefan Raspl return -0; 3040a134884aSStefan Raspl default: 3041a134884aSStefan Raspl if (cmd->hdr.return_code) { 3042a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled " 3043a134884aSStefan Raspl "rc=%d\n", 3044a134884aSStefan Raspl dev_name(&card->gdev->dev), 3045a134884aSStefan Raspl cmd->hdr.return_code); 3046a134884aSStefan Raspl return 0; 3047a134884aSStefan Raspl } 3048a134884aSStefan Raspl } 3049a134884aSStefan Raspl 30501da74b1cSFrank Blaschka if (cmd->hdr.prot_version == QETH_PROT_IPV4) { 30511da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; 30521da74b1cSFrank Blaschka card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; 3053a134884aSStefan Raspl } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { 30541da74b1cSFrank Blaschka card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; 30551da74b1cSFrank Blaschka card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; 3056a134884aSStefan Raspl } else 3057a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected" 3058a134884aSStefan Raspl "\n", dev_name(&card->gdev->dev)); 30591da74b1cSFrank Blaschka return 0; 30601da74b1cSFrank Blaschka } 30611da74b1cSFrank Blaschka 30621da74b1cSFrank Blaschka int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) 30631da74b1cSFrank Blaschka { 30641da74b1cSFrank Blaschka int rc; 30651da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 30661da74b1cSFrank Blaschka 30671da74b1cSFrank Blaschka QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); 30681da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); 30691aec42bcSThomas Richter if (!iob) 30701aec42bcSThomas Richter return -ENOMEM; 30711da74b1cSFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); 30721da74b1cSFrank Blaschka return rc; 30731da74b1cSFrank Blaschka } 30741da74b1cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_query_ipassists); 30751da74b1cSFrank Blaschka 307645cbb2e4SStefan Raspl static int qeth_query_switch_attributes_cb(struct qeth_card *card, 307745cbb2e4SStefan Raspl struct qeth_reply *reply, unsigned long data) 307845cbb2e4SStefan Raspl { 307945cbb2e4SStefan Raspl struct qeth_ipa_cmd *cmd; 308045cbb2e4SStefan Raspl struct qeth_switch_info *sw_info; 308145cbb2e4SStefan Raspl struct qeth_query_switch_attributes *attrs; 308245cbb2e4SStefan Raspl 308345cbb2e4SStefan Raspl QETH_CARD_TEXT(card, 2, "qswiatcb"); 308445cbb2e4SStefan Raspl cmd = (struct qeth_ipa_cmd *) data; 308545cbb2e4SStefan Raspl sw_info = (struct qeth_switch_info *)reply->param; 308645cbb2e4SStefan Raspl if (cmd->data.setadapterparms.hdr.return_code == 0) { 308745cbb2e4SStefan Raspl attrs = &cmd->data.setadapterparms.data.query_switch_attributes; 308845cbb2e4SStefan Raspl sw_info->capabilities = attrs->capabilities; 308945cbb2e4SStefan Raspl sw_info->settings = attrs->settings; 309045cbb2e4SStefan Raspl QETH_CARD_TEXT_(card, 2, "%04x%04x", sw_info->capabilities, 309145cbb2e4SStefan Raspl sw_info->settings); 309245cbb2e4SStefan Raspl } 309345cbb2e4SStefan Raspl qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 309445cbb2e4SStefan Raspl 309545cbb2e4SStefan Raspl return 0; 309645cbb2e4SStefan Raspl } 309745cbb2e4SStefan Raspl 309845cbb2e4SStefan Raspl int qeth_query_switch_attributes(struct qeth_card *card, 309945cbb2e4SStefan Raspl struct qeth_switch_info *sw_info) 310045cbb2e4SStefan Raspl { 310145cbb2e4SStefan Raspl struct qeth_cmd_buffer *iob; 310245cbb2e4SStefan Raspl 310345cbb2e4SStefan Raspl QETH_CARD_TEXT(card, 2, "qswiattr"); 310445cbb2e4SStefan Raspl if (!qeth_adp_supported(card, IPA_SETADP_QUERY_SWITCH_ATTRIBUTES)) 310545cbb2e4SStefan Raspl return -EOPNOTSUPP; 310645cbb2e4SStefan Raspl if (!netif_carrier_ok(card->dev)) 310745cbb2e4SStefan Raspl return -ENOMEDIUM; 310845cbb2e4SStefan Raspl iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_SWITCH_ATTRIBUTES, 310945cbb2e4SStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr)); 31101aec42bcSThomas Richter if (!iob) 31111aec42bcSThomas Richter return -ENOMEM; 311245cbb2e4SStefan Raspl return qeth_send_ipa_cmd(card, iob, 311345cbb2e4SStefan Raspl qeth_query_switch_attributes_cb, sw_info); 311445cbb2e4SStefan Raspl } 311545cbb2e4SStefan Raspl EXPORT_SYMBOL_GPL(qeth_query_switch_attributes); 311645cbb2e4SStefan Raspl 31171da74b1cSFrank Blaschka static int qeth_query_setdiagass_cb(struct qeth_card *card, 31181da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 31191da74b1cSFrank Blaschka { 31201da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31211da74b1cSFrank Blaschka __u16 rc; 31221da74b1cSFrank Blaschka 31231da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 31241da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 31251da74b1cSFrank Blaschka if (rc) 31261da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "diagq:%x", rc); 31271da74b1cSFrank Blaschka else 31281da74b1cSFrank Blaschka card->info.diagass_support = cmd->data.diagass.ext; 31291da74b1cSFrank Blaschka return 0; 31301da74b1cSFrank Blaschka } 31311da74b1cSFrank Blaschka 31321da74b1cSFrank Blaschka static int qeth_query_setdiagass(struct qeth_card *card) 31331da74b1cSFrank Blaschka { 31341da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 31351da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31361da74b1cSFrank Blaschka 31371da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qdiagass"); 31381da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 31391aec42bcSThomas Richter if (!iob) 31401aec42bcSThomas Richter return -ENOMEM; 31411da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 31421da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 16; 31431da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY; 31441da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL); 31451da74b1cSFrank Blaschka } 31461da74b1cSFrank Blaschka 31471da74b1cSFrank Blaschka static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) 31481da74b1cSFrank Blaschka { 31491da74b1cSFrank Blaschka unsigned long info = get_zeroed_page(GFP_KERNEL); 31501da74b1cSFrank Blaschka struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 31511da74b1cSFrank Blaschka struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; 31521da74b1cSFrank Blaschka struct ccw_dev_id ccwid; 3153caf757c6SHeiko Carstens int level; 31541da74b1cSFrank Blaschka 31551da74b1cSFrank Blaschka tid->chpid = card->info.chpid; 31561da74b1cSFrank Blaschka ccw_device_get_id(CARD_RDEV(card), &ccwid); 31571da74b1cSFrank Blaschka tid->ssid = ccwid.ssid; 31581da74b1cSFrank Blaschka tid->devno = ccwid.devno; 31591da74b1cSFrank Blaschka if (!info) 31601da74b1cSFrank Blaschka return; 3161caf757c6SHeiko Carstens level = stsi(NULL, 0, 0, 0); 3162caf757c6SHeiko Carstens if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0)) 31631da74b1cSFrank Blaschka tid->lparnr = info222->lpar_number; 3164caf757c6SHeiko Carstens if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) { 31651da74b1cSFrank Blaschka EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); 31661da74b1cSFrank Blaschka memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); 31671da74b1cSFrank Blaschka } 31681da74b1cSFrank Blaschka free_page(info); 31691da74b1cSFrank Blaschka return; 31701da74b1cSFrank Blaschka } 31711da74b1cSFrank Blaschka 31721da74b1cSFrank Blaschka static int qeth_hw_trap_cb(struct qeth_card *card, 31731da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 31741da74b1cSFrank Blaschka { 31751da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31761da74b1cSFrank Blaschka __u16 rc; 31771da74b1cSFrank Blaschka 31781da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 31791da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 31801da74b1cSFrank Blaschka if (rc) 31811da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "trapc:%x", rc); 31821da74b1cSFrank Blaschka return 0; 31831da74b1cSFrank Blaschka } 31841da74b1cSFrank Blaschka 31851da74b1cSFrank Blaschka int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) 31861da74b1cSFrank Blaschka { 31871da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 31881da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31891da74b1cSFrank Blaschka 31901da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "diagtrap"); 31911da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 31921aec42bcSThomas Richter if (!iob) 31931aec42bcSThomas Richter return -ENOMEM; 31941da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 31951da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 80; 31961da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP; 31971da74b1cSFrank Blaschka cmd->data.diagass.type = 1; 31981da74b1cSFrank Blaschka cmd->data.diagass.action = action; 31991da74b1cSFrank Blaschka switch (action) { 32001da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_ARM: 32011da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0003; 32021da74b1cSFrank Blaschka cmd->data.diagass.ext = 0x00010000 + 32031da74b1cSFrank Blaschka sizeof(struct qeth_trap_id); 32041da74b1cSFrank Blaschka qeth_get_trap_id(card, 32051da74b1cSFrank Blaschka (struct qeth_trap_id *)cmd->data.diagass.cdata); 32061da74b1cSFrank Blaschka break; 32071da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_DISARM: 32081da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0001; 32091da74b1cSFrank Blaschka break; 32101da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_CAPTURE: 32111da74b1cSFrank Blaschka break; 32121da74b1cSFrank Blaschka } 32131da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_hw_trap_cb, NULL); 32141da74b1cSFrank Blaschka } 32151da74b1cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hw_trap); 32161da74b1cSFrank Blaschka 3217d73ef324SJulian Wiedmann static int qeth_check_qdio_errors(struct qeth_card *card, 3218d73ef324SJulian Wiedmann struct qdio_buffer *buf, 3219d73ef324SJulian Wiedmann unsigned int qdio_error, 3220d73ef324SJulian Wiedmann const char *dbftext) 32214a71df50SFrank Blaschka { 3222779e6e1cSJan Glauber if (qdio_error) { 3223847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, dbftext); 322438593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F15=%02X", 32253ec90878SJan Glauber buf->element[15].sflags); 322638593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F14=%02X", 32273ec90878SJan Glauber buf->element[14].sflags); 322838593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); 32293ec90878SJan Glauber if ((buf->element[15].sflags) == 0x12) { 323076b11f8eSUrsula Braun card->stats.rx_dropped++; 323176b11f8eSUrsula Braun return 0; 323276b11f8eSUrsula Braun } else 32334a71df50SFrank Blaschka return 1; 32344a71df50SFrank Blaschka } 32354a71df50SFrank Blaschka return 0; 32364a71df50SFrank Blaschka } 32374a71df50SFrank Blaschka 3238d73ef324SJulian Wiedmann static void qeth_queue_input_buffer(struct qeth_card *card, int index) 32394a71df50SFrank Blaschka { 32404a71df50SFrank Blaschka struct qeth_qdio_q *queue = card->qdio.in_q; 3241b3332930SFrank Blaschka struct list_head *lh; 32424a71df50SFrank Blaschka int count; 32434a71df50SFrank Blaschka int i; 32444a71df50SFrank Blaschka int rc; 32454a71df50SFrank Blaschka int newcount = 0; 32464a71df50SFrank Blaschka 32474a71df50SFrank Blaschka count = (index < queue->next_buf_to_init)? 32484a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 32494a71df50SFrank Blaschka (queue->next_buf_to_init - index) : 32504a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 32514a71df50SFrank Blaschka (queue->next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index); 32524a71df50SFrank Blaschka /* only requeue at a certain threshold to avoid SIGAs */ 32534a71df50SFrank Blaschka if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)) { 32544a71df50SFrank Blaschka for (i = queue->next_buf_to_init; 32554a71df50SFrank Blaschka i < queue->next_buf_to_init + count; ++i) { 32564a71df50SFrank Blaschka if (qeth_init_input_buffer(card, 32574a71df50SFrank Blaschka &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { 32584a71df50SFrank Blaschka break; 32594a71df50SFrank Blaschka } else { 32604a71df50SFrank Blaschka newcount++; 32614a71df50SFrank Blaschka } 32624a71df50SFrank Blaschka } 32634a71df50SFrank Blaschka 32644a71df50SFrank Blaschka if (newcount < count) { 32654a71df50SFrank Blaschka /* we are in memory shortage so we switch back to 32664a71df50SFrank Blaschka traditional skb allocation and drop packages */ 32674a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 3); 32684a71df50SFrank Blaschka count = newcount; 32694a71df50SFrank Blaschka } else { 32704a71df50SFrank Blaschka atomic_add_unless(&card->force_alloc_skb, -1, 0); 32714a71df50SFrank Blaschka } 32724a71df50SFrank Blaschka 3273b3332930SFrank Blaschka if (!count) { 3274b3332930SFrank Blaschka i = 0; 3275b3332930SFrank Blaschka list_for_each(lh, &card->qdio.in_buf_pool.entry_list) 3276b3332930SFrank Blaschka i++; 3277b3332930SFrank Blaschka if (i == card->qdio.in_buf_pool.buf_count) { 3278b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "qsarbw"); 3279b3332930SFrank Blaschka card->reclaim_index = index; 3280b3332930SFrank Blaschka schedule_delayed_work( 3281b3332930SFrank Blaschka &card->buffer_reclaim_work, 3282b3332930SFrank Blaschka QETH_RECLAIM_WORK_TIME); 3283b3332930SFrank Blaschka } 3284b3332930SFrank Blaschka return; 3285b3332930SFrank Blaschka } 3286b3332930SFrank Blaschka 32874a71df50SFrank Blaschka /* 32884a71df50SFrank Blaschka * according to old code it should be avoided to requeue all 32894a71df50SFrank Blaschka * 128 buffers in order to benefit from PCI avoidance. 32904a71df50SFrank Blaschka * this function keeps at least one buffer (the buffer at 32914a71df50SFrank Blaschka * 'index') un-requeued -> this buffer is the first buffer that 32924a71df50SFrank Blaschka * will be requeued the next time 32934a71df50SFrank Blaschka */ 32944a71df50SFrank Blaschka if (card->options.performance_stats) { 32954a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_cnt++; 32964a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time = 32974a71df50SFrank Blaschka qeth_get_micros(); 32984a71df50SFrank Blaschka } 3299779e6e1cSJan Glauber rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 3300779e6e1cSJan Glauber queue->next_buf_to_init, count); 33014a71df50SFrank Blaschka if (card->options.performance_stats) 33024a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_time += 33034a71df50SFrank Blaschka qeth_get_micros() - 33044a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time; 33054a71df50SFrank Blaschka if (rc) { 3306847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "qinberr"); 33074a71df50SFrank Blaschka } 33084a71df50SFrank Blaschka queue->next_buf_to_init = (queue->next_buf_to_init + count) % 33094a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 33104a71df50SFrank Blaschka } 33114a71df50SFrank Blaschka } 3312d73ef324SJulian Wiedmann 3313d73ef324SJulian Wiedmann static void qeth_buffer_reclaim_work(struct work_struct *work) 3314d73ef324SJulian Wiedmann { 3315d73ef324SJulian Wiedmann struct qeth_card *card = container_of(work, struct qeth_card, 3316d73ef324SJulian Wiedmann buffer_reclaim_work.work); 3317d73ef324SJulian Wiedmann 3318d73ef324SJulian Wiedmann QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); 3319d73ef324SJulian Wiedmann qeth_queue_input_buffer(card, card->reclaim_index); 3320d73ef324SJulian Wiedmann } 33214a71df50SFrank Blaschka 3322d7a39937SJulian Wiedmann static void qeth_handle_send_error(struct qeth_card *card, 3323779e6e1cSJan Glauber struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) 33244a71df50SFrank Blaschka { 33253ec90878SJan Glauber int sbalf15 = buffer->buffer->element[15].sflags; 33264a71df50SFrank Blaschka 3327847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hdsnderr"); 332858490f18SKlaus-Dieter Wacker if (card->info.type == QETH_CARD_TYPE_IQD) { 332958490f18SKlaus-Dieter Wacker if (sbalf15 == 0) { 333058490f18SKlaus-Dieter Wacker qdio_err = 0; 333158490f18SKlaus-Dieter Wacker } else { 333258490f18SKlaus-Dieter Wacker qdio_err = 1; 333358490f18SKlaus-Dieter Wacker } 333458490f18SKlaus-Dieter Wacker } 333576b11f8eSUrsula Braun qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr"); 3336d303b6fdSJan Glauber 3337d303b6fdSJan Glauber if (!qdio_err) 3338d7a39937SJulian Wiedmann return; 3339d303b6fdSJan Glauber 3340d303b6fdSJan Glauber if ((sbalf15 >= 15) && (sbalf15 <= 31)) 3341d7a39937SJulian Wiedmann return; 3342d303b6fdSJan Glauber 3343847a50fdSCarsten Otte QETH_CARD_TEXT(card, 1, "lnkfail"); 3344847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 1, "%04x %02x", 33454a71df50SFrank Blaschka (u16)qdio_err, (u8)sbalf15); 33464a71df50SFrank Blaschka } 33474a71df50SFrank Blaschka 3348664e42acSJulian Wiedmann /** 3349664e42acSJulian Wiedmann * qeth_prep_flush_pack_buffer - Prepares flushing of a packing buffer. 3350664e42acSJulian Wiedmann * @queue: queue to check for packing buffer 3351664e42acSJulian Wiedmann * 3352664e42acSJulian Wiedmann * Returns number of buffers that were prepared for flush. 3353664e42acSJulian Wiedmann */ 3354664e42acSJulian Wiedmann static int qeth_prep_flush_pack_buffer(struct qeth_qdio_out_q *queue) 3355664e42acSJulian Wiedmann { 3356664e42acSJulian Wiedmann struct qeth_qdio_out_buffer *buffer; 3357664e42acSJulian Wiedmann 3358664e42acSJulian Wiedmann buffer = queue->bufs[queue->next_buf_to_fill]; 3359664e42acSJulian Wiedmann if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) && 3360664e42acSJulian Wiedmann (buffer->next_element_to_fill > 0)) { 3361664e42acSJulian Wiedmann /* it's a packing buffer */ 3362664e42acSJulian Wiedmann atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 3363664e42acSJulian Wiedmann queue->next_buf_to_fill = 3364664e42acSJulian Wiedmann (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q; 3365664e42acSJulian Wiedmann return 1; 3366664e42acSJulian Wiedmann } 3367664e42acSJulian Wiedmann return 0; 3368664e42acSJulian Wiedmann } 3369664e42acSJulian Wiedmann 33704a71df50SFrank Blaschka /* 33714a71df50SFrank Blaschka * Switched to packing state if the number of used buffers on a queue 33724a71df50SFrank Blaschka * reaches a certain limit. 33734a71df50SFrank Blaschka */ 33744a71df50SFrank Blaschka static void qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue) 33754a71df50SFrank Blaschka { 33764a71df50SFrank Blaschka if (!queue->do_pack) { 33774a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 33784a71df50SFrank Blaschka >= QETH_HIGH_WATERMARK_PACK){ 33794a71df50SFrank Blaschka /* switch non-PACKING -> PACKING */ 3380847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "np->pack"); 33814a71df50SFrank Blaschka if (queue->card->options.performance_stats) 33824a71df50SFrank Blaschka queue->card->perf_stats.sc_dp_p++; 33834a71df50SFrank Blaschka queue->do_pack = 1; 33844a71df50SFrank Blaschka } 33854a71df50SFrank Blaschka } 33864a71df50SFrank Blaschka } 33874a71df50SFrank Blaschka 33884a71df50SFrank Blaschka /* 33894a71df50SFrank Blaschka * Switches from packing to non-packing mode. If there is a packing 33904a71df50SFrank Blaschka * buffer on the queue this buffer will be prepared to be flushed. 33914a71df50SFrank Blaschka * In that case 1 is returned to inform the caller. If no buffer 33924a71df50SFrank Blaschka * has to be flushed, zero is returned. 33934a71df50SFrank Blaschka */ 33944a71df50SFrank Blaschka static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue) 33954a71df50SFrank Blaschka { 33964a71df50SFrank Blaschka if (queue->do_pack) { 33974a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 33984a71df50SFrank Blaschka <= QETH_LOW_WATERMARK_PACK) { 33994a71df50SFrank Blaschka /* switch PACKING -> non-PACKING */ 3400847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "pack->np"); 34014a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34024a71df50SFrank Blaschka queue->card->perf_stats.sc_p_dp++; 34034a71df50SFrank Blaschka queue->do_pack = 0; 3404664e42acSJulian Wiedmann return qeth_prep_flush_pack_buffer(queue); 34054a71df50SFrank Blaschka } 34064a71df50SFrank Blaschka } 34074a71df50SFrank Blaschka return 0; 34084a71df50SFrank Blaschka } 34094a71df50SFrank Blaschka 3410779e6e1cSJan Glauber static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, 3411779e6e1cSJan Glauber int count) 34124a71df50SFrank Blaschka { 34134a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buf; 34144a71df50SFrank Blaschka int rc; 34154a71df50SFrank Blaschka int i; 34164a71df50SFrank Blaschka unsigned int qdio_flags; 34174a71df50SFrank Blaschka 34184a71df50SFrank Blaschka for (i = index; i < index + count; ++i) { 34190da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 34200da9581dSEinar Lueck buf = queue->bufs[bidx]; 34213ec90878SJan Glauber buf->buffer->element[buf->next_element_to_fill - 1].eflags |= 34223ec90878SJan Glauber SBAL_EFLAGS_LAST_ENTRY; 34234a71df50SFrank Blaschka 34240da9581dSEinar Lueck if (queue->bufstates) 34250da9581dSEinar Lueck queue->bufstates[bidx].user = buf; 34260da9581dSEinar Lueck 34274a71df50SFrank Blaschka if (queue->card->info.type == QETH_CARD_TYPE_IQD) 34284a71df50SFrank Blaschka continue; 34294a71df50SFrank Blaschka 34304a71df50SFrank Blaschka if (!queue->do_pack) { 34314a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) >= 34324a71df50SFrank Blaschka (QETH_HIGH_WATERMARK_PACK - 34334a71df50SFrank Blaschka QETH_WATERMARK_PACK_FUZZ)) && 34344a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 34354a71df50SFrank Blaschka /* it's likely that we'll go to packing 34364a71df50SFrank Blaschka * mode soon */ 34374a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 34383ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 34394a71df50SFrank Blaschka } 34404a71df50SFrank Blaschka } else { 34414a71df50SFrank Blaschka if (!atomic_read(&queue->set_pci_flags_count)) { 34424a71df50SFrank Blaschka /* 34434a71df50SFrank Blaschka * there's no outstanding PCI any more, so we 34444a71df50SFrank Blaschka * have to request a PCI to be sure the the PCI 34454a71df50SFrank Blaschka * will wake at some time in the future then we 34464a71df50SFrank Blaschka * can flush packed buffers that might still be 34474a71df50SFrank Blaschka * hanging around, which can happen if no 34484a71df50SFrank Blaschka * further send was requested by the stack 34494a71df50SFrank Blaschka */ 34504a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 34513ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 34524a71df50SFrank Blaschka } 34534a71df50SFrank Blaschka } 34544a71df50SFrank Blaschka } 34554a71df50SFrank Blaschka 34563e66bab3SFlorian Westphal netif_trans_update(queue->card->dev); 34574a71df50SFrank Blaschka if (queue->card->options.performance_stats) { 34584a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_cnt++; 34594a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time = 34604a71df50SFrank Blaschka qeth_get_micros(); 34614a71df50SFrank Blaschka } 34624a71df50SFrank Blaschka qdio_flags = QDIO_FLAG_SYNC_OUTPUT; 34634a71df50SFrank Blaschka if (atomic_read(&queue->set_pci_flags_count)) 34644a71df50SFrank Blaschka qdio_flags |= QDIO_FLAG_PCI_OUT; 34654a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, 3466779e6e1cSJan Glauber queue->queue_no, index, count); 34674a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34684a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_time += 34694a71df50SFrank Blaschka qeth_get_micros() - 34704a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time; 3471aa3a41d0SJan Glauber atomic_add(count, &queue->used_buffers); 34724a71df50SFrank Blaschka if (rc) { 3473d303b6fdSJan Glauber queue->card->stats.tx_errors += count; 3474d303b6fdSJan Glauber /* ignore temporary SIGA errors without busy condition */ 34751549d13fSJan Glauber if (rc == -ENOBUFS) 3476d303b6fdSJan Glauber return; 3477847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 2, "flushbuf"); 34780da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no); 34790da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " idx%d", index); 34800da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " c%d", count); 3481847a50fdSCarsten Otte QETH_CARD_TEXT_(queue->card, 2, " err%d", rc); 3482d303b6fdSJan Glauber 34834a71df50SFrank Blaschka /* this must not happen under normal circumstances. if it 34844a71df50SFrank Blaschka * happens something is really wrong -> recover */ 34854a71df50SFrank Blaschka qeth_schedule_recovery(queue->card); 34864a71df50SFrank Blaschka return; 34874a71df50SFrank Blaschka } 34884a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34894a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent += count; 34904a71df50SFrank Blaschka } 34914a71df50SFrank Blaschka 34924a71df50SFrank Blaschka static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue) 34934a71df50SFrank Blaschka { 34944a71df50SFrank Blaschka int index; 34954a71df50SFrank Blaschka int flush_cnt = 0; 34964a71df50SFrank Blaschka int q_was_packing = 0; 34974a71df50SFrank Blaschka 34984a71df50SFrank Blaschka /* 34994a71df50SFrank Blaschka * check if weed have to switch to non-packing mode or if 35004a71df50SFrank Blaschka * we have to get a pci flag out on the queue 35014a71df50SFrank Blaschka */ 35024a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) <= QETH_LOW_WATERMARK_PACK) || 35034a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 35044a71df50SFrank Blaschka if (atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH) == 35054a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED) { 35064a71df50SFrank Blaschka /* 35074a71df50SFrank Blaschka * If we get in here, there was no action in 35084a71df50SFrank Blaschka * do_send_packet. So, we check if there is a 35094a71df50SFrank Blaschka * packing buffer to be flushed here. 35104a71df50SFrank Blaschka */ 35114a71df50SFrank Blaschka netif_stop_queue(queue->card->dev); 35124a71df50SFrank Blaschka index = queue->next_buf_to_fill; 35134a71df50SFrank Blaschka q_was_packing = queue->do_pack; 35144a71df50SFrank Blaschka /* queue->do_pack may change */ 35154a71df50SFrank Blaschka barrier(); 35164a71df50SFrank Blaschka flush_cnt += qeth_switch_to_nonpacking_if_needed(queue); 35174a71df50SFrank Blaschka if (!flush_cnt && 35184a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) 3519664e42acSJulian Wiedmann flush_cnt += qeth_prep_flush_pack_buffer(queue); 35204a71df50SFrank Blaschka if (queue->card->options.performance_stats && 35214a71df50SFrank Blaschka q_was_packing) 35224a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += 35234a71df50SFrank Blaschka flush_cnt; 35244a71df50SFrank Blaschka if (flush_cnt) 3525779e6e1cSJan Glauber qeth_flush_buffers(queue, index, flush_cnt); 35264a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 35274a71df50SFrank Blaschka } 35284a71df50SFrank Blaschka } 35294a71df50SFrank Blaschka } 35304a71df50SFrank Blaschka 3531a1c3ed4cSFrank Blaschka void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, 3532a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3533a1c3ed4cSFrank Blaschka { 3534a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3535a1c3ed4cSFrank Blaschka 35360cffef48SFrank Blaschka if (card->dev && (card->dev->flags & IFF_UP)) 3537a1c3ed4cSFrank Blaschka napi_schedule(&card->napi); 3538a1c3ed4cSFrank Blaschka } 3539a1c3ed4cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_start_poll); 3540a1c3ed4cSFrank Blaschka 35410da9581dSEinar Lueck int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) 35420da9581dSEinar Lueck { 35430da9581dSEinar Lueck int rc; 35440da9581dSEinar Lueck 35450da9581dSEinar Lueck if (card->options.cq == QETH_CQ_NOTAVAILABLE) { 35460da9581dSEinar Lueck rc = -1; 35470da9581dSEinar Lueck goto out; 35480da9581dSEinar Lueck } else { 35490da9581dSEinar Lueck if (card->options.cq == cq) { 35500da9581dSEinar Lueck rc = 0; 35510da9581dSEinar Lueck goto out; 35520da9581dSEinar Lueck } 35530da9581dSEinar Lueck 35540da9581dSEinar Lueck if (card->state != CARD_STATE_DOWN && 35550da9581dSEinar Lueck card->state != CARD_STATE_RECOVER) { 35560da9581dSEinar Lueck rc = -1; 35570da9581dSEinar Lueck goto out; 35580da9581dSEinar Lueck } 35590da9581dSEinar Lueck 35600da9581dSEinar Lueck qeth_free_qdio_buffers(card); 35610da9581dSEinar Lueck card->options.cq = cq; 35620da9581dSEinar Lueck rc = 0; 35630da9581dSEinar Lueck } 35640da9581dSEinar Lueck out: 35650da9581dSEinar Lueck return rc; 35660da9581dSEinar Lueck 35670da9581dSEinar Lueck } 35680da9581dSEinar Lueck EXPORT_SYMBOL_GPL(qeth_configure_cq); 35690da9581dSEinar Lueck 35700da9581dSEinar Lueck 35710da9581dSEinar Lueck static void qeth_qdio_cq_handler(struct qeth_card *card, 35720da9581dSEinar Lueck unsigned int qdio_err, 35730da9581dSEinar Lueck unsigned int queue, int first_element, int count) { 35740da9581dSEinar Lueck struct qeth_qdio_q *cq = card->qdio.c_q; 35750da9581dSEinar Lueck int i; 35760da9581dSEinar Lueck int rc; 35770da9581dSEinar Lueck 35780da9581dSEinar Lueck if (!qeth_is_cq(card, queue)) 35790da9581dSEinar Lueck goto out; 35800da9581dSEinar Lueck 35810da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element); 35820da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhc%d", count); 35830da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err); 35840da9581dSEinar Lueck 35850da9581dSEinar Lueck if (qdio_err) { 35860da9581dSEinar Lueck netif_stop_queue(card->dev); 35870da9581dSEinar Lueck qeth_schedule_recovery(card); 35880da9581dSEinar Lueck goto out; 35890da9581dSEinar Lueck } 35900da9581dSEinar Lueck 35910da9581dSEinar Lueck if (card->options.performance_stats) { 35920da9581dSEinar Lueck card->perf_stats.cq_cnt++; 35930da9581dSEinar Lueck card->perf_stats.cq_start_time = qeth_get_micros(); 35940da9581dSEinar Lueck } 35950da9581dSEinar Lueck 35960da9581dSEinar Lueck for (i = first_element; i < first_element + count; ++i) { 35970da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 35986d284bdeSSebastian Ott struct qdio_buffer *buffer = cq->qdio_bufs[bidx]; 35990da9581dSEinar Lueck int e; 36000da9581dSEinar Lueck 36010da9581dSEinar Lueck e = 0; 3602903e4853SUrsula Braun while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && 3603903e4853SUrsula Braun buffer->element[e].addr) { 36040da9581dSEinar Lueck unsigned long phys_aob_addr; 36050da9581dSEinar Lueck 36060da9581dSEinar Lueck phys_aob_addr = (unsigned long) buffer->element[e].addr; 36070da9581dSEinar Lueck qeth_qdio_handle_aob(card, phys_aob_addr); 36080da9581dSEinar Lueck buffer->element[e].addr = NULL; 36090da9581dSEinar Lueck buffer->element[e].eflags = 0; 36100da9581dSEinar Lueck buffer->element[e].sflags = 0; 36110da9581dSEinar Lueck buffer->element[e].length = 0; 36120da9581dSEinar Lueck 36130da9581dSEinar Lueck ++e; 36140da9581dSEinar Lueck } 36150da9581dSEinar Lueck 36160da9581dSEinar Lueck buffer->element[15].eflags = 0; 36170da9581dSEinar Lueck buffer->element[15].sflags = 0; 36180da9581dSEinar Lueck } 36190da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, 36200da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init, 36210da9581dSEinar Lueck count); 36220da9581dSEinar Lueck if (rc) { 36230da9581dSEinar Lueck dev_warn(&card->gdev->dev, 36240da9581dSEinar Lueck "QDIO reported an error, rc=%i\n", rc); 36250da9581dSEinar Lueck QETH_CARD_TEXT(card, 2, "qcqherr"); 36260da9581dSEinar Lueck } 36270da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init 36280da9581dSEinar Lueck + count) % QDIO_MAX_BUFFERS_PER_Q; 36290da9581dSEinar Lueck 36300da9581dSEinar Lueck netif_wake_queue(card->dev); 36310da9581dSEinar Lueck 36320da9581dSEinar Lueck if (card->options.performance_stats) { 36330da9581dSEinar Lueck int delta_t = qeth_get_micros(); 36340da9581dSEinar Lueck delta_t -= card->perf_stats.cq_start_time; 36350da9581dSEinar Lueck card->perf_stats.cq_time += delta_t; 36360da9581dSEinar Lueck } 36370da9581dSEinar Lueck out: 36380da9581dSEinar Lueck return; 36390da9581dSEinar Lueck } 36400da9581dSEinar Lueck 3641a1c3ed4cSFrank Blaschka void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err, 36420da9581dSEinar Lueck unsigned int queue, int first_elem, int count, 3643a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3644a1c3ed4cSFrank Blaschka { 3645a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3646a1c3ed4cSFrank Blaschka 36470da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qihq%d", queue); 36480da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err); 36490da9581dSEinar Lueck 36500da9581dSEinar Lueck if (qeth_is_cq(card, queue)) 36510da9581dSEinar Lueck qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count); 36520da9581dSEinar Lueck else if (qdio_err) 3653a1c3ed4cSFrank Blaschka qeth_schedule_recovery(card); 36540da9581dSEinar Lueck 36550da9581dSEinar Lueck 3656a1c3ed4cSFrank Blaschka } 3657a1c3ed4cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_input_handler); 3658a1c3ed4cSFrank Blaschka 3659779e6e1cSJan Glauber void qeth_qdio_output_handler(struct ccw_device *ccwdev, 3660779e6e1cSJan Glauber unsigned int qdio_error, int __queue, int first_element, 3661779e6e1cSJan Glauber int count, unsigned long card_ptr) 36624a71df50SFrank Blaschka { 36634a71df50SFrank Blaschka struct qeth_card *card = (struct qeth_card *) card_ptr; 36644a71df50SFrank Blaschka struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; 36654a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 36664a71df50SFrank Blaschka int i; 36674a71df50SFrank Blaschka 3668847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "qdouhdl"); 36691549d13fSJan Glauber if (qdio_error & QDIO_ERROR_FATAL) { 3670847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "achkcond"); 36714a71df50SFrank Blaschka netif_stop_queue(card->dev); 36724a71df50SFrank Blaschka qeth_schedule_recovery(card); 36734a71df50SFrank Blaschka return; 36744a71df50SFrank Blaschka } 36754a71df50SFrank Blaschka if (card->options.performance_stats) { 36764a71df50SFrank Blaschka card->perf_stats.outbound_handler_cnt++; 36774a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time = 36784a71df50SFrank Blaschka qeth_get_micros(); 36794a71df50SFrank Blaschka } 36804a71df50SFrank Blaschka for (i = first_element; i < (first_element + count); ++i) { 36810da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 36820da9581dSEinar Lueck buffer = queue->bufs[bidx]; 3683b67d801fSUrsula Braun qeth_handle_send_error(card, buffer, qdio_error); 36840da9581dSEinar Lueck 36850da9581dSEinar Lueck if (queue->bufstates && 36860da9581dSEinar Lueck (queue->bufstates[bidx].flags & 36870da9581dSEinar Lueck QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) { 368818af5c17SStefan Raspl WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); 3689b3332930SFrank Blaschka 3690b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, 3691b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED, 3692b3332930SFrank Blaschka QETH_QDIO_BUF_PENDING) == 3693b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED) { 3694b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, 3695b3332930SFrank Blaschka TX_NOTIFY_PENDING); 3696b3332930SFrank Blaschka } 36970da9581dSEinar Lueck buffer->aob = queue->bufstates[bidx].aob; 36980da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx); 3699b3332930SFrank Blaschka QETH_CARD_TEXT(queue->card, 5, "aob"); 37000da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 5, "%lx", 37010da9581dSEinar Lueck virt_to_phys(buffer->aob)); 3702b3332930SFrank Blaschka if (qeth_init_qdio_out_buf(queue, bidx)) { 3703b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "outofbuf"); 37040da9581dSEinar Lueck qeth_schedule_recovery(card); 3705b3332930SFrank Blaschka } 37060da9581dSEinar Lueck } else { 3707b3332930SFrank Blaschka if (card->options.cq == QETH_CQ_ENABLED) { 3708b3332930SFrank Blaschka enum iucv_tx_notify n; 3709b3332930SFrank Blaschka 3710b3332930SFrank Blaschka n = qeth_compute_cq_notification( 3711b3332930SFrank Blaschka buffer->buffer->element[15].sflags, 0); 3712b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, n); 3713b3332930SFrank Blaschka } 3714b3332930SFrank Blaschka 37150da9581dSEinar Lueck qeth_clear_output_buffer(queue, buffer, 37160da9581dSEinar Lueck QETH_QDIO_BUF_EMPTY); 37170da9581dSEinar Lueck } 37180da9581dSEinar Lueck qeth_cleanup_handled_pending(queue, bidx, 0); 37194a71df50SFrank Blaschka } 37204a71df50SFrank Blaschka atomic_sub(count, &queue->used_buffers); 37214a71df50SFrank Blaschka /* check if we need to do something on this outbound queue */ 37224a71df50SFrank Blaschka if (card->info.type != QETH_CARD_TYPE_IQD) 37234a71df50SFrank Blaschka qeth_check_outbound_queue(queue); 37244a71df50SFrank Blaschka 37254a71df50SFrank Blaschka netif_wake_queue(queue->card->dev); 37264a71df50SFrank Blaschka if (card->options.performance_stats) 37274a71df50SFrank Blaschka card->perf_stats.outbound_handler_time += qeth_get_micros() - 37284a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time; 37294a71df50SFrank Blaschka } 37304a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); 37314a71df50SFrank Blaschka 373270deb016SHans Wippel /* We cannot use outbound queue 3 for unicast packets on HiperSockets */ 373370deb016SHans Wippel static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num) 373470deb016SHans Wippel { 373570deb016SHans Wippel if ((card->info.type == QETH_CARD_TYPE_IQD) && (queue_num == 3)) 373670deb016SHans Wippel return 2; 373770deb016SHans Wippel return queue_num; 373870deb016SHans Wippel } 373970deb016SHans Wippel 3740290b8348SStefan Raspl /** 3741290b8348SStefan Raspl * Note: Function assumes that we have 4 outbound queues. 3742290b8348SStefan Raspl */ 37434a71df50SFrank Blaschka int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, 37444a71df50SFrank Blaschka int ipv, int cast_type) 37454a71df50SFrank Blaschka { 3746d66cb37eSStefan Raspl __be16 *tci; 3747290b8348SStefan Raspl u8 tos; 3748290b8348SStefan Raspl 37494a71df50SFrank Blaschka if (cast_type && card->info.is_multicast_different) 37504a71df50SFrank Blaschka return card->info.is_multicast_different & 37514a71df50SFrank Blaschka (card->qdio.no_out_queues - 1); 37524a71df50SFrank Blaschka 3753290b8348SStefan Raspl switch (card->qdio.do_prio_queueing) { 3754290b8348SStefan Raspl case QETH_PRIO_Q_ING_TOS: 3755290b8348SStefan Raspl case QETH_PRIO_Q_ING_PREC: 3756290b8348SStefan Raspl switch (ipv) { 3757290b8348SStefan Raspl case 4: 3758290b8348SStefan Raspl tos = ipv4_get_dsfield(ip_hdr(skb)); 3759290b8348SStefan Raspl break; 3760290b8348SStefan Raspl case 6: 3761290b8348SStefan Raspl tos = ipv6_get_dsfield(ipv6_hdr(skb)); 3762290b8348SStefan Raspl break; 37634a71df50SFrank Blaschka default: 37644a71df50SFrank Blaschka return card->qdio.default_out_queue; 37654a71df50SFrank Blaschka } 3766290b8348SStefan Raspl if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) 376770deb016SHans Wippel return qeth_cut_iqd_prio(card, ~tos >> 6 & 3); 3768290b8348SStefan Raspl if (tos & IPTOS_MINCOST) 376970deb016SHans Wippel return qeth_cut_iqd_prio(card, 3); 3770290b8348SStefan Raspl if (tos & IPTOS_RELIABILITY) 3771290b8348SStefan Raspl return 2; 3772290b8348SStefan Raspl if (tos & IPTOS_THROUGHPUT) 3773290b8348SStefan Raspl return 1; 3774290b8348SStefan Raspl if (tos & IPTOS_LOWDELAY) 3775290b8348SStefan Raspl return 0; 3776d66cb37eSStefan Raspl break; 3777d66cb37eSStefan Raspl case QETH_PRIO_Q_ING_SKB: 3778d66cb37eSStefan Raspl if (skb->priority > 5) 3779d66cb37eSStefan Raspl return 0; 378070deb016SHans Wippel return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3); 3781d66cb37eSStefan Raspl case QETH_PRIO_Q_ING_VLAN: 3782d66cb37eSStefan Raspl tci = &((struct ethhdr *)skb->data)->h_proto; 37836bee4e26SHans Wippel if (be16_to_cpu(*tci) == ETH_P_8021Q) 37846bee4e26SHans Wippel return qeth_cut_iqd_prio(card, 37856bee4e26SHans Wippel ~be16_to_cpu(*(tci + 1)) >> (VLAN_PRIO_SHIFT + 1) & 3); 3786d66cb37eSStefan Raspl break; 3787290b8348SStefan Raspl default: 3788290b8348SStefan Raspl break; 3789290b8348SStefan Raspl } 3790290b8348SStefan Raspl return card->qdio.default_out_queue; 37914a71df50SFrank Blaschka } 37924a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_priority_queue); 37934a71df50SFrank Blaschka 37942863c613SEugene Crosser /** 37952863c613SEugene Crosser * qeth_get_elements_for_frags() - find number of SBALEs for skb frags. 37962863c613SEugene Crosser * @skb: SKB address 37972863c613SEugene Crosser * 37982863c613SEugene Crosser * Returns the number of pages, and thus QDIO buffer elements, needed to cover 37992863c613SEugene Crosser * fragmented part of the SKB. Returns zero for linear SKB. 38002863c613SEugene Crosser */ 3801271648b4SFrank Blaschka int qeth_get_elements_for_frags(struct sk_buff *skb) 3802271648b4SFrank Blaschka { 38032863c613SEugene Crosser int cnt, elements = 0; 3804271648b4SFrank Blaschka 3805271648b4SFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 38062863c613SEugene Crosser struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[cnt]; 38072863c613SEugene Crosser 38082863c613SEugene Crosser elements += qeth_get_elements_for_range( 38092863c613SEugene Crosser (addr_t)skb_frag_address(frag), 38102863c613SEugene Crosser (addr_t)skb_frag_address(frag) + skb_frag_size(frag)); 3811271648b4SFrank Blaschka } 3812271648b4SFrank Blaschka return elements; 3813271648b4SFrank Blaschka } 3814271648b4SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); 3815271648b4SFrank Blaschka 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 { 38312863c613SEugene Crosser int elements = qeth_get_elements_for_range( 38327d969d2eSJulian Wiedmann (addr_t)skb->data + data_offset, 38332863c613SEugene Crosser (addr_t)skb->data + skb_headlen(skb)) + 38342863c613SEugene Crosser qeth_get_elements_for_frags(skb); 38354a71df50SFrank Blaschka 38362863c613SEugene Crosser if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { 383714cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Invalid size of IP packet " 38384a71df50SFrank Blaschka "(Number=%d / Length=%d). Discarded.\n", 38392863c613SEugene Crosser elements + extra_elems, skb->len); 38404a71df50SFrank Blaschka return 0; 38414a71df50SFrank Blaschka } 38422863c613SEugene Crosser return elements; 38434a71df50SFrank Blaschka } 38444a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_no); 38454a71df50SFrank Blaschka 3846d4ae1f5eSStefan Raspl int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) 384751aa165cSFrank Blaschka { 384851aa165cSFrank Blaschka int hroom, inpage, rest; 384951aa165cSFrank Blaschka 385051aa165cSFrank Blaschka if (((unsigned long)skb->data & PAGE_MASK) != 385151aa165cSFrank Blaschka (((unsigned long)skb->data + len - 1) & PAGE_MASK)) { 385251aa165cSFrank Blaschka hroom = skb_headroom(skb); 385351aa165cSFrank Blaschka inpage = PAGE_SIZE - ((unsigned long) skb->data % PAGE_SIZE); 385451aa165cSFrank Blaschka rest = len - inpage; 385551aa165cSFrank Blaschka if (rest > hroom) 385651aa165cSFrank Blaschka return 1; 38572863c613SEugene Crosser memmove(skb->data - rest, skb->data, skb_headlen(skb)); 385851aa165cSFrank Blaschka skb->data -= rest; 3859d4ae1f5eSStefan Raspl skb->tail -= rest; 3860d4ae1f5eSStefan Raspl *hdr = (struct qeth_hdr *)skb->data; 386151aa165cSFrank Blaschka QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest); 386251aa165cSFrank Blaschka } 386351aa165cSFrank Blaschka return 0; 386451aa165cSFrank Blaschka } 386551aa165cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); 386651aa165cSFrank Blaschka 38670d6f02d3SJulian Wiedmann /** 38680d6f02d3SJulian Wiedmann * qeth_push_hdr() - push a qeth_hdr onto an skb. 38690d6f02d3SJulian Wiedmann * @skb: skb that the qeth_hdr should be pushed onto. 38700d6f02d3SJulian Wiedmann * @hdr: double pointer to a qeth_hdr. When returning with >= 0, 38710d6f02d3SJulian Wiedmann * it contains a valid pointer to a qeth_hdr. 38720d6f02d3SJulian Wiedmann * @len: length of the hdr that needs to be pushed on. 38730d6f02d3SJulian Wiedmann * 38740d6f02d3SJulian Wiedmann * Returns the pushed length. If the header can't be pushed on 38750d6f02d3SJulian Wiedmann * (eg. because it would cross a page boundary), it is allocated from 38760d6f02d3SJulian Wiedmann * the cache instead and 0 is returned. 38770d6f02d3SJulian Wiedmann * Error to create the hdr is indicated by returning with < 0. 38780d6f02d3SJulian Wiedmann */ 38790d6f02d3SJulian Wiedmann int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len) 38800d6f02d3SJulian Wiedmann { 38810d6f02d3SJulian Wiedmann if (skb_headroom(skb) >= len && 38820d6f02d3SJulian Wiedmann qeth_get_elements_for_range((addr_t)skb->data - len, 38830d6f02d3SJulian Wiedmann (addr_t)skb->data) == 1) { 38840d6f02d3SJulian Wiedmann *hdr = skb_push(skb, len); 38850d6f02d3SJulian Wiedmann return len; 38860d6f02d3SJulian Wiedmann } 38870d6f02d3SJulian Wiedmann /* fall back */ 38880d6f02d3SJulian Wiedmann *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); 38890d6f02d3SJulian Wiedmann if (!*hdr) 38900d6f02d3SJulian Wiedmann return -ENOMEM; 38910d6f02d3SJulian Wiedmann return 0; 38920d6f02d3SJulian Wiedmann } 38930d6f02d3SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_push_hdr); 38940d6f02d3SJulian Wiedmann 3895cef6ff22SJulian Wiedmann static void __qeth_fill_buffer(struct sk_buff *skb, 3896384d2ef1SJulian Wiedmann struct qeth_qdio_out_buffer *buf, 3897cc309f83SJulian Wiedmann bool is_first_elem, unsigned int offset) 38984a71df50SFrank Blaschka { 3899384d2ef1SJulian Wiedmann struct qdio_buffer *buffer = buf->buffer; 3900384d2ef1SJulian Wiedmann int element = buf->next_element_to_fill; 3901cc309f83SJulian Wiedmann int length = skb_headlen(skb) - offset; 3902cc309f83SJulian Wiedmann char *data = skb->data + offset; 3903384d2ef1SJulian Wiedmann int length_here, cnt; 39044a71df50SFrank Blaschka 3905cc309f83SJulian Wiedmann /* map linear part into buffer element(s) */ 39064a71df50SFrank Blaschka while (length > 0) { 39074a71df50SFrank Blaschka /* length_here is the remaining amount of data in this page */ 39084a71df50SFrank Blaschka length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); 39094a71df50SFrank Blaschka if (length < length_here) 39104a71df50SFrank Blaschka length_here = length; 39114a71df50SFrank Blaschka 39124a71df50SFrank Blaschka buffer->element[element].addr = data; 39134a71df50SFrank Blaschka buffer->element[element].length = length_here; 39144a71df50SFrank Blaschka length -= length_here; 3915384d2ef1SJulian Wiedmann if (is_first_elem) { 3916384d2ef1SJulian Wiedmann is_first_elem = false; 39175258830bSJulian Wiedmann if (length || skb_is_nonlinear(skb)) 39185258830bSJulian Wiedmann /* skb needs additional elements */ 39193ec90878SJan Glauber buffer->element[element].eflags = 39203ec90878SJan Glauber SBAL_EFLAGS_FIRST_FRAG; 392151aa165cSFrank Blaschka else 39223ec90878SJan Glauber buffer->element[element].eflags = 0; 39234a71df50SFrank Blaschka } else { 39243ec90878SJan Glauber buffer->element[element].eflags = 39253ec90878SJan Glauber SBAL_EFLAGS_MIDDLE_FRAG; 39264a71df50SFrank Blaschka } 39274a71df50SFrank Blaschka data += length_here; 39284a71df50SFrank Blaschka element++; 39294a71df50SFrank Blaschka } 393051aa165cSFrank Blaschka 3931cc309f83SJulian Wiedmann /* map page frags into buffer element(s) */ 393251aa165cSFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 3933f8eb4930SJulian Wiedmann skb_frag_t *frag = &skb_shinfo(skb)->frags[cnt]; 3934f8eb4930SJulian Wiedmann 3935f8eb4930SJulian Wiedmann data = skb_frag_address(frag); 3936f8eb4930SJulian Wiedmann length = skb_frag_size(frag); 3937271648b4SFrank Blaschka while (length > 0) { 3938271648b4SFrank Blaschka length_here = PAGE_SIZE - 3939271648b4SFrank Blaschka ((unsigned long) data % PAGE_SIZE); 3940271648b4SFrank Blaschka if (length < length_here) 3941271648b4SFrank Blaschka length_here = length; 3942271648b4SFrank Blaschka 3943271648b4SFrank Blaschka buffer->element[element].addr = data; 3944271648b4SFrank Blaschka buffer->element[element].length = length_here; 3945271648b4SFrank Blaschka buffer->element[element].eflags = 3946271648b4SFrank Blaschka SBAL_EFLAGS_MIDDLE_FRAG; 3947271648b4SFrank Blaschka length -= length_here; 3948271648b4SFrank Blaschka data += length_here; 394951aa165cSFrank Blaschka element++; 395051aa165cSFrank Blaschka } 3951271648b4SFrank Blaschka } 395251aa165cSFrank Blaschka 39533ec90878SJan Glauber if (buffer->element[element - 1].eflags) 39543ec90878SJan Glauber buffer->element[element - 1].eflags = SBAL_EFLAGS_LAST_FRAG; 3955384d2ef1SJulian Wiedmann buf->next_element_to_fill = element; 39564a71df50SFrank Blaschka } 39574a71df50SFrank Blaschka 3958eaf3cc08SJulian Wiedmann /** 3959eaf3cc08SJulian Wiedmann * qeth_fill_buffer() - map skb into an output buffer 3960eaf3cc08SJulian Wiedmann * @queue: QDIO queue to submit the buffer on 3961eaf3cc08SJulian Wiedmann * @buf: buffer to transport the skb 3962eaf3cc08SJulian Wiedmann * @skb: skb to map into the buffer 3963eaf3cc08SJulian Wiedmann * @hdr: qeth_hdr for this skb. Either at skb->data, or allocated 3964eaf3cc08SJulian Wiedmann * from qeth_core_header_cache. 3965eaf3cc08SJulian Wiedmann * @offset: when mapping the skb, start at skb->data + offset 3966eaf3cc08SJulian Wiedmann * @hd_len: if > 0, build a dedicated header element of this size 3967eaf3cc08SJulian Wiedmann */ 3968cef6ff22SJulian Wiedmann static int qeth_fill_buffer(struct qeth_qdio_out_q *queue, 3969cc309f83SJulian Wiedmann struct qeth_qdio_out_buffer *buf, 3970cc309f83SJulian Wiedmann struct sk_buff *skb, struct qeth_hdr *hdr, 397113ddacb5SJulian Wiedmann unsigned int offset, unsigned int hd_len) 39724a71df50SFrank Blaschka { 3973eaf3cc08SJulian Wiedmann struct qdio_buffer *buffer = buf->buffer; 3974384d2ef1SJulian Wiedmann bool is_first_elem = true; 397513ddacb5SJulian Wiedmann int flush_cnt = 0; 39764a71df50SFrank Blaschka 397763354797SReshetova, Elena refcount_inc(&skb->users); 39784a71df50SFrank Blaschka skb_queue_tail(&buf->skb_list, skb); 39794a71df50SFrank Blaschka 3980eaf3cc08SJulian Wiedmann /* build dedicated header element */ 3981eaf3cc08SJulian Wiedmann if (hd_len) { 3982683d718aSFrank Blaschka int element = buf->next_element_to_fill; 3983384d2ef1SJulian Wiedmann is_first_elem = false; 3984384d2ef1SJulian Wiedmann 3985683d718aSFrank Blaschka buffer->element[element].addr = hdr; 3986f1588177SJulian Wiedmann buffer->element[element].length = hd_len; 39873ec90878SJan Glauber buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; 3988eaf3cc08SJulian Wiedmann /* remember to free cache-allocated qeth_hdr: */ 3989eaf3cc08SJulian Wiedmann buf->is_header[element] = ((void *)hdr != skb->data); 3990683d718aSFrank Blaschka buf->next_element_to_fill++; 3991683d718aSFrank Blaschka } 3992683d718aSFrank Blaschka 3993384d2ef1SJulian Wiedmann __qeth_fill_buffer(skb, buf, is_first_elem, offset); 39944a71df50SFrank Blaschka 39954a71df50SFrank Blaschka if (!queue->do_pack) { 3996847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfnp"); 39974a71df50SFrank Blaschka /* set state to PRIMED -> will be flushed */ 39984a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 39994a71df50SFrank Blaschka flush_cnt = 1; 40004a71df50SFrank Blaschka } else { 4001847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfpa"); 40024a71df50SFrank Blaschka if (queue->card->options.performance_stats) 40034a71df50SFrank Blaschka queue->card->perf_stats.skbs_sent_pack++; 40044a71df50SFrank Blaschka if (buf->next_element_to_fill >= 40054a71df50SFrank Blaschka QETH_MAX_BUFFER_ELEMENTS(queue->card)) { 40064a71df50SFrank Blaschka /* 40074a71df50SFrank Blaschka * packed buffer if full -> set state PRIMED 40084a71df50SFrank Blaschka * -> will be flushed 40094a71df50SFrank Blaschka */ 40104a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 40114a71df50SFrank Blaschka flush_cnt = 1; 40124a71df50SFrank Blaschka } 40134a71df50SFrank Blaschka } 40144a71df50SFrank Blaschka return flush_cnt; 40154a71df50SFrank Blaschka } 40164a71df50SFrank Blaschka 40177c2e9ba3SJulian Wiedmann int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb, 4018cc309f83SJulian Wiedmann struct qeth_hdr *hdr, unsigned int offset, 401913ddacb5SJulian Wiedmann unsigned int hd_len) 40204a71df50SFrank Blaschka { 40217c2e9ba3SJulian Wiedmann int index = queue->next_buf_to_fill; 40227c2e9ba3SJulian Wiedmann struct qeth_qdio_out_buffer *buffer = queue->bufs[index]; 40234a71df50SFrank Blaschka 40244a71df50SFrank Blaschka /* 40254a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 40264a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 40274a71df50SFrank Blaschka */ 40284a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) 40297c2e9ba3SJulian Wiedmann return -EBUSY; 40307c2e9ba3SJulian Wiedmann queue->next_buf_to_fill = (index + 1) % QDIO_MAX_BUFFERS_PER_Q; 4031683d718aSFrank Blaschka qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); 4032779e6e1cSJan Glauber qeth_flush_buffers(queue, index, 1); 40334a71df50SFrank Blaschka return 0; 40344a71df50SFrank Blaschka } 40354a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast); 40364a71df50SFrank Blaschka 40374a71df50SFrank Blaschka int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, 40384a71df50SFrank Blaschka struct sk_buff *skb, struct qeth_hdr *hdr, 40399c3bfda9SJulian Wiedmann unsigned int offset, unsigned int hd_len, 40409c3bfda9SJulian Wiedmann int elements_needed) 40414a71df50SFrank Blaschka { 40424a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 40434a71df50SFrank Blaschka int start_index; 40444a71df50SFrank Blaschka int flush_count = 0; 40454a71df50SFrank Blaschka int do_pack = 0; 40464a71df50SFrank Blaschka int tmp; 40474a71df50SFrank Blaschka int rc = 0; 40484a71df50SFrank Blaschka 40494a71df50SFrank Blaschka /* spin until we get the queue ... */ 40504a71df50SFrank Blaschka while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, 40514a71df50SFrank Blaschka QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); 40524a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 40530da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 40544a71df50SFrank Blaschka /* 40554a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 40564a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 40574a71df50SFrank Blaschka */ 40584a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { 40594a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 40604a71df50SFrank Blaschka return -EBUSY; 40614a71df50SFrank Blaschka } 40624a71df50SFrank Blaschka /* check if we need to switch packing state of this queue */ 40634a71df50SFrank Blaschka qeth_switch_to_packing_if_needed(queue); 40644a71df50SFrank Blaschka if (queue->do_pack) { 40654a71df50SFrank Blaschka do_pack = 1; 40664a71df50SFrank Blaschka /* does packet fit in current buffer? */ 40674a71df50SFrank Blaschka if ((QETH_MAX_BUFFER_ELEMENTS(card) - 40684a71df50SFrank Blaschka buffer->next_element_to_fill) < elements_needed) { 40694a71df50SFrank Blaschka /* ... no -> set state PRIMED */ 407064ef8957SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 40714a71df50SFrank Blaschka flush_count++; 40724a71df50SFrank Blaschka queue->next_buf_to_fill = 40734a71df50SFrank Blaschka (queue->next_buf_to_fill + 1) % 40744a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 40750da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 40764a71df50SFrank Blaschka /* we did a step forward, so check buffer state 40774a71df50SFrank Blaschka * again */ 40784a71df50SFrank Blaschka if (atomic_read(&buffer->state) != 40794a71df50SFrank Blaschka QETH_QDIO_BUF_EMPTY) { 4080779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, 4081779e6e1cSJan Glauber flush_count); 40824a71df50SFrank Blaschka atomic_set(&queue->state, 40834a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 40843cdc8a25SJulian Wiedmann rc = -EBUSY; 40853cdc8a25SJulian Wiedmann goto out; 40864a71df50SFrank Blaschka } 40874a71df50SFrank Blaschka } 40884a71df50SFrank Blaschka } 40899c3bfda9SJulian Wiedmann tmp = qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); 40904a71df50SFrank Blaschka queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) % 40914a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 40924a71df50SFrank Blaschka flush_count += tmp; 40934a71df50SFrank Blaschka if (flush_count) 4094779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, flush_count); 40954a71df50SFrank Blaschka else if (!atomic_read(&queue->set_pci_flags_count)) 40964a71df50SFrank Blaschka atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH); 40974a71df50SFrank Blaschka /* 40984a71df50SFrank Blaschka * queue->state will go from LOCKED -> UNLOCKED or from 40994a71df50SFrank Blaschka * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us 41004a71df50SFrank Blaschka * (switch packing state or flush buffer to get another pci flag out). 41014a71df50SFrank Blaschka * In that case we will enter this loop 41024a71df50SFrank Blaschka */ 41034a71df50SFrank Blaschka while (atomic_dec_return(&queue->state)) { 41044a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 41054a71df50SFrank Blaschka /* check if we can go back to non-packing state */ 41063cdc8a25SJulian Wiedmann tmp = qeth_switch_to_nonpacking_if_needed(queue); 41074a71df50SFrank Blaschka /* 41084a71df50SFrank Blaschka * check if we need to flush a packing buffer to get a pci 41094a71df50SFrank Blaschka * flag out on the queue 41104a71df50SFrank Blaschka */ 41113cdc8a25SJulian Wiedmann if (!tmp && !atomic_read(&queue->set_pci_flags_count)) 41123cdc8a25SJulian Wiedmann tmp = qeth_prep_flush_pack_buffer(queue); 41133cdc8a25SJulian Wiedmann if (tmp) { 41143cdc8a25SJulian Wiedmann qeth_flush_buffers(queue, start_index, tmp); 41153cdc8a25SJulian Wiedmann flush_count += tmp; 41164a71df50SFrank Blaschka } 41173cdc8a25SJulian Wiedmann } 41183cdc8a25SJulian Wiedmann out: 41194a71df50SFrank Blaschka /* at this point the queue is UNLOCKED again */ 41204a71df50SFrank Blaschka if (queue->card->options.performance_stats && do_pack) 41214a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += flush_count; 41224a71df50SFrank Blaschka 41234a71df50SFrank Blaschka return rc; 41244a71df50SFrank Blaschka } 41254a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet); 41264a71df50SFrank Blaschka 41274a71df50SFrank Blaschka static int qeth_setadp_promisc_mode_cb(struct qeth_card *card, 41284a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 41294a71df50SFrank Blaschka { 41304a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 41314a71df50SFrank Blaschka struct qeth_ipacmd_setadpparms *setparms; 41324a71df50SFrank Blaschka 4133847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "prmadpcb"); 41344a71df50SFrank Blaschka 41354a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 41364a71df50SFrank Blaschka setparms = &(cmd->data.setadapterparms); 41374a71df50SFrank Blaschka 41384a71df50SFrank Blaschka qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); 41394a71df50SFrank Blaschka if (cmd->hdr.return_code) { 41408a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "prmrc%x", cmd->hdr.return_code); 41414a71df50SFrank Blaschka setparms->data.mode = SET_PROMISC_MODE_OFF; 41424a71df50SFrank Blaschka } 41434a71df50SFrank Blaschka card->info.promisc_mode = setparms->data.mode; 41444a71df50SFrank Blaschka return 0; 41454a71df50SFrank Blaschka } 41464a71df50SFrank Blaschka 41474a71df50SFrank Blaschka void qeth_setadp_promisc_mode(struct qeth_card *card) 41484a71df50SFrank Blaschka { 41494a71df50SFrank Blaschka enum qeth_ipa_promisc_modes mode; 41504a71df50SFrank Blaschka struct net_device *dev = card->dev; 41514a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 41524a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 41534a71df50SFrank Blaschka 4154847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setprom"); 41554a71df50SFrank Blaschka 41564a71df50SFrank Blaschka if (((dev->flags & IFF_PROMISC) && 41574a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_ON)) || 41584a71df50SFrank Blaschka (!(dev->flags & IFF_PROMISC) && 41594a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_OFF))) 41604a71df50SFrank Blaschka return; 41614a71df50SFrank Blaschka mode = SET_PROMISC_MODE_OFF; 41624a71df50SFrank Blaschka if (dev->flags & IFF_PROMISC) 41634a71df50SFrank Blaschka mode = SET_PROMISC_MODE_ON; 4164847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "mode:%x", mode); 41654a71df50SFrank Blaschka 41664a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE, 4167ca5b20acSStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr) + 8); 41681aec42bcSThomas Richter if (!iob) 41691aec42bcSThomas Richter return; 41704a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE); 41714a71df50SFrank Blaschka cmd->data.setadapterparms.data.mode = mode; 41724a71df50SFrank Blaschka qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL); 41734a71df50SFrank Blaschka } 41744a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode); 41754a71df50SFrank Blaschka 41764a71df50SFrank Blaschka int qeth_change_mtu(struct net_device *dev, int new_mtu) 41774a71df50SFrank Blaschka { 41784a71df50SFrank Blaschka struct qeth_card *card; 41794a71df50SFrank Blaschka char dbf_text[15]; 41804a71df50SFrank Blaschka 4181509e2562SHeiko Carstens card = dev->ml_priv; 41824a71df50SFrank Blaschka 4183847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmtu"); 41844a71df50SFrank Blaschka sprintf(dbf_text, "%8x", new_mtu); 4185847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, dbf_text); 41864a71df50SFrank Blaschka 41874845b93fSJulian Wiedmann if (!qeth_mtu_is_valid(card, new_mtu)) 41884a71df50SFrank Blaschka return -EINVAL; 41894a71df50SFrank Blaschka dev->mtu = new_mtu; 41904a71df50SFrank Blaschka return 0; 41914a71df50SFrank Blaschka } 41924a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_change_mtu); 41934a71df50SFrank Blaschka 41944a71df50SFrank Blaschka struct net_device_stats *qeth_get_stats(struct net_device *dev) 41954a71df50SFrank Blaschka { 41964a71df50SFrank Blaschka struct qeth_card *card; 41974a71df50SFrank Blaschka 4198509e2562SHeiko Carstens card = dev->ml_priv; 41994a71df50SFrank Blaschka 4200847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "getstat"); 42014a71df50SFrank Blaschka 42024a71df50SFrank Blaschka return &card->stats; 42034a71df50SFrank Blaschka } 42044a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_stats); 42054a71df50SFrank Blaschka 42064a71df50SFrank Blaschka static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card, 42074a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 42084a71df50SFrank Blaschka { 42094a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 42104a71df50SFrank Blaschka 4211847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmaccb"); 42124a71df50SFrank Blaschka 42134a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 42144a71df50SFrank Blaschka if (!card->options.layer2 || 42154a71df50SFrank Blaschka !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) { 42164a71df50SFrank Blaschka memcpy(card->dev->dev_addr, 42174a71df50SFrank Blaschka &cmd->data.setadapterparms.data.change_addr.addr, 42184a71df50SFrank Blaschka OSA_ADDR_LEN); 42194a71df50SFrank Blaschka card->info.mac_bits |= QETH_LAYER2_MAC_READ; 42204a71df50SFrank Blaschka } 42214a71df50SFrank Blaschka qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 42224a71df50SFrank Blaschka return 0; 42234a71df50SFrank Blaschka } 42244a71df50SFrank Blaschka 42254a71df50SFrank Blaschka int qeth_setadpparms_change_macaddr(struct qeth_card *card) 42264a71df50SFrank Blaschka { 42274a71df50SFrank Blaschka int rc; 42284a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 42294a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 42304a71df50SFrank Blaschka 4231847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmac"); 42324a71df50SFrank Blaschka 42334a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_ALTER_MAC_ADDRESS, 4234ca5b20acSStefan Raspl sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4235ca5b20acSStefan Raspl sizeof(struct qeth_change_addr)); 42361aec42bcSThomas Richter if (!iob) 42371aec42bcSThomas Richter return -ENOMEM; 42384a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 42394a71df50SFrank Blaschka cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; 42404a71df50SFrank Blaschka cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN; 42414a71df50SFrank Blaschka memcpy(&cmd->data.setadapterparms.data.change_addr.addr, 42424a71df50SFrank Blaschka card->dev->dev_addr, OSA_ADDR_LEN); 42434a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_change_macaddr_cb, 42444a71df50SFrank Blaschka NULL); 42454a71df50SFrank Blaschka return rc; 42464a71df50SFrank Blaschka } 42474a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr); 42484a71df50SFrank Blaschka 4249d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, 4250d64ecc22SEinar Lueck struct qeth_reply *reply, unsigned long data) 4251d64ecc22SEinar Lueck { 4252d64ecc22SEinar Lueck struct qeth_ipa_cmd *cmd; 4253d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 42540f54761dSStefan Raspl int fallback = *(int *)reply->param; 4255d64ecc22SEinar Lueck 4256847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setaccb"); 4257d64ecc22SEinar Lueck 4258d64ecc22SEinar Lueck cmd = (struct qeth_ipa_cmd *) data; 4259d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4260d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setaccb"); 4261d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4262d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", 4263d64ecc22SEinar Lueck cmd->data.setadapterparms.hdr.return_code); 42640f54761dSStefan Raspl if (cmd->data.setadapterparms.hdr.return_code != 42650f54761dSStefan Raspl SET_ACCESS_CTRL_RC_SUCCESS) 42660f54761dSStefan Raspl QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", 42670f54761dSStefan Raspl card->gdev->dev.kobj.name, 42680f54761dSStefan Raspl access_ctrl_req->subcmd_code, 42690f54761dSStefan Raspl cmd->data.setadapterparms.hdr.return_code); 4270d64ecc22SEinar Lueck switch (cmd->data.setadapterparms.hdr.return_code) { 4271d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_SUCCESS: 4272d64ecc22SEinar Lueck if (card->options.isolation == ISOLATION_MODE_NONE) { 4273d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4274d64ecc22SEinar Lueck "QDIO data connection isolation is deactivated\n"); 4275d64ecc22SEinar Lueck } else { 4276d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4277d64ecc22SEinar Lueck "QDIO data connection isolation is activated\n"); 4278d64ecc22SEinar Lueck } 4279d64ecc22SEinar Lueck break; 42800f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: 42810f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already " 42820f54761dSStefan Raspl "deactivated\n", dev_name(&card->gdev->dev)); 42830f54761dSStefan Raspl if (fallback) 42840f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 42850f54761dSStefan Raspl break; 42860f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: 42870f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already" 42880f54761dSStefan Raspl " activated\n", dev_name(&card->gdev->dev)); 42890f54761dSStefan Raspl if (fallback) 42900f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 42910f54761dSStefan Raspl break; 4292d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: 4293d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4294d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4295d64ecc22SEinar Lueck break; 4296d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: 4297d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4298d64ecc22SEinar Lueck "Adapter is dedicated. " 4299d64ecc22SEinar Lueck "QDIO data connection isolation not supported\n"); 43000f54761dSStefan Raspl if (fallback) 43010f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4302d64ecc22SEinar Lueck break; 4303d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: 4304d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4305d64ecc22SEinar Lueck "TSO does not permit QDIO data connection isolation\n"); 43060f54761dSStefan Raspl if (fallback) 43070f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4308d64ecc22SEinar Lueck break; 43090f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED: 43100f54761dSStefan Raspl dev_err(&card->gdev->dev, "The adjacent switch port does not " 43110f54761dSStefan Raspl "support reflective relay mode\n"); 43120f54761dSStefan Raspl if (fallback) 43130f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43140f54761dSStefan Raspl break; 43150f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_FAILED: 43160f54761dSStefan Raspl dev_err(&card->gdev->dev, "The reflective relay mode cannot be " 43170f54761dSStefan Raspl "enabled at the adjacent switch port"); 43180f54761dSStefan Raspl if (fallback) 43190f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 43200f54761dSStefan Raspl break; 43210f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED: 43220f54761dSStefan Raspl dev_warn(&card->gdev->dev, "Turning off reflective relay mode " 43230f54761dSStefan Raspl "at the adjacent switch failed\n"); 43240f54761dSStefan Raspl break; 4325d64ecc22SEinar Lueck default: 4326d64ecc22SEinar Lueck /* this should never happen */ 43270f54761dSStefan Raspl if (fallback) 43280f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4329d64ecc22SEinar Lueck break; 4330d64ecc22SEinar Lueck } 4331d64ecc22SEinar Lueck qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 4332bbb822a8SUrsula Braun return 0; 4333d64ecc22SEinar Lueck } 4334d64ecc22SEinar Lueck 4335d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, 43360f54761dSStefan Raspl enum qeth_ipa_isolation_modes isolation, int fallback) 4337d64ecc22SEinar Lueck { 4338d64ecc22SEinar Lueck int rc; 4339d64ecc22SEinar Lueck struct qeth_cmd_buffer *iob; 4340d64ecc22SEinar Lueck struct qeth_ipa_cmd *cmd; 4341d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 4342d64ecc22SEinar Lueck 4343847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setacctl"); 4344d64ecc22SEinar Lueck 4345d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setacctl"); 4346d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4347d64ecc22SEinar Lueck 4348d64ecc22SEinar Lueck iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, 4349d64ecc22SEinar Lueck sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4350d64ecc22SEinar Lueck sizeof(struct qeth_set_access_ctrl)); 43511aec42bcSThomas Richter if (!iob) 43521aec42bcSThomas Richter return -ENOMEM; 4353d64ecc22SEinar Lueck cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 4354d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4355d64ecc22SEinar Lueck access_ctrl_req->subcmd_code = isolation; 4356d64ecc22SEinar Lueck 4357d64ecc22SEinar Lueck rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, 43580f54761dSStefan Raspl &fallback); 4359d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); 4360d64ecc22SEinar Lueck return rc; 4361d64ecc22SEinar Lueck } 4362d64ecc22SEinar Lueck 43630f54761dSStefan Raspl int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) 4364d64ecc22SEinar Lueck { 4365d64ecc22SEinar Lueck int rc = 0; 4366d64ecc22SEinar Lueck 4367847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setactlo"); 4368d64ecc22SEinar Lueck 43695113fec0SUrsula Braun if ((card->info.type == QETH_CARD_TYPE_OSD || 43705113fec0SUrsula Braun card->info.type == QETH_CARD_TYPE_OSX) && 4371d64ecc22SEinar Lueck qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { 4372d64ecc22SEinar Lueck rc = qeth_setadpparms_set_access_ctrl(card, 43730f54761dSStefan Raspl card->options.isolation, fallback); 4374d64ecc22SEinar Lueck if (rc) { 4375d64ecc22SEinar Lueck QETH_DBF_MESSAGE(3, 43765113fec0SUrsula Braun "IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n", 4377d64ecc22SEinar Lueck card->gdev->dev.kobj.name, 4378d64ecc22SEinar Lueck rc); 43790f54761dSStefan Raspl rc = -EOPNOTSUPP; 4380d64ecc22SEinar Lueck } 4381d64ecc22SEinar Lueck } else if (card->options.isolation != ISOLATION_MODE_NONE) { 4382d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 4383d64ecc22SEinar Lueck 4384d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4385d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4386d64ecc22SEinar Lueck rc = -EOPNOTSUPP; 4387d64ecc22SEinar Lueck } 4388d64ecc22SEinar Lueck return rc; 4389d64ecc22SEinar Lueck } 4390d64ecc22SEinar Lueck EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); 4391d64ecc22SEinar Lueck 43924a71df50SFrank Blaschka void qeth_tx_timeout(struct net_device *dev) 43934a71df50SFrank Blaschka { 43944a71df50SFrank Blaschka struct qeth_card *card; 43954a71df50SFrank Blaschka 4396509e2562SHeiko Carstens card = dev->ml_priv; 4397847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "txtimeo"); 43984a71df50SFrank Blaschka card->stats.tx_errors++; 43994a71df50SFrank Blaschka qeth_schedule_recovery(card); 44004a71df50SFrank Blaschka } 44014a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_tx_timeout); 44024a71df50SFrank Blaschka 4403942d6984SJulian Wiedmann static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) 44044a71df50SFrank Blaschka { 4405509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 44064a71df50SFrank Blaschka int rc = 0; 44074a71df50SFrank Blaschka 44084a71df50SFrank Blaschka switch (regnum) { 44094a71df50SFrank Blaschka case MII_BMCR: /* Basic mode control register */ 44104a71df50SFrank Blaschka rc = BMCR_FULLDPLX; 44114a71df50SFrank Blaschka if ((card->info.link_type != QETH_LINK_TYPE_GBIT_ETH) && 44124a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_OSN) && 44134a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_10GBIT_ETH)) 44144a71df50SFrank Blaschka rc |= BMCR_SPEED100; 44154a71df50SFrank Blaschka break; 44164a71df50SFrank Blaschka case MII_BMSR: /* Basic mode status register */ 44174a71df50SFrank Blaschka rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS | 44184a71df50SFrank Blaschka BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL | 44194a71df50SFrank Blaschka BMSR_100BASE4; 44204a71df50SFrank Blaschka break; 44214a71df50SFrank Blaschka case MII_PHYSID1: /* PHYS ID 1 */ 44224a71df50SFrank Blaschka rc = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 8) | 44234a71df50SFrank Blaschka dev->dev_addr[2]; 44244a71df50SFrank Blaschka rc = (rc >> 5) & 0xFFFF; 44254a71df50SFrank Blaschka break; 44264a71df50SFrank Blaschka case MII_PHYSID2: /* PHYS ID 2 */ 44274a71df50SFrank Blaschka rc = (dev->dev_addr[2] << 10) & 0xFFFF; 44284a71df50SFrank Blaschka break; 44294a71df50SFrank Blaschka case MII_ADVERTISE: /* Advertisement control reg */ 44304a71df50SFrank Blaschka rc = ADVERTISE_ALL; 44314a71df50SFrank Blaschka break; 44324a71df50SFrank Blaschka case MII_LPA: /* Link partner ability reg */ 44334a71df50SFrank Blaschka rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL | 44344a71df50SFrank Blaschka LPA_100BASE4 | LPA_LPACK; 44354a71df50SFrank Blaschka break; 44364a71df50SFrank Blaschka case MII_EXPANSION: /* Expansion register */ 44374a71df50SFrank Blaschka break; 44384a71df50SFrank Blaschka case MII_DCOUNTER: /* disconnect counter */ 44394a71df50SFrank Blaschka break; 44404a71df50SFrank Blaschka case MII_FCSCOUNTER: /* false carrier counter */ 44414a71df50SFrank Blaschka break; 44424a71df50SFrank Blaschka case MII_NWAYTEST: /* N-way auto-neg test register */ 44434a71df50SFrank Blaschka break; 44444a71df50SFrank Blaschka case MII_RERRCOUNTER: /* rx error counter */ 44454a71df50SFrank Blaschka rc = card->stats.rx_errors; 44464a71df50SFrank Blaschka break; 44474a71df50SFrank Blaschka case MII_SREVISION: /* silicon revision */ 44484a71df50SFrank Blaschka break; 44494a71df50SFrank Blaschka case MII_RESV1: /* reserved 1 */ 44504a71df50SFrank Blaschka break; 44514a71df50SFrank Blaschka case MII_LBRERROR: /* loopback, rx, bypass error */ 44524a71df50SFrank Blaschka break; 44534a71df50SFrank Blaschka case MII_PHYADDR: /* physical address */ 44544a71df50SFrank Blaschka break; 44554a71df50SFrank Blaschka case MII_RESV2: /* reserved 2 */ 44564a71df50SFrank Blaschka break; 44574a71df50SFrank Blaschka case MII_TPISTATUS: /* TPI status for 10mbps */ 44584a71df50SFrank Blaschka break; 44594a71df50SFrank Blaschka case MII_NCONFIG: /* network interface config */ 44604a71df50SFrank Blaschka break; 44614a71df50SFrank Blaschka default: 44624a71df50SFrank Blaschka break; 44634a71df50SFrank Blaschka } 44644a71df50SFrank Blaschka return rc; 44654a71df50SFrank Blaschka } 44664a71df50SFrank Blaschka 44674a71df50SFrank Blaschka static int qeth_send_ipa_snmp_cmd(struct qeth_card *card, 44684a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, int len, 44694a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply *, 44704a71df50SFrank Blaschka unsigned long), 44714a71df50SFrank Blaschka void *reply_param) 44724a71df50SFrank Blaschka { 44734a71df50SFrank Blaschka u16 s1, s2; 44744a71df50SFrank Blaschka 4475847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendsnmp"); 44764a71df50SFrank Blaschka 44774a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 44784a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 44794a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 44804a71df50SFrank Blaschka /* adjust PDU length fields in IPA_PDU_HEADER */ 44814a71df50SFrank Blaschka s1 = (u32) IPA_PDU_HEADER_SIZE + len; 44824a71df50SFrank Blaschka s2 = (u32) len; 44834a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2); 44844a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2); 44854a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2); 44864a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2); 44874a71df50SFrank Blaschka return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob, 44884a71df50SFrank Blaschka reply_cb, reply_param); 44894a71df50SFrank Blaschka } 44904a71df50SFrank Blaschka 44914a71df50SFrank Blaschka static int qeth_snmp_command_cb(struct qeth_card *card, 44924a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long sdata) 44934a71df50SFrank Blaschka { 44944a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 44954a71df50SFrank Blaschka struct qeth_arp_query_info *qinfo; 44964a71df50SFrank Blaschka struct qeth_snmp_cmd *snmp; 44974a71df50SFrank Blaschka unsigned char *data; 44984a71df50SFrank Blaschka __u16 data_len; 44994a71df50SFrank Blaschka 4500847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snpcmdcb"); 45014a71df50SFrank Blaschka 45024a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) sdata; 45034a71df50SFrank Blaschka data = (unsigned char *)((char *)cmd - reply->offset); 45044a71df50SFrank Blaschka qinfo = (struct qeth_arp_query_info *) reply->param; 45054a71df50SFrank Blaschka snmp = &cmd->data.setadapterparms.data.snmp; 45064a71df50SFrank Blaschka 45074a71df50SFrank Blaschka if (cmd->hdr.return_code) { 45088a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "scer1%x", cmd->hdr.return_code); 45094a71df50SFrank Blaschka return 0; 45104a71df50SFrank Blaschka } 45114a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.return_code) { 45124a71df50SFrank Blaschka cmd->hdr.return_code = 45134a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 45148a593148SThomas Richter QETH_CARD_TEXT_(card, 4, "scer2%x", cmd->hdr.return_code); 45154a71df50SFrank Blaschka return 0; 45164a71df50SFrank Blaschka } 45174a71df50SFrank Blaschka data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data)); 45184a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) 45194a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->data - (char *)cmd); 45204a71df50SFrank Blaschka else 45214a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->request - (char *)cmd); 45224a71df50SFrank Blaschka 45234a71df50SFrank Blaschka /* check if there is enough room in userspace */ 45244a71df50SFrank Blaschka if ((qinfo->udata_len - qinfo->udata_offset) < data_len) { 4525847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM); 4526e0a8114cSUrsula Braun cmd->hdr.return_code = IPA_RC_ENOMEM; 45274a71df50SFrank Blaschka return 0; 45284a71df50SFrank Blaschka } 4529847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "snore%i", 45304a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4531847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "sseqn%i", 45324a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 45334a71df50SFrank Blaschka /*copy entries to user buffer*/ 45344a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) { 45354a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 45364a71df50SFrank Blaschka (char *)snmp, 45374a71df50SFrank Blaschka data_len + offsetof(struct qeth_snmp_cmd, data)); 45384a71df50SFrank Blaschka qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data); 45394a71df50SFrank Blaschka } else { 45404a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 45414a71df50SFrank Blaschka (char *)&snmp->request, data_len); 45424a71df50SFrank Blaschka } 45434a71df50SFrank Blaschka qinfo->udata_offset += data_len; 45444a71df50SFrank Blaschka /* check if all replies received ... */ 4545847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srtot%i", 45464a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4547847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srseq%i", 45484a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 45494a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 45504a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 45514a71df50SFrank Blaschka return 1; 45524a71df50SFrank Blaschka return 0; 45534a71df50SFrank Blaschka } 45544a71df50SFrank Blaschka 4555942d6984SJulian Wiedmann static int qeth_snmp_command(struct qeth_card *card, char __user *udata) 45564a71df50SFrank Blaschka { 45574a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 45584a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 45594a71df50SFrank Blaschka struct qeth_snmp_ureq *ureq; 45606fb392b1SUrsula Braun unsigned int req_len; 45614a71df50SFrank Blaschka struct qeth_arp_query_info qinfo = {0, }; 45624a71df50SFrank Blaschka int rc = 0; 45634a71df50SFrank Blaschka 4564847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snmpcmd"); 45654a71df50SFrank Blaschka 45664a71df50SFrank Blaschka if (card->info.guestlan) 45674a71df50SFrank Blaschka return -EOPNOTSUPP; 45684a71df50SFrank Blaschka 45694a71df50SFrank Blaschka if ((!qeth_adp_supported(card, IPA_SETADP_SET_SNMP_CONTROL)) && 45704a71df50SFrank Blaschka (!card->options.layer2)) { 45714a71df50SFrank Blaschka return -EOPNOTSUPP; 45724a71df50SFrank Blaschka } 45734a71df50SFrank Blaschka /* skip 4 bytes (data_len struct member) to get req_len */ 45744a71df50SFrank Blaschka if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int))) 45754a71df50SFrank Blaschka return -EFAULT; 45766fb392b1SUrsula Braun if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - 45776fb392b1SUrsula Braun sizeof(struct qeth_ipacmd_hdr) - 45786fb392b1SUrsula Braun sizeof(struct qeth_ipacmd_setadpparms_hdr))) 45796fb392b1SUrsula Braun return -EINVAL; 45804986f3f0SJulia Lawall ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr)); 45814986f3f0SJulia Lawall if (IS_ERR(ureq)) { 4582847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "snmpnome"); 45834986f3f0SJulia Lawall return PTR_ERR(ureq); 45844a71df50SFrank Blaschka } 45854a71df50SFrank Blaschka qinfo.udata_len = ureq->hdr.data_len; 45864a71df50SFrank Blaschka qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); 45874a71df50SFrank Blaschka if (!qinfo.udata) { 45884a71df50SFrank Blaschka kfree(ureq); 45894a71df50SFrank Blaschka return -ENOMEM; 45904a71df50SFrank Blaschka } 45914a71df50SFrank Blaschka qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr); 45924a71df50SFrank Blaschka 45934a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, 45944a71df50SFrank Blaschka QETH_SNMP_SETADP_CMDLENGTH + req_len); 45951aec42bcSThomas Richter if (!iob) { 45961aec42bcSThomas Richter rc = -ENOMEM; 45971aec42bcSThomas Richter goto out; 45981aec42bcSThomas Richter } 45994a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 46004a71df50SFrank Blaschka memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len); 46014a71df50SFrank Blaschka rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len, 46024a71df50SFrank Blaschka qeth_snmp_command_cb, (void *)&qinfo); 46034a71df50SFrank Blaschka if (rc) 460414cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "SNMP command failed on %s: (0x%x)\n", 46054a71df50SFrank Blaschka QETH_CARD_IFNAME(card), rc); 46064a71df50SFrank Blaschka else { 46074a71df50SFrank Blaschka if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) 46084a71df50SFrank Blaschka rc = -EFAULT; 46094a71df50SFrank Blaschka } 46101aec42bcSThomas Richter out: 46114a71df50SFrank Blaschka kfree(ureq); 46124a71df50SFrank Blaschka kfree(qinfo.udata); 46134a71df50SFrank Blaschka return rc; 46144a71df50SFrank Blaschka } 46154a71df50SFrank Blaschka 4616c3ab96f3SFrank Blaschka static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, 4617c3ab96f3SFrank Blaschka struct qeth_reply *reply, unsigned long data) 4618c3ab96f3SFrank Blaschka { 4619c3ab96f3SFrank Blaschka struct qeth_ipa_cmd *cmd; 4620c3ab96f3SFrank Blaschka struct qeth_qoat_priv *priv; 4621c3ab96f3SFrank Blaschka char *resdata; 4622c3ab96f3SFrank Blaschka int resdatalen; 4623c3ab96f3SFrank Blaschka 4624c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcb"); 4625c3ab96f3SFrank Blaschka 4626c3ab96f3SFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 4627c3ab96f3SFrank Blaschka priv = (struct qeth_qoat_priv *)reply->param; 4628c3ab96f3SFrank Blaschka resdatalen = cmd->data.setadapterparms.hdr.cmdlength; 4629c3ab96f3SFrank Blaschka resdata = (char *)data + 28; 4630c3ab96f3SFrank Blaschka 4631c3ab96f3SFrank Blaschka if (resdatalen > (priv->buffer_len - priv->response_len)) { 4632c3ab96f3SFrank Blaschka cmd->hdr.return_code = IPA_RC_FFFF; 4633c3ab96f3SFrank Blaschka return 0; 4634c3ab96f3SFrank Blaschka } 4635c3ab96f3SFrank Blaschka 4636c3ab96f3SFrank Blaschka memcpy((priv->buffer + priv->response_len), resdata, 4637c3ab96f3SFrank Blaschka resdatalen); 4638c3ab96f3SFrank Blaschka priv->response_len += resdatalen; 4639c3ab96f3SFrank Blaschka 4640c3ab96f3SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 4641c3ab96f3SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 4642c3ab96f3SFrank Blaschka return 1; 4643c3ab96f3SFrank Blaschka return 0; 4644c3ab96f3SFrank Blaschka } 4645c3ab96f3SFrank Blaschka 4646942d6984SJulian Wiedmann static int qeth_query_oat_command(struct qeth_card *card, char __user *udata) 4647c3ab96f3SFrank Blaschka { 4648c3ab96f3SFrank Blaschka int rc = 0; 4649c3ab96f3SFrank Blaschka struct qeth_cmd_buffer *iob; 4650c3ab96f3SFrank Blaschka struct qeth_ipa_cmd *cmd; 4651c3ab96f3SFrank Blaschka struct qeth_query_oat *oat_req; 4652c3ab96f3SFrank Blaschka struct qeth_query_oat_data oat_data; 4653c3ab96f3SFrank Blaschka struct qeth_qoat_priv priv; 4654c3ab96f3SFrank Blaschka void __user *tmp; 4655c3ab96f3SFrank Blaschka 4656c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcmd"); 4657c3ab96f3SFrank Blaschka 4658c3ab96f3SFrank Blaschka if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { 4659c3ab96f3SFrank Blaschka rc = -EOPNOTSUPP; 4660c3ab96f3SFrank Blaschka goto out; 4661c3ab96f3SFrank Blaschka } 4662c3ab96f3SFrank Blaschka 4663c3ab96f3SFrank Blaschka if (copy_from_user(&oat_data, udata, 4664c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) { 4665c3ab96f3SFrank Blaschka rc = -EFAULT; 4666c3ab96f3SFrank Blaschka goto out; 4667c3ab96f3SFrank Blaschka } 4668c3ab96f3SFrank Blaschka 4669c3ab96f3SFrank Blaschka priv.buffer_len = oat_data.buffer_len; 4670c3ab96f3SFrank Blaschka priv.response_len = 0; 4671c3ab96f3SFrank Blaschka priv.buffer = kzalloc(oat_data.buffer_len, GFP_KERNEL); 4672c3ab96f3SFrank Blaschka if (!priv.buffer) { 4673c3ab96f3SFrank Blaschka rc = -ENOMEM; 4674c3ab96f3SFrank Blaschka goto out; 4675c3ab96f3SFrank Blaschka } 4676c3ab96f3SFrank Blaschka 4677c3ab96f3SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, 4678c3ab96f3SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4679c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat)); 46801aec42bcSThomas Richter if (!iob) { 46811aec42bcSThomas Richter rc = -ENOMEM; 46821aec42bcSThomas Richter goto out_free; 46831aec42bcSThomas Richter } 4684c3ab96f3SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 4685c3ab96f3SFrank Blaschka oat_req = &cmd->data.setadapterparms.data.query_oat; 4686c3ab96f3SFrank Blaschka oat_req->subcmd_code = oat_data.command; 4687c3ab96f3SFrank Blaschka 4688c3ab96f3SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb, 4689c3ab96f3SFrank Blaschka &priv); 4690c3ab96f3SFrank Blaschka if (!rc) { 4691c3ab96f3SFrank Blaschka if (is_compat_task()) 4692c3ab96f3SFrank Blaschka tmp = compat_ptr(oat_data.ptr); 4693c3ab96f3SFrank Blaschka else 4694c3ab96f3SFrank Blaschka tmp = (void __user *)(unsigned long)oat_data.ptr; 4695c3ab96f3SFrank Blaschka 4696c3ab96f3SFrank Blaschka if (copy_to_user(tmp, priv.buffer, 4697c3ab96f3SFrank Blaschka priv.response_len)) { 4698c3ab96f3SFrank Blaschka rc = -EFAULT; 4699c3ab96f3SFrank Blaschka goto out_free; 4700c3ab96f3SFrank Blaschka } 4701c3ab96f3SFrank Blaschka 4702c3ab96f3SFrank Blaschka oat_data.response_len = priv.response_len; 4703c3ab96f3SFrank Blaschka 4704c3ab96f3SFrank Blaschka if (copy_to_user(udata, &oat_data, 4705c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) 4706c3ab96f3SFrank Blaschka rc = -EFAULT; 4707c3ab96f3SFrank Blaschka } else 4708c3ab96f3SFrank Blaschka if (rc == IPA_RC_FFFF) 4709c3ab96f3SFrank Blaschka rc = -EFAULT; 4710c3ab96f3SFrank Blaschka 4711c3ab96f3SFrank Blaschka out_free: 4712c3ab96f3SFrank Blaschka kfree(priv.buffer); 4713c3ab96f3SFrank Blaschka out: 4714c3ab96f3SFrank Blaschka return rc; 4715c3ab96f3SFrank Blaschka } 4716c3ab96f3SFrank Blaschka 4717e71e4072SHeiko Carstens static int qeth_query_card_info_cb(struct qeth_card *card, 471802d5cb5bSEugene Crosser struct qeth_reply *reply, unsigned long data) 471902d5cb5bSEugene Crosser { 472002d5cb5bSEugene Crosser struct qeth_ipa_cmd *cmd; 472102d5cb5bSEugene Crosser struct qeth_query_card_info *card_info; 472202d5cb5bSEugene Crosser struct carrier_info *carrier_info; 472302d5cb5bSEugene Crosser 472402d5cb5bSEugene Crosser QETH_CARD_TEXT(card, 2, "qcrdincb"); 472502d5cb5bSEugene Crosser carrier_info = (struct carrier_info *)reply->param; 472602d5cb5bSEugene Crosser cmd = (struct qeth_ipa_cmd *)data; 472702d5cb5bSEugene Crosser card_info = &cmd->data.setadapterparms.data.card_info; 472802d5cb5bSEugene Crosser if (cmd->data.setadapterparms.hdr.return_code == 0) { 472902d5cb5bSEugene Crosser carrier_info->card_type = card_info->card_type; 473002d5cb5bSEugene Crosser carrier_info->port_mode = card_info->port_mode; 473102d5cb5bSEugene Crosser carrier_info->port_speed = card_info->port_speed; 473202d5cb5bSEugene Crosser } 473302d5cb5bSEugene Crosser 473402d5cb5bSEugene Crosser qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 473502d5cb5bSEugene Crosser return 0; 473602d5cb5bSEugene Crosser } 473702d5cb5bSEugene Crosser 4738bca51650SThomas Richter static int qeth_query_card_info(struct qeth_card *card, 473902d5cb5bSEugene Crosser struct carrier_info *carrier_info) 474002d5cb5bSEugene Crosser { 474102d5cb5bSEugene Crosser struct qeth_cmd_buffer *iob; 474202d5cb5bSEugene Crosser 474302d5cb5bSEugene Crosser QETH_CARD_TEXT(card, 2, "qcrdinfo"); 474402d5cb5bSEugene Crosser if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) 474502d5cb5bSEugene Crosser return -EOPNOTSUPP; 474602d5cb5bSEugene Crosser iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 474702d5cb5bSEugene Crosser sizeof(struct qeth_ipacmd_setadpparms_hdr)); 47481aec42bcSThomas Richter if (!iob) 47491aec42bcSThomas Richter return -ENOMEM; 475002d5cb5bSEugene Crosser return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, 475102d5cb5bSEugene Crosser (void *)carrier_info); 475202d5cb5bSEugene Crosser } 475302d5cb5bSEugene Crosser 4754ec61bd2fSJulian Wiedmann /** 4755ec61bd2fSJulian Wiedmann * qeth_vm_request_mac() - Request a hypervisor-managed MAC address 4756ec61bd2fSJulian Wiedmann * @card: pointer to a qeth_card 4757ec61bd2fSJulian Wiedmann * 4758ec61bd2fSJulian Wiedmann * Returns 4759ec61bd2fSJulian Wiedmann * 0, if a MAC address has been set for the card's netdevice 4760ec61bd2fSJulian Wiedmann * a return code, for various error conditions 4761ec61bd2fSJulian Wiedmann */ 4762ec61bd2fSJulian Wiedmann int qeth_vm_request_mac(struct qeth_card *card) 4763ec61bd2fSJulian Wiedmann { 4764ec61bd2fSJulian Wiedmann struct diag26c_mac_resp *response; 4765ec61bd2fSJulian Wiedmann struct diag26c_mac_req *request; 4766ec61bd2fSJulian Wiedmann struct ccw_dev_id id; 4767ec61bd2fSJulian Wiedmann int rc; 4768ec61bd2fSJulian Wiedmann 4769ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "vmreqmac"); 4770ec61bd2fSJulian Wiedmann 4771ec61bd2fSJulian Wiedmann if (!card->dev) 4772ec61bd2fSJulian Wiedmann return -ENODEV; 4773ec61bd2fSJulian Wiedmann 4774ec61bd2fSJulian Wiedmann request = kzalloc(sizeof(*request), GFP_KERNEL | GFP_DMA); 4775ec61bd2fSJulian Wiedmann response = kzalloc(sizeof(*response), GFP_KERNEL | GFP_DMA); 4776ec61bd2fSJulian Wiedmann if (!request || !response) { 4777ec61bd2fSJulian Wiedmann rc = -ENOMEM; 4778ec61bd2fSJulian Wiedmann goto out; 4779ec61bd2fSJulian Wiedmann } 4780ec61bd2fSJulian Wiedmann 4781ec61bd2fSJulian Wiedmann ccw_device_get_id(CARD_DDEV(card), &id); 4782ec61bd2fSJulian Wiedmann request->resp_buf_len = sizeof(*response); 4783ec61bd2fSJulian Wiedmann request->resp_version = DIAG26C_VERSION2; 4784ec61bd2fSJulian Wiedmann request->op_code = DIAG26C_GET_MAC; 4785ec61bd2fSJulian Wiedmann request->devno = id.devno; 4786ec61bd2fSJulian Wiedmann 4787ec61bd2fSJulian Wiedmann rc = diag26c(request, response, DIAG26C_MAC_SERVICES); 4788ec61bd2fSJulian Wiedmann if (rc) 4789ec61bd2fSJulian Wiedmann goto out; 4790ec61bd2fSJulian Wiedmann 4791ec61bd2fSJulian Wiedmann if (request->resp_buf_len < sizeof(*response) || 4792ec61bd2fSJulian Wiedmann response->version != request->resp_version) { 4793ec61bd2fSJulian Wiedmann rc = -EIO; 4794ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "badresp"); 4795ec61bd2fSJulian Wiedmann QETH_DBF_HEX(SETUP, 2, &request->resp_buf_len, 4796ec61bd2fSJulian Wiedmann sizeof(request->resp_buf_len)); 4797ec61bd2fSJulian Wiedmann } else if (!is_valid_ether_addr(response->mac)) { 4798ec61bd2fSJulian Wiedmann rc = -EINVAL; 4799ec61bd2fSJulian Wiedmann QETH_DBF_TEXT(SETUP, 2, "badmac"); 4800ec61bd2fSJulian Wiedmann QETH_DBF_HEX(SETUP, 2, response->mac, ETH_ALEN); 4801ec61bd2fSJulian Wiedmann } else { 4802ec61bd2fSJulian Wiedmann ether_addr_copy(card->dev->dev_addr, response->mac); 4803ec61bd2fSJulian Wiedmann } 4804ec61bd2fSJulian Wiedmann 4805ec61bd2fSJulian Wiedmann out: 4806ec61bd2fSJulian Wiedmann kfree(response); 4807ec61bd2fSJulian Wiedmann kfree(request); 4808ec61bd2fSJulian Wiedmann return rc; 4809ec61bd2fSJulian Wiedmann } 4810ec61bd2fSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_vm_request_mac); 4811ec61bd2fSJulian Wiedmann 4812cef6ff22SJulian Wiedmann static int qeth_get_qdio_q_format(struct qeth_card *card) 48134a71df50SFrank Blaschka { 4814aa59004bSJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_IQD) 4815aa59004bSJulian Wiedmann return QDIO_IQDIO_QFMT; 4816aa59004bSJulian Wiedmann else 4817aa59004bSJulian Wiedmann return QDIO_QETH_QFMT; 48184a71df50SFrank Blaschka } 48194a71df50SFrank Blaschka 4820d0ff1f52SUrsula Braun static void qeth_determine_capabilities(struct qeth_card *card) 4821d0ff1f52SUrsula Braun { 4822d0ff1f52SUrsula Braun int rc; 4823d0ff1f52SUrsula Braun int length; 4824d0ff1f52SUrsula Braun char *prcd; 4825d0ff1f52SUrsula Braun struct ccw_device *ddev; 4826d0ff1f52SUrsula Braun int ddev_offline = 0; 4827d0ff1f52SUrsula Braun 4828d0ff1f52SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "detcapab"); 4829d0ff1f52SUrsula Braun ddev = CARD_DDEV(card); 4830d0ff1f52SUrsula Braun if (!ddev->online) { 4831d0ff1f52SUrsula Braun ddev_offline = 1; 4832d0ff1f52SUrsula Braun rc = ccw_device_set_online(ddev); 4833d0ff1f52SUrsula Braun if (rc) { 4834d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 4835d0ff1f52SUrsula Braun goto out; 4836d0ff1f52SUrsula Braun } 4837d0ff1f52SUrsula Braun } 4838d0ff1f52SUrsula Braun 4839d0ff1f52SUrsula Braun rc = qeth_read_conf_data(card, (void **) &prcd, &length); 4840d0ff1f52SUrsula Braun if (rc) { 4841d0ff1f52SUrsula Braun QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", 4842d0ff1f52SUrsula Braun dev_name(&card->gdev->dev), rc); 4843d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 4844d0ff1f52SUrsula Braun goto out_offline; 4845d0ff1f52SUrsula Braun } 4846d0ff1f52SUrsula Braun qeth_configure_unitaddr(card, prcd); 48477e665afbSUrsula Braun if (ddev_offline) 4848d0ff1f52SUrsula Braun qeth_configure_blkt_default(card, prcd); 4849d0ff1f52SUrsula Braun kfree(prcd); 4850d0ff1f52SUrsula Braun 4851d0ff1f52SUrsula Braun rc = qdio_get_ssqd_desc(ddev, &card->ssqd); 4852d0ff1f52SUrsula Braun if (rc) 4853d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 4854d0ff1f52SUrsula Braun 48550da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt); 4856bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac1:%02x", card->ssqd.qdioac1); 4857bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac2:%04x", card->ssqd.qdioac2); 4858bbeb2414SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "ac3:%04x", card->ssqd.qdioac3); 48590da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt); 48600da9581dSEinar Lueck if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) || 48610da9581dSEinar Lueck ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) || 48620da9581dSEinar Lueck ((card->ssqd.qdioac3 & CHSC_AC3_FORMAT2_CQ_AVAILABLE) == 0))) { 48630da9581dSEinar Lueck dev_info(&card->gdev->dev, 48640da9581dSEinar Lueck "Completion Queueing supported\n"); 48650da9581dSEinar Lueck } else { 48660da9581dSEinar Lueck card->options.cq = QETH_CQ_NOTAVAILABLE; 48670da9581dSEinar Lueck } 48680da9581dSEinar Lueck 48690da9581dSEinar Lueck 4870d0ff1f52SUrsula Braun out_offline: 4871d0ff1f52SUrsula Braun if (ddev_offline == 1) 4872d0ff1f52SUrsula Braun ccw_device_set_offline(ddev); 4873d0ff1f52SUrsula Braun out: 4874d0ff1f52SUrsula Braun return; 4875d0ff1f52SUrsula Braun } 4876d0ff1f52SUrsula Braun 4877cef6ff22SJulian Wiedmann static void qeth_qdio_establish_cq(struct qeth_card *card, 48780da9581dSEinar Lueck struct qdio_buffer **in_sbal_ptrs, 4879cef6ff22SJulian Wiedmann void (**queue_start_poll) 4880cef6ff22SJulian Wiedmann (struct ccw_device *, int, 4881cef6ff22SJulian Wiedmann unsigned long)) 4882cef6ff22SJulian Wiedmann { 48830da9581dSEinar Lueck int i; 48840da9581dSEinar Lueck 48850da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 48860da9581dSEinar Lueck int offset = QDIO_MAX_BUFFERS_PER_Q * 48870da9581dSEinar Lueck (card->qdio.no_in_queues - 1); 48880da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 48890da9581dSEinar Lueck in_sbal_ptrs[offset + i] = (struct qdio_buffer *) 48900da9581dSEinar Lueck virt_to_phys(card->qdio.c_q->bufs[i].buffer); 48910da9581dSEinar Lueck } 48920da9581dSEinar Lueck 48930da9581dSEinar Lueck queue_start_poll[card->qdio.no_in_queues - 1] = NULL; 48940da9581dSEinar Lueck } 48950da9581dSEinar Lueck } 48960da9581dSEinar Lueck 48974a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *card) 48984a71df50SFrank Blaschka { 48994a71df50SFrank Blaschka struct qdio_initialize init_data; 49004a71df50SFrank Blaschka char *qib_param_field; 49014a71df50SFrank Blaschka struct qdio_buffer **in_sbal_ptrs; 4902104ea556Sfrank.blaschka@de.ibm.com void (**queue_start_poll) (struct ccw_device *, int, unsigned long); 49034a71df50SFrank Blaschka struct qdio_buffer **out_sbal_ptrs; 49044a71df50SFrank Blaschka int i, j, k; 49054a71df50SFrank Blaschka int rc = 0; 49064a71df50SFrank Blaschka 4907d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "qdioest"); 49084a71df50SFrank Blaschka 49094a71df50SFrank Blaschka qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char), 49104a71df50SFrank Blaschka GFP_KERNEL); 4911104ea556Sfrank.blaschka@de.ibm.com if (!qib_param_field) { 4912104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4913104ea556Sfrank.blaschka@de.ibm.com goto out_free_nothing; 4914104ea556Sfrank.blaschka@de.ibm.com } 49154a71df50SFrank Blaschka 49164a71df50SFrank Blaschka qeth_create_qib_param_field(card, qib_param_field); 49174a71df50SFrank Blaschka qeth_create_qib_param_field_blkt(card, qib_param_field); 49184a71df50SFrank Blaschka 4919b3332930SFrank Blaschka in_sbal_ptrs = kzalloc(card->qdio.no_in_queues * 49200da9581dSEinar Lueck QDIO_MAX_BUFFERS_PER_Q * sizeof(void *), 49214a71df50SFrank Blaschka GFP_KERNEL); 49224a71df50SFrank Blaschka if (!in_sbal_ptrs) { 4923104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4924104ea556Sfrank.blaschka@de.ibm.com goto out_free_qib_param; 49254a71df50SFrank Blaschka } 49260da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 49274a71df50SFrank Blaschka in_sbal_ptrs[i] = (struct qdio_buffer *) 49284a71df50SFrank Blaschka virt_to_phys(card->qdio.in_q->bufs[i].buffer); 49290da9581dSEinar Lueck } 49304a71df50SFrank Blaschka 49310da9581dSEinar Lueck queue_start_poll = kzalloc(sizeof(void *) * card->qdio.no_in_queues, 49320da9581dSEinar Lueck GFP_KERNEL); 4933104ea556Sfrank.blaschka@de.ibm.com if (!queue_start_poll) { 4934104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4935104ea556Sfrank.blaschka@de.ibm.com goto out_free_in_sbals; 4936104ea556Sfrank.blaschka@de.ibm.com } 49370da9581dSEinar Lueck for (i = 0; i < card->qdio.no_in_queues; ++i) 4938c041f2d4SSebastian Ott queue_start_poll[i] = card->discipline->start_poll; 49390da9581dSEinar Lueck 49400da9581dSEinar Lueck qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); 4941104ea556Sfrank.blaschka@de.ibm.com 49424a71df50SFrank Blaschka out_sbal_ptrs = 4943b3332930SFrank Blaschka kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * 49444a71df50SFrank Blaschka sizeof(void *), GFP_KERNEL); 49454a71df50SFrank Blaschka if (!out_sbal_ptrs) { 4946104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4947104ea556Sfrank.blaschka@de.ibm.com goto out_free_queue_start_poll; 49484a71df50SFrank Blaschka } 49494a71df50SFrank Blaschka for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i) 49504a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k) { 49514a71df50SFrank Blaschka out_sbal_ptrs[k] = (struct qdio_buffer *)virt_to_phys( 49520da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]->buffer); 49534a71df50SFrank Blaschka } 49544a71df50SFrank Blaschka 49554a71df50SFrank Blaschka memset(&init_data, 0, sizeof(struct qdio_initialize)); 49564a71df50SFrank Blaschka init_data.cdev = CARD_DDEV(card); 49574a71df50SFrank Blaschka init_data.q_format = qeth_get_qdio_q_format(card); 49584a71df50SFrank Blaschka init_data.qib_param_field_format = 0; 49594a71df50SFrank Blaschka init_data.qib_param_field = qib_param_field; 49600da9581dSEinar Lueck init_data.no_input_qs = card->qdio.no_in_queues; 49614a71df50SFrank Blaschka init_data.no_output_qs = card->qdio.no_out_queues; 4962c041f2d4SSebastian Ott init_data.input_handler = card->discipline->input_handler; 4963c041f2d4SSebastian Ott init_data.output_handler = card->discipline->output_handler; 4964e58b0d90SSteffen Maier init_data.queue_start_poll_array = queue_start_poll; 49654a71df50SFrank Blaschka init_data.int_parm = (unsigned long) card; 49664a71df50SFrank Blaschka init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; 49674a71df50SFrank Blaschka init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; 49680da9581dSEinar Lueck init_data.output_sbal_state_array = card->qdio.out_bufstates; 49693d6c76ffSJan Glauber init_data.scan_threshold = 49700fa81cd4SStefan Raspl (card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32; 49714a71df50SFrank Blaschka 49724a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, 49734a71df50SFrank Blaschka QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { 4974cc961d40SJan Glauber rc = qdio_allocate(&init_data); 4975cc961d40SJan Glauber if (rc) { 49764a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 4977cc961d40SJan Glauber goto out; 49784a71df50SFrank Blaschka } 4979cc961d40SJan Glauber rc = qdio_establish(&init_data); 4980cc961d40SJan Glauber if (rc) { 4981cc961d40SJan Glauber atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 4982cc961d40SJan Glauber qdio_free(CARD_DDEV(card)); 4983cc961d40SJan Glauber } 4984cc961d40SJan Glauber } 49850da9581dSEinar Lueck 49860da9581dSEinar Lueck switch (card->options.cq) { 49870da9581dSEinar Lueck case QETH_CQ_ENABLED: 49880da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support enabled"); 49890da9581dSEinar Lueck break; 49900da9581dSEinar Lueck case QETH_CQ_DISABLED: 49910da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support disabled"); 49920da9581dSEinar Lueck break; 49930da9581dSEinar Lueck default: 49940da9581dSEinar Lueck break; 49950da9581dSEinar Lueck } 4996cc961d40SJan Glauber out: 49974a71df50SFrank Blaschka kfree(out_sbal_ptrs); 4998104ea556Sfrank.blaschka@de.ibm.com out_free_queue_start_poll: 4999104ea556Sfrank.blaschka@de.ibm.com kfree(queue_start_poll); 5000104ea556Sfrank.blaschka@de.ibm.com out_free_in_sbals: 50014a71df50SFrank Blaschka kfree(in_sbal_ptrs); 5002104ea556Sfrank.blaschka@de.ibm.com out_free_qib_param: 50034a71df50SFrank Blaschka kfree(qib_param_field); 5004104ea556Sfrank.blaschka@de.ibm.com out_free_nothing: 50054a71df50SFrank Blaschka return rc; 50064a71df50SFrank Blaschka } 50074a71df50SFrank Blaschka 50084a71df50SFrank Blaschka static void qeth_core_free_card(struct qeth_card *card) 50094a71df50SFrank Blaschka { 50104a71df50SFrank Blaschka 5011d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freecrd"); 5012d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 50134a71df50SFrank Blaschka qeth_clean_channel(&card->read); 50144a71df50SFrank Blaschka qeth_clean_channel(&card->write); 50154a71df50SFrank Blaschka if (card->dev) 50164a71df50SFrank Blaschka free_netdev(card->dev); 50174a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 50186bcac508SMartin Schwidefsky unregister_service_level(&card->qeth_service_level); 50194a71df50SFrank Blaschka kfree(card); 50204a71df50SFrank Blaschka } 50214a71df50SFrank Blaschka 5022395672e0SStefan Raspl void qeth_trace_features(struct qeth_card *card) 5023395672e0SStefan Raspl { 5024395672e0SStefan Raspl QETH_CARD_TEXT(card, 2, "features"); 50254d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4)); 50264d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6)); 50274d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp)); 50284d7def2aSThomas Richter QETH_CARD_HEX(card, 2, &card->info.diagass_support, 50294d7def2aSThomas Richter sizeof(card->info.diagass_support)); 5030395672e0SStefan Raspl } 5031395672e0SStefan Raspl EXPORT_SYMBOL_GPL(qeth_trace_features); 5032395672e0SStefan Raspl 50334a71df50SFrank Blaschka static struct ccw_device_id qeth_ids[] = { 50345113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01), 50355113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSD}, 50365113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x05, 0x1732, 0x05), 50375113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_IQD}, 50385113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x06, 0x1732, 0x06), 50395113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSN}, 50405113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x03), 50415113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSM}, 50425113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x02), 50435113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSX}, 50444a71df50SFrank Blaschka {}, 50454a71df50SFrank Blaschka }; 50464a71df50SFrank Blaschka MODULE_DEVICE_TABLE(ccw, qeth_ids); 50474a71df50SFrank Blaschka 50484a71df50SFrank Blaschka static struct ccw_driver qeth_ccw_driver = { 50493bda058bSSebastian Ott .driver = { 50503e70b3b8SSebastian Ott .owner = THIS_MODULE, 50514a71df50SFrank Blaschka .name = "qeth", 50523bda058bSSebastian Ott }, 50534a71df50SFrank Blaschka .ids = qeth_ids, 50544a71df50SFrank Blaschka .probe = ccwgroup_probe_ccwdev, 50554a71df50SFrank Blaschka .remove = ccwgroup_remove_ccwdev, 50564a71df50SFrank Blaschka }; 50574a71df50SFrank Blaschka 50584a71df50SFrank Blaschka int qeth_core_hardsetup_card(struct qeth_card *card) 50594a71df50SFrank Blaschka { 50606ebb7f8dSStefan Raspl int retries = 3; 50614a71df50SFrank Blaschka int rc; 50624a71df50SFrank Blaschka 5063d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); 50644a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 0); 5065725b9c04SSebastian Ott qeth_update_from_chp_desc(card); 50664a71df50SFrank Blaschka retry: 50676ebb7f8dSStefan Raspl if (retries < 3) 506874eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", 506974eacdb9SFrank Blaschka dev_name(&card->gdev->dev)); 507022ae2790SUrsula Braun rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 50714a71df50SFrank Blaschka ccw_device_set_offline(CARD_DDEV(card)); 50724a71df50SFrank Blaschka ccw_device_set_offline(CARD_WDEV(card)); 50734a71df50SFrank Blaschka ccw_device_set_offline(CARD_RDEV(card)); 507422ae2790SUrsula Braun qdio_free(CARD_DDEV(card)); 5075aa909224SUrsula Braun rc = ccw_device_set_online(CARD_RDEV(card)); 5076aa909224SUrsula Braun if (rc) 5077aa909224SUrsula Braun goto retriable; 5078aa909224SUrsula Braun rc = ccw_device_set_online(CARD_WDEV(card)); 5079aa909224SUrsula Braun if (rc) 5080aa909224SUrsula Braun goto retriable; 5081aa909224SUrsula Braun rc = ccw_device_set_online(CARD_DDEV(card)); 5082aa909224SUrsula Braun if (rc) 5083aa909224SUrsula Braun goto retriable; 5084aa909224SUrsula Braun retriable: 50854a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5086d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break1"); 50874a71df50SFrank Blaschka return rc; 50884a71df50SFrank Blaschka } else if (rc) { 5089d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 50906ebb7f8dSStefan Raspl if (--retries < 0) 50914a71df50SFrank Blaschka goto out; 50924a71df50SFrank Blaschka else 50934a71df50SFrank Blaschka goto retry; 50944a71df50SFrank Blaschka } 5095d0ff1f52SUrsula Braun qeth_determine_capabilities(card); 50964a71df50SFrank Blaschka qeth_init_tokens(card); 50974a71df50SFrank Blaschka qeth_init_func_level(card); 50984a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); 50994a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5100d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break2"); 51014a71df50SFrank Blaschka return rc; 51024a71df50SFrank Blaschka } else if (rc) { 5103d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 51044a71df50SFrank Blaschka if (--retries < 0) 51054a71df50SFrank Blaschka goto out; 51064a71df50SFrank Blaschka else 51074a71df50SFrank Blaschka goto retry; 51084a71df50SFrank Blaschka } 51094a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->write, qeth_idx_write_cb); 51104a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 5111d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break3"); 51124a71df50SFrank Blaschka return rc; 51134a71df50SFrank Blaschka } else if (rc) { 5114d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 51154a71df50SFrank Blaschka if (--retries < 0) 51164a71df50SFrank Blaschka goto out; 51174a71df50SFrank Blaschka else 51184a71df50SFrank Blaschka goto retry; 51194a71df50SFrank Blaschka } 5120908abbb5SUrsula Braun card->read_or_write_problem = 0; 51214a71df50SFrank Blaschka rc = qeth_mpc_initialize(card); 51224a71df50SFrank Blaschka if (rc) { 5123d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 51244a71df50SFrank Blaschka goto out; 51254a71df50SFrank Blaschka } 51261da74b1cSFrank Blaschka 512710340510SJulian Wiedmann rc = qeth_send_startlan(card); 512810340510SJulian Wiedmann if (rc) { 512910340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 513010340510SJulian Wiedmann if (rc == IPA_RC_LAN_OFFLINE) { 513110340510SJulian Wiedmann dev_warn(&card->gdev->dev, 513210340510SJulian Wiedmann "The LAN is offline\n"); 513310340510SJulian Wiedmann card->lan_online = 0; 513410340510SJulian Wiedmann } else { 513510340510SJulian Wiedmann rc = -ENODEV; 513610340510SJulian Wiedmann goto out; 513710340510SJulian Wiedmann } 513810340510SJulian Wiedmann } else 513910340510SJulian Wiedmann card->lan_online = 1; 514010340510SJulian Wiedmann 51411da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = 0; 51424d7def2aSThomas Richter card->options.ipa6.supported_funcs = 0; 51431da74b1cSFrank Blaschka card->options.adp.supported_funcs = 0; 5144b4d72c08SEugene Crosser card->options.sbp.supported_funcs = 0; 51451da74b1cSFrank Blaschka card->info.diagass_support = 0; 51461aec42bcSThomas Richter rc = qeth_query_ipassists(card, QETH_PROT_IPV4); 51471aec42bcSThomas Richter if (rc == -ENOMEM) 51481aec42bcSThomas Richter goto out; 51491aec42bcSThomas Richter if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { 51501aec42bcSThomas Richter rc = qeth_query_setadapterparms(card); 51511aec42bcSThomas Richter if (rc < 0) { 515210340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); 51531aec42bcSThomas Richter goto out; 51541aec42bcSThomas Richter } 51551aec42bcSThomas Richter } 51561aec42bcSThomas Richter if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { 51571aec42bcSThomas Richter rc = qeth_query_setdiagass(card); 51581aec42bcSThomas Richter if (rc < 0) { 515910340510SJulian Wiedmann QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); 51601aec42bcSThomas Richter goto out; 51611aec42bcSThomas Richter } 51621aec42bcSThomas Richter } 51634a71df50SFrank Blaschka return 0; 51644a71df50SFrank Blaschka out: 516574eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " 516674eacdb9SFrank Blaschka "an error on the device\n"); 516774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n", 516874eacdb9SFrank Blaschka dev_name(&card->gdev->dev), rc); 51694a71df50SFrank Blaschka return rc; 51704a71df50SFrank Blaschka } 51714a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); 51724a71df50SFrank Blaschka 5173cef6ff22SJulian Wiedmann static int qeth_create_skb_frag(struct qeth_qdio_buffer *qethbuffer, 5174b3332930SFrank Blaschka struct qdio_buffer_element *element, 5175b6f72f96SJulian Wiedmann struct sk_buff **pskb, int offset, int data_len) 51764a71df50SFrank Blaschka { 51774a71df50SFrank Blaschka struct page *page = virt_to_page(element->addr); 5178b6f72f96SJulian Wiedmann unsigned int next_frag; 5179b6f72f96SJulian Wiedmann 51804a71df50SFrank Blaschka if (*pskb == NULL) { 5181b3332930SFrank Blaschka if (qethbuffer->rx_skb) { 5182b3332930SFrank Blaschka /* only if qeth_card.options.cq == QETH_CQ_ENABLED */ 5183b3332930SFrank Blaschka *pskb = qethbuffer->rx_skb; 5184b3332930SFrank Blaschka qethbuffer->rx_skb = NULL; 5185b3332930SFrank Blaschka } else { 5186b3332930SFrank Blaschka *pskb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); 51874a71df50SFrank Blaschka if (!(*pskb)) 51884a71df50SFrank Blaschka return -ENOMEM; 5189b3332930SFrank Blaschka } 5190b3332930SFrank Blaschka 51914a71df50SFrank Blaschka skb_reserve(*pskb, ETH_HLEN); 5192b3332930SFrank Blaschka if (data_len <= QETH_RX_PULL_LEN) { 519359ae1d12SJohannes Berg skb_put_data(*pskb, element->addr + offset, data_len); 5194b6f72f96SJulian Wiedmann return 0; 51954a71df50SFrank Blaschka } else { 519659ae1d12SJohannes Berg skb_put_data(*pskb, element->addr + offset, 519759ae1d12SJohannes Berg QETH_RX_PULL_LEN); 5198b6f72f96SJulian Wiedmann data_len -= QETH_RX_PULL_LEN; 5199b6f72f96SJulian Wiedmann offset += QETH_RX_PULL_LEN; 5200b6f72f96SJulian Wiedmann /* fall through to add page frag for remaining data */ 52014a71df50SFrank Blaschka } 5202b6f72f96SJulian Wiedmann } 5203b6f72f96SJulian Wiedmann 5204b6f72f96SJulian Wiedmann next_frag = skb_shinfo(*pskb)->nr_frags; 52054a71df50SFrank Blaschka get_page(page); 5206b6f72f96SJulian Wiedmann skb_add_rx_frag(*pskb, next_frag, page, offset, data_len, data_len); 52074a71df50SFrank Blaschka return 0; 52084a71df50SFrank Blaschka } 52094a71df50SFrank Blaschka 5210bca51650SThomas Richter static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) 5211bca51650SThomas Richter { 5212bca51650SThomas Richter return (sbale->eflags & SBAL_EFLAGS_LAST_ENTRY); 5213bca51650SThomas Richter } 5214bca51650SThomas Richter 52154a71df50SFrank Blaschka struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, 5216b3332930SFrank Blaschka struct qeth_qdio_buffer *qethbuffer, 52174a71df50SFrank Blaschka struct qdio_buffer_element **__element, int *__offset, 52184a71df50SFrank Blaschka struct qeth_hdr **hdr) 52194a71df50SFrank Blaschka { 52204a71df50SFrank Blaschka struct qdio_buffer_element *element = *__element; 5221b3332930SFrank Blaschka struct qdio_buffer *buffer = qethbuffer->buffer; 52224a71df50SFrank Blaschka int offset = *__offset; 52234a71df50SFrank Blaschka struct sk_buff *skb = NULL; 522476b11f8eSUrsula Braun int skb_len = 0; 52254a71df50SFrank Blaschka void *data_ptr; 52264a71df50SFrank Blaschka int data_len; 52274a71df50SFrank Blaschka int headroom = 0; 52284a71df50SFrank Blaschka int use_rx_sg = 0; 52294a71df50SFrank Blaschka 52304a71df50SFrank Blaschka /* qeth_hdr must not cross element boundaries */ 52314a71df50SFrank Blaschka if (element->length < offset + sizeof(struct qeth_hdr)) { 52324a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) 52334a71df50SFrank Blaschka return NULL; 52344a71df50SFrank Blaschka element++; 52354a71df50SFrank Blaschka offset = 0; 52364a71df50SFrank Blaschka if (element->length < sizeof(struct qeth_hdr)) 52374a71df50SFrank Blaschka return NULL; 52384a71df50SFrank Blaschka } 52394a71df50SFrank Blaschka *hdr = element->addr + offset; 52404a71df50SFrank Blaschka 52414a71df50SFrank Blaschka offset += sizeof(struct qeth_hdr); 524276b11f8eSUrsula Braun switch ((*hdr)->hdr.l2.id) { 524376b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER2: 52444a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l2.pkt_length; 524576b11f8eSUrsula Braun break; 524676b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER3: 52474a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l3.length; 5248b403e685SFrank Blaschka headroom = ETH_HLEN; 524976b11f8eSUrsula Braun break; 525076b11f8eSUrsula Braun case QETH_HEADER_TYPE_OSN: 525176b11f8eSUrsula Braun skb_len = (*hdr)->hdr.osn.pdu_length; 525276b11f8eSUrsula Braun headroom = sizeof(struct qeth_hdr); 525376b11f8eSUrsula Braun break; 525476b11f8eSUrsula Braun default: 525576b11f8eSUrsula Braun break; 52564a71df50SFrank Blaschka } 52574a71df50SFrank Blaschka 52584a71df50SFrank Blaschka if (!skb_len) 52594a71df50SFrank Blaschka return NULL; 52604a71df50SFrank Blaschka 5261b3332930SFrank Blaschka if (((skb_len >= card->options.rx_sg_cb) && 52624a71df50SFrank Blaschka (!(card->info.type == QETH_CARD_TYPE_OSN)) && 5263b3332930SFrank Blaschka (!atomic_read(&card->force_alloc_skb))) || 5264b3332930SFrank Blaschka (card->options.cq == QETH_CQ_ENABLED)) { 52654a71df50SFrank Blaschka use_rx_sg = 1; 52664a71df50SFrank Blaschka } else { 52674a71df50SFrank Blaschka skb = dev_alloc_skb(skb_len + headroom); 52684a71df50SFrank Blaschka if (!skb) 52694a71df50SFrank Blaschka goto no_mem; 52704a71df50SFrank Blaschka if (headroom) 52714a71df50SFrank Blaschka skb_reserve(skb, headroom); 52724a71df50SFrank Blaschka } 52734a71df50SFrank Blaschka 52744a71df50SFrank Blaschka data_ptr = element->addr + offset; 52754a71df50SFrank Blaschka while (skb_len) { 52764a71df50SFrank Blaschka data_len = min(skb_len, (int)(element->length - offset)); 52774a71df50SFrank Blaschka if (data_len) { 52784a71df50SFrank Blaschka if (use_rx_sg) { 5279b3332930SFrank Blaschka if (qeth_create_skb_frag(qethbuffer, element, 5280b6f72f96SJulian Wiedmann &skb, offset, data_len)) 52814a71df50SFrank Blaschka goto no_mem; 52824a71df50SFrank Blaschka } else { 528359ae1d12SJohannes Berg skb_put_data(skb, data_ptr, data_len); 52844a71df50SFrank Blaschka } 52854a71df50SFrank Blaschka } 52864a71df50SFrank Blaschka skb_len -= data_len; 52874a71df50SFrank Blaschka if (skb_len) { 52884a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) { 5289847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "unexeob"); 5290efd5d9a4SCarsten Otte QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); 52914a71df50SFrank Blaschka dev_kfree_skb_any(skb); 52924a71df50SFrank Blaschka card->stats.rx_errors++; 52934a71df50SFrank Blaschka return NULL; 52944a71df50SFrank Blaschka } 52954a71df50SFrank Blaschka element++; 52964a71df50SFrank Blaschka offset = 0; 52974a71df50SFrank Blaschka data_ptr = element->addr; 52984a71df50SFrank Blaschka } else { 52994a71df50SFrank Blaschka offset += data_len; 53004a71df50SFrank Blaschka } 53014a71df50SFrank Blaschka } 53024a71df50SFrank Blaschka *__element = element; 53034a71df50SFrank Blaschka *__offset = offset; 53044a71df50SFrank Blaschka if (use_rx_sg && card->options.performance_stats) { 53054a71df50SFrank Blaschka card->perf_stats.sg_skbs_rx++; 53064a71df50SFrank Blaschka card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; 53074a71df50SFrank Blaschka } 53084a71df50SFrank Blaschka return skb; 53094a71df50SFrank Blaschka no_mem: 53104a71df50SFrank Blaschka if (net_ratelimit()) { 5311847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "noskbmem"); 53124a71df50SFrank Blaschka } 53134a71df50SFrank Blaschka card->stats.rx_dropped++; 53144a71df50SFrank Blaschka return NULL; 53154a71df50SFrank Blaschka } 53164a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); 53174a71df50SFrank Blaschka 5318d73ef324SJulian Wiedmann int qeth_poll(struct napi_struct *napi, int budget) 5319d73ef324SJulian Wiedmann { 5320d73ef324SJulian Wiedmann struct qeth_card *card = container_of(napi, struct qeth_card, napi); 5321d73ef324SJulian Wiedmann int work_done = 0; 5322d73ef324SJulian Wiedmann struct qeth_qdio_buffer *buffer; 5323d73ef324SJulian Wiedmann int done; 5324d73ef324SJulian Wiedmann int new_budget = budget; 5325d73ef324SJulian Wiedmann 5326d73ef324SJulian Wiedmann if (card->options.performance_stats) { 5327d73ef324SJulian Wiedmann card->perf_stats.inbound_cnt++; 5328d73ef324SJulian Wiedmann card->perf_stats.inbound_start_time = qeth_get_micros(); 5329d73ef324SJulian Wiedmann } 5330d73ef324SJulian Wiedmann 5331d73ef324SJulian Wiedmann while (1) { 5332d73ef324SJulian Wiedmann if (!card->rx.b_count) { 5333d73ef324SJulian Wiedmann card->rx.qdio_err = 0; 5334d73ef324SJulian Wiedmann card->rx.b_count = qdio_get_next_buffers( 5335d73ef324SJulian Wiedmann card->data.ccwdev, 0, &card->rx.b_index, 5336d73ef324SJulian Wiedmann &card->rx.qdio_err); 5337d73ef324SJulian Wiedmann if (card->rx.b_count <= 0) { 5338d73ef324SJulian Wiedmann card->rx.b_count = 0; 5339d73ef324SJulian Wiedmann break; 5340d73ef324SJulian Wiedmann } 5341d73ef324SJulian Wiedmann card->rx.b_element = 5342d73ef324SJulian Wiedmann &card->qdio.in_q->bufs[card->rx.b_index] 5343d73ef324SJulian Wiedmann .buffer->element[0]; 5344d73ef324SJulian Wiedmann card->rx.e_offset = 0; 5345d73ef324SJulian Wiedmann } 5346d73ef324SJulian Wiedmann 5347d73ef324SJulian Wiedmann while (card->rx.b_count) { 5348d73ef324SJulian Wiedmann buffer = &card->qdio.in_q->bufs[card->rx.b_index]; 5349d73ef324SJulian Wiedmann if (!(card->rx.qdio_err && 5350d73ef324SJulian Wiedmann qeth_check_qdio_errors(card, buffer->buffer, 5351d73ef324SJulian Wiedmann card->rx.qdio_err, "qinerr"))) 5352d73ef324SJulian Wiedmann work_done += 5353d73ef324SJulian Wiedmann card->discipline->process_rx_buffer( 5354d73ef324SJulian Wiedmann card, new_budget, &done); 5355d73ef324SJulian Wiedmann else 5356d73ef324SJulian Wiedmann done = 1; 5357d73ef324SJulian Wiedmann 5358d73ef324SJulian Wiedmann if (done) { 5359d73ef324SJulian Wiedmann if (card->options.performance_stats) 5360d73ef324SJulian Wiedmann card->perf_stats.bufs_rec++; 5361d73ef324SJulian Wiedmann qeth_put_buffer_pool_entry(card, 5362d73ef324SJulian Wiedmann buffer->pool_entry); 5363d73ef324SJulian Wiedmann qeth_queue_input_buffer(card, card->rx.b_index); 5364d73ef324SJulian Wiedmann card->rx.b_count--; 5365d73ef324SJulian Wiedmann if (card->rx.b_count) { 5366d73ef324SJulian Wiedmann card->rx.b_index = 5367d73ef324SJulian Wiedmann (card->rx.b_index + 1) % 5368d73ef324SJulian Wiedmann QDIO_MAX_BUFFERS_PER_Q; 5369d73ef324SJulian Wiedmann card->rx.b_element = 5370d73ef324SJulian Wiedmann &card->qdio.in_q 5371d73ef324SJulian Wiedmann ->bufs[card->rx.b_index] 5372d73ef324SJulian Wiedmann .buffer->element[0]; 5373d73ef324SJulian Wiedmann card->rx.e_offset = 0; 5374d73ef324SJulian Wiedmann } 5375d73ef324SJulian Wiedmann } 5376d73ef324SJulian Wiedmann 5377d73ef324SJulian Wiedmann if (work_done >= budget) 5378d73ef324SJulian Wiedmann goto out; 5379d73ef324SJulian Wiedmann else 5380d73ef324SJulian Wiedmann new_budget = budget - work_done; 5381d73ef324SJulian Wiedmann } 5382d73ef324SJulian Wiedmann } 5383d73ef324SJulian Wiedmann 5384d73ef324SJulian Wiedmann napi_complete(napi); 5385d73ef324SJulian Wiedmann if (qdio_start_irq(card->data.ccwdev, 0)) 5386d73ef324SJulian Wiedmann napi_schedule(&card->napi); 5387d73ef324SJulian Wiedmann out: 5388d73ef324SJulian Wiedmann if (card->options.performance_stats) 5389d73ef324SJulian Wiedmann card->perf_stats.inbound_time += qeth_get_micros() - 5390d73ef324SJulian Wiedmann card->perf_stats.inbound_start_time; 5391d73ef324SJulian Wiedmann return work_done; 5392d73ef324SJulian Wiedmann } 5393d73ef324SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_poll); 5394d73ef324SJulian Wiedmann 53958f43fb00SThomas Richter int qeth_setassparms_cb(struct qeth_card *card, 53964d7def2aSThomas Richter struct qeth_reply *reply, unsigned long data) 53974d7def2aSThomas Richter { 53984d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 53994d7def2aSThomas Richter 54004d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "defadpcb"); 54014d7def2aSThomas Richter 54024d7def2aSThomas Richter cmd = (struct qeth_ipa_cmd *) data; 54034d7def2aSThomas Richter if (cmd->hdr.return_code == 0) { 54044d7def2aSThomas Richter cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; 54054d7def2aSThomas Richter if (cmd->hdr.prot_version == QETH_PROT_IPV4) 54064d7def2aSThomas Richter card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; 54074d7def2aSThomas Richter if (cmd->hdr.prot_version == QETH_PROT_IPV6) 54084d7def2aSThomas Richter card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; 54094d7def2aSThomas Richter } 54104d7def2aSThomas Richter return 0; 54114d7def2aSThomas Richter } 54128f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_setassparms_cb); 54134d7def2aSThomas Richter 5414b475e316SThomas Richter struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, 54154d7def2aSThomas Richter enum qeth_ipa_funcs ipa_func, 54164d7def2aSThomas Richter __u16 cmd_code, __u16 len, 54174d7def2aSThomas Richter enum qeth_prot_versions prot) 54184d7def2aSThomas Richter { 54194d7def2aSThomas Richter struct qeth_cmd_buffer *iob; 54204d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 54214d7def2aSThomas Richter 54224d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "getasscm"); 54234d7def2aSThomas Richter iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); 54244d7def2aSThomas Richter 54254d7def2aSThomas Richter if (iob) { 54264d7def2aSThomas Richter cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 54274d7def2aSThomas Richter cmd->data.setassparms.hdr.assist_no = ipa_func; 54284d7def2aSThomas Richter cmd->data.setassparms.hdr.length = 8 + len; 54294d7def2aSThomas Richter cmd->data.setassparms.hdr.command_code = cmd_code; 54304d7def2aSThomas Richter cmd->data.setassparms.hdr.return_code = 0; 54314d7def2aSThomas Richter cmd->data.setassparms.hdr.seq_no = 0; 54324d7def2aSThomas Richter } 54334d7def2aSThomas Richter 54344d7def2aSThomas Richter return iob; 54354d7def2aSThomas Richter } 5436b475e316SThomas Richter EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd); 54374d7def2aSThomas Richter 54384d7def2aSThomas Richter int qeth_send_setassparms(struct qeth_card *card, 54394d7def2aSThomas Richter struct qeth_cmd_buffer *iob, __u16 len, long data, 54404d7def2aSThomas Richter int (*reply_cb)(struct qeth_card *, 54414d7def2aSThomas Richter struct qeth_reply *, unsigned long), 54424d7def2aSThomas Richter void *reply_param) 54434d7def2aSThomas Richter { 54444d7def2aSThomas Richter int rc; 54454d7def2aSThomas Richter struct qeth_ipa_cmd *cmd; 54464d7def2aSThomas Richter 54474d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "sendassp"); 54484d7def2aSThomas Richter 54494d7def2aSThomas Richter cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 54504d7def2aSThomas Richter if (len <= sizeof(__u32)) 54514d7def2aSThomas Richter cmd->data.setassparms.data.flags_32bit = (__u32) data; 54524d7def2aSThomas Richter else /* (len > sizeof(__u32)) */ 54534d7def2aSThomas Richter memcpy(&cmd->data.setassparms.data, (void *) data, len); 54544d7def2aSThomas Richter 54554d7def2aSThomas Richter rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); 54564d7def2aSThomas Richter return rc; 54574d7def2aSThomas Richter } 54584d7def2aSThomas Richter EXPORT_SYMBOL_GPL(qeth_send_setassparms); 54594d7def2aSThomas Richter 54604d7def2aSThomas Richter int qeth_send_simple_setassparms(struct qeth_card *card, 54614d7def2aSThomas Richter enum qeth_ipa_funcs ipa_func, 54624d7def2aSThomas Richter __u16 cmd_code, long data) 54634d7def2aSThomas Richter { 54644d7def2aSThomas Richter int rc; 54654d7def2aSThomas Richter int length = 0; 54664d7def2aSThomas Richter struct qeth_cmd_buffer *iob; 54674d7def2aSThomas Richter 54684d7def2aSThomas Richter QETH_CARD_TEXT(card, 4, "simassp4"); 54694d7def2aSThomas Richter if (data) 54704d7def2aSThomas Richter length = sizeof(__u32); 54714d7def2aSThomas Richter iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, 54724d7def2aSThomas Richter length, QETH_PROT_IPV4); 54734d7def2aSThomas Richter if (!iob) 54744d7def2aSThomas Richter return -ENOMEM; 54754d7def2aSThomas Richter rc = qeth_send_setassparms(card, iob, length, data, 54764d7def2aSThomas Richter qeth_setassparms_cb, NULL); 54774d7def2aSThomas Richter return rc; 54784d7def2aSThomas Richter } 54794d7def2aSThomas Richter EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms); 54804d7def2aSThomas Richter 54814a71df50SFrank Blaschka static void qeth_unregister_dbf_views(void) 54824a71df50SFrank Blaschka { 5483d11ba0c4SPeter Tiedemann int x; 5484d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5485d11ba0c4SPeter Tiedemann debug_unregister(qeth_dbf[x].id); 5486d11ba0c4SPeter Tiedemann qeth_dbf[x].id = NULL; 5487d11ba0c4SPeter Tiedemann } 54884a71df50SFrank Blaschka } 54894a71df50SFrank Blaschka 54908e96c51cSCarsten Otte void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...) 5491cd023216SPeter Tiedemann { 5492cd023216SPeter Tiedemann char dbf_txt_buf[32]; 5493345aa66eSPeter Tiedemann va_list args; 5494cd023216SPeter Tiedemann 54958e6a8285SHendrik Brueckner if (!debug_level_enabled(id, level)) 5496cd023216SPeter Tiedemann return; 5497345aa66eSPeter Tiedemann va_start(args, fmt); 5498345aa66eSPeter Tiedemann vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); 5499345aa66eSPeter Tiedemann va_end(args); 55008e96c51cSCarsten Otte debug_text_event(id, level, dbf_txt_buf); 5501cd023216SPeter Tiedemann } 5502cd023216SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf_longtext); 5503cd023216SPeter Tiedemann 55044a71df50SFrank Blaschka static int qeth_register_dbf_views(void) 55054a71df50SFrank Blaschka { 5506d11ba0c4SPeter Tiedemann int ret; 5507d11ba0c4SPeter Tiedemann int x; 55084a71df50SFrank Blaschka 5509d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5510d11ba0c4SPeter Tiedemann /* register the areas */ 5511d11ba0c4SPeter Tiedemann qeth_dbf[x].id = debug_register(qeth_dbf[x].name, 5512d11ba0c4SPeter Tiedemann qeth_dbf[x].pages, 5513d11ba0c4SPeter Tiedemann qeth_dbf[x].areas, 5514d11ba0c4SPeter Tiedemann qeth_dbf[x].len); 5515d11ba0c4SPeter Tiedemann if (qeth_dbf[x].id == NULL) { 55164a71df50SFrank Blaschka qeth_unregister_dbf_views(); 55174a71df50SFrank Blaschka return -ENOMEM; 55184a71df50SFrank Blaschka } 55194a71df50SFrank Blaschka 5520d11ba0c4SPeter Tiedemann /* register a view */ 5521d11ba0c4SPeter Tiedemann ret = debug_register_view(qeth_dbf[x].id, qeth_dbf[x].view); 5522d11ba0c4SPeter Tiedemann if (ret) { 5523d11ba0c4SPeter Tiedemann qeth_unregister_dbf_views(); 5524d11ba0c4SPeter Tiedemann return ret; 5525d11ba0c4SPeter Tiedemann } 55264a71df50SFrank Blaschka 5527d11ba0c4SPeter Tiedemann /* set a passing level */ 5528d11ba0c4SPeter Tiedemann debug_set_level(qeth_dbf[x].id, qeth_dbf[x].level); 5529d11ba0c4SPeter Tiedemann } 55304a71df50SFrank Blaschka 55314a71df50SFrank Blaschka return 0; 55324a71df50SFrank Blaschka } 55334a71df50SFrank Blaschka 55344a71df50SFrank Blaschka int qeth_core_load_discipline(struct qeth_card *card, 55354a71df50SFrank Blaschka enum qeth_discipline_id discipline) 55364a71df50SFrank Blaschka { 55374a71df50SFrank Blaschka int rc = 0; 5538c70eb09dSJulian Wiedmann 55392022e00cSFrank Blaschka mutex_lock(&qeth_mod_mutex); 55404a71df50SFrank Blaschka switch (discipline) { 55414a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER3: 5542c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5543c041f2d4SSebastian Ott symbol_get(qeth_l3_discipline), "qeth_l3"); 55444a71df50SFrank Blaschka break; 55454a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER2: 5546c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5547c041f2d4SSebastian Ott symbol_get(qeth_l2_discipline), "qeth_l2"); 55484a71df50SFrank Blaschka break; 5549c70eb09dSJulian Wiedmann default: 5550c70eb09dSJulian Wiedmann break; 55514a71df50SFrank Blaschka } 5552c70eb09dSJulian Wiedmann 5553c041f2d4SSebastian Ott if (!card->discipline) { 555474eacdb9SFrank Blaschka dev_err(&card->gdev->dev, "There is no kernel module to " 555574eacdb9SFrank Blaschka "support discipline %d\n", discipline); 55564a71df50SFrank Blaschka rc = -EINVAL; 55574a71df50SFrank Blaschka } 55582022e00cSFrank Blaschka mutex_unlock(&qeth_mod_mutex); 55594a71df50SFrank Blaschka return rc; 55604a71df50SFrank Blaschka } 55614a71df50SFrank Blaschka 55624a71df50SFrank Blaschka void qeth_core_free_discipline(struct qeth_card *card) 55634a71df50SFrank Blaschka { 55644a71df50SFrank Blaschka if (card->options.layer2) 5565c041f2d4SSebastian Ott symbol_put(qeth_l2_discipline); 55664a71df50SFrank Blaschka else 5567c041f2d4SSebastian Ott symbol_put(qeth_l3_discipline); 5568c041f2d4SSebastian Ott card->discipline = NULL; 55694a71df50SFrank Blaschka } 55704a71df50SFrank Blaschka 55712d2ebb3eSJulian Wiedmann const struct device_type qeth_generic_devtype = { 5572b7169c51SSebastian Ott .name = "qeth_generic", 5573b7169c51SSebastian Ott .groups = qeth_generic_attr_groups, 5574b7169c51SSebastian Ott }; 55752d2ebb3eSJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_generic_devtype); 55762d2ebb3eSJulian Wiedmann 5577b7169c51SSebastian Ott static const struct device_type qeth_osn_devtype = { 5578b7169c51SSebastian Ott .name = "qeth_osn", 5579b7169c51SSebastian Ott .groups = qeth_osn_attr_groups, 5580b7169c51SSebastian Ott }; 5581b7169c51SSebastian Ott 5582819dc537SStefan Raspl #define DBF_NAME_LEN 20 5583819dc537SStefan Raspl 5584819dc537SStefan Raspl struct qeth_dbf_entry { 5585819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 5586819dc537SStefan Raspl debug_info_t *dbf_info; 5587819dc537SStefan Raspl struct list_head dbf_list; 5588819dc537SStefan Raspl }; 5589819dc537SStefan Raspl 5590819dc537SStefan Raspl static LIST_HEAD(qeth_dbf_list); 5591819dc537SStefan Raspl static DEFINE_MUTEX(qeth_dbf_list_mutex); 5592819dc537SStefan Raspl 5593819dc537SStefan Raspl static debug_info_t *qeth_get_dbf_entry(char *name) 5594819dc537SStefan Raspl { 5595819dc537SStefan Raspl struct qeth_dbf_entry *entry; 5596819dc537SStefan Raspl debug_info_t *rc = NULL; 5597819dc537SStefan Raspl 5598819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5599819dc537SStefan Raspl list_for_each_entry(entry, &qeth_dbf_list, dbf_list) { 5600819dc537SStefan Raspl if (strcmp(entry->dbf_name, name) == 0) { 5601819dc537SStefan Raspl rc = entry->dbf_info; 5602819dc537SStefan Raspl break; 5603819dc537SStefan Raspl } 5604819dc537SStefan Raspl } 5605819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5606819dc537SStefan Raspl return rc; 5607819dc537SStefan Raspl } 5608819dc537SStefan Raspl 5609819dc537SStefan Raspl static int qeth_add_dbf_entry(struct qeth_card *card, char *name) 5610819dc537SStefan Raspl { 5611819dc537SStefan Raspl struct qeth_dbf_entry *new_entry; 5612819dc537SStefan Raspl 5613819dc537SStefan Raspl card->debug = debug_register(name, 2, 1, 8); 5614819dc537SStefan Raspl if (!card->debug) { 5615819dc537SStefan Raspl QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); 5616819dc537SStefan Raspl goto err; 5617819dc537SStefan Raspl } 5618819dc537SStefan Raspl if (debug_register_view(card->debug, &debug_hex_ascii_view)) 5619819dc537SStefan Raspl goto err_dbg; 5620819dc537SStefan Raspl new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL); 5621819dc537SStefan Raspl if (!new_entry) 5622819dc537SStefan Raspl goto err_dbg; 5623819dc537SStefan Raspl strncpy(new_entry->dbf_name, name, DBF_NAME_LEN); 5624819dc537SStefan Raspl new_entry->dbf_info = card->debug; 5625819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5626819dc537SStefan Raspl list_add(&new_entry->dbf_list, &qeth_dbf_list); 5627819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5628819dc537SStefan Raspl 5629819dc537SStefan Raspl return 0; 5630819dc537SStefan Raspl 5631819dc537SStefan Raspl err_dbg: 5632819dc537SStefan Raspl debug_unregister(card->debug); 5633819dc537SStefan Raspl err: 5634819dc537SStefan Raspl return -ENOMEM; 5635819dc537SStefan Raspl } 5636819dc537SStefan Raspl 5637819dc537SStefan Raspl static void qeth_clear_dbf_list(void) 5638819dc537SStefan Raspl { 5639819dc537SStefan Raspl struct qeth_dbf_entry *entry, *tmp; 5640819dc537SStefan Raspl 5641819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5642819dc537SStefan Raspl list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) { 5643819dc537SStefan Raspl list_del(&entry->dbf_list); 5644819dc537SStefan Raspl debug_unregister(entry->dbf_info); 5645819dc537SStefan Raspl kfree(entry); 5646819dc537SStefan Raspl } 5647819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5648819dc537SStefan Raspl } 5649819dc537SStefan Raspl 56504a71df50SFrank Blaschka static int qeth_core_probe_device(struct ccwgroup_device *gdev) 56514a71df50SFrank Blaschka { 56524a71df50SFrank Blaschka struct qeth_card *card; 56534a71df50SFrank Blaschka struct device *dev; 56544a71df50SFrank Blaschka int rc; 5655c70eb09dSJulian Wiedmann enum qeth_discipline_id enforced_disc; 56564a71df50SFrank Blaschka unsigned long flags; 5657819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 56584a71df50SFrank Blaschka 5659d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "probedev"); 56604a71df50SFrank Blaschka 56614a71df50SFrank Blaschka dev = &gdev->dev; 56624a71df50SFrank Blaschka if (!get_device(dev)) 56634a71df50SFrank Blaschka return -ENODEV; 56644a71df50SFrank Blaschka 56652a0217d5SKay Sievers QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev)); 56664a71df50SFrank Blaschka 56674a71df50SFrank Blaschka card = qeth_alloc_card(); 56684a71df50SFrank Blaschka if (!card) { 5669d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", -ENOMEM); 56704a71df50SFrank Blaschka rc = -ENOMEM; 56714a71df50SFrank Blaschka goto err_dev; 56724a71df50SFrank Blaschka } 5673af039068SCarsten Otte 5674af039068SCarsten Otte snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s", 5675af039068SCarsten Otte dev_name(&gdev->dev)); 5676819dc537SStefan Raspl card->debug = qeth_get_dbf_entry(dbf_name); 5677af039068SCarsten Otte if (!card->debug) { 5678819dc537SStefan Raspl rc = qeth_add_dbf_entry(card, dbf_name); 5679819dc537SStefan Raspl if (rc) 5680af039068SCarsten Otte goto err_card; 5681af039068SCarsten Otte } 5682af039068SCarsten Otte 56834a71df50SFrank Blaschka card->read.ccwdev = gdev->cdev[0]; 56844a71df50SFrank Blaschka card->write.ccwdev = gdev->cdev[1]; 56854a71df50SFrank Blaschka card->data.ccwdev = gdev->cdev[2]; 56864a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, card); 56874a71df50SFrank Blaschka card->gdev = gdev; 56884a71df50SFrank Blaschka gdev->cdev[0]->handler = qeth_irq; 56894a71df50SFrank Blaschka gdev->cdev[1]->handler = qeth_irq; 56904a71df50SFrank Blaschka gdev->cdev[2]->handler = qeth_irq; 56914a71df50SFrank Blaschka 5692ed2e93efSJulian Wiedmann qeth_determine_card_type(card); 56934a71df50SFrank Blaschka rc = qeth_setup_card(card); 56944a71df50SFrank Blaschka if (rc) { 5695d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 5696819dc537SStefan Raspl goto err_card; 56974a71df50SFrank Blaschka } 56984a71df50SFrank Blaschka 5699c70eb09dSJulian Wiedmann qeth_determine_capabilities(card); 5700c70eb09dSJulian Wiedmann enforced_disc = qeth_enforce_discipline(card); 5701c70eb09dSJulian Wiedmann switch (enforced_disc) { 5702c70eb09dSJulian Wiedmann case QETH_DISCIPLINE_UNDETERMINED: 5703c70eb09dSJulian Wiedmann gdev->dev.type = &qeth_generic_devtype; 5704c70eb09dSJulian Wiedmann break; 5705c70eb09dSJulian Wiedmann default: 5706c70eb09dSJulian Wiedmann card->info.layer_enforced = true; 5707c70eb09dSJulian Wiedmann rc = qeth_core_load_discipline(card, enforced_disc); 57085113fec0SUrsula Braun if (rc) 5709819dc537SStefan Raspl goto err_card; 57102d2ebb3eSJulian Wiedmann 57112d2ebb3eSJulian Wiedmann gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) 57122d2ebb3eSJulian Wiedmann ? card->discipline->devtype 57132d2ebb3eSJulian Wiedmann : &qeth_osn_devtype; 5714c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 57155113fec0SUrsula Braun if (rc) 57165113fec0SUrsula Braun goto err_disc; 57172d2ebb3eSJulian Wiedmann break; 57184a71df50SFrank Blaschka } 57194a71df50SFrank Blaschka 57204a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 57214a71df50SFrank Blaschka list_add_tail(&card->list, &qeth_core_card_list.list); 57224a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 57234a71df50SFrank Blaschka return 0; 57244a71df50SFrank Blaschka 57255113fec0SUrsula Braun err_disc: 57265113fec0SUrsula Braun qeth_core_free_discipline(card); 57274a71df50SFrank Blaschka err_card: 57284a71df50SFrank Blaschka qeth_core_free_card(card); 57294a71df50SFrank Blaschka err_dev: 57304a71df50SFrank Blaschka put_device(dev); 57314a71df50SFrank Blaschka return rc; 57324a71df50SFrank Blaschka } 57334a71df50SFrank Blaschka 57344a71df50SFrank Blaschka static void qeth_core_remove_device(struct ccwgroup_device *gdev) 57354a71df50SFrank Blaschka { 57364a71df50SFrank Blaschka unsigned long flags; 57374a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 57384a71df50SFrank Blaschka 573928a7e4c9SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "removedv"); 57404a71df50SFrank Blaschka 5741c041f2d4SSebastian Ott if (card->discipline) { 5742c041f2d4SSebastian Ott card->discipline->remove(gdev); 57439dc48cccSUrsula Braun qeth_core_free_discipline(card); 57449dc48cccSUrsula Braun } 57459dc48cccSUrsula Braun 57464a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 57474a71df50SFrank Blaschka list_del(&card->list); 57484a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 57494a71df50SFrank Blaschka qeth_core_free_card(card); 57504a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, NULL); 57514a71df50SFrank Blaschka put_device(&gdev->dev); 57524a71df50SFrank Blaschka return; 57534a71df50SFrank Blaschka } 57544a71df50SFrank Blaschka 57554a71df50SFrank Blaschka static int qeth_core_set_online(struct ccwgroup_device *gdev) 57564a71df50SFrank Blaschka { 57574a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 57584a71df50SFrank Blaschka int rc = 0; 5759c70eb09dSJulian Wiedmann enum qeth_discipline_id def_discipline; 57604a71df50SFrank Blaschka 5761c041f2d4SSebastian Ott if (!card->discipline) { 57624a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 57634a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER3; 57644a71df50SFrank Blaschka else 57654a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER2; 57664a71df50SFrank Blaschka rc = qeth_core_load_discipline(card, def_discipline); 57674a71df50SFrank Blaschka if (rc) 57684a71df50SFrank Blaschka goto err; 5769c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 57709111e788SUrsula Braun if (rc) { 57719111e788SUrsula Braun qeth_core_free_discipline(card); 57724a71df50SFrank Blaschka goto err; 57734a71df50SFrank Blaschka } 57749111e788SUrsula Braun } 5775c041f2d4SSebastian Ott rc = card->discipline->set_online(gdev); 57764a71df50SFrank Blaschka err: 57774a71df50SFrank Blaschka return rc; 57784a71df50SFrank Blaschka } 57794a71df50SFrank Blaschka 57804a71df50SFrank Blaschka static int qeth_core_set_offline(struct ccwgroup_device *gdev) 57814a71df50SFrank Blaschka { 57824a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5783c041f2d4SSebastian Ott return card->discipline->set_offline(gdev); 57844a71df50SFrank Blaschka } 57854a71df50SFrank Blaschka 57864a71df50SFrank Blaschka static void qeth_core_shutdown(struct ccwgroup_device *gdev) 57874a71df50SFrank Blaschka { 57884a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 578996d1bb53SJulian Wiedmann qeth_set_allowed_threads(card, 0, 1); 579096d1bb53SJulian Wiedmann if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) 579196d1bb53SJulian Wiedmann qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); 579296d1bb53SJulian Wiedmann qeth_qdio_clear_card(card, 0); 579396d1bb53SJulian Wiedmann qeth_clear_qdio_buffers(card); 579496d1bb53SJulian Wiedmann qdio_free(CARD_DDEV(card)); 57954a71df50SFrank Blaschka } 57964a71df50SFrank Blaschka 5797bbcfcdc8SFrank Blaschka static int qeth_core_freeze(struct ccwgroup_device *gdev) 5798bbcfcdc8SFrank Blaschka { 5799bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5800c041f2d4SSebastian Ott if (card->discipline && card->discipline->freeze) 5801c041f2d4SSebastian Ott return card->discipline->freeze(gdev); 5802bbcfcdc8SFrank Blaschka return 0; 5803bbcfcdc8SFrank Blaschka } 5804bbcfcdc8SFrank Blaschka 5805bbcfcdc8SFrank Blaschka static int qeth_core_thaw(struct ccwgroup_device *gdev) 5806bbcfcdc8SFrank Blaschka { 5807bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5808c041f2d4SSebastian Ott if (card->discipline && card->discipline->thaw) 5809c041f2d4SSebastian Ott return card->discipline->thaw(gdev); 5810bbcfcdc8SFrank Blaschka return 0; 5811bbcfcdc8SFrank Blaschka } 5812bbcfcdc8SFrank Blaschka 5813bbcfcdc8SFrank Blaschka static int qeth_core_restore(struct ccwgroup_device *gdev) 5814bbcfcdc8SFrank Blaschka { 5815bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5816c041f2d4SSebastian Ott if (card->discipline && card->discipline->restore) 5817c041f2d4SSebastian Ott return card->discipline->restore(gdev); 5818bbcfcdc8SFrank Blaschka return 0; 5819bbcfcdc8SFrank Blaschka } 5820bbcfcdc8SFrank Blaschka 58214a71df50SFrank Blaschka static struct ccwgroup_driver qeth_core_ccwgroup_driver = { 58223c190c51SSebastian Ott .driver = { 58234a71df50SFrank Blaschka .owner = THIS_MODULE, 58244a71df50SFrank Blaschka .name = "qeth", 58253c190c51SSebastian Ott }, 5826b7169c51SSebastian Ott .setup = qeth_core_probe_device, 58274a71df50SFrank Blaschka .remove = qeth_core_remove_device, 58284a71df50SFrank Blaschka .set_online = qeth_core_set_online, 58294a71df50SFrank Blaschka .set_offline = qeth_core_set_offline, 58304a71df50SFrank Blaschka .shutdown = qeth_core_shutdown, 58316ffa4d1bSJulian Wiedmann .prepare = NULL, 58326ffa4d1bSJulian Wiedmann .complete = NULL, 5833bbcfcdc8SFrank Blaschka .freeze = qeth_core_freeze, 5834bbcfcdc8SFrank Blaschka .thaw = qeth_core_thaw, 5835bbcfcdc8SFrank Blaschka .restore = qeth_core_restore, 58364a71df50SFrank Blaschka }; 58374a71df50SFrank Blaschka 583836369569SGreg Kroah-Hartman static ssize_t group_store(struct device_driver *ddrv, const char *buf, 583936369569SGreg Kroah-Hartman size_t count) 58404a71df50SFrank Blaschka { 58414a71df50SFrank Blaschka int err; 58424a71df50SFrank Blaschka 5843b7169c51SSebastian Ott err = ccwgroup_create_dev(qeth_core_root_dev, 5844b7169c51SSebastian Ott &qeth_core_ccwgroup_driver, 3, buf); 5845b7169c51SSebastian Ott 5846b7169c51SSebastian Ott return err ? err : count; 5847b7169c51SSebastian Ott } 584836369569SGreg Kroah-Hartman static DRIVER_ATTR_WO(group); 58494a71df50SFrank Blaschka 5850f47e2256SSebastian Ott static struct attribute *qeth_drv_attrs[] = { 5851f47e2256SSebastian Ott &driver_attr_group.attr, 5852f47e2256SSebastian Ott NULL, 5853f47e2256SSebastian Ott }; 5854f47e2256SSebastian Ott static struct attribute_group qeth_drv_attr_group = { 5855f47e2256SSebastian Ott .attrs = qeth_drv_attrs, 5856f47e2256SSebastian Ott }; 5857f47e2256SSebastian Ott static const struct attribute_group *qeth_drv_attr_groups[] = { 5858f47e2256SSebastian Ott &qeth_drv_attr_group, 5859f47e2256SSebastian Ott NULL, 5860f47e2256SSebastian Ott }; 5861f47e2256SSebastian Ott 5862942d6984SJulian Wiedmann int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 5863942d6984SJulian Wiedmann { 5864942d6984SJulian Wiedmann struct qeth_card *card = dev->ml_priv; 5865942d6984SJulian Wiedmann struct mii_ioctl_data *mii_data; 5866942d6984SJulian Wiedmann int rc = 0; 5867942d6984SJulian Wiedmann 5868942d6984SJulian Wiedmann if (!card) 5869942d6984SJulian Wiedmann return -ENODEV; 5870942d6984SJulian Wiedmann 5871942d6984SJulian Wiedmann if (!qeth_card_hw_is_reachable(card)) 5872942d6984SJulian Wiedmann return -ENODEV; 5873942d6984SJulian Wiedmann 5874942d6984SJulian Wiedmann if (card->info.type == QETH_CARD_TYPE_OSN) 5875942d6984SJulian Wiedmann return -EPERM; 5876942d6984SJulian Wiedmann 5877942d6984SJulian Wiedmann switch (cmd) { 5878942d6984SJulian Wiedmann case SIOC_QETH_ADP_SET_SNMP_CONTROL: 5879942d6984SJulian Wiedmann rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); 5880942d6984SJulian Wiedmann break; 5881942d6984SJulian Wiedmann case SIOC_QETH_GET_CARD_TYPE: 5882942d6984SJulian Wiedmann if ((card->info.type == QETH_CARD_TYPE_OSD || 5883942d6984SJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSM || 5884942d6984SJulian Wiedmann card->info.type == QETH_CARD_TYPE_OSX) && 5885942d6984SJulian Wiedmann !card->info.guestlan) 5886942d6984SJulian Wiedmann return 1; 5887942d6984SJulian Wiedmann else 5888942d6984SJulian Wiedmann return 0; 5889942d6984SJulian Wiedmann case SIOCGMIIPHY: 5890942d6984SJulian Wiedmann mii_data = if_mii(rq); 5891942d6984SJulian Wiedmann mii_data->phy_id = 0; 5892942d6984SJulian Wiedmann break; 5893942d6984SJulian Wiedmann case SIOCGMIIREG: 5894942d6984SJulian Wiedmann mii_data = if_mii(rq); 5895942d6984SJulian Wiedmann if (mii_data->phy_id != 0) 5896942d6984SJulian Wiedmann rc = -EINVAL; 5897942d6984SJulian Wiedmann else 5898942d6984SJulian Wiedmann mii_data->val_out = qeth_mdio_read(dev, 5899942d6984SJulian Wiedmann mii_data->phy_id, mii_data->reg_num); 5900942d6984SJulian Wiedmann break; 5901942d6984SJulian Wiedmann case SIOC_QETH_QUERY_OAT: 5902942d6984SJulian Wiedmann rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); 5903942d6984SJulian Wiedmann break; 5904942d6984SJulian Wiedmann default: 5905942d6984SJulian Wiedmann if (card->discipline->do_ioctl) 5906942d6984SJulian Wiedmann rc = card->discipline->do_ioctl(dev, rq, cmd); 5907942d6984SJulian Wiedmann else 5908942d6984SJulian Wiedmann rc = -EOPNOTSUPP; 5909942d6984SJulian Wiedmann } 5910942d6984SJulian Wiedmann if (rc) 5911942d6984SJulian Wiedmann QETH_CARD_TEXT_(card, 2, "ioce%x", rc); 5912942d6984SJulian Wiedmann return rc; 5913942d6984SJulian Wiedmann } 5914942d6984SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_do_ioctl); 5915942d6984SJulian Wiedmann 59164a71df50SFrank Blaschka static struct { 59174a71df50SFrank Blaschka const char str[ETH_GSTRING_LEN]; 59184a71df50SFrank Blaschka } qeth_ethtool_stats_keys[] = { 59194a71df50SFrank Blaschka /* 0 */{"rx skbs"}, 59204a71df50SFrank Blaschka {"rx buffers"}, 59214a71df50SFrank Blaschka {"tx skbs"}, 59224a71df50SFrank Blaschka {"tx buffers"}, 59234a71df50SFrank Blaschka {"tx skbs no packing"}, 59244a71df50SFrank Blaschka {"tx buffers no packing"}, 59254a71df50SFrank Blaschka {"tx skbs packing"}, 59264a71df50SFrank Blaschka {"tx buffers packing"}, 59274a71df50SFrank Blaschka {"tx sg skbs"}, 59284a71df50SFrank Blaschka {"tx sg frags"}, 59294a71df50SFrank Blaschka /* 10 */{"rx sg skbs"}, 59304a71df50SFrank Blaschka {"rx sg frags"}, 59314a71df50SFrank Blaschka {"rx sg page allocs"}, 59324a71df50SFrank Blaschka {"tx large kbytes"}, 59334a71df50SFrank Blaschka {"tx large count"}, 59344a71df50SFrank Blaschka {"tx pk state ch n->p"}, 59354a71df50SFrank Blaschka {"tx pk state ch p->n"}, 59364a71df50SFrank Blaschka {"tx pk watermark low"}, 59374a71df50SFrank Blaschka {"tx pk watermark high"}, 59384a71df50SFrank Blaschka {"queue 0 buffer usage"}, 59394a71df50SFrank Blaschka /* 20 */{"queue 1 buffer usage"}, 59404a71df50SFrank Blaschka {"queue 2 buffer usage"}, 59414a71df50SFrank Blaschka {"queue 3 buffer usage"}, 5942a1c3ed4cSFrank Blaschka {"rx poll time"}, 5943a1c3ed4cSFrank Blaschka {"rx poll count"}, 59444a71df50SFrank Blaschka {"rx do_QDIO time"}, 59454a71df50SFrank Blaschka {"rx do_QDIO count"}, 59464a71df50SFrank Blaschka {"tx handler time"}, 59474a71df50SFrank Blaschka {"tx handler count"}, 59484a71df50SFrank Blaschka {"tx time"}, 59494a71df50SFrank Blaschka /* 30 */{"tx count"}, 59504a71df50SFrank Blaschka {"tx do_QDIO time"}, 59514a71df50SFrank Blaschka {"tx do_QDIO count"}, 5952f61a0d05SFrank Blaschka {"tx csum"}, 5953c3b4a740SFrank Blaschka {"tx lin"}, 59546059c905SEugene Crosser {"tx linfail"}, 59550da9581dSEinar Lueck {"cq handler count"}, 59560da9581dSEinar Lueck {"cq handler time"} 59574a71df50SFrank Blaschka }; 59584a71df50SFrank Blaschka 5959df8b4ec8SBen Hutchings int qeth_core_get_sset_count(struct net_device *dev, int stringset) 59604a71df50SFrank Blaschka { 5961df8b4ec8SBen Hutchings switch (stringset) { 5962df8b4ec8SBen Hutchings case ETH_SS_STATS: 59634a71df50SFrank Blaschka return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); 5964df8b4ec8SBen Hutchings default: 5965df8b4ec8SBen Hutchings return -EINVAL; 59664a71df50SFrank Blaschka } 5967df8b4ec8SBen Hutchings } 5968df8b4ec8SBen Hutchings EXPORT_SYMBOL_GPL(qeth_core_get_sset_count); 59694a71df50SFrank Blaschka 59704a71df50SFrank Blaschka void qeth_core_get_ethtool_stats(struct net_device *dev, 59714a71df50SFrank Blaschka struct ethtool_stats *stats, u64 *data) 59724a71df50SFrank Blaschka { 5973509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 59744a71df50SFrank Blaschka data[0] = card->stats.rx_packets - 59754a71df50SFrank Blaschka card->perf_stats.initial_rx_packets; 59764a71df50SFrank Blaschka data[1] = card->perf_stats.bufs_rec; 59774a71df50SFrank Blaschka data[2] = card->stats.tx_packets - 59784a71df50SFrank Blaschka card->perf_stats.initial_tx_packets; 59794a71df50SFrank Blaschka data[3] = card->perf_stats.bufs_sent; 59804a71df50SFrank Blaschka data[4] = card->stats.tx_packets - card->perf_stats.initial_tx_packets 59814a71df50SFrank Blaschka - card->perf_stats.skbs_sent_pack; 59824a71df50SFrank Blaschka data[5] = card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack; 59834a71df50SFrank Blaschka data[6] = card->perf_stats.skbs_sent_pack; 59844a71df50SFrank Blaschka data[7] = card->perf_stats.bufs_sent_pack; 59854a71df50SFrank Blaschka data[8] = card->perf_stats.sg_skbs_sent; 59864a71df50SFrank Blaschka data[9] = card->perf_stats.sg_frags_sent; 59874a71df50SFrank Blaschka data[10] = card->perf_stats.sg_skbs_rx; 59884a71df50SFrank Blaschka data[11] = card->perf_stats.sg_frags_rx; 59894a71df50SFrank Blaschka data[12] = card->perf_stats.sg_alloc_page_rx; 59904a71df50SFrank Blaschka data[13] = (card->perf_stats.large_send_bytes >> 10); 59914a71df50SFrank Blaschka data[14] = card->perf_stats.large_send_cnt; 59924a71df50SFrank Blaschka data[15] = card->perf_stats.sc_dp_p; 59934a71df50SFrank Blaschka data[16] = card->perf_stats.sc_p_dp; 59944a71df50SFrank Blaschka data[17] = QETH_LOW_WATERMARK_PACK; 59954a71df50SFrank Blaschka data[18] = QETH_HIGH_WATERMARK_PACK; 59964a71df50SFrank Blaschka data[19] = atomic_read(&card->qdio.out_qs[0]->used_buffers); 59974a71df50SFrank Blaschka data[20] = (card->qdio.no_out_queues > 1) ? 59984a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[1]->used_buffers) : 0; 59994a71df50SFrank Blaschka data[21] = (card->qdio.no_out_queues > 2) ? 60004a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[2]->used_buffers) : 0; 60014a71df50SFrank Blaschka data[22] = (card->qdio.no_out_queues > 3) ? 60024a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[3]->used_buffers) : 0; 60034a71df50SFrank Blaschka data[23] = card->perf_stats.inbound_time; 60044a71df50SFrank Blaschka data[24] = card->perf_stats.inbound_cnt; 60054a71df50SFrank Blaschka data[25] = card->perf_stats.inbound_do_qdio_time; 60064a71df50SFrank Blaschka data[26] = card->perf_stats.inbound_do_qdio_cnt; 60074a71df50SFrank Blaschka data[27] = card->perf_stats.outbound_handler_time; 60084a71df50SFrank Blaschka data[28] = card->perf_stats.outbound_handler_cnt; 60094a71df50SFrank Blaschka data[29] = card->perf_stats.outbound_time; 60104a71df50SFrank Blaschka data[30] = card->perf_stats.outbound_cnt; 60114a71df50SFrank Blaschka data[31] = card->perf_stats.outbound_do_qdio_time; 60124a71df50SFrank Blaschka data[32] = card->perf_stats.outbound_do_qdio_cnt; 6013f61a0d05SFrank Blaschka data[33] = card->perf_stats.tx_csum; 6014c3b4a740SFrank Blaschka data[34] = card->perf_stats.tx_lin; 60156059c905SEugene Crosser data[35] = card->perf_stats.tx_linfail; 60166059c905SEugene Crosser data[36] = card->perf_stats.cq_cnt; 60176059c905SEugene Crosser data[37] = card->perf_stats.cq_time; 60184a71df50SFrank Blaschka } 60194a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); 60204a71df50SFrank Blaschka 60214a71df50SFrank Blaschka void qeth_core_get_strings(struct net_device *dev, u32 stringset, u8 *data) 60224a71df50SFrank Blaschka { 60234a71df50SFrank Blaschka switch (stringset) { 60244a71df50SFrank Blaschka case ETH_SS_STATS: 60254a71df50SFrank Blaschka memcpy(data, &qeth_ethtool_stats_keys, 60264a71df50SFrank Blaschka sizeof(qeth_ethtool_stats_keys)); 60274a71df50SFrank Blaschka break; 60284a71df50SFrank Blaschka default: 60294a71df50SFrank Blaschka WARN_ON(1); 60304a71df50SFrank Blaschka break; 60314a71df50SFrank Blaschka } 60324a71df50SFrank Blaschka } 60334a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_strings); 60344a71df50SFrank Blaschka 60354a71df50SFrank Blaschka void qeth_core_get_drvinfo(struct net_device *dev, 60364a71df50SFrank Blaschka struct ethtool_drvinfo *info) 60374a71df50SFrank Blaschka { 6038509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 60394a71df50SFrank Blaschka 60407826d43fSJiri Pirko strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", 60417826d43fSJiri Pirko sizeof(info->driver)); 60427826d43fSJiri Pirko strlcpy(info->version, "1.0", sizeof(info->version)); 60437826d43fSJiri Pirko strlcpy(info->fw_version, card->info.mcl_level, 60447826d43fSJiri Pirko sizeof(info->fw_version)); 60457826d43fSJiri Pirko snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", 60467826d43fSJiri Pirko CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); 60474a71df50SFrank Blaschka } 60484a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); 60494a71df50SFrank Blaschka 6050774afb8eSJulian Wiedmann /* Helper function to fill 'advertising' and 'supported' which are the same. */ 6051774afb8eSJulian Wiedmann /* Autoneg and full-duplex are supported and advertised unconditionally. */ 6052774afb8eSJulian Wiedmann /* Always advertise and support all speeds up to specified, and only one */ 605302d5cb5bSEugene Crosser /* specified port type. */ 6054993e19c0SJulian Wiedmann static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, 605502d5cb5bSEugene Crosser int maxspeed, int porttype) 605602d5cb5bSEugene Crosser { 605741fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, supported); 605841fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, advertising); 605941fc3b65SJulian Wiedmann ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); 6060774afb8eSJulian Wiedmann 606141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); 606241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); 606302d5cb5bSEugene Crosser 606402d5cb5bSEugene Crosser switch (porttype) { 606502d5cb5bSEugene Crosser case PORT_TP: 606641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 606741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 606802d5cb5bSEugene Crosser break; 606902d5cb5bSEugene Crosser case PORT_FIBRE: 607041fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); 607141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); 607202d5cb5bSEugene Crosser break; 607302d5cb5bSEugene Crosser default: 607441fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, TP); 607541fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); 607602d5cb5bSEugene Crosser WARN_ON_ONCE(1); 607702d5cb5bSEugene Crosser } 607802d5cb5bSEugene Crosser 6079774afb8eSJulian Wiedmann /* fallthrough from high to low, to select all legal speeds: */ 608002d5cb5bSEugene Crosser switch (maxspeed) { 608102d5cb5bSEugene Crosser case SPEED_10000: 608241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 608341fc3b65SJulian Wiedmann 10000baseT_Full); 608441fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 608541fc3b65SJulian Wiedmann 10000baseT_Full); 608602d5cb5bSEugene Crosser case SPEED_1000: 608741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 608841fc3b65SJulian Wiedmann 1000baseT_Full); 608941fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 609041fc3b65SJulian Wiedmann 1000baseT_Full); 609141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 609241fc3b65SJulian Wiedmann 1000baseT_Half); 609341fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 609441fc3b65SJulian Wiedmann 1000baseT_Half); 609502d5cb5bSEugene Crosser case SPEED_100: 609641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 609741fc3b65SJulian Wiedmann 100baseT_Full); 609841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 609941fc3b65SJulian Wiedmann 100baseT_Full); 610041fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 610141fc3b65SJulian Wiedmann 100baseT_Half); 610241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 610341fc3b65SJulian Wiedmann 100baseT_Half); 610402d5cb5bSEugene Crosser case SPEED_10: 610541fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 610641fc3b65SJulian Wiedmann 10baseT_Full); 610741fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 610841fc3b65SJulian Wiedmann 10baseT_Full); 610941fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 611041fc3b65SJulian Wiedmann 10baseT_Half); 611141fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 611241fc3b65SJulian Wiedmann 10baseT_Half); 6113774afb8eSJulian Wiedmann /* end fallthrough */ 611402d5cb5bSEugene Crosser break; 611502d5cb5bSEugene Crosser default: 611641fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 611741fc3b65SJulian Wiedmann 10baseT_Full); 611841fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 611941fc3b65SJulian Wiedmann 10baseT_Full); 612041fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, supported, 612141fc3b65SJulian Wiedmann 10baseT_Half); 612241fc3b65SJulian Wiedmann ethtool_link_ksettings_add_link_mode(cmd, advertising, 612341fc3b65SJulian Wiedmann 10baseT_Half); 612402d5cb5bSEugene Crosser WARN_ON_ONCE(1); 612502d5cb5bSEugene Crosser } 612602d5cb5bSEugene Crosser } 612702d5cb5bSEugene Crosser 6128993e19c0SJulian Wiedmann int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev, 6129993e19c0SJulian Wiedmann struct ethtool_link_ksettings *cmd) 61303f9975aaSFrank Blaschka { 6131509e2562SHeiko Carstens struct qeth_card *card = netdev->ml_priv; 61323f9975aaSFrank Blaschka enum qeth_link_types link_type; 613302d5cb5bSEugene Crosser struct carrier_info carrier_info; 6134511c2445SEugene Crosser int rc; 61353f9975aaSFrank Blaschka 61363f9975aaSFrank Blaschka if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) 61373f9975aaSFrank Blaschka link_type = QETH_LINK_TYPE_10GBIT_ETH; 61383f9975aaSFrank Blaschka else 61393f9975aaSFrank Blaschka link_type = card->info.link_type; 61403f9975aaSFrank Blaschka 6141993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_FULL; 6142993e19c0SJulian Wiedmann cmd->base.autoneg = AUTONEG_ENABLE; 6143993e19c0SJulian Wiedmann cmd->base.phy_address = 0; 6144993e19c0SJulian Wiedmann cmd->base.mdio_support = 0; 6145993e19c0SJulian Wiedmann cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; 6146993e19c0SJulian Wiedmann cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; 61473f9975aaSFrank Blaschka 61483f9975aaSFrank Blaschka switch (link_type) { 61493f9975aaSFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 61503f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 6151993e19c0SJulian Wiedmann cmd->base.speed = SPEED_100; 6152993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 61533f9975aaSFrank Blaschka break; 61543f9975aaSFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 61553f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 6156993e19c0SJulian Wiedmann cmd->base.speed = SPEED_1000; 6157993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 61583f9975aaSFrank Blaschka break; 61593f9975aaSFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 6160993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10000; 6161993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 61623f9975aaSFrank Blaschka break; 61633f9975aaSFrank Blaschka default: 6164993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10; 6165993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 61663f9975aaSFrank Blaschka } 6167993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port); 61683f9975aaSFrank Blaschka 616902d5cb5bSEugene Crosser /* Check if we can obtain more accurate information. */ 617002d5cb5bSEugene Crosser /* If QUERY_CARD_INFO command is not supported or fails, */ 617102d5cb5bSEugene Crosser /* just return the heuristics that was filled above. */ 6172511c2445SEugene Crosser if (!qeth_card_hw_is_reachable(card)) 6173511c2445SEugene Crosser return -ENODEV; 6174511c2445SEugene Crosser rc = qeth_query_card_info(card, &carrier_info); 6175511c2445SEugene Crosser if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */ 617602d5cb5bSEugene Crosser return 0; 6177511c2445SEugene Crosser if (rc) /* report error from the hardware operation */ 6178511c2445SEugene Crosser return rc; 6179511c2445SEugene Crosser /* on success, fill in the information got from the hardware */ 618002d5cb5bSEugene Crosser 618102d5cb5bSEugene Crosser netdev_dbg(netdev, 618202d5cb5bSEugene Crosser "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", 618302d5cb5bSEugene Crosser carrier_info.card_type, 618402d5cb5bSEugene Crosser carrier_info.port_mode, 618502d5cb5bSEugene Crosser carrier_info.port_speed); 618602d5cb5bSEugene Crosser 618702d5cb5bSEugene Crosser /* Update attributes for which we've obtained more authoritative */ 618802d5cb5bSEugene Crosser /* information, leave the rest the way they where filled above. */ 618902d5cb5bSEugene Crosser switch (carrier_info.card_type) { 619002d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_COPPER_A: 619102d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_COPPER_B: 6192993e19c0SJulian Wiedmann cmd->base.port = PORT_TP; 6193993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 619402d5cb5bSEugene Crosser break; 619502d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_FIBRE_A: 619602d5cb5bSEugene Crosser case CARD_INFO_TYPE_1G_FIBRE_B: 6197993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 6198993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); 619902d5cb5bSEugene Crosser break; 620002d5cb5bSEugene Crosser case CARD_INFO_TYPE_10G_FIBRE_A: 620102d5cb5bSEugene Crosser case CARD_INFO_TYPE_10G_FIBRE_B: 6202993e19c0SJulian Wiedmann cmd->base.port = PORT_FIBRE; 6203993e19c0SJulian Wiedmann qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port); 620402d5cb5bSEugene Crosser break; 620502d5cb5bSEugene Crosser } 620602d5cb5bSEugene Crosser 620702d5cb5bSEugene Crosser switch (carrier_info.port_mode) { 620802d5cb5bSEugene Crosser case CARD_INFO_PORTM_FULLDUPLEX: 6209993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_FULL; 621002d5cb5bSEugene Crosser break; 621102d5cb5bSEugene Crosser case CARD_INFO_PORTM_HALFDUPLEX: 6212993e19c0SJulian Wiedmann cmd->base.duplex = DUPLEX_HALF; 621302d5cb5bSEugene Crosser break; 621402d5cb5bSEugene Crosser } 621502d5cb5bSEugene Crosser 621602d5cb5bSEugene Crosser switch (carrier_info.port_speed) { 621702d5cb5bSEugene Crosser case CARD_INFO_PORTS_10M: 6218993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10; 621902d5cb5bSEugene Crosser break; 622002d5cb5bSEugene Crosser case CARD_INFO_PORTS_100M: 6221993e19c0SJulian Wiedmann cmd->base.speed = SPEED_100; 622202d5cb5bSEugene Crosser break; 622302d5cb5bSEugene Crosser case CARD_INFO_PORTS_1G: 6224993e19c0SJulian Wiedmann cmd->base.speed = SPEED_1000; 622502d5cb5bSEugene Crosser break; 622602d5cb5bSEugene Crosser case CARD_INFO_PORTS_10G: 6227993e19c0SJulian Wiedmann cmd->base.speed = SPEED_10000; 622802d5cb5bSEugene Crosser break; 622902d5cb5bSEugene Crosser } 623002d5cb5bSEugene Crosser 62313f9975aaSFrank Blaschka return 0; 62323f9975aaSFrank Blaschka } 6233993e19c0SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_link_ksettings); 62343f9975aaSFrank Blaschka 6235c9475369SThomas Richter /* Callback to handle checksum offload command reply from OSA card. 6236c9475369SThomas Richter * Verify that required features have been enabled on the card. 6237c9475369SThomas Richter * Return error in hdr->return_code as this value is checked by caller. 6238c9475369SThomas Richter * 6239c9475369SThomas Richter * Always returns zero to indicate no further messages from the OSA card. 6240c9475369SThomas Richter */ 6241c9475369SThomas Richter static int qeth_ipa_checksum_run_cmd_cb(struct qeth_card *card, 6242c9475369SThomas Richter struct qeth_reply *reply, 6243c9475369SThomas Richter unsigned long data) 6244c9475369SThomas Richter { 6245c9475369SThomas Richter struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 6246c9475369SThomas Richter struct qeth_checksum_cmd *chksum_cb = 6247c9475369SThomas Richter (struct qeth_checksum_cmd *)reply->param; 6248c9475369SThomas Richter 6249c9475369SThomas Richter QETH_CARD_TEXT(card, 4, "chkdoccb"); 6250c9475369SThomas Richter if (cmd->hdr.return_code) 6251c9475369SThomas Richter return 0; 6252c9475369SThomas Richter 6253c9475369SThomas Richter memset(chksum_cb, 0, sizeof(*chksum_cb)); 6254c9475369SThomas Richter if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { 6255c9475369SThomas Richter chksum_cb->supported = 6256c9475369SThomas Richter cmd->data.setassparms.data.chksum.supported; 6257c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "strt:%x", chksum_cb->supported); 6258c9475369SThomas Richter } 6259c9475369SThomas Richter if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_ENABLE) { 6260c9475369SThomas Richter chksum_cb->supported = 6261c9475369SThomas Richter cmd->data.setassparms.data.chksum.supported; 6262c9475369SThomas Richter chksum_cb->enabled = 6263c9475369SThomas Richter cmd->data.setassparms.data.chksum.enabled; 6264c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "supp:%x", chksum_cb->supported); 6265c9475369SThomas Richter QETH_CARD_TEXT_(card, 3, "enab:%x", chksum_cb->enabled); 6266c9475369SThomas Richter } 6267c9475369SThomas Richter return 0; 6268c9475369SThomas Richter } 6269c9475369SThomas Richter 6270c9475369SThomas Richter /* Send command to OSA card and check results. */ 6271c9475369SThomas Richter static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, 6272c9475369SThomas Richter enum qeth_ipa_funcs ipa_func, 6273c9475369SThomas Richter __u16 cmd_code, long data, 6274c9475369SThomas Richter struct qeth_checksum_cmd *chksum_cb) 6275c9475369SThomas Richter { 6276c9475369SThomas Richter struct qeth_cmd_buffer *iob; 6277c9475369SThomas Richter int rc = -ENOMEM; 6278c9475369SThomas Richter 6279c9475369SThomas Richter QETH_CARD_TEXT(card, 4, "chkdocmd"); 6280c9475369SThomas Richter iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, 6281c9475369SThomas Richter sizeof(__u32), QETH_PROT_IPV4); 6282c9475369SThomas Richter if (iob) 6283c9475369SThomas Richter rc = qeth_send_setassparms(card, iob, sizeof(__u32), data, 6284c9475369SThomas Richter qeth_ipa_checksum_run_cmd_cb, 6285c9475369SThomas Richter chksum_cb); 6286c9475369SThomas Richter return rc; 6287c9475369SThomas Richter } 6288c9475369SThomas Richter 62898f43fb00SThomas Richter static int qeth_send_checksum_on(struct qeth_card *card, int cstype) 62904d7def2aSThomas Richter { 6291f9d8e6dcSThomas Richter const __u32 required_features = QETH_IPA_CHECKSUM_IP_HDR | 6292f9d8e6dcSThomas Richter QETH_IPA_CHECKSUM_UDP | 6293f9d8e6dcSThomas Richter QETH_IPA_CHECKSUM_TCP; 6294c9475369SThomas Richter struct qeth_checksum_cmd chksum_cb; 62954d7def2aSThomas Richter int rc; 62964d7def2aSThomas Richter 6297c9475369SThomas Richter rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, 6298c9475369SThomas Richter &chksum_cb); 6299f9d8e6dcSThomas Richter if (!rc) { 6300f9d8e6dcSThomas Richter if ((required_features & chksum_cb.supported) != 6301f9d8e6dcSThomas Richter required_features) 6302f9d8e6dcSThomas Richter rc = -EIO; 6303dae84c8eSThomas Richter else if (!(QETH_IPA_CHECKSUM_LP2LP & chksum_cb.supported) && 6304dae84c8eSThomas Richter cstype == IPA_INBOUND_CHECKSUM) 6305dae84c8eSThomas Richter dev_warn(&card->gdev->dev, 6306dae84c8eSThomas Richter "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n", 6307dae84c8eSThomas Richter QETH_CARD_IFNAME(card)); 6308f9d8e6dcSThomas Richter } 63094d7def2aSThomas Richter if (rc) { 6310c9475369SThomas Richter qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); 63118f43fb00SThomas Richter dev_warn(&card->gdev->dev, 63128f43fb00SThomas Richter "Starting HW checksumming for %s failed, using SW checksumming\n", 63134d7def2aSThomas Richter QETH_CARD_IFNAME(card)); 63144d7def2aSThomas Richter return rc; 63154d7def2aSThomas Richter } 6316c9475369SThomas Richter rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE, 6317c9475369SThomas Richter chksum_cb.supported, &chksum_cb); 6318f9d8e6dcSThomas Richter if (!rc) { 6319f9d8e6dcSThomas Richter if ((required_features & chksum_cb.enabled) != 6320f9d8e6dcSThomas Richter required_features) 6321f9d8e6dcSThomas Richter rc = -EIO; 6322f9d8e6dcSThomas Richter } 63234d7def2aSThomas Richter if (rc) { 6324c9475369SThomas Richter qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); 63258f43fb00SThomas Richter dev_warn(&card->gdev->dev, 63268f43fb00SThomas Richter "Enabling HW checksumming for %s failed, using SW checksumming\n", 63274d7def2aSThomas Richter QETH_CARD_IFNAME(card)); 63284d7def2aSThomas Richter return rc; 63294d7def2aSThomas Richter } 63308f43fb00SThomas Richter 63318f43fb00SThomas Richter dev_info(&card->gdev->dev, "HW Checksumming (%sbound) enabled\n", 63328f43fb00SThomas Richter cstype == IPA_INBOUND_CHECKSUM ? "in" : "out"); 63334d7def2aSThomas Richter return 0; 63344d7def2aSThomas Richter } 63354d7def2aSThomas Richter 63368f43fb00SThomas Richter static int qeth_set_ipa_csum(struct qeth_card *card, int on, int cstype) 63374d7def2aSThomas Richter { 6338c9475369SThomas Richter int rc = (on) ? qeth_send_checksum_on(card, cstype) 6339c9475369SThomas Richter : qeth_send_simple_setassparms(card, cstype, 63408f43fb00SThomas Richter IPA_CMD_ASS_STOP, 0); 6341c9475369SThomas Richter return rc ? -EIO : 0; 63424d7def2aSThomas Richter } 63434d7def2aSThomas Richter 63448f43fb00SThomas Richter static int qeth_set_ipa_tso(struct qeth_card *card, int on) 63454d7def2aSThomas Richter { 63468f43fb00SThomas Richter int rc; 63474d7def2aSThomas Richter 63488f43fb00SThomas Richter QETH_CARD_TEXT(card, 3, "sttso"); 63498f43fb00SThomas Richter 63508f43fb00SThomas Richter if (on) { 63518f43fb00SThomas Richter rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, 63524d7def2aSThomas Richter IPA_CMD_ASS_START, 0); 63538f43fb00SThomas Richter if (rc) { 63548f43fb00SThomas Richter dev_warn(&card->gdev->dev, 63558f43fb00SThomas Richter "Starting outbound TCP segmentation offload for %s failed\n", 63568f43fb00SThomas Richter QETH_CARD_IFNAME(card)); 63578f43fb00SThomas Richter return -EIO; 63588f43fb00SThomas Richter } 63598f43fb00SThomas Richter dev_info(&card->gdev->dev, "Outbound TSO enabled\n"); 63608f43fb00SThomas Richter } else { 63618f43fb00SThomas Richter rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, 63628f43fb00SThomas Richter IPA_CMD_ASS_STOP, 0); 63638f43fb00SThomas Richter } 63644d7def2aSThomas Richter return rc; 63654d7def2aSThomas Richter } 63668f43fb00SThomas Richter 6367ce344356SJulian Wiedmann #define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO) 6368ce344356SJulian Wiedmann 6369ce344356SJulian Wiedmann /** 6370ce344356SJulian Wiedmann * qeth_recover_features() - Restore device features after recovery 6371ce344356SJulian Wiedmann * @dev: the recovering net_device 6372ce344356SJulian Wiedmann * 6373ce344356SJulian Wiedmann * Caller must hold rtnl lock. 6374ce344356SJulian Wiedmann */ 6375ce344356SJulian Wiedmann void qeth_recover_features(struct net_device *dev) 6376e830baa9SHans Wippel { 6377ce344356SJulian Wiedmann netdev_features_t features = dev->features; 6378e830baa9SHans Wippel struct qeth_card *card = dev->ml_priv; 6379e830baa9SHans Wippel 6380ce344356SJulian Wiedmann /* force-off any feature that needs an IPA sequence. 6381ce344356SJulian Wiedmann * netdev_update_features() will restart them. 6382ce344356SJulian Wiedmann */ 6383ce344356SJulian Wiedmann dev->features &= ~QETH_HW_FEATURES; 6384ce344356SJulian Wiedmann netdev_update_features(dev); 6385e830baa9SHans Wippel 6386ce344356SJulian Wiedmann if (features == dev->features) 6387ce344356SJulian Wiedmann return; 6388e830baa9SHans Wippel dev_warn(&card->gdev->dev, 6389e830baa9SHans Wippel "Device recovery failed to restore all offload features\n"); 6390e830baa9SHans Wippel } 6391e830baa9SHans Wippel EXPORT_SYMBOL_GPL(qeth_recover_features); 6392e830baa9SHans Wippel 63938f43fb00SThomas Richter int qeth_set_features(struct net_device *dev, netdev_features_t features) 63948f43fb00SThomas Richter { 63958f43fb00SThomas Richter struct qeth_card *card = dev->ml_priv; 63966c7cd712SHans Wippel netdev_features_t changed = dev->features ^ features; 63978f43fb00SThomas Richter int rc = 0; 63988f43fb00SThomas Richter 63998f43fb00SThomas Richter QETH_DBF_TEXT(SETUP, 2, "setfeat"); 64008f43fb00SThomas Richter QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); 64018f43fb00SThomas Richter 64026c7cd712SHans Wippel if ((changed & NETIF_F_IP_CSUM)) { 64038f43fb00SThomas Richter rc = qeth_set_ipa_csum(card, 64048f43fb00SThomas Richter features & NETIF_F_IP_CSUM ? 1 : 0, 64058f43fb00SThomas Richter IPA_OUTBOUND_CHECKSUM); 64066c7cd712SHans Wippel if (rc) 64076c7cd712SHans Wippel changed ^= NETIF_F_IP_CSUM; 64086c7cd712SHans Wippel } 64096c7cd712SHans Wippel if ((changed & NETIF_F_RXCSUM)) { 64106c7cd712SHans Wippel rc = qeth_set_ipa_csum(card, 64118f43fb00SThomas Richter features & NETIF_F_RXCSUM ? 1 : 0, 64128f43fb00SThomas Richter IPA_INBOUND_CHECKSUM); 64136c7cd712SHans Wippel if (rc) 64146c7cd712SHans Wippel changed ^= NETIF_F_RXCSUM; 64156c7cd712SHans Wippel } 64166c7cd712SHans Wippel if ((changed & NETIF_F_TSO)) { 64176c7cd712SHans Wippel rc = qeth_set_ipa_tso(card, features & NETIF_F_TSO ? 1 : 0); 64186c7cd712SHans Wippel if (rc) 64196c7cd712SHans Wippel changed ^= NETIF_F_TSO; 64206c7cd712SHans Wippel } 64216c7cd712SHans Wippel 64226c7cd712SHans Wippel /* everything changed successfully? */ 64236c7cd712SHans Wippel if ((dev->features ^ features) == changed) 64246c7cd712SHans Wippel return 0; 64256c7cd712SHans Wippel /* something went wrong. save changed features and return error */ 64266c7cd712SHans Wippel dev->features ^= changed; 64276c7cd712SHans Wippel return -EIO; 64288f43fb00SThomas Richter } 64298f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_set_features); 64308f43fb00SThomas Richter 64318f43fb00SThomas Richter netdev_features_t qeth_fix_features(struct net_device *dev, 64328f43fb00SThomas Richter netdev_features_t features) 64338f43fb00SThomas Richter { 64348f43fb00SThomas Richter struct qeth_card *card = dev->ml_priv; 64358f43fb00SThomas Richter 64368f43fb00SThomas Richter QETH_DBF_TEXT(SETUP, 2, "fixfeat"); 64378f43fb00SThomas Richter if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) 64388f43fb00SThomas Richter features &= ~NETIF_F_IP_CSUM; 64398f43fb00SThomas Richter if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) 64408f43fb00SThomas Richter features &= ~NETIF_F_RXCSUM; 6441cf536ffeSJulian Wiedmann if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) 64428f43fb00SThomas Richter features &= ~NETIF_F_TSO; 64436c7cd712SHans Wippel /* if the card isn't up, remove features that require hw changes */ 64446c7cd712SHans Wippel if (card->state == CARD_STATE_DOWN || 64456c7cd712SHans Wippel card->state == CARD_STATE_RECOVER) 6446ce344356SJulian Wiedmann features &= ~QETH_HW_FEATURES; 64478f43fb00SThomas Richter QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); 64488f43fb00SThomas Richter return features; 64498f43fb00SThomas Richter } 64508f43fb00SThomas Richter EXPORT_SYMBOL_GPL(qeth_fix_features); 64514d7def2aSThomas Richter 64524a71df50SFrank Blaschka static int __init qeth_core_init(void) 64534a71df50SFrank Blaschka { 64544a71df50SFrank Blaschka int rc; 64554a71df50SFrank Blaschka 645674eacdb9SFrank Blaschka pr_info("loading core functions\n"); 64574a71df50SFrank Blaschka INIT_LIST_HEAD(&qeth_core_card_list.list); 6458819dc537SStefan Raspl INIT_LIST_HEAD(&qeth_dbf_list); 64594a71df50SFrank Blaschka rwlock_init(&qeth_core_card_list.rwlock); 64602022e00cSFrank Blaschka mutex_init(&qeth_mod_mutex); 64614a71df50SFrank Blaschka 64620f54761dSStefan Raspl qeth_wq = create_singlethread_workqueue("qeth_wq"); 64630f54761dSStefan Raspl 64644a71df50SFrank Blaschka rc = qeth_register_dbf_views(); 64654a71df50SFrank Blaschka if (rc) 64664a71df50SFrank Blaschka goto out_err; 6467035da16fSMark McLoughlin qeth_core_root_dev = root_device_register("qeth"); 64689262c6c2SDuan Jiong rc = PTR_ERR_OR_ZERO(qeth_core_root_dev); 64694a71df50SFrank Blaschka if (rc) 64704a71df50SFrank Blaschka goto register_err; 6471683d718aSFrank Blaschka qeth_core_header_cache = kmem_cache_create("qeth_hdr", 6472683d718aSFrank Blaschka sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL); 6473683d718aSFrank Blaschka if (!qeth_core_header_cache) { 6474683d718aSFrank Blaschka rc = -ENOMEM; 6475683d718aSFrank Blaschka goto slab_err; 6476683d718aSFrank Blaschka } 64770da9581dSEinar Lueck qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf", 64780da9581dSEinar Lueck sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL); 64790da9581dSEinar Lueck if (!qeth_qdio_outbuf_cache) { 64800da9581dSEinar Lueck rc = -ENOMEM; 64810da9581dSEinar Lueck goto cqslab_err; 64820da9581dSEinar Lueck } 64834a71df50SFrank Blaschka rc = ccw_driver_register(&qeth_ccw_driver); 64844a71df50SFrank Blaschka if (rc) 64854a71df50SFrank Blaschka goto ccw_err; 6486f47e2256SSebastian Ott qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups; 64874a71df50SFrank Blaschka rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); 64884a71df50SFrank Blaschka if (rc) 64894a71df50SFrank Blaschka goto ccwgroup_err; 64900da9581dSEinar Lueck 6491683d718aSFrank Blaschka return 0; 6492afb6ac59SSebastian Ott 6493afb6ac59SSebastian Ott ccwgroup_err: 6494afb6ac59SSebastian Ott ccw_driver_unregister(&qeth_ccw_driver); 6495afb6ac59SSebastian Ott ccw_err: 6496afb6ac59SSebastian Ott kmem_cache_destroy(qeth_qdio_outbuf_cache); 64970da9581dSEinar Lueck cqslab_err: 64980da9581dSEinar Lueck kmem_cache_destroy(qeth_core_header_cache); 6499683d718aSFrank Blaschka slab_err: 6500035da16fSMark McLoughlin root_device_unregister(qeth_core_root_dev); 65014a71df50SFrank Blaschka register_err: 65024a71df50SFrank Blaschka qeth_unregister_dbf_views(); 65034a71df50SFrank Blaschka out_err: 650474eacdb9SFrank Blaschka pr_err("Initializing the qeth device driver failed\n"); 65054a71df50SFrank Blaschka return rc; 65064a71df50SFrank Blaschka } 65074a71df50SFrank Blaschka 65084a71df50SFrank Blaschka static void __exit qeth_core_exit(void) 65094a71df50SFrank Blaschka { 6510819dc537SStefan Raspl qeth_clear_dbf_list(); 65110f54761dSStefan Raspl destroy_workqueue(qeth_wq); 65124a71df50SFrank Blaschka ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); 65134a71df50SFrank Blaschka ccw_driver_unregister(&qeth_ccw_driver); 65140da9581dSEinar Lueck kmem_cache_destroy(qeth_qdio_outbuf_cache); 6515683d718aSFrank Blaschka kmem_cache_destroy(qeth_core_header_cache); 6516afb6ac59SSebastian Ott root_device_unregister(qeth_core_root_dev); 65174a71df50SFrank Blaschka qeth_unregister_dbf_views(); 651874eacdb9SFrank Blaschka pr_info("core functions removed\n"); 65194a71df50SFrank Blaschka } 65204a71df50SFrank Blaschka 65214a71df50SFrank Blaschka module_init(qeth_core_init); 65224a71df50SFrank Blaschka module_exit(qeth_core_exit); 65234a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 65244a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth core functions"); 65254a71df50SFrank Blaschka MODULE_LICENSE("GPL"); 6526