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> 234a71df50SFrank Blaschka 24ab4227cbSMartin Schwidefsky #include <asm/ebcdic.h> 25ab4227cbSMartin Schwidefsky #include <asm/io.h> 261da74b1cSFrank Blaschka #include <asm/sysinfo.h> 27c3ab96f3SFrank Blaschka #include <asm/compat.h> 284a71df50SFrank Blaschka 294a71df50SFrank Blaschka #include "qeth_core.h" 304a71df50SFrank Blaschka 31d11ba0c4SPeter Tiedemann struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { 32d11ba0c4SPeter Tiedemann /* define dbf - Name, Pages, Areas, Maxlen, Level, View, Handle */ 33d11ba0c4SPeter Tiedemann /* N P A M L V H */ 34d11ba0c4SPeter Tiedemann [QETH_DBF_SETUP] = {"qeth_setup", 35d11ba0c4SPeter Tiedemann 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, 36d11ba0c4SPeter Tiedemann [QETH_DBF_MSG] = {"qeth_msg", 37d11ba0c4SPeter Tiedemann 8, 1, 128, 3, &debug_sprintf_view, NULL}, 38d11ba0c4SPeter Tiedemann [QETH_DBF_CTRL] = {"qeth_control", 39d11ba0c4SPeter Tiedemann 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, 40d11ba0c4SPeter Tiedemann }; 41d11ba0c4SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf); 424a71df50SFrank Blaschka 434a71df50SFrank Blaschka struct qeth_card_list_struct qeth_core_card_list; 444a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_card_list); 45683d718aSFrank Blaschka struct kmem_cache *qeth_core_header_cache; 46683d718aSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_header_cache); 470da9581dSEinar Lueck static struct kmem_cache *qeth_qdio_outbuf_cache; 484a71df50SFrank Blaschka 494a71df50SFrank Blaschka static struct device *qeth_core_root_dev; 505113fec0SUrsula Braun static unsigned int known_devices[][6] = QETH_MODELLIST_ARRAY; 514a71df50SFrank Blaschka static struct lock_class_key qdio_out_skb_queue_key; 522022e00cSFrank Blaschka static struct mutex qeth_mod_mutex; 534a71df50SFrank Blaschka 544a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *, 554a71df50SFrank Blaschka struct qeth_cmd_buffer *); 564a71df50SFrank Blaschka static int qeth_issue_next_read(struct qeth_card *); 574a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *); 584a71df50SFrank Blaschka static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32); 594a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *); 604a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *); 610da9581dSEinar Lueck static void qeth_free_qdio_buffers(struct qeth_card *); 62b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *queue, 63b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 64b3332930SFrank Blaschka enum iucv_tx_notify notification); 65b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); 660da9581dSEinar Lueck static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, 670da9581dSEinar Lueck struct qeth_qdio_out_buffer *buf, 680da9581dSEinar Lueck enum qeth_qdio_buffer_states newbufstate); 6972861ae7SEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); 704a71df50SFrank Blaschka 710f54761dSStefan Raspl static struct workqueue_struct *qeth_wq; 720f54761dSStefan Raspl 730f54761dSStefan Raspl static void qeth_close_dev_handler(struct work_struct *work) 740f54761dSStefan Raspl { 750f54761dSStefan Raspl struct qeth_card *card; 760f54761dSStefan Raspl 770f54761dSStefan Raspl card = container_of(work, struct qeth_card, close_dev_work); 780f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevhdl"); 790f54761dSStefan Raspl rtnl_lock(); 800f54761dSStefan Raspl dev_close(card->dev); 810f54761dSStefan Raspl rtnl_unlock(); 820f54761dSStefan Raspl ccwgroup_set_offline(card->gdev); 830f54761dSStefan Raspl } 840f54761dSStefan Raspl 850f54761dSStefan Raspl void qeth_close_dev(struct qeth_card *card) 860f54761dSStefan Raspl { 870f54761dSStefan Raspl QETH_CARD_TEXT(card, 2, "cldevsubm"); 880f54761dSStefan Raspl queue_work(qeth_wq, &card->close_dev_work); 890f54761dSStefan Raspl } 900f54761dSStefan Raspl EXPORT_SYMBOL_GPL(qeth_close_dev); 910f54761dSStefan Raspl 924a71df50SFrank Blaschka static inline const char *qeth_get_cardname(struct qeth_card *card) 934a71df50SFrank Blaschka { 944a71df50SFrank Blaschka if (card->info.guestlan) { 954a71df50SFrank Blaschka switch (card->info.type) { 965113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 977096b187SStefan Raspl return " Virtual NIC QDIO"; 984a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 997096b187SStefan Raspl return " Virtual NIC Hiper"; 1005113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1017096b187SStefan Raspl return " Virtual NIC QDIO - OSM"; 1025113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1037096b187SStefan Raspl return " Virtual NIC QDIO - OSX"; 1044a71df50SFrank Blaschka default: 1054a71df50SFrank Blaschka return " unknown"; 1064a71df50SFrank Blaschka } 1074a71df50SFrank Blaschka } else { 1084a71df50SFrank Blaschka switch (card->info.type) { 1095113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1104a71df50SFrank Blaschka return " OSD Express"; 1114a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1124a71df50SFrank Blaschka return " HiperSockets"; 1134a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1144a71df50SFrank Blaschka return " OSN QDIO"; 1155113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1165113fec0SUrsula Braun return " OSM QDIO"; 1175113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1185113fec0SUrsula Braun return " OSX QDIO"; 1194a71df50SFrank Blaschka default: 1204a71df50SFrank Blaschka return " unknown"; 1214a71df50SFrank Blaschka } 1224a71df50SFrank Blaschka } 1234a71df50SFrank Blaschka return " n/a"; 1244a71df50SFrank Blaschka } 1254a71df50SFrank Blaschka 1264a71df50SFrank Blaschka /* max length to be returned: 14 */ 1274a71df50SFrank Blaschka const char *qeth_get_cardname_short(struct qeth_card *card) 1284a71df50SFrank Blaschka { 1294a71df50SFrank Blaschka if (card->info.guestlan) { 1304a71df50SFrank Blaschka switch (card->info.type) { 1315113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1327096b187SStefan Raspl return "Virt.NIC QDIO"; 1334a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1347096b187SStefan Raspl return "Virt.NIC Hiper"; 1355113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1367096b187SStefan Raspl return "Virt.NIC OSM"; 1375113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1387096b187SStefan Raspl return "Virt.NIC OSX"; 1394a71df50SFrank Blaschka default: 1404a71df50SFrank Blaschka return "unknown"; 1414a71df50SFrank Blaschka } 1424a71df50SFrank Blaschka } else { 1434a71df50SFrank Blaschka switch (card->info.type) { 1445113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 1454a71df50SFrank Blaschka switch (card->info.link_type) { 1464a71df50SFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 1474a71df50SFrank Blaschka return "OSD_100"; 1484a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 1494a71df50SFrank Blaschka return "HSTR"; 1504a71df50SFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 1514a71df50SFrank Blaschka return "OSD_1000"; 1524a71df50SFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 1534a71df50SFrank Blaschka return "OSD_10GIG"; 1544a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 1554a71df50SFrank Blaschka return "OSD_FE_LANE"; 1564a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_TR: 1574a71df50SFrank Blaschka return "OSD_TR_LANE"; 1584a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 1594a71df50SFrank Blaschka return "OSD_GbE_LANE"; 1604a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE: 1614a71df50SFrank Blaschka return "OSD_ATM_LANE"; 1624a71df50SFrank Blaschka default: 1634a71df50SFrank Blaschka return "OSD_Express"; 1644a71df50SFrank Blaschka } 1654a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 1664a71df50SFrank Blaschka return "HiperSockets"; 1674a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 1684a71df50SFrank Blaschka return "OSN"; 1695113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 1705113fec0SUrsula Braun return "OSM_1000"; 1715113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 1725113fec0SUrsula Braun return "OSX_10GIG"; 1734a71df50SFrank Blaschka default: 1744a71df50SFrank Blaschka return "unknown"; 1754a71df50SFrank Blaschka } 1764a71df50SFrank Blaschka } 1774a71df50SFrank Blaschka return "n/a"; 1784a71df50SFrank Blaschka } 1794a71df50SFrank Blaschka 18065d8013cSStefan Raspl void qeth_set_recovery_task(struct qeth_card *card) 18165d8013cSStefan Raspl { 18265d8013cSStefan Raspl card->recovery_task = current; 18365d8013cSStefan Raspl } 18465d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_set_recovery_task); 18565d8013cSStefan Raspl 18665d8013cSStefan Raspl void qeth_clear_recovery_task(struct qeth_card *card) 18765d8013cSStefan Raspl { 18865d8013cSStefan Raspl card->recovery_task = NULL; 18965d8013cSStefan Raspl } 19065d8013cSStefan Raspl EXPORT_SYMBOL_GPL(qeth_clear_recovery_task); 19165d8013cSStefan Raspl 19265d8013cSStefan Raspl static bool qeth_is_recovery_task(const struct qeth_card *card) 19365d8013cSStefan Raspl { 19465d8013cSStefan Raspl return card->recovery_task == current; 19565d8013cSStefan Raspl } 19665d8013cSStefan Raspl 1974a71df50SFrank Blaschka void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads, 1984a71df50SFrank Blaschka int clear_start_mask) 1994a71df50SFrank Blaschka { 2004a71df50SFrank Blaschka unsigned long flags; 2014a71df50SFrank Blaschka 2024a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2034a71df50SFrank Blaschka card->thread_allowed_mask = threads; 2044a71df50SFrank Blaschka if (clear_start_mask) 2054a71df50SFrank Blaschka card->thread_start_mask &= threads; 2064a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2074a71df50SFrank Blaschka wake_up(&card->wait_q); 2084a71df50SFrank Blaschka } 2094a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_set_allowed_threads); 2104a71df50SFrank Blaschka 2114a71df50SFrank Blaschka int qeth_threads_running(struct qeth_card *card, unsigned long threads) 2124a71df50SFrank Blaschka { 2134a71df50SFrank Blaschka unsigned long flags; 2144a71df50SFrank Blaschka int rc = 0; 2154a71df50SFrank Blaschka 2164a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 2174a71df50SFrank Blaschka rc = (card->thread_running_mask & threads); 2184a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 2194a71df50SFrank Blaschka return rc; 2204a71df50SFrank Blaschka } 2214a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_threads_running); 2224a71df50SFrank Blaschka 2234a71df50SFrank Blaschka int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) 2244a71df50SFrank Blaschka { 22565d8013cSStefan Raspl if (qeth_is_recovery_task(card)) 22665d8013cSStefan Raspl return 0; 2274a71df50SFrank Blaschka return wait_event_interruptible(card->wait_q, 2284a71df50SFrank Blaschka qeth_threads_running(card, threads) == 0); 2294a71df50SFrank Blaschka } 2304a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_threads); 2314a71df50SFrank Blaschka 2324a71df50SFrank Blaschka void qeth_clear_working_pool_list(struct qeth_card *card) 2334a71df50SFrank Blaschka { 2344a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 2354a71df50SFrank Blaschka 236847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "clwrklst"); 2374a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 2384a71df50SFrank Blaschka &card->qdio.in_buf_pool.entry_list, list){ 2394a71df50SFrank Blaschka list_del(&pool_entry->list); 2404a71df50SFrank Blaschka } 2414a71df50SFrank Blaschka } 2424a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_working_pool_list); 2434a71df50SFrank Blaschka 2444a71df50SFrank Blaschka static int qeth_alloc_buffer_pool(struct qeth_card *card) 2454a71df50SFrank Blaschka { 2464a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 2474a71df50SFrank Blaschka void *ptr; 2484a71df50SFrank Blaschka int i, j; 2494a71df50SFrank Blaschka 250847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "alocpool"); 2514a71df50SFrank Blaschka for (i = 0; i < card->qdio.init_pool.buf_count; ++i) { 252b3332930SFrank Blaschka pool_entry = kzalloc(sizeof(*pool_entry), GFP_KERNEL); 2534a71df50SFrank Blaschka if (!pool_entry) { 2544a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2554a71df50SFrank Blaschka return -ENOMEM; 2564a71df50SFrank Blaschka } 2574a71df50SFrank Blaschka for (j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j) { 258508b3c4fSUrsula Braun ptr = (void *) __get_free_page(GFP_KERNEL); 2594a71df50SFrank Blaschka if (!ptr) { 2604a71df50SFrank Blaschka while (j > 0) 2614a71df50SFrank Blaschka free_page((unsigned long) 2624a71df50SFrank Blaschka pool_entry->elements[--j]); 2634a71df50SFrank Blaschka kfree(pool_entry); 2644a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2654a71df50SFrank Blaschka return -ENOMEM; 2664a71df50SFrank Blaschka } 2674a71df50SFrank Blaschka pool_entry->elements[j] = ptr; 2684a71df50SFrank Blaschka } 2694a71df50SFrank Blaschka list_add(&pool_entry->init_list, 2704a71df50SFrank Blaschka &card->qdio.init_pool.entry_list); 2714a71df50SFrank Blaschka } 2724a71df50SFrank Blaschka return 0; 2734a71df50SFrank Blaschka } 2744a71df50SFrank Blaschka 2754a71df50SFrank Blaschka int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt) 2764a71df50SFrank Blaschka { 277847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "realcbp"); 2784a71df50SFrank Blaschka 2794a71df50SFrank Blaschka if ((card->state != CARD_STATE_DOWN) && 2804a71df50SFrank Blaschka (card->state != CARD_STATE_RECOVER)) 2814a71df50SFrank Blaschka return -EPERM; 2824a71df50SFrank Blaschka 2834a71df50SFrank Blaschka /* TODO: steel/add buffers from/to a running card's buffer pool (?) */ 2844a71df50SFrank Blaschka qeth_clear_working_pool_list(card); 2854a71df50SFrank Blaschka qeth_free_buffer_pool(card); 2864a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = bufcnt; 2874a71df50SFrank Blaschka card->qdio.init_pool.buf_count = bufcnt; 2884a71df50SFrank Blaschka return qeth_alloc_buffer_pool(card); 2894a71df50SFrank Blaschka } 29076b11f8eSUrsula Braun EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool); 2914a71df50SFrank Blaschka 2920da9581dSEinar Lueck static inline int qeth_cq_init(struct qeth_card *card) 2930da9581dSEinar Lueck { 2940da9581dSEinar Lueck int rc; 2950da9581dSEinar Lueck 2960da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 2970da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqinit"); 2980da9581dSEinar Lueck memset(card->qdio.c_q->qdio_bufs, 0, 2990da9581dSEinar Lueck QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); 3000da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = 127; 3010da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 3020da9581dSEinar Lueck card->qdio.no_in_queues - 1, 0, 3030da9581dSEinar Lueck 127); 3040da9581dSEinar Lueck if (rc) { 3050da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 3060da9581dSEinar Lueck goto out; 3070da9581dSEinar Lueck } 3080da9581dSEinar Lueck } 3090da9581dSEinar Lueck rc = 0; 3100da9581dSEinar Lueck out: 3110da9581dSEinar Lueck return rc; 3120da9581dSEinar Lueck } 3130da9581dSEinar Lueck 3140da9581dSEinar Lueck static inline int qeth_alloc_cq(struct qeth_card *card) 3150da9581dSEinar Lueck { 3160da9581dSEinar Lueck int rc; 3170da9581dSEinar Lueck 3180da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 3190da9581dSEinar Lueck int i; 3200da9581dSEinar Lueck struct qdio_outbuf_state *outbuf_states; 3210da9581dSEinar Lueck 3220da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cqon"); 3230da9581dSEinar Lueck card->qdio.c_q = kzalloc(sizeof(struct qeth_qdio_q), 3240da9581dSEinar Lueck GFP_KERNEL); 3250da9581dSEinar Lueck if (!card->qdio.c_q) { 3260da9581dSEinar Lueck rc = -1; 3270da9581dSEinar Lueck goto kmsg_out; 3280da9581dSEinar Lueck } 3290da9581dSEinar Lueck QETH_DBF_HEX(SETUP, 2, &card->qdio.c_q, sizeof(void *)); 3300da9581dSEinar Lueck 3310da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 3320da9581dSEinar Lueck card->qdio.c_q->bufs[i].buffer = 3330da9581dSEinar Lueck &card->qdio.c_q->qdio_bufs[i]; 3340da9581dSEinar Lueck } 3350da9581dSEinar Lueck 3360da9581dSEinar Lueck card->qdio.no_in_queues = 2; 3370da9581dSEinar Lueck 3384a912f98SZhang Yanfei card->qdio.out_bufstates = 3390da9581dSEinar Lueck kzalloc(card->qdio.no_out_queues * 3400da9581dSEinar Lueck QDIO_MAX_BUFFERS_PER_Q * 3410da9581dSEinar Lueck sizeof(struct qdio_outbuf_state), GFP_KERNEL); 3420da9581dSEinar Lueck outbuf_states = card->qdio.out_bufstates; 3430da9581dSEinar Lueck if (outbuf_states == NULL) { 3440da9581dSEinar Lueck rc = -1; 3450da9581dSEinar Lueck goto free_cq_out; 3460da9581dSEinar Lueck } 3470da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 3480da9581dSEinar Lueck card->qdio.out_qs[i]->bufstates = outbuf_states; 3490da9581dSEinar Lueck outbuf_states += QDIO_MAX_BUFFERS_PER_Q; 3500da9581dSEinar Lueck } 3510da9581dSEinar Lueck } else { 3520da9581dSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "nocq"); 3530da9581dSEinar Lueck card->qdio.c_q = NULL; 3540da9581dSEinar Lueck card->qdio.no_in_queues = 1; 3550da9581dSEinar Lueck } 3560da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "iqc%d", card->qdio.no_in_queues); 3570da9581dSEinar Lueck rc = 0; 3580da9581dSEinar Lueck out: 3590da9581dSEinar Lueck return rc; 3600da9581dSEinar Lueck free_cq_out: 3610da9581dSEinar Lueck kfree(card->qdio.c_q); 3620da9581dSEinar Lueck card->qdio.c_q = NULL; 3630da9581dSEinar Lueck kmsg_out: 3640da9581dSEinar Lueck dev_err(&card->gdev->dev, "Failed to create completion queue\n"); 3650da9581dSEinar Lueck goto out; 3660da9581dSEinar Lueck } 3670da9581dSEinar Lueck 3680da9581dSEinar Lueck static inline void qeth_free_cq(struct qeth_card *card) 3690da9581dSEinar Lueck { 3700da9581dSEinar Lueck if (card->qdio.c_q) { 3710da9581dSEinar Lueck --card->qdio.no_in_queues; 3720da9581dSEinar Lueck kfree(card->qdio.c_q); 3730da9581dSEinar Lueck card->qdio.c_q = NULL; 3740da9581dSEinar Lueck } 3750da9581dSEinar Lueck kfree(card->qdio.out_bufstates); 3760da9581dSEinar Lueck card->qdio.out_bufstates = NULL; 3770da9581dSEinar Lueck } 3780da9581dSEinar Lueck 379b3332930SFrank Blaschka static inline enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, 380b3332930SFrank Blaschka int delayed) { 381b3332930SFrank Blaschka enum iucv_tx_notify n; 382b3332930SFrank Blaschka 383b3332930SFrank Blaschka switch (sbalf15) { 384b3332930SFrank Blaschka case 0: 385b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_OK : TX_NOTIFY_OK; 386b3332930SFrank Blaschka break; 387b3332930SFrank Blaschka case 4: 388b3332930SFrank Blaschka case 16: 389b3332930SFrank Blaschka case 17: 390b3332930SFrank Blaschka case 18: 391b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_UNREACHABLE : 392b3332930SFrank Blaschka TX_NOTIFY_UNREACHABLE; 393b3332930SFrank Blaschka break; 394b3332930SFrank Blaschka default: 395b3332930SFrank Blaschka n = delayed ? TX_NOTIFY_DELAYED_GENERALERROR : 396b3332930SFrank Blaschka TX_NOTIFY_GENERALERROR; 397b3332930SFrank Blaschka break; 398b3332930SFrank Blaschka } 399b3332930SFrank Blaschka 400b3332930SFrank Blaschka return n; 401b3332930SFrank Blaschka } 402b3332930SFrank Blaschka 4030da9581dSEinar Lueck static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, 4040da9581dSEinar Lueck int bidx, int forced_cleanup) 4050da9581dSEinar Lueck { 40672861ae7SEinar Lueck if (q->card->options.cq != QETH_CQ_ENABLED) 40772861ae7SEinar Lueck return; 40872861ae7SEinar Lueck 4090da9581dSEinar Lueck if (q->bufs[bidx]->next_pending != NULL) { 4100da9581dSEinar Lueck struct qeth_qdio_out_buffer *head = q->bufs[bidx]; 4110da9581dSEinar Lueck struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending; 4120da9581dSEinar Lueck 4130da9581dSEinar Lueck while (c) { 4140da9581dSEinar Lueck if (forced_cleanup || 4150da9581dSEinar Lueck atomic_read(&c->state) == 4160da9581dSEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED) { 4170da9581dSEinar Lueck struct qeth_qdio_out_buffer *f = c; 4180da9581dSEinar Lueck QETH_CARD_TEXT(f->q->card, 5, "fp"); 4190da9581dSEinar Lueck QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f); 420b3332930SFrank Blaschka /* release here to avoid interleaving between 421b3332930SFrank Blaschka outbound tasklet and inbound tasklet 422b3332930SFrank Blaschka regarding notifications and lifecycle */ 423b3332930SFrank Blaschka qeth_release_skbs(c); 424b3332930SFrank Blaschka 4250da9581dSEinar Lueck c = f->next_pending; 42618af5c17SStefan Raspl WARN_ON_ONCE(head->next_pending != f); 4270da9581dSEinar Lueck head->next_pending = c; 4280da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, f); 4290da9581dSEinar Lueck } else { 4300da9581dSEinar Lueck head = c; 4310da9581dSEinar Lueck c = c->next_pending; 4320da9581dSEinar Lueck } 4330da9581dSEinar Lueck 4340da9581dSEinar Lueck } 4350da9581dSEinar Lueck } 43672861ae7SEinar Lueck if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == 43772861ae7SEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED)) { 43872861ae7SEinar Lueck /* for recovery situations */ 43972861ae7SEinar Lueck q->bufs[bidx]->aob = q->bufstates[bidx].aob; 44072861ae7SEinar Lueck qeth_init_qdio_out_buf(q, bidx); 44172861ae7SEinar Lueck QETH_CARD_TEXT(q->card, 2, "clprecov"); 44272861ae7SEinar Lueck } 4430da9581dSEinar Lueck } 4440da9581dSEinar Lueck 4450da9581dSEinar Lueck 4460da9581dSEinar Lueck static inline void qeth_qdio_handle_aob(struct qeth_card *card, 4470da9581dSEinar Lueck unsigned long phys_aob_addr) { 4480da9581dSEinar Lueck struct qaob *aob; 4490da9581dSEinar Lueck struct qeth_qdio_out_buffer *buffer; 450b3332930SFrank Blaschka enum iucv_tx_notify notification; 4510da9581dSEinar Lueck 4520da9581dSEinar Lueck aob = (struct qaob *) phys_to_virt(phys_aob_addr); 4530da9581dSEinar Lueck QETH_CARD_TEXT(card, 5, "haob"); 4540da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", phys_aob_addr); 4550da9581dSEinar Lueck buffer = (struct qeth_qdio_out_buffer *) aob->user1; 4560da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); 4570da9581dSEinar Lueck 458b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, 459b3332930SFrank Blaschka QETH_QDIO_BUF_IN_CQ) == QETH_QDIO_BUF_PRIMED) { 460b3332930SFrank Blaschka notification = TX_NOTIFY_OK; 461b3332930SFrank Blaschka } else { 46218af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buffer->state) != 46318af5c17SStefan Raspl QETH_QDIO_BUF_PENDING); 464b3332930SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); 465b3332930SFrank Blaschka notification = TX_NOTIFY_DELAYED_OK; 466b3332930SFrank Blaschka } 467b3332930SFrank Blaschka 468b3332930SFrank Blaschka if (aob->aorc != 0) { 469b3332930SFrank Blaschka QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc); 470b3332930SFrank Blaschka notification = qeth_compute_cq_notification(aob->aorc, 1); 471b3332930SFrank Blaschka } 472b3332930SFrank Blaschka qeth_notify_skbs(buffer->q, buffer, notification); 473b3332930SFrank Blaschka 4740da9581dSEinar Lueck buffer->aob = NULL; 4750da9581dSEinar Lueck qeth_clear_output_buffer(buffer->q, buffer, 4760da9581dSEinar Lueck QETH_QDIO_BUF_HANDLED_DELAYED); 47772861ae7SEinar Lueck 4780da9581dSEinar Lueck /* from here on: do not touch buffer anymore */ 4790da9581dSEinar Lueck qdio_release_aob(aob); 4800da9581dSEinar Lueck } 4810da9581dSEinar Lueck 4820da9581dSEinar Lueck static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue) 4830da9581dSEinar Lueck { 4840da9581dSEinar Lueck return card->options.cq == QETH_CQ_ENABLED && 4850da9581dSEinar Lueck card->qdio.c_q != NULL && 4860da9581dSEinar Lueck queue != 0 && 4870da9581dSEinar Lueck queue == card->qdio.no_in_queues - 1; 4880da9581dSEinar Lueck } 4890da9581dSEinar Lueck 4900da9581dSEinar Lueck 4914a71df50SFrank Blaschka static int qeth_issue_next_read(struct qeth_card *card) 4924a71df50SFrank Blaschka { 4934a71df50SFrank Blaschka int rc; 4944a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 4954a71df50SFrank Blaschka 496847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "issnxrd"); 4974a71df50SFrank Blaschka if (card->read.state != CH_STATE_UP) 4984a71df50SFrank Blaschka return -EIO; 4994a71df50SFrank Blaschka iob = qeth_get_buffer(&card->read); 5004a71df50SFrank Blaschka if (!iob) { 50174eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver " 50274eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 50374eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob " 50474eacdb9SFrank Blaschka "available\n", dev_name(&card->gdev->dev)); 5054a71df50SFrank Blaschka return -ENOMEM; 5064a71df50SFrank Blaschka } 5074a71df50SFrank Blaschka qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE); 508847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 5094a71df50SFrank Blaschka rc = ccw_device_start(card->read.ccwdev, &card->read.ccw, 5104a71df50SFrank Blaschka (addr_t) iob, 0, 0); 5114a71df50SFrank Blaschka if (rc) { 51274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! " 51374eacdb9SFrank Blaschka "rc=%i\n", dev_name(&card->gdev->dev), rc); 5144a71df50SFrank Blaschka atomic_set(&card->read.irq_pending, 0); 515908abbb5SUrsula Braun card->read_or_write_problem = 1; 5164a71df50SFrank Blaschka qeth_schedule_recovery(card); 5174a71df50SFrank Blaschka wake_up(&card->wait_q); 5184a71df50SFrank Blaschka } 5194a71df50SFrank Blaschka return rc; 5204a71df50SFrank Blaschka } 5214a71df50SFrank Blaschka 5224a71df50SFrank Blaschka static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card) 5234a71df50SFrank Blaschka { 5244a71df50SFrank Blaschka struct qeth_reply *reply; 5254a71df50SFrank Blaschka 5264a71df50SFrank Blaschka reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC); 5274a71df50SFrank Blaschka if (reply) { 5284a71df50SFrank Blaschka atomic_set(&reply->refcnt, 1); 5294a71df50SFrank Blaschka atomic_set(&reply->received, 0); 5304a71df50SFrank Blaschka reply->card = card; 5316531084cSPeter Senna Tschudin } 5324a71df50SFrank Blaschka return reply; 5334a71df50SFrank Blaschka } 5344a71df50SFrank Blaschka 5354a71df50SFrank Blaschka static void qeth_get_reply(struct qeth_reply *reply) 5364a71df50SFrank Blaschka { 5374a71df50SFrank Blaschka WARN_ON(atomic_read(&reply->refcnt) <= 0); 5384a71df50SFrank Blaschka atomic_inc(&reply->refcnt); 5394a71df50SFrank Blaschka } 5404a71df50SFrank Blaschka 5414a71df50SFrank Blaschka static void qeth_put_reply(struct qeth_reply *reply) 5424a71df50SFrank Blaschka { 5434a71df50SFrank Blaschka WARN_ON(atomic_read(&reply->refcnt) <= 0); 5444a71df50SFrank Blaschka if (atomic_dec_and_test(&reply->refcnt)) 5454a71df50SFrank Blaschka kfree(reply); 5464a71df50SFrank Blaschka } 5474a71df50SFrank Blaschka 548d11ba0c4SPeter Tiedemann static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, 5494a71df50SFrank Blaschka struct qeth_card *card) 5504a71df50SFrank Blaschka { 5514a71df50SFrank Blaschka char *ipa_name; 552d11ba0c4SPeter Tiedemann int com = cmd->hdr.command; 5534a71df50SFrank Blaschka ipa_name = qeth_get_ipa_cmd_name(com); 554d11ba0c4SPeter Tiedemann if (rc) 55570919e23SUrsula Braun QETH_DBF_MESSAGE(2, "IPA: %s(x%X) for %s/%s returned " 55670919e23SUrsula Braun "x%X \"%s\"\n", 55770919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 55870919e23SUrsula Braun QETH_CARD_IFNAME(card), rc, 55970919e23SUrsula Braun qeth_get_ipa_msg(rc)); 560d11ba0c4SPeter Tiedemann else 56170919e23SUrsula Braun QETH_DBF_MESSAGE(5, "IPA: %s(x%X) for %s/%s succeeded\n", 56270919e23SUrsula Braun ipa_name, com, dev_name(&card->gdev->dev), 56370919e23SUrsula Braun QETH_CARD_IFNAME(card)); 5644a71df50SFrank Blaschka } 5654a71df50SFrank Blaschka 5664a71df50SFrank Blaschka static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, 5674a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 5684a71df50SFrank Blaschka { 5694a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd = NULL; 5704a71df50SFrank Blaschka 571847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "chkipad"); 5724a71df50SFrank Blaschka if (IS_IPA(iob->data)) { 5734a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data); 5744a71df50SFrank Blaschka if (IS_IPA_REPLY(cmd)) { 57576b11f8eSUrsula Braun if (cmd->hdr.command != IPA_CMD_SETCCID && 57676b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_DELCCID && 57776b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_MODCCID && 57876b11f8eSUrsula Braun cmd->hdr.command != IPA_CMD_SET_DIAG_ASS) 579d11ba0c4SPeter Tiedemann qeth_issue_ipa_msg(cmd, 580d11ba0c4SPeter Tiedemann cmd->hdr.return_code, card); 5814a71df50SFrank Blaschka return cmd; 5824a71df50SFrank Blaschka } else { 5834a71df50SFrank Blaschka switch (cmd->hdr.command) { 5844a71df50SFrank Blaschka case IPA_CMD_STOPLAN: 5850f54761dSStefan Raspl if (cmd->hdr.return_code == 5860f54761dSStefan Raspl IPA_RC_VEPA_TO_VEB_TRANSITION) { 5870f54761dSStefan Raspl dev_err(&card->gdev->dev, 5880f54761dSStefan Raspl "Interface %s is down because the " 5890f54761dSStefan Raspl "adjacent port is no longer in " 5900f54761dSStefan Raspl "reflective relay mode\n", 5910f54761dSStefan Raspl QETH_CARD_IFNAME(card)); 5920f54761dSStefan Raspl qeth_close_dev(card); 5930f54761dSStefan Raspl } else { 59474eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, 59574eacdb9SFrank Blaschka "The link for interface %s on CHPID" 59674eacdb9SFrank Blaschka " 0x%X failed\n", 5974a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 5984a71df50SFrank Blaschka card->info.chpid); 5990f54761dSStefan Raspl qeth_issue_ipa_msg(cmd, 6000f54761dSStefan Raspl cmd->hdr.return_code, card); 6010f54761dSStefan Raspl } 6024a71df50SFrank Blaschka card->lan_online = 0; 6034a71df50SFrank Blaschka if (card->dev && netif_carrier_ok(card->dev)) 6044a71df50SFrank Blaschka netif_carrier_off(card->dev); 6054a71df50SFrank Blaschka return NULL; 6064a71df50SFrank Blaschka case IPA_CMD_STARTLAN: 60774eacdb9SFrank Blaschka dev_info(&card->gdev->dev, 60874eacdb9SFrank Blaschka "The link for %s on CHPID 0x%X has" 60974eacdb9SFrank Blaschka " been restored\n", 6104a71df50SFrank Blaschka QETH_CARD_IFNAME(card), 6114a71df50SFrank Blaschka card->info.chpid); 6124a71df50SFrank Blaschka netif_carrier_on(card->dev); 613922dc062SUrsula Braun card->lan_online = 1; 6141da74b1cSFrank Blaschka if (card->info.hwtrap) 6151da74b1cSFrank Blaschka card->info.hwtrap = 2; 6164a71df50SFrank Blaschka qeth_schedule_recovery(card); 6174a71df50SFrank Blaschka return NULL; 6184a71df50SFrank Blaschka case IPA_CMD_MODCCID: 6194a71df50SFrank Blaschka return cmd; 6204a71df50SFrank Blaschka case IPA_CMD_REGISTER_LOCAL_ADDR: 621847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "irla"); 6224a71df50SFrank Blaschka break; 6234a71df50SFrank Blaschka case IPA_CMD_UNREGISTER_LOCAL_ADDR: 624847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "urla"); 6254a71df50SFrank Blaschka break; 6264a71df50SFrank Blaschka default: 627c4cef07cSFrank Blaschka QETH_DBF_MESSAGE(2, "Received data is IPA " 6284a71df50SFrank Blaschka "but not a reply!\n"); 6294a71df50SFrank Blaschka break; 6304a71df50SFrank Blaschka } 6314a71df50SFrank Blaschka } 6324a71df50SFrank Blaschka } 6334a71df50SFrank Blaschka return cmd; 6344a71df50SFrank Blaschka } 6354a71df50SFrank Blaschka 6364a71df50SFrank Blaschka void qeth_clear_ipacmd_list(struct qeth_card *card) 6374a71df50SFrank Blaschka { 6384a71df50SFrank Blaschka struct qeth_reply *reply, *r; 6394a71df50SFrank Blaschka unsigned long flags; 6404a71df50SFrank Blaschka 641847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "clipalst"); 6424a71df50SFrank Blaschka 6434a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 6444a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 6454a71df50SFrank Blaschka qeth_get_reply(reply); 6464a71df50SFrank Blaschka reply->rc = -EIO; 6474a71df50SFrank Blaschka atomic_inc(&reply->received); 6484a71df50SFrank Blaschka list_del_init(&reply->list); 6494a71df50SFrank Blaschka wake_up(&reply->wait_q); 6504a71df50SFrank Blaschka qeth_put_reply(reply); 6514a71df50SFrank Blaschka } 6524a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 653908abbb5SUrsula Braun atomic_set(&card->write.irq_pending, 0); 6544a71df50SFrank Blaschka } 6554a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list); 6564a71df50SFrank Blaschka 6575113fec0SUrsula Braun static int qeth_check_idx_response(struct qeth_card *card, 6585113fec0SUrsula Braun unsigned char *buffer) 6594a71df50SFrank Blaschka { 6604a71df50SFrank Blaschka if (!buffer) 6614a71df50SFrank Blaschka return 0; 6624a71df50SFrank Blaschka 663d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN); 6644a71df50SFrank Blaschka if ((buffer[2] & 0xc0) == 0xc0) { 66574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "received an IDX TERMINATE " 6664a71df50SFrank Blaschka "with cause code 0x%02x%s\n", 6674a71df50SFrank Blaschka buffer[4], 6684a71df50SFrank Blaschka ((buffer[4] == 0x22) ? 6694a71df50SFrank Blaschka " -- try another portname" : "")); 670847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckidxres"); 671847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " idxterm"); 672847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 6735113fec0SUrsula Braun if (buffer[4] == 0xf6) { 6745113fec0SUrsula Braun dev_err(&card->gdev->dev, 6755113fec0SUrsula Braun "The qeth device is not configured " 6765113fec0SUrsula Braun "for the OSI layer required by z/VM\n"); 6775113fec0SUrsula Braun return -EPERM; 6785113fec0SUrsula Braun } 6794a71df50SFrank Blaschka return -EIO; 6804a71df50SFrank Blaschka } 6814a71df50SFrank Blaschka return 0; 6824a71df50SFrank Blaschka } 6834a71df50SFrank Blaschka 6844a71df50SFrank Blaschka static void qeth_setup_ccw(struct qeth_channel *channel, unsigned char *iob, 6854a71df50SFrank Blaschka __u32 len) 6864a71df50SFrank Blaschka { 6874a71df50SFrank Blaschka struct qeth_card *card; 6884a71df50SFrank Blaschka 6894a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 690847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setupccw"); 6914a71df50SFrank Blaschka if (channel == &card->read) 6924a71df50SFrank Blaschka memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); 6934a71df50SFrank Blaschka else 6944a71df50SFrank Blaschka memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); 6954a71df50SFrank Blaschka channel->ccw.count = len; 6964a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob); 6974a71df50SFrank Blaschka } 6984a71df50SFrank Blaschka 6994a71df50SFrank Blaschka static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel) 7004a71df50SFrank Blaschka { 7014a71df50SFrank Blaschka __u8 index; 7024a71df50SFrank Blaschka 703847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "getbuff"); 7044a71df50SFrank Blaschka index = channel->io_buf_no; 7054a71df50SFrank Blaschka do { 7064a71df50SFrank Blaschka if (channel->iob[index].state == BUF_STATE_FREE) { 7074a71df50SFrank Blaschka channel->iob[index].state = BUF_STATE_LOCKED; 7084a71df50SFrank Blaschka channel->io_buf_no = (channel->io_buf_no + 1) % 7094a71df50SFrank Blaschka QETH_CMD_BUFFER_NO; 7104a71df50SFrank Blaschka memset(channel->iob[index].data, 0, QETH_BUFSIZE); 7114a71df50SFrank Blaschka return channel->iob + index; 7124a71df50SFrank Blaschka } 7134a71df50SFrank Blaschka index = (index + 1) % QETH_CMD_BUFFER_NO; 7144a71df50SFrank Blaschka } while (index != channel->io_buf_no); 7154a71df50SFrank Blaschka 7164a71df50SFrank Blaschka return NULL; 7174a71df50SFrank Blaschka } 7184a71df50SFrank Blaschka 7194a71df50SFrank Blaschka void qeth_release_buffer(struct qeth_channel *channel, 7204a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 7214a71df50SFrank Blaschka { 7224a71df50SFrank Blaschka unsigned long flags; 7234a71df50SFrank Blaschka 724847a50fdSCarsten Otte QETH_CARD_TEXT(CARD_FROM_CDEV(channel->ccwdev), 6, "relbuff"); 7254a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7264a71df50SFrank Blaschka memset(iob->data, 0, QETH_BUFSIZE); 7274a71df50SFrank Blaschka iob->state = BUF_STATE_FREE; 7284a71df50SFrank Blaschka iob->callback = qeth_send_control_data_cb; 7294a71df50SFrank Blaschka iob->rc = 0; 7304a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 731039055b9SUrsula Braun wake_up(&channel->wait_q); 7324a71df50SFrank Blaschka } 7334a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_release_buffer); 7344a71df50SFrank Blaschka 7354a71df50SFrank Blaschka static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *channel) 7364a71df50SFrank Blaschka { 7374a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer = NULL; 7384a71df50SFrank Blaschka unsigned long flags; 7394a71df50SFrank Blaschka 7404a71df50SFrank Blaschka spin_lock_irqsave(&channel->iob_lock, flags); 7414a71df50SFrank Blaschka buffer = __qeth_get_buffer(channel); 7424a71df50SFrank Blaschka spin_unlock_irqrestore(&channel->iob_lock, flags); 7434a71df50SFrank Blaschka return buffer; 7444a71df50SFrank Blaschka } 7454a71df50SFrank Blaschka 7464a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *channel) 7474a71df50SFrank Blaschka { 7484a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer; 7494a71df50SFrank Blaschka wait_event(channel->wait_q, 7504a71df50SFrank Blaschka ((buffer = qeth_get_buffer(channel)) != NULL)); 7514a71df50SFrank Blaschka return buffer; 7524a71df50SFrank Blaschka } 7534a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_wait_for_buffer); 7544a71df50SFrank Blaschka 7554a71df50SFrank Blaschka void qeth_clear_cmd_buffers(struct qeth_channel *channel) 7564a71df50SFrank Blaschka { 7574a71df50SFrank Blaschka int cnt; 7584a71df50SFrank Blaschka 7594a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 7604a71df50SFrank Blaschka qeth_release_buffer(channel, &channel->iob[cnt]); 7614a71df50SFrank Blaschka channel->buf_no = 0; 7624a71df50SFrank Blaschka channel->io_buf_no = 0; 7634a71df50SFrank Blaschka } 7644a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers); 7654a71df50SFrank Blaschka 7664a71df50SFrank Blaschka static void qeth_send_control_data_cb(struct qeth_channel *channel, 7674a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 7684a71df50SFrank Blaschka { 7694a71df50SFrank Blaschka struct qeth_card *card; 7704a71df50SFrank Blaschka struct qeth_reply *reply, *r; 7714a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 7724a71df50SFrank Blaschka unsigned long flags; 7734a71df50SFrank Blaschka int keep_reply; 7745113fec0SUrsula Braun int rc = 0; 7754a71df50SFrank Blaschka 7764a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 777847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sndctlcb"); 7785113fec0SUrsula Braun rc = qeth_check_idx_response(card, iob->data); 7795113fec0SUrsula Braun switch (rc) { 7805113fec0SUrsula Braun case 0: 7815113fec0SUrsula Braun break; 7825113fec0SUrsula Braun case -EIO: 7834a71df50SFrank Blaschka qeth_clear_ipacmd_list(card); 7844a71df50SFrank Blaschka qeth_schedule_recovery(card); 78501fc3e86SUrsula Braun /* fall through */ 7865113fec0SUrsula Braun default: 7874a71df50SFrank Blaschka goto out; 7884a71df50SFrank Blaschka } 7894a71df50SFrank Blaschka 7904a71df50SFrank Blaschka cmd = qeth_check_ipa_data(card, iob); 7914a71df50SFrank Blaschka if ((cmd == NULL) && (card->state != CARD_STATE_DOWN)) 7924a71df50SFrank Blaschka goto out; 7934a71df50SFrank Blaschka /*in case of OSN : check if cmd is set */ 7944a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN && 7954a71df50SFrank Blaschka cmd && 7964a71df50SFrank Blaschka cmd->hdr.command != IPA_CMD_STARTLAN && 7974a71df50SFrank Blaschka card->osn_info.assist_cb != NULL) { 7984a71df50SFrank Blaschka card->osn_info.assist_cb(card->dev, cmd); 7994a71df50SFrank Blaschka goto out; 8004a71df50SFrank Blaschka } 8014a71df50SFrank Blaschka 8024a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8034a71df50SFrank Blaschka list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) { 8044a71df50SFrank Blaschka if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) || 8054a71df50SFrank Blaschka ((cmd) && (reply->seqno == cmd->hdr.seqno))) { 8064a71df50SFrank Blaschka qeth_get_reply(reply); 8074a71df50SFrank Blaschka list_del_init(&reply->list); 8084a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8094a71df50SFrank Blaschka keep_reply = 0; 8104a71df50SFrank Blaschka if (reply->callback != NULL) { 8114a71df50SFrank Blaschka if (cmd) { 8124a71df50SFrank Blaschka reply->offset = (__u16)((char *)cmd - 8134a71df50SFrank Blaschka (char *)iob->data); 8144a71df50SFrank Blaschka keep_reply = reply->callback(card, 8154a71df50SFrank Blaschka reply, 8164a71df50SFrank Blaschka (unsigned long)cmd); 8174a71df50SFrank Blaschka } else 8184a71df50SFrank Blaschka keep_reply = reply->callback(card, 8194a71df50SFrank Blaschka reply, 8204a71df50SFrank Blaschka (unsigned long)iob); 8214a71df50SFrank Blaschka } 8224a71df50SFrank Blaschka if (cmd) 8234a71df50SFrank Blaschka reply->rc = (u16) cmd->hdr.return_code; 8244a71df50SFrank Blaschka else if (iob->rc) 8254a71df50SFrank Blaschka reply->rc = iob->rc; 8264a71df50SFrank Blaschka if (keep_reply) { 8274a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 8284a71df50SFrank Blaschka list_add_tail(&reply->list, 8294a71df50SFrank Blaschka &card->cmd_waiter_list); 8304a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8314a71df50SFrank Blaschka } else { 8324a71df50SFrank Blaschka atomic_inc(&reply->received); 8334a71df50SFrank Blaschka wake_up(&reply->wait_q); 8344a71df50SFrank Blaschka } 8354a71df50SFrank Blaschka qeth_put_reply(reply); 8364a71df50SFrank Blaschka goto out; 8374a71df50SFrank Blaschka } 8384a71df50SFrank Blaschka } 8394a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 8404a71df50SFrank Blaschka out: 8414a71df50SFrank Blaschka memcpy(&card->seqno.pdu_hdr_ack, 8424a71df50SFrank Blaschka QETH_PDU_HEADER_SEQ_NO(iob->data), 8434a71df50SFrank Blaschka QETH_SEQ_NO_LENGTH); 8444a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 8454a71df50SFrank Blaschka } 8464a71df50SFrank Blaschka 8474a71df50SFrank Blaschka static int qeth_setup_channel(struct qeth_channel *channel) 8484a71df50SFrank Blaschka { 8494a71df50SFrank Blaschka int cnt; 8504a71df50SFrank Blaschka 851d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupch"); 8524a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) { 853ae57b20aSJulia Lawall channel->iob[cnt].data = 854b3332930SFrank Blaschka kzalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); 8554a71df50SFrank Blaschka if (channel->iob[cnt].data == NULL) 8564a71df50SFrank Blaschka break; 8574a71df50SFrank Blaschka channel->iob[cnt].state = BUF_STATE_FREE; 8584a71df50SFrank Blaschka channel->iob[cnt].channel = channel; 8594a71df50SFrank Blaschka channel->iob[cnt].callback = qeth_send_control_data_cb; 8604a71df50SFrank Blaschka channel->iob[cnt].rc = 0; 8614a71df50SFrank Blaschka } 8624a71df50SFrank Blaschka if (cnt < QETH_CMD_BUFFER_NO) { 8634a71df50SFrank Blaschka while (cnt-- > 0) 8644a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 8654a71df50SFrank Blaschka return -ENOMEM; 8664a71df50SFrank Blaschka } 8674a71df50SFrank Blaschka channel->buf_no = 0; 8684a71df50SFrank Blaschka channel->io_buf_no = 0; 8694a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 8704a71df50SFrank Blaschka spin_lock_init(&channel->iob_lock); 8714a71df50SFrank Blaschka 8724a71df50SFrank Blaschka init_waitqueue_head(&channel->wait_q); 8734a71df50SFrank Blaschka return 0; 8744a71df50SFrank Blaschka } 8754a71df50SFrank Blaschka 8764a71df50SFrank Blaschka static int qeth_set_thread_start_bit(struct qeth_card *card, 8774a71df50SFrank Blaschka unsigned long thread) 8784a71df50SFrank Blaschka { 8794a71df50SFrank Blaschka unsigned long flags; 8804a71df50SFrank Blaschka 8814a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 8824a71df50SFrank Blaschka if (!(card->thread_allowed_mask & thread) || 8834a71df50SFrank Blaschka (card->thread_start_mask & thread)) { 8844a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 8854a71df50SFrank Blaschka return -EPERM; 8864a71df50SFrank Blaschka } 8874a71df50SFrank Blaschka card->thread_start_mask |= thread; 8884a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 8894a71df50SFrank Blaschka return 0; 8904a71df50SFrank Blaschka } 8914a71df50SFrank Blaschka 8924a71df50SFrank Blaschka void qeth_clear_thread_start_bit(struct qeth_card *card, unsigned long thread) 8934a71df50SFrank Blaschka { 8944a71df50SFrank Blaschka unsigned long flags; 8954a71df50SFrank Blaschka 8964a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 8974a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 8984a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 8994a71df50SFrank Blaschka wake_up(&card->wait_q); 9004a71df50SFrank Blaschka } 9014a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_start_bit); 9024a71df50SFrank Blaschka 9034a71df50SFrank Blaschka void qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread) 9044a71df50SFrank Blaschka { 9054a71df50SFrank Blaschka unsigned long flags; 9064a71df50SFrank Blaschka 9074a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9084a71df50SFrank Blaschka card->thread_running_mask &= ~thread; 9094a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9104a71df50SFrank Blaschka wake_up(&card->wait_q); 9114a71df50SFrank Blaschka } 9124a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_thread_running_bit); 9134a71df50SFrank Blaschka 9144a71df50SFrank Blaschka static int __qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9154a71df50SFrank Blaschka { 9164a71df50SFrank Blaschka unsigned long flags; 9174a71df50SFrank Blaschka int rc = 0; 9184a71df50SFrank Blaschka 9194a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 9204a71df50SFrank Blaschka if (card->thread_start_mask & thread) { 9214a71df50SFrank Blaschka if ((card->thread_allowed_mask & thread) && 9224a71df50SFrank Blaschka !(card->thread_running_mask & thread)) { 9234a71df50SFrank Blaschka rc = 1; 9244a71df50SFrank Blaschka card->thread_start_mask &= ~thread; 9254a71df50SFrank Blaschka card->thread_running_mask |= thread; 9264a71df50SFrank Blaschka } else 9274a71df50SFrank Blaschka rc = -EPERM; 9284a71df50SFrank Blaschka } 9294a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 9304a71df50SFrank Blaschka return rc; 9314a71df50SFrank Blaschka } 9324a71df50SFrank Blaschka 9334a71df50SFrank Blaschka int qeth_do_run_thread(struct qeth_card *card, unsigned long thread) 9344a71df50SFrank Blaschka { 9354a71df50SFrank Blaschka int rc = 0; 9364a71df50SFrank Blaschka 9374a71df50SFrank Blaschka wait_event(card->wait_q, 9384a71df50SFrank Blaschka (rc = __qeth_do_run_thread(card, thread)) >= 0); 9394a71df50SFrank Blaschka return rc; 9404a71df50SFrank Blaschka } 9414a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_run_thread); 9424a71df50SFrank Blaschka 9434a71df50SFrank Blaschka void qeth_schedule_recovery(struct qeth_card *card) 9444a71df50SFrank Blaschka { 945847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "startrec"); 9464a71df50SFrank Blaschka if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0) 9474a71df50SFrank Blaschka schedule_work(&card->kernel_thread_starter); 9484a71df50SFrank Blaschka } 9494a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_schedule_recovery); 9504a71df50SFrank Blaschka 9514a71df50SFrank Blaschka static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb) 9524a71df50SFrank Blaschka { 9534a71df50SFrank Blaschka int dstat, cstat; 9544a71df50SFrank Blaschka char *sense; 955847a50fdSCarsten Otte struct qeth_card *card; 9564a71df50SFrank Blaschka 9574a71df50SFrank Blaschka sense = (char *) irb->ecw; 95823d805b6SPeter Oberparleiter cstat = irb->scsw.cmd.cstat; 95923d805b6SPeter Oberparleiter dstat = irb->scsw.cmd.dstat; 960847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 9614a71df50SFrank Blaschka 9624a71df50SFrank Blaschka if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | 9634a71df50SFrank Blaschka SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | 9644a71df50SFrank Blaschka SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) { 965847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CGENCHK"); 96674eacdb9SFrank Blaschka dev_warn(&cdev->dev, "The qeth device driver " 96774eacdb9SFrank Blaschka "failed to recover an error on the device\n"); 9685113fec0SUrsula Braun QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x\n", 9692a0217d5SKay Sievers dev_name(&cdev->dev), dstat, cstat); 9704a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET, 9714a71df50SFrank Blaschka 16, 1, irb, 64, 1); 9724a71df50SFrank Blaschka return 1; 9734a71df50SFrank Blaschka } 9744a71df50SFrank Blaschka 9754a71df50SFrank Blaschka if (dstat & DEV_STAT_UNIT_CHECK) { 9764a71df50SFrank Blaschka if (sense[SENSE_RESETTING_EVENT_BYTE] & 9774a71df50SFrank Blaschka SENSE_RESETTING_EVENT_FLAG) { 978847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "REVIND"); 9794a71df50SFrank Blaschka return 1; 9804a71df50SFrank Blaschka } 9814a71df50SFrank Blaschka if (sense[SENSE_COMMAND_REJECT_BYTE] & 9824a71df50SFrank Blaschka SENSE_COMMAND_REJECT_FLAG) { 983847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "CMDREJi"); 98428a7e4c9SUrsula Braun return 1; 9854a71df50SFrank Blaschka } 9864a71df50SFrank Blaschka if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { 987847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "AFFE"); 9884a71df50SFrank Blaschka return 1; 9894a71df50SFrank Blaschka } 9904a71df50SFrank Blaschka if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { 991847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ZEROSEN"); 9924a71df50SFrank Blaschka return 0; 9934a71df50SFrank Blaschka } 994847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "DGENCHK"); 9954a71df50SFrank Blaschka return 1; 9964a71df50SFrank Blaschka } 9974a71df50SFrank Blaschka return 0; 9984a71df50SFrank Blaschka } 9994a71df50SFrank Blaschka 10004a71df50SFrank Blaschka static long __qeth_check_irb_error(struct ccw_device *cdev, 10014a71df50SFrank Blaschka unsigned long intparm, struct irb *irb) 10024a71df50SFrank Blaschka { 1003847a50fdSCarsten Otte struct qeth_card *card; 1004847a50fdSCarsten Otte 1005847a50fdSCarsten Otte card = CARD_FROM_CDEV(cdev); 1006847a50fdSCarsten Otte 10074a71df50SFrank Blaschka if (!IS_ERR(irb)) 10084a71df50SFrank Blaschka return 0; 10094a71df50SFrank Blaschka 10104a71df50SFrank Blaschka switch (PTR_ERR(irb)) { 10114a71df50SFrank Blaschka case -EIO: 101274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s i/o-error on device\n", 101374eacdb9SFrank Blaschka dev_name(&cdev->dev)); 1014847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1015847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); 10164a71df50SFrank Blaschka break; 10174a71df50SFrank Blaschka case -ETIMEDOUT: 101874eacdb9SFrank Blaschka dev_warn(&cdev->dev, "A hardware operation timed out" 101974eacdb9SFrank Blaschka " on the device\n"); 1020847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1021847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " rc%d", -ETIMEDOUT); 10224a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 10234a71df50SFrank Blaschka if (card && (card->data.ccwdev == cdev)) { 10244a71df50SFrank Blaschka card->data.state = CH_STATE_DOWN; 10254a71df50SFrank Blaschka wake_up(&card->wait_q); 10264a71df50SFrank Blaschka } 10274a71df50SFrank Blaschka } 10284a71df50SFrank Blaschka break; 10294a71df50SFrank Blaschka default: 103074eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n", 103174eacdb9SFrank Blaschka dev_name(&cdev->dev), PTR_ERR(irb)); 1032847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "ckirberr"); 1033847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, " rc???"); 10344a71df50SFrank Blaschka } 10354a71df50SFrank Blaschka return PTR_ERR(irb); 10364a71df50SFrank Blaschka } 10374a71df50SFrank Blaschka 10384a71df50SFrank Blaschka static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, 10394a71df50SFrank Blaschka struct irb *irb) 10404a71df50SFrank Blaschka { 10414a71df50SFrank Blaschka int rc; 10424a71df50SFrank Blaschka int cstat, dstat; 10434a71df50SFrank Blaschka struct qeth_cmd_buffer *buffer; 10444a71df50SFrank Blaschka struct qeth_channel *channel; 10454a71df50SFrank Blaschka struct qeth_card *card; 10464a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 10474a71df50SFrank Blaschka __u8 index; 10484a71df50SFrank Blaschka 10494a71df50SFrank Blaschka if (__qeth_check_irb_error(cdev, intparm, irb)) 10504a71df50SFrank Blaschka return; 105123d805b6SPeter Oberparleiter cstat = irb->scsw.cmd.cstat; 105223d805b6SPeter Oberparleiter dstat = irb->scsw.cmd.dstat; 10534a71df50SFrank Blaschka 10544a71df50SFrank Blaschka card = CARD_FROM_CDEV(cdev); 10554a71df50SFrank Blaschka if (!card) 10564a71df50SFrank Blaschka return; 10574a71df50SFrank Blaschka 1058847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "irq"); 1059847a50fdSCarsten Otte 10604a71df50SFrank Blaschka if (card->read.ccwdev == cdev) { 10614a71df50SFrank Blaschka channel = &card->read; 1062847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "read"); 10634a71df50SFrank Blaschka } else if (card->write.ccwdev == cdev) { 10644a71df50SFrank Blaschka channel = &card->write; 1065847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "write"); 10664a71df50SFrank Blaschka } else { 10674a71df50SFrank Blaschka channel = &card->data; 1068847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "data"); 10694a71df50SFrank Blaschka } 10704a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 10714a71df50SFrank Blaschka 107223d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) 10734a71df50SFrank Blaschka channel->state = CH_STATE_STOPPED; 10744a71df50SFrank Blaschka 107523d805b6SPeter Oberparleiter if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) 10764a71df50SFrank Blaschka channel->state = CH_STATE_HALTED; 10774a71df50SFrank Blaschka 10784a71df50SFrank Blaschka /*let's wake up immediately on data channel*/ 10794a71df50SFrank Blaschka if ((channel == &card->data) && (intparm != 0) && 10804a71df50SFrank Blaschka (intparm != QETH_RCD_PARM)) 10814a71df50SFrank Blaschka goto out; 10824a71df50SFrank Blaschka 10834a71df50SFrank Blaschka if (intparm == QETH_CLEAR_CHANNEL_PARM) { 1084847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "clrchpar"); 10854a71df50SFrank Blaschka /* we don't have to handle this further */ 10864a71df50SFrank Blaschka intparm = 0; 10874a71df50SFrank Blaschka } 10884a71df50SFrank Blaschka if (intparm == QETH_HALT_CHANNEL_PARM) { 1089847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hltchpar"); 10904a71df50SFrank Blaschka /* we don't have to handle this further */ 10914a71df50SFrank Blaschka intparm = 0; 10924a71df50SFrank Blaschka } 10934a71df50SFrank Blaschka if ((dstat & DEV_STAT_UNIT_EXCEP) || 10944a71df50SFrank Blaschka (dstat & DEV_STAT_UNIT_CHECK) || 10954a71df50SFrank Blaschka (cstat)) { 10964a71df50SFrank Blaschka if (irb->esw.esw0.erw.cons) { 109774eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, 109874eacdb9SFrank Blaschka "The qeth device driver failed to recover " 109974eacdb9SFrank Blaschka "an error on the device\n"); 110074eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s sense data available. cstat " 110174eacdb9SFrank Blaschka "0x%X dstat 0x%X\n", 110274eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev), cstat, dstat); 11034a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: irb ", 11044a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1); 11054a71df50SFrank Blaschka print_hex_dump(KERN_WARNING, "qeth: sense data ", 11064a71df50SFrank Blaschka DUMP_PREFIX_OFFSET, 16, 1, irb->ecw, 32, 1); 11074a71df50SFrank Blaschka } 11084a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11094a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 11104a71df50SFrank Blaschka goto out; 11114a71df50SFrank Blaschka } 11124a71df50SFrank Blaschka rc = qeth_get_problem(cdev, irb); 11134a71df50SFrank Blaschka if (rc) { 111428a7e4c9SUrsula Braun qeth_clear_ipacmd_list(card); 11154a71df50SFrank Blaschka qeth_schedule_recovery(card); 11164a71df50SFrank Blaschka goto out; 11174a71df50SFrank Blaschka } 11184a71df50SFrank Blaschka } 11194a71df50SFrank Blaschka 11204a71df50SFrank Blaschka if (intparm == QETH_RCD_PARM) { 11214a71df50SFrank Blaschka channel->state = CH_STATE_RCD_DONE; 11224a71df50SFrank Blaschka goto out; 11234a71df50SFrank Blaschka } 11244a71df50SFrank Blaschka if (intparm) { 11254a71df50SFrank Blaschka buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm); 11264a71df50SFrank Blaschka buffer->state = BUF_STATE_PROCESSED; 11274a71df50SFrank Blaschka } 11284a71df50SFrank Blaschka if (channel == &card->data) 11294a71df50SFrank Blaschka return; 11304a71df50SFrank Blaschka if (channel == &card->read && 11314a71df50SFrank Blaschka channel->state == CH_STATE_UP) 11324a71df50SFrank Blaschka qeth_issue_next_read(card); 11334a71df50SFrank Blaschka 11344a71df50SFrank Blaschka iob = channel->iob; 11354a71df50SFrank Blaschka index = channel->buf_no; 11364a71df50SFrank Blaschka while (iob[index].state == BUF_STATE_PROCESSED) { 11374a71df50SFrank Blaschka if (iob[index].callback != NULL) 11384a71df50SFrank Blaschka iob[index].callback(channel, iob + index); 11394a71df50SFrank Blaschka 11404a71df50SFrank Blaschka index = (index + 1) % QETH_CMD_BUFFER_NO; 11414a71df50SFrank Blaschka } 11424a71df50SFrank Blaschka channel->buf_no = index; 11434a71df50SFrank Blaschka out: 11444a71df50SFrank Blaschka wake_up(&card->wait_q); 11454a71df50SFrank Blaschka return; 11464a71df50SFrank Blaschka } 11474a71df50SFrank Blaschka 1148b3332930SFrank Blaschka static void qeth_notify_skbs(struct qeth_qdio_out_q *q, 1149b3332930SFrank Blaschka struct qeth_qdio_out_buffer *buf, 1150b3332930SFrank Blaschka enum iucv_tx_notify notification) 1151b3332930SFrank Blaschka { 1152b3332930SFrank Blaschka struct sk_buff *skb; 1153b3332930SFrank Blaschka 1154b3332930SFrank Blaschka if (skb_queue_empty(&buf->skb_list)) 1155b3332930SFrank Blaschka goto out; 1156b3332930SFrank Blaschka skb = skb_peek(&buf->skb_list); 1157b3332930SFrank Blaschka while (skb) { 1158b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); 1159b3332930SFrank Blaschka QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); 1160b3332930SFrank Blaschka if (skb->protocol == ETH_P_AF_IUCV) { 1161b3332930SFrank Blaschka if (skb->sk) { 1162b3332930SFrank Blaschka struct iucv_sock *iucv = iucv_sk(skb->sk); 1163b3332930SFrank Blaschka iucv->sk_txnotify(skb, notification); 1164b3332930SFrank Blaschka } 1165b3332930SFrank Blaschka } 1166b3332930SFrank Blaschka if (skb_queue_is_last(&buf->skb_list, skb)) 1167b3332930SFrank Blaschka skb = NULL; 1168b3332930SFrank Blaschka else 1169b3332930SFrank Blaschka skb = skb_queue_next(&buf->skb_list, skb); 1170b3332930SFrank Blaschka } 1171b3332930SFrank Blaschka out: 1172b3332930SFrank Blaschka return; 1173b3332930SFrank Blaschka } 1174b3332930SFrank Blaschka 1175b3332930SFrank Blaschka static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) 1176b3332930SFrank Blaschka { 1177b3332930SFrank Blaschka struct sk_buff *skb; 117872861ae7SEinar Lueck struct iucv_sock *iucv; 117972861ae7SEinar Lueck int notify_general_error = 0; 118072861ae7SEinar Lueck 118172861ae7SEinar Lueck if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING) 118272861ae7SEinar Lueck notify_general_error = 1; 118372861ae7SEinar Lueck 118472861ae7SEinar Lueck /* release may never happen from within CQ tasklet scope */ 118518af5c17SStefan Raspl WARN_ON_ONCE(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); 1186b3332930SFrank Blaschka 1187b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1188b3332930SFrank Blaschka while (skb) { 1189b3332930SFrank Blaschka QETH_CARD_TEXT(buf->q->card, 5, "skbr"); 1190b3332930SFrank Blaschka QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); 119172861ae7SEinar Lueck if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) { 119272861ae7SEinar Lueck if (skb->sk) { 119372861ae7SEinar Lueck iucv = iucv_sk(skb->sk); 119472861ae7SEinar Lueck iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); 119572861ae7SEinar Lueck } 119672861ae7SEinar Lueck } 1197b3332930SFrank Blaschka atomic_dec(&skb->users); 1198b3332930SFrank Blaschka dev_kfree_skb_any(skb); 1199b3332930SFrank Blaschka skb = skb_dequeue(&buf->skb_list); 1200b3332930SFrank Blaschka } 1201b3332930SFrank Blaschka } 1202b3332930SFrank Blaschka 1203b67d801fSUrsula Braun static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, 12040da9581dSEinar Lueck struct qeth_qdio_out_buffer *buf, 12050da9581dSEinar Lueck enum qeth_qdio_buffer_states newbufstate) 12064a71df50SFrank Blaschka { 12074a71df50SFrank Blaschka int i; 12084a71df50SFrank Blaschka 12094a71df50SFrank Blaschka /* is PCI flag set on buffer? */ 12103ec90878SJan Glauber if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) 12114a71df50SFrank Blaschka atomic_dec(&queue->set_pci_flags_count); 12124a71df50SFrank Blaschka 1213b3332930SFrank Blaschka if (newbufstate == QETH_QDIO_BUF_EMPTY) { 1214b3332930SFrank Blaschka qeth_release_skbs(buf); 12154a71df50SFrank Blaschka } 12164a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { 1217683d718aSFrank Blaschka if (buf->buffer->element[i].addr && buf->is_header[i]) 1218683d718aSFrank Blaschka kmem_cache_free(qeth_core_header_cache, 1219683d718aSFrank Blaschka buf->buffer->element[i].addr); 1220683d718aSFrank Blaschka buf->is_header[i] = 0; 12214a71df50SFrank Blaschka buf->buffer->element[i].length = 0; 12224a71df50SFrank Blaschka buf->buffer->element[i].addr = NULL; 12233ec90878SJan Glauber buf->buffer->element[i].eflags = 0; 12243ec90878SJan Glauber buf->buffer->element[i].sflags = 0; 12254a71df50SFrank Blaschka } 12263ec90878SJan Glauber buf->buffer->element[15].eflags = 0; 12273ec90878SJan Glauber buf->buffer->element[15].sflags = 0; 12284a71df50SFrank Blaschka buf->next_element_to_fill = 0; 12290da9581dSEinar Lueck atomic_set(&buf->state, newbufstate); 12300da9581dSEinar Lueck } 12310da9581dSEinar Lueck 12320da9581dSEinar Lueck static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) 12330da9581dSEinar Lueck { 12340da9581dSEinar Lueck int j; 12350da9581dSEinar Lueck 12360da9581dSEinar Lueck for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 12370da9581dSEinar Lueck if (!q->bufs[j]) 12380da9581dSEinar Lueck continue; 123972861ae7SEinar Lueck qeth_cleanup_handled_pending(q, j, 1); 12400da9581dSEinar Lueck qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY); 12410da9581dSEinar Lueck if (free) { 12420da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); 12430da9581dSEinar Lueck q->bufs[j] = NULL; 12440da9581dSEinar Lueck } 12450da9581dSEinar Lueck } 12464a71df50SFrank Blaschka } 12474a71df50SFrank Blaschka 12484a71df50SFrank Blaschka void qeth_clear_qdio_buffers(struct qeth_card *card) 12494a71df50SFrank Blaschka { 12500da9581dSEinar Lueck int i; 12514a71df50SFrank Blaschka 1252847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "clearqdbf"); 12534a71df50SFrank Blaschka /* clear outbound buffers to free skbs */ 12540da9581dSEinar Lueck for (i = 0; i < card->qdio.no_out_queues; ++i) { 12554a71df50SFrank Blaschka if (card->qdio.out_qs[i]) { 12560da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 0); 12570da9581dSEinar Lueck } 12584a71df50SFrank Blaschka } 12594a71df50SFrank Blaschka } 12604a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_clear_qdio_buffers); 12614a71df50SFrank Blaschka 12624a71df50SFrank Blaschka static void qeth_free_buffer_pool(struct qeth_card *card) 12634a71df50SFrank Blaschka { 12644a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry, *tmp; 12654a71df50SFrank Blaschka int i = 0; 12664a71df50SFrank Blaschka list_for_each_entry_safe(pool_entry, tmp, 12674a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list){ 12684a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) 12694a71df50SFrank Blaschka free_page((unsigned long)pool_entry->elements[i]); 12704a71df50SFrank Blaschka list_del(&pool_entry->init_list); 12714a71df50SFrank Blaschka kfree(pool_entry); 12724a71df50SFrank Blaschka } 12734a71df50SFrank Blaschka } 12744a71df50SFrank Blaschka 12754a71df50SFrank Blaschka static void qeth_free_qdio_buffers(struct qeth_card *card) 12764a71df50SFrank Blaschka { 1277b3332930SFrank Blaschka int i, j; 12784a71df50SFrank Blaschka 12794a71df50SFrank Blaschka if (atomic_xchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED) == 12804a71df50SFrank Blaschka QETH_QDIO_UNINITIALIZED) 12814a71df50SFrank Blaschka return; 12820da9581dSEinar Lueck 12830da9581dSEinar Lueck qeth_free_cq(card); 1284b3332930SFrank Blaschka cancel_delayed_work_sync(&card->buffer_reclaim_work); 1285b3332930SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) 128672861ae7SEinar Lueck dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); 12874a71df50SFrank Blaschka kfree(card->qdio.in_q); 12884a71df50SFrank Blaschka card->qdio.in_q = NULL; 12894a71df50SFrank Blaschka /* inbound buffer pool */ 12904a71df50SFrank Blaschka qeth_free_buffer_pool(card); 12914a71df50SFrank Blaschka /* free outbound qdio_qs */ 12924a71df50SFrank Blaschka if (card->qdio.out_qs) { 12934a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 12940da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 12954a71df50SFrank Blaschka kfree(card->qdio.out_qs[i]); 12964a71df50SFrank Blaschka } 12974a71df50SFrank Blaschka kfree(card->qdio.out_qs); 12984a71df50SFrank Blaschka card->qdio.out_qs = NULL; 12994a71df50SFrank Blaschka } 13004a71df50SFrank Blaschka } 13014a71df50SFrank Blaschka 13024a71df50SFrank Blaschka static void qeth_clean_channel(struct qeth_channel *channel) 13034a71df50SFrank Blaschka { 13044a71df50SFrank Blaschka int cnt; 13054a71df50SFrank Blaschka 1306d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freech"); 13074a71df50SFrank Blaschka for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) 13084a71df50SFrank Blaschka kfree(channel->iob[cnt].data); 13094a71df50SFrank Blaschka } 13104a71df50SFrank Blaschka 1311725b9c04SSebastian Ott static void qeth_set_single_write_queues(struct qeth_card *card) 1312725b9c04SSebastian Ott { 1313725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1314725b9c04SSebastian Ott (card->qdio.no_out_queues == 4)) 1315725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1316725b9c04SSebastian Ott 1317725b9c04SSebastian Ott card->qdio.no_out_queues = 1; 1318725b9c04SSebastian Ott if (card->qdio.default_out_queue != 0) 1319725b9c04SSebastian Ott dev_info(&card->gdev->dev, "Priority Queueing not supported\n"); 1320725b9c04SSebastian Ott 1321725b9c04SSebastian Ott card->qdio.default_out_queue = 0; 1322725b9c04SSebastian Ott } 1323725b9c04SSebastian Ott 1324725b9c04SSebastian Ott static void qeth_set_multiple_write_queues(struct qeth_card *card) 1325725b9c04SSebastian Ott { 1326725b9c04SSebastian Ott if ((atomic_read(&card->qdio.state) != QETH_QDIO_UNINITIALIZED) && 1327725b9c04SSebastian Ott (card->qdio.no_out_queues == 1)) { 1328725b9c04SSebastian Ott qeth_free_qdio_buffers(card); 1329725b9c04SSebastian Ott card->qdio.default_out_queue = 2; 1330725b9c04SSebastian Ott } 1331725b9c04SSebastian Ott card->qdio.no_out_queues = 4; 1332725b9c04SSebastian Ott } 1333725b9c04SSebastian Ott 1334725b9c04SSebastian Ott static void qeth_update_from_chp_desc(struct qeth_card *card) 13354a71df50SFrank Blaschka { 13364a71df50SFrank Blaschka struct ccw_device *ccwdev; 13374a71df50SFrank Blaschka struct channelPath_dsc { 13384a71df50SFrank Blaschka u8 flags; 13394a71df50SFrank Blaschka u8 lsn; 13404a71df50SFrank Blaschka u8 desc; 13414a71df50SFrank Blaschka u8 chpid; 13424a71df50SFrank Blaschka u8 swla; 13434a71df50SFrank Blaschka u8 zeroes; 13444a71df50SFrank Blaschka u8 chla; 13454a71df50SFrank Blaschka u8 chpp; 13464a71df50SFrank Blaschka } *chp_dsc; 13474a71df50SFrank Blaschka 13485113fec0SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "chp_desc"); 13494a71df50SFrank Blaschka 13504a71df50SFrank Blaschka ccwdev = card->data.ccwdev; 1351725b9c04SSebastian Ott chp_dsc = ccw_device_get_chp_desc(ccwdev, 0); 1352725b9c04SSebastian Ott if (!chp_dsc) 1353725b9c04SSebastian Ott goto out; 1354725b9c04SSebastian Ott 1355d0ff1f52SUrsula Braun card->info.func_level = 0x4100 + chp_dsc->desc; 1356725b9c04SSebastian Ott if (card->info.type == QETH_CARD_TYPE_IQD) 1357725b9c04SSebastian Ott goto out; 1358725b9c04SSebastian Ott 1359725b9c04SSebastian Ott /* CHPP field bit 6 == 1 -> single queue */ 1360725b9c04SSebastian Ott if ((chp_dsc->chpp & 0x02) == 0x02) 1361725b9c04SSebastian Ott qeth_set_single_write_queues(card); 1362725b9c04SSebastian Ott else 1363725b9c04SSebastian Ott qeth_set_multiple_write_queues(card); 1364725b9c04SSebastian Ott out: 1365d0ff1f52SUrsula Braun kfree(chp_dsc); 13665113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "nr:%x", card->qdio.no_out_queues); 13675113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lvl:%02x", card->info.func_level); 13684a71df50SFrank Blaschka } 13694a71df50SFrank Blaschka 13704a71df50SFrank Blaschka static void qeth_init_qdio_info(struct qeth_card *card) 13714a71df50SFrank Blaschka { 1372d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 4, "intqdinf"); 13734a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 13744a71df50SFrank Blaschka /* inbound */ 13754a71df50SFrank Blaschka card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 1376dcf4ae2dSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1377dcf4ae2dSFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT; 1378dcf4ae2dSFrank Blaschka else 13794a71df50SFrank Blaschka card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; 13804a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count; 13814a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list); 13824a71df50SFrank Blaschka INIT_LIST_HEAD(&card->qdio.init_pool.entry_list); 13834a71df50SFrank Blaschka } 13844a71df50SFrank Blaschka 13854a71df50SFrank Blaschka static void qeth_set_intial_options(struct qeth_card *card) 13864a71df50SFrank Blaschka { 13874a71df50SFrank Blaschka card->options.route4.type = NO_ROUTER; 13884a71df50SFrank Blaschka card->options.route6.type = NO_ROUTER; 13894a71df50SFrank Blaschka card->options.fake_broadcast = 0; 13904a71df50SFrank Blaschka card->options.add_hhlen = DEFAULT_ADD_HHLEN; 13914a71df50SFrank Blaschka card->options.performance_stats = 0; 13924a71df50SFrank Blaschka card->options.rx_sg_cb = QETH_RX_SG_CB; 1393d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 13940da9581dSEinar Lueck card->options.cq = QETH_CQ_DISABLED; 13954a71df50SFrank Blaschka } 13964a71df50SFrank Blaschka 13974a71df50SFrank Blaschka static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) 13984a71df50SFrank Blaschka { 13994a71df50SFrank Blaschka unsigned long flags; 14004a71df50SFrank Blaschka int rc = 0; 14014a71df50SFrank Blaschka 14024a71df50SFrank Blaschka spin_lock_irqsave(&card->thread_mask_lock, flags); 1403847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, " %02x%02x%02x", 14044a71df50SFrank Blaschka (u8) card->thread_start_mask, 14054a71df50SFrank Blaschka (u8) card->thread_allowed_mask, 14064a71df50SFrank Blaschka (u8) card->thread_running_mask); 14074a71df50SFrank Blaschka rc = (card->thread_start_mask & thread); 14084a71df50SFrank Blaschka spin_unlock_irqrestore(&card->thread_mask_lock, flags); 14094a71df50SFrank Blaschka return rc; 14104a71df50SFrank Blaschka } 14114a71df50SFrank Blaschka 14124a71df50SFrank Blaschka static void qeth_start_kernel_thread(struct work_struct *work) 14134a71df50SFrank Blaschka { 14143f36b890SFrank Blaschka struct task_struct *ts; 14154a71df50SFrank Blaschka struct qeth_card *card = container_of(work, struct qeth_card, 14164a71df50SFrank Blaschka kernel_thread_starter); 1417847a50fdSCarsten Otte QETH_CARD_TEXT(card , 2, "strthrd"); 14184a71df50SFrank Blaschka 14194a71df50SFrank Blaschka if (card->read.state != CH_STATE_UP && 14204a71df50SFrank Blaschka card->write.state != CH_STATE_UP) 14214a71df50SFrank Blaschka return; 14223f36b890SFrank Blaschka if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { 1423c041f2d4SSebastian Ott ts = kthread_run(card->discipline->recover, (void *)card, 14244a71df50SFrank Blaschka "qeth_recover"); 14253f36b890SFrank Blaschka if (IS_ERR(ts)) { 14263f36b890SFrank Blaschka qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); 14273f36b890SFrank Blaschka qeth_clear_thread_running_bit(card, 14283f36b890SFrank Blaschka QETH_RECOVER_THREAD); 14293f36b890SFrank Blaschka } 14303f36b890SFrank Blaschka } 14314a71df50SFrank Blaschka } 14324a71df50SFrank Blaschka 14334a71df50SFrank Blaschka static int qeth_setup_card(struct qeth_card *card) 14344a71df50SFrank Blaschka { 14354a71df50SFrank Blaschka 1436d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "setupcrd"); 1437d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 14384a71df50SFrank Blaschka 14394a71df50SFrank Blaschka card->read.state = CH_STATE_DOWN; 14404a71df50SFrank Blaschka card->write.state = CH_STATE_DOWN; 14414a71df50SFrank Blaschka card->data.state = CH_STATE_DOWN; 14424a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 14434a71df50SFrank Blaschka card->lan_online = 0; 1444908abbb5SUrsula Braun card->read_or_write_problem = 0; 14454a71df50SFrank Blaschka card->dev = NULL; 14464a71df50SFrank Blaschka spin_lock_init(&card->vlanlock); 14474a71df50SFrank Blaschka spin_lock_init(&card->mclock); 14484a71df50SFrank Blaschka spin_lock_init(&card->lock); 14494a71df50SFrank Blaschka spin_lock_init(&card->ip_lock); 14504a71df50SFrank Blaschka spin_lock_init(&card->thread_mask_lock); 1451c4949f07SFrank Blaschka mutex_init(&card->conf_mutex); 14529dc48cccSUrsula Braun mutex_init(&card->discipline_mutex); 14534a71df50SFrank Blaschka card->thread_start_mask = 0; 14544a71df50SFrank Blaschka card->thread_allowed_mask = 0; 14554a71df50SFrank Blaschka card->thread_running_mask = 0; 14564a71df50SFrank Blaschka INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); 14574a71df50SFrank Blaschka INIT_LIST_HEAD(&card->ip_list); 14584a71df50SFrank Blaschka INIT_LIST_HEAD(card->ip_tbd_list); 14594a71df50SFrank Blaschka INIT_LIST_HEAD(&card->cmd_waiter_list); 14604a71df50SFrank Blaschka init_waitqueue_head(&card->wait_q); 146125985edcSLucas De Marchi /* initial options */ 14624a71df50SFrank Blaschka qeth_set_intial_options(card); 14634a71df50SFrank Blaschka /* IP address takeover */ 14644a71df50SFrank Blaschka INIT_LIST_HEAD(&card->ipato.entries); 14654a71df50SFrank Blaschka card->ipato.enabled = 0; 14664a71df50SFrank Blaschka card->ipato.invert4 = 0; 14674a71df50SFrank Blaschka card->ipato.invert6 = 0; 14684a71df50SFrank Blaschka /* init QDIO stuff */ 14694a71df50SFrank Blaschka qeth_init_qdio_info(card); 1470b3332930SFrank Blaschka INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); 14710f54761dSStefan Raspl INIT_WORK(&card->close_dev_work, qeth_close_dev_handler); 14724a71df50SFrank Blaschka return 0; 14734a71df50SFrank Blaschka } 14744a71df50SFrank Blaschka 14756bcac508SMartin Schwidefsky static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) 14766bcac508SMartin Schwidefsky { 14776bcac508SMartin Schwidefsky struct qeth_card *card = container_of(slr, struct qeth_card, 14786bcac508SMartin Schwidefsky qeth_service_level); 14790d788c7dSKlaus-Dieter Wacker if (card->info.mcl_level[0]) 14800d788c7dSKlaus-Dieter Wacker seq_printf(m, "qeth: %s firmware level %s\n", 14810d788c7dSKlaus-Dieter Wacker CARD_BUS_ID(card), card->info.mcl_level); 14826bcac508SMartin Schwidefsky } 14836bcac508SMartin Schwidefsky 14844a71df50SFrank Blaschka static struct qeth_card *qeth_alloc_card(void) 14854a71df50SFrank Blaschka { 14864a71df50SFrank Blaschka struct qeth_card *card; 14874a71df50SFrank Blaschka 1488d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "alloccrd"); 14894a71df50SFrank Blaschka card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL); 14904a71df50SFrank Blaschka if (!card) 149176b11f8eSUrsula Braun goto out; 1492d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 1493b3332930SFrank Blaschka card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); 149476b11f8eSUrsula Braun if (!card->ip_tbd_list) { 149576b11f8eSUrsula Braun QETH_DBF_TEXT(SETUP, 0, "iptbdnom"); 149676b11f8eSUrsula Braun goto out_card; 14974a71df50SFrank Blaschka } 149876b11f8eSUrsula Braun if (qeth_setup_channel(&card->read)) 149976b11f8eSUrsula Braun goto out_ip; 150076b11f8eSUrsula Braun if (qeth_setup_channel(&card->write)) 150176b11f8eSUrsula Braun goto out_channel; 15024a71df50SFrank Blaschka card->options.layer2 = -1; 15036bcac508SMartin Schwidefsky card->qeth_service_level.seq_print = qeth_core_sl_print; 15046bcac508SMartin Schwidefsky register_service_level(&card->qeth_service_level); 15054a71df50SFrank Blaschka return card; 150676b11f8eSUrsula Braun 150776b11f8eSUrsula Braun out_channel: 150876b11f8eSUrsula Braun qeth_clean_channel(&card->read); 150976b11f8eSUrsula Braun out_ip: 151076b11f8eSUrsula Braun kfree(card->ip_tbd_list); 151176b11f8eSUrsula Braun out_card: 151276b11f8eSUrsula Braun kfree(card); 151376b11f8eSUrsula Braun out: 151476b11f8eSUrsula Braun return NULL; 15154a71df50SFrank Blaschka } 15164a71df50SFrank Blaschka 15174a71df50SFrank Blaschka static int qeth_determine_card_type(struct qeth_card *card) 15184a71df50SFrank Blaschka { 15194a71df50SFrank Blaschka int i = 0; 15204a71df50SFrank Blaschka 1521d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "detcdtyp"); 15224a71df50SFrank Blaschka 15234a71df50SFrank Blaschka card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT; 15244a71df50SFrank Blaschka card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; 15255113fec0SUrsula Braun while (known_devices[i][QETH_DEV_MODEL_IND]) { 15265113fec0SUrsula Braun if ((CARD_RDEV(card)->id.dev_type == 15275113fec0SUrsula Braun known_devices[i][QETH_DEV_TYPE_IND]) && 15285113fec0SUrsula Braun (CARD_RDEV(card)->id.dev_model == 15295113fec0SUrsula Braun known_devices[i][QETH_DEV_MODEL_IND])) { 15305113fec0SUrsula Braun card->info.type = known_devices[i][QETH_DEV_MODEL_IND]; 15315113fec0SUrsula Braun card->qdio.no_out_queues = 15325113fec0SUrsula Braun known_devices[i][QETH_QUEUE_NO_IND]; 15330da9581dSEinar Lueck card->qdio.no_in_queues = 1; 15345113fec0SUrsula Braun card->info.is_multicast_different = 15355113fec0SUrsula Braun known_devices[i][QETH_MULTICAST_IND]; 1536725b9c04SSebastian Ott qeth_update_from_chp_desc(card); 15374a71df50SFrank Blaschka return 0; 15384a71df50SFrank Blaschka } 15394a71df50SFrank Blaschka i++; 15404a71df50SFrank Blaschka } 15414a71df50SFrank Blaschka card->info.type = QETH_CARD_TYPE_UNKNOWN; 154274eacdb9SFrank Blaschka dev_err(&card->gdev->dev, "The adapter hardware is of an " 154374eacdb9SFrank Blaschka "unknown type\n"); 15444a71df50SFrank Blaschka return -ENOENT; 15454a71df50SFrank Blaschka } 15464a71df50SFrank Blaschka 15474a71df50SFrank Blaschka static int qeth_clear_channel(struct qeth_channel *channel) 15484a71df50SFrank Blaschka { 15494a71df50SFrank Blaschka unsigned long flags; 15504a71df50SFrank Blaschka struct qeth_card *card; 15514a71df50SFrank Blaschka int rc; 15524a71df50SFrank Blaschka 15534a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1554847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearch"); 15554a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15564a71df50SFrank Blaschka rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); 15574a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15584a71df50SFrank Blaschka 15594a71df50SFrank Blaschka if (rc) 15604a71df50SFrank Blaschka return rc; 15614a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15624a71df50SFrank Blaschka channel->state == CH_STATE_STOPPED, QETH_TIMEOUT); 15634a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15644a71df50SFrank Blaschka return rc; 15654a71df50SFrank Blaschka if (channel->state != CH_STATE_STOPPED) 15664a71df50SFrank Blaschka return -ETIME; 15674a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 15684a71df50SFrank Blaschka return 0; 15694a71df50SFrank Blaschka } 15704a71df50SFrank Blaschka 15714a71df50SFrank Blaschka static int qeth_halt_channel(struct qeth_channel *channel) 15724a71df50SFrank Blaschka { 15734a71df50SFrank Blaschka unsigned long flags; 15744a71df50SFrank Blaschka struct qeth_card *card; 15754a71df50SFrank Blaschka int rc; 15764a71df50SFrank Blaschka 15774a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 1578847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltch"); 15794a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 15804a71df50SFrank Blaschka rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); 15814a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 15824a71df50SFrank Blaschka 15834a71df50SFrank Blaschka if (rc) 15844a71df50SFrank Blaschka return rc; 15854a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 15864a71df50SFrank Blaschka channel->state == CH_STATE_HALTED, QETH_TIMEOUT); 15874a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 15884a71df50SFrank Blaschka return rc; 15894a71df50SFrank Blaschka if (channel->state != CH_STATE_HALTED) 15904a71df50SFrank Blaschka return -ETIME; 15914a71df50SFrank Blaschka return 0; 15924a71df50SFrank Blaschka } 15934a71df50SFrank Blaschka 15944a71df50SFrank Blaschka static int qeth_halt_channels(struct qeth_card *card) 15954a71df50SFrank Blaschka { 15964a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 15974a71df50SFrank Blaschka 1598847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "haltchs"); 15994a71df50SFrank Blaschka rc1 = qeth_halt_channel(&card->read); 16004a71df50SFrank Blaschka rc2 = qeth_halt_channel(&card->write); 16014a71df50SFrank Blaschka rc3 = qeth_halt_channel(&card->data); 16024a71df50SFrank Blaschka if (rc1) 16034a71df50SFrank Blaschka return rc1; 16044a71df50SFrank Blaschka if (rc2) 16054a71df50SFrank Blaschka return rc2; 16064a71df50SFrank Blaschka return rc3; 16074a71df50SFrank Blaschka } 16084a71df50SFrank Blaschka 16094a71df50SFrank Blaschka static int qeth_clear_channels(struct qeth_card *card) 16104a71df50SFrank Blaschka { 16114a71df50SFrank Blaschka int rc1 = 0, rc2 = 0, rc3 = 0; 16124a71df50SFrank Blaschka 1613847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clearchs"); 16144a71df50SFrank Blaschka rc1 = qeth_clear_channel(&card->read); 16154a71df50SFrank Blaschka rc2 = qeth_clear_channel(&card->write); 16164a71df50SFrank Blaschka rc3 = qeth_clear_channel(&card->data); 16174a71df50SFrank Blaschka if (rc1) 16184a71df50SFrank Blaschka return rc1; 16194a71df50SFrank Blaschka if (rc2) 16204a71df50SFrank Blaschka return rc2; 16214a71df50SFrank Blaschka return rc3; 16224a71df50SFrank Blaschka } 16234a71df50SFrank Blaschka 16244a71df50SFrank Blaschka static int qeth_clear_halt_card(struct qeth_card *card, int halt) 16254a71df50SFrank Blaschka { 16264a71df50SFrank Blaschka int rc = 0; 16274a71df50SFrank Blaschka 1628847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "clhacrd"); 16294a71df50SFrank Blaschka 16304a71df50SFrank Blaschka if (halt) 16314a71df50SFrank Blaschka rc = qeth_halt_channels(card); 16324a71df50SFrank Blaschka if (rc) 16334a71df50SFrank Blaschka return rc; 16344a71df50SFrank Blaschka return qeth_clear_channels(card); 16354a71df50SFrank Blaschka } 16364a71df50SFrank Blaschka 16374a71df50SFrank Blaschka int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) 16384a71df50SFrank Blaschka { 16394a71df50SFrank Blaschka int rc = 0; 16404a71df50SFrank Blaschka 1641847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "qdioclr"); 16424a71df50SFrank Blaschka switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED, 16434a71df50SFrank Blaschka QETH_QDIO_CLEANING)) { 16444a71df50SFrank Blaschka case QETH_QDIO_ESTABLISHED: 16454a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 1646cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16474a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_HALT); 16484a71df50SFrank Blaschka else 1649cc961d40SJan Glauber rc = qdio_shutdown(CARD_DDEV(card), 16504a71df50SFrank Blaschka QDIO_FLAG_CLEANUP_USING_CLEAR); 16514a71df50SFrank Blaschka if (rc) 1652847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "1err%d", rc); 1653cc961d40SJan Glauber qdio_free(CARD_DDEV(card)); 16544a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 16554a71df50SFrank Blaschka break; 16564a71df50SFrank Blaschka case QETH_QDIO_CLEANING: 16574a71df50SFrank Blaschka return rc; 16584a71df50SFrank Blaschka default: 16594a71df50SFrank Blaschka break; 16604a71df50SFrank Blaschka } 16614a71df50SFrank Blaschka rc = qeth_clear_halt_card(card, use_halt); 16624a71df50SFrank Blaschka if (rc) 1663847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 3, "2err%d", rc); 16644a71df50SFrank Blaschka card->state = CARD_STATE_DOWN; 16654a71df50SFrank Blaschka return rc; 16664a71df50SFrank Blaschka } 16674a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_clear_card); 16684a71df50SFrank Blaschka 16694a71df50SFrank Blaschka static int qeth_read_conf_data(struct qeth_card *card, void **buffer, 16704a71df50SFrank Blaschka int *length) 16714a71df50SFrank Blaschka { 16724a71df50SFrank Blaschka struct ciw *ciw; 16734a71df50SFrank Blaschka char *rcd_buf; 16744a71df50SFrank Blaschka int ret; 16754a71df50SFrank Blaschka struct qeth_channel *channel = &card->data; 16764a71df50SFrank Blaschka unsigned long flags; 16774a71df50SFrank Blaschka 16784a71df50SFrank Blaschka /* 16794a71df50SFrank Blaschka * scan for RCD command in extended SenseID data 16804a71df50SFrank Blaschka */ 16814a71df50SFrank Blaschka ciw = ccw_device_get_ciw(channel->ccwdev, CIW_TYPE_RCD); 16824a71df50SFrank Blaschka if (!ciw || ciw->cmd == 0) 16834a71df50SFrank Blaschka return -EOPNOTSUPP; 16844a71df50SFrank Blaschka rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); 16854a71df50SFrank Blaschka if (!rcd_buf) 16864a71df50SFrank Blaschka return -ENOMEM; 16874a71df50SFrank Blaschka 16884a71df50SFrank Blaschka channel->ccw.cmd_code = ciw->cmd; 16894a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(rcd_buf); 16904a71df50SFrank Blaschka channel->ccw.count = ciw->count; 16914a71df50SFrank Blaschka channel->ccw.flags = CCW_FLAG_SLI; 16924a71df50SFrank Blaschka channel->state = CH_STATE_RCD; 16934a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 16944a71df50SFrank Blaschka ret = ccw_device_start_timeout(channel->ccwdev, &channel->ccw, 16954a71df50SFrank Blaschka QETH_RCD_PARM, LPM_ANYPATH, 0, 16964a71df50SFrank Blaschka QETH_RCD_TIMEOUT); 16974a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 16984a71df50SFrank Blaschka if (!ret) 16994a71df50SFrank Blaschka wait_event(card->wait_q, 17004a71df50SFrank Blaschka (channel->state == CH_STATE_RCD_DONE || 17014a71df50SFrank Blaschka channel->state == CH_STATE_DOWN)); 17024a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) 17034a71df50SFrank Blaschka ret = -EIO; 17044a71df50SFrank Blaschka else 17054a71df50SFrank Blaschka channel->state = CH_STATE_DOWN; 17064a71df50SFrank Blaschka if (ret) { 17074a71df50SFrank Blaschka kfree(rcd_buf); 17084a71df50SFrank Blaschka *buffer = NULL; 17094a71df50SFrank Blaschka *length = 0; 17104a71df50SFrank Blaschka } else { 17114a71df50SFrank Blaschka *length = ciw->count; 17124a71df50SFrank Blaschka *buffer = rcd_buf; 17134a71df50SFrank Blaschka } 17144a71df50SFrank Blaschka return ret; 17154a71df50SFrank Blaschka } 17164a71df50SFrank Blaschka 1717a60389abSEinar Lueck static void qeth_configure_unitaddr(struct qeth_card *card, char *prcd) 17184a71df50SFrank Blaschka { 1719a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgunit"); 17204a71df50SFrank Blaschka card->info.chpid = prcd[30]; 17214a71df50SFrank Blaschka card->info.unit_addr2 = prcd[31]; 17224a71df50SFrank Blaschka card->info.cula = prcd[63]; 17234a71df50SFrank Blaschka card->info.guestlan = ((prcd[0x10] == _ascebc['V']) && 17244a71df50SFrank Blaschka (prcd[0x11] == _ascebc['M'])); 1725a60389abSEinar Lueck } 1726a60389abSEinar Lueck 1727a60389abSEinar Lueck static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd) 1728a60389abSEinar Lueck { 1729a60389abSEinar Lueck QETH_DBF_TEXT(SETUP, 2, "cfgblkt"); 1730a60389abSEinar Lueck 1731e6e056baSStefan Raspl if (prcd[74] == 0xF0 && prcd[75] == 0xF0 && 1732e6e056baSStefan Raspl (prcd[76] == 0xF5 || prcd[76] == 0xF6)) { 1733a60389abSEinar Lueck card->info.blkt.time_total = 250; 1734a60389abSEinar Lueck card->info.blkt.inter_packet = 5; 1735a60389abSEinar Lueck card->info.blkt.inter_packet_jumbo = 15; 1736a60389abSEinar Lueck } else { 1737a60389abSEinar Lueck card->info.blkt.time_total = 0; 1738a60389abSEinar Lueck card->info.blkt.inter_packet = 0; 1739a60389abSEinar Lueck card->info.blkt.inter_packet_jumbo = 0; 1740a60389abSEinar Lueck } 17414a71df50SFrank Blaschka } 17424a71df50SFrank Blaschka 17434a71df50SFrank Blaschka static void qeth_init_tokens(struct qeth_card *card) 17444a71df50SFrank Blaschka { 17454a71df50SFrank Blaschka card->token.issuer_rm_w = 0x00010103UL; 17464a71df50SFrank Blaschka card->token.cm_filter_w = 0x00010108UL; 17474a71df50SFrank Blaschka card->token.cm_connection_w = 0x0001010aUL; 17484a71df50SFrank Blaschka card->token.ulp_filter_w = 0x0001010bUL; 17494a71df50SFrank Blaschka card->token.ulp_connection_w = 0x0001010dUL; 17504a71df50SFrank Blaschka } 17514a71df50SFrank Blaschka 17524a71df50SFrank Blaschka static void qeth_init_func_level(struct qeth_card *card) 17534a71df50SFrank Blaschka { 17545113fec0SUrsula Braun switch (card->info.type) { 17555113fec0SUrsula Braun case QETH_CARD_TYPE_IQD: 17566298263aSKlaus-Dieter Wacker card->info.func_level = QETH_IDX_FUNC_LEVEL_IQD; 17575113fec0SUrsula Braun break; 17585113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 17590132951eSUrsula Braun case QETH_CARD_TYPE_OSN: 17605113fec0SUrsula Braun card->info.func_level = QETH_IDX_FUNC_LEVEL_OSD; 17615113fec0SUrsula Braun break; 17625113fec0SUrsula Braun default: 17635113fec0SUrsula Braun break; 17644a71df50SFrank Blaschka } 17654a71df50SFrank Blaschka } 17664a71df50SFrank Blaschka 17674a71df50SFrank Blaschka static int qeth_idx_activate_get_answer(struct qeth_channel *channel, 17684a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 17694a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 17704a71df50SFrank Blaschka { 17714a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 17724a71df50SFrank Blaschka unsigned long flags; 17734a71df50SFrank Blaschka int rc; 17744a71df50SFrank Blaschka struct qeth_card *card; 17754a71df50SFrank Blaschka 1776d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxanswr"); 17774a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 17784a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 17794a71df50SFrank Blaschka iob->callback = idx_reply_cb; 17804a71df50SFrank Blaschka memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); 17814a71df50SFrank Blaschka channel->ccw.count = QETH_BUFSIZE; 17824a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob->data); 17834a71df50SFrank Blaschka 17844a71df50SFrank Blaschka wait_event(card->wait_q, 17854a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1786d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 17874a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 17884a71df50SFrank Blaschka rc = ccw_device_start(channel->ccwdev, 17894a71df50SFrank Blaschka &channel->ccw, (addr_t) iob, 0, 0); 17904a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 17914a71df50SFrank Blaschka 17924a71df50SFrank Blaschka if (rc) { 179314cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc); 1794d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 17954a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 17964a71df50SFrank Blaschka wake_up(&card->wait_q); 17974a71df50SFrank Blaschka return rc; 17984a71df50SFrank Blaschka } 17994a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 18004a71df50SFrank Blaschka channel->state == CH_STATE_UP, QETH_TIMEOUT); 18014a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 18024a71df50SFrank Blaschka return rc; 18034a71df50SFrank Blaschka if (channel->state != CH_STATE_UP) { 18044a71df50SFrank Blaschka rc = -ETIME; 1805d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 18064a71df50SFrank Blaschka qeth_clear_cmd_buffers(channel); 18074a71df50SFrank Blaschka } else 18084a71df50SFrank Blaschka rc = 0; 18094a71df50SFrank Blaschka return rc; 18104a71df50SFrank Blaschka } 18114a71df50SFrank Blaschka 18124a71df50SFrank Blaschka static int qeth_idx_activate_channel(struct qeth_channel *channel, 18134a71df50SFrank Blaschka void (*idx_reply_cb)(struct qeth_channel *, 18144a71df50SFrank Blaschka struct qeth_cmd_buffer *)) 18154a71df50SFrank Blaschka { 18164a71df50SFrank Blaschka struct qeth_card *card; 18174a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 18184a71df50SFrank Blaschka unsigned long flags; 18194a71df50SFrank Blaschka __u16 temp; 18204a71df50SFrank Blaschka __u8 tmp; 18214a71df50SFrank Blaschka int rc; 1822f06f6f32SCornelia Huck struct ccw_dev_id temp_devid; 18234a71df50SFrank Blaschka 18244a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 18254a71df50SFrank Blaschka 1826d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "idxactch"); 18274a71df50SFrank Blaschka 18284a71df50SFrank Blaschka iob = qeth_get_buffer(channel); 18294a71df50SFrank Blaschka iob->callback = idx_reply_cb; 18304a71df50SFrank Blaschka memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); 18314a71df50SFrank Blaschka channel->ccw.count = IDX_ACTIVATE_SIZE; 18324a71df50SFrank Blaschka channel->ccw.cda = (__u32) __pa(iob->data); 18334a71df50SFrank Blaschka if (channel == &card->write) { 18344a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_WRITE, IDX_ACTIVATE_SIZE); 18354a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 18364a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 18374a71df50SFrank Blaschka card->seqno.trans_hdr++; 18384a71df50SFrank Blaschka } else { 18394a71df50SFrank Blaschka memcpy(iob->data, IDX_ACTIVATE_READ, IDX_ACTIVATE_SIZE); 18404a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 18414a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 18424a71df50SFrank Blaschka } 18434a71df50SFrank Blaschka tmp = ((__u8)card->info.portno) | 0x80; 18444a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1); 18454a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 18464a71df50SFrank Blaschka &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); 18474a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data), 18484a71df50SFrank Blaschka &card->info.func_level, sizeof(__u16)); 1849f06f6f32SCornelia Huck ccw_device_get_id(CARD_DDEV(card), &temp_devid); 1850f06f6f32SCornelia Huck memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 2); 18514a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 18524a71df50SFrank Blaschka memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2); 18534a71df50SFrank Blaschka 18544a71df50SFrank Blaschka wait_event(card->wait_q, 18554a71df50SFrank Blaschka atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0); 1856d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 6, "noirqpnd"); 18574a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); 18584a71df50SFrank Blaschka rc = ccw_device_start(channel->ccwdev, 18594a71df50SFrank Blaschka &channel->ccw, (addr_t) iob, 0, 0); 18604a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); 18614a71df50SFrank Blaschka 18624a71df50SFrank Blaschka if (rc) { 186314cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Error1 in activating channel. rc=%d\n", 186414cc21b6SFrank Blaschka rc); 1865d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 18664a71df50SFrank Blaschka atomic_set(&channel->irq_pending, 0); 18674a71df50SFrank Blaschka wake_up(&card->wait_q); 18684a71df50SFrank Blaschka return rc; 18694a71df50SFrank Blaschka } 18704a71df50SFrank Blaschka rc = wait_event_interruptible_timeout(card->wait_q, 18714a71df50SFrank Blaschka channel->state == CH_STATE_ACTIVATING, QETH_TIMEOUT); 18724a71df50SFrank Blaschka if (rc == -ERESTARTSYS) 18734a71df50SFrank Blaschka return rc; 18744a71df50SFrank Blaschka if (channel->state != CH_STATE_ACTIVATING) { 187574eacdb9SFrank Blaschka dev_warn(&channel->ccwdev->dev, "The qeth device driver" 187674eacdb9SFrank Blaschka " failed to recover an error on the device\n"); 187774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n", 187874eacdb9SFrank Blaschka dev_name(&channel->ccwdev->dev)); 1879d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME); 18804a71df50SFrank Blaschka qeth_clear_cmd_buffers(channel); 18814a71df50SFrank Blaschka return -ETIME; 18824a71df50SFrank Blaschka } 18834a71df50SFrank Blaschka return qeth_idx_activate_get_answer(channel, idx_reply_cb); 18844a71df50SFrank Blaschka } 18854a71df50SFrank Blaschka 18864a71df50SFrank Blaschka static int qeth_peer_func_level(int level) 18874a71df50SFrank Blaschka { 18884a71df50SFrank Blaschka if ((level & 0xff) == 8) 18894a71df50SFrank Blaschka return (level & 0xff) + 0x400; 18904a71df50SFrank Blaschka if (((level >> 8) & 3) == 1) 18914a71df50SFrank Blaschka return (level & 0xff) + 0x200; 18924a71df50SFrank Blaschka return level; 18934a71df50SFrank Blaschka } 18944a71df50SFrank Blaschka 18954a71df50SFrank Blaschka static void qeth_idx_write_cb(struct qeth_channel *channel, 18964a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 18974a71df50SFrank Blaschka { 18984a71df50SFrank Blaschka struct qeth_card *card; 18994a71df50SFrank Blaschka __u16 temp; 19004a71df50SFrank Blaschka 1901d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxwrcb"); 19024a71df50SFrank Blaschka 19034a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19044a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 19054a71df50SFrank Blaschka goto out; 19064a71df50SFrank Blaschka } 19074a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 19084a71df50SFrank Blaschka 19094a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 19105113fec0SUrsula Braun if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == QETH_IDX_ACT_ERR_EXCL) 191174eacdb9SFrank Blaschka dev_err(&card->write.ccwdev->dev, 191274eacdb9SFrank Blaschka "The adapter is used exclusively by another " 191374eacdb9SFrank Blaschka "host\n"); 19144a71df50SFrank Blaschka else 191574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:" 191674eacdb9SFrank Blaschka " negative reply\n", 191774eacdb9SFrank Blaschka dev_name(&card->write.ccwdev->dev)); 19184a71df50SFrank Blaschka goto out; 19194a71df50SFrank Blaschka } 19204a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 19214a71df50SFrank Blaschka if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { 192274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: " 192374eacdb9SFrank Blaschka "function level mismatch (sent: 0x%x, received: " 192474eacdb9SFrank Blaschka "0x%x)\n", dev_name(&card->write.ccwdev->dev), 192574eacdb9SFrank Blaschka card->info.func_level, temp); 19264a71df50SFrank Blaschka goto out; 19274a71df50SFrank Blaschka } 19284a71df50SFrank Blaschka channel->state = CH_STATE_UP; 19294a71df50SFrank Blaschka out: 19304a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 19314a71df50SFrank Blaschka } 19324a71df50SFrank Blaschka 19334a71df50SFrank Blaschka static void qeth_idx_read_cb(struct qeth_channel *channel, 19344a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 19354a71df50SFrank Blaschka { 19364a71df50SFrank Blaschka struct qeth_card *card; 19374a71df50SFrank Blaschka __u16 temp; 19384a71df50SFrank Blaschka 1939d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP , 2, "idxrdcb"); 19404a71df50SFrank Blaschka if (channel->state == CH_STATE_DOWN) { 19414a71df50SFrank Blaschka channel->state = CH_STATE_ACTIVATING; 19424a71df50SFrank Blaschka goto out; 19434a71df50SFrank Blaschka } 19444a71df50SFrank Blaschka 19454a71df50SFrank Blaschka card = CARD_FROM_CDEV(channel->ccwdev); 19465113fec0SUrsula Braun if (qeth_check_idx_response(card, iob->data)) 19474a71df50SFrank Blaschka goto out; 19484a71df50SFrank Blaschka 19494a71df50SFrank Blaschka if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { 19505113fec0SUrsula Braun switch (QETH_IDX_ACT_CAUSE_CODE(iob->data)) { 19515113fec0SUrsula Braun case QETH_IDX_ACT_ERR_EXCL: 195274eacdb9SFrank Blaschka dev_err(&card->write.ccwdev->dev, 195374eacdb9SFrank Blaschka "The adapter is used exclusively by another " 195474eacdb9SFrank Blaschka "host\n"); 19555113fec0SUrsula Braun break; 19565113fec0SUrsula Braun case QETH_IDX_ACT_ERR_AUTH: 195701fc3e86SUrsula Braun case QETH_IDX_ACT_ERR_AUTH_USER: 19585113fec0SUrsula Braun dev_err(&card->read.ccwdev->dev, 19595113fec0SUrsula Braun "Setting the device online failed because of " 196001fc3e86SUrsula Braun "insufficient authorization\n"); 19615113fec0SUrsula Braun break; 19625113fec0SUrsula Braun default: 196374eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:" 196474eacdb9SFrank Blaschka " negative reply\n", 196574eacdb9SFrank Blaschka dev_name(&card->read.ccwdev->dev)); 19665113fec0SUrsula Braun } 196701fc3e86SUrsula Braun QETH_CARD_TEXT_(card, 2, "idxread%c", 196801fc3e86SUrsula Braun QETH_IDX_ACT_CAUSE_CODE(iob->data)); 19694a71df50SFrank Blaschka goto out; 19704a71df50SFrank Blaschka } 19714a71df50SFrank Blaschka 19724a71df50SFrank Blaschka /** 19735113fec0SUrsula Braun * * temporary fix for microcode bug 19745113fec0SUrsula Braun * * to revert it,replace OR by AND 19755113fec0SUrsula Braun * */ 19764a71df50SFrank Blaschka if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) || 19775113fec0SUrsula Braun (card->info.type == QETH_CARD_TYPE_OSD)) 19784a71df50SFrank Blaschka card->info.portname_required = 1; 19794a71df50SFrank Blaschka 19804a71df50SFrank Blaschka memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); 19814a71df50SFrank Blaschka if (temp != qeth_peer_func_level(card->info.func_level)) { 198274eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " 19834a71df50SFrank Blaschka "level mismatch (sent: 0x%x, received: 0x%x)\n", 198474eacdb9SFrank Blaschka dev_name(&card->read.ccwdev->dev), 198574eacdb9SFrank Blaschka card->info.func_level, temp); 19864a71df50SFrank Blaschka goto out; 19874a71df50SFrank Blaschka } 19884a71df50SFrank Blaschka memcpy(&card->token.issuer_rm_r, 19894a71df50SFrank Blaschka QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), 19904a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 19914a71df50SFrank Blaschka memcpy(&card->info.mcl_level[0], 19924a71df50SFrank Blaschka QETH_IDX_REPLY_LEVEL(iob->data), QETH_MCL_LENGTH); 19934a71df50SFrank Blaschka channel->state = CH_STATE_UP; 19944a71df50SFrank Blaschka out: 19954a71df50SFrank Blaschka qeth_release_buffer(channel, iob); 19964a71df50SFrank Blaschka } 19974a71df50SFrank Blaschka 19984a71df50SFrank Blaschka void qeth_prepare_control_data(struct qeth_card *card, int len, 19994a71df50SFrank Blaschka struct qeth_cmd_buffer *iob) 20004a71df50SFrank Blaschka { 20014a71df50SFrank Blaschka qeth_setup_ccw(&card->write, iob->data, len); 20024a71df50SFrank Blaschka iob->callback = qeth_release_buffer; 20034a71df50SFrank Blaschka 20044a71df50SFrank Blaschka memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), 20054a71df50SFrank Blaschka &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); 20064a71df50SFrank Blaschka card->seqno.trans_hdr++; 20074a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data), 20084a71df50SFrank Blaschka &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH); 20094a71df50SFrank Blaschka card->seqno.pdu_hdr++; 20104a71df50SFrank Blaschka memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data), 20114a71df50SFrank Blaschka &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH); 2012d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN); 20134a71df50SFrank Blaschka } 20144a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_control_data); 20154a71df50SFrank Blaschka 20164a71df50SFrank Blaschka int qeth_send_control_data(struct qeth_card *card, int len, 20174a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, 20184a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply *, 20194a71df50SFrank Blaschka unsigned long), 20204a71df50SFrank Blaschka void *reply_param) 20214a71df50SFrank Blaschka { 20224a71df50SFrank Blaschka int rc; 20234a71df50SFrank Blaschka unsigned long flags; 20244a71df50SFrank Blaschka struct qeth_reply *reply = NULL; 20257834cd5aSHeiko Carstens unsigned long timeout, event_timeout; 20265b54e16fSFrank Blaschka struct qeth_ipa_cmd *cmd; 20274a71df50SFrank Blaschka 2028847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "sendctl"); 20294a71df50SFrank Blaschka 2030908abbb5SUrsula Braun if (card->read_or_write_problem) { 2031908abbb5SUrsula Braun qeth_release_buffer(iob->channel, iob); 2032908abbb5SUrsula Braun return -EIO; 2033908abbb5SUrsula Braun } 20344a71df50SFrank Blaschka reply = qeth_alloc_reply(card); 20354a71df50SFrank Blaschka if (!reply) { 20364a71df50SFrank Blaschka return -ENOMEM; 20374a71df50SFrank Blaschka } 20384a71df50SFrank Blaschka reply->callback = reply_cb; 20394a71df50SFrank Blaschka reply->param = reply_param; 20404a71df50SFrank Blaschka if (card->state == CARD_STATE_DOWN) 20414a71df50SFrank Blaschka reply->seqno = QETH_IDX_COMMAND_SEQNO; 20424a71df50SFrank Blaschka else 20434a71df50SFrank Blaschka reply->seqno = card->seqno.ipa++; 20444a71df50SFrank Blaschka init_waitqueue_head(&reply->wait_q); 20454a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 20464a71df50SFrank Blaschka list_add_tail(&reply->list, &card->cmd_waiter_list); 20474a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 2048d11ba0c4SPeter Tiedemann QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN); 20494a71df50SFrank Blaschka 20504a71df50SFrank Blaschka while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ; 20514a71df50SFrank Blaschka qeth_prepare_control_data(card, len, iob); 20524a71df50SFrank Blaschka 20534a71df50SFrank Blaschka if (IS_IPA(iob->data)) 20547834cd5aSHeiko Carstens event_timeout = QETH_IPA_TIMEOUT; 20554a71df50SFrank Blaschka else 20567834cd5aSHeiko Carstens event_timeout = QETH_TIMEOUT; 20577834cd5aSHeiko Carstens timeout = jiffies + event_timeout; 20584a71df50SFrank Blaschka 2059847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "noirqpnd"); 20604a71df50SFrank Blaschka spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags); 20614a71df50SFrank Blaschka rc = ccw_device_start(card->write.ccwdev, &card->write.ccw, 20624a71df50SFrank Blaschka (addr_t) iob, 0, 0); 20634a71df50SFrank Blaschka spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags); 20644a71df50SFrank Blaschka if (rc) { 206574eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: " 206674eacdb9SFrank Blaschka "ccw_device_start rc = %i\n", 206774eacdb9SFrank Blaschka dev_name(&card->write.ccwdev->dev), rc); 2068847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, " err%d", rc); 20694a71df50SFrank Blaschka spin_lock_irqsave(&card->lock, flags); 20704a71df50SFrank Blaschka list_del_init(&reply->list); 20714a71df50SFrank Blaschka qeth_put_reply(reply); 20724a71df50SFrank Blaschka spin_unlock_irqrestore(&card->lock, flags); 20734a71df50SFrank Blaschka qeth_release_buffer(iob->channel, iob); 20744a71df50SFrank Blaschka atomic_set(&card->write.irq_pending, 0); 20754a71df50SFrank Blaschka wake_up(&card->wait_q); 20764a71df50SFrank Blaschka return rc; 20774a71df50SFrank Blaschka } 20785b54e16fSFrank Blaschka 20795b54e16fSFrank Blaschka /* we have only one long running ipassist, since we can ensure 20805b54e16fSFrank Blaschka process context of this command we can sleep */ 20815b54e16fSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 20825b54e16fSFrank Blaschka if ((cmd->hdr.command == IPA_CMD_SETIP) && 20835b54e16fSFrank Blaschka (cmd->hdr.prot_version == QETH_PROT_IPV4)) { 20845b54e16fSFrank Blaschka if (!wait_event_timeout(reply->wait_q, 20857834cd5aSHeiko Carstens atomic_read(&reply->received), event_timeout)) 20865b54e16fSFrank Blaschka goto time_err; 20875b54e16fSFrank Blaschka } else { 20884a71df50SFrank Blaschka while (!atomic_read(&reply->received)) { 20895b54e16fSFrank Blaschka if (time_after(jiffies, timeout)) 20905b54e16fSFrank Blaschka goto time_err; 20915b54e16fSFrank Blaschka cpu_relax(); 20926531084cSPeter Senna Tschudin } 20935b54e16fSFrank Blaschka } 20945b54e16fSFrank Blaschka 209570919e23SUrsula Braun if (reply->rc == -EIO) 209670919e23SUrsula Braun goto error; 20975b54e16fSFrank Blaschka rc = reply->rc; 20985b54e16fSFrank Blaschka qeth_put_reply(reply); 20995b54e16fSFrank Blaschka return rc; 21005b54e16fSFrank Blaschka 21015b54e16fSFrank Blaschka time_err: 210270919e23SUrsula Braun reply->rc = -ETIME; 21034a71df50SFrank Blaschka spin_lock_irqsave(&reply->card->lock, flags); 21044a71df50SFrank Blaschka list_del_init(&reply->list); 21054a71df50SFrank Blaschka spin_unlock_irqrestore(&reply->card->lock, flags); 21064a71df50SFrank Blaschka atomic_inc(&reply->received); 210770919e23SUrsula Braun error: 2108908abbb5SUrsula Braun atomic_set(&card->write.irq_pending, 0); 2109908abbb5SUrsula Braun qeth_release_buffer(iob->channel, iob); 2110908abbb5SUrsula Braun card->write.buf_no = (card->write.buf_no + 1) % QETH_CMD_BUFFER_NO; 21114a71df50SFrank Blaschka rc = reply->rc; 21124a71df50SFrank Blaschka qeth_put_reply(reply); 21134a71df50SFrank Blaschka return rc; 21144a71df50SFrank Blaschka } 21154a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_control_data); 21164a71df50SFrank Blaschka 21174a71df50SFrank Blaschka static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 21184a71df50SFrank Blaschka unsigned long data) 21194a71df50SFrank Blaschka { 21204a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21214a71df50SFrank Blaschka 2122d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenblcb"); 21234a71df50SFrank Blaschka 21244a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 21254a71df50SFrank Blaschka memcpy(&card->token.cm_filter_r, 21264a71df50SFrank Blaschka QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data), 21274a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2128d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 21294a71df50SFrank Blaschka return 0; 21304a71df50SFrank Blaschka } 21314a71df50SFrank Blaschka 21324a71df50SFrank Blaschka static int qeth_cm_enable(struct qeth_card *card) 21334a71df50SFrank Blaschka { 21344a71df50SFrank Blaschka int rc; 21354a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21364a71df50SFrank Blaschka 2137d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmenable"); 21384a71df50SFrank Blaschka 21394a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 21404a71df50SFrank Blaschka memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE); 21414a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data), 21424a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 21434a71df50SFrank Blaschka memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data), 21444a71df50SFrank Blaschka &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH); 21454a71df50SFrank Blaschka 21464a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob, 21474a71df50SFrank Blaschka qeth_cm_enable_cb, NULL); 21484a71df50SFrank Blaschka return rc; 21494a71df50SFrank Blaschka } 21504a71df50SFrank Blaschka 21514a71df50SFrank Blaschka static int qeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 21524a71df50SFrank Blaschka unsigned long data) 21534a71df50SFrank Blaschka { 21544a71df50SFrank Blaschka 21554a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21564a71df50SFrank Blaschka 2157d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetpcb"); 21584a71df50SFrank Blaschka 21594a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 21604a71df50SFrank Blaschka memcpy(&card->token.cm_connection_r, 21614a71df50SFrank Blaschka QETH_CM_SETUP_RESP_DEST_ADDR(iob->data), 21624a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 2163d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 21644a71df50SFrank Blaschka return 0; 21654a71df50SFrank Blaschka } 21664a71df50SFrank Blaschka 21674a71df50SFrank Blaschka static int qeth_cm_setup(struct qeth_card *card) 21684a71df50SFrank Blaschka { 21694a71df50SFrank Blaschka int rc; 21704a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 21714a71df50SFrank Blaschka 2172d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "cmsetup"); 21734a71df50SFrank Blaschka 21744a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 21754a71df50SFrank Blaschka memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE); 21764a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data), 21774a71df50SFrank Blaschka &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH); 21784a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data), 21794a71df50SFrank Blaschka &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH); 21804a71df50SFrank Blaschka memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data), 21814a71df50SFrank Blaschka &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH); 21824a71df50SFrank Blaschka rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob, 21834a71df50SFrank Blaschka qeth_cm_setup_cb, NULL); 21844a71df50SFrank Blaschka return rc; 21854a71df50SFrank Blaschka 21864a71df50SFrank Blaschka } 21874a71df50SFrank Blaschka 21884a71df50SFrank Blaschka static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card) 21894a71df50SFrank Blaschka { 21904a71df50SFrank Blaschka switch (card->info.type) { 21914a71df50SFrank Blaschka case QETH_CARD_TYPE_UNKNOWN: 21924a71df50SFrank Blaschka return 1500; 21934a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 21944a71df50SFrank Blaschka return card->info.max_mtu; 21955113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 21964a71df50SFrank Blaschka switch (card->info.link_type) { 21974a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 21984a71df50SFrank Blaschka case QETH_LINK_TYPE_LANE_TR: 21994a71df50SFrank Blaschka return 2000; 22004a71df50SFrank Blaschka default: 2201fe44014aSStefan Raspl return card->options.layer2 ? 1500 : 1492; 22024a71df50SFrank Blaschka } 22035113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 22045113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 2205fe44014aSStefan Raspl return card->options.layer2 ? 1500 : 1492; 22064a71df50SFrank Blaschka default: 22074a71df50SFrank Blaschka return 1500; 22084a71df50SFrank Blaschka } 22094a71df50SFrank Blaschka } 22104a71df50SFrank Blaschka 22114a71df50SFrank Blaschka static inline int qeth_get_mtu_outof_framesize(int framesize) 22124a71df50SFrank Blaschka { 22134a71df50SFrank Blaschka switch (framesize) { 22144a71df50SFrank Blaschka case 0x4000: 22154a71df50SFrank Blaschka return 8192; 22164a71df50SFrank Blaschka case 0x6000: 22174a71df50SFrank Blaschka return 16384; 22184a71df50SFrank Blaschka case 0xa000: 22194a71df50SFrank Blaschka return 32768; 22204a71df50SFrank Blaschka case 0xffff: 22214a71df50SFrank Blaschka return 57344; 22224a71df50SFrank Blaschka default: 22234a71df50SFrank Blaschka return 0; 22244a71df50SFrank Blaschka } 22254a71df50SFrank Blaschka } 22264a71df50SFrank Blaschka 22274a71df50SFrank Blaschka static inline int qeth_mtu_is_valid(struct qeth_card *card, int mtu) 22284a71df50SFrank Blaschka { 22294a71df50SFrank Blaschka switch (card->info.type) { 22305113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 22315113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 22325113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 22334a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 22344a71df50SFrank Blaschka return ((mtu >= 576) && 22359853b97bSFrank Blaschka (mtu <= card->info.max_mtu)); 22364a71df50SFrank Blaschka case QETH_CARD_TYPE_OSN: 22374a71df50SFrank Blaschka case QETH_CARD_TYPE_UNKNOWN: 22384a71df50SFrank Blaschka default: 22394a71df50SFrank Blaschka return 1; 22404a71df50SFrank Blaschka } 22414a71df50SFrank Blaschka } 22424a71df50SFrank Blaschka 22434a71df50SFrank Blaschka static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, 22444a71df50SFrank Blaschka unsigned long data) 22454a71df50SFrank Blaschka { 22464a71df50SFrank Blaschka 22474a71df50SFrank Blaschka __u16 mtu, framesize; 22484a71df50SFrank Blaschka __u16 len; 22494a71df50SFrank Blaschka __u8 link_type; 22504a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 22514a71df50SFrank Blaschka 2252d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenacb"); 22534a71df50SFrank Blaschka 22544a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 22554a71df50SFrank Blaschka memcpy(&card->token.ulp_filter_r, 22564a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data), 22574a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 22589853b97bSFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) { 22594a71df50SFrank Blaschka memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); 22604a71df50SFrank Blaschka mtu = qeth_get_mtu_outof_framesize(framesize); 22614a71df50SFrank Blaschka if (!mtu) { 22624a71df50SFrank Blaschka iob->rc = -EINVAL; 2263d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 22644a71df50SFrank Blaschka return 0; 22654a71df50SFrank Blaschka } 22668b2e18f6SUrsula Braun if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { 22678b2e18f6SUrsula Braun /* frame size has changed */ 22688b2e18f6SUrsula Braun if (card->dev && 22698b2e18f6SUrsula Braun ((card->dev->mtu == card->info.initial_mtu) || 22708b2e18f6SUrsula Braun (card->dev->mtu > mtu))) 22718b2e18f6SUrsula Braun card->dev->mtu = mtu; 22728b2e18f6SUrsula Braun qeth_free_qdio_buffers(card); 22738b2e18f6SUrsula Braun } 22744a71df50SFrank Blaschka card->info.initial_mtu = mtu; 22758b2e18f6SUrsula Braun card->info.max_mtu = mtu; 22764a71df50SFrank Blaschka card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; 22774a71df50SFrank Blaschka } else { 22789853b97bSFrank Blaschka card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( 22799853b97bSFrank Blaschka iob->data); 2280fe44014aSStefan Raspl card->info.initial_mtu = min(card->info.max_mtu, 2281fe44014aSStefan Raspl qeth_get_initial_mtu_for_card(card)); 22824a71df50SFrank Blaschka card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 22834a71df50SFrank Blaschka } 22844a71df50SFrank Blaschka 22854a71df50SFrank Blaschka memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); 22864a71df50SFrank Blaschka if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { 22874a71df50SFrank Blaschka memcpy(&link_type, 22884a71df50SFrank Blaschka QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1); 22894a71df50SFrank Blaschka card->info.link_type = link_type; 22904a71df50SFrank Blaschka } else 22914a71df50SFrank Blaschka card->info.link_type = 0; 229201fc3e86SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "link%d", card->info.link_type); 2293d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 22944a71df50SFrank Blaschka return 0; 22954a71df50SFrank Blaschka } 22964a71df50SFrank Blaschka 22974a71df50SFrank Blaschka static int qeth_ulp_enable(struct qeth_card *card) 22984a71df50SFrank Blaschka { 22994a71df50SFrank Blaschka int rc; 23004a71df50SFrank Blaschka char prot_type; 23014a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23024a71df50SFrank Blaschka 23034a71df50SFrank Blaschka /*FIXME: trace view callbacks*/ 2304d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpenabl"); 23054a71df50SFrank Blaschka 23064a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 23074a71df50SFrank Blaschka memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); 23084a71df50SFrank Blaschka 23094a71df50SFrank Blaschka *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = 23104a71df50SFrank Blaschka (__u8) card->info.portno; 23114a71df50SFrank Blaschka if (card->options.layer2) 23124a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN) 23134a71df50SFrank Blaschka prot_type = QETH_PROT_OSN2; 23144a71df50SFrank Blaschka else 23154a71df50SFrank Blaschka prot_type = QETH_PROT_LAYER2; 23164a71df50SFrank Blaschka else 23174a71df50SFrank Blaschka prot_type = QETH_PROT_TCPIP; 23184a71df50SFrank Blaschka 23194a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data), &prot_type, 1); 23204a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data), 23214a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 23224a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), 23234a71df50SFrank Blaschka &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); 23244a71df50SFrank Blaschka memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data), 23254a71df50SFrank Blaschka card->info.portname, 9); 23264a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, 23274a71df50SFrank Blaschka qeth_ulp_enable_cb, NULL); 23284a71df50SFrank Blaschka return rc; 23294a71df50SFrank Blaschka 23304a71df50SFrank Blaschka } 23314a71df50SFrank Blaschka 23324a71df50SFrank Blaschka static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, 23334a71df50SFrank Blaschka unsigned long data) 23344a71df50SFrank Blaschka { 23354a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23364a71df50SFrank Blaschka 2337d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpstpcb"); 23384a71df50SFrank Blaschka 23394a71df50SFrank Blaschka iob = (struct qeth_cmd_buffer *) data; 23404a71df50SFrank Blaschka memcpy(&card->token.ulp_connection_r, 23414a71df50SFrank Blaschka QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 23424a71df50SFrank Blaschka QETH_MPC_TOKEN_LENGTH); 234365a1f898SUrsula Braun if (!strncmp("00S", QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data), 234465a1f898SUrsula Braun 3)) { 234565a1f898SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "olmlimit"); 234665a1f898SUrsula Braun dev_err(&card->gdev->dev, "A connection could not be " 234765a1f898SUrsula Braun "established because of an OLM limit\n"); 2348bbb822a8SUrsula Braun iob->rc = -EMLINK; 234965a1f898SUrsula Braun } 2350d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); 23517bf9bcffSStefan Raspl return 0; 23524a71df50SFrank Blaschka } 23534a71df50SFrank Blaschka 23544a71df50SFrank Blaschka static int qeth_ulp_setup(struct qeth_card *card) 23554a71df50SFrank Blaschka { 23564a71df50SFrank Blaschka int rc; 23574a71df50SFrank Blaschka __u16 temp; 23584a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 23594a71df50SFrank Blaschka struct ccw_dev_id dev_id; 23604a71df50SFrank Blaschka 2361d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "ulpsetup"); 23624a71df50SFrank Blaschka 23634a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 23644a71df50SFrank Blaschka memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE); 23654a71df50SFrank Blaschka 23664a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data), 23674a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 23684a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data), 23694a71df50SFrank Blaschka &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH); 23704a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data), 23714a71df50SFrank Blaschka &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH); 23724a71df50SFrank Blaschka 23734a71df50SFrank Blaschka ccw_device_get_id(CARD_DDEV(card), &dev_id); 23744a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2); 23754a71df50SFrank Blaschka temp = (card->info.cula << 8) + card->info.unit_addr2; 23764a71df50SFrank Blaschka memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2); 23774a71df50SFrank Blaschka rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob, 23784a71df50SFrank Blaschka qeth_ulp_setup_cb, NULL); 23794a71df50SFrank Blaschka return rc; 23804a71df50SFrank Blaschka } 23814a71df50SFrank Blaschka 23820da9581dSEinar Lueck static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx) 23830da9581dSEinar Lueck { 23840da9581dSEinar Lueck int rc; 23850da9581dSEinar Lueck struct qeth_qdio_out_buffer *newbuf; 23860da9581dSEinar Lueck 23870da9581dSEinar Lueck rc = 0; 23880da9581dSEinar Lueck newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC); 23890da9581dSEinar Lueck if (!newbuf) { 23900da9581dSEinar Lueck rc = -ENOMEM; 23910da9581dSEinar Lueck goto out; 23920da9581dSEinar Lueck } 23930da9581dSEinar Lueck newbuf->buffer = &q->qdio_bufs[bidx]; 23940da9581dSEinar Lueck skb_queue_head_init(&newbuf->skb_list); 23950da9581dSEinar Lueck lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); 23960da9581dSEinar Lueck newbuf->q = q; 23970da9581dSEinar Lueck newbuf->aob = NULL; 23980da9581dSEinar Lueck newbuf->next_pending = q->bufs[bidx]; 23990da9581dSEinar Lueck atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); 24000da9581dSEinar Lueck q->bufs[bidx] = newbuf; 24010da9581dSEinar Lueck if (q->bufstates) { 24020da9581dSEinar Lueck q->bufstates[bidx].user = newbuf; 24030da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "nbs%d", bidx); 24040da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "%lx", (long) newbuf); 24050da9581dSEinar Lueck QETH_CARD_TEXT_(q->card, 2, "%lx", 24060da9581dSEinar Lueck (long) newbuf->next_pending); 24070da9581dSEinar Lueck } 24080da9581dSEinar Lueck out: 24090da9581dSEinar Lueck return rc; 24100da9581dSEinar Lueck } 24110da9581dSEinar Lueck 24120da9581dSEinar Lueck 24134a71df50SFrank Blaschka static int qeth_alloc_qdio_buffers(struct qeth_card *card) 24144a71df50SFrank Blaschka { 24154a71df50SFrank Blaschka int i, j; 24164a71df50SFrank Blaschka 2417d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "allcqdbf"); 24184a71df50SFrank Blaschka 24194a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED, 24204a71df50SFrank Blaschka QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED) 24214a71df50SFrank Blaschka return 0; 24224a71df50SFrank Blaschka 2423b3332930SFrank Blaschka card->qdio.in_q = kzalloc(sizeof(struct qeth_qdio_q), 2424508b3c4fSUrsula Braun GFP_KERNEL); 24254a71df50SFrank Blaschka if (!card->qdio.in_q) 24264a71df50SFrank Blaschka goto out_nomem; 2427d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "inq"); 2428d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card->qdio.in_q, sizeof(void *)); 24294a71df50SFrank Blaschka memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); 24304a71df50SFrank Blaschka /* give inbound qeth_qdio_buffers their qdio_buffers */ 2431b3332930SFrank Blaschka for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 24324a71df50SFrank Blaschka card->qdio.in_q->bufs[i].buffer = 24334a71df50SFrank Blaschka &card->qdio.in_q->qdio_bufs[i]; 2434b3332930SFrank Blaschka card->qdio.in_q->bufs[i].rx_skb = NULL; 2435b3332930SFrank Blaschka } 24364a71df50SFrank Blaschka /* inbound buffer pool */ 24374a71df50SFrank Blaschka if (qeth_alloc_buffer_pool(card)) 24384a71df50SFrank Blaschka goto out_freeinq; 24390da9581dSEinar Lueck 24404a71df50SFrank Blaschka /* outbound */ 24414a71df50SFrank Blaschka card->qdio.out_qs = 2442b3332930SFrank Blaschka kzalloc(card->qdio.no_out_queues * 24434a71df50SFrank Blaschka sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); 24444a71df50SFrank Blaschka if (!card->qdio.out_qs) 24454a71df50SFrank Blaschka goto out_freepool; 24464a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 2447b3332930SFrank Blaschka card->qdio.out_qs[i] = kzalloc(sizeof(struct qeth_qdio_out_q), 2448508b3c4fSUrsula Braun GFP_KERNEL); 24494a71df50SFrank Blaschka if (!card->qdio.out_qs[i]) 24504a71df50SFrank Blaschka goto out_freeoutq; 2451d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "outq %i", i); 2452d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card->qdio.out_qs[i], sizeof(void *)); 24534a71df50SFrank Blaschka card->qdio.out_qs[i]->queue_no = i; 24544a71df50SFrank Blaschka /* give outbound qeth_qdio_buffers their qdio_buffers */ 24554a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 245618af5c17SStefan Raspl WARN_ON(card->qdio.out_qs[i]->bufs[j] != NULL); 24570da9581dSEinar Lueck if (qeth_init_qdio_out_buf(card->qdio.out_qs[i], j)) 24580da9581dSEinar Lueck goto out_freeoutqbufs; 24594a71df50SFrank Blaschka } 24604a71df50SFrank Blaschka } 24610da9581dSEinar Lueck 24620da9581dSEinar Lueck /* completion */ 24630da9581dSEinar Lueck if (qeth_alloc_cq(card)) 24640da9581dSEinar Lueck goto out_freeoutq; 24650da9581dSEinar Lueck 24664a71df50SFrank Blaschka return 0; 24674a71df50SFrank Blaschka 24680da9581dSEinar Lueck out_freeoutqbufs: 24690da9581dSEinar Lueck while (j > 0) { 24700da9581dSEinar Lueck --j; 24710da9581dSEinar Lueck kmem_cache_free(qeth_qdio_outbuf_cache, 24720da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]); 24730da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j] = NULL; 24740da9581dSEinar Lueck } 24754a71df50SFrank Blaschka out_freeoutq: 24760da9581dSEinar Lueck while (i > 0) { 24774a71df50SFrank Blaschka kfree(card->qdio.out_qs[--i]); 24780da9581dSEinar Lueck qeth_clear_outq_buffers(card->qdio.out_qs[i], 1); 24790da9581dSEinar Lueck } 24804a71df50SFrank Blaschka kfree(card->qdio.out_qs); 24814a71df50SFrank Blaschka card->qdio.out_qs = NULL; 24824a71df50SFrank Blaschka out_freepool: 24834a71df50SFrank Blaschka qeth_free_buffer_pool(card); 24844a71df50SFrank Blaschka out_freeinq: 24854a71df50SFrank Blaschka kfree(card->qdio.in_q); 24864a71df50SFrank Blaschka card->qdio.in_q = NULL; 24874a71df50SFrank Blaschka out_nomem: 24884a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 24894a71df50SFrank Blaschka return -ENOMEM; 24904a71df50SFrank Blaschka } 24914a71df50SFrank Blaschka 24924a71df50SFrank Blaschka static void qeth_create_qib_param_field(struct qeth_card *card, 24934a71df50SFrank Blaschka char *param_field) 24944a71df50SFrank Blaschka { 24954a71df50SFrank Blaschka 24964a71df50SFrank Blaschka param_field[0] = _ascebc['P']; 24974a71df50SFrank Blaschka param_field[1] = _ascebc['C']; 24984a71df50SFrank Blaschka param_field[2] = _ascebc['I']; 24994a71df50SFrank Blaschka param_field[3] = _ascebc['T']; 25004a71df50SFrank Blaschka *((unsigned int *) (¶m_field[4])) = QETH_PCI_THRESHOLD_A(card); 25014a71df50SFrank Blaschka *((unsigned int *) (¶m_field[8])) = QETH_PCI_THRESHOLD_B(card); 25024a71df50SFrank Blaschka *((unsigned int *) (¶m_field[12])) = QETH_PCI_TIMER_VALUE(card); 25034a71df50SFrank Blaschka } 25044a71df50SFrank Blaschka 25054a71df50SFrank Blaschka static void qeth_create_qib_param_field_blkt(struct qeth_card *card, 25064a71df50SFrank Blaschka char *param_field) 25074a71df50SFrank Blaschka { 25084a71df50SFrank Blaschka param_field[16] = _ascebc['B']; 25094a71df50SFrank Blaschka param_field[17] = _ascebc['L']; 25104a71df50SFrank Blaschka param_field[18] = _ascebc['K']; 25114a71df50SFrank Blaschka param_field[19] = _ascebc['T']; 25124a71df50SFrank Blaschka *((unsigned int *) (¶m_field[20])) = card->info.blkt.time_total; 25134a71df50SFrank Blaschka *((unsigned int *) (¶m_field[24])) = card->info.blkt.inter_packet; 25144a71df50SFrank Blaschka *((unsigned int *) (¶m_field[28])) = 25154a71df50SFrank Blaschka card->info.blkt.inter_packet_jumbo; 25164a71df50SFrank Blaschka } 25174a71df50SFrank Blaschka 25184a71df50SFrank Blaschka static int qeth_qdio_activate(struct qeth_card *card) 25194a71df50SFrank Blaschka { 2520d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 3, "qdioact"); 2521779e6e1cSJan Glauber return qdio_activate(CARD_DDEV(card)); 25224a71df50SFrank Blaschka } 25234a71df50SFrank Blaschka 25244a71df50SFrank Blaschka static int qeth_dm_act(struct qeth_card *card) 25254a71df50SFrank Blaschka { 25264a71df50SFrank Blaschka int rc; 25274a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 25284a71df50SFrank Blaschka 2529d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "dmact"); 25304a71df50SFrank Blaschka 25314a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 25324a71df50SFrank Blaschka memcpy(iob->data, DM_ACT, DM_ACT_SIZE); 25334a71df50SFrank Blaschka 25344a71df50SFrank Blaschka memcpy(QETH_DM_ACT_DEST_ADDR(iob->data), 25354a71df50SFrank Blaschka &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); 25364a71df50SFrank Blaschka memcpy(QETH_DM_ACT_CONNECTION_TOKEN(iob->data), 25374a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 25384a71df50SFrank Blaschka rc = qeth_send_control_data(card, DM_ACT_SIZE, iob, NULL, NULL); 25394a71df50SFrank Blaschka return rc; 25404a71df50SFrank Blaschka } 25414a71df50SFrank Blaschka 25424a71df50SFrank Blaschka static int qeth_mpc_initialize(struct qeth_card *card) 25434a71df50SFrank Blaschka { 25444a71df50SFrank Blaschka int rc; 25454a71df50SFrank Blaschka 2546d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "mpcinit"); 25474a71df50SFrank Blaschka 25484a71df50SFrank Blaschka rc = qeth_issue_next_read(card); 25494a71df50SFrank Blaschka if (rc) { 2550d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 25514a71df50SFrank Blaschka return rc; 25524a71df50SFrank Blaschka } 25534a71df50SFrank Blaschka rc = qeth_cm_enable(card); 25544a71df50SFrank Blaschka if (rc) { 2555d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 25564a71df50SFrank Blaschka goto out_qdio; 25574a71df50SFrank Blaschka } 25584a71df50SFrank Blaschka rc = qeth_cm_setup(card); 25594a71df50SFrank Blaschka if (rc) { 2560d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 25614a71df50SFrank Blaschka goto out_qdio; 25624a71df50SFrank Blaschka } 25634a71df50SFrank Blaschka rc = qeth_ulp_enable(card); 25644a71df50SFrank Blaschka if (rc) { 2565d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 25664a71df50SFrank Blaschka goto out_qdio; 25674a71df50SFrank Blaschka } 25684a71df50SFrank Blaschka rc = qeth_ulp_setup(card); 25694a71df50SFrank Blaschka if (rc) { 2570d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 25714a71df50SFrank Blaschka goto out_qdio; 25724a71df50SFrank Blaschka } 25734a71df50SFrank Blaschka rc = qeth_alloc_qdio_buffers(card); 25744a71df50SFrank Blaschka if (rc) { 2575d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 25764a71df50SFrank Blaschka goto out_qdio; 25774a71df50SFrank Blaschka } 25784a71df50SFrank Blaschka rc = qeth_qdio_establish(card); 25794a71df50SFrank Blaschka if (rc) { 2580d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 25814a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 25824a71df50SFrank Blaschka goto out_qdio; 25834a71df50SFrank Blaschka } 25844a71df50SFrank Blaschka rc = qeth_qdio_activate(card); 25854a71df50SFrank Blaschka if (rc) { 2586d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); 25874a71df50SFrank Blaschka goto out_qdio; 25884a71df50SFrank Blaschka } 25894a71df50SFrank Blaschka rc = qeth_dm_act(card); 25904a71df50SFrank Blaschka if (rc) { 2591d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); 25924a71df50SFrank Blaschka goto out_qdio; 25934a71df50SFrank Blaschka } 25944a71df50SFrank Blaschka 25954a71df50SFrank Blaschka return 0; 25964a71df50SFrank Blaschka out_qdio: 25974a71df50SFrank Blaschka qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 25984a71df50SFrank Blaschka return rc; 25994a71df50SFrank Blaschka } 26004a71df50SFrank Blaschka 26014a71df50SFrank Blaschka static void qeth_print_status_with_portname(struct qeth_card *card) 26024a71df50SFrank Blaschka { 26034a71df50SFrank Blaschka char dbf_text[15]; 26044a71df50SFrank Blaschka int i; 26054a71df50SFrank Blaschka 26064a71df50SFrank Blaschka sprintf(dbf_text, "%s", card->info.portname + 1); 26074a71df50SFrank Blaschka for (i = 0; i < 8; i++) 26084a71df50SFrank Blaschka dbf_text[i] = 26094a71df50SFrank Blaschka (char) _ebcasc[(__u8) dbf_text[i]]; 26104a71df50SFrank Blaschka dbf_text[8] = 0; 261174eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n" 26124a71df50SFrank Blaschka "with link type %s (portname: %s)\n", 26134a71df50SFrank Blaschka qeth_get_cardname(card), 26144a71df50SFrank Blaschka (card->info.mcl_level[0]) ? " (level: " : "", 26154a71df50SFrank Blaschka (card->info.mcl_level[0]) ? card->info.mcl_level : "", 26164a71df50SFrank Blaschka (card->info.mcl_level[0]) ? ")" : "", 26174a71df50SFrank Blaschka qeth_get_cardname_short(card), 26184a71df50SFrank Blaschka dbf_text); 26194a71df50SFrank Blaschka 26204a71df50SFrank Blaschka } 26214a71df50SFrank Blaschka 26224a71df50SFrank Blaschka static void qeth_print_status_no_portname(struct qeth_card *card) 26234a71df50SFrank Blaschka { 26244a71df50SFrank Blaschka if (card->info.portname[0]) 262574eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "Device is a%s " 26264a71df50SFrank Blaschka "card%s%s%s\nwith link type %s " 26274a71df50SFrank Blaschka "(no portname needed by interface).\n", 26284a71df50SFrank Blaschka qeth_get_cardname(card), 26294a71df50SFrank Blaschka (card->info.mcl_level[0]) ? " (level: " : "", 26304a71df50SFrank Blaschka (card->info.mcl_level[0]) ? card->info.mcl_level : "", 26314a71df50SFrank Blaschka (card->info.mcl_level[0]) ? ")" : "", 26324a71df50SFrank Blaschka qeth_get_cardname_short(card)); 26334a71df50SFrank Blaschka else 263474eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "Device is a%s " 26354a71df50SFrank Blaschka "card%s%s%s\nwith link type %s.\n", 26364a71df50SFrank Blaschka qeth_get_cardname(card), 26374a71df50SFrank Blaschka (card->info.mcl_level[0]) ? " (level: " : "", 26384a71df50SFrank Blaschka (card->info.mcl_level[0]) ? card->info.mcl_level : "", 26394a71df50SFrank Blaschka (card->info.mcl_level[0]) ? ")" : "", 26404a71df50SFrank Blaschka qeth_get_cardname_short(card)); 26414a71df50SFrank Blaschka } 26424a71df50SFrank Blaschka 26434a71df50SFrank Blaschka void qeth_print_status_message(struct qeth_card *card) 26444a71df50SFrank Blaschka { 26454a71df50SFrank Blaschka switch (card->info.type) { 26465113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 26475113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 26485113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 26494a71df50SFrank Blaschka /* VM will use a non-zero first character 26504a71df50SFrank Blaschka * to indicate a HiperSockets like reporting 26514a71df50SFrank Blaschka * of the level OSA sets the first character to zero 26524a71df50SFrank Blaschka * */ 26534a71df50SFrank Blaschka if (!card->info.mcl_level[0]) { 26544a71df50SFrank Blaschka sprintf(card->info.mcl_level, "%02x%02x", 26554a71df50SFrank Blaschka card->info.mcl_level[2], 26564a71df50SFrank Blaschka card->info.mcl_level[3]); 26574a71df50SFrank Blaschka 26584a71df50SFrank Blaschka card->info.mcl_level[QETH_MCL_LENGTH] = 0; 26594a71df50SFrank Blaschka break; 26604a71df50SFrank Blaschka } 26614a71df50SFrank Blaschka /* fallthrough */ 26624a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 2663906f1f07SKlaus-Dieter Wacker if ((card->info.guestlan) || 2664906f1f07SKlaus-Dieter Wacker (card->info.mcl_level[0] & 0x80)) { 26654a71df50SFrank Blaschka card->info.mcl_level[0] = (char) _ebcasc[(__u8) 26664a71df50SFrank Blaschka card->info.mcl_level[0]]; 26674a71df50SFrank Blaschka card->info.mcl_level[1] = (char) _ebcasc[(__u8) 26684a71df50SFrank Blaschka card->info.mcl_level[1]]; 26694a71df50SFrank Blaschka card->info.mcl_level[2] = (char) _ebcasc[(__u8) 26704a71df50SFrank Blaschka card->info.mcl_level[2]]; 26714a71df50SFrank Blaschka card->info.mcl_level[3] = (char) _ebcasc[(__u8) 26724a71df50SFrank Blaschka card->info.mcl_level[3]]; 26734a71df50SFrank Blaschka card->info.mcl_level[QETH_MCL_LENGTH] = 0; 26744a71df50SFrank Blaschka } 26754a71df50SFrank Blaschka break; 26764a71df50SFrank Blaschka default: 26774a71df50SFrank Blaschka memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); 26784a71df50SFrank Blaschka } 26794a71df50SFrank Blaschka if (card->info.portname_required) 26804a71df50SFrank Blaschka qeth_print_status_with_portname(card); 26814a71df50SFrank Blaschka else 26824a71df50SFrank Blaschka qeth_print_status_no_portname(card); 26834a71df50SFrank Blaschka } 26844a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_print_status_message); 26854a71df50SFrank Blaschka 26864a71df50SFrank Blaschka static void qeth_initialize_working_pool_list(struct qeth_card *card) 26874a71df50SFrank Blaschka { 26884a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 26894a71df50SFrank Blaschka 2690847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "inwrklst"); 26914a71df50SFrank Blaschka 26924a71df50SFrank Blaschka list_for_each_entry(entry, 26934a71df50SFrank Blaschka &card->qdio.init_pool.entry_list, init_list) { 26944a71df50SFrank Blaschka qeth_put_buffer_pool_entry(card, entry); 26954a71df50SFrank Blaschka } 26964a71df50SFrank Blaschka } 26974a71df50SFrank Blaschka 26984a71df50SFrank Blaschka static inline struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry( 26994a71df50SFrank Blaschka struct qeth_card *card) 27004a71df50SFrank Blaschka { 27014a71df50SFrank Blaschka struct list_head *plh; 27024a71df50SFrank Blaschka struct qeth_buffer_pool_entry *entry; 27034a71df50SFrank Blaschka int i, free; 27044a71df50SFrank Blaschka struct page *page; 27054a71df50SFrank Blaschka 27064a71df50SFrank Blaschka if (list_empty(&card->qdio.in_buf_pool.entry_list)) 27074a71df50SFrank Blaschka return NULL; 27084a71df50SFrank Blaschka 27094a71df50SFrank Blaschka list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { 27104a71df50SFrank Blaschka entry = list_entry(plh, struct qeth_buffer_pool_entry, list); 27114a71df50SFrank Blaschka free = 1; 27124a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27134a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 27144a71df50SFrank Blaschka free = 0; 27154a71df50SFrank Blaschka break; 27164a71df50SFrank Blaschka } 27174a71df50SFrank Blaschka } 27184a71df50SFrank Blaschka if (free) { 27194a71df50SFrank Blaschka list_del_init(&entry->list); 27204a71df50SFrank Blaschka return entry; 27214a71df50SFrank Blaschka } 27224a71df50SFrank Blaschka } 27234a71df50SFrank Blaschka 27244a71df50SFrank Blaschka /* no free buffer in pool so take first one and swap pages */ 27254a71df50SFrank Blaschka entry = list_entry(card->qdio.in_buf_pool.entry_list.next, 27264a71df50SFrank Blaschka struct qeth_buffer_pool_entry, list); 27274a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27284a71df50SFrank Blaschka if (page_count(virt_to_page(entry->elements[i])) > 1) { 2729508b3c4fSUrsula Braun page = alloc_page(GFP_ATOMIC); 27304a71df50SFrank Blaschka if (!page) { 27314a71df50SFrank Blaschka return NULL; 27324a71df50SFrank Blaschka } else { 27334a71df50SFrank Blaschka free_page((unsigned long)entry->elements[i]); 27344a71df50SFrank Blaschka entry->elements[i] = page_address(page); 27354a71df50SFrank Blaschka if (card->options.performance_stats) 27364a71df50SFrank Blaschka card->perf_stats.sg_alloc_page_rx++; 27374a71df50SFrank Blaschka } 27384a71df50SFrank Blaschka } 27394a71df50SFrank Blaschka } 27404a71df50SFrank Blaschka list_del_init(&entry->list); 27414a71df50SFrank Blaschka return entry; 27424a71df50SFrank Blaschka } 27434a71df50SFrank Blaschka 27444a71df50SFrank Blaschka static int qeth_init_input_buffer(struct qeth_card *card, 27454a71df50SFrank Blaschka struct qeth_qdio_buffer *buf) 27464a71df50SFrank Blaschka { 27474a71df50SFrank Blaschka struct qeth_buffer_pool_entry *pool_entry; 27484a71df50SFrank Blaschka int i; 27494a71df50SFrank Blaschka 2750b3332930SFrank Blaschka if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) { 2751b3332930SFrank Blaschka buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); 2752b3332930SFrank Blaschka if (!buf->rx_skb) 2753b3332930SFrank Blaschka return 1; 2754b3332930SFrank Blaschka } 2755b3332930SFrank Blaschka 27564a71df50SFrank Blaschka pool_entry = qeth_find_free_buffer_pool_entry(card); 27574a71df50SFrank Blaschka if (!pool_entry) 27584a71df50SFrank Blaschka return 1; 27594a71df50SFrank Blaschka 27604a71df50SFrank Blaschka /* 27614a71df50SFrank Blaschka * since the buffer is accessed only from the input_tasklet 27624a71df50SFrank Blaschka * there shouldn't be a need to synchronize; also, since we use 27634a71df50SFrank Blaschka * the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off 27644a71df50SFrank Blaschka * buffers 27654a71df50SFrank Blaschka */ 27664a71df50SFrank Blaschka 27674a71df50SFrank Blaschka buf->pool_entry = pool_entry; 27684a71df50SFrank Blaschka for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { 27694a71df50SFrank Blaschka buf->buffer->element[i].length = PAGE_SIZE; 27704a71df50SFrank Blaschka buf->buffer->element[i].addr = pool_entry->elements[i]; 27714a71df50SFrank Blaschka if (i == QETH_MAX_BUFFER_ELEMENTS(card) - 1) 27723ec90878SJan Glauber buf->buffer->element[i].eflags = SBAL_EFLAGS_LAST_ENTRY; 27734a71df50SFrank Blaschka else 27743ec90878SJan Glauber buf->buffer->element[i].eflags = 0; 27753ec90878SJan Glauber buf->buffer->element[i].sflags = 0; 27764a71df50SFrank Blaschka } 27774a71df50SFrank Blaschka return 0; 27784a71df50SFrank Blaschka } 27794a71df50SFrank Blaschka 27804a71df50SFrank Blaschka int qeth_init_qdio_queues(struct qeth_card *card) 27814a71df50SFrank Blaschka { 27824a71df50SFrank Blaschka int i, j; 27834a71df50SFrank Blaschka int rc; 27844a71df50SFrank Blaschka 2785d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "initqdqs"); 27864a71df50SFrank Blaschka 27874a71df50SFrank Blaschka /* inbound queue */ 27884a71df50SFrank Blaschka memset(card->qdio.in_q->qdio_bufs, 0, 27894a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); 27904a71df50SFrank Blaschka qeth_initialize_working_pool_list(card); 27914a71df50SFrank Blaschka /*give only as many buffers to hardware as we have buffer pool entries*/ 27924a71df50SFrank Blaschka for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++i) 27934a71df50SFrank Blaschka qeth_init_input_buffer(card, &card->qdio.in_q->bufs[i]); 27944a71df50SFrank Blaschka card->qdio.in_q->next_buf_to_init = 27954a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 1; 27964a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0, 2797779e6e1cSJan Glauber card->qdio.in_buf_pool.buf_count - 1); 27984a71df50SFrank Blaschka if (rc) { 2799d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 28004a71df50SFrank Blaschka return rc; 28014a71df50SFrank Blaschka } 28020da9581dSEinar Lueck 28030da9581dSEinar Lueck /* completion */ 28040da9581dSEinar Lueck rc = qeth_cq_init(card); 28050da9581dSEinar Lueck if (rc) { 28060da9581dSEinar Lueck return rc; 28070da9581dSEinar Lueck } 28080da9581dSEinar Lueck 28094a71df50SFrank Blaschka /* outbound queue */ 28104a71df50SFrank Blaschka for (i = 0; i < card->qdio.no_out_queues; ++i) { 28114a71df50SFrank Blaschka memset(card->qdio.out_qs[i]->qdio_bufs, 0, 28124a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer)); 28134a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { 28144a71df50SFrank Blaschka qeth_clear_output_buffer(card->qdio.out_qs[i], 28150da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j], 28160da9581dSEinar Lueck QETH_QDIO_BUF_EMPTY); 28174a71df50SFrank Blaschka } 28184a71df50SFrank Blaschka card->qdio.out_qs[i]->card = card; 28194a71df50SFrank Blaschka card->qdio.out_qs[i]->next_buf_to_fill = 0; 28204a71df50SFrank Blaschka card->qdio.out_qs[i]->do_pack = 0; 28214a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->used_buffers, 0); 28224a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->set_pci_flags_count, 0); 28234a71df50SFrank Blaschka atomic_set(&card->qdio.out_qs[i]->state, 28244a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 28254a71df50SFrank Blaschka } 28264a71df50SFrank Blaschka return 0; 28274a71df50SFrank Blaschka } 28284a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_init_qdio_queues); 28294a71df50SFrank Blaschka 28304a71df50SFrank Blaschka static inline __u8 qeth_get_ipa_adp_type(enum qeth_link_types link_type) 28314a71df50SFrank Blaschka { 28324a71df50SFrank Blaschka switch (link_type) { 28334a71df50SFrank Blaschka case QETH_LINK_TYPE_HSTR: 28344a71df50SFrank Blaschka return 2; 28354a71df50SFrank Blaschka default: 28364a71df50SFrank Blaschka return 1; 28374a71df50SFrank Blaschka } 28384a71df50SFrank Blaschka } 28394a71df50SFrank Blaschka 28404a71df50SFrank Blaschka static void qeth_fill_ipacmd_header(struct qeth_card *card, 28414a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd, __u8 command, 28424a71df50SFrank Blaschka enum qeth_prot_versions prot) 28434a71df50SFrank Blaschka { 28444a71df50SFrank Blaschka memset(cmd, 0, sizeof(struct qeth_ipa_cmd)); 28454a71df50SFrank Blaschka cmd->hdr.command = command; 28464a71df50SFrank Blaschka cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; 28474a71df50SFrank Blaschka cmd->hdr.seqno = card->seqno.ipa; 28484a71df50SFrank Blaschka cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); 28494a71df50SFrank Blaschka cmd->hdr.rel_adapter_no = (__u8) card->info.portno; 28504a71df50SFrank Blaschka if (card->options.layer2) 28514a71df50SFrank Blaschka cmd->hdr.prim_version_no = 2; 28524a71df50SFrank Blaschka else 28534a71df50SFrank Blaschka cmd->hdr.prim_version_no = 1; 28544a71df50SFrank Blaschka cmd->hdr.param_count = 1; 28554a71df50SFrank Blaschka cmd->hdr.prot_version = prot; 28564a71df50SFrank Blaschka cmd->hdr.ipa_supported = 0; 28574a71df50SFrank Blaschka cmd->hdr.ipa_enabled = 0; 28584a71df50SFrank Blaschka } 28594a71df50SFrank Blaschka 28604a71df50SFrank Blaschka struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card, 28614a71df50SFrank Blaschka enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot) 28624a71df50SFrank Blaschka { 28634a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 28644a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 28654a71df50SFrank Blaschka 28664a71df50SFrank Blaschka iob = qeth_wait_for_buffer(&card->write); 28674a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 28684a71df50SFrank Blaschka qeth_fill_ipacmd_header(card, cmd, ipacmd, prot); 28694a71df50SFrank Blaschka 28704a71df50SFrank Blaschka return iob; 28714a71df50SFrank Blaschka } 28724a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer); 28734a71df50SFrank Blaschka 28744a71df50SFrank Blaschka void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, 28754a71df50SFrank Blaschka char prot_type) 28764a71df50SFrank Blaschka { 28774a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 28784a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1); 28794a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 28804a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 28814a71df50SFrank Blaschka } 28824a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd); 28834a71df50SFrank Blaschka 28844a71df50SFrank Blaschka int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, 28854a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply*, 28864a71df50SFrank Blaschka unsigned long), 28874a71df50SFrank Blaschka void *reply_param) 28884a71df50SFrank Blaschka { 28894a71df50SFrank Blaschka int rc; 28904a71df50SFrank Blaschka char prot_type; 28914a71df50SFrank Blaschka 2892847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendipa"); 28934a71df50SFrank Blaschka 28944a71df50SFrank Blaschka if (card->options.layer2) 28954a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_OSN) 28964a71df50SFrank Blaschka prot_type = QETH_PROT_OSN2; 28974a71df50SFrank Blaschka else 28984a71df50SFrank Blaschka prot_type = QETH_PROT_LAYER2; 28994a71df50SFrank Blaschka else 29004a71df50SFrank Blaschka prot_type = QETH_PROT_TCPIP; 29014a71df50SFrank Blaschka qeth_prepare_ipa_cmd(card, iob, prot_type); 2902d11ba0c4SPeter Tiedemann rc = qeth_send_control_data(card, IPA_CMD_LENGTH, 2903d11ba0c4SPeter Tiedemann iob, reply_cb, reply_param); 2904908abbb5SUrsula Braun if (rc == -ETIME) { 2905908abbb5SUrsula Braun qeth_clear_ipacmd_list(card); 2906908abbb5SUrsula Braun qeth_schedule_recovery(card); 2907908abbb5SUrsula Braun } 29084a71df50SFrank Blaschka return rc; 29094a71df50SFrank Blaschka } 29104a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd); 29114a71df50SFrank Blaschka 291270919e23SUrsula Braun int qeth_send_startlan(struct qeth_card *card) 29134a71df50SFrank Blaschka { 29144a71df50SFrank Blaschka int rc; 29154a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29164a71df50SFrank Blaschka 2917d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "strtlan"); 29184a71df50SFrank Blaschka 291970919e23SUrsula Braun iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0); 292070919e23SUrsula Braun rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); 29214a71df50SFrank Blaschka return rc; 29224a71df50SFrank Blaschka } 29234a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_send_startlan); 29244a71df50SFrank Blaschka 2925eb3fb0baSStefan Raspl static int qeth_default_setadapterparms_cb(struct qeth_card *card, 29264a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 29274a71df50SFrank Blaschka { 29284a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29294a71df50SFrank Blaschka 2930847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "defadpcb"); 29314a71df50SFrank Blaschka 29324a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 29334a71df50SFrank Blaschka if (cmd->hdr.return_code == 0) 29344a71df50SFrank Blaschka cmd->hdr.return_code = 29354a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 29364a71df50SFrank Blaschka return 0; 29374a71df50SFrank Blaschka } 29384a71df50SFrank Blaschka 29394a71df50SFrank Blaschka static int qeth_query_setadapterparms_cb(struct qeth_card *card, 29404a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 29414a71df50SFrank Blaschka { 29424a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29434a71df50SFrank Blaschka 2944847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "quyadpcb"); 29454a71df50SFrank Blaschka 29464a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 29475113fec0SUrsula Braun if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) { 29484a71df50SFrank Blaschka card->info.link_type = 29494a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.lan_type; 29505113fec0SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "lnk %d", card->info.link_type); 29515113fec0SUrsula Braun } 29524a71df50SFrank Blaschka card->options.adp.supported_funcs = 29534a71df50SFrank Blaschka cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; 29544a71df50SFrank Blaschka return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); 29554a71df50SFrank Blaschka } 29564a71df50SFrank Blaschka 2957eb3fb0baSStefan Raspl static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, 29584a71df50SFrank Blaschka __u32 command, __u32 cmdlen) 29594a71df50SFrank Blaschka { 29604a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29614a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 29624a71df50SFrank Blaschka 29634a71df50SFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETADAPTERPARMS, 29644a71df50SFrank Blaschka QETH_PROT_IPV4); 29654a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 29664a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.cmdlength = cmdlen; 29674a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.command_code = command; 29684a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total = 1; 29694a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no = 1; 29704a71df50SFrank Blaschka 29714a71df50SFrank Blaschka return iob; 29724a71df50SFrank Blaschka } 29734a71df50SFrank Blaschka 29744a71df50SFrank Blaschka int qeth_query_setadapterparms(struct qeth_card *card) 29754a71df50SFrank Blaschka { 29764a71df50SFrank Blaschka int rc; 29774a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 29784a71df50SFrank Blaschka 2979847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "queryadp"); 29804a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED, 29814a71df50SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms)); 29824a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); 29834a71df50SFrank Blaschka return rc; 29844a71df50SFrank Blaschka } 29854a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_query_setadapterparms); 29864a71df50SFrank Blaschka 29871da74b1cSFrank Blaschka static int qeth_query_ipassists_cb(struct qeth_card *card, 29881da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 29891da74b1cSFrank Blaschka { 29901da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 29911da74b1cSFrank Blaschka 29921da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qipasscb"); 29931da74b1cSFrank Blaschka 29941da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 2995a134884aSStefan Raspl 2996a134884aSStefan Raspl switch (cmd->hdr.return_code) { 2997a134884aSStefan Raspl case IPA_RC_NOTSUPP: 2998a134884aSStefan Raspl case IPA_RC_L2_UNSUPPORTED_CMD: 2999a134884aSStefan Raspl QETH_DBF_TEXT(SETUP, 2, "ipaunsup"); 3000a134884aSStefan Raspl card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; 3001a134884aSStefan Raspl card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; 3002a134884aSStefan Raspl return -0; 3003a134884aSStefan Raspl default: 3004a134884aSStefan Raspl if (cmd->hdr.return_code) { 3005a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Unhandled " 3006a134884aSStefan Raspl "rc=%d\n", 3007a134884aSStefan Raspl dev_name(&card->gdev->dev), 3008a134884aSStefan Raspl cmd->hdr.return_code); 3009a134884aSStefan Raspl return 0; 3010a134884aSStefan Raspl } 3011a134884aSStefan Raspl } 3012a134884aSStefan Raspl 30131da74b1cSFrank Blaschka if (cmd->hdr.prot_version == QETH_PROT_IPV4) { 30141da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; 30151da74b1cSFrank Blaschka card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; 3016a134884aSStefan Raspl } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { 30171da74b1cSFrank Blaschka card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; 30181da74b1cSFrank Blaschka card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; 3019a134884aSStefan Raspl } else 3020a134884aSStefan Raspl QETH_DBF_MESSAGE(1, "%s IPA_CMD_QIPASSIST: Flawed LIC detected" 3021a134884aSStefan Raspl "\n", dev_name(&card->gdev->dev)); 30221da74b1cSFrank Blaschka return 0; 30231da74b1cSFrank Blaschka } 30241da74b1cSFrank Blaschka 30251da74b1cSFrank Blaschka int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) 30261da74b1cSFrank Blaschka { 30271da74b1cSFrank Blaschka int rc; 30281da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 30291da74b1cSFrank Blaschka 30301da74b1cSFrank Blaschka QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); 30311da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); 30321da74b1cSFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); 30331da74b1cSFrank Blaschka return rc; 30341da74b1cSFrank Blaschka } 30351da74b1cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_query_ipassists); 30361da74b1cSFrank Blaschka 30371da74b1cSFrank Blaschka static int qeth_query_setdiagass_cb(struct qeth_card *card, 30381da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 30391da74b1cSFrank Blaschka { 30401da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 30411da74b1cSFrank Blaschka __u16 rc; 30421da74b1cSFrank Blaschka 30431da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 30441da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 30451da74b1cSFrank Blaschka if (rc) 30461da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "diagq:%x", rc); 30471da74b1cSFrank Blaschka else 30481da74b1cSFrank Blaschka card->info.diagass_support = cmd->data.diagass.ext; 30491da74b1cSFrank Blaschka return 0; 30501da74b1cSFrank Blaschka } 30511da74b1cSFrank Blaschka 30521da74b1cSFrank Blaschka static int qeth_query_setdiagass(struct qeth_card *card) 30531da74b1cSFrank Blaschka { 30541da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 30551da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 30561da74b1cSFrank Blaschka 30571da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "qdiagass"); 30581da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 30591da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 30601da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 16; 30611da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY; 30621da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL); 30631da74b1cSFrank Blaschka } 30641da74b1cSFrank Blaschka 30651da74b1cSFrank Blaschka static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid) 30661da74b1cSFrank Blaschka { 30671da74b1cSFrank Blaschka unsigned long info = get_zeroed_page(GFP_KERNEL); 30681da74b1cSFrank Blaschka struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 30691da74b1cSFrank Blaschka struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info; 30701da74b1cSFrank Blaschka struct ccw_dev_id ccwid; 3071caf757c6SHeiko Carstens int level; 30721da74b1cSFrank Blaschka 30731da74b1cSFrank Blaschka tid->chpid = card->info.chpid; 30741da74b1cSFrank Blaschka ccw_device_get_id(CARD_RDEV(card), &ccwid); 30751da74b1cSFrank Blaschka tid->ssid = ccwid.ssid; 30761da74b1cSFrank Blaschka tid->devno = ccwid.devno; 30771da74b1cSFrank Blaschka if (!info) 30781da74b1cSFrank Blaschka return; 3079caf757c6SHeiko Carstens level = stsi(NULL, 0, 0, 0); 3080caf757c6SHeiko Carstens if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0)) 30811da74b1cSFrank Blaschka tid->lparnr = info222->lpar_number; 3082caf757c6SHeiko Carstens if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) { 30831da74b1cSFrank Blaschka EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name)); 30841da74b1cSFrank Blaschka memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname)); 30851da74b1cSFrank Blaschka } 30861da74b1cSFrank Blaschka free_page(info); 30871da74b1cSFrank Blaschka return; 30881da74b1cSFrank Blaschka } 30891da74b1cSFrank Blaschka 30901da74b1cSFrank Blaschka static int qeth_hw_trap_cb(struct qeth_card *card, 30911da74b1cSFrank Blaschka struct qeth_reply *reply, unsigned long data) 30921da74b1cSFrank Blaschka { 30931da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 30941da74b1cSFrank Blaschka __u16 rc; 30951da74b1cSFrank Blaschka 30961da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 30971da74b1cSFrank Blaschka rc = cmd->hdr.return_code; 30981da74b1cSFrank Blaschka if (rc) 30991da74b1cSFrank Blaschka QETH_CARD_TEXT_(card, 2, "trapc:%x", rc); 31001da74b1cSFrank Blaschka return 0; 31011da74b1cSFrank Blaschka } 31021da74b1cSFrank Blaschka 31031da74b1cSFrank Blaschka int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) 31041da74b1cSFrank Blaschka { 31051da74b1cSFrank Blaschka struct qeth_cmd_buffer *iob; 31061da74b1cSFrank Blaschka struct qeth_ipa_cmd *cmd; 31071da74b1cSFrank Blaschka 31081da74b1cSFrank Blaschka QETH_DBF_TEXT(SETUP, 2, "diagtrap"); 31091da74b1cSFrank Blaschka iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); 31101da74b1cSFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 31111da74b1cSFrank Blaschka cmd->data.diagass.subcmd_len = 80; 31121da74b1cSFrank Blaschka cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP; 31131da74b1cSFrank Blaschka cmd->data.diagass.type = 1; 31141da74b1cSFrank Blaschka cmd->data.diagass.action = action; 31151da74b1cSFrank Blaschka switch (action) { 31161da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_ARM: 31171da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0003; 31181da74b1cSFrank Blaschka cmd->data.diagass.ext = 0x00010000 + 31191da74b1cSFrank Blaschka sizeof(struct qeth_trap_id); 31201da74b1cSFrank Blaschka qeth_get_trap_id(card, 31211da74b1cSFrank Blaschka (struct qeth_trap_id *)cmd->data.diagass.cdata); 31221da74b1cSFrank Blaschka break; 31231da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_DISARM: 31241da74b1cSFrank Blaschka cmd->data.diagass.options = 0x0001; 31251da74b1cSFrank Blaschka break; 31261da74b1cSFrank Blaschka case QETH_DIAGS_TRAP_CAPTURE: 31271da74b1cSFrank Blaschka break; 31281da74b1cSFrank Blaschka } 31291da74b1cSFrank Blaschka return qeth_send_ipa_cmd(card, iob, qeth_hw_trap_cb, NULL); 31301da74b1cSFrank Blaschka } 31311da74b1cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hw_trap); 31321da74b1cSFrank Blaschka 313376b11f8eSUrsula Braun int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf, 313476b11f8eSUrsula Braun unsigned int qdio_error, const char *dbftext) 31354a71df50SFrank Blaschka { 3136779e6e1cSJan Glauber if (qdio_error) { 3137847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, dbftext); 313838593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F15=%02X", 31393ec90878SJan Glauber buf->element[15].sflags); 314038593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " F14=%02X", 31413ec90878SJan Glauber buf->element[14].sflags); 314238593d01SCarsten Otte QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error); 31433ec90878SJan Glauber if ((buf->element[15].sflags) == 0x12) { 314476b11f8eSUrsula Braun card->stats.rx_dropped++; 314576b11f8eSUrsula Braun return 0; 314676b11f8eSUrsula Braun } else 31474a71df50SFrank Blaschka return 1; 31484a71df50SFrank Blaschka } 31494a71df50SFrank Blaschka return 0; 31504a71df50SFrank Blaschka } 31514a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_check_qdio_errors); 31524a71df50SFrank Blaschka 3153b3332930SFrank Blaschka void qeth_buffer_reclaim_work(struct work_struct *work) 3154b3332930SFrank Blaschka { 3155b3332930SFrank Blaschka struct qeth_card *card = container_of(work, struct qeth_card, 3156b3332930SFrank Blaschka buffer_reclaim_work.work); 3157b3332930SFrank Blaschka 3158b3332930SFrank Blaschka QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); 3159b3332930SFrank Blaschka qeth_queue_input_buffer(card, card->reclaim_index); 3160b3332930SFrank Blaschka } 3161b3332930SFrank Blaschka 31624a71df50SFrank Blaschka void qeth_queue_input_buffer(struct qeth_card *card, int index) 31634a71df50SFrank Blaschka { 31644a71df50SFrank Blaschka struct qeth_qdio_q *queue = card->qdio.in_q; 3165b3332930SFrank Blaschka struct list_head *lh; 31664a71df50SFrank Blaschka int count; 31674a71df50SFrank Blaschka int i; 31684a71df50SFrank Blaschka int rc; 31694a71df50SFrank Blaschka int newcount = 0; 31704a71df50SFrank Blaschka 31714a71df50SFrank Blaschka count = (index < queue->next_buf_to_init)? 31724a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 31734a71df50SFrank Blaschka (queue->next_buf_to_init - index) : 31744a71df50SFrank Blaschka card->qdio.in_buf_pool.buf_count - 31754a71df50SFrank Blaschka (queue->next_buf_to_init + QDIO_MAX_BUFFERS_PER_Q - index); 31764a71df50SFrank Blaschka /* only requeue at a certain threshold to avoid SIGAs */ 31774a71df50SFrank Blaschka if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)) { 31784a71df50SFrank Blaschka for (i = queue->next_buf_to_init; 31794a71df50SFrank Blaschka i < queue->next_buf_to_init + count; ++i) { 31804a71df50SFrank Blaschka if (qeth_init_input_buffer(card, 31814a71df50SFrank Blaschka &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { 31824a71df50SFrank Blaschka break; 31834a71df50SFrank Blaschka } else { 31844a71df50SFrank Blaschka newcount++; 31854a71df50SFrank Blaschka } 31864a71df50SFrank Blaschka } 31874a71df50SFrank Blaschka 31884a71df50SFrank Blaschka if (newcount < count) { 31894a71df50SFrank Blaschka /* we are in memory shortage so we switch back to 31904a71df50SFrank Blaschka traditional skb allocation and drop packages */ 31914a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 3); 31924a71df50SFrank Blaschka count = newcount; 31934a71df50SFrank Blaschka } else { 31944a71df50SFrank Blaschka atomic_add_unless(&card->force_alloc_skb, -1, 0); 31954a71df50SFrank Blaschka } 31964a71df50SFrank Blaschka 3197b3332930SFrank Blaschka if (!count) { 3198b3332930SFrank Blaschka i = 0; 3199b3332930SFrank Blaschka list_for_each(lh, &card->qdio.in_buf_pool.entry_list) 3200b3332930SFrank Blaschka i++; 3201b3332930SFrank Blaschka if (i == card->qdio.in_buf_pool.buf_count) { 3202b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "qsarbw"); 3203b3332930SFrank Blaschka card->reclaim_index = index; 3204b3332930SFrank Blaschka schedule_delayed_work( 3205b3332930SFrank Blaschka &card->buffer_reclaim_work, 3206b3332930SFrank Blaschka QETH_RECLAIM_WORK_TIME); 3207b3332930SFrank Blaschka } 3208b3332930SFrank Blaschka return; 3209b3332930SFrank Blaschka } 3210b3332930SFrank Blaschka 32114a71df50SFrank Blaschka /* 32124a71df50SFrank Blaschka * according to old code it should be avoided to requeue all 32134a71df50SFrank Blaschka * 128 buffers in order to benefit from PCI avoidance. 32144a71df50SFrank Blaschka * this function keeps at least one buffer (the buffer at 32154a71df50SFrank Blaschka * 'index') un-requeued -> this buffer is the first buffer that 32164a71df50SFrank Blaschka * will be requeued the next time 32174a71df50SFrank Blaschka */ 32184a71df50SFrank Blaschka if (card->options.performance_stats) { 32194a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_cnt++; 32204a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time = 32214a71df50SFrank Blaschka qeth_get_micros(); 32224a71df50SFrank Blaschka } 3223779e6e1cSJan Glauber rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 3224779e6e1cSJan Glauber queue->next_buf_to_init, count); 32254a71df50SFrank Blaschka if (card->options.performance_stats) 32264a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_time += 32274a71df50SFrank Blaschka qeth_get_micros() - 32284a71df50SFrank Blaschka card->perf_stats.inbound_do_qdio_start_time; 32294a71df50SFrank Blaschka if (rc) { 3230847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "qinberr"); 32314a71df50SFrank Blaschka } 32324a71df50SFrank Blaschka queue->next_buf_to_init = (queue->next_buf_to_init + count) % 32334a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 32344a71df50SFrank Blaschka } 32354a71df50SFrank Blaschka } 32364a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_queue_input_buffer); 32374a71df50SFrank Blaschka 32384a71df50SFrank Blaschka static int qeth_handle_send_error(struct qeth_card *card, 3239779e6e1cSJan Glauber struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) 32404a71df50SFrank Blaschka { 32413ec90878SJan Glauber int sbalf15 = buffer->buffer->element[15].sflags; 32424a71df50SFrank Blaschka 3243847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "hdsnderr"); 324458490f18SKlaus-Dieter Wacker if (card->info.type == QETH_CARD_TYPE_IQD) { 324558490f18SKlaus-Dieter Wacker if (sbalf15 == 0) { 324658490f18SKlaus-Dieter Wacker qdio_err = 0; 324758490f18SKlaus-Dieter Wacker } else { 324858490f18SKlaus-Dieter Wacker qdio_err = 1; 324958490f18SKlaus-Dieter Wacker } 325058490f18SKlaus-Dieter Wacker } 325176b11f8eSUrsula Braun qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr"); 3252d303b6fdSJan Glauber 3253d303b6fdSJan Glauber if (!qdio_err) 3254d303b6fdSJan Glauber return QETH_SEND_ERROR_NONE; 3255d303b6fdSJan Glauber 3256d303b6fdSJan Glauber if ((sbalf15 >= 15) && (sbalf15 <= 31)) 3257d303b6fdSJan Glauber return QETH_SEND_ERROR_RETRY; 3258d303b6fdSJan Glauber 3259847a50fdSCarsten Otte QETH_CARD_TEXT(card, 1, "lnkfail"); 3260847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 1, "%04x %02x", 32614a71df50SFrank Blaschka (u16)qdio_err, (u8)sbalf15); 32624a71df50SFrank Blaschka return QETH_SEND_ERROR_LINK_FAILURE; 32634a71df50SFrank Blaschka } 32644a71df50SFrank Blaschka 32654a71df50SFrank Blaschka /* 32664a71df50SFrank Blaschka * Switched to packing state if the number of used buffers on a queue 32674a71df50SFrank Blaschka * reaches a certain limit. 32684a71df50SFrank Blaschka */ 32694a71df50SFrank Blaschka static void qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue) 32704a71df50SFrank Blaschka { 32714a71df50SFrank Blaschka if (!queue->do_pack) { 32724a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 32734a71df50SFrank Blaschka >= QETH_HIGH_WATERMARK_PACK){ 32744a71df50SFrank Blaschka /* switch non-PACKING -> PACKING */ 3275847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "np->pack"); 32764a71df50SFrank Blaschka if (queue->card->options.performance_stats) 32774a71df50SFrank Blaschka queue->card->perf_stats.sc_dp_p++; 32784a71df50SFrank Blaschka queue->do_pack = 1; 32794a71df50SFrank Blaschka } 32804a71df50SFrank Blaschka } 32814a71df50SFrank Blaschka } 32824a71df50SFrank Blaschka 32834a71df50SFrank Blaschka /* 32844a71df50SFrank Blaschka * Switches from packing to non-packing mode. If there is a packing 32854a71df50SFrank Blaschka * buffer on the queue this buffer will be prepared to be flushed. 32864a71df50SFrank Blaschka * In that case 1 is returned to inform the caller. If no buffer 32874a71df50SFrank Blaschka * has to be flushed, zero is returned. 32884a71df50SFrank Blaschka */ 32894a71df50SFrank Blaschka static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue) 32904a71df50SFrank Blaschka { 32914a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 32924a71df50SFrank Blaschka int flush_count = 0; 32934a71df50SFrank Blaschka 32944a71df50SFrank Blaschka if (queue->do_pack) { 32954a71df50SFrank Blaschka if (atomic_read(&queue->used_buffers) 32964a71df50SFrank Blaschka <= QETH_LOW_WATERMARK_PACK) { 32974a71df50SFrank Blaschka /* switch PACKING -> non-PACKING */ 3298847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "pack->np"); 32994a71df50SFrank Blaschka if (queue->card->options.performance_stats) 33004a71df50SFrank Blaschka queue->card->perf_stats.sc_p_dp++; 33014a71df50SFrank Blaschka queue->do_pack = 0; 33024a71df50SFrank Blaschka /* flush packing buffers */ 33030da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 33044a71df50SFrank Blaschka if ((atomic_read(&buffer->state) == 33054a71df50SFrank Blaschka QETH_QDIO_BUF_EMPTY) && 33064a71df50SFrank Blaschka (buffer->next_element_to_fill > 0)) { 33074a71df50SFrank Blaschka atomic_set(&buffer->state, 33084a71df50SFrank Blaschka QETH_QDIO_BUF_PRIMED); 33094a71df50SFrank Blaschka flush_count++; 33104a71df50SFrank Blaschka queue->next_buf_to_fill = 33114a71df50SFrank Blaschka (queue->next_buf_to_fill + 1) % 33124a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 33134a71df50SFrank Blaschka } 33144a71df50SFrank Blaschka } 33154a71df50SFrank Blaschka } 33164a71df50SFrank Blaschka return flush_count; 33174a71df50SFrank Blaschka } 33184a71df50SFrank Blaschka 33190da9581dSEinar Lueck 33204a71df50SFrank Blaschka /* 33214a71df50SFrank Blaschka * Called to flush a packing buffer if no more pci flags are on the queue. 33224a71df50SFrank Blaschka * Checks if there is a packing buffer and prepares it to be flushed. 33234a71df50SFrank Blaschka * In that case returns 1, otherwise zero. 33244a71df50SFrank Blaschka */ 33254a71df50SFrank Blaschka static int qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue) 33264a71df50SFrank Blaschka { 33274a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 33284a71df50SFrank Blaschka 33290da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 33304a71df50SFrank Blaschka if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) && 33314a71df50SFrank Blaschka (buffer->next_element_to_fill > 0)) { 33324a71df50SFrank Blaschka /* it's a packing buffer */ 33334a71df50SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 33344a71df50SFrank Blaschka queue->next_buf_to_fill = 33354a71df50SFrank Blaschka (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q; 33364a71df50SFrank Blaschka return 1; 33374a71df50SFrank Blaschka } 33384a71df50SFrank Blaschka return 0; 33394a71df50SFrank Blaschka } 33404a71df50SFrank Blaschka 3341779e6e1cSJan Glauber static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, 3342779e6e1cSJan Glauber int count) 33434a71df50SFrank Blaschka { 33444a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buf; 33454a71df50SFrank Blaschka int rc; 33464a71df50SFrank Blaschka int i; 33474a71df50SFrank Blaschka unsigned int qdio_flags; 33484a71df50SFrank Blaschka 33494a71df50SFrank Blaschka for (i = index; i < index + count; ++i) { 33500da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 33510da9581dSEinar Lueck buf = queue->bufs[bidx]; 33523ec90878SJan Glauber buf->buffer->element[buf->next_element_to_fill - 1].eflags |= 33533ec90878SJan Glauber SBAL_EFLAGS_LAST_ENTRY; 33544a71df50SFrank Blaschka 33550da9581dSEinar Lueck if (queue->bufstates) 33560da9581dSEinar Lueck queue->bufstates[bidx].user = buf; 33570da9581dSEinar Lueck 33584a71df50SFrank Blaschka if (queue->card->info.type == QETH_CARD_TYPE_IQD) 33594a71df50SFrank Blaschka continue; 33604a71df50SFrank Blaschka 33614a71df50SFrank Blaschka if (!queue->do_pack) { 33624a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) >= 33634a71df50SFrank Blaschka (QETH_HIGH_WATERMARK_PACK - 33644a71df50SFrank Blaschka QETH_WATERMARK_PACK_FUZZ)) && 33654a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 33664a71df50SFrank Blaschka /* it's likely that we'll go to packing 33674a71df50SFrank Blaschka * mode soon */ 33684a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 33693ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 33704a71df50SFrank Blaschka } 33714a71df50SFrank Blaschka } else { 33724a71df50SFrank Blaschka if (!atomic_read(&queue->set_pci_flags_count)) { 33734a71df50SFrank Blaschka /* 33744a71df50SFrank Blaschka * there's no outstanding PCI any more, so we 33754a71df50SFrank Blaschka * have to request a PCI to be sure the the PCI 33764a71df50SFrank Blaschka * will wake at some time in the future then we 33774a71df50SFrank Blaschka * can flush packed buffers that might still be 33784a71df50SFrank Blaschka * hanging around, which can happen if no 33794a71df50SFrank Blaschka * further send was requested by the stack 33804a71df50SFrank Blaschka */ 33814a71df50SFrank Blaschka atomic_inc(&queue->set_pci_flags_count); 33823ec90878SJan Glauber buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; 33834a71df50SFrank Blaschka } 33844a71df50SFrank Blaschka } 33854a71df50SFrank Blaschka } 33864a71df50SFrank Blaschka 33874a71df50SFrank Blaschka queue->card->dev->trans_start = jiffies; 33884a71df50SFrank Blaschka if (queue->card->options.performance_stats) { 33894a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_cnt++; 33904a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time = 33914a71df50SFrank Blaschka qeth_get_micros(); 33924a71df50SFrank Blaschka } 33934a71df50SFrank Blaschka qdio_flags = QDIO_FLAG_SYNC_OUTPUT; 33944a71df50SFrank Blaschka if (atomic_read(&queue->set_pci_flags_count)) 33954a71df50SFrank Blaschka qdio_flags |= QDIO_FLAG_PCI_OUT; 33964a71df50SFrank Blaschka rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, 3397779e6e1cSJan Glauber queue->queue_no, index, count); 33984a71df50SFrank Blaschka if (queue->card->options.performance_stats) 33994a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_time += 34004a71df50SFrank Blaschka qeth_get_micros() - 34014a71df50SFrank Blaschka queue->card->perf_stats.outbound_do_qdio_start_time; 3402aa3a41d0SJan Glauber atomic_add(count, &queue->used_buffers); 34034a71df50SFrank Blaschka if (rc) { 3404d303b6fdSJan Glauber queue->card->stats.tx_errors += count; 3405d303b6fdSJan Glauber /* ignore temporary SIGA errors without busy condition */ 34061549d13fSJan Glauber if (rc == -ENOBUFS) 3407d303b6fdSJan Glauber return; 3408847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 2, "flushbuf"); 34090da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no); 34100da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " idx%d", index); 34110da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 2, " c%d", count); 3412847a50fdSCarsten Otte QETH_CARD_TEXT_(queue->card, 2, " err%d", rc); 3413d303b6fdSJan Glauber 34144a71df50SFrank Blaschka /* this must not happen under normal circumstances. if it 34154a71df50SFrank Blaschka * happens something is really wrong -> recover */ 34164a71df50SFrank Blaschka qeth_schedule_recovery(queue->card); 34174a71df50SFrank Blaschka return; 34184a71df50SFrank Blaschka } 34194a71df50SFrank Blaschka if (queue->card->options.performance_stats) 34204a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent += count; 34214a71df50SFrank Blaschka } 34224a71df50SFrank Blaschka 34234a71df50SFrank Blaschka static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue) 34244a71df50SFrank Blaschka { 34254a71df50SFrank Blaschka int index; 34264a71df50SFrank Blaschka int flush_cnt = 0; 34274a71df50SFrank Blaschka int q_was_packing = 0; 34284a71df50SFrank Blaschka 34294a71df50SFrank Blaschka /* 34304a71df50SFrank Blaschka * check if weed have to switch to non-packing mode or if 34314a71df50SFrank Blaschka * we have to get a pci flag out on the queue 34324a71df50SFrank Blaschka */ 34334a71df50SFrank Blaschka if ((atomic_read(&queue->used_buffers) <= QETH_LOW_WATERMARK_PACK) || 34344a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) { 34354a71df50SFrank Blaschka if (atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH) == 34364a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED) { 34374a71df50SFrank Blaschka /* 34384a71df50SFrank Blaschka * If we get in here, there was no action in 34394a71df50SFrank Blaschka * do_send_packet. So, we check if there is a 34404a71df50SFrank Blaschka * packing buffer to be flushed here. 34414a71df50SFrank Blaschka */ 34424a71df50SFrank Blaschka netif_stop_queue(queue->card->dev); 34434a71df50SFrank Blaschka index = queue->next_buf_to_fill; 34444a71df50SFrank Blaschka q_was_packing = queue->do_pack; 34454a71df50SFrank Blaschka /* queue->do_pack may change */ 34464a71df50SFrank Blaschka barrier(); 34474a71df50SFrank Blaschka flush_cnt += qeth_switch_to_nonpacking_if_needed(queue); 34484a71df50SFrank Blaschka if (!flush_cnt && 34494a71df50SFrank Blaschka !atomic_read(&queue->set_pci_flags_count)) 34504a71df50SFrank Blaschka flush_cnt += 34514a71df50SFrank Blaschka qeth_flush_buffers_on_no_pci(queue); 34524a71df50SFrank Blaschka if (queue->card->options.performance_stats && 34534a71df50SFrank Blaschka q_was_packing) 34544a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += 34554a71df50SFrank Blaschka flush_cnt; 34564a71df50SFrank Blaschka if (flush_cnt) 3457779e6e1cSJan Glauber qeth_flush_buffers(queue, index, flush_cnt); 34584a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 34594a71df50SFrank Blaschka } 34604a71df50SFrank Blaschka } 34614a71df50SFrank Blaschka } 34624a71df50SFrank Blaschka 3463a1c3ed4cSFrank Blaschka void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, 3464a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3465a1c3ed4cSFrank Blaschka { 3466a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3467a1c3ed4cSFrank Blaschka 34680cffef48SFrank Blaschka if (card->dev && (card->dev->flags & IFF_UP)) 3469a1c3ed4cSFrank Blaschka napi_schedule(&card->napi); 3470a1c3ed4cSFrank Blaschka } 3471a1c3ed4cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_start_poll); 3472a1c3ed4cSFrank Blaschka 34730da9581dSEinar Lueck int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) 34740da9581dSEinar Lueck { 34750da9581dSEinar Lueck int rc; 34760da9581dSEinar Lueck 34770da9581dSEinar Lueck if (card->options.cq == QETH_CQ_NOTAVAILABLE) { 34780da9581dSEinar Lueck rc = -1; 34790da9581dSEinar Lueck goto out; 34800da9581dSEinar Lueck } else { 34810da9581dSEinar Lueck if (card->options.cq == cq) { 34820da9581dSEinar Lueck rc = 0; 34830da9581dSEinar Lueck goto out; 34840da9581dSEinar Lueck } 34850da9581dSEinar Lueck 34860da9581dSEinar Lueck if (card->state != CARD_STATE_DOWN && 34870da9581dSEinar Lueck card->state != CARD_STATE_RECOVER) { 34880da9581dSEinar Lueck rc = -1; 34890da9581dSEinar Lueck goto out; 34900da9581dSEinar Lueck } 34910da9581dSEinar Lueck 34920da9581dSEinar Lueck qeth_free_qdio_buffers(card); 34930da9581dSEinar Lueck card->options.cq = cq; 34940da9581dSEinar Lueck rc = 0; 34950da9581dSEinar Lueck } 34960da9581dSEinar Lueck out: 34970da9581dSEinar Lueck return rc; 34980da9581dSEinar Lueck 34990da9581dSEinar Lueck } 35000da9581dSEinar Lueck EXPORT_SYMBOL_GPL(qeth_configure_cq); 35010da9581dSEinar Lueck 35020da9581dSEinar Lueck 35030da9581dSEinar Lueck static void qeth_qdio_cq_handler(struct qeth_card *card, 35040da9581dSEinar Lueck unsigned int qdio_err, 35050da9581dSEinar Lueck unsigned int queue, int first_element, int count) { 35060da9581dSEinar Lueck struct qeth_qdio_q *cq = card->qdio.c_q; 35070da9581dSEinar Lueck int i; 35080da9581dSEinar Lueck int rc; 35090da9581dSEinar Lueck 35100da9581dSEinar Lueck if (!qeth_is_cq(card, queue)) 35110da9581dSEinar Lueck goto out; 35120da9581dSEinar Lueck 35130da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element); 35140da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqhc%d", count); 35150da9581dSEinar Lueck QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err); 35160da9581dSEinar Lueck 35170da9581dSEinar Lueck if (qdio_err) { 35180da9581dSEinar Lueck netif_stop_queue(card->dev); 35190da9581dSEinar Lueck qeth_schedule_recovery(card); 35200da9581dSEinar Lueck goto out; 35210da9581dSEinar Lueck } 35220da9581dSEinar Lueck 35230da9581dSEinar Lueck if (card->options.performance_stats) { 35240da9581dSEinar Lueck card->perf_stats.cq_cnt++; 35250da9581dSEinar Lueck card->perf_stats.cq_start_time = qeth_get_micros(); 35260da9581dSEinar Lueck } 35270da9581dSEinar Lueck 35280da9581dSEinar Lueck for (i = first_element; i < first_element + count; ++i) { 35290da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 35300da9581dSEinar Lueck struct qdio_buffer *buffer = &cq->qdio_bufs[bidx]; 35310da9581dSEinar Lueck int e; 35320da9581dSEinar Lueck 35330da9581dSEinar Lueck e = 0; 35340da9581dSEinar Lueck while (buffer->element[e].addr) { 35350da9581dSEinar Lueck unsigned long phys_aob_addr; 35360da9581dSEinar Lueck 35370da9581dSEinar Lueck phys_aob_addr = (unsigned long) buffer->element[e].addr; 35380da9581dSEinar Lueck qeth_qdio_handle_aob(card, phys_aob_addr); 35390da9581dSEinar Lueck buffer->element[e].addr = NULL; 35400da9581dSEinar Lueck buffer->element[e].eflags = 0; 35410da9581dSEinar Lueck buffer->element[e].sflags = 0; 35420da9581dSEinar Lueck buffer->element[e].length = 0; 35430da9581dSEinar Lueck 35440da9581dSEinar Lueck ++e; 35450da9581dSEinar Lueck } 35460da9581dSEinar Lueck 35470da9581dSEinar Lueck buffer->element[15].eflags = 0; 35480da9581dSEinar Lueck buffer->element[15].sflags = 0; 35490da9581dSEinar Lueck } 35500da9581dSEinar Lueck rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, 35510da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init, 35520da9581dSEinar Lueck count); 35530da9581dSEinar Lueck if (rc) { 35540da9581dSEinar Lueck dev_warn(&card->gdev->dev, 35550da9581dSEinar Lueck "QDIO reported an error, rc=%i\n", rc); 35560da9581dSEinar Lueck QETH_CARD_TEXT(card, 2, "qcqherr"); 35570da9581dSEinar Lueck } 35580da9581dSEinar Lueck card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init 35590da9581dSEinar Lueck + count) % QDIO_MAX_BUFFERS_PER_Q; 35600da9581dSEinar Lueck 35610da9581dSEinar Lueck netif_wake_queue(card->dev); 35620da9581dSEinar Lueck 35630da9581dSEinar Lueck if (card->options.performance_stats) { 35640da9581dSEinar Lueck int delta_t = qeth_get_micros(); 35650da9581dSEinar Lueck delta_t -= card->perf_stats.cq_start_time; 35660da9581dSEinar Lueck card->perf_stats.cq_time += delta_t; 35670da9581dSEinar Lueck } 35680da9581dSEinar Lueck out: 35690da9581dSEinar Lueck return; 35700da9581dSEinar Lueck } 35710da9581dSEinar Lueck 3572a1c3ed4cSFrank Blaschka void qeth_qdio_input_handler(struct ccw_device *ccwdev, unsigned int qdio_err, 35730da9581dSEinar Lueck unsigned int queue, int first_elem, int count, 3574a1c3ed4cSFrank Blaschka unsigned long card_ptr) 3575a1c3ed4cSFrank Blaschka { 3576a1c3ed4cSFrank Blaschka struct qeth_card *card = (struct qeth_card *)card_ptr; 3577a1c3ed4cSFrank Blaschka 35780da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qihq%d", queue); 35790da9581dSEinar Lueck QETH_CARD_TEXT_(card, 2, "qiec%d", qdio_err); 35800da9581dSEinar Lueck 35810da9581dSEinar Lueck if (qeth_is_cq(card, queue)) 35820da9581dSEinar Lueck qeth_qdio_cq_handler(card, qdio_err, queue, first_elem, count); 35830da9581dSEinar Lueck else if (qdio_err) 3584a1c3ed4cSFrank Blaschka qeth_schedule_recovery(card); 35850da9581dSEinar Lueck 35860da9581dSEinar Lueck 3587a1c3ed4cSFrank Blaschka } 3588a1c3ed4cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_input_handler); 3589a1c3ed4cSFrank Blaschka 3590779e6e1cSJan Glauber void qeth_qdio_output_handler(struct ccw_device *ccwdev, 3591779e6e1cSJan Glauber unsigned int qdio_error, int __queue, int first_element, 3592779e6e1cSJan Glauber int count, unsigned long card_ptr) 35934a71df50SFrank Blaschka { 35944a71df50SFrank Blaschka struct qeth_card *card = (struct qeth_card *) card_ptr; 35954a71df50SFrank Blaschka struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; 35964a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 35974a71df50SFrank Blaschka int i; 35984a71df50SFrank Blaschka 3599847a50fdSCarsten Otte QETH_CARD_TEXT(card, 6, "qdouhdl"); 36001549d13fSJan Glauber if (qdio_error & QDIO_ERROR_FATAL) { 3601847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "achkcond"); 36024a71df50SFrank Blaschka netif_stop_queue(card->dev); 36034a71df50SFrank Blaschka qeth_schedule_recovery(card); 36044a71df50SFrank Blaschka return; 36054a71df50SFrank Blaschka } 36064a71df50SFrank Blaschka if (card->options.performance_stats) { 36074a71df50SFrank Blaschka card->perf_stats.outbound_handler_cnt++; 36084a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time = 36094a71df50SFrank Blaschka qeth_get_micros(); 36104a71df50SFrank Blaschka } 36114a71df50SFrank Blaschka for (i = first_element; i < (first_element + count); ++i) { 36120da9581dSEinar Lueck int bidx = i % QDIO_MAX_BUFFERS_PER_Q; 36130da9581dSEinar Lueck buffer = queue->bufs[bidx]; 3614b67d801fSUrsula Braun qeth_handle_send_error(card, buffer, qdio_error); 36150da9581dSEinar Lueck 36160da9581dSEinar Lueck if (queue->bufstates && 36170da9581dSEinar Lueck (queue->bufstates[bidx].flags & 36180da9581dSEinar Lueck QDIO_OUTBUF_STATE_FLAG_PENDING) != 0) { 361918af5c17SStefan Raspl WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); 3620b3332930SFrank Blaschka 3621b3332930SFrank Blaschka if (atomic_cmpxchg(&buffer->state, 3622b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED, 3623b3332930SFrank Blaschka QETH_QDIO_BUF_PENDING) == 3624b3332930SFrank Blaschka QETH_QDIO_BUF_PRIMED) { 3625b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, 3626b3332930SFrank Blaschka TX_NOTIFY_PENDING); 3627b3332930SFrank Blaschka } 36280da9581dSEinar Lueck buffer->aob = queue->bufstates[bidx].aob; 36290da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx); 3630b3332930SFrank Blaschka QETH_CARD_TEXT(queue->card, 5, "aob"); 36310da9581dSEinar Lueck QETH_CARD_TEXT_(queue->card, 5, "%lx", 36320da9581dSEinar Lueck virt_to_phys(buffer->aob)); 3633b3332930SFrank Blaschka if (qeth_init_qdio_out_buf(queue, bidx)) { 3634b3332930SFrank Blaschka QETH_CARD_TEXT(card, 2, "outofbuf"); 36350da9581dSEinar Lueck qeth_schedule_recovery(card); 3636b3332930SFrank Blaschka } 36370da9581dSEinar Lueck } else { 3638b3332930SFrank Blaschka if (card->options.cq == QETH_CQ_ENABLED) { 3639b3332930SFrank Blaschka enum iucv_tx_notify n; 3640b3332930SFrank Blaschka 3641b3332930SFrank Blaschka n = qeth_compute_cq_notification( 3642b3332930SFrank Blaschka buffer->buffer->element[15].sflags, 0); 3643b3332930SFrank Blaschka qeth_notify_skbs(queue, buffer, n); 3644b3332930SFrank Blaschka } 3645b3332930SFrank Blaschka 36460da9581dSEinar Lueck qeth_clear_output_buffer(queue, buffer, 36470da9581dSEinar Lueck QETH_QDIO_BUF_EMPTY); 36480da9581dSEinar Lueck } 36490da9581dSEinar Lueck qeth_cleanup_handled_pending(queue, bidx, 0); 36504a71df50SFrank Blaschka } 36514a71df50SFrank Blaschka atomic_sub(count, &queue->used_buffers); 36524a71df50SFrank Blaschka /* check if we need to do something on this outbound queue */ 36534a71df50SFrank Blaschka if (card->info.type != QETH_CARD_TYPE_IQD) 36544a71df50SFrank Blaschka qeth_check_outbound_queue(queue); 36554a71df50SFrank Blaschka 36564a71df50SFrank Blaschka netif_wake_queue(queue->card->dev); 36574a71df50SFrank Blaschka if (card->options.performance_stats) 36584a71df50SFrank Blaschka card->perf_stats.outbound_handler_time += qeth_get_micros() - 36594a71df50SFrank Blaschka card->perf_stats.outbound_handler_start_time; 36604a71df50SFrank Blaschka } 36614a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); 36624a71df50SFrank Blaschka 36634a71df50SFrank Blaschka int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, 36644a71df50SFrank Blaschka int ipv, int cast_type) 36654a71df50SFrank Blaschka { 36665113fec0SUrsula Braun if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD || 36675113fec0SUrsula Braun card->info.type == QETH_CARD_TYPE_OSX)) 36684a71df50SFrank Blaschka return card->qdio.default_out_queue; 36694a71df50SFrank Blaschka switch (card->qdio.no_out_queues) { 36704a71df50SFrank Blaschka case 4: 36714a71df50SFrank Blaschka if (cast_type && card->info.is_multicast_different) 36724a71df50SFrank Blaschka return card->info.is_multicast_different & 36734a71df50SFrank Blaschka (card->qdio.no_out_queues - 1); 36744a71df50SFrank Blaschka if (card->qdio.do_prio_queueing && (ipv == 4)) { 36754a71df50SFrank Blaschka const u8 tos = ip_hdr(skb)->tos; 36764a71df50SFrank Blaschka 36774a71df50SFrank Blaschka if (card->qdio.do_prio_queueing == 36784a71df50SFrank Blaschka QETH_PRIO_Q_ING_TOS) { 36794a71df50SFrank Blaschka if (tos & IP_TOS_NOTIMPORTANT) 36804a71df50SFrank Blaschka return 3; 36814a71df50SFrank Blaschka if (tos & IP_TOS_HIGHRELIABILITY) 36824a71df50SFrank Blaschka return 2; 36834a71df50SFrank Blaschka if (tos & IP_TOS_HIGHTHROUGHPUT) 36844a71df50SFrank Blaschka return 1; 36854a71df50SFrank Blaschka if (tos & IP_TOS_LOWDELAY) 36864a71df50SFrank Blaschka return 0; 36874a71df50SFrank Blaschka } 36884a71df50SFrank Blaschka if (card->qdio.do_prio_queueing == 36894a71df50SFrank Blaschka QETH_PRIO_Q_ING_PREC) 36904a71df50SFrank Blaschka return 3 - (tos >> 6); 36914a71df50SFrank Blaschka } else if (card->qdio.do_prio_queueing && (ipv == 6)) { 36924a71df50SFrank Blaschka /* TODO: IPv6!!! */ 36934a71df50SFrank Blaschka } 36944a71df50SFrank Blaschka return card->qdio.default_out_queue; 36954a71df50SFrank Blaschka case 1: /* fallthrough for single-out-queue 1920-device */ 36964a71df50SFrank Blaschka default: 36974a71df50SFrank Blaschka return card->qdio.default_out_queue; 36984a71df50SFrank Blaschka } 36994a71df50SFrank Blaschka } 37004a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_priority_queue); 37014a71df50SFrank Blaschka 3702271648b4SFrank Blaschka int qeth_get_elements_for_frags(struct sk_buff *skb) 3703271648b4SFrank Blaschka { 3704271648b4SFrank Blaschka int cnt, length, e, elements = 0; 3705271648b4SFrank Blaschka struct skb_frag_struct *frag; 3706271648b4SFrank Blaschka char *data; 3707271648b4SFrank Blaschka 3708271648b4SFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 3709271648b4SFrank Blaschka frag = &skb_shinfo(skb)->frags[cnt]; 3710271648b4SFrank Blaschka data = (char *)page_to_phys(skb_frag_page(frag)) + 3711271648b4SFrank Blaschka frag->page_offset; 3712271648b4SFrank Blaschka length = frag->size; 3713271648b4SFrank Blaschka e = PFN_UP((unsigned long)data + length - 1) - 3714271648b4SFrank Blaschka PFN_DOWN((unsigned long)data); 3715271648b4SFrank Blaschka elements += e; 3716271648b4SFrank Blaschka } 3717271648b4SFrank Blaschka return elements; 3718271648b4SFrank Blaschka } 3719271648b4SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); 3720271648b4SFrank Blaschka 3721065cc782SStefan Raspl int qeth_get_elements_no(struct qeth_card *card, 37224a71df50SFrank Blaschka struct sk_buff *skb, int elems) 37234a71df50SFrank Blaschka { 372451aa165cSFrank Blaschka int dlen = skb->len - skb->data_len; 372551aa165cSFrank Blaschka int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) - 372651aa165cSFrank Blaschka PFN_DOWN((unsigned long)skb->data); 37274a71df50SFrank Blaschka 3728271648b4SFrank Blaschka elements_needed += qeth_get_elements_for_frags(skb); 3729271648b4SFrank Blaschka 37304a71df50SFrank Blaschka if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { 373114cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "Invalid size of IP packet " 37324a71df50SFrank Blaschka "(Number=%d / Length=%d). Discarded.\n", 37334a71df50SFrank Blaschka (elements_needed+elems), skb->len); 37344a71df50SFrank Blaschka return 0; 37354a71df50SFrank Blaschka } 37364a71df50SFrank Blaschka return elements_needed; 37374a71df50SFrank Blaschka } 37384a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_elements_no); 37394a71df50SFrank Blaschka 3740d4ae1f5eSStefan Raspl int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) 374151aa165cSFrank Blaschka { 374251aa165cSFrank Blaschka int hroom, inpage, rest; 374351aa165cSFrank Blaschka 374451aa165cSFrank Blaschka if (((unsigned long)skb->data & PAGE_MASK) != 374551aa165cSFrank Blaschka (((unsigned long)skb->data + len - 1) & PAGE_MASK)) { 374651aa165cSFrank Blaschka hroom = skb_headroom(skb); 374751aa165cSFrank Blaschka inpage = PAGE_SIZE - ((unsigned long) skb->data % PAGE_SIZE); 374851aa165cSFrank Blaschka rest = len - inpage; 374951aa165cSFrank Blaschka if (rest > hroom) 375051aa165cSFrank Blaschka return 1; 375151aa165cSFrank Blaschka memmove(skb->data - rest, skb->data, skb->len - skb->data_len); 375251aa165cSFrank Blaschka skb->data -= rest; 3753d4ae1f5eSStefan Raspl skb->tail -= rest; 3754d4ae1f5eSStefan Raspl *hdr = (struct qeth_hdr *)skb->data; 375551aa165cSFrank Blaschka QETH_DBF_MESSAGE(2, "skb bounce len: %d rest: %d\n", len, rest); 375651aa165cSFrank Blaschka } 375751aa165cSFrank Blaschka return 0; 375851aa165cSFrank Blaschka } 375951aa165cSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); 376051aa165cSFrank Blaschka 3761f90b744eSFrank Blaschka static inline void __qeth_fill_buffer(struct sk_buff *skb, 3762683d718aSFrank Blaschka struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill, 3763683d718aSFrank Blaschka int offset) 37644a71df50SFrank Blaschka { 376551aa165cSFrank Blaschka int length = skb->len - skb->data_len; 37664a71df50SFrank Blaschka int length_here; 37674a71df50SFrank Blaschka int element; 37684a71df50SFrank Blaschka char *data; 376951aa165cSFrank Blaschka int first_lap, cnt; 377051aa165cSFrank Blaschka struct skb_frag_struct *frag; 37714a71df50SFrank Blaschka 37724a71df50SFrank Blaschka element = *next_element_to_fill; 37734a71df50SFrank Blaschka data = skb->data; 37744a71df50SFrank Blaschka first_lap = (is_tso == 0 ? 1 : 0); 37754a71df50SFrank Blaschka 3776683d718aSFrank Blaschka if (offset >= 0) { 3777683d718aSFrank Blaschka data = skb->data + offset; 3778e1f03ae8SFrank Blaschka length -= offset; 3779683d718aSFrank Blaschka first_lap = 0; 3780683d718aSFrank Blaschka } 3781683d718aSFrank Blaschka 37824a71df50SFrank Blaschka while (length > 0) { 37834a71df50SFrank Blaschka /* length_here is the remaining amount of data in this page */ 37844a71df50SFrank Blaschka length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); 37854a71df50SFrank Blaschka if (length < length_here) 37864a71df50SFrank Blaschka length_here = length; 37874a71df50SFrank Blaschka 37884a71df50SFrank Blaschka buffer->element[element].addr = data; 37894a71df50SFrank Blaschka buffer->element[element].length = length_here; 37904a71df50SFrank Blaschka length -= length_here; 37914a71df50SFrank Blaschka if (!length) { 37924a71df50SFrank Blaschka if (first_lap) 379351aa165cSFrank Blaschka if (skb_shinfo(skb)->nr_frags) 37943ec90878SJan Glauber buffer->element[element].eflags = 37953ec90878SJan Glauber SBAL_EFLAGS_FIRST_FRAG; 379651aa165cSFrank Blaschka else 37973ec90878SJan Glauber buffer->element[element].eflags = 0; 37984a71df50SFrank Blaschka else 37993ec90878SJan Glauber buffer->element[element].eflags = 38003ec90878SJan Glauber SBAL_EFLAGS_MIDDLE_FRAG; 38014a71df50SFrank Blaschka } else { 38024a71df50SFrank Blaschka if (first_lap) 38033ec90878SJan Glauber buffer->element[element].eflags = 38043ec90878SJan Glauber SBAL_EFLAGS_FIRST_FRAG; 38054a71df50SFrank Blaschka else 38063ec90878SJan Glauber buffer->element[element].eflags = 38073ec90878SJan Glauber SBAL_EFLAGS_MIDDLE_FRAG; 38084a71df50SFrank Blaschka } 38094a71df50SFrank Blaschka data += length_here; 38104a71df50SFrank Blaschka element++; 38114a71df50SFrank Blaschka first_lap = 0; 38124a71df50SFrank Blaschka } 381351aa165cSFrank Blaschka 381451aa165cSFrank Blaschka for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { 381551aa165cSFrank Blaschka frag = &skb_shinfo(skb)->frags[cnt]; 3816271648b4SFrank Blaschka data = (char *)page_to_phys(skb_frag_page(frag)) + 3817271648b4SFrank Blaschka frag->page_offset; 3818271648b4SFrank Blaschka length = frag->size; 3819271648b4SFrank Blaschka while (length > 0) { 3820271648b4SFrank Blaschka length_here = PAGE_SIZE - 3821271648b4SFrank Blaschka ((unsigned long) data % PAGE_SIZE); 3822271648b4SFrank Blaschka if (length < length_here) 3823271648b4SFrank Blaschka length_here = length; 3824271648b4SFrank Blaschka 3825271648b4SFrank Blaschka buffer->element[element].addr = data; 3826271648b4SFrank Blaschka buffer->element[element].length = length_here; 3827271648b4SFrank Blaschka buffer->element[element].eflags = 3828271648b4SFrank Blaschka SBAL_EFLAGS_MIDDLE_FRAG; 3829271648b4SFrank Blaschka length -= length_here; 3830271648b4SFrank Blaschka data += length_here; 383151aa165cSFrank Blaschka element++; 383251aa165cSFrank Blaschka } 3833271648b4SFrank Blaschka } 383451aa165cSFrank Blaschka 38353ec90878SJan Glauber if (buffer->element[element - 1].eflags) 38363ec90878SJan Glauber buffer->element[element - 1].eflags = SBAL_EFLAGS_LAST_FRAG; 38374a71df50SFrank Blaschka *next_element_to_fill = element; 38384a71df50SFrank Blaschka } 38394a71df50SFrank Blaschka 3840f90b744eSFrank Blaschka static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, 3841683d718aSFrank Blaschka struct qeth_qdio_out_buffer *buf, struct sk_buff *skb, 3842683d718aSFrank Blaschka struct qeth_hdr *hdr, int offset, int hd_len) 38434a71df50SFrank Blaschka { 38444a71df50SFrank Blaschka struct qdio_buffer *buffer; 38454a71df50SFrank Blaschka int flush_cnt = 0, hdr_len, large_send = 0; 38464a71df50SFrank Blaschka 38474a71df50SFrank Blaschka buffer = buf->buffer; 38484a71df50SFrank Blaschka atomic_inc(&skb->users); 38494a71df50SFrank Blaschka skb_queue_tail(&buf->skb_list, skb); 38504a71df50SFrank Blaschka 38514a71df50SFrank Blaschka /*check first on TSO ....*/ 3852683d718aSFrank Blaschka if (hdr->hdr.l3.id == QETH_HEADER_TYPE_TSO) { 38534a71df50SFrank Blaschka int element = buf->next_element_to_fill; 38544a71df50SFrank Blaschka 3855683d718aSFrank Blaschka hdr_len = sizeof(struct qeth_hdr_tso) + 3856683d718aSFrank Blaschka ((struct qeth_hdr_tso *)hdr)->ext.dg_hdr_len; 38574a71df50SFrank Blaschka /*fill first buffer entry only with header information */ 38584a71df50SFrank Blaschka buffer->element[element].addr = skb->data; 38594a71df50SFrank Blaschka buffer->element[element].length = hdr_len; 38603ec90878SJan Glauber buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; 38614a71df50SFrank Blaschka buf->next_element_to_fill++; 38624a71df50SFrank Blaschka skb->data += hdr_len; 38634a71df50SFrank Blaschka skb->len -= hdr_len; 38644a71df50SFrank Blaschka large_send = 1; 38654a71df50SFrank Blaschka } 3866683d718aSFrank Blaschka 3867683d718aSFrank Blaschka if (offset >= 0) { 3868683d718aSFrank Blaschka int element = buf->next_element_to_fill; 3869683d718aSFrank Blaschka buffer->element[element].addr = hdr; 3870683d718aSFrank Blaschka buffer->element[element].length = sizeof(struct qeth_hdr) + 3871683d718aSFrank Blaschka hd_len; 38723ec90878SJan Glauber buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; 3873683d718aSFrank Blaschka buf->is_header[element] = 1; 3874683d718aSFrank Blaschka buf->next_element_to_fill++; 3875683d718aSFrank Blaschka } 3876683d718aSFrank Blaschka 38774a71df50SFrank Blaschka __qeth_fill_buffer(skb, buffer, large_send, 3878683d718aSFrank Blaschka (int *)&buf->next_element_to_fill, offset); 38794a71df50SFrank Blaschka 38804a71df50SFrank Blaschka if (!queue->do_pack) { 3881847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfnp"); 38824a71df50SFrank Blaschka /* set state to PRIMED -> will be flushed */ 38834a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 38844a71df50SFrank Blaschka flush_cnt = 1; 38854a71df50SFrank Blaschka } else { 3886847a50fdSCarsten Otte QETH_CARD_TEXT(queue->card, 6, "fillbfpa"); 38874a71df50SFrank Blaschka if (queue->card->options.performance_stats) 38884a71df50SFrank Blaschka queue->card->perf_stats.skbs_sent_pack++; 38894a71df50SFrank Blaschka if (buf->next_element_to_fill >= 38904a71df50SFrank Blaschka QETH_MAX_BUFFER_ELEMENTS(queue->card)) { 38914a71df50SFrank Blaschka /* 38924a71df50SFrank Blaschka * packed buffer if full -> set state PRIMED 38934a71df50SFrank Blaschka * -> will be flushed 38944a71df50SFrank Blaschka */ 38954a71df50SFrank Blaschka atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); 38964a71df50SFrank Blaschka flush_cnt = 1; 38974a71df50SFrank Blaschka } 38984a71df50SFrank Blaschka } 38994a71df50SFrank Blaschka return flush_cnt; 39004a71df50SFrank Blaschka } 39014a71df50SFrank Blaschka 39024a71df50SFrank Blaschka int qeth_do_send_packet_fast(struct qeth_card *card, 39034a71df50SFrank Blaschka struct qeth_qdio_out_q *queue, struct sk_buff *skb, 39044a71df50SFrank Blaschka struct qeth_hdr *hdr, int elements_needed, 390564ef8957SFrank Blaschka int offset, int hd_len) 39064a71df50SFrank Blaschka { 39074a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 39084a71df50SFrank Blaschka int index; 39094a71df50SFrank Blaschka 39104a71df50SFrank Blaschka /* spin until we get the queue ... */ 39114a71df50SFrank Blaschka while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, 39124a71df50SFrank Blaschka QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); 39134a71df50SFrank Blaschka /* ... now we've got the queue */ 39144a71df50SFrank Blaschka index = queue->next_buf_to_fill; 39150da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 39164a71df50SFrank Blaschka /* 39174a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 39184a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 39194a71df50SFrank Blaschka */ 39204a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) 39214a71df50SFrank Blaschka goto out; 39224a71df50SFrank Blaschka queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % 39234a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 39244a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 3925683d718aSFrank Blaschka qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); 3926779e6e1cSJan Glauber qeth_flush_buffers(queue, index, 1); 39274a71df50SFrank Blaschka return 0; 39284a71df50SFrank Blaschka out: 39294a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 39304a71df50SFrank Blaschka return -EBUSY; 39314a71df50SFrank Blaschka } 39324a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast); 39334a71df50SFrank Blaschka 39344a71df50SFrank Blaschka int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, 39354a71df50SFrank Blaschka struct sk_buff *skb, struct qeth_hdr *hdr, 393664ef8957SFrank Blaschka int elements_needed) 39374a71df50SFrank Blaschka { 39384a71df50SFrank Blaschka struct qeth_qdio_out_buffer *buffer; 39394a71df50SFrank Blaschka int start_index; 39404a71df50SFrank Blaschka int flush_count = 0; 39414a71df50SFrank Blaschka int do_pack = 0; 39424a71df50SFrank Blaschka int tmp; 39434a71df50SFrank Blaschka int rc = 0; 39444a71df50SFrank Blaschka 39454a71df50SFrank Blaschka /* spin until we get the queue ... */ 39464a71df50SFrank Blaschka while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, 39474a71df50SFrank Blaschka QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED); 39484a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 39490da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 39504a71df50SFrank Blaschka /* 39514a71df50SFrank Blaschka * check if buffer is empty to make sure that we do not 'overtake' 39524a71df50SFrank Blaschka * ourselves and try to fill a buffer that is already primed 39534a71df50SFrank Blaschka */ 39544a71df50SFrank Blaschka if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { 39554a71df50SFrank Blaschka atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 39564a71df50SFrank Blaschka return -EBUSY; 39574a71df50SFrank Blaschka } 39584a71df50SFrank Blaschka /* check if we need to switch packing state of this queue */ 39594a71df50SFrank Blaschka qeth_switch_to_packing_if_needed(queue); 39604a71df50SFrank Blaschka if (queue->do_pack) { 39614a71df50SFrank Blaschka do_pack = 1; 39624a71df50SFrank Blaschka /* does packet fit in current buffer? */ 39634a71df50SFrank Blaschka if ((QETH_MAX_BUFFER_ELEMENTS(card) - 39644a71df50SFrank Blaschka buffer->next_element_to_fill) < elements_needed) { 39654a71df50SFrank Blaschka /* ... no -> set state PRIMED */ 396664ef8957SFrank Blaschka atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); 39674a71df50SFrank Blaschka flush_count++; 39684a71df50SFrank Blaschka queue->next_buf_to_fill = 39694a71df50SFrank Blaschka (queue->next_buf_to_fill + 1) % 39704a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 39710da9581dSEinar Lueck buffer = queue->bufs[queue->next_buf_to_fill]; 39724a71df50SFrank Blaschka /* we did a step forward, so check buffer state 39734a71df50SFrank Blaschka * again */ 39744a71df50SFrank Blaschka if (atomic_read(&buffer->state) != 39754a71df50SFrank Blaschka QETH_QDIO_BUF_EMPTY) { 3976779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, 3977779e6e1cSJan Glauber flush_count); 39784a71df50SFrank Blaschka atomic_set(&queue->state, 39794a71df50SFrank Blaschka QETH_OUT_Q_UNLOCKED); 39804a71df50SFrank Blaschka return -EBUSY; 39814a71df50SFrank Blaschka } 39824a71df50SFrank Blaschka } 39834a71df50SFrank Blaschka } 3984683d718aSFrank Blaschka tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0); 39854a71df50SFrank Blaschka queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) % 39864a71df50SFrank Blaschka QDIO_MAX_BUFFERS_PER_Q; 39874a71df50SFrank Blaschka flush_count += tmp; 39884a71df50SFrank Blaschka if (flush_count) 3989779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, flush_count); 39904a71df50SFrank Blaschka else if (!atomic_read(&queue->set_pci_flags_count)) 39914a71df50SFrank Blaschka atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH); 39924a71df50SFrank Blaschka /* 39934a71df50SFrank Blaschka * queue->state will go from LOCKED -> UNLOCKED or from 39944a71df50SFrank Blaschka * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us 39954a71df50SFrank Blaschka * (switch packing state or flush buffer to get another pci flag out). 39964a71df50SFrank Blaschka * In that case we will enter this loop 39974a71df50SFrank Blaschka */ 39984a71df50SFrank Blaschka while (atomic_dec_return(&queue->state)) { 39994a71df50SFrank Blaschka flush_count = 0; 40004a71df50SFrank Blaschka start_index = queue->next_buf_to_fill; 40014a71df50SFrank Blaschka /* check if we can go back to non-packing state */ 40024a71df50SFrank Blaschka flush_count += qeth_switch_to_nonpacking_if_needed(queue); 40034a71df50SFrank Blaschka /* 40044a71df50SFrank Blaschka * check if we need to flush a packing buffer to get a pci 40054a71df50SFrank Blaschka * flag out on the queue 40064a71df50SFrank Blaschka */ 40074a71df50SFrank Blaschka if (!flush_count && !atomic_read(&queue->set_pci_flags_count)) 40084a71df50SFrank Blaschka flush_count += qeth_flush_buffers_on_no_pci(queue); 40094a71df50SFrank Blaschka if (flush_count) 4010779e6e1cSJan Glauber qeth_flush_buffers(queue, start_index, flush_count); 40114a71df50SFrank Blaschka } 40124a71df50SFrank Blaschka /* at this point the queue is UNLOCKED again */ 40134a71df50SFrank Blaschka if (queue->card->options.performance_stats && do_pack) 40144a71df50SFrank Blaschka queue->card->perf_stats.bufs_sent_pack += flush_count; 40154a71df50SFrank Blaschka 40164a71df50SFrank Blaschka return rc; 40174a71df50SFrank Blaschka } 40184a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_do_send_packet); 40194a71df50SFrank Blaschka 40204a71df50SFrank Blaschka static int qeth_setadp_promisc_mode_cb(struct qeth_card *card, 40214a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 40224a71df50SFrank Blaschka { 40234a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 40244a71df50SFrank Blaschka struct qeth_ipacmd_setadpparms *setparms; 40254a71df50SFrank Blaschka 4026847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "prmadpcb"); 40274a71df50SFrank Blaschka 40284a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 40294a71df50SFrank Blaschka setparms = &(cmd->data.setadapterparms); 40304a71df50SFrank Blaschka 40314a71df50SFrank Blaschka qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd); 40324a71df50SFrank Blaschka if (cmd->hdr.return_code) { 4033847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "prmrc%2.2x", cmd->hdr.return_code); 40344a71df50SFrank Blaschka setparms->data.mode = SET_PROMISC_MODE_OFF; 40354a71df50SFrank Blaschka } 40364a71df50SFrank Blaschka card->info.promisc_mode = setparms->data.mode; 40374a71df50SFrank Blaschka return 0; 40384a71df50SFrank Blaschka } 40394a71df50SFrank Blaschka 40404a71df50SFrank Blaschka void qeth_setadp_promisc_mode(struct qeth_card *card) 40414a71df50SFrank Blaschka { 40424a71df50SFrank Blaschka enum qeth_ipa_promisc_modes mode; 40434a71df50SFrank Blaschka struct net_device *dev = card->dev; 40444a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 40454a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 40464a71df50SFrank Blaschka 4047847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setprom"); 40484a71df50SFrank Blaschka 40494a71df50SFrank Blaschka if (((dev->flags & IFF_PROMISC) && 40504a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_ON)) || 40514a71df50SFrank Blaschka (!(dev->flags & IFF_PROMISC) && 40524a71df50SFrank Blaschka (card->info.promisc_mode == SET_PROMISC_MODE_OFF))) 40534a71df50SFrank Blaschka return; 40544a71df50SFrank Blaschka mode = SET_PROMISC_MODE_OFF; 40554a71df50SFrank Blaschka if (dev->flags & IFF_PROMISC) 40564a71df50SFrank Blaschka mode = SET_PROMISC_MODE_ON; 4057847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "mode:%x", mode); 40584a71df50SFrank Blaschka 40594a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE, 40604a71df50SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms)); 40614a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE); 40624a71df50SFrank Blaschka cmd->data.setadapterparms.data.mode = mode; 40634a71df50SFrank Blaschka qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL); 40644a71df50SFrank Blaschka } 40654a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode); 40664a71df50SFrank Blaschka 40674a71df50SFrank Blaschka int qeth_change_mtu(struct net_device *dev, int new_mtu) 40684a71df50SFrank Blaschka { 40694a71df50SFrank Blaschka struct qeth_card *card; 40704a71df50SFrank Blaschka char dbf_text[15]; 40714a71df50SFrank Blaschka 4072509e2562SHeiko Carstens card = dev->ml_priv; 40734a71df50SFrank Blaschka 4074847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmtu"); 40754a71df50SFrank Blaschka sprintf(dbf_text, "%8x", new_mtu); 4076847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, dbf_text); 40774a71df50SFrank Blaschka 40784a71df50SFrank Blaschka if (new_mtu < 64) 40794a71df50SFrank Blaschka return -EINVAL; 40804a71df50SFrank Blaschka if (new_mtu > 65535) 40814a71df50SFrank Blaschka return -EINVAL; 40824a71df50SFrank Blaschka if ((!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) && 40834a71df50SFrank Blaschka (!qeth_mtu_is_valid(card, new_mtu))) 40844a71df50SFrank Blaschka return -EINVAL; 40854a71df50SFrank Blaschka dev->mtu = new_mtu; 40864a71df50SFrank Blaschka return 0; 40874a71df50SFrank Blaschka } 40884a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_change_mtu); 40894a71df50SFrank Blaschka 40904a71df50SFrank Blaschka struct net_device_stats *qeth_get_stats(struct net_device *dev) 40914a71df50SFrank Blaschka { 40924a71df50SFrank Blaschka struct qeth_card *card; 40934a71df50SFrank Blaschka 4094509e2562SHeiko Carstens card = dev->ml_priv; 40954a71df50SFrank Blaschka 4096847a50fdSCarsten Otte QETH_CARD_TEXT(card, 5, "getstat"); 40974a71df50SFrank Blaschka 40984a71df50SFrank Blaschka return &card->stats; 40994a71df50SFrank Blaschka } 41004a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_get_stats); 41014a71df50SFrank Blaschka 41024a71df50SFrank Blaschka static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card, 41034a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data) 41044a71df50SFrank Blaschka { 41054a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 41064a71df50SFrank Blaschka 4107847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmaccb"); 41084a71df50SFrank Blaschka 41094a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data; 41104a71df50SFrank Blaschka if (!card->options.layer2 || 41114a71df50SFrank Blaschka !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) { 41124a71df50SFrank Blaschka memcpy(card->dev->dev_addr, 41134a71df50SFrank Blaschka &cmd->data.setadapterparms.data.change_addr.addr, 41144a71df50SFrank Blaschka OSA_ADDR_LEN); 41154a71df50SFrank Blaschka card->info.mac_bits |= QETH_LAYER2_MAC_READ; 41164a71df50SFrank Blaschka } 41174a71df50SFrank Blaschka qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 41184a71df50SFrank Blaschka return 0; 41194a71df50SFrank Blaschka } 41204a71df50SFrank Blaschka 41214a71df50SFrank Blaschka int qeth_setadpparms_change_macaddr(struct qeth_card *card) 41224a71df50SFrank Blaschka { 41234a71df50SFrank Blaschka int rc; 41244a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 41254a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 41264a71df50SFrank Blaschka 4127847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "chgmac"); 41284a71df50SFrank Blaschka 41294a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_ALTER_MAC_ADDRESS, 41304a71df50SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms)); 41314a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 41324a71df50SFrank Blaschka cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; 41334a71df50SFrank Blaschka cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN; 41344a71df50SFrank Blaschka memcpy(&cmd->data.setadapterparms.data.change_addr.addr, 41354a71df50SFrank Blaschka card->dev->dev_addr, OSA_ADDR_LEN); 41364a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_change_macaddr_cb, 41374a71df50SFrank Blaschka NULL); 41384a71df50SFrank Blaschka return rc; 41394a71df50SFrank Blaschka } 41404a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr); 41414a71df50SFrank Blaschka 4142d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card, 4143d64ecc22SEinar Lueck struct qeth_reply *reply, unsigned long data) 4144d64ecc22SEinar Lueck { 4145d64ecc22SEinar Lueck struct qeth_ipa_cmd *cmd; 4146d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 41470f54761dSStefan Raspl int fallback = *(int *)reply->param; 4148d64ecc22SEinar Lueck 4149847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setaccb"); 4150d64ecc22SEinar Lueck 4151d64ecc22SEinar Lueck cmd = (struct qeth_ipa_cmd *) data; 4152d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4153d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setaccb"); 4154d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4155d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", 4156d64ecc22SEinar Lueck cmd->data.setadapterparms.hdr.return_code); 41570f54761dSStefan Raspl if (cmd->data.setadapterparms.hdr.return_code != 41580f54761dSStefan Raspl SET_ACCESS_CTRL_RC_SUCCESS) 41590f54761dSStefan Raspl QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n", 41600f54761dSStefan Raspl card->gdev->dev.kobj.name, 41610f54761dSStefan Raspl access_ctrl_req->subcmd_code, 41620f54761dSStefan Raspl cmd->data.setadapterparms.hdr.return_code); 4163d64ecc22SEinar Lueck switch (cmd->data.setadapterparms.hdr.return_code) { 4164d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_SUCCESS: 4165d64ecc22SEinar Lueck if (card->options.isolation == ISOLATION_MODE_NONE) { 4166d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4167d64ecc22SEinar Lueck "QDIO data connection isolation is deactivated\n"); 4168d64ecc22SEinar Lueck } else { 4169d64ecc22SEinar Lueck dev_info(&card->gdev->dev, 4170d64ecc22SEinar Lueck "QDIO data connection isolation is activated\n"); 4171d64ecc22SEinar Lueck } 4172d64ecc22SEinar Lueck break; 41730f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED: 41740f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already " 41750f54761dSStefan Raspl "deactivated\n", dev_name(&card->gdev->dev)); 41760f54761dSStefan Raspl if (fallback) 41770f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 41780f54761dSStefan Raspl break; 41790f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED: 41800f54761dSStefan Raspl QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already" 41810f54761dSStefan Raspl " activated\n", dev_name(&card->gdev->dev)); 41820f54761dSStefan Raspl if (fallback) 41830f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 41840f54761dSStefan Raspl break; 4185d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NOT_SUPPORTED: 4186d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4187d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4188d64ecc22SEinar Lueck break; 4189d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER: 4190d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4191d64ecc22SEinar Lueck "Adapter is dedicated. " 4192d64ecc22SEinar Lueck "QDIO data connection isolation not supported\n"); 41930f54761dSStefan Raspl if (fallback) 41940f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4195d64ecc22SEinar Lueck break; 4196d64ecc22SEinar Lueck case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF: 4197d64ecc22SEinar Lueck dev_err(&card->gdev->dev, 4198d64ecc22SEinar Lueck "TSO does not permit QDIO data connection isolation\n"); 41990f54761dSStefan Raspl if (fallback) 42000f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4201d64ecc22SEinar Lueck break; 42020f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED: 42030f54761dSStefan Raspl dev_err(&card->gdev->dev, "The adjacent switch port does not " 42040f54761dSStefan Raspl "support reflective relay mode\n"); 42050f54761dSStefan Raspl if (fallback) 42060f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 42070f54761dSStefan Raspl break; 42080f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_FAILED: 42090f54761dSStefan Raspl dev_err(&card->gdev->dev, "The reflective relay mode cannot be " 42100f54761dSStefan Raspl "enabled at the adjacent switch port"); 42110f54761dSStefan Raspl if (fallback) 42120f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 42130f54761dSStefan Raspl break; 42140f54761dSStefan Raspl case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED: 42150f54761dSStefan Raspl dev_warn(&card->gdev->dev, "Turning off reflective relay mode " 42160f54761dSStefan Raspl "at the adjacent switch failed\n"); 42170f54761dSStefan Raspl break; 4218d64ecc22SEinar Lueck default: 4219d64ecc22SEinar Lueck /* this should never happen */ 42200f54761dSStefan Raspl if (fallback) 42210f54761dSStefan Raspl card->options.isolation = card->options.prev_isolation; 4222d64ecc22SEinar Lueck break; 4223d64ecc22SEinar Lueck } 4224d64ecc22SEinar Lueck qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd); 4225bbb822a8SUrsula Braun return 0; 4226d64ecc22SEinar Lueck } 4227d64ecc22SEinar Lueck 4228d64ecc22SEinar Lueck static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, 42290f54761dSStefan Raspl enum qeth_ipa_isolation_modes isolation, int fallback) 4230d64ecc22SEinar Lueck { 4231d64ecc22SEinar Lueck int rc; 4232d64ecc22SEinar Lueck struct qeth_cmd_buffer *iob; 4233d64ecc22SEinar Lueck struct qeth_ipa_cmd *cmd; 4234d64ecc22SEinar Lueck struct qeth_set_access_ctrl *access_ctrl_req; 4235d64ecc22SEinar Lueck 4236847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setacctl"); 4237d64ecc22SEinar Lueck 4238d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "setacctl"); 4239d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name); 4240d64ecc22SEinar Lueck 4241d64ecc22SEinar Lueck iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, 4242d64ecc22SEinar Lueck sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4243d64ecc22SEinar Lueck sizeof(struct qeth_set_access_ctrl)); 4244d64ecc22SEinar Lueck cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 4245d64ecc22SEinar Lueck access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; 4246d64ecc22SEinar Lueck access_ctrl_req->subcmd_code = isolation; 4247d64ecc22SEinar Lueck 4248d64ecc22SEinar Lueck rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb, 42490f54761dSStefan Raspl &fallback); 4250d64ecc22SEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc); 4251d64ecc22SEinar Lueck return rc; 4252d64ecc22SEinar Lueck } 4253d64ecc22SEinar Lueck 42540f54761dSStefan Raspl int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback) 4255d64ecc22SEinar Lueck { 4256d64ecc22SEinar Lueck int rc = 0; 4257d64ecc22SEinar Lueck 4258847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setactlo"); 4259d64ecc22SEinar Lueck 42605113fec0SUrsula Braun if ((card->info.type == QETH_CARD_TYPE_OSD || 42615113fec0SUrsula Braun card->info.type == QETH_CARD_TYPE_OSX) && 4262d64ecc22SEinar Lueck qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) { 4263d64ecc22SEinar Lueck rc = qeth_setadpparms_set_access_ctrl(card, 42640f54761dSStefan Raspl card->options.isolation, fallback); 4265d64ecc22SEinar Lueck if (rc) { 4266d64ecc22SEinar Lueck QETH_DBF_MESSAGE(3, 42675113fec0SUrsula Braun "IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n", 4268d64ecc22SEinar Lueck card->gdev->dev.kobj.name, 4269d64ecc22SEinar Lueck rc); 42700f54761dSStefan Raspl rc = -EOPNOTSUPP; 4271d64ecc22SEinar Lueck } 4272d64ecc22SEinar Lueck } else if (card->options.isolation != ISOLATION_MODE_NONE) { 4273d64ecc22SEinar Lueck card->options.isolation = ISOLATION_MODE_NONE; 4274d64ecc22SEinar Lueck 4275d64ecc22SEinar Lueck dev_err(&card->gdev->dev, "Adapter does not " 4276d64ecc22SEinar Lueck "support QDIO data connection isolation\n"); 4277d64ecc22SEinar Lueck rc = -EOPNOTSUPP; 4278d64ecc22SEinar Lueck } 4279d64ecc22SEinar Lueck return rc; 4280d64ecc22SEinar Lueck } 4281d64ecc22SEinar Lueck EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online); 4282d64ecc22SEinar Lueck 42834a71df50SFrank Blaschka void qeth_tx_timeout(struct net_device *dev) 42844a71df50SFrank Blaschka { 42854a71df50SFrank Blaschka struct qeth_card *card; 42864a71df50SFrank Blaschka 4287509e2562SHeiko Carstens card = dev->ml_priv; 4288847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "txtimeo"); 42894a71df50SFrank Blaschka card->stats.tx_errors++; 42904a71df50SFrank Blaschka qeth_schedule_recovery(card); 42914a71df50SFrank Blaschka } 42924a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_tx_timeout); 42934a71df50SFrank Blaschka 42944a71df50SFrank Blaschka int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) 42954a71df50SFrank Blaschka { 4296509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 42974a71df50SFrank Blaschka int rc = 0; 42984a71df50SFrank Blaschka 42994a71df50SFrank Blaschka switch (regnum) { 43004a71df50SFrank Blaschka case MII_BMCR: /* Basic mode control register */ 43014a71df50SFrank Blaschka rc = BMCR_FULLDPLX; 43024a71df50SFrank Blaschka if ((card->info.link_type != QETH_LINK_TYPE_GBIT_ETH) && 43034a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_OSN) && 43044a71df50SFrank Blaschka (card->info.link_type != QETH_LINK_TYPE_10GBIT_ETH)) 43054a71df50SFrank Blaschka rc |= BMCR_SPEED100; 43064a71df50SFrank Blaschka break; 43074a71df50SFrank Blaschka case MII_BMSR: /* Basic mode status register */ 43084a71df50SFrank Blaschka rc = BMSR_ERCAP | BMSR_ANEGCOMPLETE | BMSR_LSTATUS | 43094a71df50SFrank Blaschka BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL | 43104a71df50SFrank Blaschka BMSR_100BASE4; 43114a71df50SFrank Blaschka break; 43124a71df50SFrank Blaschka case MII_PHYSID1: /* PHYS ID 1 */ 43134a71df50SFrank Blaschka rc = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 8) | 43144a71df50SFrank Blaschka dev->dev_addr[2]; 43154a71df50SFrank Blaschka rc = (rc >> 5) & 0xFFFF; 43164a71df50SFrank Blaschka break; 43174a71df50SFrank Blaschka case MII_PHYSID2: /* PHYS ID 2 */ 43184a71df50SFrank Blaschka rc = (dev->dev_addr[2] << 10) & 0xFFFF; 43194a71df50SFrank Blaschka break; 43204a71df50SFrank Blaschka case MII_ADVERTISE: /* Advertisement control reg */ 43214a71df50SFrank Blaschka rc = ADVERTISE_ALL; 43224a71df50SFrank Blaschka break; 43234a71df50SFrank Blaschka case MII_LPA: /* Link partner ability reg */ 43244a71df50SFrank Blaschka rc = LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL | 43254a71df50SFrank Blaschka LPA_100BASE4 | LPA_LPACK; 43264a71df50SFrank Blaschka break; 43274a71df50SFrank Blaschka case MII_EXPANSION: /* Expansion register */ 43284a71df50SFrank Blaschka break; 43294a71df50SFrank Blaschka case MII_DCOUNTER: /* disconnect counter */ 43304a71df50SFrank Blaschka break; 43314a71df50SFrank Blaschka case MII_FCSCOUNTER: /* false carrier counter */ 43324a71df50SFrank Blaschka break; 43334a71df50SFrank Blaschka case MII_NWAYTEST: /* N-way auto-neg test register */ 43344a71df50SFrank Blaschka break; 43354a71df50SFrank Blaschka case MII_RERRCOUNTER: /* rx error counter */ 43364a71df50SFrank Blaschka rc = card->stats.rx_errors; 43374a71df50SFrank Blaschka break; 43384a71df50SFrank Blaschka case MII_SREVISION: /* silicon revision */ 43394a71df50SFrank Blaschka break; 43404a71df50SFrank Blaschka case MII_RESV1: /* reserved 1 */ 43414a71df50SFrank Blaschka break; 43424a71df50SFrank Blaschka case MII_LBRERROR: /* loopback, rx, bypass error */ 43434a71df50SFrank Blaschka break; 43444a71df50SFrank Blaschka case MII_PHYADDR: /* physical address */ 43454a71df50SFrank Blaschka break; 43464a71df50SFrank Blaschka case MII_RESV2: /* reserved 2 */ 43474a71df50SFrank Blaschka break; 43484a71df50SFrank Blaschka case MII_TPISTATUS: /* TPI status for 10mbps */ 43494a71df50SFrank Blaschka break; 43504a71df50SFrank Blaschka case MII_NCONFIG: /* network interface config */ 43514a71df50SFrank Blaschka break; 43524a71df50SFrank Blaschka default: 43534a71df50SFrank Blaschka break; 43544a71df50SFrank Blaschka } 43554a71df50SFrank Blaschka return rc; 43564a71df50SFrank Blaschka } 43574a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_mdio_read); 43584a71df50SFrank Blaschka 43594a71df50SFrank Blaschka static int qeth_send_ipa_snmp_cmd(struct qeth_card *card, 43604a71df50SFrank Blaschka struct qeth_cmd_buffer *iob, int len, 43614a71df50SFrank Blaschka int (*reply_cb)(struct qeth_card *, struct qeth_reply *, 43624a71df50SFrank Blaschka unsigned long), 43634a71df50SFrank Blaschka void *reply_param) 43644a71df50SFrank Blaschka { 43654a71df50SFrank Blaschka u16 s1, s2; 43664a71df50SFrank Blaschka 4367847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "sendsnmp"); 43684a71df50SFrank Blaschka 43694a71df50SFrank Blaschka memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE); 43704a71df50SFrank Blaschka memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data), 43714a71df50SFrank Blaschka &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH); 43724a71df50SFrank Blaschka /* adjust PDU length fields in IPA_PDU_HEADER */ 43734a71df50SFrank Blaschka s1 = (u32) IPA_PDU_HEADER_SIZE + len; 43744a71df50SFrank Blaschka s2 = (u32) len; 43754a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2); 43764a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2); 43774a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2); 43784a71df50SFrank Blaschka memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2); 43794a71df50SFrank Blaschka return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob, 43804a71df50SFrank Blaschka reply_cb, reply_param); 43814a71df50SFrank Blaschka } 43824a71df50SFrank Blaschka 43834a71df50SFrank Blaschka static int qeth_snmp_command_cb(struct qeth_card *card, 43844a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long sdata) 43854a71df50SFrank Blaschka { 43864a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 43874a71df50SFrank Blaschka struct qeth_arp_query_info *qinfo; 43884a71df50SFrank Blaschka struct qeth_snmp_cmd *snmp; 43894a71df50SFrank Blaschka unsigned char *data; 43904a71df50SFrank Blaschka __u16 data_len; 43914a71df50SFrank Blaschka 4392847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snpcmdcb"); 43934a71df50SFrank Blaschka 43944a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) sdata; 43954a71df50SFrank Blaschka data = (unsigned char *)((char *)cmd - reply->offset); 43964a71df50SFrank Blaschka qinfo = (struct qeth_arp_query_info *) reply->param; 43974a71df50SFrank Blaschka snmp = &cmd->data.setadapterparms.data.snmp; 43984a71df50SFrank Blaschka 43994a71df50SFrank Blaschka if (cmd->hdr.return_code) { 4400847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "scer1%i", cmd->hdr.return_code); 44014a71df50SFrank Blaschka return 0; 44024a71df50SFrank Blaschka } 44034a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.return_code) { 44044a71df50SFrank Blaschka cmd->hdr.return_code = 44054a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.return_code; 4406847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "scer2%i", cmd->hdr.return_code); 44074a71df50SFrank Blaschka return 0; 44084a71df50SFrank Blaschka } 44094a71df50SFrank Blaschka data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data)); 44104a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) 44114a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->data - (char *)cmd); 44124a71df50SFrank Blaschka else 44134a71df50SFrank Blaschka data_len -= (__u16)((char *)&snmp->request - (char *)cmd); 44144a71df50SFrank Blaschka 44154a71df50SFrank Blaschka /* check if there is enough room in userspace */ 44164a71df50SFrank Blaschka if ((qinfo->udata_len - qinfo->udata_offset) < data_len) { 4417847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM); 4418e0a8114cSUrsula Braun cmd->hdr.return_code = IPA_RC_ENOMEM; 44194a71df50SFrank Blaschka return 0; 44204a71df50SFrank Blaschka } 4421847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "snore%i", 44224a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4423847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "sseqn%i", 44244a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 44254a71df50SFrank Blaschka /*copy entries to user buffer*/ 44264a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no == 1) { 44274a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 44284a71df50SFrank Blaschka (char *)snmp, 44294a71df50SFrank Blaschka data_len + offsetof(struct qeth_snmp_cmd, data)); 44304a71df50SFrank Blaschka qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data); 44314a71df50SFrank Blaschka } else { 44324a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset, 44334a71df50SFrank Blaschka (char *)&snmp->request, data_len); 44344a71df50SFrank Blaschka } 44354a71df50SFrank Blaschka qinfo->udata_offset += data_len; 44364a71df50SFrank Blaschka /* check if all replies received ... */ 4437847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srtot%i", 44384a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total); 4439847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 4, "srseq%i", 44404a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.seq_no); 44414a71df50SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 44424a71df50SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 44434a71df50SFrank Blaschka return 1; 44444a71df50SFrank Blaschka return 0; 44454a71df50SFrank Blaschka } 44464a71df50SFrank Blaschka 44474a71df50SFrank Blaschka int qeth_snmp_command(struct qeth_card *card, char __user *udata) 44484a71df50SFrank Blaschka { 44494a71df50SFrank Blaschka struct qeth_cmd_buffer *iob; 44504a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd; 44514a71df50SFrank Blaschka struct qeth_snmp_ureq *ureq; 44524a71df50SFrank Blaschka int req_len; 44534a71df50SFrank Blaschka struct qeth_arp_query_info qinfo = {0, }; 44544a71df50SFrank Blaschka int rc = 0; 44554a71df50SFrank Blaschka 4456847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "snmpcmd"); 44574a71df50SFrank Blaschka 44584a71df50SFrank Blaschka if (card->info.guestlan) 44594a71df50SFrank Blaschka return -EOPNOTSUPP; 44604a71df50SFrank Blaschka 44614a71df50SFrank Blaschka if ((!qeth_adp_supported(card, IPA_SETADP_SET_SNMP_CONTROL)) && 44624a71df50SFrank Blaschka (!card->options.layer2)) { 44634a71df50SFrank Blaschka return -EOPNOTSUPP; 44644a71df50SFrank Blaschka } 44654a71df50SFrank Blaschka /* skip 4 bytes (data_len struct member) to get req_len */ 44664a71df50SFrank Blaschka if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int))) 44674a71df50SFrank Blaschka return -EFAULT; 44684986f3f0SJulia Lawall ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr)); 44694986f3f0SJulia Lawall if (IS_ERR(ureq)) { 4470847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "snmpnome"); 44714986f3f0SJulia Lawall return PTR_ERR(ureq); 44724a71df50SFrank Blaschka } 44734a71df50SFrank Blaschka qinfo.udata_len = ureq->hdr.data_len; 44744a71df50SFrank Blaschka qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); 44754a71df50SFrank Blaschka if (!qinfo.udata) { 44764a71df50SFrank Blaschka kfree(ureq); 44774a71df50SFrank Blaschka return -ENOMEM; 44784a71df50SFrank Blaschka } 44794a71df50SFrank Blaschka qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr); 44804a71df50SFrank Blaschka 44814a71df50SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, 44824a71df50SFrank Blaschka QETH_SNMP_SETADP_CMDLENGTH + req_len); 44834a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 44844a71df50SFrank Blaschka memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len); 44854a71df50SFrank Blaschka rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len, 44864a71df50SFrank Blaschka qeth_snmp_command_cb, (void *)&qinfo); 44874a71df50SFrank Blaschka if (rc) 448814cc21b6SFrank Blaschka QETH_DBF_MESSAGE(2, "SNMP command failed on %s: (0x%x)\n", 44894a71df50SFrank Blaschka QETH_CARD_IFNAME(card), rc); 44904a71df50SFrank Blaschka else { 44914a71df50SFrank Blaschka if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) 44924a71df50SFrank Blaschka rc = -EFAULT; 44934a71df50SFrank Blaschka } 44944a71df50SFrank Blaschka 44954a71df50SFrank Blaschka kfree(ureq); 44964a71df50SFrank Blaschka kfree(qinfo.udata); 44974a71df50SFrank Blaschka return rc; 44984a71df50SFrank Blaschka } 44994a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_snmp_command); 45004a71df50SFrank Blaschka 4501c3ab96f3SFrank Blaschka static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, 4502c3ab96f3SFrank Blaschka struct qeth_reply *reply, unsigned long data) 4503c3ab96f3SFrank Blaschka { 4504c3ab96f3SFrank Blaschka struct qeth_ipa_cmd *cmd; 4505c3ab96f3SFrank Blaschka struct qeth_qoat_priv *priv; 4506c3ab96f3SFrank Blaschka char *resdata; 4507c3ab96f3SFrank Blaschka int resdatalen; 4508c3ab96f3SFrank Blaschka 4509c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcb"); 4510c3ab96f3SFrank Blaschka 4511c3ab96f3SFrank Blaschka cmd = (struct qeth_ipa_cmd *)data; 4512c3ab96f3SFrank Blaschka priv = (struct qeth_qoat_priv *)reply->param; 4513c3ab96f3SFrank Blaschka resdatalen = cmd->data.setadapterparms.hdr.cmdlength; 4514c3ab96f3SFrank Blaschka resdata = (char *)data + 28; 4515c3ab96f3SFrank Blaschka 4516c3ab96f3SFrank Blaschka if (resdatalen > (priv->buffer_len - priv->response_len)) { 4517c3ab96f3SFrank Blaschka cmd->hdr.return_code = IPA_RC_FFFF; 4518c3ab96f3SFrank Blaschka return 0; 4519c3ab96f3SFrank Blaschka } 4520c3ab96f3SFrank Blaschka 4521c3ab96f3SFrank Blaschka memcpy((priv->buffer + priv->response_len), resdata, 4522c3ab96f3SFrank Blaschka resdatalen); 4523c3ab96f3SFrank Blaschka priv->response_len += resdatalen; 4524c3ab96f3SFrank Blaschka 4525c3ab96f3SFrank Blaschka if (cmd->data.setadapterparms.hdr.seq_no < 4526c3ab96f3SFrank Blaschka cmd->data.setadapterparms.hdr.used_total) 4527c3ab96f3SFrank Blaschka return 1; 4528c3ab96f3SFrank Blaschka return 0; 4529c3ab96f3SFrank Blaschka } 4530c3ab96f3SFrank Blaschka 4531c3ab96f3SFrank Blaschka int qeth_query_oat_command(struct qeth_card *card, char __user *udata) 4532c3ab96f3SFrank Blaschka { 4533c3ab96f3SFrank Blaschka int rc = 0; 4534c3ab96f3SFrank Blaschka struct qeth_cmd_buffer *iob; 4535c3ab96f3SFrank Blaschka struct qeth_ipa_cmd *cmd; 4536c3ab96f3SFrank Blaschka struct qeth_query_oat *oat_req; 4537c3ab96f3SFrank Blaschka struct qeth_query_oat_data oat_data; 4538c3ab96f3SFrank Blaschka struct qeth_qoat_priv priv; 4539c3ab96f3SFrank Blaschka void __user *tmp; 4540c3ab96f3SFrank Blaschka 4541c3ab96f3SFrank Blaschka QETH_CARD_TEXT(card, 3, "qoatcmd"); 4542c3ab96f3SFrank Blaschka 4543c3ab96f3SFrank Blaschka if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { 4544c3ab96f3SFrank Blaschka rc = -EOPNOTSUPP; 4545c3ab96f3SFrank Blaschka goto out; 4546c3ab96f3SFrank Blaschka } 4547c3ab96f3SFrank Blaschka 4548c3ab96f3SFrank Blaschka if (copy_from_user(&oat_data, udata, 4549c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) { 4550c3ab96f3SFrank Blaschka rc = -EFAULT; 4551c3ab96f3SFrank Blaschka goto out; 4552c3ab96f3SFrank Blaschka } 4553c3ab96f3SFrank Blaschka 4554c3ab96f3SFrank Blaschka priv.buffer_len = oat_data.buffer_len; 4555c3ab96f3SFrank Blaschka priv.response_len = 0; 4556c3ab96f3SFrank Blaschka priv.buffer = kzalloc(oat_data.buffer_len, GFP_KERNEL); 4557c3ab96f3SFrank Blaschka if (!priv.buffer) { 4558c3ab96f3SFrank Blaschka rc = -ENOMEM; 4559c3ab96f3SFrank Blaschka goto out; 4560c3ab96f3SFrank Blaschka } 4561c3ab96f3SFrank Blaschka 4562c3ab96f3SFrank Blaschka iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, 4563c3ab96f3SFrank Blaschka sizeof(struct qeth_ipacmd_setadpparms_hdr) + 4564c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat)); 4565c3ab96f3SFrank Blaschka cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); 4566c3ab96f3SFrank Blaschka oat_req = &cmd->data.setadapterparms.data.query_oat; 4567c3ab96f3SFrank Blaschka oat_req->subcmd_code = oat_data.command; 4568c3ab96f3SFrank Blaschka 4569c3ab96f3SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb, 4570c3ab96f3SFrank Blaschka &priv); 4571c3ab96f3SFrank Blaschka if (!rc) { 4572c3ab96f3SFrank Blaschka if (is_compat_task()) 4573c3ab96f3SFrank Blaschka tmp = compat_ptr(oat_data.ptr); 4574c3ab96f3SFrank Blaschka else 4575c3ab96f3SFrank Blaschka tmp = (void __user *)(unsigned long)oat_data.ptr; 4576c3ab96f3SFrank Blaschka 4577c3ab96f3SFrank Blaschka if (copy_to_user(tmp, priv.buffer, 4578c3ab96f3SFrank Blaschka priv.response_len)) { 4579c3ab96f3SFrank Blaschka rc = -EFAULT; 4580c3ab96f3SFrank Blaschka goto out_free; 4581c3ab96f3SFrank Blaschka } 4582c3ab96f3SFrank Blaschka 4583c3ab96f3SFrank Blaschka oat_data.response_len = priv.response_len; 4584c3ab96f3SFrank Blaschka 4585c3ab96f3SFrank Blaschka if (copy_to_user(udata, &oat_data, 4586c3ab96f3SFrank Blaschka sizeof(struct qeth_query_oat_data))) 4587c3ab96f3SFrank Blaschka rc = -EFAULT; 4588c3ab96f3SFrank Blaschka } else 4589c3ab96f3SFrank Blaschka if (rc == IPA_RC_FFFF) 4590c3ab96f3SFrank Blaschka rc = -EFAULT; 4591c3ab96f3SFrank Blaschka 4592c3ab96f3SFrank Blaschka out_free: 4593c3ab96f3SFrank Blaschka kfree(priv.buffer); 4594c3ab96f3SFrank Blaschka out: 4595c3ab96f3SFrank Blaschka return rc; 4596c3ab96f3SFrank Blaschka } 4597c3ab96f3SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_query_oat_command); 4598c3ab96f3SFrank Blaschka 45994a71df50SFrank Blaschka static inline int qeth_get_qdio_q_format(struct qeth_card *card) 46004a71df50SFrank Blaschka { 46014a71df50SFrank Blaschka switch (card->info.type) { 46024a71df50SFrank Blaschka case QETH_CARD_TYPE_IQD: 46034a71df50SFrank Blaschka return 2; 46044a71df50SFrank Blaschka default: 46054a71df50SFrank Blaschka return 0; 46064a71df50SFrank Blaschka } 46074a71df50SFrank Blaschka } 46084a71df50SFrank Blaschka 4609d0ff1f52SUrsula Braun static void qeth_determine_capabilities(struct qeth_card *card) 4610d0ff1f52SUrsula Braun { 4611d0ff1f52SUrsula Braun int rc; 4612d0ff1f52SUrsula Braun int length; 4613d0ff1f52SUrsula Braun char *prcd; 4614d0ff1f52SUrsula Braun struct ccw_device *ddev; 4615d0ff1f52SUrsula Braun int ddev_offline = 0; 4616d0ff1f52SUrsula Braun 4617d0ff1f52SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "detcapab"); 4618d0ff1f52SUrsula Braun ddev = CARD_DDEV(card); 4619d0ff1f52SUrsula Braun if (!ddev->online) { 4620d0ff1f52SUrsula Braun ddev_offline = 1; 4621d0ff1f52SUrsula Braun rc = ccw_device_set_online(ddev); 4622d0ff1f52SUrsula Braun if (rc) { 4623d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 4624d0ff1f52SUrsula Braun goto out; 4625d0ff1f52SUrsula Braun } 4626d0ff1f52SUrsula Braun } 4627d0ff1f52SUrsula Braun 4628d0ff1f52SUrsula Braun rc = qeth_read_conf_data(card, (void **) &prcd, &length); 4629d0ff1f52SUrsula Braun if (rc) { 4630d0ff1f52SUrsula Braun QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n", 4631d0ff1f52SUrsula Braun dev_name(&card->gdev->dev), rc); 4632d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 4633d0ff1f52SUrsula Braun goto out_offline; 4634d0ff1f52SUrsula Braun } 4635d0ff1f52SUrsula Braun qeth_configure_unitaddr(card, prcd); 46367e665afbSUrsula Braun if (ddev_offline) 4637d0ff1f52SUrsula Braun qeth_configure_blkt_default(card, prcd); 4638d0ff1f52SUrsula Braun kfree(prcd); 4639d0ff1f52SUrsula Braun 4640d0ff1f52SUrsula Braun rc = qdio_get_ssqd_desc(ddev, &card->ssqd); 4641d0ff1f52SUrsula Braun if (rc) 4642d0ff1f52SUrsula Braun QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); 4643d0ff1f52SUrsula Braun 46440da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt); 46450da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac1); 46460da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac3); 46470da9581dSEinar Lueck QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt); 46480da9581dSEinar Lueck if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) || 46490da9581dSEinar Lueck ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) || 46500da9581dSEinar Lueck ((card->ssqd.qdioac3 & CHSC_AC3_FORMAT2_CQ_AVAILABLE) == 0))) { 46510da9581dSEinar Lueck dev_info(&card->gdev->dev, 46520da9581dSEinar Lueck "Completion Queueing supported\n"); 46530da9581dSEinar Lueck } else { 46540da9581dSEinar Lueck card->options.cq = QETH_CQ_NOTAVAILABLE; 46550da9581dSEinar Lueck } 46560da9581dSEinar Lueck 46570da9581dSEinar Lueck 4658d0ff1f52SUrsula Braun out_offline: 4659d0ff1f52SUrsula Braun if (ddev_offline == 1) 4660d0ff1f52SUrsula Braun ccw_device_set_offline(ddev); 4661d0ff1f52SUrsula Braun out: 4662d0ff1f52SUrsula Braun return; 4663d0ff1f52SUrsula Braun } 4664d0ff1f52SUrsula Braun 46650da9581dSEinar Lueck static inline void qeth_qdio_establish_cq(struct qeth_card *card, 46660da9581dSEinar Lueck struct qdio_buffer **in_sbal_ptrs, 46670da9581dSEinar Lueck void (**queue_start_poll) (struct ccw_device *, int, unsigned long)) { 46680da9581dSEinar Lueck int i; 46690da9581dSEinar Lueck 46700da9581dSEinar Lueck if (card->options.cq == QETH_CQ_ENABLED) { 46710da9581dSEinar Lueck int offset = QDIO_MAX_BUFFERS_PER_Q * 46720da9581dSEinar Lueck (card->qdio.no_in_queues - 1); 46730da9581dSEinar Lueck i = QDIO_MAX_BUFFERS_PER_Q * (card->qdio.no_in_queues - 1); 46740da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 46750da9581dSEinar Lueck in_sbal_ptrs[offset + i] = (struct qdio_buffer *) 46760da9581dSEinar Lueck virt_to_phys(card->qdio.c_q->bufs[i].buffer); 46770da9581dSEinar Lueck } 46780da9581dSEinar Lueck 46790da9581dSEinar Lueck queue_start_poll[card->qdio.no_in_queues - 1] = NULL; 46800da9581dSEinar Lueck } 46810da9581dSEinar Lueck } 46820da9581dSEinar Lueck 46834a71df50SFrank Blaschka static int qeth_qdio_establish(struct qeth_card *card) 46844a71df50SFrank Blaschka { 46854a71df50SFrank Blaschka struct qdio_initialize init_data; 46864a71df50SFrank Blaschka char *qib_param_field; 46874a71df50SFrank Blaschka struct qdio_buffer **in_sbal_ptrs; 4688104ea556Sfrank.blaschka@de.ibm.com void (**queue_start_poll) (struct ccw_device *, int, unsigned long); 46894a71df50SFrank Blaschka struct qdio_buffer **out_sbal_ptrs; 46904a71df50SFrank Blaschka int i, j, k; 46914a71df50SFrank Blaschka int rc = 0; 46924a71df50SFrank Blaschka 4693d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "qdioest"); 46944a71df50SFrank Blaschka 46954a71df50SFrank Blaschka qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char), 46964a71df50SFrank Blaschka GFP_KERNEL); 4697104ea556Sfrank.blaschka@de.ibm.com if (!qib_param_field) { 4698104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4699104ea556Sfrank.blaschka@de.ibm.com goto out_free_nothing; 4700104ea556Sfrank.blaschka@de.ibm.com } 47014a71df50SFrank Blaschka 47024a71df50SFrank Blaschka qeth_create_qib_param_field(card, qib_param_field); 47034a71df50SFrank Blaschka qeth_create_qib_param_field_blkt(card, qib_param_field); 47044a71df50SFrank Blaschka 4705b3332930SFrank Blaschka in_sbal_ptrs = kzalloc(card->qdio.no_in_queues * 47060da9581dSEinar Lueck QDIO_MAX_BUFFERS_PER_Q * sizeof(void *), 47074a71df50SFrank Blaschka GFP_KERNEL); 47084a71df50SFrank Blaschka if (!in_sbal_ptrs) { 4709104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4710104ea556Sfrank.blaschka@de.ibm.com goto out_free_qib_param; 47114a71df50SFrank Blaschka } 47120da9581dSEinar Lueck for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { 47134a71df50SFrank Blaschka in_sbal_ptrs[i] = (struct qdio_buffer *) 47144a71df50SFrank Blaschka virt_to_phys(card->qdio.in_q->bufs[i].buffer); 47150da9581dSEinar Lueck } 47164a71df50SFrank Blaschka 47170da9581dSEinar Lueck queue_start_poll = kzalloc(sizeof(void *) * card->qdio.no_in_queues, 47180da9581dSEinar Lueck GFP_KERNEL); 4719104ea556Sfrank.blaschka@de.ibm.com if (!queue_start_poll) { 4720104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4721104ea556Sfrank.blaschka@de.ibm.com goto out_free_in_sbals; 4722104ea556Sfrank.blaschka@de.ibm.com } 47230da9581dSEinar Lueck for (i = 0; i < card->qdio.no_in_queues; ++i) 4724c041f2d4SSebastian Ott queue_start_poll[i] = card->discipline->start_poll; 47250da9581dSEinar Lueck 47260da9581dSEinar Lueck qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); 4727104ea556Sfrank.blaschka@de.ibm.com 47284a71df50SFrank Blaschka out_sbal_ptrs = 4729b3332930SFrank Blaschka kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * 47304a71df50SFrank Blaschka sizeof(void *), GFP_KERNEL); 47314a71df50SFrank Blaschka if (!out_sbal_ptrs) { 4732104ea556Sfrank.blaschka@de.ibm.com rc = -ENOMEM; 4733104ea556Sfrank.blaschka@de.ibm.com goto out_free_queue_start_poll; 47344a71df50SFrank Blaschka } 47354a71df50SFrank Blaschka for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i) 47364a71df50SFrank Blaschka for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k) { 47374a71df50SFrank Blaschka out_sbal_ptrs[k] = (struct qdio_buffer *)virt_to_phys( 47380da9581dSEinar Lueck card->qdio.out_qs[i]->bufs[j]->buffer); 47394a71df50SFrank Blaschka } 47404a71df50SFrank Blaschka 47414a71df50SFrank Blaschka memset(&init_data, 0, sizeof(struct qdio_initialize)); 47424a71df50SFrank Blaschka init_data.cdev = CARD_DDEV(card); 47434a71df50SFrank Blaschka init_data.q_format = qeth_get_qdio_q_format(card); 47444a71df50SFrank Blaschka init_data.qib_param_field_format = 0; 47454a71df50SFrank Blaschka init_data.qib_param_field = qib_param_field; 47460da9581dSEinar Lueck init_data.no_input_qs = card->qdio.no_in_queues; 47474a71df50SFrank Blaschka init_data.no_output_qs = card->qdio.no_out_queues; 4748c041f2d4SSebastian Ott init_data.input_handler = card->discipline->input_handler; 4749c041f2d4SSebastian Ott init_data.output_handler = card->discipline->output_handler; 4750e58b0d90SSteffen Maier init_data.queue_start_poll_array = queue_start_poll; 47514a71df50SFrank Blaschka init_data.int_parm = (unsigned long) card; 47524a71df50SFrank Blaschka init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; 47534a71df50SFrank Blaschka init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; 47540da9581dSEinar Lueck init_data.output_sbal_state_array = card->qdio.out_bufstates; 47553d6c76ffSJan Glauber init_data.scan_threshold = 47560fa81cd4SStefan Raspl (card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32; 47574a71df50SFrank Blaschka 47584a71df50SFrank Blaschka if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, 47594a71df50SFrank Blaschka QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { 4760cc961d40SJan Glauber rc = qdio_allocate(&init_data); 4761cc961d40SJan Glauber if (rc) { 47624a71df50SFrank Blaschka atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 4763cc961d40SJan Glauber goto out; 47644a71df50SFrank Blaschka } 4765cc961d40SJan Glauber rc = qdio_establish(&init_data); 4766cc961d40SJan Glauber if (rc) { 4767cc961d40SJan Glauber atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 4768cc961d40SJan Glauber qdio_free(CARD_DDEV(card)); 4769cc961d40SJan Glauber } 4770cc961d40SJan Glauber } 47710da9581dSEinar Lueck 47720da9581dSEinar Lueck switch (card->options.cq) { 47730da9581dSEinar Lueck case QETH_CQ_ENABLED: 47740da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support enabled"); 47750da9581dSEinar Lueck break; 47760da9581dSEinar Lueck case QETH_CQ_DISABLED: 47770da9581dSEinar Lueck dev_info(&card->gdev->dev, "Completion Queue support disabled"); 47780da9581dSEinar Lueck break; 47790da9581dSEinar Lueck default: 47800da9581dSEinar Lueck break; 47810da9581dSEinar Lueck } 4782cc961d40SJan Glauber out: 47834a71df50SFrank Blaschka kfree(out_sbal_ptrs); 4784104ea556Sfrank.blaschka@de.ibm.com out_free_queue_start_poll: 4785104ea556Sfrank.blaschka@de.ibm.com kfree(queue_start_poll); 4786104ea556Sfrank.blaschka@de.ibm.com out_free_in_sbals: 47874a71df50SFrank Blaschka kfree(in_sbal_ptrs); 4788104ea556Sfrank.blaschka@de.ibm.com out_free_qib_param: 47894a71df50SFrank Blaschka kfree(qib_param_field); 4790104ea556Sfrank.blaschka@de.ibm.com out_free_nothing: 47914a71df50SFrank Blaschka return rc; 47924a71df50SFrank Blaschka } 47934a71df50SFrank Blaschka 47944a71df50SFrank Blaschka static void qeth_core_free_card(struct qeth_card *card) 47954a71df50SFrank Blaschka { 47964a71df50SFrank Blaschka 4797d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "freecrd"); 4798d11ba0c4SPeter Tiedemann QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); 47994a71df50SFrank Blaschka qeth_clean_channel(&card->read); 48004a71df50SFrank Blaschka qeth_clean_channel(&card->write); 48014a71df50SFrank Blaschka if (card->dev) 48024a71df50SFrank Blaschka free_netdev(card->dev); 48034a71df50SFrank Blaschka kfree(card->ip_tbd_list); 48044a71df50SFrank Blaschka qeth_free_qdio_buffers(card); 48056bcac508SMartin Schwidefsky unregister_service_level(&card->qeth_service_level); 48064a71df50SFrank Blaschka kfree(card); 48074a71df50SFrank Blaschka } 48084a71df50SFrank Blaschka 4809395672e0SStefan Raspl void qeth_trace_features(struct qeth_card *card) 4810395672e0SStefan Raspl { 4811395672e0SStefan Raspl QETH_CARD_TEXT(card, 2, "features"); 4812395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs); 4813395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs); 4814395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs); 4815395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs); 4816395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs); 4817395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs); 4818395672e0SStefan Raspl QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support); 4819395672e0SStefan Raspl } 4820395672e0SStefan Raspl EXPORT_SYMBOL_GPL(qeth_trace_features); 4821395672e0SStefan Raspl 48224a71df50SFrank Blaschka static struct ccw_device_id qeth_ids[] = { 48235113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x01, 0x1732, 0x01), 48245113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSD}, 48255113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x05, 0x1732, 0x05), 48265113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_IQD}, 48275113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x06, 0x1732, 0x06), 48285113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSN}, 48295113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x03), 48305113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSM}, 48315113fec0SUrsula Braun {CCW_DEVICE_DEVTYPE(0x1731, 0x02, 0x1732, 0x02), 48325113fec0SUrsula Braun .driver_info = QETH_CARD_TYPE_OSX}, 48334a71df50SFrank Blaschka {}, 48344a71df50SFrank Blaschka }; 48354a71df50SFrank Blaschka MODULE_DEVICE_TABLE(ccw, qeth_ids); 48364a71df50SFrank Blaschka 48374a71df50SFrank Blaschka static struct ccw_driver qeth_ccw_driver = { 48383bda058bSSebastian Ott .driver = { 48393e70b3b8SSebastian Ott .owner = THIS_MODULE, 48404a71df50SFrank Blaschka .name = "qeth", 48413bda058bSSebastian Ott }, 48424a71df50SFrank Blaschka .ids = qeth_ids, 48434a71df50SFrank Blaschka .probe = ccwgroup_probe_ccwdev, 48444a71df50SFrank Blaschka .remove = ccwgroup_remove_ccwdev, 48454a71df50SFrank Blaschka }; 48464a71df50SFrank Blaschka 48474a71df50SFrank Blaschka int qeth_core_hardsetup_card(struct qeth_card *card) 48484a71df50SFrank Blaschka { 48496ebb7f8dSStefan Raspl int retries = 3; 48504a71df50SFrank Blaschka int rc; 48514a71df50SFrank Blaschka 4852d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "hrdsetup"); 48534a71df50SFrank Blaschka atomic_set(&card->force_alloc_skb, 0); 4854725b9c04SSebastian Ott qeth_update_from_chp_desc(card); 48554a71df50SFrank Blaschka retry: 48566ebb7f8dSStefan Raspl if (retries < 3) 485774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", 485874eacdb9SFrank Blaschka dev_name(&card->gdev->dev)); 48594a71df50SFrank Blaschka ccw_device_set_offline(CARD_DDEV(card)); 48604a71df50SFrank Blaschka ccw_device_set_offline(CARD_WDEV(card)); 48614a71df50SFrank Blaschka ccw_device_set_offline(CARD_RDEV(card)); 4862aa909224SUrsula Braun rc = ccw_device_set_online(CARD_RDEV(card)); 4863aa909224SUrsula Braun if (rc) 4864aa909224SUrsula Braun goto retriable; 4865aa909224SUrsula Braun rc = ccw_device_set_online(CARD_WDEV(card)); 4866aa909224SUrsula Braun if (rc) 4867aa909224SUrsula Braun goto retriable; 4868aa909224SUrsula Braun rc = ccw_device_set_online(CARD_DDEV(card)); 4869aa909224SUrsula Braun if (rc) 4870aa909224SUrsula Braun goto retriable; 48714a71df50SFrank Blaschka rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); 4872aa909224SUrsula Braun retriable: 48734a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 4874d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break1"); 48754a71df50SFrank Blaschka return rc; 48764a71df50SFrank Blaschka } else if (rc) { 4877d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); 48786ebb7f8dSStefan Raspl if (--retries < 0) 48794a71df50SFrank Blaschka goto out; 48804a71df50SFrank Blaschka else 48814a71df50SFrank Blaschka goto retry; 48824a71df50SFrank Blaschka } 4883d0ff1f52SUrsula Braun qeth_determine_capabilities(card); 48844a71df50SFrank Blaschka qeth_init_tokens(card); 48854a71df50SFrank Blaschka qeth_init_func_level(card); 48864a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb); 48874a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 4888d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break2"); 48894a71df50SFrank Blaschka return rc; 48904a71df50SFrank Blaschka } else if (rc) { 4891d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 48924a71df50SFrank Blaschka if (--retries < 0) 48934a71df50SFrank Blaschka goto out; 48944a71df50SFrank Blaschka else 48954a71df50SFrank Blaschka goto retry; 48964a71df50SFrank Blaschka } 48974a71df50SFrank Blaschka rc = qeth_idx_activate_channel(&card->write, qeth_idx_write_cb); 48984a71df50SFrank Blaschka if (rc == -ERESTARTSYS) { 4899d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "break3"); 49004a71df50SFrank Blaschka return rc; 49014a71df50SFrank Blaschka } else if (rc) { 4902d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); 49034a71df50SFrank Blaschka if (--retries < 0) 49044a71df50SFrank Blaschka goto out; 49054a71df50SFrank Blaschka else 49064a71df50SFrank Blaschka goto retry; 49074a71df50SFrank Blaschka } 4908908abbb5SUrsula Braun card->read_or_write_problem = 0; 49094a71df50SFrank Blaschka rc = qeth_mpc_initialize(card); 49104a71df50SFrank Blaschka if (rc) { 4911d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); 49124a71df50SFrank Blaschka goto out; 49134a71df50SFrank Blaschka } 49141da74b1cSFrank Blaschka 49151da74b1cSFrank Blaschka card->options.ipa4.supported_funcs = 0; 49161da74b1cSFrank Blaschka card->options.adp.supported_funcs = 0; 49171da74b1cSFrank Blaschka card->info.diagass_support = 0; 49181da74b1cSFrank Blaschka qeth_query_ipassists(card, QETH_PROT_IPV4); 49191da74b1cSFrank Blaschka if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) 49201da74b1cSFrank Blaschka qeth_query_setadapterparms(card); 49211da74b1cSFrank Blaschka if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) 49221da74b1cSFrank Blaschka qeth_query_setdiagass(card); 49234a71df50SFrank Blaschka return 0; 49244a71df50SFrank Blaschka out: 492574eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " 492674eacdb9SFrank Blaschka "an error on the device\n"); 492774eacdb9SFrank Blaschka QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n", 492874eacdb9SFrank Blaschka dev_name(&card->gdev->dev), rc); 49294a71df50SFrank Blaschka return rc; 49304a71df50SFrank Blaschka } 49314a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); 49324a71df50SFrank Blaschka 4933b3332930SFrank Blaschka static inline int qeth_create_skb_frag(struct qeth_qdio_buffer *qethbuffer, 4934b3332930SFrank Blaschka struct qdio_buffer_element *element, 49354a71df50SFrank Blaschka struct sk_buff **pskb, int offset, int *pfrag, int data_len) 49364a71df50SFrank Blaschka { 49374a71df50SFrank Blaschka struct page *page = virt_to_page(element->addr); 49384a71df50SFrank Blaschka if (*pskb == NULL) { 4939b3332930SFrank Blaschka if (qethbuffer->rx_skb) { 4940b3332930SFrank Blaschka /* only if qeth_card.options.cq == QETH_CQ_ENABLED */ 4941b3332930SFrank Blaschka *pskb = qethbuffer->rx_skb; 4942b3332930SFrank Blaschka qethbuffer->rx_skb = NULL; 4943b3332930SFrank Blaschka } else { 4944b3332930SFrank Blaschka *pskb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN); 49454a71df50SFrank Blaschka if (!(*pskb)) 49464a71df50SFrank Blaschka return -ENOMEM; 4947b3332930SFrank Blaschka } 4948b3332930SFrank Blaschka 49494a71df50SFrank Blaschka skb_reserve(*pskb, ETH_HLEN); 4950b3332930SFrank Blaschka if (data_len <= QETH_RX_PULL_LEN) { 49514a71df50SFrank Blaschka memcpy(skb_put(*pskb, data_len), element->addr + offset, 49524a71df50SFrank Blaschka data_len); 49534a71df50SFrank Blaschka } else { 49544a71df50SFrank Blaschka get_page(page); 4955b3332930SFrank Blaschka memcpy(skb_put(*pskb, QETH_RX_PULL_LEN), 4956b3332930SFrank Blaschka element->addr + offset, QETH_RX_PULL_LEN); 4957b3332930SFrank Blaschka skb_fill_page_desc(*pskb, *pfrag, page, 4958b3332930SFrank Blaschka offset + QETH_RX_PULL_LEN, 4959b3332930SFrank Blaschka data_len - QETH_RX_PULL_LEN); 4960b3332930SFrank Blaschka (*pskb)->data_len += data_len - QETH_RX_PULL_LEN; 4961b3332930SFrank Blaschka (*pskb)->len += data_len - QETH_RX_PULL_LEN; 4962b3332930SFrank Blaschka (*pskb)->truesize += data_len - QETH_RX_PULL_LEN; 49634a71df50SFrank Blaschka (*pfrag)++; 49644a71df50SFrank Blaschka } 49654a71df50SFrank Blaschka } else { 49664a71df50SFrank Blaschka get_page(page); 49674a71df50SFrank Blaschka skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len); 49684a71df50SFrank Blaschka (*pskb)->data_len += data_len; 49694a71df50SFrank Blaschka (*pskb)->len += data_len; 49704a71df50SFrank Blaschka (*pskb)->truesize += data_len; 49714a71df50SFrank Blaschka (*pfrag)++; 49724a71df50SFrank Blaschka } 49730da9581dSEinar Lueck 49740da9581dSEinar Lueck 49754a71df50SFrank Blaschka return 0; 49764a71df50SFrank Blaschka } 49774a71df50SFrank Blaschka 49784a71df50SFrank Blaschka struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, 4979b3332930SFrank Blaschka struct qeth_qdio_buffer *qethbuffer, 49804a71df50SFrank Blaschka struct qdio_buffer_element **__element, int *__offset, 49814a71df50SFrank Blaschka struct qeth_hdr **hdr) 49824a71df50SFrank Blaschka { 49834a71df50SFrank Blaschka struct qdio_buffer_element *element = *__element; 4984b3332930SFrank Blaschka struct qdio_buffer *buffer = qethbuffer->buffer; 49854a71df50SFrank Blaschka int offset = *__offset; 49864a71df50SFrank Blaschka struct sk_buff *skb = NULL; 498776b11f8eSUrsula Braun int skb_len = 0; 49884a71df50SFrank Blaschka void *data_ptr; 49894a71df50SFrank Blaschka int data_len; 49904a71df50SFrank Blaschka int headroom = 0; 49914a71df50SFrank Blaschka int use_rx_sg = 0; 49924a71df50SFrank Blaschka int frag = 0; 49934a71df50SFrank Blaschka 49944a71df50SFrank Blaschka /* qeth_hdr must not cross element boundaries */ 49954a71df50SFrank Blaschka if (element->length < offset + sizeof(struct qeth_hdr)) { 49964a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) 49974a71df50SFrank Blaschka return NULL; 49984a71df50SFrank Blaschka element++; 49994a71df50SFrank Blaschka offset = 0; 50004a71df50SFrank Blaschka if (element->length < sizeof(struct qeth_hdr)) 50014a71df50SFrank Blaschka return NULL; 50024a71df50SFrank Blaschka } 50034a71df50SFrank Blaschka *hdr = element->addr + offset; 50044a71df50SFrank Blaschka 50054a71df50SFrank Blaschka offset += sizeof(struct qeth_hdr); 500676b11f8eSUrsula Braun switch ((*hdr)->hdr.l2.id) { 500776b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER2: 50084a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l2.pkt_length; 500976b11f8eSUrsula Braun break; 501076b11f8eSUrsula Braun case QETH_HEADER_TYPE_LAYER3: 50114a71df50SFrank Blaschka skb_len = (*hdr)->hdr.l3.length; 5012b403e685SFrank Blaschka headroom = ETH_HLEN; 501376b11f8eSUrsula Braun break; 501476b11f8eSUrsula Braun case QETH_HEADER_TYPE_OSN: 501576b11f8eSUrsula Braun skb_len = (*hdr)->hdr.osn.pdu_length; 501676b11f8eSUrsula Braun headroom = sizeof(struct qeth_hdr); 501776b11f8eSUrsula Braun break; 501876b11f8eSUrsula Braun default: 501976b11f8eSUrsula Braun break; 50204a71df50SFrank Blaschka } 50214a71df50SFrank Blaschka 50224a71df50SFrank Blaschka if (!skb_len) 50234a71df50SFrank Blaschka return NULL; 50244a71df50SFrank Blaschka 5025b3332930SFrank Blaschka if (((skb_len >= card->options.rx_sg_cb) && 50264a71df50SFrank Blaschka (!(card->info.type == QETH_CARD_TYPE_OSN)) && 5027b3332930SFrank Blaschka (!atomic_read(&card->force_alloc_skb))) || 5028b3332930SFrank Blaschka (card->options.cq == QETH_CQ_ENABLED)) { 50294a71df50SFrank Blaschka use_rx_sg = 1; 50304a71df50SFrank Blaschka } else { 50314a71df50SFrank Blaschka skb = dev_alloc_skb(skb_len + headroom); 50324a71df50SFrank Blaschka if (!skb) 50334a71df50SFrank Blaschka goto no_mem; 50344a71df50SFrank Blaschka if (headroom) 50354a71df50SFrank Blaschka skb_reserve(skb, headroom); 50364a71df50SFrank Blaschka } 50374a71df50SFrank Blaschka 50384a71df50SFrank Blaschka data_ptr = element->addr + offset; 50394a71df50SFrank Blaschka while (skb_len) { 50404a71df50SFrank Blaschka data_len = min(skb_len, (int)(element->length - offset)); 50414a71df50SFrank Blaschka if (data_len) { 50424a71df50SFrank Blaschka if (use_rx_sg) { 5043b3332930SFrank Blaschka if (qeth_create_skb_frag(qethbuffer, element, 5044b3332930SFrank Blaschka &skb, offset, &frag, data_len)) 50454a71df50SFrank Blaschka goto no_mem; 50464a71df50SFrank Blaschka } else { 50474a71df50SFrank Blaschka memcpy(skb_put(skb, data_len), data_ptr, 50484a71df50SFrank Blaschka data_len); 50494a71df50SFrank Blaschka } 50504a71df50SFrank Blaschka } 50514a71df50SFrank Blaschka skb_len -= data_len; 50524a71df50SFrank Blaschka if (skb_len) { 50534a71df50SFrank Blaschka if (qeth_is_last_sbale(element)) { 5054847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "unexeob"); 5055efd5d9a4SCarsten Otte QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); 50564a71df50SFrank Blaschka dev_kfree_skb_any(skb); 50574a71df50SFrank Blaschka card->stats.rx_errors++; 50584a71df50SFrank Blaschka return NULL; 50594a71df50SFrank Blaschka } 50604a71df50SFrank Blaschka element++; 50614a71df50SFrank Blaschka offset = 0; 50624a71df50SFrank Blaschka data_ptr = element->addr; 50634a71df50SFrank Blaschka } else { 50644a71df50SFrank Blaschka offset += data_len; 50654a71df50SFrank Blaschka } 50664a71df50SFrank Blaschka } 50674a71df50SFrank Blaschka *__element = element; 50684a71df50SFrank Blaschka *__offset = offset; 50694a71df50SFrank Blaschka if (use_rx_sg && card->options.performance_stats) { 50704a71df50SFrank Blaschka card->perf_stats.sg_skbs_rx++; 50714a71df50SFrank Blaschka card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; 50724a71df50SFrank Blaschka } 50734a71df50SFrank Blaschka return skb; 50744a71df50SFrank Blaschka no_mem: 50754a71df50SFrank Blaschka if (net_ratelimit()) { 5076847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "noskbmem"); 50774a71df50SFrank Blaschka } 50784a71df50SFrank Blaschka card->stats.rx_dropped++; 50794a71df50SFrank Blaschka return NULL; 50804a71df50SFrank Blaschka } 50814a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); 50824a71df50SFrank Blaschka 50834a71df50SFrank Blaschka static void qeth_unregister_dbf_views(void) 50844a71df50SFrank Blaschka { 5085d11ba0c4SPeter Tiedemann int x; 5086d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5087d11ba0c4SPeter Tiedemann debug_unregister(qeth_dbf[x].id); 5088d11ba0c4SPeter Tiedemann qeth_dbf[x].id = NULL; 5089d11ba0c4SPeter Tiedemann } 50904a71df50SFrank Blaschka } 50914a71df50SFrank Blaschka 50928e96c51cSCarsten Otte void qeth_dbf_longtext(debug_info_t *id, int level, char *fmt, ...) 5093cd023216SPeter Tiedemann { 5094cd023216SPeter Tiedemann char dbf_txt_buf[32]; 5095345aa66eSPeter Tiedemann va_list args; 5096cd023216SPeter Tiedemann 50978e96c51cSCarsten Otte if (level > id->level) 5098cd023216SPeter Tiedemann return; 5099345aa66eSPeter Tiedemann va_start(args, fmt); 5100345aa66eSPeter Tiedemann vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); 5101345aa66eSPeter Tiedemann va_end(args); 51028e96c51cSCarsten Otte debug_text_event(id, level, dbf_txt_buf); 5103cd023216SPeter Tiedemann } 5104cd023216SPeter Tiedemann EXPORT_SYMBOL_GPL(qeth_dbf_longtext); 5105cd023216SPeter Tiedemann 51064a71df50SFrank Blaschka static int qeth_register_dbf_views(void) 51074a71df50SFrank Blaschka { 5108d11ba0c4SPeter Tiedemann int ret; 5109d11ba0c4SPeter Tiedemann int x; 51104a71df50SFrank Blaschka 5111d11ba0c4SPeter Tiedemann for (x = 0; x < QETH_DBF_INFOS; x++) { 5112d11ba0c4SPeter Tiedemann /* register the areas */ 5113d11ba0c4SPeter Tiedemann qeth_dbf[x].id = debug_register(qeth_dbf[x].name, 5114d11ba0c4SPeter Tiedemann qeth_dbf[x].pages, 5115d11ba0c4SPeter Tiedemann qeth_dbf[x].areas, 5116d11ba0c4SPeter Tiedemann qeth_dbf[x].len); 5117d11ba0c4SPeter Tiedemann if (qeth_dbf[x].id == NULL) { 51184a71df50SFrank Blaschka qeth_unregister_dbf_views(); 51194a71df50SFrank Blaschka return -ENOMEM; 51204a71df50SFrank Blaschka } 51214a71df50SFrank Blaschka 5122d11ba0c4SPeter Tiedemann /* register a view */ 5123d11ba0c4SPeter Tiedemann ret = debug_register_view(qeth_dbf[x].id, qeth_dbf[x].view); 5124d11ba0c4SPeter Tiedemann if (ret) { 5125d11ba0c4SPeter Tiedemann qeth_unregister_dbf_views(); 5126d11ba0c4SPeter Tiedemann return ret; 5127d11ba0c4SPeter Tiedemann } 51284a71df50SFrank Blaschka 5129d11ba0c4SPeter Tiedemann /* set a passing level */ 5130d11ba0c4SPeter Tiedemann debug_set_level(qeth_dbf[x].id, qeth_dbf[x].level); 5131d11ba0c4SPeter Tiedemann } 51324a71df50SFrank Blaschka 51334a71df50SFrank Blaschka return 0; 51344a71df50SFrank Blaschka } 51354a71df50SFrank Blaschka 51364a71df50SFrank Blaschka int qeth_core_load_discipline(struct qeth_card *card, 51374a71df50SFrank Blaschka enum qeth_discipline_id discipline) 51384a71df50SFrank Blaschka { 51394a71df50SFrank Blaschka int rc = 0; 51402022e00cSFrank Blaschka mutex_lock(&qeth_mod_mutex); 51414a71df50SFrank Blaschka switch (discipline) { 51424a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER3: 5143c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5144c041f2d4SSebastian Ott symbol_get(qeth_l3_discipline), "qeth_l3"); 51454a71df50SFrank Blaschka break; 51464a71df50SFrank Blaschka case QETH_DISCIPLINE_LAYER2: 5147c041f2d4SSebastian Ott card->discipline = try_then_request_module( 5148c041f2d4SSebastian Ott symbol_get(qeth_l2_discipline), "qeth_l2"); 51494a71df50SFrank Blaschka break; 51504a71df50SFrank Blaschka } 5151c041f2d4SSebastian Ott if (!card->discipline) { 515274eacdb9SFrank Blaschka dev_err(&card->gdev->dev, "There is no kernel module to " 515374eacdb9SFrank Blaschka "support discipline %d\n", discipline); 51544a71df50SFrank Blaschka rc = -EINVAL; 51554a71df50SFrank Blaschka } 51562022e00cSFrank Blaschka mutex_unlock(&qeth_mod_mutex); 51574a71df50SFrank Blaschka return rc; 51584a71df50SFrank Blaschka } 51594a71df50SFrank Blaschka 51604a71df50SFrank Blaschka void qeth_core_free_discipline(struct qeth_card *card) 51614a71df50SFrank Blaschka { 51624a71df50SFrank Blaschka if (card->options.layer2) 5163c041f2d4SSebastian Ott symbol_put(qeth_l2_discipline); 51644a71df50SFrank Blaschka else 5165c041f2d4SSebastian Ott symbol_put(qeth_l3_discipline); 5166c041f2d4SSebastian Ott card->discipline = NULL; 51674a71df50SFrank Blaschka } 51684a71df50SFrank Blaschka 5169b7169c51SSebastian Ott static const struct device_type qeth_generic_devtype = { 5170b7169c51SSebastian Ott .name = "qeth_generic", 5171b7169c51SSebastian Ott .groups = qeth_generic_attr_groups, 5172b7169c51SSebastian Ott }; 5173b7169c51SSebastian Ott static const struct device_type qeth_osn_devtype = { 5174b7169c51SSebastian Ott .name = "qeth_osn", 5175b7169c51SSebastian Ott .groups = qeth_osn_attr_groups, 5176b7169c51SSebastian Ott }; 5177b7169c51SSebastian Ott 5178819dc537SStefan Raspl #define DBF_NAME_LEN 20 5179819dc537SStefan Raspl 5180819dc537SStefan Raspl struct qeth_dbf_entry { 5181819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 5182819dc537SStefan Raspl debug_info_t *dbf_info; 5183819dc537SStefan Raspl struct list_head dbf_list; 5184819dc537SStefan Raspl }; 5185819dc537SStefan Raspl 5186819dc537SStefan Raspl static LIST_HEAD(qeth_dbf_list); 5187819dc537SStefan Raspl static DEFINE_MUTEX(qeth_dbf_list_mutex); 5188819dc537SStefan Raspl 5189819dc537SStefan Raspl static debug_info_t *qeth_get_dbf_entry(char *name) 5190819dc537SStefan Raspl { 5191819dc537SStefan Raspl struct qeth_dbf_entry *entry; 5192819dc537SStefan Raspl debug_info_t *rc = NULL; 5193819dc537SStefan Raspl 5194819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5195819dc537SStefan Raspl list_for_each_entry(entry, &qeth_dbf_list, dbf_list) { 5196819dc537SStefan Raspl if (strcmp(entry->dbf_name, name) == 0) { 5197819dc537SStefan Raspl rc = entry->dbf_info; 5198819dc537SStefan Raspl break; 5199819dc537SStefan Raspl } 5200819dc537SStefan Raspl } 5201819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5202819dc537SStefan Raspl return rc; 5203819dc537SStefan Raspl } 5204819dc537SStefan Raspl 5205819dc537SStefan Raspl static int qeth_add_dbf_entry(struct qeth_card *card, char *name) 5206819dc537SStefan Raspl { 5207819dc537SStefan Raspl struct qeth_dbf_entry *new_entry; 5208819dc537SStefan Raspl 5209819dc537SStefan Raspl card->debug = debug_register(name, 2, 1, 8); 5210819dc537SStefan Raspl if (!card->debug) { 5211819dc537SStefan Raspl QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf"); 5212819dc537SStefan Raspl goto err; 5213819dc537SStefan Raspl } 5214819dc537SStefan Raspl if (debug_register_view(card->debug, &debug_hex_ascii_view)) 5215819dc537SStefan Raspl goto err_dbg; 5216819dc537SStefan Raspl new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL); 5217819dc537SStefan Raspl if (!new_entry) 5218819dc537SStefan Raspl goto err_dbg; 5219819dc537SStefan Raspl strncpy(new_entry->dbf_name, name, DBF_NAME_LEN); 5220819dc537SStefan Raspl new_entry->dbf_info = card->debug; 5221819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5222819dc537SStefan Raspl list_add(&new_entry->dbf_list, &qeth_dbf_list); 5223819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5224819dc537SStefan Raspl 5225819dc537SStefan Raspl return 0; 5226819dc537SStefan Raspl 5227819dc537SStefan Raspl err_dbg: 5228819dc537SStefan Raspl debug_unregister(card->debug); 5229819dc537SStefan Raspl err: 5230819dc537SStefan Raspl return -ENOMEM; 5231819dc537SStefan Raspl } 5232819dc537SStefan Raspl 5233819dc537SStefan Raspl static void qeth_clear_dbf_list(void) 5234819dc537SStefan Raspl { 5235819dc537SStefan Raspl struct qeth_dbf_entry *entry, *tmp; 5236819dc537SStefan Raspl 5237819dc537SStefan Raspl mutex_lock(&qeth_dbf_list_mutex); 5238819dc537SStefan Raspl list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) { 5239819dc537SStefan Raspl list_del(&entry->dbf_list); 5240819dc537SStefan Raspl debug_unregister(entry->dbf_info); 5241819dc537SStefan Raspl kfree(entry); 5242819dc537SStefan Raspl } 5243819dc537SStefan Raspl mutex_unlock(&qeth_dbf_list_mutex); 5244819dc537SStefan Raspl } 5245819dc537SStefan Raspl 52464a71df50SFrank Blaschka static int qeth_core_probe_device(struct ccwgroup_device *gdev) 52474a71df50SFrank Blaschka { 52484a71df50SFrank Blaschka struct qeth_card *card; 52494a71df50SFrank Blaschka struct device *dev; 52504a71df50SFrank Blaschka int rc; 52514a71df50SFrank Blaschka unsigned long flags; 5252819dc537SStefan Raspl char dbf_name[DBF_NAME_LEN]; 52534a71df50SFrank Blaschka 5254d11ba0c4SPeter Tiedemann QETH_DBF_TEXT(SETUP, 2, "probedev"); 52554a71df50SFrank Blaschka 52564a71df50SFrank Blaschka dev = &gdev->dev; 52574a71df50SFrank Blaschka if (!get_device(dev)) 52584a71df50SFrank Blaschka return -ENODEV; 52594a71df50SFrank Blaschka 52602a0217d5SKay Sievers QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev)); 52614a71df50SFrank Blaschka 52624a71df50SFrank Blaschka card = qeth_alloc_card(); 52634a71df50SFrank Blaschka if (!card) { 5264d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "1err%d", -ENOMEM); 52654a71df50SFrank Blaschka rc = -ENOMEM; 52664a71df50SFrank Blaschka goto err_dev; 52674a71df50SFrank Blaschka } 5268af039068SCarsten Otte 5269af039068SCarsten Otte snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s", 5270af039068SCarsten Otte dev_name(&gdev->dev)); 5271819dc537SStefan Raspl card->debug = qeth_get_dbf_entry(dbf_name); 5272af039068SCarsten Otte if (!card->debug) { 5273819dc537SStefan Raspl rc = qeth_add_dbf_entry(card, dbf_name); 5274819dc537SStefan Raspl if (rc) 5275af039068SCarsten Otte goto err_card; 5276af039068SCarsten Otte } 5277af039068SCarsten Otte 52784a71df50SFrank Blaschka card->read.ccwdev = gdev->cdev[0]; 52794a71df50SFrank Blaschka card->write.ccwdev = gdev->cdev[1]; 52804a71df50SFrank Blaschka card->data.ccwdev = gdev->cdev[2]; 52814a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, card); 52824a71df50SFrank Blaschka card->gdev = gdev; 52834a71df50SFrank Blaschka gdev->cdev[0]->handler = qeth_irq; 52844a71df50SFrank Blaschka gdev->cdev[1]->handler = qeth_irq; 52854a71df50SFrank Blaschka gdev->cdev[2]->handler = qeth_irq; 52864a71df50SFrank Blaschka 52874a71df50SFrank Blaschka rc = qeth_determine_card_type(card); 52884a71df50SFrank Blaschka if (rc) { 5289d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); 5290819dc537SStefan Raspl goto err_card; 52914a71df50SFrank Blaschka } 52924a71df50SFrank Blaschka rc = qeth_setup_card(card); 52934a71df50SFrank Blaschka if (rc) { 5294d11ba0c4SPeter Tiedemann QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); 5295819dc537SStefan Raspl goto err_card; 52964a71df50SFrank Blaschka } 52974a71df50SFrank Blaschka 52985113fec0SUrsula Braun if (card->info.type == QETH_CARD_TYPE_OSN) 5299b7169c51SSebastian Ott gdev->dev.type = &qeth_osn_devtype; 53005113fec0SUrsula Braun else 5301b7169c51SSebastian Ott gdev->dev.type = &qeth_generic_devtype; 5302b7169c51SSebastian Ott 53035113fec0SUrsula Braun switch (card->info.type) { 53045113fec0SUrsula Braun case QETH_CARD_TYPE_OSN: 53055113fec0SUrsula Braun case QETH_CARD_TYPE_OSM: 53065113fec0SUrsula Braun rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2); 53075113fec0SUrsula Braun if (rc) 5308819dc537SStefan Raspl goto err_card; 5309c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 53105113fec0SUrsula Braun if (rc) 53115113fec0SUrsula Braun goto err_disc; 53125113fec0SUrsula Braun case QETH_CARD_TYPE_OSD: 53135113fec0SUrsula Braun case QETH_CARD_TYPE_OSX: 53145113fec0SUrsula Braun default: 53155113fec0SUrsula Braun break; 53164a71df50SFrank Blaschka } 53174a71df50SFrank Blaschka 53184a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 53194a71df50SFrank Blaschka list_add_tail(&card->list, &qeth_core_card_list.list); 53204a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 532176b11f8eSUrsula Braun 532276b11f8eSUrsula Braun qeth_determine_capabilities(card); 53234a71df50SFrank Blaschka return 0; 53244a71df50SFrank Blaschka 53255113fec0SUrsula Braun err_disc: 53265113fec0SUrsula Braun qeth_core_free_discipline(card); 53274a71df50SFrank Blaschka err_card: 53284a71df50SFrank Blaschka qeth_core_free_card(card); 53294a71df50SFrank Blaschka err_dev: 53304a71df50SFrank Blaschka put_device(dev); 53314a71df50SFrank Blaschka return rc; 53324a71df50SFrank Blaschka } 53334a71df50SFrank Blaschka 53344a71df50SFrank Blaschka static void qeth_core_remove_device(struct ccwgroup_device *gdev) 53354a71df50SFrank Blaschka { 53364a71df50SFrank Blaschka unsigned long flags; 53374a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 53384a71df50SFrank Blaschka 533928a7e4c9SUrsula Braun QETH_DBF_TEXT(SETUP, 2, "removedv"); 53404a71df50SFrank Blaschka 5341c041f2d4SSebastian Ott if (card->discipline) { 5342c041f2d4SSebastian Ott card->discipline->remove(gdev); 53439dc48cccSUrsula Braun qeth_core_free_discipline(card); 53449dc48cccSUrsula Braun } 53459dc48cccSUrsula Braun 53464a71df50SFrank Blaschka write_lock_irqsave(&qeth_core_card_list.rwlock, flags); 53474a71df50SFrank Blaschka list_del(&card->list); 53484a71df50SFrank Blaschka write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); 53494a71df50SFrank Blaschka qeth_core_free_card(card); 53504a71df50SFrank Blaschka dev_set_drvdata(&gdev->dev, NULL); 53514a71df50SFrank Blaschka put_device(&gdev->dev); 53524a71df50SFrank Blaschka return; 53534a71df50SFrank Blaschka } 53544a71df50SFrank Blaschka 53554a71df50SFrank Blaschka static int qeth_core_set_online(struct ccwgroup_device *gdev) 53564a71df50SFrank Blaschka { 53574a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 53584a71df50SFrank Blaschka int rc = 0; 53594a71df50SFrank Blaschka int def_discipline; 53604a71df50SFrank Blaschka 5361c041f2d4SSebastian Ott if (!card->discipline) { 53624a71df50SFrank Blaschka if (card->info.type == QETH_CARD_TYPE_IQD) 53634a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER3; 53644a71df50SFrank Blaschka else 53654a71df50SFrank Blaschka def_discipline = QETH_DISCIPLINE_LAYER2; 53664a71df50SFrank Blaschka rc = qeth_core_load_discipline(card, def_discipline); 53674a71df50SFrank Blaschka if (rc) 53684a71df50SFrank Blaschka goto err; 5369c041f2d4SSebastian Ott rc = card->discipline->setup(card->gdev); 53704a71df50SFrank Blaschka if (rc) 53714a71df50SFrank Blaschka goto err; 53724a71df50SFrank Blaschka } 5373c041f2d4SSebastian Ott rc = card->discipline->set_online(gdev); 53744a71df50SFrank Blaschka err: 53754a71df50SFrank Blaschka return rc; 53764a71df50SFrank Blaschka } 53774a71df50SFrank Blaschka 53784a71df50SFrank Blaschka static int qeth_core_set_offline(struct ccwgroup_device *gdev) 53794a71df50SFrank Blaschka { 53804a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5381c041f2d4SSebastian Ott return card->discipline->set_offline(gdev); 53824a71df50SFrank Blaschka } 53834a71df50SFrank Blaschka 53844a71df50SFrank Blaschka static void qeth_core_shutdown(struct ccwgroup_device *gdev) 53854a71df50SFrank Blaschka { 53864a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5387c041f2d4SSebastian Ott if (card->discipline && card->discipline->shutdown) 5388c041f2d4SSebastian Ott card->discipline->shutdown(gdev); 53894a71df50SFrank Blaschka } 53904a71df50SFrank Blaschka 5391bbcfcdc8SFrank Blaschka static int qeth_core_prepare(struct ccwgroup_device *gdev) 5392bbcfcdc8SFrank Blaschka { 5393bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5394c041f2d4SSebastian Ott if (card->discipline && card->discipline->prepare) 5395c041f2d4SSebastian Ott return card->discipline->prepare(gdev); 5396bbcfcdc8SFrank Blaschka return 0; 5397bbcfcdc8SFrank Blaschka } 5398bbcfcdc8SFrank Blaschka 5399bbcfcdc8SFrank Blaschka static void qeth_core_complete(struct ccwgroup_device *gdev) 5400bbcfcdc8SFrank Blaschka { 5401bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5402c041f2d4SSebastian Ott if (card->discipline && card->discipline->complete) 5403c041f2d4SSebastian Ott card->discipline->complete(gdev); 5404bbcfcdc8SFrank Blaschka } 5405bbcfcdc8SFrank Blaschka 5406bbcfcdc8SFrank Blaschka static int qeth_core_freeze(struct ccwgroup_device *gdev) 5407bbcfcdc8SFrank Blaschka { 5408bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5409c041f2d4SSebastian Ott if (card->discipline && card->discipline->freeze) 5410c041f2d4SSebastian Ott return card->discipline->freeze(gdev); 5411bbcfcdc8SFrank Blaschka return 0; 5412bbcfcdc8SFrank Blaschka } 5413bbcfcdc8SFrank Blaschka 5414bbcfcdc8SFrank Blaschka static int qeth_core_thaw(struct ccwgroup_device *gdev) 5415bbcfcdc8SFrank Blaschka { 5416bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5417c041f2d4SSebastian Ott if (card->discipline && card->discipline->thaw) 5418c041f2d4SSebastian Ott return card->discipline->thaw(gdev); 5419bbcfcdc8SFrank Blaschka return 0; 5420bbcfcdc8SFrank Blaschka } 5421bbcfcdc8SFrank Blaschka 5422bbcfcdc8SFrank Blaschka static int qeth_core_restore(struct ccwgroup_device *gdev) 5423bbcfcdc8SFrank Blaschka { 5424bbcfcdc8SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev); 5425c041f2d4SSebastian Ott if (card->discipline && card->discipline->restore) 5426c041f2d4SSebastian Ott return card->discipline->restore(gdev); 5427bbcfcdc8SFrank Blaschka return 0; 5428bbcfcdc8SFrank Blaschka } 5429bbcfcdc8SFrank Blaschka 54304a71df50SFrank Blaschka static struct ccwgroup_driver qeth_core_ccwgroup_driver = { 54313c190c51SSebastian Ott .driver = { 54324a71df50SFrank Blaschka .owner = THIS_MODULE, 54334a71df50SFrank Blaschka .name = "qeth", 54343c190c51SSebastian Ott }, 5435b7169c51SSebastian Ott .setup = qeth_core_probe_device, 54364a71df50SFrank Blaschka .remove = qeth_core_remove_device, 54374a71df50SFrank Blaschka .set_online = qeth_core_set_online, 54384a71df50SFrank Blaschka .set_offline = qeth_core_set_offline, 54394a71df50SFrank Blaschka .shutdown = qeth_core_shutdown, 5440bbcfcdc8SFrank Blaschka .prepare = qeth_core_prepare, 5441bbcfcdc8SFrank Blaschka .complete = qeth_core_complete, 5442bbcfcdc8SFrank Blaschka .freeze = qeth_core_freeze, 5443bbcfcdc8SFrank Blaschka .thaw = qeth_core_thaw, 5444bbcfcdc8SFrank Blaschka .restore = qeth_core_restore, 54454a71df50SFrank Blaschka }; 54464a71df50SFrank Blaschka 5447b7169c51SSebastian Ott static ssize_t qeth_core_driver_group_store(struct device_driver *ddrv, 5448b7169c51SSebastian Ott const char *buf, size_t count) 54494a71df50SFrank Blaschka { 54504a71df50SFrank Blaschka int err; 54514a71df50SFrank Blaschka 5452b7169c51SSebastian Ott err = ccwgroup_create_dev(qeth_core_root_dev, 5453b7169c51SSebastian Ott &qeth_core_ccwgroup_driver, 3, buf); 5454b7169c51SSebastian Ott 5455b7169c51SSebastian Ott return err ? err : count; 5456b7169c51SSebastian Ott } 54574a71df50SFrank Blaschka static DRIVER_ATTR(group, 0200, NULL, qeth_core_driver_group_store); 54584a71df50SFrank Blaschka 5459f47e2256SSebastian Ott static struct attribute *qeth_drv_attrs[] = { 5460f47e2256SSebastian Ott &driver_attr_group.attr, 5461f47e2256SSebastian Ott NULL, 5462f47e2256SSebastian Ott }; 5463f47e2256SSebastian Ott static struct attribute_group qeth_drv_attr_group = { 5464f47e2256SSebastian Ott .attrs = qeth_drv_attrs, 5465f47e2256SSebastian Ott }; 5466f47e2256SSebastian Ott static const struct attribute_group *qeth_drv_attr_groups[] = { 5467f47e2256SSebastian Ott &qeth_drv_attr_group, 5468f47e2256SSebastian Ott NULL, 5469f47e2256SSebastian Ott }; 5470f47e2256SSebastian Ott 54714a71df50SFrank Blaschka static struct { 54724a71df50SFrank Blaschka const char str[ETH_GSTRING_LEN]; 54734a71df50SFrank Blaschka } qeth_ethtool_stats_keys[] = { 54744a71df50SFrank Blaschka /* 0 */{"rx skbs"}, 54754a71df50SFrank Blaschka {"rx buffers"}, 54764a71df50SFrank Blaschka {"tx skbs"}, 54774a71df50SFrank Blaschka {"tx buffers"}, 54784a71df50SFrank Blaschka {"tx skbs no packing"}, 54794a71df50SFrank Blaschka {"tx buffers no packing"}, 54804a71df50SFrank Blaschka {"tx skbs packing"}, 54814a71df50SFrank Blaschka {"tx buffers packing"}, 54824a71df50SFrank Blaschka {"tx sg skbs"}, 54834a71df50SFrank Blaschka {"tx sg frags"}, 54844a71df50SFrank Blaschka /* 10 */{"rx sg skbs"}, 54854a71df50SFrank Blaschka {"rx sg frags"}, 54864a71df50SFrank Blaschka {"rx sg page allocs"}, 54874a71df50SFrank Blaschka {"tx large kbytes"}, 54884a71df50SFrank Blaschka {"tx large count"}, 54894a71df50SFrank Blaschka {"tx pk state ch n->p"}, 54904a71df50SFrank Blaschka {"tx pk state ch p->n"}, 54914a71df50SFrank Blaschka {"tx pk watermark low"}, 54924a71df50SFrank Blaschka {"tx pk watermark high"}, 54934a71df50SFrank Blaschka {"queue 0 buffer usage"}, 54944a71df50SFrank Blaschka /* 20 */{"queue 1 buffer usage"}, 54954a71df50SFrank Blaschka {"queue 2 buffer usage"}, 54964a71df50SFrank Blaschka {"queue 3 buffer usage"}, 5497a1c3ed4cSFrank Blaschka {"rx poll time"}, 5498a1c3ed4cSFrank Blaschka {"rx poll count"}, 54994a71df50SFrank Blaschka {"rx do_QDIO time"}, 55004a71df50SFrank Blaschka {"rx do_QDIO count"}, 55014a71df50SFrank Blaschka {"tx handler time"}, 55024a71df50SFrank Blaschka {"tx handler count"}, 55034a71df50SFrank Blaschka {"tx time"}, 55044a71df50SFrank Blaschka /* 30 */{"tx count"}, 55054a71df50SFrank Blaschka {"tx do_QDIO time"}, 55064a71df50SFrank Blaschka {"tx do_QDIO count"}, 5507f61a0d05SFrank Blaschka {"tx csum"}, 5508c3b4a740SFrank Blaschka {"tx lin"}, 55090da9581dSEinar Lueck {"cq handler count"}, 55100da9581dSEinar Lueck {"cq handler time"} 55114a71df50SFrank Blaschka }; 55124a71df50SFrank Blaschka 5513df8b4ec8SBen Hutchings int qeth_core_get_sset_count(struct net_device *dev, int stringset) 55144a71df50SFrank Blaschka { 5515df8b4ec8SBen Hutchings switch (stringset) { 5516df8b4ec8SBen Hutchings case ETH_SS_STATS: 55174a71df50SFrank Blaschka return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN); 5518df8b4ec8SBen Hutchings default: 5519df8b4ec8SBen Hutchings return -EINVAL; 55204a71df50SFrank Blaschka } 5521df8b4ec8SBen Hutchings } 5522df8b4ec8SBen Hutchings EXPORT_SYMBOL_GPL(qeth_core_get_sset_count); 55234a71df50SFrank Blaschka 55244a71df50SFrank Blaschka void qeth_core_get_ethtool_stats(struct net_device *dev, 55254a71df50SFrank Blaschka struct ethtool_stats *stats, u64 *data) 55264a71df50SFrank Blaschka { 5527509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 55284a71df50SFrank Blaschka data[0] = card->stats.rx_packets - 55294a71df50SFrank Blaschka card->perf_stats.initial_rx_packets; 55304a71df50SFrank Blaschka data[1] = card->perf_stats.bufs_rec; 55314a71df50SFrank Blaschka data[2] = card->stats.tx_packets - 55324a71df50SFrank Blaschka card->perf_stats.initial_tx_packets; 55334a71df50SFrank Blaschka data[3] = card->perf_stats.bufs_sent; 55344a71df50SFrank Blaschka data[4] = card->stats.tx_packets - card->perf_stats.initial_tx_packets 55354a71df50SFrank Blaschka - card->perf_stats.skbs_sent_pack; 55364a71df50SFrank Blaschka data[5] = card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack; 55374a71df50SFrank Blaschka data[6] = card->perf_stats.skbs_sent_pack; 55384a71df50SFrank Blaschka data[7] = card->perf_stats.bufs_sent_pack; 55394a71df50SFrank Blaschka data[8] = card->perf_stats.sg_skbs_sent; 55404a71df50SFrank Blaschka data[9] = card->perf_stats.sg_frags_sent; 55414a71df50SFrank Blaschka data[10] = card->perf_stats.sg_skbs_rx; 55424a71df50SFrank Blaschka data[11] = card->perf_stats.sg_frags_rx; 55434a71df50SFrank Blaschka data[12] = card->perf_stats.sg_alloc_page_rx; 55444a71df50SFrank Blaschka data[13] = (card->perf_stats.large_send_bytes >> 10); 55454a71df50SFrank Blaschka data[14] = card->perf_stats.large_send_cnt; 55464a71df50SFrank Blaschka data[15] = card->perf_stats.sc_dp_p; 55474a71df50SFrank Blaschka data[16] = card->perf_stats.sc_p_dp; 55484a71df50SFrank Blaschka data[17] = QETH_LOW_WATERMARK_PACK; 55494a71df50SFrank Blaschka data[18] = QETH_HIGH_WATERMARK_PACK; 55504a71df50SFrank Blaschka data[19] = atomic_read(&card->qdio.out_qs[0]->used_buffers); 55514a71df50SFrank Blaschka data[20] = (card->qdio.no_out_queues > 1) ? 55524a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[1]->used_buffers) : 0; 55534a71df50SFrank Blaschka data[21] = (card->qdio.no_out_queues > 2) ? 55544a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[2]->used_buffers) : 0; 55554a71df50SFrank Blaschka data[22] = (card->qdio.no_out_queues > 3) ? 55564a71df50SFrank Blaschka atomic_read(&card->qdio.out_qs[3]->used_buffers) : 0; 55574a71df50SFrank Blaschka data[23] = card->perf_stats.inbound_time; 55584a71df50SFrank Blaschka data[24] = card->perf_stats.inbound_cnt; 55594a71df50SFrank Blaschka data[25] = card->perf_stats.inbound_do_qdio_time; 55604a71df50SFrank Blaschka data[26] = card->perf_stats.inbound_do_qdio_cnt; 55614a71df50SFrank Blaschka data[27] = card->perf_stats.outbound_handler_time; 55624a71df50SFrank Blaschka data[28] = card->perf_stats.outbound_handler_cnt; 55634a71df50SFrank Blaschka data[29] = card->perf_stats.outbound_time; 55644a71df50SFrank Blaschka data[30] = card->perf_stats.outbound_cnt; 55654a71df50SFrank Blaschka data[31] = card->perf_stats.outbound_do_qdio_time; 55664a71df50SFrank Blaschka data[32] = card->perf_stats.outbound_do_qdio_cnt; 5567f61a0d05SFrank Blaschka data[33] = card->perf_stats.tx_csum; 5568c3b4a740SFrank Blaschka data[34] = card->perf_stats.tx_lin; 55690da9581dSEinar Lueck data[35] = card->perf_stats.cq_cnt; 55700da9581dSEinar Lueck data[36] = card->perf_stats.cq_time; 55714a71df50SFrank Blaschka } 55724a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); 55734a71df50SFrank Blaschka 55744a71df50SFrank Blaschka void qeth_core_get_strings(struct net_device *dev, u32 stringset, u8 *data) 55754a71df50SFrank Blaschka { 55764a71df50SFrank Blaschka switch (stringset) { 55774a71df50SFrank Blaschka case ETH_SS_STATS: 55784a71df50SFrank Blaschka memcpy(data, &qeth_ethtool_stats_keys, 55794a71df50SFrank Blaschka sizeof(qeth_ethtool_stats_keys)); 55804a71df50SFrank Blaschka break; 55814a71df50SFrank Blaschka default: 55824a71df50SFrank Blaschka WARN_ON(1); 55834a71df50SFrank Blaschka break; 55844a71df50SFrank Blaschka } 55854a71df50SFrank Blaschka } 55864a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_strings); 55874a71df50SFrank Blaschka 55884a71df50SFrank Blaschka void qeth_core_get_drvinfo(struct net_device *dev, 55894a71df50SFrank Blaschka struct ethtool_drvinfo *info) 55904a71df50SFrank Blaschka { 5591509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv; 55924a71df50SFrank Blaschka 55937826d43fSJiri Pirko strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", 55947826d43fSJiri Pirko sizeof(info->driver)); 55957826d43fSJiri Pirko strlcpy(info->version, "1.0", sizeof(info->version)); 55967826d43fSJiri Pirko strlcpy(info->fw_version, card->info.mcl_level, 55977826d43fSJiri Pirko sizeof(info->fw_version)); 55987826d43fSJiri Pirko snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", 55997826d43fSJiri Pirko CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); 56004a71df50SFrank Blaschka } 56014a71df50SFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); 56024a71df50SFrank Blaschka 56033f9975aaSFrank Blaschka int qeth_core_ethtool_get_settings(struct net_device *netdev, 56043f9975aaSFrank Blaschka struct ethtool_cmd *ecmd) 56053f9975aaSFrank Blaschka { 5606509e2562SHeiko Carstens struct qeth_card *card = netdev->ml_priv; 56073f9975aaSFrank Blaschka enum qeth_link_types link_type; 56083f9975aaSFrank Blaschka 56093f9975aaSFrank Blaschka if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) 56103f9975aaSFrank Blaschka link_type = QETH_LINK_TYPE_10GBIT_ETH; 56113f9975aaSFrank Blaschka else 56123f9975aaSFrank Blaschka link_type = card->info.link_type; 56133f9975aaSFrank Blaschka 56143f9975aaSFrank Blaschka ecmd->transceiver = XCVR_INTERNAL; 56153f9975aaSFrank Blaschka ecmd->supported = SUPPORTED_Autoneg; 56163f9975aaSFrank Blaschka ecmd->advertising = ADVERTISED_Autoneg; 56173f9975aaSFrank Blaschka ecmd->duplex = DUPLEX_FULL; 56183f9975aaSFrank Blaschka ecmd->autoneg = AUTONEG_ENABLE; 56193f9975aaSFrank Blaschka 56203f9975aaSFrank Blaschka switch (link_type) { 56213f9975aaSFrank Blaschka case QETH_LINK_TYPE_FAST_ETH: 56223f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH100: 56233f9975aaSFrank Blaschka ecmd->supported |= SUPPORTED_10baseT_Half | 56243f9975aaSFrank Blaschka SUPPORTED_10baseT_Full | 56253f9975aaSFrank Blaschka SUPPORTED_100baseT_Half | 56263f9975aaSFrank Blaschka SUPPORTED_100baseT_Full | 56273f9975aaSFrank Blaschka SUPPORTED_TP; 56283f9975aaSFrank Blaschka ecmd->advertising |= ADVERTISED_10baseT_Half | 56293f9975aaSFrank Blaschka ADVERTISED_10baseT_Full | 56303f9975aaSFrank Blaschka ADVERTISED_100baseT_Half | 56313f9975aaSFrank Blaschka ADVERTISED_100baseT_Full | 56323f9975aaSFrank Blaschka ADVERTISED_TP; 56333f9975aaSFrank Blaschka ecmd->speed = SPEED_100; 56343f9975aaSFrank Blaschka ecmd->port = PORT_TP; 56353f9975aaSFrank Blaschka break; 56363f9975aaSFrank Blaschka 56373f9975aaSFrank Blaschka case QETH_LINK_TYPE_GBIT_ETH: 56383f9975aaSFrank Blaschka case QETH_LINK_TYPE_LANE_ETH1000: 56393f9975aaSFrank Blaschka ecmd->supported |= SUPPORTED_10baseT_Half | 56403f9975aaSFrank Blaschka SUPPORTED_10baseT_Full | 56413f9975aaSFrank Blaschka SUPPORTED_100baseT_Half | 56423f9975aaSFrank Blaschka SUPPORTED_100baseT_Full | 56433f9975aaSFrank Blaschka SUPPORTED_1000baseT_Half | 56443f9975aaSFrank Blaschka SUPPORTED_1000baseT_Full | 56453f9975aaSFrank Blaschka SUPPORTED_FIBRE; 56463f9975aaSFrank Blaschka ecmd->advertising |= ADVERTISED_10baseT_Half | 56473f9975aaSFrank Blaschka ADVERTISED_10baseT_Full | 56483f9975aaSFrank Blaschka ADVERTISED_100baseT_Half | 56493f9975aaSFrank Blaschka ADVERTISED_100baseT_Full | 56503f9975aaSFrank Blaschka ADVERTISED_1000baseT_Half | 56513f9975aaSFrank Blaschka ADVERTISED_1000baseT_Full | 56523f9975aaSFrank Blaschka ADVERTISED_FIBRE; 56533f9975aaSFrank Blaschka ecmd->speed = SPEED_1000; 56543f9975aaSFrank Blaschka ecmd->port = PORT_FIBRE; 56553f9975aaSFrank Blaschka break; 56563f9975aaSFrank Blaschka 56573f9975aaSFrank Blaschka case QETH_LINK_TYPE_10GBIT_ETH: 56583f9975aaSFrank Blaschka ecmd->supported |= SUPPORTED_10baseT_Half | 56593f9975aaSFrank Blaschka SUPPORTED_10baseT_Full | 56603f9975aaSFrank Blaschka SUPPORTED_100baseT_Half | 56613f9975aaSFrank Blaschka SUPPORTED_100baseT_Full | 56623f9975aaSFrank Blaschka SUPPORTED_1000baseT_Half | 56633f9975aaSFrank Blaschka SUPPORTED_1000baseT_Full | 56643f9975aaSFrank Blaschka SUPPORTED_10000baseT_Full | 56653f9975aaSFrank Blaschka SUPPORTED_FIBRE; 56663f9975aaSFrank Blaschka ecmd->advertising |= ADVERTISED_10baseT_Half | 56673f9975aaSFrank Blaschka ADVERTISED_10baseT_Full | 56683f9975aaSFrank Blaschka ADVERTISED_100baseT_Half | 56693f9975aaSFrank Blaschka ADVERTISED_100baseT_Full | 56703f9975aaSFrank Blaschka ADVERTISED_1000baseT_Half | 56713f9975aaSFrank Blaschka ADVERTISED_1000baseT_Full | 56723f9975aaSFrank Blaschka ADVERTISED_10000baseT_Full | 56733f9975aaSFrank Blaschka ADVERTISED_FIBRE; 56743f9975aaSFrank Blaschka ecmd->speed = SPEED_10000; 56753f9975aaSFrank Blaschka ecmd->port = PORT_FIBRE; 56763f9975aaSFrank Blaschka break; 56773f9975aaSFrank Blaschka 56783f9975aaSFrank Blaschka default: 56793f9975aaSFrank Blaschka ecmd->supported |= SUPPORTED_10baseT_Half | 56803f9975aaSFrank Blaschka SUPPORTED_10baseT_Full | 56813f9975aaSFrank Blaschka SUPPORTED_TP; 56823f9975aaSFrank Blaschka ecmd->advertising |= ADVERTISED_10baseT_Half | 56833f9975aaSFrank Blaschka ADVERTISED_10baseT_Full | 56843f9975aaSFrank Blaschka ADVERTISED_TP; 56853f9975aaSFrank Blaschka ecmd->speed = SPEED_10; 56863f9975aaSFrank Blaschka ecmd->port = PORT_TP; 56873f9975aaSFrank Blaschka } 56883f9975aaSFrank Blaschka 56893f9975aaSFrank Blaschka return 0; 56903f9975aaSFrank Blaschka } 56913f9975aaSFrank Blaschka EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); 56923f9975aaSFrank Blaschka 56934a71df50SFrank Blaschka static int __init qeth_core_init(void) 56944a71df50SFrank Blaschka { 56954a71df50SFrank Blaschka int rc; 56964a71df50SFrank Blaschka 569774eacdb9SFrank Blaschka pr_info("loading core functions\n"); 56984a71df50SFrank Blaschka INIT_LIST_HEAD(&qeth_core_card_list.list); 5699819dc537SStefan Raspl INIT_LIST_HEAD(&qeth_dbf_list); 57004a71df50SFrank Blaschka rwlock_init(&qeth_core_card_list.rwlock); 57012022e00cSFrank Blaschka mutex_init(&qeth_mod_mutex); 57024a71df50SFrank Blaschka 57030f54761dSStefan Raspl qeth_wq = create_singlethread_workqueue("qeth_wq"); 57040f54761dSStefan Raspl 57054a71df50SFrank Blaschka rc = qeth_register_dbf_views(); 57064a71df50SFrank Blaschka if (rc) 57074a71df50SFrank Blaschka goto out_err; 5708035da16fSMark McLoughlin qeth_core_root_dev = root_device_register("qeth"); 57094a71df50SFrank Blaschka rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; 57104a71df50SFrank Blaschka if (rc) 57114a71df50SFrank Blaschka goto register_err; 5712683d718aSFrank Blaschka qeth_core_header_cache = kmem_cache_create("qeth_hdr", 5713683d718aSFrank Blaschka sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL); 5714683d718aSFrank Blaschka if (!qeth_core_header_cache) { 5715683d718aSFrank Blaschka rc = -ENOMEM; 5716683d718aSFrank Blaschka goto slab_err; 5717683d718aSFrank Blaschka } 57180da9581dSEinar Lueck qeth_qdio_outbuf_cache = kmem_cache_create("qeth_buf", 57190da9581dSEinar Lueck sizeof(struct qeth_qdio_out_buffer), 0, 0, NULL); 57200da9581dSEinar Lueck if (!qeth_qdio_outbuf_cache) { 57210da9581dSEinar Lueck rc = -ENOMEM; 57220da9581dSEinar Lueck goto cqslab_err; 57230da9581dSEinar Lueck } 57244a71df50SFrank Blaschka rc = ccw_driver_register(&qeth_ccw_driver); 57254a71df50SFrank Blaschka if (rc) 57264a71df50SFrank Blaschka goto ccw_err; 5727f47e2256SSebastian Ott qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups; 57284a71df50SFrank Blaschka rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); 57294a71df50SFrank Blaschka if (rc) 57304a71df50SFrank Blaschka goto ccwgroup_err; 57310da9581dSEinar Lueck 5732683d718aSFrank Blaschka return 0; 5733afb6ac59SSebastian Ott 5734afb6ac59SSebastian Ott ccwgroup_err: 5735afb6ac59SSebastian Ott ccw_driver_unregister(&qeth_ccw_driver); 5736afb6ac59SSebastian Ott ccw_err: 5737afb6ac59SSebastian Ott kmem_cache_destroy(qeth_qdio_outbuf_cache); 57380da9581dSEinar Lueck cqslab_err: 57390da9581dSEinar Lueck kmem_cache_destroy(qeth_core_header_cache); 5740683d718aSFrank Blaschka slab_err: 5741035da16fSMark McLoughlin root_device_unregister(qeth_core_root_dev); 57424a71df50SFrank Blaschka register_err: 57434a71df50SFrank Blaschka qeth_unregister_dbf_views(); 57444a71df50SFrank Blaschka out_err: 574574eacdb9SFrank Blaschka pr_err("Initializing the qeth device driver failed\n"); 57464a71df50SFrank Blaschka return rc; 57474a71df50SFrank Blaschka } 57484a71df50SFrank Blaschka 57494a71df50SFrank Blaschka static void __exit qeth_core_exit(void) 57504a71df50SFrank Blaschka { 5751819dc537SStefan Raspl qeth_clear_dbf_list(); 57520f54761dSStefan Raspl destroy_workqueue(qeth_wq); 57534a71df50SFrank Blaschka ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); 57544a71df50SFrank Blaschka ccw_driver_unregister(&qeth_ccw_driver); 57550da9581dSEinar Lueck kmem_cache_destroy(qeth_qdio_outbuf_cache); 5756683d718aSFrank Blaschka kmem_cache_destroy(qeth_core_header_cache); 5757afb6ac59SSebastian Ott root_device_unregister(qeth_core_root_dev); 57584a71df50SFrank Blaschka qeth_unregister_dbf_views(); 575974eacdb9SFrank Blaschka pr_info("core functions removed\n"); 57604a71df50SFrank Blaschka } 57614a71df50SFrank Blaschka 57624a71df50SFrank Blaschka module_init(qeth_core_init); 57634a71df50SFrank Blaschka module_exit(qeth_core_exit); 57644a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 57654a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth core functions"); 57664a71df50SFrank Blaschka MODULE_LICENSE("GPL"); 5767