diff options
author | Petri Gynther <pgynther@google.com> | 2015-03-13 17:45:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-15 22:04:17 -0400 |
commit | 0034de4193e4aad30bbbef4e74ca5e0631ba08a7 (patch) | |
tree | a9361ac774e5c2cb7661502afa9a04c8b753371b /drivers/net/ethernet | |
parent | 70006af955151042aa62958b92b53f4296030fe5 (diff) |
net: bcmgenet: add support for Hardware Filter Block
Add support for Hardware Filter Block (HFB) so that incoming Rx traffic
can be matched and directed to desired Rx queues.
Signed-off-by: Petri Gynther <pgynther@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet.c | 175 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet.h | 1 |
2 files changed, 176 insertions, 0 deletions
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index e74ae628bbb9..12956b143b11 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c | |||
@@ -197,6 +197,14 @@ enum dma_reg { | |||
197 | DMA_PRIORITY_0, | 197 | DMA_PRIORITY_0, |
198 | DMA_PRIORITY_1, | 198 | DMA_PRIORITY_1, |
199 | DMA_PRIORITY_2, | 199 | DMA_PRIORITY_2, |
200 | DMA_INDEX2RING_0, | ||
201 | DMA_INDEX2RING_1, | ||
202 | DMA_INDEX2RING_2, | ||
203 | DMA_INDEX2RING_3, | ||
204 | DMA_INDEX2RING_4, | ||
205 | DMA_INDEX2RING_5, | ||
206 | DMA_INDEX2RING_6, | ||
207 | DMA_INDEX2RING_7, | ||
200 | }; | 208 | }; |
201 | 209 | ||
202 | static const u8 bcmgenet_dma_regs_v3plus[] = { | 210 | static const u8 bcmgenet_dma_regs_v3plus[] = { |
@@ -208,6 +216,14 @@ static const u8 bcmgenet_dma_regs_v3plus[] = { | |||
208 | [DMA_PRIORITY_0] = 0x30, | 216 | [DMA_PRIORITY_0] = 0x30, |
209 | [DMA_PRIORITY_1] = 0x34, | 217 | [DMA_PRIORITY_1] = 0x34, |
210 | [DMA_PRIORITY_2] = 0x38, | 218 | [DMA_PRIORITY_2] = 0x38, |
219 | [DMA_INDEX2RING_0] = 0x70, | ||
220 | [DMA_INDEX2RING_1] = 0x74, | ||
221 | [DMA_INDEX2RING_2] = 0x78, | ||
222 | [DMA_INDEX2RING_3] = 0x7C, | ||
223 | [DMA_INDEX2RING_4] = 0x80, | ||
224 | [DMA_INDEX2RING_5] = 0x84, | ||
225 | [DMA_INDEX2RING_6] = 0x88, | ||
226 | [DMA_INDEX2RING_7] = 0x8C, | ||
211 | }; | 227 | }; |
212 | 228 | ||
213 | static const u8 bcmgenet_dma_regs_v2[] = { | 229 | static const u8 bcmgenet_dma_regs_v2[] = { |
@@ -2283,6 +2299,160 @@ static void bcmgenet_enable_dma(struct bcmgenet_priv *priv, u32 dma_ctrl) | |||
2283 | bcmgenet_tdma_writel(priv, reg, DMA_CTRL); | 2299 | bcmgenet_tdma_writel(priv, reg, DMA_CTRL); |
2284 | } | 2300 | } |
2285 | 2301 | ||
2302 | static bool bcmgenet_hfb_is_filter_enabled(struct bcmgenet_priv *priv, | ||
2303 | u32 f_index) | ||
2304 | { | ||
2305 | u32 offset; | ||
2306 | u32 reg; | ||
2307 | |||
2308 | offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32); | ||
2309 | reg = bcmgenet_hfb_reg_readl(priv, offset); | ||
2310 | return !!(reg & (1 << (f_index % 32))); | ||
2311 | } | ||
2312 | |||
2313 | static void bcmgenet_hfb_enable_filter(struct bcmgenet_priv *priv, u32 f_index) | ||
2314 | { | ||
2315 | u32 offset; | ||
2316 | u32 reg; | ||
2317 | |||
2318 | offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32); | ||
2319 | reg = bcmgenet_hfb_reg_readl(priv, offset); | ||
2320 | reg |= (1 << (f_index % 32)); | ||
2321 | bcmgenet_hfb_reg_writel(priv, reg, offset); | ||
2322 | } | ||
2323 | |||
2324 | static void bcmgenet_hfb_set_filter_rx_queue_mapping(struct bcmgenet_priv *priv, | ||
2325 | u32 f_index, u32 rx_queue) | ||
2326 | { | ||
2327 | u32 offset; | ||
2328 | u32 reg; | ||
2329 | |||
2330 | offset = f_index / 8; | ||
2331 | reg = bcmgenet_rdma_readl(priv, DMA_INDEX2RING_0 + offset); | ||
2332 | reg &= ~(0xF << (4 * (f_index % 8))); | ||
2333 | reg |= ((rx_queue & 0xF) << (4 * (f_index % 8))); | ||
2334 | bcmgenet_rdma_writel(priv, reg, DMA_INDEX2RING_0 + offset); | ||
2335 | } | ||
2336 | |||
2337 | static void bcmgenet_hfb_set_filter_length(struct bcmgenet_priv *priv, | ||
2338 | u32 f_index, u32 f_length) | ||
2339 | { | ||
2340 | u32 offset; | ||
2341 | u32 reg; | ||
2342 | |||
2343 | offset = HFB_FLT_LEN_V3PLUS + | ||
2344 | ((priv->hw_params->hfb_filter_cnt - 1 - f_index) / 4) * | ||
2345 | sizeof(u32); | ||
2346 | reg = bcmgenet_hfb_reg_readl(priv, offset); | ||
2347 | reg &= ~(0xFF << (8 * (f_index % 4))); | ||
2348 | reg |= ((f_length & 0xFF) << (8 * (f_index % 4))); | ||
2349 | bcmgenet_hfb_reg_writel(priv, reg, offset); | ||
2350 | } | ||
2351 | |||
2352 | static int bcmgenet_hfb_find_unused_filter(struct bcmgenet_priv *priv) | ||
2353 | { | ||
2354 | u32 f_index; | ||
2355 | |||
2356 | for (f_index = 0; f_index < priv->hw_params->hfb_filter_cnt; f_index++) | ||
2357 | if (!bcmgenet_hfb_is_filter_enabled(priv, f_index)) | ||
2358 | return f_index; | ||
2359 | |||
2360 | return -ENOMEM; | ||
2361 | } | ||
2362 | |||
2363 | /* bcmgenet_hfb_add_filter | ||
2364 | * | ||
2365 | * Add new filter to Hardware Filter Block to match and direct Rx traffic to | ||
2366 | * desired Rx queue. | ||
2367 | * | ||
2368 | * f_data is an array of unsigned 32-bit integers where each 32-bit integer | ||
2369 | * provides filter data for 2 bytes (4 nibbles) of Rx frame: | ||
2370 | * | ||
2371 | * bits 31:20 - unused | ||
2372 | * bit 19 - nibble 0 match enable | ||
2373 | * bit 18 - nibble 1 match enable | ||
2374 | * bit 17 - nibble 2 match enable | ||
2375 | * bit 16 - nibble 3 match enable | ||
2376 | * bits 15:12 - nibble 0 data | ||
2377 | * bits 11:8 - nibble 1 data | ||
2378 | * bits 7:4 - nibble 2 data | ||
2379 | * bits 3:0 - nibble 3 data | ||
2380 | * | ||
2381 | * Example: | ||
2382 | * In order to match: | ||
2383 | * - Ethernet frame type = 0x0800 (IP) | ||
2384 | * - IP version field = 4 | ||
2385 | * - IP protocol field = 0x11 (UDP) | ||
2386 | * | ||
2387 | * The following filter is needed: | ||
2388 | * u32 hfb_filter_ipv4_udp[] = { | ||
2389 | * Rx frame offset 0x00: 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
2390 | * Rx frame offset 0x08: 0x00000000, 0x00000000, 0x000F0800, 0x00084000, | ||
2391 | * Rx frame offset 0x10: 0x00000000, 0x00000000, 0x00000000, 0x00030011, | ||
2392 | * }; | ||
2393 | * | ||
2394 | * To add the filter to HFB and direct the traffic to Rx queue 0, call: | ||
2395 | * bcmgenet_hfb_add_filter(priv, hfb_filter_ipv4_udp, | ||
2396 | * ARRAY_SIZE(hfb_filter_ipv4_udp), 0); | ||
2397 | */ | ||
2398 | int bcmgenet_hfb_add_filter(struct bcmgenet_priv *priv, u32 *f_data, | ||
2399 | u32 f_length, u32 rx_queue) | ||
2400 | { | ||
2401 | int f_index; | ||
2402 | u32 i; | ||
2403 | |||
2404 | f_index = bcmgenet_hfb_find_unused_filter(priv); | ||
2405 | if (f_index < 0) | ||
2406 | return -ENOMEM; | ||
2407 | |||
2408 | if (f_length > priv->hw_params->hfb_filter_size) | ||
2409 | return -EINVAL; | ||
2410 | |||
2411 | for (i = 0; i < f_length; i++) | ||
2412 | bcmgenet_hfb_writel(priv, f_data[i], | ||
2413 | (f_index * priv->hw_params->hfb_filter_size + i) * | ||
2414 | sizeof(u32)); | ||
2415 | |||
2416 | bcmgenet_hfb_set_filter_length(priv, f_index, 2 * f_length); | ||
2417 | bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f_index, rx_queue); | ||
2418 | bcmgenet_hfb_enable_filter(priv, f_index); | ||
2419 | bcmgenet_hfb_reg_writel(priv, 0x1, HFB_CTRL); | ||
2420 | |||
2421 | return 0; | ||
2422 | } | ||
2423 | |||
2424 | /* bcmgenet_hfb_clear | ||
2425 | * | ||
2426 | * Clear Hardware Filter Block and disable all filtering. | ||
2427 | */ | ||
2428 | static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv) | ||
2429 | { | ||
2430 | u32 i; | ||
2431 | |||
2432 | bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL); | ||
2433 | bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS); | ||
2434 | bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4); | ||
2435 | |||
2436 | for (i = DMA_INDEX2RING_0; i <= DMA_INDEX2RING_7; i++) | ||
2437 | bcmgenet_rdma_writel(priv, 0x0, i); | ||
2438 | |||
2439 | for (i = 0; i < (priv->hw_params->hfb_filter_cnt / 4); i++) | ||
2440 | bcmgenet_hfb_reg_writel(priv, 0x0, | ||
2441 | HFB_FLT_LEN_V3PLUS + i * sizeof(u32)); | ||
2442 | |||
2443 | for (i = 0; i < priv->hw_params->hfb_filter_cnt * | ||
2444 | priv->hw_params->hfb_filter_size; i++) | ||
2445 | bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32)); | ||
2446 | } | ||
2447 | |||
2448 | static void bcmgenet_hfb_init(struct bcmgenet_priv *priv) | ||
2449 | { | ||
2450 | if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) | ||
2451 | return; | ||
2452 | |||
2453 | bcmgenet_hfb_clear(priv); | ||
2454 | } | ||
2455 | |||
2286 | static void bcmgenet_netif_start(struct net_device *dev) | 2456 | static void bcmgenet_netif_start(struct net_device *dev) |
2287 | { | 2457 | { |
2288 | struct bcmgenet_priv *priv = netdev_priv(dev); | 2458 | struct bcmgenet_priv *priv = netdev_priv(dev); |
@@ -2348,6 +2518,9 @@ static int bcmgenet_open(struct net_device *dev) | |||
2348 | /* Always enable ring 16 - descriptor ring */ | 2518 | /* Always enable ring 16 - descriptor ring */ |
2349 | bcmgenet_enable_dma(priv, dma_ctrl); | 2519 | bcmgenet_enable_dma(priv, dma_ctrl); |
2350 | 2520 | ||
2521 | /* HFB init */ | ||
2522 | bcmgenet_hfb_init(priv); | ||
2523 | |||
2351 | ret = request_irq(priv->irq0, bcmgenet_isr0, IRQF_SHARED, | 2524 | ret = request_irq(priv->irq0, bcmgenet_isr0, IRQF_SHARED, |
2352 | dev->name, priv); | 2525 | dev->name, priv); |
2353 | if (ret < 0) { | 2526 | if (ret < 0) { |
@@ -2592,6 +2765,7 @@ static struct bcmgenet_hw_params bcmgenet_hw_params[] = { | |||
2592 | .bp_in_en_shift = 17, | 2765 | .bp_in_en_shift = 17, |
2593 | .bp_in_mask = 0x1ffff, | 2766 | .bp_in_mask = 0x1ffff, |
2594 | .hfb_filter_cnt = 48, | 2767 | .hfb_filter_cnt = 48, |
2768 | .hfb_filter_size = 128, | ||
2595 | .qtag_mask = 0x3F, | 2769 | .qtag_mask = 0x3F, |
2596 | .tbuf_offset = 0x0600, | 2770 | .tbuf_offset = 0x0600, |
2597 | .hfb_offset = 0x8000, | 2771 | .hfb_offset = 0x8000, |
@@ -2609,6 +2783,7 @@ static struct bcmgenet_hw_params bcmgenet_hw_params[] = { | |||
2609 | .bp_in_en_shift = 17, | 2783 | .bp_in_en_shift = 17, |
2610 | .bp_in_mask = 0x1ffff, | 2784 | .bp_in_mask = 0x1ffff, |
2611 | .hfb_filter_cnt = 48, | 2785 | .hfb_filter_cnt = 48, |
2786 | .hfb_filter_size = 128, | ||
2612 | .qtag_mask = 0x3F, | 2787 | .qtag_mask = 0x3F, |
2613 | .tbuf_offset = 0x0600, | 2788 | .tbuf_offset = 0x0600, |
2614 | .hfb_offset = 0x8000, | 2789 | .hfb_offset = 0x8000, |
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 2a8113898aed..1ea838946318 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h | |||
@@ -509,6 +509,7 @@ struct bcmgenet_hw_params { | |||
509 | u8 bp_in_en_shift; | 509 | u8 bp_in_en_shift; |
510 | u32 bp_in_mask; | 510 | u32 bp_in_mask; |
511 | u8 hfb_filter_cnt; | 511 | u8 hfb_filter_cnt; |
512 | u8 hfb_filter_size; | ||
512 | u8 qtag_mask; | 513 | u8 qtag_mask; |
513 | u16 tbuf_offset; | 514 | u16 tbuf_offset; |
514 | u32 hfb_offset; | 515 | u32 hfb_offset; |