| .. | .. |
|---|
| 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 (had_link) |
|---|
| 93 | + netif_carrier_off(ndp->ndev.dev); |
|---|
| 94 | + else |
|---|
| 95 | + netif_carrier_on(ndp->ndev.dev); |
|---|
| 92 | 96 | |
|---|
| 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); |
|---|
| 97 | + if (!ndp->multi_package && !nc->package->multi_channel) { |
|---|
| 98 | + if (had_link) { |
|---|
| 99 | + ndp->flags |= NCSI_DEV_RESHUFFLE; |
|---|
| 100 | + ncsi_stop_channel_monitor(nc); |
|---|
| 101 | + spin_lock_irqsave(&ndp->lock, flags); |
|---|
| 102 | + list_add_tail_rcu(&nc->link, &ndp->channel_queue); |
|---|
| 103 | + spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 104 | + return ncsi_process_next_channel(ndp); |
|---|
| 105 | + } |
|---|
| 106 | + /* Configured channel came up */ |
|---|
| 107 | + return 0; |
|---|
| 108 | + } |
|---|
| 97 | 109 | |
|---|
| 98 | | - return ncsi_process_next_channel(ndp); |
|---|
| 110 | + if (had_link) { |
|---|
| 111 | + ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; |
|---|
| 112 | + if (ncsi_channel_is_last(ndp, nc)) { |
|---|
| 113 | + /* No channels left, reconfigure */ |
|---|
| 114 | + return ncsi_reset_dev(&ndp->ndev); |
|---|
| 115 | + } else if (ncm->enable) { |
|---|
| 116 | + /* Need to failover Tx channel */ |
|---|
| 117 | + ncsi_update_tx_channel(ndp, nc->package, nc, NULL); |
|---|
| 118 | + } |
|---|
| 119 | + } else if (has_link && nc->package->preferred_channel == nc) { |
|---|
| 120 | + /* Return Tx to preferred channel */ |
|---|
| 121 | + ncsi_update_tx_channel(ndp, nc->package, NULL, nc); |
|---|
| 122 | + } else if (has_link) { |
|---|
| 123 | + NCSI_FOR_EACH_PACKAGE(ndp, np) { |
|---|
| 124 | + NCSI_FOR_EACH_CHANNEL(np, tmp) { |
|---|
| 125 | + /* Enable Tx on this channel if the current Tx |
|---|
| 126 | + * channel is down. |
|---|
| 127 | + */ |
|---|
| 128 | + ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; |
|---|
| 129 | + if (ncm->enable && |
|---|
| 130 | + !ncsi_channel_has_link(tmp)) { |
|---|
| 131 | + ncsi_update_tx_channel(ndp, nc->package, |
|---|
| 132 | + tmp, nc); |
|---|
| 133 | + break; |
|---|
| 134 | + } |
|---|
| 135 | + } |
|---|
| 136 | + } |
|---|
| 137 | + } |
|---|
| 138 | + |
|---|
| 139 | + /* Leave configured channels active in a multi-channel scenario so |
|---|
| 140 | + * AEN events are still received. |
|---|
| 141 | + */ |
|---|
| 142 | + return 0; |
|---|
| 99 | 143 | } |
|---|
| 100 | 144 | |
|---|
| 101 | 145 | static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, |
|---|
| .. | .. |
|---|
| 126 | 170 | nc->state = NCSI_CHANNEL_INACTIVE; |
|---|
| 127 | 171 | list_add_tail_rcu(&nc->link, &ndp->channel_queue); |
|---|
| 128 | 172 | spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 173 | + nc->modes[NCSI_MODE_TX_ENABLE].enable = 0; |
|---|
| 129 | 174 | |
|---|
| 130 | 175 | return ncsi_process_next_channel(ndp); |
|---|
| 131 | 176 | } |
|---|