diff options
| author | Mugunthan V N <mugunthanvnm@ti.com> | 2013-02-11 04:52:20 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-02-12 16:15:10 -0500 |
| commit | d9ba8f9e6298af71ec1c1fd3d88c3ef68abd0ec3 (patch) | |
| tree | 933b4cdcb097e37e8eaea6970f791e862040bd0b | |
| parent | 9232b16df2167c8afcb89de39ee85f5091ebacff (diff) | |
driver: net: ethernet: cpsw: dual emac interface implementation
The CPSW switch can act as Dual EMAC by segregating the switch ports
using VLAN and port VLAN as per the TRM description in
14.3.2.10.2 Dual Mac Mode
Following CPSW components will be common for both the interfaces.
* Interrupt source is common for both eth interfaces
* Interrupt pacing is common for both interfaces
* Hardware statistics is common for all the ports
* CPDMA is common for both eth interface
* CPTS is common for both the interface and it should not be enabled on
both the interface as timestamping information doesn't contain port
information.
Constrains
* Reserved VID of One port should not be used in other interface which will
enable switching functionality
* Same VID must not be used in both the interface which will enable switching
functionality
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | Documentation/devicetree/bindings/net/cpsw.txt | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 335 | ||||
| -rw-r--r-- | include/linux/platform_data/cpsw.h | 3 |
3 files changed, 288 insertions, 52 deletions
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 6ddd0286a9b7..ecfdf756d10f 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt | |||
| @@ -24,6 +24,8 @@ Required properties: | |||
| 24 | Optional properties: | 24 | Optional properties: |
| 25 | - ti,hwmods : Must be "cpgmac0" | 25 | - ti,hwmods : Must be "cpgmac0" |
| 26 | - no_bd_ram : Must be 0 or 1 | 26 | - no_bd_ram : Must be 0 or 1 |
| 27 | - dual_emac : Specifies Switch to act as Dual EMAC | ||
| 28 | - dual_emac_res_vlan : Specifies VID to be used to segregate the ports | ||
| 27 | 29 | ||
| 28 | Note: "ti,hwmods" field is used to fetch the base address and irq | 30 | Note: "ti,hwmods" field is used to fetch the base address and irq |
| 29 | resources from TI, omap hwmod data base during device registration. | 31 | resources from TI, omap hwmod data base during device registration. |
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 4b964bb02d4c..4ceed6e0f1be 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c | |||
| @@ -122,6 +122,10 @@ do { \ | |||
| 122 | #define CPSW_VLAN_AWARE BIT(1) | 122 | #define CPSW_VLAN_AWARE BIT(1) |
| 123 | #define CPSW_ALE_VLAN_AWARE 1 | 123 | #define CPSW_ALE_VLAN_AWARE 1 |
| 124 | 124 | ||
| 125 | #define CPSW_FIFO_NORMAL_MODE (0 << 15) | ||
| 126 | #define CPSW_FIFO_DUAL_MAC_MODE (1 << 15) | ||
| 127 | #define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15) | ||
| 128 | |||
| 125 | #define cpsw_enable_irq(priv) \ | 129 | #define cpsw_enable_irq(priv) \ |
| 126 | do { \ | 130 | do { \ |
| 127 | u32 i; \ | 131 | u32 i; \ |
| @@ -254,7 +258,7 @@ struct cpsw_ss_regs { | |||
| 254 | struct cpsw_host_regs { | 258 | struct cpsw_host_regs { |
| 255 | u32 max_blks; | 259 | u32 max_blks; |
| 256 | u32 blk_cnt; | 260 | u32 blk_cnt; |
| 257 | u32 flow_thresh; | 261 | u32 tx_in_ctl; |
| 258 | u32 port_vlan; | 262 | u32 port_vlan; |
| 259 | u32 tx_pri_map; | 263 | u32 tx_pri_map; |
| 260 | u32 cpdma_tx_pri_map; | 264 | u32 cpdma_tx_pri_map; |
| @@ -281,6 +285,9 @@ struct cpsw_slave { | |||
| 281 | u32 mac_control; | 285 | u32 mac_control; |
| 282 | struct cpsw_slave_data *data; | 286 | struct cpsw_slave_data *data; |
| 283 | struct phy_device *phy; | 287 | struct phy_device *phy; |
| 288 | struct net_device *ndev; | ||
| 289 | u32 port_vlan; | ||
| 290 | u32 open_stat; | ||
| 284 | }; | 291 | }; |
| 285 | 292 | ||
| 286 | static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) | 293 | static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) |
| @@ -320,15 +327,63 @@ struct cpsw_priv { | |||
| 320 | u32 irqs_table[4]; | 327 | u32 irqs_table[4]; |
| 321 | u32 num_irqs; | 328 | u32 num_irqs; |
| 322 | struct cpts *cpts; | 329 | struct cpts *cpts; |
| 330 | u32 emac_port; | ||
| 323 | }; | 331 | }; |
| 324 | 332 | ||
| 325 | #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) | 333 | #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) |
| 326 | #define for_each_slave(priv, func, arg...) \ | 334 | #define for_each_slave(priv, func, arg...) \ |
| 327 | do { \ | 335 | do { \ |
| 328 | int idx; \ | 336 | int idx; \ |
| 329 | for (idx = 0; idx < (priv)->data.slaves; idx++) \ | 337 | if (priv->data.dual_emac) \ |
| 330 | (func)((priv)->slaves + idx, ##arg); \ | 338 | (func)((priv)->slaves + priv->emac_port, ##arg);\ |
| 339 | else \ | ||
| 340 | for (idx = 0; idx < (priv)->data.slaves; idx++) \ | ||
| 341 | (func)((priv)->slaves + idx, ##arg); \ | ||
| 342 | } while (0) | ||
| 343 | #define cpsw_get_slave_ndev(priv, __slave_no__) \ | ||
| 344 | (priv->slaves[__slave_no__].ndev) | ||
| 345 | #define cpsw_get_slave_priv(priv, __slave_no__) \ | ||
| 346 | ((priv->slaves[__slave_no__].ndev) ? \ | ||
| 347 | netdev_priv(priv->slaves[__slave_no__].ndev) : NULL) \ | ||
| 348 | |||
| 349 | #define cpsw_dual_emac_src_port_detect(status, priv, ndev, skb) \ | ||
| 350 | do { \ | ||
| 351 | if (!priv->data.dual_emac) \ | ||
| 352 | break; \ | ||
| 353 | if (CPDMA_RX_SOURCE_PORT(status) == 1) { \ | ||
| 354 | ndev = cpsw_get_slave_ndev(priv, 0); \ | ||
| 355 | priv = netdev_priv(ndev); \ | ||
| 356 | skb->dev = ndev; \ | ||
| 357 | } else if (CPDMA_RX_SOURCE_PORT(status) == 2) { \ | ||
| 358 | ndev = cpsw_get_slave_ndev(priv, 1); \ | ||
| 359 | priv = netdev_priv(ndev); \ | ||
| 360 | skb->dev = ndev; \ | ||
| 361 | } \ | ||
| 331 | } while (0) | 362 | } while (0) |
| 363 | #define cpsw_add_mcast(priv, addr) \ | ||
| 364 | do { \ | ||
| 365 | if (priv->data.dual_emac) { \ | ||
| 366 | struct cpsw_slave *slave = priv->slaves + \ | ||
| 367 | priv->emac_port; \ | ||
| 368 | int slave_port = cpsw_get_slave_port(priv, \ | ||
| 369 | slave->slave_num); \ | ||
| 370 | cpsw_ale_add_mcast(priv->ale, addr, \ | ||
| 371 | 1 << slave_port | 1 << priv->host_port, \ | ||
| 372 | ALE_VLAN, slave->port_vlan, 0); \ | ||
| 373 | } else { \ | ||
| 374 | cpsw_ale_add_mcast(priv->ale, addr, \ | ||
| 375 | ALE_ALL_PORTS << priv->host_port, \ | ||
| 376 | 0, 0, 0); \ | ||
| 377 | } \ | ||
| 378 | } while (0) | ||
| 379 | |||
| 380 | static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) | ||
| 381 | { | ||
| 382 | if (priv->host_port == 0) | ||
| 383 | return slave_num + 1; | ||
| 384 | else | ||
| 385 | return slave_num; | ||
| 386 | } | ||
| 332 | 387 | ||
| 333 | static void cpsw_ndo_set_rx_mode(struct net_device *ndev) | 388 | static void cpsw_ndo_set_rx_mode(struct net_device *ndev) |
| 334 | { | 389 | { |
| @@ -348,8 +403,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) | |||
| 348 | 403 | ||
| 349 | /* program multicast address list into ALE register */ | 404 | /* program multicast address list into ALE register */ |
| 350 | netdev_for_each_mc_addr(ha, ndev) { | 405 | netdev_for_each_mc_addr(ha, ndev) { |
| 351 | cpsw_ale_add_mcast(priv->ale, (u8 *)ha->addr, | 406 | cpsw_add_mcast(priv, (u8 *)ha->addr); |
| 352 | ALE_ALL_PORTS << priv->host_port, 0, 0, 0); | ||
| 353 | } | 407 | } |
| 354 | } | 408 | } |
| 355 | } | 409 | } |
| @@ -396,6 +450,8 @@ void cpsw_rx_handler(void *token, int len, int status) | |||
| 396 | struct cpsw_priv *priv = netdev_priv(ndev); | 450 | struct cpsw_priv *priv = netdev_priv(ndev); |
| 397 | int ret = 0; | 451 | int ret = 0; |
| 398 | 452 | ||
| 453 | cpsw_dual_emac_src_port_detect(status, priv, ndev, skb); | ||
| 454 | |||
| 399 | /* free and bail if we are shutting down */ | 455 | /* free and bail if we are shutting down */ |
| 400 | if (unlikely(!netif_running(ndev)) || | 456 | if (unlikely(!netif_running(ndev)) || |
| 401 | unlikely(!netif_carrier_ok(ndev))) { | 457 | unlikely(!netif_carrier_ok(ndev))) { |
| @@ -437,18 +493,17 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id) | |||
| 437 | cpsw_intr_disable(priv); | 493 | cpsw_intr_disable(priv); |
| 438 | cpsw_disable_irq(priv); | 494 | cpsw_disable_irq(priv); |
| 439 | napi_schedule(&priv->napi); | 495 | napi_schedule(&priv->napi); |
| 496 | } else { | ||
| 497 | priv = cpsw_get_slave_priv(priv, 1); | ||
| 498 | if (likely(priv) && likely(netif_running(priv->ndev))) { | ||
| 499 | cpsw_intr_disable(priv); | ||
| 500 | cpsw_disable_irq(priv); | ||
| 501 | napi_schedule(&priv->napi); | ||
| 502 | } | ||
| 440 | } | 503 | } |
| 441 | return IRQ_HANDLED; | 504 | return IRQ_HANDLED; |
| 442 | } | 505 | } |
| 443 | 506 | ||
| 444 | static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) | ||
| 445 | { | ||
| 446 | if (priv->host_port == 0) | ||
| 447 | return slave_num + 1; | ||
| 448 | else | ||
| 449 | return slave_num; | ||
| 450 | } | ||
| 451 | |||
| 452 | static int cpsw_poll(struct napi_struct *napi, int budget) | 507 | static int cpsw_poll(struct napi_struct *napi, int budget) |
| 453 | { | 508 | { |
| 454 | struct cpsw_priv *priv = napi_to_priv(napi); | 509 | struc |
