diff options
author | Sebastian Siewior <bigeasy@linutronix.de> | 2013-04-30 08:16:52 (GMT) |
---|---|---|
committer | Emil Medve <Emilian.Medve@Freescale.com> | 2013-04-30 08:16:52 (GMT) |
commit | fe14a3e9f04d8c3cbeb37672732b1a88ed150f91 (patch) | |
tree | 0ceb327ef755a726d5f8605b15c3c42b50ce60d4 /drivers/net/ethernet/ti | |
parent | 0c174f0ffa7face4670b88d0b9c326b2da790b28 (diff) | |
download | linux-fsl-qoriq-fe14a3e9f04d8c3cbeb37672732b1a88ed150f91.tar.xz |
net/cpsw: giant cpsw patch
This patch contains:
git diff v3.8..net-merge drivers/net/ethernet/ti arch/arm/boot/dts/am33* include/linux/platform_data/cpsw.h
where the net-merge branch contains the following branches merged:
net-next as of 37fe066 net: fix address check in rtnl_fdb_del
net as of b9e48de isdn/sc: Fix incorrect module_param_array types
linus as of 697dfd8 Merge tag 'efi-urgent' into x86/urgent
including the following patches:
|commit 15c6ff3bc0ff3464a8c7efcdea09c86454571622
|Author: Jiri Pirko <jiri@resnulli.us>
|Date: Tue Jan 1 03:30:17 2013 +0000
|
| net: remove unnecessary NET_ADDR_RANDOM "bitclean"
|
| NET_ADDR_SET is set in dev_set_mac_address() no need to alter
| dev->addr_assign_type value in drivers.
|
| Signed-off-by: Jiri Pirko <jiri@resnulli.us>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 7826d43f2db45c9305a6e0ba165650e1a203f517
|Author: Jiri Pirko <jiri@resnulli.us>
|Date: Sun Jan 6 00:44:26 2013 +0000
|
| ethtool: fix drvinfo strings set in drivers
|
| Use strlcpy where possible to ensure the string is \0 terminated.
| Use always sizeof(string) instead of 32, ETHTOOL_BUSINFO_LEN
| and custom defines.
| Use snprintf instead of sprint.
| Remove unnecessary inits of ->fw_version
| Remove unnecessary inits of drvinfo struct.
|
| Signed-off-by: Jiri Pirko <jiri@resnulli.us>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 8ff25eebb8161d08ddc48539e6b4afa0b18d778f
|Author: Kees Cook <keescook@chromium.org>
|Date: Tue Oct 2 11:18:24 2012 -0700
|
| drivers/net/ethernet/ti: remove depends on CONFIG_EXPERIMENTAL
|
| The CONFIG_EXPERIMENTAL config item has not carried much meaning for a
| while now and is almost always enabled by default. As agreed during the
| Linux kernel summit, remove it from any "depends on" lines in Kconfigs.
|
| CC: Tony Lindgren <tony@atomide.com>
| CC: Mugunthan V N <mugunthanvnm@ti.com>
| CC: Kevin Hilman <khilman@ti.com>
| CC: "David S. Miller" <davem@davemloft.net>
| CC: Cyril Chemparathy <cyril@ti.com>
| Signed-off-by: Kees Cook <keescook@chromium.org>
| Acked-by: David S. Miller <davem@davemloft.net>
|
|commit f9a8f83b04e0c362a2fc660dbad980d24af209fc
|Author: Florian Fainelli <florian@openwrt.org>
|Date: Mon Jan 14 00:52:52 2013 +0000
|
| net: phy: remove flags argument from phy_{attach, connect, connect_direct}
|
| The flags argument of the phy_{attach,connect,connect_direct} functions
| is then used to assign a struct phy_device dev_flags with its value.
| All callers but the tg3 driver pass the flag 0, which results in the
| underlying PHY drivers in drivers/net/phy/ not being able to actually
| use any of the flags they would set in dev_flags. This patch gets rid of
| the flags argument, and passes phydev->dev_flags to the internal PHY
| library call phy_attach_direct() such that drivers which actually modify
| a phy device dev_flags get the value preserved for use by the underlying
| phy driver.
|
| Acked-by: Kosta Zertsekel <konszert@marvell.com>
| Signed-off-by: Florian Fainelli <florian@openwrt.org>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit fae50823d0ee579e006a7ba2b20880e354388b25
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Thu Jan 17 06:31:34 2013 +0000
|
| net: ethernet: davinci_cpdma: Add boundary for rx and tx descriptors
|
| When there is heavy transmission traffic in the CPDMA, then Rx descriptors
| memory is also utilized as tx desc memory looses all rx descriptors and the
| driver stops working then.
|
| This patch adds boundary for tx and rx descriptors in bd ram dividing the
| descriptor memory to ensure that during heavy transmission tx doesn't use
| rx descriptors.
|
| This patch is already applied to davinci_emac driver, since CPSW and
| davici_dmac shares the same CPDMA, moving the boundry seperation from
| Davinci EMAC driver to CPDMA driver which was done in the following
| commit
|
| commit 86d8c07ff2448eb4e860e50f34ef6ee78e45c40c
| Author: Sascha Hauer <s.hauer@pengutronix.de>
| Date: Tue Jan 3 05:27:47 2012 +0000
|
| net/davinci: do not use all descriptors for tx packets
|
| The driver uses a shared pool for both rx and tx descriptors.
| During open it queues fixed number of 128 descriptors for receive
| packets. For each received packet it tries to queue another
| descriptor. If this fails the descriptor is lost for rx.
| The driver has no limitation on tx descriptors to use, so it
| can happen during a nmap / ping -f attack that the driver
| allocates all descriptors for tx and looses all rx descriptors.
| The driver stops working then.
| To fix this limit the number of tx descriptors used to half of
| the descriptors available, the rx path uses the other half.
|
| Tested on a custom board using nmap / ping -f to the board from
| two different hosts.
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 7373470202a3c98e1a338bf1acf51247cd100868
|Author: Thierry Reding <thierry.reding@avionic-design.de>
|Date: Mon Jan 21 10:38:39 2013 +0100
|
| net: ethernet: davinci: Fix build breakage
|
| The correct name of the transmit DMA channel field in struct emac_priv
| is txchan, not txch.
|
| Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit b2adaca92c63b9bb8beb021d554f656e387a7648
|Author: Joe Perches <joe@perches.com>
|Date: Sun Feb 3 17:43:58 2013 +0000
|
| ethernet: Remove unnecessary alloc/OOM messages, alloc cleanups
|
| alloc failures already get standardized OOM
| messages and a dump_stack.
|
| Convert kzalloc's with multiplies to kcalloc.
| Convert kmalloc's with multiplies to kmalloc_array.
| Fix a few whitespace defects.
| Convert a constant 6 to ETH_ALEN.
| Use parentheses around sizeof.
| Convert vmalloc/memset to vzalloc.
| Remove now unused size variables.
|
| Signed-off-by: Joe Perches <joe@perches.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit e11b220f336c654db876027d40953acef90b0cae
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Tue Feb 5 08:26:47 2013 +0000
|
| drivers: net: cpsw: Add helper functions for VLAN ALE implementation
|
| Add helper functions for VLAN ALE implementations for Add, Delete
| Dump VLAN related ALE entries
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 3b72c2fe0c6bbec42ed7f899931daef227b80322
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Tue Feb 5 08:26:48 2013 +0000
|
| drivers: net:ethernet: cpsw: add support for VLAN
|
| adding support for VLAN interface for cpsw.
|
| CPSW VLAN Capability
| * Can filter VLAN packets in Hardware
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit f6575c90f6fc637697f130ea4a05892296c9a473
|Author: Vaibhav Bedia <vaibhav.bedia@ti.com>
|Date: Tue Jan 29 16:45:07 2013 +0530
|
| ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3
|
| Since AM33XX supports only DT-boot, this is needed
| for the appropriate device nodes to be created.
|
| Note: OCMC RAM is part of the PER power domain and supports
| retention. The assembly code for low power entry/exit will
| run from OCMC RAM. To ensure that the OMAP PM code does not
| attempt to disable the clock to OCMC RAM as part of the
| suspend process add the no_idle_on_suspend flag.
|
| Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
| Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
| Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
| Signed-off-by: Paul Walmsley <paul@pwsan.com>
|
|commit f6e135c81eeb648c6addc6aeff2ee80f28ea413b
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Feb 11 09:52:18 2013 +0000
|
| driver: net: ethernet: davinci_cpdma: add support for directed packet and source port detection
|
| * Introduced parameter to add port number for directed packet in cpdma_chan_submit
| * Source port detection macro with DMA descriptor status
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 9232b16df2167c8afcb89de39ee85f5091ebacff
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Feb 11 09:52:19 2013 +0000
|
| driver: net: ethernet: cpsw: make cpts as pointer
|
| As CPTS is common module for both EMAC in Dual EMAC mode so making cpts as
| pointer.
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit d9ba8f9e6298af71ec1c1fd3d88c3ef68abd0ec3
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Feb 11 09:52:20 2013 +0000
|
| 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>
|
|commit 79876e0394aa46e74267a5871c4f4469544dcacf
|Author: Cyril Roelandt <tipecaml@gmail.com>
|Date: Tue Feb 12 12:52:30 2013 +0000
|
| net: ethernet: ti: remove redundant NULL check.
|
| cpdma_chan_destroy() on a NULL pointer is a no-op, so the NULL check in
| cpdma_ctlr_destroy() can safely be removed.
|
| Signed-off-by: Cyril Roelandt <tipecaml@gmail.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 6929e24e4cc46ce8d5b7dd8f8bdf4244c8d77f76
|Author: Arnd Bergmann <arnd@arndb.de>
|Date: Thu Feb 14 17:53:01 2013 +0100
|
| net: cwdavinci_cpdma: export symbols for cpsw
|
| With the support for ARM AM33xx in multiplatform kernels
| in 3.9, an older bug appears in ARM allmodconfig:
| When the cpsw driver is built as a module with cpdma
| support enabled, it uses symbols that the cpdma driver
| does not export.
|
| Without this patch, building allmodconfig results in:
|
| ERROR: "cpdma_ctlr_int_ctrl" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined!
| ERROR: "cpdma_control_set" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined!
| ERROR: "cpdma_ctlr_eoi" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined!
|
| Signed-off-by: Arnd Bergmann <arnd@arndb.de>
| Acked-by: David S. Miller <davem@davemloft.net>
| Cc: Mugunthan V N <mugunthanvnm@ti.com>
| Cc: Vaibhav Hiremath <hvaibhav@ti.com>
| Cc: Richard Cochran <richardcochran@gmail.com>
| Cc: netdev@vger.kernel.org
|
|commit 510a1e7249298f6bbd049e1ec98041ddf5ef6452
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Sun Feb 17 22:19:20 2013 +0000
|
| drivers: net: davinci_cpdma: acknowledge interrupt properly
|
| CPDMA interrupts are not properly acknowledged which leads to interrupt
| storm, only cpdma interrupt 0 is acknowledged in Davinci CPDMA driver.
| Changed cpdma_ctlr_eoi api to acknowledge 1 and 2 interrupts which are
| used for rx and tx respectively.
|
| Reported-by: Pantelis Antoniou <panto@antoniou-consulting.com>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 06991c28f37ad68e5c03777f5c3b679b56e3dac1
|Merge: 460dc1e 74fef7a
|Author: Linus Torvalds <torvalds@linux-foundation.org>
|Date: Thu Feb 21 12:05:51 2013 -0800
|
| Merge tag 'driver-core-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
|
| Pull driver core patches from Greg Kroah-Hartman:
| "Here is the big driver core merge for 3.9-rc1
|
| There are two major series here, both of which touch lots of drivers
| all over the kernel, and will cause you some merge conflicts:
|
| - add a new function called devm_ioremap_resource() to properly be
| able to check return values.
|
| - remove CONFIG_EXPERIMENTAL
|
| Other than those patches, there's not much here, some minor fixes and
| updates"
|
| Fix up trivial conflicts
|
| * tag 'driver-core-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (221 commits)
| base: memory: fix soft/hard_offline_page permissions
| drivercore: Fix ordering between deferred_probe and exiting initcalls
| backlight: fix class_find_device() arguments
| TTY: mark tty_get_device call with the proper const values
| driver-core: constify data for class_find_device()
| firmware: Ignore abort check when no user-helper is used
| firmware: Reduce ifdef CONFIG_FW_LOADER_USER_HELPER
| firmware: Make user-mode helper optional
| firmware: Refactoring for splitting user-mode helper code
| Driver core: treat unregistered bus_types as having no devices
| watchdog: Convert to devm_ioremap_resource()
| thermal: Convert to devm_ioremap_resource()
| spi: Convert to devm_ioremap_resource()
| power: Convert to devm_ioremap_resource()
| mtd: Convert to devm_ioremap_resource()
| mmc: Convert to devm_ioremap_resource()
| mfd: Convert to devm_ioremap_resource()
| media: Convert to devm_ioremap_resource()
| iommu: Convert to devm_ioremap_resource()
| drm: Convert to devm_ioremap_resource()
| ...
|
|commit 3298a3511f1e73255a8dc023efd909e569eea037
|Merge: 5ce7aba acb7452
|Author: Linus Torvalds <torvalds@linux-foundation.org>
|Date: Thu Feb 21 15:20:41 2013 -0800
|
| Merge tag 'multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
|
| Pull ARM SoC multiplatform support from Arnd Bergmann:
| "Converting more ARM platforms to multiplatform support. This time,
| OMAP gets converted, which is a major step since this is by far the
| largest platform in terms of code size. The same thing happens to the
| vt8500 platform."
|
| * tag 'multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc:
| net: cwdavinci_cpdma: export symbols for cpsw
| remoteproc: omap: depend on OMAP_MBOX_FWK
| [media] davinci: do not include mach/hardware.h
| ARM: OMAP2+: Make sure files with omap initcalls include soc.h
| ARM: OMAP2+: Include soc.h to drm.c to fix compiling
| ARM: OMAP2+: Fix warning for hwspinlock omap_postcore_initcall
| ARM: multi_v7_defconfig: add ARCH_ZYNQ
| ARM: multi_v7_defconfig: remove unnecessary CONFIG_GPIOLIB
| arm: vt8500: Remove remaining mach includes
| arm: vt8500: Convert debug-macro.S to be multiplatform friendly
| arm: vt8500: Remove single platform Kconfig options
| ARM: OMAP2+: Remove now obsolete uncompress.h and debug-macro.S
| ARM: OMAP2+: Add minimal support for booting vexpress
| ARM: OMAP2+: Enable ARCH_MULTIPLATFORM support
| ARM: OMAP2+: Disable code that currently does not work with multiplaform
| ARM: OMAP2+: Add multiplatform debug_ll support
| ARM: OMAP: Fix dmaengine init for multiplatform
| ARM: OMAP: Fix i2c cmdline initcall for multiplatform
| ARM: OMAP2+: Use omap initcalls
| ARM: OMAP2+: Limit omap initcalls to omap only on multiplatform kernels
|
|commit 0237c11044b3670adcbe80cd6dd721285347f497
|Author: Daniel Mack <zonque@gmail.com>
|Date: Tue Feb 26 04:06:20 2013 +0000
|
| drivers: net: ethernet: cpsw: consider number of slaves in interation
|
| Make cpsw_add_default_vlan() look at the actual number of slaves for its
| iteration, so boards with less than 2 slaves don't ooops at boot.
|
| Signed-off-by: Daniel Mack <zonque@gmail.com>
| Cc: Mugunthan V N <mugunthanvnm@ti.com>
| Cc: David S. Miller <davem@davemloft.net>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 7307c00f335a4e986586b12334696098d2fc2bcd
|Merge: f8f466c 55ccb1a
|Author: Linus Torvalds <torvalds@linux-foundation.org>
|Date: Thu Feb 28 20:00:40 2013 -0800
|
| Merge tag 'late-omap' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
|
| Pull ARM SoC late OMAP changes from Olof Johansson:
| "This branch contains changes for OMAP that came in late during the
| release staging, close to when the merge window opened.
|
| It contains, among other things:
|
| - OMAP PM fixes and some patches for audio device integration
| - OMAP clock fixes related to common clock conversion
| - A set of patches cleaning up WFI entry and blocking.
| - A set of fixes and IP block support for PM on TI AM33xx SoCs
| (Beaglebone, etc)
| - A set of smaller fixes and cleanups around AM33xx restart and
| revision detection, as well as removal of some dead code
| (CONFIG_32K_TIMER_HZ)"
|
| * tag 'late-omap' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (34 commits)
| ARM: omap2: include linux/errno.h in hwmod_reset
| ARM: OMAP2+: fix some omap_device_build() calls that aren't compiled by default
| ARM: OMAP4: hwmod data: Enable AESS hwmod device
| ARM: OMAP4: hwmod data: Update AESS data with memory bank area
| ARM: OMAP4+: AESS: enable internal auto-gating during initial setup
| ASoC: TI AESS: add autogating-enable function, callable from architecture code
| ARM: OMAP2+: hwmod: add enable_preprogram hook
| ARM: OMAP4: clock data: Add missing clkdm association for dpll_usb
| ARM: OMAP2+: PM: Fix the dt return condition in pm_late_init()
| ARM: OMAP2: am33xx-hwmod: Fix "register offset NULL check" bug
| ARM: OMAP2+: AM33xx: hwmod: add missing HWMOD_NO_IDLEST flags
| ARM: OMAP: AM33xx hwmod: Add parent-child relationship for PWM subsystem
| ARM: OMAP: AM33xx hwmod: Corrects PWM subsystem HWMOD entries
| ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3
| ARM: OMAP2+: AM33XX: Update the hardreset API
| ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset status bit
| ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry
| ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags
| ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod
| ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files
| ...
|
|commit 9da060d0ed571bbff434c4a1ef3e48db99a37ee0
|Merge: e3b5951 aab2b4b
|Author: Linus Torvalds <torvalds@linux-foundation.org>
|Date: Tue Mar 5 18:42:29 2013 -0800
|
| Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
|
| Pull networking fixes from David Miller:
| "A moderately sized pile of fixes, some specifically for merge window
| introduced regressions although others are for longer standing items
| and have been queued up for -stable.
|
| I'm kind of tired of all the RDS protocol bugs over the years, to be
| honest, it's way out of proportion to the number of people who
| actually use it.
|
| 1) Fix missing range initialization in netfilter IPSET, from Jozsef
| Kadlecsik.
|
| 2) ieee80211_local->tim_lock needs to use BH disabling, from Johannes
| Berg.
|
| 3) Fix DMA syncing in SFC driver, from Ben Hutchings.
|
| 4) Fix regression in BOND device MAC address setting, from Jiri
| Pirko.
|
| 5) Missing usb_free_urb in ISDN Hisax driver, from Marina Makienko.
|
| 6) Fix UDP checksumming in bnx2x driver for 57710 and 57711 chips,
| fix from Dmitry Kravkov.
|
| 7) Missing cfgspace_lock initialization in BCMA driver.
|
| 8) Validate parameter size for SCTP assoc stats getsockopt(), from
| Guenter Roeck.
|
| 9) Fix SCTP association hangs, from Lee A Roberts.
|
| 10) Fix jumbo frame handling in r8169, from Francois Romieu.
|
| 11) Fix phy_device memory leak, from Petr Malat.
|
| 12) Omit trailing FCS from frames received in BGMAC driver, from Hauke
| Mehrtens.
|
| 13) Missing socket refcount release in L2TP, from Guillaume Nault.
|
| 14) sctp_endpoint_init should respect passed in gfp_t, rather than use
| GFP_KERNEL unconditionally. From Dan Carpenter.
|
| 15) Add AISX AX88179 USB driver, from Freddy Xin.
|
| 16) Remove MAINTAINERS entries for drivers deleted during the merge
| window, from Cesar Eduardo Barros.
|
| 17) RDS protocol can try to allocate huge amounts of memory, check
| that the user's request length makes sense, from Cong Wang.
|
| 18) SCTP should use the provided KMALLOC_MAX_SIZE instead of it's own,
| bogus, definition. From Cong Wang.
|
| 19) Fix deadlocks in FEC driver by moving TX reclaim into NAPI poll,
| from Frank Li. Also, fix a build error introduced in the merge
| window.
|
| 20) Fix bogus purging of default routes in ipv6, from Lorenzo Colitti.
|
| 21) Don't double count RTT measurements when we leave the TCP receive
| fast path, from Neal Cardwell."
|
| * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (61 commits)
| tcp: fix double-counted receiver RTT when leaving receiver fast path
| CAIF: fix sparse warning for caif_usb
| rds: simplify a warning message
| net: fec: fix build error in no MXC platform
| net: ipv6: Don't purge default router if accept_ra=2
| net: fec: put tx to napi poll function to fix dead lock
| sctp: use KMALLOC_MAX_SIZE instead of its own MAX_KMALLOC_SIZE
| rds: limit the size allocated by rds_message_alloc()
| MAINTAINERS: remove eexpress
| MAINTAINERS: remove drivers/net/wan/cycx*
| MAINTAINERS: remove 3c505
| caif_dev: fix sparse warnings for caif_flow_cb
| ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
| sctp: use the passed in gfp flags instead GFP_KERNEL
| ipv[4|6]: correct dropwatch false positive in local_deliver_finish
| l2tp: Restore socket refcount when sendmsg succeeds
| net/phy: micrel: Disable asymmetric pause for KSZ9021
| bgmac: omit the fcs
| phy: Fix phy_device_free memory leak
| bnx2x: Fix KR2 work-around condition
| ...
|
|commit 720a43efd30f04a0a492c85fb997361c44fbae05
|Author: Joe Perches <joe@perches.com>
|Date: Fri Mar 8 15:03:25 2013 +0000
|
| drivers:net: Remove unnecessary OOM messages after netdev_alloc_skb
|
| Emitting netdev_alloc_skb and netdev_alloc_skb_ip_align OOM
| messages is unnecessary as there is already a dump_stack
| after allocation failures.
|
| Other trivial changes around these removals:
|
| Convert a few comparisons of pointer to 0 to !pointer.
| Change flow to remove unnecessary label.
| Remove now unused variable.
| Hoist assignment from if.
|
| Signed-off-by: Joe Perches <joe@perches.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit e86ac13b031cf71d8f40ff513e627aac80e6b765
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Mar 11 23:16:35 2013 +0000
|
| drivers: net: ethernet: cpsw: change cpts_active_slave to active_slave
|
| Change cpts_active_slave to active_slave so that the same DT property
| can be used to ethtool and SIOCGMIIPHY.
|
| CC: Richard Cochran <richardcochran@gmail.com>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit d3bb9c58b567d240eaaa2dc8bd778696eaed5fbd
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Mar 11 23:16:36 2013 +0000
|
| driver: net: ethernet: cpsw: implement ethtool get/set phy setting
|
| This patch implements get/set of the phy settings via ethtool apis
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit ff5b8ef2ef3af0fd7e1cf6c8c1ed9ec5afbda422
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Mar 11 23:16:37 2013 +0000
|
| driver: net: ethernet: cpsw: implement interrupt pacing via ethtool
|
| This patch implements support for interrupt pacing block of CPSW via ethtool
| Inetrrupt pacing block is common of both the ethernet interface in
| dual emac mode
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 11f2c988382b880e602a005c26436043c5d2c274
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Mar 11 23:16:38 2013 +0000
|
| drivers: net: ethernet: cpsw: implement get phy_id via ioctl
|
| Implement get phy_id via ioctl SIOCGMIIPHY. In switch mode active phy_id
| is returned and in dual EMAC mode slave's specific phy_id is returned.
|
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit d35162f89b8f00537d7b240b76d2d0e8b8d29aa0
|Author: Daniel Mack <zonque@gmail.com>
|Date: Tue Mar 12 06:31:19 2013 +0000
|
| net: ethernet: cpsw: fix usage of cpdma_check_free_tx_desc()
|
| Commit fae50823d0 ("net: ethernet: davinci_cpdma: Add boundary for rx
| and tx descriptors") introduced a function to check the current
| allocation state of tx packets. The return value is taken into account
| to stop the netqork queue on the adapter in case there are no free
| slots.
|
| However, cpdma_check_free_tx_desc() returns 'true' if there is room in
| the bitmap, not 'false', so the usage of the function is wrong.
|
| Signed-off-by: Daniel Mack <zonque@gmail.com>
| Cc: Mugunthan V N <mugunthanvnm@ti.com>
| Reported-by: Sven Neumann <s.neumann@raumfeld.com>
| Reported-by: Andreas Fenkart <andreas.fenkart@streamunlimited.com>
| Tested-by: Mugunthan V N <mugunthanvnm@ti.com>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Tested-by: Andreas Fenkart <andreas.fenkart@streamunlimited.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 75b9b61bb8a18e75afe7b10dd55681e748fa27df
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Fri Mar 15 04:10:16 2013 +0000
|
| drivers: net: ethernet: ti: davinci_emac: fix usage of cpdma_check_free_tx_desc()
|
| Fix which was done in the following commit in cpsw driver has
| to be taken forward to davinci emac driver as well.
|
| commit d35162f89b8f00537d7b240b76d2d0e8b8d29aa0
| Author: Daniel Mack <zonque@gmail.com>
| Date: Tue Mar 12 06:31:19 2013 +0000
|
| net: ethernet: cpsw: fix usage of cpdma_check_free_tx_desc()
|
| Commit fae50823d0 ("net: ethernet: davinci_cpdma: Add boundary for rx
| and tx descriptors") introduced a function to check the current
| allocation state of tx packets. The return value is taken into account
| to stop the netqork queue on the adapter in case there are no free
| slots.
|
| However, cpdma_check_free_tx_desc() returns 'true' if there is room in
| the bitmap, not 'false', so the usage of the function is wrong.
|
| Reported-by: Prabhakar Lad <prabhakar.csengg@gmail.com>
| Tested-by: Prabhakar Lad <prabhakar.csengg@gmail.com>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 61816596d1c9026d0ecb20c44f90452c41596ffe
|Merge: 23a9072 da2191e
|Author: David S. Miller <davem@davemloft.net>
|Date: Wed Mar 20 12:46:26 2013 -0400
|
| Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
|
| Pull in the 'net' tree to get Daniel Borkmann's flow dissector
| infrastructure change.
|
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit e052a5893b78d43bd183c6cc33bc346efe6bc6e5
|Author: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
|Date: Wed Mar 20 05:01:45 2013 +0000
|
| net: ethernet: davinci_emac: make local function emac_poll_controller() static
|
| emac_poll_controller() was not declared. It should be static.
|
| Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit ce16294fda230c787ce5c35f61b2f80d14d70a72
|Author: Lothar Waßmann <LW@KARO-electronics.de>
|Date: Thu Mar 21 02:20:11 2013 +0000
|
| net: ethernet: cpsw: fix erroneous condition in error check
|
| The error check in cpsw_probe_dt() has an '&&' where an '||' is
| meant to be. This causes a NULL pointer dereference when incomplet DT
| data is passed to the driver ('phy_id' property for cpsw_emac1
| missing).
|
| Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit ea3d1cc285bf1ae1fa81b47418cd7fd79990bb06
|Merge: 2fa70df f4541d6
|Author: David S. Miller <davem@davemloft.net>
|Date: Fri Mar 22 12:53:09 2013 -0400
|
| Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
|
| Pull to get the thermal netlink multicast group name fix, otherwise
| the assertion added in net-next to netlink to detect that kind of bug
| makes systems unbootable for some folks.
|
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit b8092861efd827deb8d84292674704ee8bf41b04
|Author: Sekhar Nori <nsekhar@ti.com>
|Date: Sun Mar 24 23:25:46 2013 +0000
|
| net/davinci_emac: use devres APIs
|
| Use devres APIs where possible to simplify error handling
| in driver probe.
|
| While at it, also rename the goto targets in error path to
| introduce some consistency in how they are named.
|
| Signed-off-by: Sekhar Nori <nsekhar@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit b56d6b3fca6d1214dbc9c5655f26e5d4ec04afc8
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Wed Mar 27 04:41:59 2013 +0000
|
| drivers: net: ethernet: cpsw: use netif_wake_queue() while restarting tx queue
|
| To restart tx queue use netif_wake_queue() intead of netif_start_queue()
| so that net schedule will restart transmission immediately which will
| increase network performance while doing huge data transfers.
|
| Reported-by: Dan Franke <dan.franke@schneider-electric.com>
| Suggested-by: Sriramakrishnan A G <srk@ti.com>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Acked-by: Eric Dumazet <edumazet@google.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 7e51cde276ca820d526c6c21cf8147df595a36bf
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Wed Mar 27 04:42:00 2013 +0000
|
| drivers: net: ethernet: davinci_emac: use netif_wake_queue() while restarting tx queue
|
| To restart tx queue use netif_wake_queue() intead of netif_start_queue()
| so that net schedule will restart transmission immediately which will
| increase network performance while doing huge data transfers.
|
| Reported-by: Dan Franke <dan.franke@schneider-electric.com>
| Suggested-by: Sriramakrishnan A G <srk@ti.com>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Acked-by: Eric Dumazet <edumazet@google.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit a210576cf891e9e6d2c238eabcf5c1286b1e7526
|Merge: 7d4c04f 3658f36
|Author: David S. Miller <davem@davemloft.net>
|Date: Mon Apr 1 13:36:50 2013 -0400
|
| Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
|
| Conflicts:
| net/mac80211/sta_info.c
| net/wireless/core.h
|
| Two minor conflicts in wireless. Overlapping additions of extern
| declarations in net/wireless/core.h and a bug fix overlapping with
| the addition of a boolean parameter to __ieee80211_key_free().
|
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 91c4166c1a01c00b8bed74f7a7defa620071de88
|Author: Mugunthan V N <mugunthanvnm@ti.com>
|Date: Mon Apr 15 07:31:28 2013 +0000
|
| drivers: net: ethernet: cpsw: get slave VLAN id from slave node instead of cpsw node
|
| Dual EMAC slave VLAN id must be got from slave node instead of cpsw node as
| VLAN id for each slave will be different.
|
| Reported-by: Mark Jackson <mpfj-list@mimc.co.uk>
| Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 1e0a8b13d35510e711fdf72e9a3e30bcb2bd49fa
|Author: Devendra Naga <devendra.aaru@gmail.com>
|Date: Tue Apr 16 01:30:38 2013 +0000
|
| tlan: cancel work at remove path
|
| the work has been scheduled from interrupt, and not been
| cancelled when the driver is unloaded, which doesn't remove
| the work item from the global workqueue. call the
| cancel_work_sync when the driver is removed (rmmod'ed).
|
| Cc: Sriram <srk@ti.com>
| Cc: Cyril Chemparathy <cyril@ti.com>
| Cc: Vinay Hegde <vinay.hegde@ti.com>
| Signed-off-by: Devendra Naga <devendra.aaru@gmail.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit f646968f8f7c624587de729115d802372b9063dd
|Author: Patrick McHardy <kaber@trash.net>
|Date: Fri Apr 19 02:04:27 2013 +0000
|
| net: vlan: rename NETIF_F_HW_VLAN_* feature flags to NETIF_F_HW_VLAN_CTAG_*
|
| Rename the hardware VLAN acceleration features to include "CTAG" to indicate
| that they only support CTAGs. Follow up patches will introduce 802.1ad
| server provider tagging (STAGs) and require the distinction for hardware not
| supporting acclerating both.
|
| Signed-off-by: Patrick McHardy <kaber@trash.net>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 80d5c3689b886308247da295a228a54df49a44f6
|Author: Patrick McHardy <kaber@trash.net>
|Date: Fri Apr 19 02:04:28 2013 +0000
|
| net: vlan: prepare for 802.1ad VLAN filtering offload
|
| Change the rx_{add,kill}_vid callbacks to take a protocol argument in
| preparation of 802.1ad support. The protocol argument used so far is
| always htons(ETH_P_8021Q).
|
| Signed-off-by: Patrick McHardy <kaber@trash.net>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 6e0895c2ea326cc4bb11e8fa2f654628d5754c31
|Merge: 55fbbe4 60d509f
|Author: David S. Miller <davem@davemloft.net>
|Date: Mon Apr 22 20:32:51 2013 -0400
|
| Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
|
| Conflicts:
| drivers/net/ethernet/emulex/benet/be_main.c
| drivers/net/ethernet/intel/igb/igb_main.c
| drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
| include/net/scm.h
| net/batman-adv/routing.c
| net/ipv4/tcp_input.c
|
| The e{uid,gid} --> {uid,gid} credentials fix conflicted with the
| cleanup in net-next to now pass cred structs around.
|
| The be2net driver had a bug fix in 'net' that overlapped with the VLAN
| interface changes by Patrick McHardy in net-next.
|
| An IGB conflict existed because in 'net' the build_skb() support was
| reverted, and in 'net-next' there was a comment style fix within that
| code.
|
| Several batman-adv conflicts were resolved by making sure that all
| calls to batadv_is_my_mac() are changed to have a new bat_priv first
| argument.
|
| Eric Dumazet's TS ECR fix in TCP in 'net' conflicted with the F-RTO
| rewrite in 'net-next', mostly overlapping changes.
|
| Thanks to Stephen Rothwell and Antonio Quartulli for help with several
| of these merge resolutions.
|
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 817f6d1a13754b043e1a6c1cb713763022860689
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Tue Apr 23 07:31:35 2013 +0000
|
| net/davinci_cpdma: don't check for jiffies with interrupts
|
| __cpdma_chan_process() holds the lock with interrupts off (and its
| caller as well), same goes for cpdma_ctlr_start(). With interrupts off,
| jiffies will not make any progress and if the wait condition never gets
| true we wait for ever.
| Tgis patch adds a a simple udelay and counting down attempt.
|
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit aacebbf8026ecdae1b55db3912e65c6b1308f5ed
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Tue Apr 23 07:31:36 2013 +0000
|
| net/cpsw: don't continue if we miss to allocate rx skbs
|
| if during "ifconfig up" we run out of mem we continue regardless how
| many skbs we got. In worst case we have zero RX skbs and can't ever
| receive further packets since the RX skbs are never reallocated. If
| cpdma_chan_submit() fails we even leak the skb.
| This patch changes the behavior here:
| If we fail to allocate an skb during bring up we don't continue and
| report that error. Same goes for errors from cpdma_chan_submit().
| While here I changed to __netdev_alloc_skb_ip_align() so GFP_KERNEL can
| be used.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit fd51cf199421197d14099b4ba382301cc28e5544
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Tue Apr 23 07:31:37 2013 +0000
|
| net/cpsw: don't rely only on netif_running() to check which device is active
|
| netif_running() reports false before the ->ndo_stop() callback is
| called. That means if one executes "ifconfig down" and the system
| receives an interrupt before the interrupt source has been disabled we
| hang for always for two reasons:
| - we never disable the interrupt source because devices claim to be
| already inactive and don't feel responsible.
| - since the ISR always reports IRQ_HANDLED the line is never deactivated
| because it looks like the ISR feels responsible.
|
| This patch changes the logic in the ISR a little:
| - If none of the status registers reports an active source (RX or TX,
| misc is ignored because it is not actived) we leave with IRQ_NONE.
| - the interrupt is deactivated
| - The first active network device is taken and napi is scheduled. If
| none are active (a small race window between ndo_down() and the
| interrupt the) then we leave and should not come back because the
| source is off.
| There is no need to schedule the second NAPI because both share the
| same dma queue.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit aef614e13dfbdd3b9ae44ad110159f75b9029bba
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Tue Apr 23 07:31:38 2013 +0000
|
| net/davinci_cpdma: remove unused argument in cpdma_chan_submit()
|
| The gfp_mask argument is not used in cpdma_chan_submit() and always set
| to GFP_KERNEL even in atomic sections. This patch drops it since it is
| unused.
|
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit b4727e69b81b71c6e9696185091e8256d863f9be
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Tue Apr 23 07:31:39 2013 +0000
|
| net/cpsw: redo rx skb allocation in rx path
|
| In case that we run into OOM during the allocation of the new rx-skb we
| don't get one and we have one skb less than we used to have. If this
| continues to happen then we end up with no rx-skbs at all.
| This patch changes the following:
| - if we fail to allocate the new skb, then we treat the currently
| completed skb as the new one and so drop the currently received data.
| - instead of testing multiple times if the device is gone we rely one
| the status field which is set to -ENOSYS in case the channel is going
| down and incomplete requests are purged.
| cpdma_chan_stop() removes most of the packages with -ENOSYS. The
| currently active packet which is removed has the "tear down" bit set.
| So if that bit is set, we send ENOSYS as well otherwise we pass the
| status bits which are required to figure out which of the two possible
| just finished.
|
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 4bc21d4162366bb892dc1a4a92110c656e2622ca
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Wed Apr 24 08:48:22 2013 +0000
|
| net/ti: add MODULE_DEVICE_TABLE + MODULE_LICENSE
|
| If compiled as modules each one of these modules is missing something.
| With this patch the modules are loaded on demand and don't taint the
| kernel due to license issues.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit d1bd9acfa3419dc9d5c32589b34a370ca6ae100e
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Wed Apr 24 08:48:23 2013 +0000
|
| net/cpsw: make sure modules remove does not leak any ressources
|
| This driver does not clean up properly after leaving. Here is a list:
| - Use unregister_netdev(). free_netdev() is good but not enough
| - Use the above also on the other ndev in case of dual mac
| - Free data.slave_data. The name of the strucre makes it look like
| it is platform_data but it is not. It is just a trick!
| - Free all irqs. Again: freeing one irq is good start, but freeing all
| of them is better.
|
| With this rmmod & modprobe of cpsw seems to work. The remaining issue
| is:
| |WARNING: at fs/sysfs/dir.c:536 sysfs_add_one+0x9c/0xd4()
| |sysfs: cannot create duplicate filename '/devices/ocp.2/4a100000.ethernet/4a101000.mdio'
| |WARNING: at lib/kobject.c:196 kobject_add_internal+0x1a4/0x1c8()
|
| comming from of_platform_populate() and I am not sure that this belongs
| here.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit 6e6ceaedb5901c7ebd23e5222726dab5362938bd
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Wed Apr 24 08:48:24 2013 +0000
|
| net/cpsw: optimize the for_each_slave_macro()
|
| text data bss dec hex filename
| 15530 92 4 15626 3d0a cpsw.o.before
| 15478 92 4 15574 3cd6 cpsw.o.after
|
| 52 bytes smaller, 13 for each invocation.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
|
|commit a11fbba9a7d338c4a4e4be624af0334bbf2c9a5a
|Author: Sebastian Siewior <bigeasy@linutronix.de>
|Date: Wed Apr 24 08:48:25 2013 +0000
|
| net/cpsw: fix irq_disable() with threaded interrupts
|
| During high throughput it is likely that we receive both: an RX and TX
| interrupt. The normal behaviour is that once we enter the ISR the
| interrupts are disabled in the IRQ chip and so the ISR is invoked only
| once and the interrupt line is disabled once. It will be re-enabled
| after napi completes.
| With threaded interrupts on the other hand the interrupt the interrupt
| is disabled immediately and the ISR is marked for "later". By having TX
| and RX interrupt marked pending we invoke them both and disable the
| interrupt line twice. The napi callback is still executed once and so
| after it completes we remain with interrupts disabled.
|
| The initial patch simply removed the cpsw_{enable|disable}_irq() calls
| and it worked well on my AM335X ES1.0 (beagle bone). On ES2.0 (beagle
| bone black) it caused an never ending interrupt (even after the mask via
| cpsw_intr_disable()) according to Mugunthan V N. Since I don't have the
| ES2.0 and no idea what is going on this patch tracks the state of the
| irq_disable() call and execute it only when not yet done.
| The book keeping is done on the first struct since with dual_emac we can
| have two of those and only one interrupt line.
|
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
| Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
| Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Diffstat (limited to 'drivers/net/ethernet/ti')
-rw-r--r-- | drivers/net/ethernet/ti/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/cpmac.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 814 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/cpsw_ale.c | 107 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/cpsw_ale.h | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/davinci_cpdma.c | 106 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/davinci_cpdma.h | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/davinci_emac.c | 76 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/davinci_mdio.c | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/tlan.c | 5 |
10 files changed, 926 insertions, 238 deletions
diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 4426151..de71b1e 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -88,8 +88,8 @@ config TLAN Please email feedback to <torben.mathiasen@compaq.com>. config CPMAC - tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" - depends on EXPERIMENTAL && AR7 + tristate "TI AR7 CPMAC Ethernet support" + depends on AR7 select PHYLIB ---help--- TI AR7 CPMAC Ethernet support diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index d9625f6..31bbbca 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -904,10 +904,9 @@ static int cpmac_set_ringparam(struct net_device *dev, static void cpmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "cpmac"); - strcpy(info->version, CPMAC_VERSION); - info->fw_version[0] = '\0'; - sprintf(info->bus_info, "%s", "cpmac"); + strlcpy(info->driver, "cpmac", sizeof(info->driver)); + strlcpy(info->version, CPMAC_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac"); info->regdump_len = 0; } @@ -1173,8 +1172,8 @@ static int cpmac_probe(struct platform_device *pdev) snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); - priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, 0, - PHY_INTERFACE_MODE_MII); + priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { if (netif_msg_drv(priv)) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 40aff68..4e2d224 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -32,6 +32,7 @@ #include <linux/of.h> #include <linux/of_net.h> #include <linux/of_device.h> +#include <linux/if_vlan.h> #include <linux/platform_data/cpsw.h> @@ -118,6 +119,20 @@ do { \ #define TX_PRIORITY_MAPPING 0x33221100 #define CPDMA_TX_PRIORITY_MAP 0x76543210 +#define CPSW_VLAN_AWARE BIT(1) +#define CPSW_ALE_VLAN_AWARE 1 + +#define CPSW_FIFO_NORMAL_MODE (0 << 15) +#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15) +#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15) + +#define CPSW_INTPACEEN (0x3f << 16) +#define CPSW_INTPRESCALE_MASK (0x7FF << 0) +#define CPSW_CMINTMAX_CNT 63 +#define CPSW_CMINTMIN_CNT 2 +#define CPSW_CMINTMAX_INTVL (1000 / CPSW_CMINTMIN_CNT) +#define CPSW_CMINTMIN_INTVL ((1000 / CPSW_CMINTMAX_CNT) + 1) + #define cpsw_enable_irq(priv) \ do { \ u32 i; \ @@ -131,6 +146,10 @@ do { \ disable_irq_nosync(priv->irqs_table[i]); \ } while (0); +#define cpsw_slave_index(priv) \ + ((priv->data.dual_emac) ? priv->emac_port : \ + priv->data.active_slave) + static int debug_level; module_param(debug_level, int, 0); MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)"); @@ -152,6 +171,15 @@ struct cpsw_wr_regs { u32 rx_en; u32 tx_en; u32 misc_en; + u32 mem_allign1[8]; + u32 rx_thresh_stat; + u32 rx_stat; + u32 tx_stat; + u32 misc_stat; + u32 mem_allign2[8]; + u32 rx_imax; + u32 tx_imax; + }; struct cpsw_ss_regs { @@ -250,7 +278,7 @@ struct cpsw_ss_regs { struct cpsw_host_regs { u32 max_blks; u32 blk_cnt; - u32 flow_thresh; + u32 tx_in_ctl; u32 port_vlan; u32 tx_pri_map; u32 cpdma_tx_pri_map; @@ -277,6 +305,9 @@ struct cpsw_slave { u32 mac_control; struct cpsw_slave_data *data; struct phy_device *phy; + struct net_device *ndev; + u32 port_vlan; + u32 open_stat; }; static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) @@ -303,6 +334,8 @@ struct cpsw_priv { struct cpsw_host_regs __iomem *host_port_regs; u32 msg_enable; u32 version; + u32 coal_intvl; + u32 bus_freq_mhz; struct net_device_stats stats; int rx_packet_max; int host_port; @@ -315,17 +348,69 @@ struct cpsw_priv { /* snapshot of IRQ numbers */ u32 irqs_table[4]; u32 num_irqs; - struct cpts cpts; + bool irq_enabled; + struct cpts *cpts; + u32 emac_port; }; #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) -#define for_each_slave(priv, func, arg...) \ - do { \ - int idx; \ - for (idx = 0; idx < (priv)->data.slaves; idx++) \ - (func)((priv)->slaves + idx, ##arg); \ +#define for_each_slave(priv, func, arg...) \ + do { \ + struct cpsw_slave *slave; \ + int n; \ + if (priv->data.dual_emac) \ + (func)((priv)->slaves + priv->emac_port, ##arg);\ + else \ + for (n = (priv)->data.slaves, \ + slave = (priv)->slaves; \ + n; n--) \ + (func)(slave++, ##arg); \ + } while (0) +#define cpsw_get_slave_ndev(priv, __slave_no__) \ + (priv->slaves[__slave_no__].ndev) +#define cpsw_get_slave_priv(priv, __slave_no__) \ + ((priv->slaves[__slave_no__].ndev) ? \ + netdev_priv(priv->slaves[__slave_no__].ndev) : NULL) \ + +#define cpsw_dual_emac_src_port_detect(status, priv, ndev, skb) \ + do { \ + if (!priv->data.dual_emac) \ + break; \ + if (CPDMA_RX_SOURCE_PORT(status) == 1) { \ + ndev = cpsw_get_slave_ndev(priv, 0); \ + priv = netdev_priv(ndev); \ + skb->dev = ndev; \ + } else if (CPDMA_RX_SOURCE_PORT(status) == 2) { \ + ndev = cpsw_get_slave_ndev(priv, 1); \ + priv = netdev_priv(ndev); \ + skb->dev = ndev; \ + } \ + } while (0) +#define cpsw_add_mcast(priv, addr) \ + do { \ + if (priv->data.dual_emac) { \ + struct cpsw_slave *slave = priv->slaves + \ + priv->emac_port; \ + int slave_port = cpsw_get_slave_port(priv, \ + slave->slave_num); \ + cpsw_ale_add_mcast(priv->ale, addr, \ + 1 << slave_port | 1 << priv->host_port, \ + ALE_VLAN, slave->port_vlan, 0); \ + } else { \ + cpsw_ale_add_mcast(priv->ale, addr, \ + ALE_ALL_PORTS << priv->host_port, \ + 0, 0, 0); \ + } \ } while (0) +static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +{ + if (priv->host_port == 0) + return slave_num + 1; + else + return slave_num; +} + static void cpsw_ndo_set_rx_mode(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); @@ -344,8 +429,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) /* program multicast address list into ALE register */ netdev_for_each_mc_addr(ha, ndev) { - cpsw_ale_add_mcast(priv->ale, (u8 *)ha->addr, - ALE_ALL_PORTS << priv->host_port, 0, 0); + cpsw_add_mcast(priv, (u8 *)ha->addr); } } } @@ -374,9 +458,12 @@ void cpsw_tx_handler(void *token, int len, int status) struct net_device *ndev = skb->dev; struct cpsw_priv *priv = netdev_priv(ndev); + /* Check whether the queue is stopped due to stalled tx dma, if the + * queue is stopped then start the queue as we have free desc for tx + */ if (unlikely(netif_queue_stopped(ndev))) - netif_start_queue(ndev); - cpts_tx_timestamp(&priv->cpts, skb); + netif_wake_queue(ndev); + cpts_tx_timestamp(priv->cpts, skb); priv->stats.tx_packets++; priv->stats.tx_bytes += len; dev_kfree_skb_any(skb); @@ -385,61 +472,69 @@ void cpsw_tx_handler(void *token, int len, int status) void cpsw_rx_handler(void *token, int len, int status) { struct sk_buff *skb = token; + struct sk_buff *new_skb; struct net_device *ndev = skb->dev; struct cpsw_priv *priv = netdev_priv(ndev); int ret = 0; - /* free and bail if we are shutting down */ - if (unlikely(!netif_running(ndev)) || - unlikely(!netif_carrier_ok(ndev))) { + cpsw_dual_emac_src_port_detect(status, priv, ndev, skb); + + if (unlikely(status < 0)) { + /* the interface is going down, skbs are purged */ dev_kfree_skb_any(skb); return; } - if (likely(status >= 0)) { + + new_skb = netdev_alloc_skb_ip_align(ndev, priv->rx_packet_max); + if (new_skb) { skb_put(skb, len); - cpts_rx_timestamp(&priv->cpts, skb); + cpts_rx_timestamp(priv->cpts, skb); skb->protocol = eth_type_trans(skb, ndev); netif_receive_skb(skb); priv->stats.rx_bytes += len; priv->stats.rx_packets++; - skb = NULL; - } - - if (unlikely(!netif_running(ndev))) { - if (skb) - dev_kfree_skb_any(skb); - return; + } else { + priv->stats.rx_dropped++; + new_skb = skb; } - if (likely(!skb)) { - skb = netdev_alloc_skb_ip_align(ndev, priv->rx_packet_max); - if (WARN_ON(!skb)) - return; - - ret = cpdma_chan_submit(priv->rxch, skb, skb->data, - skb_tailroom(skb), GFP_KERNEL); - } - WARN_ON(ret < 0); + ret = cpdma_chan_submit(priv->rxch, new_skb, new_skb->data, + skb_tailroom(new_skb), 0); + if (WARN_ON(ret < 0)) + dev_kfree_skb_any(new_skb); } static irqreturn_t cpsw_interrupt(int irq, void *dev_id) { struct cpsw_priv *priv = dev_id; + u32 rx, tx, rx_thresh; - if (likely(netif_running(priv->ndev))) { - cpsw_intr_disable(priv); + rx_thresh = __raw_readl(&priv->wr_regs->rx_thresh_stat); + rx = __raw_readl(&priv->wr_regs->rx_stat); + tx = __raw_readl(&priv->wr_regs->tx_stat); + if (!rx_thresh && !rx && !tx) + return IRQ_NONE; + + cpsw_intr_disable(priv); + if (priv->irq_enabled == true) { cpsw_disable_irq(priv); + priv->irq_enabled = false; + } + + if (netif_running(priv->ndev)) { napi_schedule(&priv->napi); + return IRQ_HANDLED; } - return IRQ_HANDLED; -} -static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) -{ - if (priv->host_port == 0) - return slave_num + 1; - else - return slave_num; + priv = cpsw_get_slave_priv(priv, 1); + if (!priv) + return IRQ_NONE; + + if (netif_running(priv->ndev)) { + napi_schedule(&priv->napi); + return IRQ_HANDLED; + } + return IRQ_NONE; } static int cpsw_poll(struct napi_struct *napi, int budget) @@ -448,19 +543,27 @@ static int cpsw_poll(struct napi_struct *napi, int budget) int num_tx, num_rx; num_tx = cpdma_chan_process(priv->txch, 128); - num_rx = cpdma_chan_process(priv->rxch, budget); - - if (num_rx || num_tx) - cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n", - num_rx, num_tx); + if (num_tx) + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + num_rx = cpdma_chan_process(priv->rxch, budget); if (num_rx < budget) { + struct cpsw_priv *prim_cpsw; + napi_complete(napi); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma); - cpsw_enable_irq(priv); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + prim_cpsw = cpsw_get_slave_priv(priv, 0); + if (prim_cpsw->irq_enabled == false) { + cpsw_enable_irq(priv); + prim_cpsw->irq_enabled = true; + } } + if (num_rx || num_tx) + cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n", + num_rx, num_tx); + return num_rx; } @@ -548,6 +651,77 @@ static void cpsw_adjust_link(struct net_device *ndev) } } +static int cpsw_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *coal) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + coal->rx_coalesce_usecs = priv->coal_intvl; + return 0; +} + +static int cpsw_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *coal) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + u32 int_ctrl; + u32 num_interrupts = 0; + u32 prescale = 0; + u32 addnl_dvdr = 1; + u32 coal_intvl = 0; + + if (!coal->rx_coalesce_usecs) + return -EINVAL; + + coal_intvl = coal->rx_coalesce_usecs; + + int_ctrl = readl(&priv->wr_regs->int_control); + prescale = priv->bus_freq_mhz * 4; + + if (coal_intvl < CPSW_CMINTMIN_INTVL) + coal_intvl = CPSW_CMINTMIN_INTVL; + + if (coal_intvl > CPSW_CMINTMAX_INTVL) { + /* Interrupt pacer works with 4us Pulse, we can + * throttle further by dilating the 4us pulse. + */ + addnl_dvdr = CPSW_INTPRESCALE_MASK / prescale; + + if (addnl_dvdr > 1) { + prescale *= addnl_dvdr; + if (coal_intvl > (CPSW_CMINTMAX_INTVL * addnl_dvdr)) + coal_intvl = (CPSW_CMINTMAX_INTVL + * addnl_dvdr); + } else { + addnl_dvdr = 1; + coal_intvl = CPSW_CMINTMAX_INTVL; + } + } + + num_interrupts = (1000 * addnl_dvdr) / coal_intvl; + writel(num_interrupts, &priv->wr_regs->rx_imax); + writel(num_interrupts, &priv->wr_regs->tx_imax); + + int_ctrl |= CPSW_INTPACEEN; + int_ctrl &= (~CPSW_INTPRESCALE_MASK); + int_ctrl |= (prescale & CPSW_INTPRESCALE_MASK); + writel(int_ctrl, &priv->wr_regs->int_control); + + cpsw_notice(priv, timer, "Set coalesce to %d usecs.\n", coal_intvl); + if (priv->data.dual_emac) { + int i; + + for (i = 0; i < priv->data.slaves; i++) { + priv = netdev_priv(priv->slaves[i].ndev); + priv->coal_intvl = coal_intvl; + } + } else { + priv->coal_intvl = coal_intvl; + } + + return 0; +} + static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) { static char *leader = "........................................"; @@ -559,6 +733,54 @@ static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) leader + strlen(name), val); } +static int cpsw_common_res_usage_state(struct cpsw_priv *priv) +{ + u32 i; + u32 usage_count = 0; + + if (!priv->data.dual_emac) + return 0; + + for (i = 0; i < priv->data.slaves; i++) + if (priv->slaves[i].open_stat) + usage_count++; + + return usage_count; +} + +static inline int cpsw_tx_packet_submit(struct net_device *ndev, + struct cpsw_priv *priv, struct sk_buff *skb) +{ + if (!priv->data.dual_emac) + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 0); + + if (ndev == cpsw_get_slave_ndev(priv, 0)) + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 1); + else + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 2); +} + +static inline void cpsw_add_dual_emac_def_ale_entries( + struct cpsw_priv *priv, struct cpsw_slave *slave, + u32 slave_port) +{ + u32 port_mask = 1 << slave_port | 1 << priv->host_port; + + if (priv->version == CPSW_VERSION_1) + slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN); + else + slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN); + cpsw_ale_add_vlan(priv->ale, slave->port_vlan, port_mask, + port_mask, port_mask, 0); + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + port_mask, ALE_VLAN, slave->port_vlan, 0); + cpsw_ale_add_ucast(priv->ale, priv->mac_addr, + priv->host_port, ALE_VLAN, slave->port_vlan); +} + static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) { char name[32]; @@ -588,11 +810,14 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_port = cpsw_get_slave_port(priv, slave->slave_num); - cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, - 1 << slave_port, 0, ALE_MCAST_FWD_2); + if (priv->data.dual_emac) + cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); + else + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); slave->phy = phy_connect(priv->ndev, slave->data->phy_id, - &cpsw_adjust_link, 0, slave->data->phy_if); + &cpsw_adjust_link, slave->data->phy_if); if (IS_ERR(slave->phy)) { dev_err(priv->dev, "phy %s not found on slave %d\n", slave->data->phy_id, slave->slave_num); @@ -604,14 +829,44 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) } } +static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) +{ + const int vlan = priv->data.default_vlan; + const int port = priv->host_port; + u32 reg; + int i; + + reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN : + CPSW2_PORT_VLAN; + + writel(vlan, &priv->host_port_regs->port_vlan); + + for (i = 0; i < priv->data.slaves; i++) + slave_write(priv->slaves + i, vlan, reg); + + cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port, + ALE_ALL_PORTS << port, ALE_ALL_PORTS << port, + (ALE_PORT_1 | ALE_PORT_2) << port); +} + static void cpsw_init_host_port(struct cpsw_priv *priv) { + u32 control_reg; + u32 fifo_mode; + /* soft reset the controller and initialize ale */ soft_reset("cpsw", &priv->regs->soft_reset); cpsw_ale_start(priv->ale); /* switch to vlan unaware mode */ - cpsw_ale_control_set(priv->ale, 0, ALE_VLAN_AWARE, 0); + cpsw_ale_control_set(priv->ale, priv->host_port, ALE_VLAN_AWARE, + CPSW_ALE_VLAN_AWARE); + control_reg = readl(&priv->regs->control); + control_reg |= CPSW_VLAN_AWARE; + writel(control_reg, &priv->regs->control); + fifo_mode = (priv->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE : + CPSW_FIFO_NORMAL_MODE; + writel(fifo_mode, &priv->host_port_regs->tx_in_ctl); /* setup host port priority mapping */ __raw_writel(CPDMA_TX_PRIORITY_MAP, @@ -621,18 +876,32 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) cpsw_ale_control_set(priv->ale, priv->host_port, ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); - cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, 0); - cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, - 1 << priv->host_port, 0, ALE_MCAST_FWD_2); + if (!priv->data.dual_emac) { + cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, + 0, 0); + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << priv->host_port, 0, 0, ALE_MCAST_FWD_2); + } +} + +static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + if (!slave->phy) + return; + phy_stop(slave->phy); + phy_disconnect(slave->phy); + slave->phy = NULL; } static int cpsw_ndo_open(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_priv *prim_cpsw; int i, ret; u32 reg; - cpsw_intr_disable(priv); + if (!cpsw_common_res_usage_state(priv)) + cpsw_intr_disable(priv); netif_carrier_off(ndev); pm_runtime_get_sync(&priv->pdev->dev); @@ -644,53 +913,81 @@ static int cpsw_ndo_open(struct net_device *ndev) CPSW_RTL_VERSION(reg)); /* initialize host and slave ports */ - cpsw_init_host_port(priv); + if (!cpsw_common_res_usage_state(priv)) + cpsw_init_host_port(priv); for_each_slave(priv, cpsw_slave_open, priv); - /* setup tx dma to fixed prio and zero offset */ - cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); - cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); - - /* disable priority elevation and enable statistics on all ports */ - __raw_writel(0, &priv->regs->ptype); - - /* enable statistics collection only on the host port */ - __raw_writel(0x7, &priv->regs->stat_port_en); + /* Add default VLAN */ + if (!priv->data.dual_emac) + cpsw_add_default_vlan(priv); + + if (!cpsw_common_res_usage_state(priv)) { + /* setup tx dma to fixed prio and zero offset */ + cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); + cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); + + /* disable priority elevation */ + __raw_writel(0, &priv->regs->ptype); + + /* enable statistics collection only on all ports */ + __raw_writel(0x7, &priv->regs->stat_port_en); + + if (WARN_ON(!priv->data.rx_descs)) + priv->data.rx_descs = 128; + + for (i = 0; i < priv->data.rx_descs; i++) { + struct sk_buff *skb; + + ret = -ENOMEM; + skb = __netdev_alloc_skb_ip_align(priv->ndev, + priv->rx_packet_max, GFP_KERNEL); + if (!skb) + goto err_cleanup; + ret = cpdma_chan_submit(priv->rxch, skb, skb->data, + skb_tailroom(skb), 0); + if (ret < 0) { + kfree_skb(skb); + goto err_cleanup; + } + } + /* continue even if we didn't manage to submit all + * receive descs + */ + cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); + } - if (WARN_ON(!priv->data.rx_descs)) - priv->data.rx_descs = 128; + /* Enable Interrupt pacing if configured */ + if (priv->coal_intvl != 0) { + struct ethtool_coalesce coal; - for (i = 0; i < priv->data.rx_descs; i++) { - struct sk_buff *skb; + coal.rx_coalesce_usecs = (priv->coal_intvl << 4); + cpsw_set_coalesce(ndev, &coal); + } - ret = -ENOMEM; - skb = netdev_alloc_skb_ip_align(priv->ndev, - priv->rx_packet_max); - if (!skb) - break; - ret = cpdma_chan_submit(priv->rxch, skb, skb->data, - skb_tailroom(skb), GFP_KERNEL); - if (WARN_ON(ret < 0)) - break; + prim_cpsw = cpsw_get_slave_priv(priv, 0); + if (prim_cpsw->irq_enabled == false) { + if ((priv == prim_cpsw) || !netif_running(prim_cpsw->ndev)) { + prim_cpsw->irq_enabled = true; + cpsw_enable_irq(prim_cpsw); + } } - /* continue even if we didn't manage to submit all receive descs */ - cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); cpdma_ctlr_start(priv->dma); cpsw_intr_enable(priv); napi_enable(&priv->napi); - cpdma_ctlr_eoi(priv->dma); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + if (priv->data.dual_emac) + priv->slaves[priv->emac_port].open_stat = true; return 0; -} -static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) -{ - if (!slave->phy) - return; - phy_stop(slave->phy); - phy_disconnect(slave->phy); - slave->phy = NULL; +err_cleanup: + cpdma_ctlr_stop(priv->dma); + for_each_slave(priv, cpsw_slave_stop, priv); + pm_runtime_put_sync(&priv->pdev->dev); + netif_carrier_off(priv->ndev); + return ret; } static int cpsw_ndo_stop(struct net_device *ndev) @@ -701,12 +998,17 @@ static int cpsw_ndo_stop(struct net_device *ndev) netif_stop_queue(priv->ndev); napi_disable(&priv->napi); netif_carrier_off(priv->ndev); - cpsw_intr_disable(priv); - cpdma_ctlr_int_ctrl(priv->dma, false); - cpdma_ctlr_stop(priv->dma); - cpsw_ale_stop(priv->ale); + + if (cpsw_common_res_usage_state(priv) <= 1) { + cpsw_intr_disable(priv); + cpdma_ctlr_int_ctrl(priv->dma, false); + cpdma_ctlr_stop(priv->dma); + cpsw_ale_stop(priv->ale); + } for_each_slave(priv, cpsw_slave_stop, priv); pm_runtime_put_sync(&priv->pdev->dev); + if (priv->data.dual_emac) + priv->slaves[priv->emac_port].open_stat = false; return 0; } @@ -724,18 +1026,24 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } - if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && priv->cpts.tx_enable) + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + priv->cpts->tx_enable) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_tx_timestamp(skb); - ret = cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, GFP_KERNEL); + ret = cpsw_tx_packet_submit(ndev, priv, skb); if (unlikely(ret != 0)) { cpsw_err(priv, tx_err, "desc submit failed\n"); goto fail; } + /* If there is no more tx desc left free then we need to + * tell the kernel to stop sending us tx frames. + */ + if (unlikely(!cpdma_check_free_tx_desc(priv->txch))) + netif_stop_queue(ndev); + return NETDEV_TX_OK; fail: priv->stats.tx_dropped++; @@ -770,10 +1078,10 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags) static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) { - struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; + struct cpsw_slave *slave = &priv->slaves[priv->data.active_slave]; u32 ts_en, seq_id; - if (!priv->cpts.tx_enable && !priv->cpts.rx_enable) { + if (!priv->cpts->tx_enable && !priv->cpts->rx_enable) { slave_write(slave, 0, CPSW1_TS_CTL); return; } @@ -781,10 +1089,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588; ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS; - if (priv->cpts.tx_enable) + if (priv->cpts->tx_enable) ts_en |= CPSW_V1_TS_TX_EN; - if (priv->cpts.rx_enable) + if (priv->cpts->rx_enable) ts_en |= CPSW_V1_TS_RX_EN; slave_write(slave, ts_en, CPSW1_TS_CTL); @@ -793,16 +1101,21 @@ static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) { - struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; + struct cpsw_slave *slave; u32 ctrl, mtype; + if (priv->data.dual_emac) + slave = &priv->slaves[priv->emac_port]; + else + slave = &priv->slaves[priv->data.active_slave]; + ctrl = slave_read(slave, CPSW2_CONTROL); ctrl &= ~CTRL_ALL_TS_MASK; - if (priv->cpts.tx_enable) + if (priv->cpts->tx_enable) ctrl |= CTRL_TX_TS_BITS; - if (priv->cpts.rx_enable) + if (priv->cpts->rx_enable) ctrl |= CTRL_RX_TS_BITS; mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS; @@ -815,7 +1128,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) { struct cpsw_priv *priv = netdev_priv(dev); - struct cpts *cpts = &priv->cpts; + struct cpts *cpts = priv->cpts; struct hwtstamp_config cfg; if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) @@ -879,14 +1192,26 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) { + struct cpsw_priv *priv = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(req); + int slave_no = cpsw_slave_index(priv); + if (!netif_running(dev)) return -EINVAL; + switch (cmd) { #ifdef CONFIG_TI_CPTS - if (cmd == SIOCSHWTSTAMP) + case SIOCSHWTSTAMP: return cpsw_hwtstamp_ioctl(dev, req); #endif - return -ENOTSUPP; + case SIOCGMIIPHY: + data->phy_id = priv->slaves[slave_no].phy->addr; + break; + default: + return -ENOTSUPP; + } + + return 0; } static void cpsw_ndo_tx_timeout(struct net_device *ndev) @@ -901,7 +1226,9 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev) cpdma_chan_start(priv->txch); cpdma_ctlr_int_ctrl(priv->dma, true); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + } static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev) @@ -920,10 +1247,79 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev) cpsw_interrupt(ndev->irq, priv); cpdma_ctlr_int_ctrl(priv->dma, true); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + } #endif +static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, + unsigned short vid) +{ + int ret; + + ret = cpsw_ale_add_vlan(priv->ale, vid, + ALE_ALL_PORTS << priv->host_port, + 0, ALE_ALL_PORTS << priv->host_port, + (ALE_PORT_1 | ALE_PORT_2) << priv->host_port); + if (ret != 0) + return ret; + + ret = cpsw_ale_add_ucast(priv->ale, priv->mac_addr, + priv->host_port, ALE_VLAN, vid); + if (ret != 0) + goto clean_vid; + + ret = cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + ALE_ALL_PORTS << priv->host_port, + ALE_VLAN, vid, 0); + if (ret != 0) + goto clean_vlan_ucast; + return 0; + +clean_vlan_ucast: + cpsw_ale_del_ucast(priv->ale, priv->mac_addr, + priv->host_port, ALE_VLAN, vid); +clean_vid: + cpsw_ale_del_vlan(priv->ale, vid, 0); + return ret; +} + +static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, + __be16 proto, u16 vid) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + if (vid == priv->data.default_vlan) + return 0; + + dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid); + return cpsw_add_vlan_ale_entry(priv, vid); +} + +static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, + __be16 proto, u16 vid) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int ret; + + if (vid == priv->data.default_vlan) + return 0; + + dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid); + ret = cpsw_ale_del_vlan(priv->ale, vid, 0); + if (ret != 0) + return ret; + + ret = cpsw_ale_del_ucast(priv->ale, priv->mac_addr, + priv->host_port, ALE_VLAN, vid); + if (ret != 0) + return ret; + + return cpsw_ale_del_mcast(priv->ale, priv->ndev->broadcast, + 0, ALE_VLAN, vid); +} + static const struct net_device_ops cpsw_netdev_ops = { .ndo_open = cpsw_ndo_open, .ndo_stop = cpsw_ndo_stop, @@ -938,15 +1334,18 @@ static const struct net_device_ops cpsw_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cpsw_ndo_poll_controller, #endif + .ndo_vlan_rx_add_vid = cpsw_ndo_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = cpsw_ndo_vlan_rx_kill_vid, }; static void cpsw_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct cpsw_priv *priv = netdev_priv(ndev); - strcpy(info->driver, "TI CPSW Driver v1.0"); - strcpy(info->version, "1.0"); - strcpy(info->bus_info, priv->pdev->name); + + strlcpy(info->driver, "TI CPSW Driver v1.0", sizeof(info->driver)); + strlcpy(info->version, "1.0", sizeof(info->version)); + strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); } static u32 cpsw_get_msglevel(struct net_device *ndev) @@ -974,7 +1373,7 @@ static int cpsw_get_ts_info(struct net_device *ndev, SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RAW_HARDWARE; - info->phc_index = priv->cpts.phc_index; + info->phc_index = priv->cpts->phc_index; info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); @@ -993,12 +1392,39 @@ static int cpsw_get_ts_info(struct net_device *ndev, return 0; } +static int cpsw_get_settings(struct net_device *ndev, + struct ethtool_cmd *ecmd) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int slave_no = cpsw_slave_index(priv); + + if (priv->slaves[slave_no].phy) + return phy_ethtool_gset(priv->slaves[slave_no].phy, ecmd); + else + return -EOPNOTSUPP; +} + +static int cpsw_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int slave_no = cpsw_slave_index(priv); + + if (priv->slaves[slave_no].phy) + return phy_ethtool_sset(priv->slaves[slave_no].phy, ecmd); + else + return -EOPNOTSUPP; +} + static const struct ethtool_ops cpsw_ethtool_ops = { .get_drvinfo = cpsw_get_drvinfo, .get_msglevel = cpsw_get_msglevel, .set_msglevel = cpsw_set_msglevel, .get_link = ethtool_op_get_link, .get_ts_info = cpsw_get_ts_info, + .get_settings = cpsw_get_settings, + .set_settings = cpsw_set_settings, + .get_coalesce = cpsw_get_coalesce, + .set_coalesce = cpsw_set_coalesce, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, @@ -1011,6 +1437,7 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, slave->data = data; slave->regs = regs + slave_reg_ofs; slave->sliver = regs + sliver_reg_ofs; + slave->port_vlan = data->dual_emac_res_vlan; } static int cpsw_probe_dt(struct cpsw_platform_data *data, @@ -1030,12 +1457,12 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; - if (of_property_read_u32(node, "cpts_active_slave", &prop)) { - pr_err("Missing cpts_active_slave property in the DT.\n"); + if (of_property_read_u32(node, "active_slave", &prop)) { + pr_err("Missing active_slave property in the DT.\n"); ret = -EINVAL; goto error_ret; } - data->cpts_active_slave = prop; + data->active_slave = prop; if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { pr_err("Missing cpts_clock_mult property in the DT.\n"); @@ -1051,12 +1478,10 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->cpts_clock_shift = prop; - data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) * - data->slaves, GFP_KERNEL); - if (!data->slave_data) { - pr_err("Could not allocate slave memory.\n"); + data->slave_data = kcalloc(data->slaves, sizeof(struct cpsw_slave_data), + GFP_KERNEL); + if (!data->slave_data) return -EINVAL; - } if (of_property_read_u32(node, "cpdma_channels", &prop)) { pr_err("Missing cpdma_channels property in the DT.\n"); @@ -1093,6 +1518,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->mac_control = prop; + if (!of_property_read_u32(node, "dual_emac", &prop)) + data->dual_emac = prop; + /* * Populate all the child nodes here... */ @@ -1111,7 +1539,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, struct platform_device *mdio; parp = of_get_property(slave_node, "phy_id", &lenp); - if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { + if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) { pr_err("Missing slave[%d] phy_id property\n", i); ret = -EINVAL; goto error_ret; @@ -1126,6 +1554,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, if (mac_addr) memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); + if (data->dual_emac) { + if (of_property_read_u32(slave_node, "dual_emac_res_vlan", + &prop)) { + pr_err("Missing dual_emac_res_vlan in DT.\n"); + slave_data->dual_emac_res_vlan = i+1; + pr_err("Using %d as Reserved VLAN for %d slave\n", + slave_data->dual_emac_res_vlan, i); + } else { + slave_data->dual_emac_res_vlan = prop; + } + } + i++; } @@ -1136,9 +1576,85 @@ error_ret: return ret; } +static int cpsw_probe_dual_emac(struct platform_device *pdev, + struct cpsw_priv *priv) +{ + struct cpsw_platform_data *data = &priv->data; + struct net_device *ndev; + struct cpsw_priv *priv_sl2; + int ret = 0, i; + + ndev = alloc_etherdev(sizeof(struct cpsw_priv)); + if (!ndev) { + pr_err("cpsw: error allocating net_device\n"); + return -ENOMEM; + } + + priv_sl2 = netdev_priv(ndev); + spin_lock_init(&priv_sl2->lock); + priv_sl2->data = *data; + priv_sl2->pdev = pdev; + priv_sl2->ndev = ndev; + priv_sl2->dev = &ndev->dev; + priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); + priv_sl2->rx_packet_max = max(rx_packet_max, 128); + + if (is_valid_ether_addr(data->slave_data[1].mac_addr)) { + memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr, + ETH_ALEN); + pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr); + } else { + random_ether_addr(priv_sl2->mac_addr); + pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr); + } + memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN); + + priv_sl2->slaves = priv->slaves; + priv_sl2->clk = priv->clk; + + priv_sl2->coal_intvl = 0; + priv_sl2->bus_freq_mhz = priv->bus_freq_mhz; + + priv_sl2->cpsw_res = priv->cpsw_res; + priv_sl2->regs = priv->regs; + priv_sl2->host_port = priv->host_port; + priv_sl2->host_port_regs = priv->host_port_regs; + priv_sl2->wr_regs = priv->wr_regs; + priv_sl2->dma = priv->dma; + priv_sl2->txch = priv->txch; + priv_sl2->rxch = priv->rxch; + priv_sl2->ale = priv->ale; + priv_sl2->emac_port = 1; + priv->slaves[1].ndev = ndev; + priv_sl2->cpts = priv->cpts; + priv_sl2->version = priv->version; + + for (i = 0; i < priv->num_irqs; i++) { + priv_sl2->irqs_table[i] = priv->irqs_table[i]; + priv_sl2->num_irqs = priv->num_irqs; + } + priv->irq_enabled = true; + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + + ndev->netdev_ops = &cpsw_netdev_ops; + SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); + netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT); + + /* register the network device */ + SET_NETDEV_DEV(ndev, &pdev->dev); + ret = register_netdev(ndev); + if (ret) { + pr_err("cpsw: error registering net device\n"); + free_netdev(ndev); + ret = -ENODEV; + } + + return ret; +} + static int cpsw_probe(struct platform_device *pdev) { - struct cpsw_platform_data *data = pdev->dev.platform_data; + struct cpsw_platform_data *data; struct net_device *ndev; struct cpsw_priv *priv; struct cpdma_params dma_params; @@ -1162,6 +1678,11 @@ static int cpsw_probe(struct platform_device *pdev) priv->dev = &ndev->dev; priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); priv->rx_packet_max = max(rx_packet_max, 128); + priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL); + if (!ndev) { + pr_err("error allocating cpts\n"); + goto clean_ndev_ret; + } /* * This may be required here for child devices. @@ -1194,12 +1715,17 @@ static int cpsw_probe(struct platform_device *pdev) for (i = 0; i < data->slaves; i++) priv->slaves[i].slave_num = i; + priv->slaves[0].ndev = ndev; + priv->emac_port = 0; + priv->clk = clk_get(&pdev->dev, "fck"); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "fck is not found\n"); ret = -ENODEV; goto clean_slave_ret; } + priv->coal_intvl = 0; + priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000; priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!priv->cpsw_res) { @@ -1248,7 +1774,7 @@ static int cpsw_probe(struct platform_device *pdev) switch (priv->version) { case CPSW_VERSION_1: priv->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; - priv->cpts.reg = ss_regs + CPSW1_CPTS_OFFSET; + priv->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; ale_params.ale_regs = ss_regs + CPSW1_ALE_OFFSET; @@ -1259,7 +1785,7 @@ static int cpsw_probe(struct platform_device *pdev) break; case CPSW_VERSION_2: priv->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; - priv->cpts.reg = ss_regs + CPSW2_CPTS_OFFSET; + priv->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; ale_params.ale_regs = ss_regs + CPSW2_ALE_OFFSET; @@ -1341,12 +1867,12 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_ale_ret; } priv->irqs_table[k] = i; - priv->num_irqs = k; + priv->num_irqs = k + 1; } k++; } - ndev->flags |= IFF_ALLMULTI; /* see cpsw_ndo_change_rx_flags() */ + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; ndev->netdev_ops = &cpsw_netdev_ops; SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); @@ -1361,17 +1887,26 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_irq_ret; } - if (cpts_register(&pdev->dev, &priv->cpts, + if (cpts_register(&pdev->dev, priv->cpts, data->cpts_clock_mult, data->cpts_clock_shift)) dev_err(priv->dev, "error registering cpts device\n"); cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n", priv->cpsw_res->start, ndev->irq); + if (priv->data.dual_emac) { + ret = cpsw_probe_dual_emac(pdev, priv); + if (ret) { + cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); + goto clean_irq_ret; + } + } + return 0; clean_irq_ret: - free_irq(ndev->irq, priv); + for (i = 0; i < priv->num_irqs; i++) + free_irq(priv->irqs_table[i], priv); clean_ale_ret: cpsw_ale_destroy(priv->ale); clean_dma_ret: @@ -1394,7 +1929,8 @@ clean_slave_ret: pm_runtime_disable(&pdev->dev); kfree(priv->slaves); clean_ndev_ret: - free_netdev(ndev); + kfree(priv->data.slave_data); + free_netdev(priv->ndev); return ret; } @@ -1402,12 +1938,17 @@ static int cpsw_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct cpsw_priv *priv = netdev_priv(ndev); + int i; - pr_info("removing device"); platform_set_drvdata(pdev, NULL); + if (priv->data.dual_emac) + unregister_netdev(cpsw_get_slave_ndev(priv, 1)); + unregister_netdev(ndev); + + cpts_unregister(priv->cpts); + for (i = 0; i < priv->num_irqs; i++) + free_irq(priv->irqs_table[i], priv); - cpts_unregister(&priv->cpts); - free_irq(ndev->irq, priv); cpsw_ale_destroy(priv->ale); cpdma_chan_destroy(priv->txch); cpdma_chan_destroy(priv->rxch); @@ -1421,8 +1962,10 @@ static int cpsw_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_put(priv->clk); kfree(priv->slaves); + kfree(priv->data.slave_data); + if (priv->data.dual_emac) + free_netdev(cpsw_get_slave_ndev(priv, 1)); free_netdev(ndev); - return 0; } @@ -1458,6 +2001,7 @@ static const struct of_device_id cpsw_of_mtable[] = { { .compatible = "ti,cpsw", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, cpsw_of_mtable); static struct platform_driver cpsw_driver = { .driver = { diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 0e9ccc2..7fa60d6 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -148,7 +148,7 @@ static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry) return idx; } -static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr) +int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; @@ -160,6 +160,8 @@ static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr) type = cpsw_ale_get_entry_type(ale_entry); if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) continue; + if (cpsw_ale_get_vlan_id(ale_entry) != vid) + continue; cpsw_ale_get_addr(ale_entry, entry_addr); if (memcmp(entry_addr, addr, 6) == 0) return idx; @@ -167,6 +169,22 @@ static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr) return -ENOENT; } +int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < ale->params.ale_entries; idx++) { + cpsw_ale_read(ale, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_VLAN) + continue; + if (cpsw_ale_get_vlan_id(ale_entry) == vid) + return idx; + } + return -ENOENT; +} + static int cpsw_ale_match_free(struct cpsw_ale *ale) { u32 ale_entry[ALE_ENTRY_WORDS]; @@ -274,19 +292,32 @@ int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask) return 0; } -int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags) +static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry, + int flags, u16 vid) +{ + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } +} + +int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, + int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid); + cpsw_ale_set_addr(ale_entry, addr); cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); cpsw_ale_set_port_num(ale_entry, port); - idx = cpsw_ale_match_addr(ale, addr); + idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0); if (idx < 0) idx = cpsw_ale_match_free(ale); if (idx < 0) @@ -298,12 +329,13 @@ int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags) return 0; } -int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port) +int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, + int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - idx = cpsw_ale_match_addr(ale, addr); + idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0); if (idx < 0) return -ENOENT; @@ -313,18 +345,19 @@ int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port) } int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, - int super, int mcast_state) + int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx, mask; - idx = cpsw_ale_match_addr(ale, addr); + idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0); if (idx >= 0) cpsw_ale_read(ale, idx, ale_entry); - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid); + cpsw_ale_set_addr(ale_entry, addr); - cpsw_ale_set_super(ale_entry, super); + cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); cpsw_ale_set_mcast_state(ale_entry, mcast_state); mask = cpsw_ale_get_port_mask(ale_entry); @@ -342,12 +375,13 @@ int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, return 0; } -int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask) +int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, + int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - idx = cpsw_ale_match_addr(ale, addr); + idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0); if (idx < 0) return -EINVAL; @@ -362,6 +396,55 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask) return 0; } +int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, + int reg_mcast, int unreg_mcast) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + idx = cpsw_ale_match_vlan(ale, vid); + if (idx >= 0) + cpsw_ale_read(ale, idx, ale_entry); + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); + cpsw_ale_set_vlan_id(ale_entry, vid); + + cpsw_ale_set_vlan_untag_force(ale_entry, untag); + cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast); + cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast); + cpsw_ale_set_vlan_member_list(ale_entry, port); + + if (idx < 0) + idx = cpsw_ale_match_free(ale); + if (idx < 0) + idx = cpsw_ale_find_ageable(ale); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(ale, idx, ale_entry); + return 0; +} + +int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + idx = cpsw_ale_match_vlan(ale, vid); + if (idx < 0) + return -ENOENT; + + cpsw_ale_read(ale, idx, ale_entry); + + if (port_mask) + cpsw_ale_set_vlan_member_list(ale_entry, port_mask); + else + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); + + cpsw_ale_write(ale, idx, ale_entry); + return 0; +} + struct ale_control_info { const char *name; int offset, port_offset; diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index 2bd09cb..30daa12 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -64,8 +64,14 @@ enum cpsw_ale_port_state { }; /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ -#define ALE_SECURE 1 -#define ALE_BLOCKED 2 +#define ALE_SECURE BIT(0) +#define ALE_BLOCKED BIT(1) +#define ALE_SUPER BIT(2) +#define ALE_VLAN BIT(3) + +#define ALE_PORT_HOST BIT(0) +#define ALE_PORT_1 BIT(1) +#define ALE_PORT_2 BIT(2) #define ALE_MCAST_FWD 0 #define ALE_MCAST_BLOCK_LEARN_FWD 1 @@ -81,11 +87,17 @@ void cpsw_ale_stop(struct cpsw_ale *ale); int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout); int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask); int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask); -int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags); -int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port); +int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, + int flags, u16 vid); +int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, + int flags, u16 vid); int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, - int super, int mcast_state); -int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask); + int flags, u16 vid, int mcast_state); +int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, + int flags, u16 vid); +int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, + int reg_mcast, int unreg_mcast); +int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port); int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control); int cpsw_ale_control_set(struct cpsw_ale *ale, int port, diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 4995673..49dfd59 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -20,6 +20,7 @@ #include <linux/err.h> #include <linux/dma-mapping.h> #include <linux/io.h> +#include <linux/delay.h> #include "davinci_cpdma.h" @@ -60,6 +61,9 @@ #define CPDMA_DESC_EOQ BIT(28) #define CPDMA_DESC_TD_COMPLETE BIT(27) #define CPDMA_DESC_PASS_CRC BIT(26) +#define CPDMA_DESC_TO_PORT_EN BIT(20) +#define CPDMA_TO_PORT_SHIFT 16 +#define CPDMA_DESC_PORT_MASK (BIT(18) | BIT(17) | BIT(16)) #define CPDMA_TEARDOWN_VALUE 0xfffffffc @@ -105,13 +109,13 @@ struct cpdma_ctlr { }; struct cpdma_chan { + struct cpdma_desc __iomem *head, *tail; + void __iomem *hdp, *cp, *rxfree; enum cpdma_state state; struct cpdma_ctlr *ctlr; int chan_num; spinlock_t lock; - struct cpdma_desc __iomem *head, *tail; int count; - void __iomem *hdp, *cp, *rxfree; u32 mask; cpdma_handler_fn handler; enum dma_data_direction dir; @@ -132,6 +136,14 @@ struct cpdma_chan { #define chan_write(chan, fld, v) __raw_writel(v, (chan)->fld) #define desc_write(desc, fld, v) __raw_writel((u32)(v), &(desc)->fld) +#define cpdma_desc_to_port(chan, mode, directed) \ + do { \ + if (!is_rx_chan(chan) && ((directed == 1) || \ + (directed == 2))) \ + mode |= (CPDMA_DESC_TO_PORT_EN | \ + (directed << CPDMA_TO_PORT_SHIFT)); \ + } while (0) + /* * Utility constructs for a cpdma descriptor pool. Some devices (e.g. davinci * emac) have dedicated on-chip memory for these descriptors. Some other @@ -217,17 +229,27 @@ desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma) } static struct cpdma_desc __iomem * -cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc) +cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc, bool is_rx) { unsigned long flags; int index; + int desc_start; + int desc_end; struct cpdma_desc __iomem *desc = NULL; spin_lock_irqsave(&pool->lock, flags); - index = bitmap_find_next_zero_area(pool->bitmap, pool->num_desc, 0, - num_desc, 0); - if (index < pool->num_desc) { + if (is_rx) { + desc_start = 0; + desc_end = pool->num_desc/2; + } else { + desc_start = pool->num_desc/2; + desc_end = pool->num_desc; + } + + index = bitmap_find_next_zero_area(pool->bitmap, + desc_end, desc_start, num_desc, 0); + if (index < desc_end) { bitmap_set(pool->bitmap, index, num_desc); desc = pool->iomap + pool->desc_size * index; pool->used_desc++; @@ -291,14 +313,16 @@ int cpdma_ctlr_start(struct cpdma_ctlr *ctlr) } if (ctlr->params.has_soft_reset) { - unsigned long timeout = jiffies + HZ/10; + unsigned timeout = 10 * 100; dma_reg_write(ctlr, CPDMA_SOFTRESET, 1); - while (time_before(jiffies, timeout)) { + while (timeout) { if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0) break; + udelay(10); + timeout--; } - WARN_ON(!time_before(jiffies, timeout)); + WARN_ON(!timeout); } for (i = 0; i < ctlr->num_chan; i++) { @@ -439,10 +463,8 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) if (ctlr->state != CPDMA_STATE_IDLE) cpdma_ctlr_stop(ctlr); - for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { - if (ctlr->channels[i]) - cpdma_chan_destroy(ctlr->channels[i]); - } + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) + cpdma_chan_destroy(ctlr->channels[i]); cpdma_desc_pool_destroy(ctlr->pool); spin_unlock_irqrestore(&ctlr->lock, flags); @@ -473,11 +495,13 @@ int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable) spin_unlock_irqrestore(&ctlr->lock, flags); return 0; } +EXPORT_SYMBOL_GPL(cpdma_ctlr_int_ctrl); -void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr) +void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value) { - dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0); + dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, value); } +EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi); struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, cpdma_handler_fn handler) @@ -652,7 +676,7 @@ static void __cpdma_chan_submit(struct cpdma_chan *chan, } int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, - int len, gfp_t gfp_mask) + int len, int directed) { struct cpdma_ctlr *ctlr = chan->ctlr; struct cpdma_desc __iomem *desc; @@ -668,7 +692,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, goto unlock_ret; } - desc = cpdma_desc_alloc(ctlr->pool, 1); + desc = cpdma_desc_alloc(ctlr->pool, 1, is_rx_chan(chan)); if (!desc) { chan->stats.desc_alloc_fail++; ret = -ENOMEM; @@ -682,6 +706,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, buffer = dma_map_single(ctlr->dev, data, len, chan->dir); mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + cpdma_desc_to_port(chan, mode, directed); desc_write(desc, hw_next, 0); desc_write(desc, hw_buffer, buffer); @@ -704,6 +729,29 @@ unlock_ret: } EXPORT_SYMBOL_GPL(cpdma_chan_submit); +bool cpdma_check_free_tx_desc(struct cpdma_chan *chan) +{ + unsigned long flags; + int index; + bool ret; + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc_pool *pool = ctlr->pool; + + spin_lock_irqsave(&pool->lock, flags); + + index = bitmap_find_next_zero_area(pool->bitmap, + pool->num_desc, pool->num_desc/2, 1, 0); + + if (index < pool->num_desc) + ret = true; + else + ret = false; + + spin_unlock_irqrestore(&pool->lock, flags); + return ret; +} +EXPORT_SYMBOL_GPL(cpdma_check_free_tx_desc); + static void __cpdma_chan_free(struct cpdma_chan *chan, struct cpdma_desc __iomem *desc, int outlen, int status) @@ -728,6 +776,7 @@ static int __cpdma_chan_process(struct cpdma_chan *chan) struct cpdma_ctlr *ctlr = chan->ctlr; struct cpdma_desc __iomem *desc; int status, outlen; + int cb_status = 0; struct cpdma_desc_pool *pool = ctlr->pool; dma_addr_t desc_dma; unsigned long flags; @@ -749,7 +798,8 @@ static int __cpdma_chan_process(struct cpdma_chan *chan) status = -EBUSY; goto unlock_ret; } - status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE); + status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE | + CPDMA_DESC_PORT_MASK); chan->head = desc_from_phys(pool, desc_read(desc, hw_next)); chan_write(chan, cp, desc_dma); @@ -762,8 +812,12 @@ static int __cpdma_chan_process(struct cpdma_chan *chan) } spin_unlock_irqrestore(&chan->lock, flags); + if (unlikely(status & CPDMA_DESC_TD_COMPLETE)) + cb_status = -ENOSYS; + else + cb_status = status; - __cpdma_chan_free(chan, desc, outlen, status); + __cpdma_chan_free(chan, desc, outlen, cb_status); return status; unlock_ret: @@ -822,7 +876,7 @@ int cpdma_chan_stop(struct cpdma_chan *chan) struct cpdma_desc_pool *pool = ctlr->pool; unsigned long flags; int ret; - unsigned long timeout; + unsigned timeout; spin_lock_irqsave(&chan->lock, flags); if (chan->state != CPDMA_STATE_ACTIVE) { @@ -837,14 +891,15 @@ int cpdma_chan_stop(struct cpdma_chan *chan) dma_reg_write(ctlr, chan->td, chan_linear(chan)); /* wait for teardown complete */ - timeout = jiffies + HZ/10; /* 100 msec */ - while (time_before(jiffies, timeout)) { + timeout = 100 * 100; /* 100 ms */ + while (timeout) { u32 cp = chan_read(chan, cp); if ((cp & CPDMA_TEARDOWN_VALUE) == CPDMA_TEARDOWN_VALUE) break; - cpu_relax(); + udelay(10); + timeout--; } - WARN_ON(!time_before(jiffies, timeout)); + WARN_ON(!timeout); chan_write(chan, cp, CPDMA_TEARDOWN_VALUE); /* handle completed packets */ @@ -984,3 +1039,6 @@ unlock_ret: spin_unlock_irqrestore(&ctlr->lock, flags); return ret; } +EXPORT_SYMBOL_GPL(cpdma_control_set); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h index afa19a0..86dee48 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.h +++ b/drivers/net/ethernet/ti/davinci_cpdma.h @@ -24,6 +24,13 @@ #define __chan_linear(chan_num) ((chan_num) & (CPDMA_MAX_CHANNELS - 1)) #define chan_linear(chan) __chan_linear((chan)->chan_num) +#define CPDMA_RX_SOURCE_PORT(__status__) ((__status__ >> 16) & 0x7) + +#define CPDMA_EOI_RX_THRESH 0x0 +#define CPDMA_EOI_RX 0x1 +#define CPDMA_EOI_TX 0x2 +#define CPDMA_EOI_MISC 0x3 + struct cpdma_params { struct device *dev; void __iomem *dmaregs; @@ -82,12 +89,13 @@ int cpdma_chan_dump(struct cpdma_chan *chan); int cpdma_chan_get_stats(struct cpdma_chan *chan, struct cpdma_chan_stats *stats); int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, - int len, gfp_t gfp_mask); + int len, int directed); int cpdma_chan_process(struct cpdma_chan *chan, int quota); int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable); -void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr); +void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value); int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable); +bool cpdma_check_free_tx_desc(struct cpdma_chan *chan); enum cpdma_control { CPDMA_CMD_IDLE, /* write-only */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 2a3e2c5..860e15d 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -120,7 +120,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_DEF_TX_CH (0) /* Default 0th channel */ #define EMAC_DEF_RX_CH (0) /* Default 0th channel */ #define EMAC_DEF_RX_NUM_DESC (128) -#define EMAC_DEF_TX_NUM_DESC (128) #define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */ #define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */ #define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */ @@ -342,7 +341,6 @@ struct emac_priv { u32 mac_hash2; u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS]; u32 rx_addr_type; - atomic_t cur_tx; const char *phy_id; #ifdef CONFIG_OF struct device_node *phy_node; @@ -480,8 +478,8 @@ static void emac_dump_regs(struct emac_priv *priv) static void emac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strcpy(info->driver, emac_version_string); - strcpy(info->version, EMAC_MODULE_VERSION); + strlcpy(info->driver, emac_version_string, sizeof(info->driver)); + strlcpy(info->version, EMAC_MODULE_VERSION, sizeof(info->version)); } /** @@ -1039,7 +1037,7 @@ static void emac_rx_handler(void *token, int len, int status) recycle: ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, - skb_tailroom(skb), GFP_KERNEL); + skb_tailroom(skb), 0); WARN_ON(ret == -ENOMEM); if (unlikely(ret < 0)) @@ -1050,12 +1048,12 @@ static void emac_tx_handler(void *token, int len, int status) { struct sk_buff *skb = token; struct net_device *ndev = skb->dev; - struct emac_priv *priv = netdev_priv(ndev); - - atomic_dec(&priv->cur_tx); + /* Check whether the queue is stopped due to stalled tx dma, if the + * queue is stopped then start the queue as we have free desc for tx + */ if (unlikely(netif_queue_stopped(ndev))) - netif_start_queue(ndev); + netif_wake_queue(ndev); ndev->stats.tx_packets++; ndev->stats.tx_bytes += len; dev_kfree_skb_any(skb); @@ -1094,14 +1092,17 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) skb_tx_timestamp(skb); ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len, - GFP_KERNEL); + 0); if (unlikely(ret_code != 0)) { if (netif_msg_tx_err(priv) && net_ratelimit()) dev_err(emac_dev, "DaVinci EMAC: desc submit failed"); goto fail_tx; } - if (atomic_inc_return(&priv->cur_tx) >= EMAC_DEF_TX_NUM_DESC) + /* If there is no more tx desc left free then we need to + * tell the kernel to stop sending us tx frames. + */ + if (unlikely(!cpdma_check_free_tx_desc(priv->txchan))) netif_stop_queue(ndev); return NETDEV_TX_OK; @@ -1264,7 +1265,6 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) /* Store mac addr in priv and rx channel and set it in EMAC hw */ memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len); - ndev->addr_assign_type &= ~NET_ADDR_RANDOM; /* MAC address is configured only after the interface is enabled. */ if (netif_running(ndev)) { @@ -1438,7 +1438,7 @@ static int emac_poll(struct napi_struct *napi, int budget) * Polled functionality used by netconsole and others in non interrupt mode * */ -void emac_poll_controller(struct net_device *ndev) +static void emac_poll_controller(struct net_device *ndev) { struct emac_priv *priv = netdev_priv(ndev); @@ -1558,7 +1558,7 @@ static int emac_dev_open(struct net_device *ndev) break; ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, - skb_tailroom(skb), GFP_KERNEL); + skb_tailroom(skb), 0); if (WARN_ON(ret < 0)) break; } @@ -1600,7 +1600,7 @@ static int emac_dev_open(struct net_device *ndev) if (priv->phy_id && *priv->phy_id) { priv->phydev = phy_connect(ndev, priv->phy_id, - &emac_adjust_link, 0, + &emac_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { @@ -1865,21 +1865,18 @@ static int davinci_emac_probe(struct platform_device *pdev) /* obtain emac clock from kernel */ - emac_clk = clk_get(&pdev->dev, NULL); + emac_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(emac_clk)) { dev_err(&pdev->dev, "failed to get EMAC clock\n"); return -EBUSY; } emac_bus_frequency = clk_get_rate(emac_clk); - clk_put(emac_clk); /* TODO: Probe PHY here if possible */ ndev = alloc_etherdev(sizeof(struct emac_priv)); - if (!ndev) { - rc = -ENOMEM; - goto no_ndev; - } + if (!ndev) + return -ENOMEM; platform_set_drvdata(pdev, ndev); priv = netdev_priv(ndev); @@ -1893,7 +1890,7 @@ static int davinci_emac_probe(struct platform_device *pdev) if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); rc = -ENODEV; - goto probe_quit; + goto no_pdata; } /* MAC addr and PHY mask , RMII enable info from platform_data */ @@ -1913,23 +1910,23 @@ static int davinci_emac_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev,"error getting res\n"); rc = -ENOENT; - goto probe_quit; + goto no_pdata; } priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; size = resource_size(res); - if (!request_mem_region(res->start, size, ndev->name)) { + if (!devm_request_mem_region(&pdev->dev, res->start, + size, ndev->name)) { dev_err(&pdev->dev, "failed request_mem_region() for regs\n"); rc = -ENXIO; - goto probe_quit; + goto no_pdata; } - priv->remap_addr = ioremap(res->start, size); + priv->remap_addr = devm_ioremap(&pdev->dev, res->start, size); if (!priv->remap_addr) { dev_err(&pdev->dev, "unable to map IO\n"); rc = -ENOMEM; - release_mem_region(res->start, size); - goto probe_quit; + goto no_pdata; } priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset; ndev->base_addr = (unsigned long)priv->remap_addr; @@ -1962,7 +1959,7 @@ static int davinci_emac_probe(struct platform_device *pdev) if (!priv->dma) { dev_err(&pdev->dev, "error initializing DMA\n"); rc = -ENOMEM; - goto no_dma; + goto no_pdata; } priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH), @@ -1971,14 +1968,14 @@ static int davinci_emac_probe(struct platform_device *pdev) emac_rx_handler); if (WARN_ON(!priv->txchan || !priv->rxchan)) { rc = -ENOMEM; - goto no_irq_res; + goto no_cpdma_chan; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "error getting irq res\n"); rc = -ENOENT; - goto no_irq_res; + goto no_cpdma_chan; } ndev->irq = res->start; @@ -2000,7 +1997,7 @@ static int davinci_emac_probe(struct platform_device *pdev) if (rc) { dev_err(&pdev->dev, "error in register_netdev\n"); rc = -ENODEV; - goto no_irq_res; + goto no_cpdma_chan; } @@ -2015,20 +2012,14 @@ static int davinci_emac_probe(struct platform_device *pdev) return 0; -no_irq_res: +no_cpdma_chan: if (priv->txchan) cpdma_chan_destroy(priv->txchan); if (priv->rxchan) cpdma_chan_destroy(priv->rxchan); cpdma_ctlr_destroy(priv->dma); -no_dma: - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - iounmap(priv->remap_addr); - -probe_quit: +no_pdata: free_netdev(ndev); -no_ndev: return rc; } @@ -2041,14 +2032,12 @@ no_ndev: */ static int davinci_emac_remove(struct platform_device *pdev) { - struct resource *res; struct net_device *ndev = platform_get_drvdata(pdev); struct emac_priv *priv = netdev_priv(ndev); dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n"); platform_set_drvdata(pdev, NULL); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (priv->txchan) cpdma_chan_destroy(priv->txchan); @@ -2056,10 +2045,7 @@ static int davinci_emac_remove(struct platform_device *pdev) cpdma_chan_destroy(priv->rxchan); cpdma_ctlr_destroy(priv->dma); - release_mem_region(res->start, resource_size(res)); - unregister_netdev(ndev); - iounmap(priv->remap_addr); free_netdev(ndev); return 0; diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index cca2550..12aec17 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -320,10 +320,8 @@ static int davinci_mdio_probe(struct platform_device *pdev) int ret, addr; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(dev, "failed to alloc device data\n"); + if (!data) return -ENOMEM; - } data->bus = mdiobus_alloc(); if (!data->bus) { @@ -487,6 +485,7 @@ static const struct of_device_id davinci_mdio_of_mtable[] = { { .compatible = "ti,davinci_mdio", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable); static struct platform_driver davinci_mdio_driver = { .driver = { diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 2272538..60c400f 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -320,6 +320,7 @@ static void tlan_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_set_drvdata(pdev, NULL); + cancel_work_sync(&priv->tlan_tqueue); } static void tlan_start(struct net_device *dev) @@ -1911,10 +1912,8 @@ static void tlan_reset_lists(struct net_device *dev) list->frame_size = TLAN_MAX_FRAME_SIZE; list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; skb = netdev_alloc_skb_ip_align(dev, TLAN_MAX_FRAME_SIZE + 5); - if (!skb) { - netdev_err(dev, "Out of memory for received data\n"); + if (!skb) break; - } list->buffer[0].address = pci_map_single(priv->pci_dev, skb->data, |