.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright Gavin Shan, IBM Corporation 2016. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify |
---|
5 | | - * it under the terms of the GNU General Public License as published by |
---|
6 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
7 | | - * (at your option) any later version. |
---|
8 | 4 | */ |
---|
9 | 5 | |
---|
10 | 6 | #include <linux/module.h> |
---|
.. | .. |
---|
50 | 46 | static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, |
---|
51 | 47 | struct ncsi_aen_pkt_hdr *h) |
---|
52 | 48 | { |
---|
53 | | - struct ncsi_aen_lsc_pkt *lsc; |
---|
54 | | - struct ncsi_channel *nc; |
---|
| 49 | + struct ncsi_channel *nc, *tmp; |
---|
55 | 50 | struct ncsi_channel_mode *ncm; |
---|
| 51 | + unsigned long old_data, data; |
---|
| 52 | + struct ncsi_aen_lsc_pkt *lsc; |
---|
| 53 | + struct ncsi_package *np; |
---|
| 54 | + bool had_link, has_link; |
---|
| 55 | + unsigned long flags; |
---|
56 | 56 | bool chained; |
---|
57 | 57 | int state; |
---|
58 | | - unsigned long old_data, data; |
---|
59 | | - unsigned long flags; |
---|
60 | 58 | |
---|
61 | 59 | /* Find the NCSI channel */ |
---|
62 | 60 | ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); |
---|
.. | .. |
---|
73 | 71 | ncm->data[2] = data; |
---|
74 | 72 | ncm->data[4] = ntohl(lsc->oem_status); |
---|
75 | 73 | |
---|
| 74 | + had_link = !!(old_data & 0x1); |
---|
| 75 | + has_link = !!(data & 0x1); |
---|
| 76 | + |
---|
76 | 77 | netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", |
---|
77 | 78 | nc->id, data & 0x1 ? "up" : "down"); |
---|
78 | 79 | |
---|
.. | .. |
---|
80 | 81 | state = nc->state; |
---|
81 | 82 | spin_unlock_irqrestore(&nc->lock, flags); |
---|
82 | 83 | |
---|
83 | | - if (!((old_data ^ data) & 0x1) || chained) |
---|
84 | | - return 0; |
---|
85 | | - if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && |
---|
86 | | - !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) |
---|
| 84 | + if (state == NCSI_CHANNEL_INACTIVE) |
---|
| 85 | + netdev_warn(ndp->ndev.dev, |
---|
| 86 | + "NCSI: Inactive channel %u received AEN!\n", |
---|
| 87 | + nc->id); |
---|
| 88 | + |
---|
| 89 | + if ((had_link == has_link) || chained) |
---|
87 | 90 | return 0; |
---|
88 | 91 | |
---|
89 | | - if (!(ndp->flags & NCSI_DEV_HWA) && |
---|
90 | | - state == NCSI_CHANNEL_ACTIVE) |
---|
91 | | - ndp->flags |= NCSI_DEV_RESHUFFLE; |
---|
| 92 | + if (!ndp->multi_package && !nc->package->multi_channel) { |
---|
| 93 | + if (had_link) { |
---|
| 94 | + ndp->flags |= NCSI_DEV_RESHUFFLE; |
---|
| 95 | + ncsi_stop_channel_monitor(nc); |
---|
| 96 | + spin_lock_irqsave(&ndp->lock, flags); |
---|
| 97 | + list_add_tail_rcu(&nc->link, &ndp->channel_queue); |
---|
| 98 | + spin_unlock_irqrestore(&ndp->lock, flags); |
---|
| 99 | + return ncsi_process_next_channel(ndp); |
---|
| 100 | + } |
---|
| 101 | + /* Configured channel came up */ |
---|
| 102 | + return 0; |
---|
| 103 | + } |
---|
92 | 104 | |
---|
93 | | - ncsi_stop_channel_monitor(nc); |
---|
94 | | - spin_lock_irqsave(&ndp->lock, flags); |
---|
95 | | - list_add_tail_rcu(&nc->link, &ndp->channel_queue); |
---|
96 | | - spin_unlock_irqrestore(&ndp->lock, flags); |
---|
| 105 | + if (had_link) { |
---|
| 106 | + ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; |
---|
| 107 | + if (ncsi_channel_is_last(ndp, nc)) { |
---|
| 108 | + /* No channels left, reconfigure */ |
---|
| 109 | + return ncsi_reset_dev(&ndp->ndev); |
---|
| 110 | + } else if (ncm->enable) { |
---|
| 111 | + /* Need to failover Tx channel */ |
---|
| 112 | + ncsi_update_tx_channel(ndp, nc->package, nc, NULL); |
---|
| 113 | + } |
---|
| 114 | + } else if (has_link && nc->package->preferred_channel == nc) { |
---|
| 115 | + /* Return Tx to preferred channel */ |
---|
| 116 | + ncsi_update_tx_channel(ndp, nc->package, NULL, nc); |
---|
| 117 | + } else if (has_link) { |
---|
| 118 | + NCSI_FOR_EACH_PACKAGE(ndp, np) { |
---|
| 119 | + NCSI_FOR_EACH_CHANNEL(np, tmp) { |
---|
| 120 | + /* Enable Tx on this channel if the current Tx |
---|
| 121 | + * channel is down. |
---|
| 122 | + */ |
---|
| 123 | + ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; |
---|
| 124 | + if (ncm->enable && |
---|
| 125 | + !ncsi_channel_has_link(tmp)) { |
---|
| 126 | + ncsi_update_tx_channel(ndp, nc->package, |
---|
| 127 | + tmp, nc); |
---|
| 128 | + break; |
---|
| 129 | + } |
---|
| 130 | + } |
---|
| 131 | + } |
---|
| 132 | + } |
---|
97 | 133 | |
---|
98 | | - return ncsi_process_next_channel(ndp); |
---|
| 134 | + /* Leave configured channels active in a multi-channel scenario so |
---|
| 135 | + * AEN events are still received. |
---|
| 136 | + */ |
---|
| 137 | + return 0; |
---|
99 | 138 | } |
---|
100 | 139 | |
---|
101 | 140 | static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, |
---|