diff options
author | Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org> | 2016-08-22 14:18:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-08-23 03:13:11 -0400 |
commit | ce52c744574bbe31e5c30788f69d19f20d328225 (patch) | |
tree | f8bb6ff440b2fc6841cdcfd78021cc0976fb6139 /drivers/net/ethernet/ti | |
parent | 925d65e6d8a4c84c54fbad060f32385b57e210ed (diff) |
net: ethernet: ti: cpsw: add ethtool channels support
These ops allow to control number of channels driver is allowed to
work with at cpdma level. The maximum number of channels is 8 for
rx and 8 for tx. In dual_emac mode the h/w channels are shared
between two interfaces and changing number on one interface changes
number of channels on another.
How many channels are supported and enabled:
$ ethtool -l ethX
Change number of channels (up to 8)
$ ethtool -L ethX rx 6 tx 6
Per-channel statistic:
$ ethtool -S ethX
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
Reviewed-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/ti')
-rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 1fbb50f25603..4273e7f9b4aa 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c | |||
@@ -736,6 +736,11 @@ static void cpsw_rx_handler(void *token, int len, int status) | |||
736 | } | 736 | } |
737 | 737 | ||
738 | requeue: | 738 | requeue: |
739 | if (netif_dormant(ndev)) { | ||
740 | dev_kfree_skb_any(new_skb); | ||
741 | return; | ||
742 | } | ||
743 | |||
739 | ch = cpsw->rxch[skb_get_queue_mapping(new_skb)]; | 744 | ch = cpsw->rxch[skb_get_queue_mapping(new_skb)]; |
740 | ret = cpdma_chan_submit(ch, new_skb, new_skb->data, | 745 | ret = cpdma_chan_submit(ch, new_skb, new_skb->data, |
741 | skb_tailroom(new_skb), 0); | 746 | skb_tailroom(new_skb), 0); |
@@ -2060,6 +2065,179 @@ static void cpsw_ethtool_op_complete(struct net_device *ndev) | |||
2060 | cpsw_err(priv, drv, "ethtool complete failed %d\n", ret); | 2065 | cpsw_err(priv, drv, "ethtool complete failed %d\n", ret); |
2061 | } | 2066 | } |
2062 | 2067 | ||
2068 | static void cpsw_get_channels(struct net_device *ndev, | ||
2069 | struct ethtool_channels *ch) | ||
2070 | { | ||
2071 | struct cpsw_common *cpsw = ndev_to_cpsw(ndev); | ||
2072 | |||
2073 | ch->max_combined = 0; | ||
2074 | ch->max_rx = CPSW_MAX_QUEUES; | ||
2075 | ch->max_tx = CPSW_MAX_QUEUES; | ||
2076 | ch->max_other = 0; | ||
2077 | ch->other_count = 0; | ||
2078 | ch->rx_count = cpsw->rx_ch_num; | ||
2079 | ch->tx_count = cpsw->tx_ch_num; | ||
2080 | ch->combined_count = 0; | ||
2081 | } | ||
2082 | |||
2083 | static int cpsw_check_ch_settings(struct cpsw_common *cpsw, | ||
2084 | struct ethtool_channels *ch) | ||
2085 | { | ||
2086 | if (ch->combined_count) | ||
2087 | return -EINVAL; | ||
2088 | |||
2089 | /* verify we have at least one channel in each direction */ | ||
2090 | if (!ch->rx_count || !ch->tx_count) | ||
2091 | return -EINVAL; | ||
2092 | |||
2093 | if (ch->rx_count > cpsw->data.channels || | ||
2094 | ch->tx_count > cpsw->data.channels) | ||
2095 | return -EINVAL; | ||
2096 | |||
2097 | return 0; | ||
2098 | } | ||
2099 | |||
2100 | static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) | ||
2101 | { | ||
2102 | int (*poll)(struct napi_struct *, int); | ||
2103 | struct cpsw_common *cpsw = priv->cpsw; | ||
2104 | void (*handler)(void *, int, int); | ||
2105 | struct cpdma_chan **chan; | ||
2106 | int ret, *ch; | ||
2107 | |||
2108 | if (rx) { | ||
2109 | ch = &cpsw->rx_ch_num; | ||
2110 | chan = cpsw->rxch; | ||
2111 | handler = cpsw_rx_handler; | ||
2112 | poll = cpsw_rx_poll; | ||
2113 | } else { | ||
2114 | ch = &cpsw->tx_ch_num; | ||
2115 | chan = cpsw->txch; | ||
2116 | handler = cpsw_tx_handler; | ||
2117 | poll = cpsw_tx_poll; | ||
2118 | } | ||
2119 | |||
2120 | while (*ch < ch_num) { | ||
2121 | chan[*ch] = cpdma_chan_create(cpsw->dma, *ch, handler, rx); | ||
2122 | |||
2123 | if (IS_ERR(chan[*ch])) | ||
2124 | return PTR_ERR(chan[*ch]); | ||
2125 | |||
2126 | if (!chan[*ch]) | ||
2127 | return -EINVAL; | ||
2128 | |||
2129 | cpsw_info(priv, ifup, "created new %d %s channel\n", *ch, | ||
2130 | (rx ? "rx" : "tx")); | ||
2131 | (*ch)++; | ||
2132 | } | ||
2133 | |||
2134 | while (*ch > ch_num) { | ||
2135 | (*ch)--; | ||
2136 | |||
2137 | ret = cpdma_chan_destroy(chan[*ch]); | ||
2138 | if (ret) | ||
2139 | return ret; | ||
2140 | |||
2141 | cpsw_info(priv, ifup, "destroyed %d %s channel\n", *ch, | ||
2142 | (rx ? "rx" : "tx")); | ||
2143 | } | ||
2144 | |||
2145 | return 0; | ||
2146 | } | ||
2147 | |||
2148 | static int cpsw_update_channels(struct cpsw_priv *priv, | ||
2149 | struct ethtool_channels *ch) | ||
2150 | { | ||
2151 | int ret; | ||
2152 | |||
2153 | ret = cpsw_update_channels_res(priv, ch->rx_count, 1); | ||
2154 | if (ret) | ||
2155 | return ret; | ||
2156 | |||
2157 | ret = cpsw_update_channels_res(priv, ch->tx_count, 0); | ||
2158 | if (ret) | ||
2159 | return ret; | ||
2160 | |||
2161 | return 0; | ||
2162 | } | ||
2163 | |||
2164 | static int cpsw_set_channels(struct net_device *ndev, | ||
2165 | struct ethtool_channels *chs) | ||
2166 | { | ||
2167 | struct cpsw_priv *priv = netdev_priv(ndev); | ||
2168 | struct cpsw_common *cpsw = priv->cpsw; | ||
2169 | struct cpsw_slave *slave; | ||
2170 | int i, ret; | ||
2171 | |||
2172 | ret = cpsw_check_ch_settings(cpsw, chs); | ||
2173 | if (ret < 0) | ||
2174 | return ret; | ||
2175 | |||
2176 | /* Disable NAPI scheduling */ | ||
2177 | cpsw_intr_disable(cpsw); | ||
2178 | |||
2179 | /* Stop all transmit queues for every network device. | ||
2180 | * Disable re-using rx descriptors with dormant_on. | ||
2181 | */ | ||
2182 | for (i = cpsw->data.slaves, slave = cpsw->slaves; i; i--, slave++) { | ||
2183 | if (!(slave->ndev && netif_running(slave->ndev))) | ||
2184 | continue; | ||
2185 | |||
2186 | netif_tx_stop_all_queues(slave->ndev); | ||
2187 | netif_dormant_on(slave->ndev); | ||
2188 | } | ||
2189 | |||
2190 | /* Handle rest of tx packets and stop cpdma channels */ | ||
2191 | cpdma_ctlr_stop(cpsw->dma); | ||
2192 | ret = cpsw_update_channels(priv, chs); | ||
2193 | if (ret) | ||
2194 | goto err; | ||
2195 | |||
2196 | for (i = cpsw->data.slaves, slave = cpsw->slaves; i; i--, slave++) { | ||
2197 | if (!(slave->ndev && netif_running(slave->ndev))) | ||
2198 | continue; | ||
2199 | |||
2200 | /* Inform stack about new count of queues */ | ||
2201 | ret = netif_set_real_num_tx_queues(slave->ndev, | ||
2202 | cpsw->tx_ch_num); | ||
2203 | if (ret) { | ||
2204 | dev_err(priv->dev, "cannot set real number of tx queues\n"); | ||
2205 | goto err; | ||
2206 | } | ||
2207 | |||
2208 | ret = netif_set_real_num_rx_queues(slave->ndev, | ||
2209 | cpsw->rx_ch_num); | ||
2210 | if (ret) { | ||
2211 | dev_err(priv->dev, "cannot set real number of rx queues\n"); | ||
2212 | goto err; | ||
2213 | } | ||
2214 | |||
2215 | /* Enable rx packets handling */ | ||
2216 | netif_dormant_off(slave->ndev); | ||
2217 | } | ||
2218 | |||
2219 | if (cpsw_common_res_usage_state(cpsw)) { | ||
2220 | if (cpsw_fill_rx_channels(priv)) | ||
2221 | goto err; | ||
2222 | |||
2223 | /* After this receive is started */ | ||
2224 | cpdma_ctlr_start(cpsw->dma); | ||
2225 | cpsw_intr_enable(cpsw); | ||
2226 | } | ||
2227 | |||
2228 | /* Resume transmit for every affected interface */ | ||
2229 | for (i = cpsw->data.slaves, slave = cpsw->slaves; i; i--, slave++) { | ||
2230 | if (!(slave->ndev && netif_running(slave->ndev))) | ||
2231 | continue; | ||
2232 | netif_tx_start_all_queues(slave->ndev); | ||
2233 | } | ||
2234 | return 0; | ||
2235 | err: | ||
2236 | dev_err(priv->dev, "cannot update channels number, closing device\n"); | ||
2237 | dev_close(ndev); | ||
2238 | return ret; | ||
2239 | } | ||
2240 | |||
2063 | static const struct ethtool_ops cpsw_ethtool_ops = { | 2241 | static const struct ethtool_ops cpsw_ethtool_ops = { |
2064 | .get_drvinfo = cpsw_get_drvinfo, | 2242 | .get_drvinfo = cpsw_get_drvinfo, |
2065 | .get_msglevel = cpsw_get_msglevel, | 2243 | .get_msglevel = cpsw_get_msglevel, |
@@ -2081,6 +2259,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = { | |||
2081 | .get_regs = cpsw_get_regs, | 2259 | .get_regs = cpsw_get_regs, |
2082 | .begin = cpsw_ethtool_op_begin, | 2260 | .begin = cpsw_ethtool_op_begin, |
2083 | .complete = cpsw_ethtool_op_complete, | 2261 | .complete = cpsw_ethtool_op_complete, |
2262 | .get_channels = cpsw_get_channels, | ||
2263 | .set_channels = cpsw_set_channels, | ||
2084 | }; | 2264 | }; |
2085 | 2265 | ||
2086 | static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_common *cpsw, | 2266 | static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_common *cpsw, |