summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig72
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/can/Kconfig167
-rw-r--r--drivers/net/can/Makefile46
-rw-r--r--drivers/net/can/dev.c528
-rw-r--r--drivers/net/can/flexcan/Makefile3
-rw-r--r--drivers/net/can/flexcan/dev.c619
-rw-r--r--drivers/net/can/flexcan/drv.c624
-rw-r--r--drivers/net/can/flexcan/flexcan.h223
-rw-r--r--drivers/net/can/flexcan/mbm.c347
-rw-r--r--drivers/net/can/mcp251x.c1239
-rw-r--r--drivers/net/can/mscan/Makefile23
-rw-r--r--drivers/net/can/mscan/mpc52xx_can.c293
-rw-r--r--drivers/net/can/mscan/mscan.c679
-rw-r--r--drivers/net/can/mscan/mscan.h237
-rw-r--r--drivers/net/can/old/Kconfig67
-rw-r--r--drivers/net/can/old/ccan/Makefile20
-rw-r--r--drivers/net/can/old/ccan/ccan.c557
-rw-r--r--drivers/net/can/old/ccan/ccan.h140
-rw-r--r--drivers/net/can/old/ccan/h7202_can.c199
-rw-r--r--drivers/net/can/old/hal/c200.c206
-rw-r--r--drivers/net/can/old/hal/esdio.c186
-rw-r--r--drivers/net/can/old/hal/gw2.c161
-rw-r--r--drivers/net/can/old/hal/hal.h99
-rw-r--r--drivers/net/can/old/hal/io.c123
-rw-r--r--drivers/net/can/old/hal/iomem.c142
-rw-r--r--drivers/net/can/old/hal/iomux.c125
-rw-r--r--drivers/net/can/old/hal/pc7io.c126
-rw-r--r--drivers/net/can/old/i82527/Makefile24
-rw-r--r--drivers/net/can/old/i82527/i82527.c1251
-rw-r--r--drivers/net/can/old/i82527/i82527.h300
-rw-r--r--drivers/net/can/old/i82527/proc.c209
-rw-r--r--drivers/net/can/old/mscan/Makefile21
-rw-r--r--drivers/net/can/old/mscan/mpc52xx_can.c248
-rw-r--r--drivers/net/can/old/mscan/mscan.c708
-rw-r--r--drivers/net/can/old/mscan/mscan.h247
-rw-r--r--drivers/net/can/old/sja1000/Makefile27
-rw-r--r--drivers/net/can/old/sja1000/proc.c230
-rw-r--r--drivers/net/can/old/sja1000/sja1000.c1140
-rw-r--r--drivers/net/can/old/sja1000/sja1000.h187
-rw-r--r--drivers/net/can/sja1000/Makefile30
-rw-r--r--drivers/net/can/sja1000/ems_pci.c328
-rw-r--r--drivers/net/can/sja1000/ems_pcmcia.c418
-rw-r--r--drivers/net/can/sja1000/ixxat_pci.c272
-rw-r--r--drivers/net/can/sja1000/kvaser_pci.c415
-rw-r--r--drivers/net/can/sja1000/peak_pci.c348
-rw-r--r--drivers/net/can/sja1000/pipcan.c201
-rw-r--r--drivers/net/can/sja1000/sja1000.c677
-rw-r--r--drivers/net/can/sja1000/sja1000.h177
-rw-r--r--drivers/net/can/sja1000/sja1000_platform.c169
-rw-r--r--drivers/net/can/slcan.c904
-rw-r--r--drivers/net/can/softing/Makefile20
-rw-r--r--drivers/net/can/softing/softing.h268
-rw-r--r--drivers/net/can/softing/softing_cs.c427
-rw-r--r--drivers/net/can/softing/softing_fw.c685
-rw-r--r--drivers/net/can/softing/softing_main.c1058
-rw-r--r--drivers/net/can/sysfs.c509
-rw-r--r--drivers/net/can/sysfs.h24
-rw-r--r--drivers/net/can/vcan.c21
-rw-r--r--drivers/net/cs89x0.c21
-rw-r--r--drivers/net/enc28j60.c186
-rw-r--r--drivers/net/fec.c744
-rw-r--r--drivers/net/fec.h18
-rw-r--r--drivers/net/irda/Kconfig4
-rw-r--r--drivers/net/irda/Makefile1
-rw-r--r--drivers/net/irda/mxc_ir.c1777
-rw-r--r--drivers/net/irda/mxc_ir.h133
-rwxr-xr-xdrivers/net/ns9xxx-eth.c1499
-rw-r--r--drivers/net/smc911x.h8
-rw-r--r--drivers/net/smsc9118/Makefile1
-rw-r--r--drivers/net/smsc9118/smsc911x.c2711
-rw-r--r--drivers/net/smsc9118/smsc911x.h393
-rw-r--r--drivers/net/smsc911x.c2253
-rw-r--r--drivers/net/smsc911x.h395
-rw-r--r--drivers/net/wireless/Kconfig39
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/digiPiper/Kconfig27
-rw-r--r--drivers/net/wireless/digiPiper/Makefile16
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.c161
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.h17
-rw-r--r--drivers/net/wireless/digiPiper/airoha.c986
-rw-r--r--drivers/net/wireless/digiPiper/airoha.h30
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.c974
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.h108
-rw-r--r--drivers/net/wireless/digiPiper/digiDebug.c205
-rw-r--r--drivers/net/wireless/digiPiper/digiIsr.c159
-rw-r--r--drivers/net/wireless/digiPiper/digiMac80211.c1000
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.c1164
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.h50
-rw-r--r--drivers/net/wireless/digiPiper/digiRx.c412
-rw-r--r--drivers/net/wireless/digiPiper/digiTx.c604
-rw-r--r--drivers/net/wireless/digiPiper/mac.h375
-rw-r--r--drivers/net/wireless/digiPiper/phy.c269
-rw-r--r--drivers/net/wireless/digiPiper/phy.h28
-rw-r--r--drivers/net/wireless/digiPiper/piper.c1032
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.h8
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.h9
-rw-r--r--drivers/net/wireless/digiPiper/pipermain.h381
-rw-r--r--drivers/net/wireless/digi_wi_g.c5021
-rw-r--r--drivers/net/wireless/digi_wi_g.h660
-rw-r--r--drivers/net/wireless/digi_wi_g_priv_handler.c345
103 files changed, 43450 insertions, 143 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 231eeaf1d552..601215803b79 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -962,6 +962,17 @@ config ENC28J60_WRITEVERIFY
Enable the verify after the buffer write useful for debugging purpose.
If unsure, say N.
+config NS9XXX_ETH
+ tristate "Digi NS9xxx ethernet support"
+ depends on ARM && ARCH_NS9XXX
+ depends on PHYLIB
+ depends on CRC32
+ help
+ If you have a Digi NS9xxx based system and wish to use its ethernet
+ port.
+ If you choose to build this driver as a module, it will be called
+ ns9xxx-eth.
+
config SMC911X
tristate "SMSC LAN911[5678] support"
select CRC32
@@ -970,14 +981,35 @@ config SMC911X
help
This is a driver for SMSC's LAN911x series of Ethernet chipsets
including the new LAN9115, LAN9116, LAN9117, and LAN9118.
- Say Y if you want it compiled into the kernel,
+ Say Y if you want it compiled into the kernel,
and read the Ethernet-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>.
- This driver is also available as a module. The module will be
- called smc911x. If you want to compile it as a module, say M
+ This driver is also available as a module. The module will be
+ called smc911x. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>
+config SMSC911X
+ tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+ depends on NET_ETHERNET
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you want support for SMSC LAN911x and LAN921x families
+ of ethernet controllers.
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called smsc911x.
+
+config SMSC9118
+ tristate "SMSC LAN9218 support"
+ depends on NET_ETHERNET && (MACH_CC9M2443JS || MACH_CCW9M2443JS || MACH_CCWMX51JS || MACH_CCMX51JS)
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you want support for SMSC LAN921x families
+ of ethernet controllers.
+
config NET_VENDOR_RACAL
bool "Racal-Interlan (Micom) NI cards"
depends on ISA
@@ -1393,7 +1425,7 @@ config FORCEDETH_NAPI
config CS89x0
tristate "CS89x0 support"
depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
- || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_MX31ADS)
+ || ARCH_IXDP2X01 || ARCH_PNX010X || ARCH_MXC)
---help---
Support for CS89x0 chipset based Ethernet cards. If you have a
network (Ethernet) card of this type, say Y and read the
@@ -1407,7 +1439,7 @@ config CS89x0
config CS89x0_NONISA_IRQ
def_bool y
depends on CS89x0 != n
- depends on MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_MX31ADS
+ depends on MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || ARCH_MXC
config TC35815
tristate "TOSHIBA TC35815 Ethernet support"
@@ -1433,9 +1465,9 @@ config E100
select MII
---help---
This driver supports Intel(R) PRO/100 family of adapters.
- To verify that your adapter is supported, find the board ID number
- on the adapter. Look for a label that has a barcode and a number
- in the format 123456-001 (six digits hyphen three digits).
+ To verify that your adapter is supported, find the board ID number
+ on the adapter. Look for a label that has a barcode and a number
+ in the format 123456-001 (six digits hyphen three digits).
Use the above information and the Adapter & Driver ID Guide at:
@@ -1447,7 +1479,7 @@ config E100
<http://appsr.intel.com/scripts-df/support_intel.asp>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/e100.txt>.
To compile this driver as a module, choose M here. The module
@@ -1810,11 +1842,11 @@ config 68360_ENET
the Motorola 68360 processor.
config FEC
- bool "FEC ethernet controller (of ColdFire CPUs)"
- depends on M523x || M527x || M5272 || M528x || M520x
+ tristate "FEC ethernet controller"
+ depends on M523x || M527x || M5272 || M528x || M520x || ARCH_MX27 || ARCH_MX37 || ARCH_MX35 || ARCH_MX51 || ARCH_MX25
help
Say Y here if you want to use the built-in 10/100 Fast ethernet
- controller on some Motorola ColdFire processors.
+ controller on some Motorola/Freescale processors.
config FEC2
bool "Second FEC ethernet controller (on some ColdFire CPUs)"
@@ -1935,7 +1967,7 @@ config E1000
depends on PCI
---help---
This driver supports Intel(R) PRO/1000 gigabit ethernet family of
- adapters. For more information on how to identify your adapter, go
+ adapters. For more information on how to identify your adapter, go
to the Adapter & Driver ID Guide at:
<http://support.intel.com/support/network/adapter/pro100/21397.htm>
@@ -1945,7 +1977,7 @@ config E1000
<http://support.intel.com>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/e1000.txt>.
To compile this driver as a module, choose M here. The module
@@ -2122,7 +2154,7 @@ config SKGE
and related Gigabit Ethernet adapters. It is a new smaller driver
with better performance and more complete ethtool support.
- It does not support the link failover and network management
+ It does not support the link failover and network management
features that "portable" vendor supplied sk98lin driver does.
This driver supports adapters based on the original Yukon chipset:
@@ -2466,7 +2498,7 @@ config IXGB
<http://support.intel.com>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/ixgb.txt>.
To compile this driver as a module, choose M here. The module
@@ -2476,8 +2508,8 @@ config S2IO
tristate "S2IO 10Gbe XFrame NIC"
depends on PCI
---help---
- This driver supports the 10Gbe XFrame NIC of S2IO.
- More specific information on configuring the driver is in
+ This driver supports the 10Gbe XFrame NIC of S2IO.
+ More specific information on configuring the driver is in
<file:Documentation/networking/s2io.txt>.
config MYRI10GE
@@ -2897,9 +2929,9 @@ config PPPOE
Support for PPP over Ethernet.
This driver requires the latest version of pppd from the CVS
- repository at cvs.samba.org. Alternatively, see the
+ repository at cvs.samba.org. Alternatively, see the
RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
- which contains instruction on how to use this driver (under
+ which contains instruction on how to use this driver (under
the heading "Kernel mode PPPoE").
config PPPOATM
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 017383ad5ec6..c7cf052d08cf 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -220,18 +220,21 @@ obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_NS9XXX_ETH) += ns9xxx-eth.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_ARM) += arm/
+obj-$(CONFIG_SMSC9118) += smsc9118/
obj-$(CONFIG_DEV_APPLETALK) += appletalk/
obj-$(CONFIG_TR) += tokenring/
obj-$(CONFIG_WAN) += wan/
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 57def0d57371..3ac5a12a0c8b 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -12,6 +12,164 @@ config CAN_VCAN
This driver can also be built as a module. If so, the module
will be called vcan.
+config CAN_SLCAN
+ tristate "Serial / USB serial CAN Adaptors (slcan)"
+ depends on CAN && EXPERIMENTAL
+ default N
+ ---help---
+ CAN driver for several 'low cost' CAN interfaces that are attached
+ via serial lines or via USB-to-serial adapters using the LAWICEL
+ ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
+
+ This driver can also be built as a module. If so, the module
+ will be called slcan.
+
+config CAN_OLD_DRIVERS
+ tristate "Prompt for old CAN drivers (e.g. no sysfs support)"
+ depends on CAN
+ default N
+ ---help---
+ The old drivers do not support sysfs nor proper platform device
+ support. Some of the old drivers might only be configured by
+ module commandline options.
+
+if CAN_OLD_DRIVERS
+source "drivers/net/can/old/Kconfig"
+endif
+
+config CAN_DEV
+ tristate "Prompt for platform CAN drivers with sysfs support"
+ depends on CAN && SYSFS
+ default Y
+ ---help---
+ Enables the common framework for platform CAN drivers with sysfs
+ support. This is the standard library for CAN drivers.
+ If unsure, say Y.
+
+config CAN_CALC_BITTIMING
+ bool "CAN bit-timing calculation"
+ depends on CAN_DEV
+ default Y
+ ---help---
+ If enabled, CAN bit-timing parameters will be calculated for the
+ bit-rate specified via SYSFS file "bitrate" when the device gets
+ started. This works fine for the most common CAN controllers
+ with standard bit-rates but may fail for exotic bit-rates or CAN
+ source clock frequencies. Disabling saves some space, but then the
+ bit-timing parameters must be specified directly using the SYSFS
+ files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw".
+ If unsure, say Y.
+
+config CAN_SJA1000
+ depends on CAN_DEV
+ tristate "Philips SJA1000"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+
+ This driver will use the new device interface.
+
+config CAN_SJA1000_PLATFORM
+ depends on CAN_SJA1000
+ tristate "generic Platform Bus based SJA1000 driver"
+ ---help---
+ This driver adds support for the SJA1000 chips connected to
+ the "platform bus" (Linux abstraction for directly to the
+ processor attached devices). Which can be found on various
+ boards from Phytec (http://www.phytec.de) like the PCM027,
+ PCM038.
+
+config CAN_SJA1000_OF_PLATFORM
+ depends on CAN_SJA1000 && PPC_OF
+ tristate "generic OF Platform Bus based SJA1000 driver"
+ ---help---
+ This driver adds support for the SJA1000 chips connected to
+ the OpenFirmware "platform bus" found on embedded systems with
+ OpenFirmware bindings, e.g. if you have a PowerPC based system
+ you should enable this option.
+
+config CAN_EMS_PCI
+ tristate "EMS CPC-PCI and CPC-PCIe Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the one or two channel CPC-PCI and CPC-PCIe
+ cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_EMS_PCMCIA
+ tristate "EMS CPC-CARD Card"
+ depends on PCMCIA && CAN_SJA1000
+ ---help---
+ This driver is for the one or two channel CPC-CARD cards from
+ EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_IXXAT_PCI
+ tristate "IXXAT PCI Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the IXXAT PC-I 04/PCI card (1 or 2 channel)
+ from the IXXAT Automation GmbH (http://www.ixxat.de).
+
+config CAN_PEAK_PCI
+ tristate "PEAK PCAN PCI Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the PCAN PCI, the PC-PCI CAN plug-in card (1 or
+ 2 channel) from PEAK Systems (http://www.peak-system.com).
+
+config CAN_PIPCAN
+ depends on CAN_SJA1000
+ tristate "MPL PIPCAN CAN module driver (SJA1000)"
+ ---help---
+ This driver adds support for the PIPCAN module used on some SBC
+ boards from MPL AG (http://www.mpl.ch).
+
+config CAN_KVASER_PCI
+ tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the the PCIcanx and PCIcan cards (1, 2 or
+ 4 channel) from Kvaser (http://www.kvaser.com).
+
+config CAN_SOFTING
+ tristate "Softing Gmbh CAN generic support"
+ depends on CAN_DEV
+ ---help---
+ generic softing CAN cards
+
+config CAN_SOFTING_CS
+ tristate "Softing CAN pcmcia cards"
+ depends on CAN_SOFTING && PCMCIA
+
+config CAN_MSCAN
+ depends on CAN_DEV && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX
+ tristate "Freescale MPC5200 onboard CAN controller"
+ depends on CAN_MSCAN && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_MCP251X
+ tristate "Microchip MCP251x SPI CAN controllers"
+ depends on CAN_DEV && SPI
+ ---help---
+ Driver for the Microchip MCP251x SPI CAN controllers.
+
config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
@@ -22,4 +180,13 @@ config CAN_DEBUG_DEVICES
a problem with CAN support and want to see more of what is going
on.
+config CAN_FLEXCAN
+ tristate "Freescale FlexCAN"
+ depends on CAN && (ARCH_MX25 || ARCH_MX35)
+ default m
+ ---help---
+ This select the support of Freescale CAN(FlexCAN).
+ This driver can also be built as a module.
+ If unsure, say N.
+
endmenu
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c4bead705cd9..60c7ecca200d 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -1,5 +1,49 @@
#
-# Makefile for the Linux Controller Area Network drivers.
#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../..
+
+export CONFIG_CAN_VCAN=m
+export CONFIG_CAN_DEV=m
+#export CONFIG_CAN_SJA1000_OLD=m
+#export CONFIG_CAN_I82527_OLD=m
+export CONFIG_CAN_SJA1000=m
+export CONFIG_CAN_SJA1000_PLATFORM=m
+export CONFIG_CAN_EMS_PCI=m
+export CONFIG_CAN_EMS_PCMCIA=m
+export CONFIG_CAN_PIPCAN=m
+export CONFIG_CAN_SOFTING=m
+export CONFIG_CAN_SOFTING_CS=m
+export CONFIG_CAN_MCP251X=m
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
obj-$(CONFIG_CAN_VCAN) += vcan.o
+obj-$(CONFIG_CAN_SLCAN) += slcan.o
+
+obj-$(CONFIG_CAN_DEV) += can-dev.o
+can-dev-y := dev.o sysfs.o
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000/
+obj-$(CONFIG_CAN_SOFTING) += softing/
+obj-$(CONFIG_CAN_MSCAN) += mscan/
+obj-$(CONFIG_CAN_SJA1000_OLD) += old/sja1000/
+obj-$(CONFIG_CAN_I82527_OLD) += old/i82527/
+obj-$(CONFIG_CAN_MSCAN_OLD) += old/mscan/
+obj-$(CONFIG_CAN_CCAN_OLD) += old/ccan/
+obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
+obj-$(CONFIG_CAN_FLEXCAN) += flexcan/
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
new file mode 100644
index 000000000000..faa75d14cf29
--- /dev/null
+++ b/drivers/net/can/dev.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <net/rtnetlink.h>
+
+#include "sysfs.h"
+
+#define MOD_DESC "CAN device driver interface"
+
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+
+#ifdef CONFIG_CAN_CALC_BITTIMING
+#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+
+/*
+ * Bit-timing calculation derived from:
+ *
+ * Code based on LinCAN sources and H8S2638 project
+ * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ * Copyright 2005 Stanislav Marek
+ * email: pisa@cmp.felk.cvut.cz
+ */
+static int can_update_spt(const struct can_bittiming_const *btc,
+ int sampl_pt, int tseg, int *tseg1, int *tseg2)
+{
+ *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
+ if (*tseg2 < btc->tseg2_min)
+ *tseg2 = btc->tseg2_min;
+ if (*tseg2 > btc->tseg2_max)
+ *tseg2 = btc->tseg2_max;
+ *tseg1 = tseg - *tseg2;
+ if (*tseg1 > btc->tseg1_max) {
+ *tseg1 = btc->tseg1_max;
+ *tseg2 = tseg - *tseg1;
+ }
+ return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+}
+
+static int can_calc_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ long rate, best_rate = 0;
+ long best_error = 1000000000, error = 0;
+ int best_tseg = 0, best_brp = 0, brp = 0;
+ int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
+ int spt_error = 1000, spt = 0, sampl_pt;
+ u64 v64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ /* Use CIA recommended sample points */
+ if (bt->sample_point) {
+ sampl_pt = bt->sample_point;
+ } else {
+ if (bt->bitrate > 800000)
+ sampl_pt = 750;
+ else if (bt->bitrate > 500000)
+ sampl_pt = 800;
+ else
+ sampl_pt = 875;
+ }
+
+ /* tseg even = round down, odd = round up */
+ for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
+ tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
+ tsegall = 1 + tseg / 2;
+ /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
+ brp = bt->clock / (tsegall * bt->bitrate) + tseg % 2;
+ /* chose brp step which is possible in system */
+ brp = (brp / btc->brp_inc) * btc->brp_inc;
+ if ((brp < btc->brp_min) || (brp > btc->brp_max))
+ continue;
+ rate = bt->clock / (brp * tsegall);
+ error = bt->bitrate - rate;
+ /* tseg brp biterror */
+ if (error < 0)
+ error = -error;
+ if (error > best_error)
+ continue;
+ best_error = error;
+ if (error == 0) {
+ spt = can_update_spt(btc, sampl_pt, tseg / 2,
+ &tseg1, &tseg2);
+ error = sampl_pt - spt;
+ if (error < 0)
+ error = -error;
+ if (error > spt_error)
+ continue;
+ spt_error = error;
+ }
+ best_tseg = tseg / 2;
+ best_brp = brp;
+ best_rate = rate;
+ if (error == 0)
+ break;
+ }
+
+ if (best_error) {
+ /* Error in one-tenth of a percent */
+ error = (best_error * 1000) / bt->bitrate;
+ if (error > CAN_CALC_MAX_ERROR) {
+ dev_err(ND2D(dev), "bitrate error %ld.%ld%% too high\n",
+ error / 10, error % 10);
+ return -EDOM;
+ } else {
+ dev_warn(ND2D(dev), "bitrate error %ld.%ld%%\n",
+ error / 10, error % 10);
+ }
+ }
+
+ spt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2);
+
+ v64 = (u64)best_brp * 1000000000UL;
+ do_div(v64, bt->clock);
+ bt->tq = (u32)v64;
+ bt->prop_seg = tseg1 / 2;
+ bt->phase_seg1 = tseg1 - bt->prop_seg;
+ bt->phase_seg2 = tseg2;
+ bt->sjw = 1;
+ bt->brp = best_brp;
+
+ return 0;
+}
+#else /* !CONFIG_CAN_CALC_BITTIMING */
+static int can_calc_bittiming(struct net_device *dev)
+{
+ dev_err(ND2D(dev), "bit-timing calculation not available\n");
+ return -EINVAL;
+}
+#endif /* CONFIG_CAN_CALC_BITTIMING */
+
+int can_sample_point(struct can_bittiming *bt)
+{
+ return ((bt->prop_seg + bt->phase_seg1 + 1) * 1000) /
+ (bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1);
+}
+
+static int can_fixup_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ int tseg1, alltseg;
+ u32 bitrate;
+ u64 brp64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ tseg1 = bt->prop_seg + bt->phase_seg1;
+ if (bt->sjw > btc->sjw_max ||
+ tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
+ bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
+ return -EINVAL;
+
+ brp64 = (u64)bt->clock * (u64)bt->tq;
+ if (btc->brp_inc > 1)
+ do_div(brp64, btc->brp_inc);
+ brp64 += 500000000UL - 1;
+ do_div(brp64, 1000000000UL); /* the practicable BRP */
+ if (btc->brp_inc > 1)
+ brp64 *= btc->brp_inc;
+ bt->brp = (u32)brp64;
+
+ if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
+ return -EINVAL;
+
+ alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
+ bitrate = bt->clock / (bt->brp * alltseg);
+ bt->bitrate = bitrate;
+
+ return 0;
+}
+
+/*
+ * Set CAN bit-timing for the device
+ *
+ * This functions should be called in the open function of the device
+ * driver to determine, check and set appropriate bit-timing parameters.
+ */
+int can_set_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* Check if bit-timing parameters have been pre-defined */
+ if (!priv->bittiming.tq && !priv->bittiming.bitrate) {
+ dev_err(ND2D(dev), "bit-timing not yet defined\n");
+ return -EINVAL;
+ }
+
+ /* Check if the CAN device has bit-timing parameters */
+ if (priv->bittiming_const) {
+
+ /* Check if bit-timing parameters have already been set */
+ if (priv->bittiming.tq && priv->bittiming.bitrate)
+ return 0;
+
+ /* Non-expert mode? Check if the bitrate has been pre-defined */
+ if (!priv->bittiming.tq)
+ /* Determine bit-timing parameters */
+ err = can_calc_bittiming(dev);
+ else
+ /* Check bit-timing params and calculate proper brp */
+ err = can_fixup_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ if (priv->do_set_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(can_set_bittiming);
+
+static void can_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+}
+
+/*
+ * Allocate and setup space for the CAN network device
+ */
+struct net_device *alloc_candev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ priv->state = CAN_STATE_STOPPED;
+ spin_lock_init(&priv->irq_lock);
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_candev);
+
+/*
+ * Allocate space of the CAN network device
+ */
+void free_candev(struct net_device *dev)
+{
+ free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_candev);
+
+/*
+ * Register the CAN network device
+ */
+int register_candev(struct net_device *dev)
+{
+ int err;
+
+ err = register_netdev(dev);
+ if (err)
+ return err;
+
+ can_create_sysfs(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_candev);
+
+/*
+ * Unregister the CAN network device
+ */
+void unregister_candev(struct net_device *dev)
+{
+ can_remove_sysfs(dev);
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_candev);
+
+/*
+ * Local echo of CAN messages
+ *
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented. In the case that the driver does not support the echo
+ * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
+ * to perform the echo as a fallback solution.
+ */
+
+static void can_flush_echo_skb(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ int i;
+
+ for (i = 0; i < CAN_ECHO_SKB_MAX; i++) {
+ if (priv->echo_skb[i]) {
+ kfree_skb(priv->echo_skb[i]);
+ priv->echo_skb[i] = NULL;
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+ }
+ }
+}
+
+/*
+ * Put the skb on the stack to be looped backed locally lateron
+ *
+ * The function is typically called in the start_xmit function
+ * of the device driver.
+ */
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set flag whether this packet has to be looped back */
+ if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (!priv->echo_skb[idx]) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ kfree_skb(old_skb);
+ if (!skb)
+ return;
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb[idx] = skb;
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: BUG! echo_skb is occupied!\n",
+ dev->name, __func__);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL_GPL(can_put_echo_skb);
+
+/*
+ * Get the skb from the stack and loop it back locally
+ *
+ * The function is typically called when the TX done interrupt
+ * is handled in the device driver.
+ */
+void can_get_echo_skb(struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if ((dev->flags & IFF_ECHO) && priv->echo_skb[idx]) {
+ netif_rx(priv->echo_skb[idx]);
+ priv->echo_skb[idx] = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(can_get_echo_skb);
+
+/*
+ * CAN device restart for bus-off recovery
+ */
+int can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ int err;
+
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+
+ /* Cancel restart in progress */
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0; /* mark inactive timer */
+ }
+
+ can_flush_echo_skb(dev);
+
+ err = priv->do_set_mode(dev, CAN_MODE_START);
+ if (err)
+ return err;
+
+ netif_carrier_on(dev);
+
+ dev_dbg(ND2D(dev), "restarted\n");
+ priv->can_stats.restarts++;
+
+ /* send restart message upstream */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+static void can_restart_after(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ priv->timer.expires = 0; /* mark inactive timer */
+ can_restart_now(dev);
+}
+
+/*
+ * CAN bus-off
+ *
+ * This functions should be called when the device goes bus-off to
+ * tell the netif layer that no more packets can be sent or received.
+ * If enabled, a timer is started to trigger bus-off recovery.
+ */
+void can_bus_off(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ dev_dbg(ND2D(dev), "bus-off\n");
+
+ netif_carrier_off(dev);
+
+ if (priv->restart_ms > 0 && !priv->timer.expires) {
+
+ priv->timer.function = can_restart_after;
+ priv->timer.data = (unsigned long)dev;
+ priv->timer.expires =
+ jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+ }
+}
+EXPORT_SYMBOL_GPL(can_bus_off);
+
+/*
+ * Cleanup function before the device gets closed.
+ *
+ * This functions should be called in the close function of the device
+ * driver.
+ */
+void can_close_cleanup(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ can_flush_echo_skb(dev);
+}
+EXPORT_SYMBOL_GPL(can_close_cleanup);
+
+static __init int can_dev_init(void)
+{
+ printk(KERN_INFO MOD_DESC "\n");
+
+ return 0;
+}
+module_init(can_dev_init);
+
+static __exit void can_dev_exit(void)
+{
+}
+module_exit(can_dev_exit);
diff --git a/drivers/net/can/flexcan/Makefile b/drivers/net/can/flexcan/Makefile
new file mode 100644
index 000000000000..b2dbb4fb2793
--- /dev/null
+++ b/drivers/net/can/flexcan/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
+
+flexcan-y := dev.o drv.o mbm.o
diff --git a/drivers/net/can/flexcan/dev.c b/drivers/net/can/flexcan/dev.c
new file mode 100644
index 000000000000..f2040c135a89
--- /dev/null
+++ b/drivers/net/can/flexcan/dev.c
@@ -0,0 +1,619 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file dev.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <linux/module.h>
+#include "flexcan.h"
+
+enum {
+ FLEXCAN_ATTR_STATE = 0,
+ FLEXCAN_ATTR_BITRATE,
+ FLEXCAN_ATTR_BR_PRESDIV,
+ FLEXCAN_ATTR_BR_RJW,
+ FLEXCAN_ATTR_BR_PROPSEG,
+ FLEXCAN_ATTR_BR_PSEG1,
+ FLEXCAN_ATTR_BR_PSEG2,
+ FLEXCAN_ATTR_BR_CLKSRC,
+ FLEXCAN_ATTR_MAXMB,
+ FLEXCAN_ATTR_XMIT_MAXMB,
+ FLEXCAN_ATTR_FIFO,
+ FLEXCAN_ATTR_WAKEUP,
+ FLEXCAN_ATTR_SRX_DIS,
+ FLEXCAN_ATTR_WAK_SRC,
+ FLEXCAN_ATTR_BCC,
+ FLEXCAN_ATTR_LOCAL_PRIORITY,
+ FLEXCAN_ATTR_ABORT,
+ FLEXCAN_ATTR_LOOPBACK,
+ FLEXCAN_ATTR_SMP,
+ FLEXCAN_ATTR_BOFF_REC,
+ FLEXCAN_ATTR_TSYN,
+ FLEXCAN_ATTR_LISTEN,
+ FLEXCAN_ATTR_EXTEND_MSG,
+ FLEXCAN_ATTR_STANDARD_MSG,
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ FLEXCAN_ATTR_DUMP_REG,
+ FLEXCAN_ATTR_DUMP_XMIT_MB,
+ FLEXCAN_ATTR_DUMP_RX_MB,
+#endif
+ FLEXCAN_ATTR_MAX
+};
+
+static ssize_t flexcan_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t flexcan_set_attr(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count);
+
+static struct device_attribute flexcan_dev_attr[FLEXCAN_ATTR_MAX] = {
+ [FLEXCAN_ATTR_STATE] = __ATTR(state, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_BITRATE] =
+ __ATTR(bitrate, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PRESDIV] =
+ __ATTR(br_presdiv, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_RJW] =
+ __ATTR(br_rjw, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PROPSEG] =
+ __ATTR(br_propseg, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PSEG1] =
+ __ATTR(br_pseg1, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PSEG2] =
+ __ATTR(br_pseg2, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_CLKSRC] =
+ __ATTR(br_clksrc, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_MAXMB] =
+ __ATTR(maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_XMIT_MAXMB] =
+ __ATTR(xmit_maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_FIFO] =
+ __ATTR(fifo, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_WAKEUP] =
+ __ATTR(wakeup, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_SRX_DIS] =
+ __ATTR(srx_dis, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_WAK_SRC] =
+ __ATTR(wak_src, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BCC] =
+ __ATTR(bcc, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LOCAL_PRIORITY] =
+ __ATTR(local_priority, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_ABORT] =
+ __ATTR(abort, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LOOPBACK] =
+ __ATTR(loopback, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_SMP] =
+ __ATTR(smp, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BOFF_REC] =
+ __ATTR(boff_rec, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_TSYN] =
+ __ATTR(tsyn, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LISTEN] =
+ __ATTR(listen, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_EXTEND_MSG] =
+ __ATTR(ext_msg, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_STANDARD_MSG] =
+ __ATTR(std_msg, 0644, flexcan_show_attr, flexcan_set_attr),
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ [FLEXCAN_ATTR_DUMP_REG] =
+ __ATTR(dump_reg, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_DUMP_XMIT_MB] =
+ __ATTR(dump_xmit_mb, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_DUMP_RX_MB] =
+ __ATTR(dump_rx_mb, 0444, flexcan_show_attr, NULL),
+#endif
+};
+
+static void flexcan_set_bitrate(struct flexcan_device *flexcan, int bitrate)
+{
+ /* TODO:: implement in future
+ * based on the bitrate to get the timing of
+ * presdiv, pseg1, pseg2, propseg
+ */
+}
+
+static void flexcan_update_bitrate(struct flexcan_device *flexcan)
+{
+ int rate, div;
+
+ if (flexcan->br_clksrc)
+ rate = clk_get_rate(flexcan->clk);
+ else {
+ struct clk *clk;
+ clk = clk_get(NULL, "ckih");
+ if (!clk)
+ return;
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+ }
+ if (!rate)
+ return;
+
+ div = (flexcan->br_presdiv + 1);
+ div *=
+ (flexcan->br_propseg + flexcan->br_pseg1 + flexcan->br_pseg2 + 4);
+ flexcan->bitrate = (rate + div - 1) / div;
+}
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int flexcan_dump_reg(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0;
+ unsigned int reg;
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ ret += sprintf(buf + ret, "MCR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ ret += sprintf(buf + ret, "CTRL::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RXGMASK);
+ ret += sprintf(buf + ret, "RXGMASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX14MASK);
+ ret += sprintf(buf + ret, "RX14MASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX15MASK);
+ ret += sprintf(buf + ret, "RX15MASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ ret += sprintf(buf + ret, "ECR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ ret += sprintf(buf + ret, "ESR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK2);
+ ret += sprintf(buf + ret, "IMASK2::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ ret += sprintf(buf + ret, "IMASK1::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG2);
+ ret += sprintf(buf + ret, "IFLAG2::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1);
+ ret += sprintf(buf + ret, "IFLAG1::0x%x\n", reg);
+ return ret;
+}
+
+static int flexcan_dump_xmit_mb(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0, i;
+ i = flexcan->xmit_maxmb + 1;
+ for (; i <= flexcan->maxmb; i++)
+ ret +=
+ sprintf(buf + ret,
+ "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
+ i, flexcan->hwmb[i].mb_cs.data,
+ flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
+ flexcan->hwmb[i].mb_data[2]);
+ return ret;
+}
+
+static int flexcan_dump_rx_mb(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0, i;
+ for (i = 0; i <= flexcan->xmit_maxmb; i++)
+ ret +=
+ sprintf(buf + ret,
+ "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
+ i, flexcan->hwmb[i].mb_cs.data,
+ flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
+ flexcan->hwmb[i].mb_data[2]);
+ return ret;
+}
+#endif
+
+static ssize_t flexcan_show_state(struct net_device *net, char *buf)
+{
+ int ret, esr;
+ struct flexcan_device *flexcan = netdev_priv(net);
+ ret = sprintf(buf, "%s::", netif_running(net) ? "Start" : "Stop");
+ if (netif_carrier_ok(net)) {
+ esr = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ switch ((esr & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) {
+ case 0:
+ ret += sprintf(buf + ret, "normal\n");
+ break;
+ case 1:
+ ret += sprintf(buf + ret, "error passive\n");
+ break;
+ default:
+ ret += sprintf(buf + ret, "bus off\n");
+ }
+ } else
+ ret += sprintf(buf + ret, "bus off\n");
+ return ret;
+}
+
+static ssize_t flexcan_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int attr_id;
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+
+ net = dev_get_drvdata(dev);
+ BUG_ON(!net);
+ flexcan = netdev_priv(net);
+ BUG_ON(!flexcan);
+
+ attr_id = attr - flexcan_dev_attr;
+ switch (attr_id) {
+ case FLEXCAN_ATTR_STATE:
+ return flexcan_show_state(net, buf);
+ case FLEXCAN_ATTR_BITRATE:
+ return sprintf(buf, "%d\n", flexcan->bitrate);
+ case FLEXCAN_ATTR_BR_PRESDIV:
+ return sprintf(buf, "%d\n", flexcan->br_presdiv + 1);
+ case FLEXCAN_ATTR_BR_RJW:
+ return sprintf(buf, "%d\n", flexcan->br_rjw);
+ case FLEXCAN_ATTR_BR_PROPSEG:
+ return sprintf(buf, "%d\n", flexcan->br_propseg + 1);
+ case FLEXCAN_ATTR_BR_PSEG1:
+ return sprintf(buf, "%d\n", flexcan->br_pseg1 + 1);
+ case FLEXCAN_ATTR_BR_PSEG2:
+ return sprintf(buf, "%d\n", flexcan->br_pseg2 + 1);
+ case FLEXCAN_ATTR_BR_CLKSRC:
+ return sprintf(buf, "%s\n", flexcan->br_clksrc ? "bus" : "osc");
+ case FLEXCAN_ATTR_MAXMB:
+ return sprintf(buf, "%d\n", flexcan->maxmb + 1);
+ case FLEXCAN_ATTR_XMIT_MAXMB:
+ return sprintf(buf, "%d\n", flexcan->xmit_maxmb + 1);
+ case FLEXCAN_ATTR_FIFO:
+ return sprintf(buf, "%d\n", flexcan->fifo);
+ case FLEXCAN_ATTR_WAKEUP:
+ return sprintf(buf, "%d\n", flexcan->wakeup);
+ case FLEXCAN_ATTR_SRX_DIS:
+ return sprintf(buf, "%d\n", flexcan->srx_dis);
+ case FLEXCAN_ATTR_WAK_SRC:
+ return sprintf(buf, "%d\n", flexcan->wak_src);
+ case FLEXCAN_ATTR_BCC:
+ return sprintf(buf, "%d\n", flexcan->bcc);
+ case FLEXCAN_ATTR_LOCAL_PRIORITY:
+ return sprintf(buf, "%d\n", flexcan->lprio);
+ case FLEXCAN_ATTR_ABORT:
+ return sprintf(buf, "%d\n", flexcan->abort);
+ case FLEXCAN_ATTR_LOOPBACK:
+ return sprintf(buf, "%d\n", flexcan->loopback);
+ case FLEXCAN_ATTR_SMP:
+ return sprintf(buf, "%d\n", flexcan->smp);
+ case FLEXCAN_ATTR_BOFF_REC:
+ return sprintf(buf, "%d\n", flexcan->boff_rec);
+ case FLEXCAN_ATTR_TSYN:
+ return sprintf(buf, "%d\n", flexcan->tsyn);
+ case FLEXCAN_ATTR_LISTEN:
+ return sprintf(buf, "%d\n", flexcan->listen);
+ case FLEXCAN_ATTR_EXTEND_MSG:
+ return sprintf(buf, "%d\n", flexcan->ext_msg);
+ case FLEXCAN_ATTR_STANDARD_MSG:
+ return sprintf(buf, "%d\n", flexcan->std_msg);
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ case FLEXCAN_ATTR_DUMP_REG:
+ return flexcan_dump_reg(flexcan, buf);
+ case FLEXCAN_ATTR_DUMP_XMIT_MB:
+ return flexcan_dump_xmit_mb(flexcan, buf);
+ case FLEXCAN_ATTR_DUMP_RX_MB:
+ return flexcan_dump_rx_mb(flexcan, buf);
+#endif
+ default:
+ return sprintf(buf, "%s:%p->%p\n", __func__, flexcan_dev_attr,
+ attr);
+ }
+}
+
+static ssize_t flexcan_set_attr(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int attr_id, tmp;
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+
+ net = dev_get_drvdata(dev);
+ BUG_ON(!net);
+ flexcan = netdev_priv(net);
+ BUG_ON(!flexcan);
+
+ attr_id = attr - flexcan_dev_attr;
+
+ if (mutex_lock_interruptible(&flexcan->mutex))
+ return count;
+
+ if (netif_running(net))
+ goto set_finish;
+
+ if (attr_id == FLEXCAN_ATTR_BR_CLKSRC) {
+ if (!strcasecmp(buf, "bus"))
+ flexcan->br_clksrc = 1;
+ else if (!strcasecmp(buf, "osc"))
+ flexcan->br_clksrc = 0;
+ goto set_finish;
+ }
+
+ tmp = simple_strtoul(buf, NULL, 0);
+ switch (attr_id) {
+ case FLEXCAN_ATTR_BITRATE:
+ flexcan_set_bitrate(flexcan, tmp);
+ break;
+ case FLEXCAN_ATTR_BR_PRESDIV:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PRESDIV)) {
+ flexcan->br_presdiv = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_RJW:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_RJW))
+ flexcan->br_rjw = tmp - 1;
+ break;
+ case FLEXCAN_ATTR_BR_PROPSEG:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PROPSEG)) {
+ flexcan->br_propseg = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_PSEG1:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG1)) {
+ flexcan->br_pseg1 = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_PSEG2:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG2)) {
+ flexcan->br_pseg2 = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_MAXMB:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_MB)) {
+ if (flexcan->maxmb != (tmp - 1)) {
+ flexcan->maxmb = tmp - 1;
+ if (flexcan->xmit_maxmb < flexcan->maxmb)
+ flexcan->xmit_maxmb = flexcan->maxmb;
+ }
+ }
+ break;
+ case FLEXCAN_ATTR_XMIT_MAXMB:
+ if ((tmp > 0) && (tmp <= (flexcan->maxmb + 1))) {
+ if (flexcan->xmit_maxmb != (tmp - 1))
+ flexcan->xmit_maxmb = tmp - 1;
+ }
+ break;
+ case FLEXCAN_ATTR_FIFO:
+ flexcan->fifo = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_WAKEUP:
+ flexcan->wakeup = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_SRX_DIS:
+ flexcan->srx_dis = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_WAK_SRC:
+ flexcan->wak_src = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_BCC:
+ flexcan->bcc = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LOCAL_PRIORITY:
+ flexcan->lprio = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_ABORT:
+ flexcan->abort = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LOOPBACK:
+ flexcan->loopback = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_SMP:
+ flexcan->smp = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_BOFF_REC:
+ flexcan->boff_rec = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_TSYN:
+ flexcan->tsyn = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LISTEN:
+ flexcan->listen = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_EXTEND_MSG:
+ flexcan->ext_msg = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_STANDARD_MSG:
+ flexcan->std_msg = tmp ? 1 : 0;
+ break;
+ }
+ set_finish:
+ mutex_unlock(&flexcan->mutex);
+ return count;
+}
+
+static void flexcan_device_default(struct flexcan_device *dev)
+{
+ dev->br_clksrc = 1;
+ dev->br_rjw = 2;
+ dev->br_presdiv = 6;
+ dev->br_propseg = 4;
+ dev->br_pseg1 = 4;
+ dev->br_pseg2 = 7;
+
+ dev->bcc = 1;
+ dev->srx_dis = 1;
+ dev->smp = 1;
+ dev->boff_rec = 1;
+
+ dev->maxmb = FLEXCAN_MAX_MB - 1;
+ dev->xmit_maxmb = (FLEXCAN_MAX_MB >> 1) - 1;
+ dev->xmit_mb = dev->maxmb - dev->xmit_maxmb;
+
+ dev->ext_msg = 1;
+ dev->std_msg = 1;
+}
+
+static int flexcan_device_attach(struct flexcan_device *flexcan)
+{
+ int ret;
+ struct resource *res;
+ struct platform_device *pdev = flexcan->dev;
+ struct flexcan_platform_data *plat_data = (pdev->dev).platform_data;
+
+ res = platform_get_resource(flexcan->dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ flexcan->io_base = ioremap(res->start, res->end - res->start + 1);
+ if (!flexcan->io_base)
+ return -ENOMEM;
+
+ flexcan->irq = platform_get_irq(flexcan->dev, 0);
+ if (!flexcan->irq) {
+ ret = -ENODEV;
+ goto no_irq_err;
+ }
+
+ ret = -EINVAL;
+ if (plat_data) {
+ if (plat_data->core_reg) {
+ flexcan->core_reg = regulator_get(&pdev->dev,
+ plat_data->core_reg);
+ if (!flexcan->core_reg)
+ goto plat_err;
+ }
+
+ if (plat_data->io_reg) {
+ flexcan->io_reg = regulator_get(&pdev->dev,
+ plat_data->io_reg);
+ if (!flexcan->io_reg)
+ goto plat_err;
+ }
+ }
+ flexcan->clk = clk_get(&(flexcan->dev)->dev, "can_clk");
+ flexcan->hwmb = (struct can_hw_mb *)(flexcan->io_base + CAN_MB_BASE);
+ flexcan->rx_mask = (unsigned int *)(flexcan->io_base + CAN_RXMASK_BASE);
+
+ return 0;
+ plat_err:
+ if (flexcan->core_reg) {
+ regulator_put(flexcan->core_reg);
+ flexcan->core_reg = NULL;
+ }
+ no_irq_err:
+ if (flexcan->io_base)
+ iounmap(flexcan->io_base);
+ return ret;
+}
+
+static void flexcan_device_detach(struct flexcan_device *flexcan)
+{
+ struct platform_device *pdev = flexcan->dev;
+ if (flexcan->clk) {
+ clk_put(flexcan->clk);
+ flexcan->clk = NULL;
+ }
+
+ if (flexcan->io_reg) {
+ regulator_put(flexcan->io_reg);
+ flexcan->io_reg = NULL;
+ }
+
+ if (flexcan->core_reg) {
+ regulator_put(flexcan->core_reg);
+ flexcan->core_reg = NULL;
+ }
+
+ if (flexcan->io_base)
+ iounmap(flexcan->io_base);
+}
+
+/*!
+ * @brief The function allocates can device.
+ *
+ * @param pdev the pointer of platform device.
+ * @param setup the initial function pointer of network device.
+ *
+ * @return none
+ */
+struct net_device *flexcan_device_alloc(struct platform_device *pdev,
+ void (*setup) (struct net_device *dev))
+{
+ struct flexcan_device *flexcan;
+ struct net_device *net;
+ int i, num;
+
+ net = alloc_netdev(sizeof(*flexcan), "can%d", setup);
+ if (net == NULL) {
+ printk(KERN_ERR "Allocate netdevice for FlexCAN fail!\n");
+ return NULL;
+ }
+ flexcan = netdev_priv(net);
+ memset(flexcan, 0, sizeof(*flexcan));
+
+ mutex_init(&flexcan->mutex);
+ init_timer(&flexcan->timer);
+
+ flexcan->dev = pdev;
+ if (flexcan_device_attach(flexcan)) {
+ printk(KERN_ERR "Attach FlexCAN fail!\n");
+ free_netdev(net);
+ return NULL;
+ }
+ flexcan_device_default(flexcan);
+ flexcan_update_bitrate(flexcan);
+
+ num = ARRAY_SIZE(flexcan_dev_attr);
+
+ for (i = 0; i < num; i++) {
+ if (device_create_file(&pdev->dev, flexcan_dev_attr + i)) {
+ printk(KERN_ERR "Create attribute file fail!\n");
+ break;
+ }
+ }
+
+ if (i != num) {
+ for (; i >= 0; i--)
+ device_remove_file(&pdev->dev, flexcan_dev_attr + i);
+ free_netdev(net);
+ return NULL;
+ }
+ dev_set_drvdata(&pdev->dev, net);
+ return net;
+}
+
+/*!
+ * @brief The function frees can device.
+ *
+ * @param pdev the pointer of platform device.
+ *
+ * @return none
+ */
+void flexcan_device_free(struct platform_device *pdev)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ int i, num;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+
+ unregister_netdev(net);
+ flexcan = netdev_priv(net);
+ del_timer(&flexcan->timer);
+
+ num = ARRAY_SIZE(flexcan_dev_attr);
+
+ for (i = 0; i < num; i++)
+ device_remove_file(&pdev->dev, flexcan_dev_attr + i);
+
+ flexcan_device_detach(netdev_priv(net));
+ free_netdev(net);
+}
diff --git a/drivers/net/can/flexcan/drv.c b/drivers/net/can/flexcan/drv.c
new file mode 100644
index 000000000000..1eaeac50d6ee
--- /dev/null
+++ b/drivers/net/can/flexcan/drv.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file drv.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include "flexcan.h"
+
+static void flexcan_hw_start(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ if ((flexcan->maxmb + 1) > 32) {
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IMASK1);
+ reg = (1 << (flexcan->maxmb - 31)) - 1;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_IMASK2);
+ } else {
+ reg = (1 << (flexcan->maxmb + 1)) - 1;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_IMASK1);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ }
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR) & (~__MCR_HALT);
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static void flexcan_hw_stop(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_HALT, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static int flexcan_hw_reset(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ int timeout = 100000;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_MDIS, flexcan->io_base + CAN_HW_REG_MCR);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ if (flexcan->br_clksrc)
+ reg |= __CTRL_CLK_SRC;
+ else
+ reg &= ~__CTRL_CLK_SRC;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_CTRL);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR) & (~__MCR_MDIS);
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+ reg |= __MCR_SOFT_RST;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ while (reg & __MCR_SOFT_RST) {
+ if (--timeout <= 0) {
+ printk(KERN_ERR "Flexcan software Reset Timeouted\n");
+ return -1;
+ }
+ udelay(10);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ }
+ return 0;
+}
+
+static inline void flexcan_mcr_setup(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ reg &= ~(__MCR_MAX_MB_MASK | __MCR_WAK_MSK | __MCR_MAX_IDAM_MASK);
+
+ if (flexcan->fifo)
+ reg |= __MCR_FEN;
+ else
+ reg &= ~__MCR_FEN;
+
+ if (flexcan->wakeup)
+ reg |= __MCR_SLF_WAK | __MCR_WAK_MSK;
+ else
+ reg &= ~(__MCR_SLF_WAK | __MCR_WAK_MSK);
+
+ if (flexcan->wak_src)
+ reg |= __MCR_WAK_SRC;
+ else
+ reg &= ~__MCR_WAK_SRC;
+
+ if (flexcan->srx_dis)
+ reg |= __MCR_SRX_DIS;
+ else
+ reg &= ~__MCR_SRX_DIS;
+
+ if (flexcan->bcc)
+ reg |= __MCR_BCC;
+ else
+ reg &= ~__MCR_BCC;
+
+ if (flexcan->lprio)
+ reg |= __MCR_LPRIO_EN;
+ else
+ reg &= ~__MCR_LPRIO_EN;
+
+ if (flexcan->abort)
+ reg |= __MCR_AEN;
+ else
+ reg &= ~__MCR_AEN;
+
+ reg |= (flexcan->maxmb << __MCR_MAX_MB_OFFSET);
+ reg |= __MCR_DOZE | __MCR_MAX_IDAM_C;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static inline void flexcan_ctrl_setup(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ reg &= ~(__CTRL_PRESDIV_MASK | __CTRL_RJW_MASK | __CTRL_PSEG1_MASK |
+ __CTRL_PSEG2_MASK | __CTRL_PROPSEG_MASK);
+
+ if (flexcan->loopback)
+ reg |= __CTRL_LPB;
+ else
+ reg &= ~__CTRL_LPB;
+
+ if (flexcan->smp)
+ reg |= __CTRL_SMP;
+ else
+ reg &= ~__CTRL_SMP;
+
+ if (flexcan->boff_rec)
+ reg |= __CTRL_BOFF_REC;
+ else
+ reg &= ~__CTRL_BOFF_REC;
+
+ if (flexcan->tsyn)
+ reg |= __CTRL_TSYN;
+ else
+ reg &= ~__CTRL_TSYN;
+
+ if (flexcan->listen)
+ reg |= __CTRL_LOM;
+ else
+ reg &= ~__CTRL_LOM;
+
+ reg |= (flexcan->br_presdiv << __CTRL_PRESDIV_OFFSET) |
+ (flexcan->br_rjw << __CTRL_RJW_OFFSET) |
+ (flexcan->br_pseg1 << __CTRL_PSEG1_OFFSET) |
+ (flexcan->br_pseg2 << __CTRL_PSEG2_OFFSET) |
+ (flexcan->br_propseg << __CTRL_PROPSEG_OFFSET);
+
+ reg &= ~__CTRL_LBUF;
+
+ reg |= __CTRL_TWRN_MSK | __CTRL_RWRN_MSK | __CTRL_BOFF_MSK |
+ __CTRL_ERR_MSK;
+
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_CTRL);
+}
+
+static int flexcan_hw_restart(struct net_device *dev)
+{
+ unsigned int reg;
+ struct flexcan_device *flexcan = netdev_priv(dev);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ if (reg & __MCR_SOFT_RST)
+ return 1;
+
+ flexcan_mcr_setup(flexcan);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK1);
+
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG2);
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG1);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_ECR);
+
+ flexcan_mbm_init(flexcan);
+ netif_carrier_on(dev);
+ flexcan_hw_start(flexcan);
+
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void flexcan_hw_watch(unsigned long data)
+{
+ unsigned int reg, ecr;
+ struct net_device *dev = (struct net_device *)data;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+
+ BUG_ON(!flexcan);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ if (reg & __MCR_MDIS) {
+ if (flexcan_hw_restart(dev))
+ mod_timer(&flexcan->timer, HZ / 20);
+ return;
+ }
+ ecr = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ if (flexcan->boff_rec) {
+ if (((reg & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) > 1) {
+ reg |= __MCR_SOFT_RST;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+ mod_timer(&flexcan->timer, HZ / 20);
+ return;
+ }
+ netif_carrier_on(dev);
+ }
+}
+
+static void flexcan_hw_busoff(struct net_device *dev)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ unsigned int reg;
+
+ netif_carrier_off(dev);
+
+ flexcan->timer.function = flexcan_hw_watch;
+ flexcan->timer.data = (unsigned long)dev;
+
+ if (flexcan->boff_rec) {
+ mod_timer(&flexcan->timer, HZ / 10);
+ return;
+ }
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_SOFT_RST, flexcan->io_base + CAN_HW_REG_MCR);
+ mod_timer(&flexcan->timer, HZ / 20);
+}
+
+static int flexcan_hw_open(struct flexcan_device *flexcan)
+{
+ if (flexcan_hw_reset(flexcan))
+ return -EFAULT;
+
+ flexcan_mcr_setup(flexcan);
+ flexcan_ctrl_setup(flexcan);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK1);
+
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG2);
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG1);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_ECR);
+ return 0;
+}
+
+static void flexcan_err_handler(struct net_device *dev)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ unsigned int esr, ecr;
+
+ esr = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ __raw_writel(esr & __ESR_INTERRUPTS, flexcan->io_base + CAN_HW_REG_ESR);
+
+ if (esr & __ESR_WAK_INT)
+ return;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ printk(KERN_ERR "%s: allocates skb fail in\n", __func__);
+ return;
+ }
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ if (esr & __ESR_TWRN_INT)
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+
+ if (esr & __ESR_RWRN_INT)
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+
+ if (esr & __ESR_BOFF_INT)
+ frame->can_id |= CAN_ERR_BUSOFF;
+
+ if (esr & __ESR_ERR_INT) {
+ if (esr & __ESR_BIT1_ERR)
+ frame->data[2] |= CAN_ERR_PROT_BIT1;
+
+ if (esr & __ESR_BIT0_ERR)
+ frame->data[2] |= CAN_ERR_PROT_BIT0;
+
+ if (esr & __ESR_ACK_ERR)
+ frame->can_id |= CAN_ERR_ACK;
+
+ /*TODO:// if (esr & __ESR_CRC_ERR) */
+
+ if (esr & __ESR_FRM_ERR)
+ frame->data[2] |= CAN_ERR_PROT_FORM;
+
+ if (esr & __ESR_STF_ERR)
+ frame->data[2] |= CAN_ERR_PROT_STUFF;
+
+ ecr = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ switch ((esr & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) {
+ case 0:
+ if (__ECR_TX_ERR_COUNTER(ecr) >= __ECR_ACTIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ if (__ECR_RX_ERR_COUNTER(ecr) >= __ECR_ACTIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ break;
+ case 1:
+ if (__ECR_TX_ERR_COUNTER(ecr) >=
+ __ECR_PASSIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+
+ if (__ECR_RX_ERR_COUNTER(ecr) >=
+ __ECR_PASSIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ default:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ }
+ }
+
+ if (frame->can_id & CAN_ERR_BUSOFF)
+ flexcan_hw_busoff(dev);
+
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+}
+
+static irqreturn_t flexcan_irq_handler(int irq, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+ unsigned int reg;
+
+ BUG_ON(!flexcan);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ if (reg & __ESR_INTERRUPTS) {
+ flexcan_err_handler(dev);
+ return IRQ_HANDLED;
+ }
+
+ flexcan_mbm_isr(dev);
+ return IRQ_HANDLED;
+}
+
+static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+
+ BUG_ON(!flexcan);
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ if (!flexcan_mbm_xmit(flexcan, frame)) {
+ dev_kfree_skb(skb);
+ stats->tx_bytes += frame->can_dlc;
+ stats->tx_packets++;
+ dev->trans_start = jiffies;
+ return NETDEV_TX_OK;
+ }
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+}
+
+static int flexcan_open(struct net_device *dev)
+{
+ struct flexcan_device *flexcan;
+ struct platform_device *pdev;
+ struct flexcan_platform_data *plat_data;
+
+ flexcan = netdev_priv(dev);
+ BUG_ON(!flexcan);
+
+ pdev = flexcan->dev;
+ plat_data = (pdev->dev).platform_data;
+ if (plat_data && plat_data->active)
+ plat_data->active(pdev->id);
+
+ if (flexcan->clk)
+ if (clk_enable(flexcan->clk))
+ goto clk_err;
+
+ if (flexcan->core_reg)
+ if (regulator_enable(flexcan->core_reg))
+ goto core_reg_err;
+
+ if (flexcan->io_reg)
+ if (regulator_enable(flexcan->io_reg))
+ goto io_reg_err;
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 1);
+
+ if (request_irq(flexcan->irq, flexcan_irq_handler, IRQF_SAMPLE_RANDOM,
+ dev->name, dev))
+ goto irq_err;
+
+ if (flexcan_hw_open(flexcan))
+ goto open_err;
+
+ flexcan_mbm_init(flexcan);
+ netif_carrier_on(dev);
+ flexcan_hw_start(flexcan);
+ return 0;
+ open_err:
+ free_irq(flexcan->irq, dev);
+ irq_err:
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ io_reg_err:
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ core_reg_err:
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ clk_err:
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ return -ENODEV;
+}
+
+static int flexcan_stop(struct net_device *dev)
+{
+ struct flexcan_device *flexcan;
+ struct platform_device *pdev;
+ struct flexcan_platform_data *plat_data;
+
+ flexcan = netdev_priv(dev);
+
+ BUG_ON(!flexcan);
+
+ pdev = flexcan->dev;
+ plat_data = (pdev->dev).platform_data;
+
+ flexcan_hw_stop(flexcan);
+
+ free_irq(flexcan->irq, dev);
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ return 0;
+}
+
+static void flexcan_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = FLEXCAN_MAX_MB;
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = flexcan_open;
+ dev->stop = flexcan_stop;
+ dev->hard_start_xmit = flexcan_start_xmit;
+}
+
+static int flexcan_probe(struct platform_device *pdev)
+{
+ struct net_device *net;
+ net = flexcan_device_alloc(pdev, flexcan_setup);
+ if (!net)
+ return -ENOMEM;
+
+ if (register_netdev(net)) {
+ flexcan_device_free(pdev);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int flexcan_remove(struct platform_device *pdev)
+{
+ flexcan_device_free(pdev);
+ return 0;
+}
+
+static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ struct flexcan_platform_data *plat_data;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+ flexcan = netdev_priv(net);
+
+ BUG_ON(!flexcan);
+
+ if (!(net->flags & IFF_UP))
+ return 0;
+ if (flexcan->wakeup)
+ set_irq_wake(flexcan->irq, 1);
+ else {
+ plat_data = (pdev->dev).platform_data;
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ }
+ return 0;
+}
+
+static int flexcan_resume(struct platform_device *pdev)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ struct flexcan_platform_data *plat_data;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+ flexcan = netdev_priv(net);
+
+ BUG_ON(!flexcan);
+
+ if (!(net->flags & IFF_UP))
+ return 0;
+
+ if (flexcan->wakeup)
+ set_irq_wake(flexcan->irq, 0);
+ else {
+ plat_data = (pdev->dev).platform_data;
+ if (plat_data && plat_data->active)
+ plat_data->active(pdev->id);
+
+ if (flexcan->clk) {
+ if (clk_enable(flexcan->clk))
+ printk(KERN_ERR "%s:enable clock fail\n",
+ __func__);
+ }
+
+ if (flexcan->core_reg) {
+ if (regulator_enable(flexcan->core_reg))
+ printk(KERN_ERR "%s:enable core voltage\n",
+ __func__);
+ }
+ if (flexcan->io_reg) {
+ if (regulator_enable(flexcan->io_reg))
+ printk(KERN_ERR "%s:enable io voltage\n",
+ __func__);
+ }
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 1);
+ }
+ return 0;
+}
+
+static struct platform_driver flexcan_driver = {
+ .driver = {
+ .name = FLEXCAN_DEVICE_NAME,
+ },
+ .probe = flexcan_probe,
+ .remove = flexcan_remove,
+ .suspend = flexcan_suspend,
+ .resume = flexcan_resume,
+};
+
+static __init int flexcan_init(void)
+{
+ pr_info("Freescale FlexCAN Driver \n");
+ return platform_driver_register(&flexcan_driver);
+}
+
+static __exit void flexcan_exit(void)
+{
+ return platform_driver_unregister(&flexcan_driver);
+}
+
+module_init(flexcan_init);
+module_exit(flexcan_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
new file mode 100644
index 000000000000..d19cc1ee0620
--- /dev/null
+++ b/drivers/net/can/flexcan/flexcan.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file flexcan.h
+ *
+ * @brief FlexCan definitions.
+ *
+ * @ingroup can
+ */
+
+#ifndef __CAN_FLEXCAN_H__
+#define __CAN_FLEXCAN_H__
+
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/error.h>
+
+#define FLEXCAN_DEVICE_NAME "FlexCAN"
+
+struct can_mb_cs {
+ unsigned int time_stamp:16;
+ unsigned int length:4;
+ unsigned int rtr:1;
+ unsigned int ide:1;
+ unsigned int srr:1;
+ unsigned int nouse1:1;
+ unsigned int code:4;
+ unsigned int nouse2:4;
+};
+
+#define CAN_MB_RX_INACTIVE 0x0
+#define CAN_MB_RX_EMPTY 0x4
+#define CAN_MB_RX_FULL 0x2
+#define CAN_MB_RX_OVERRUN 0x6
+#define CAN_MB_RX_BUSY 0x1
+
+#define CAN_MB_TX_INACTIVE 0x8
+#define CAN_MB_TX_ABORT 0x9
+#define CAN_MB_TX_ONCE 0xC
+#define CAN_MB_TX_REMOTE 0xA
+
+struct can_hw_mb {
+ union {
+ struct can_mb_cs cs;
+ unsigned int data;
+ } mb_cs;
+ unsigned int mb_id;
+ unsigned char mb_data[8];
+};
+
+#define CAN_HW_REG_MCR 0x00
+#define CAN_HW_REG_CTRL 0x04
+#define CAN_HW_REG_TIMER 0x08
+#define CAN_HW_REG_RXGMASK 0x10
+#define CAN_HW_REG_RX14MASK 0x14
+#define CAN_HW_REG_RX15MASK 0x18
+#define CAN_HW_REG_ECR 0x1C
+#define CAN_HW_REG_ESR 0x20
+#define CAN_HW_REG_IMASK2 0x24
+#define CAN_HW_REG_IMASK1 0x28
+#define CAN_HW_REG_IFLAG2 0x2C
+#define CAN_HW_REG_IFLAG1 0x30
+
+#define CAN_MB_BASE 0x0080
+#define CAN_RXMASK_BASE 0x0880
+#define CAN_FIFO_BASE 0xE0
+
+#define __MCR_MDIS (1 << 31)
+#define __MCR_FRZ (1 << 30)
+#define __MCR_FEN (1 << 29)
+#define __MCR_HALT (1 << 28)
+#define __MCR_NOTRDY (1 << 27)
+#define __MCR_WAK_MSK (1 << 26)
+#define __MCR_SOFT_RST (1 << 25)
+#define __MCR_FRZ_ACK (1 << 24)
+#define __MCR_SLF_WAK (1 << 22)
+#define __MCR_WRN_EN (1 << 21)
+#define __MCR_LPM_ACK (1 << 20)
+#define __MCR_WAK_SRC (1 << 19)
+#define __MCR_DOZE (1 << 18)
+#define __MCR_SRX_DIS (1 << 17)
+#define __MCR_BCC (1 << 16)
+#define __MCR_LPRIO_EN (1 << 13)
+#define __MCR_AEN (1 << 12)
+#define __MCR_MAX_IDAM_OFFSET 8
+#define __MCR_MAX_IDAM_MASK (0x3 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_A (0x0 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_B (0x1 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_C (0x2 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_D (0x3 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_MB_OFFSET 0
+#define __MCR_MAX_MB_MASK (0x3F)
+
+#define __CTRL_PRESDIV_OFFSET 24
+#define __CTRL_PRESDIV_MASK (0xFF << __CTRL_PRESDIV_OFFSET)
+#define __CTRL_RJW_OFFSET 22
+#define __CTRL_RJW_MASK (0x3 << __CTRL_RJW_OFFSET)
+#define __CTRL_PSEG1_OFFSET 19
+#define __CTRL_PSEG1_MASK (0x7 << __CTRL_PSEG1_OFFSET)
+#define __CTRL_PSEG2_OFFSET 16
+#define __CTRL_PSEG2_MASK (0x7 << __CTRL_PSEG2_OFFSET)
+#define __CTRL_BOFF_MSK (0x1 << 15)
+#define __CTRL_ERR_MSK (0x1 << 14)
+#define __CTRL_CLK_SRC (0x1 << 13)
+#define __CTRL_LPB (0x1 << 12)
+#define __CTRL_TWRN_MSK (0x1 << 11)
+#define __CTRL_RWRN_MSK (0x1 << 10)
+#define __CTRL_SMP (0x1 << 7)
+#define __CTRL_BOFF_REC (0x1 << 6)
+#define __CTRL_TSYN (0x1 << 5)
+#define __CTRL_LBUF (0x1 << 4)
+#define __CTRL_LOM (0x1 << 3)
+#define __CTRL_PROPSEG_OFFSET 0
+#define __CTRL_PROPSEG_MASK (0x7)
+
+#define __ECR_TX_ERR_COUNTER(x) ((x) & 0xFF)
+#define __ECR_RX_ERR_COUNTER(x) (((x) >> 8) & 0xFF)
+#define __ECR_PASSIVE_THRESHOLD 128
+#define __ECR_ACTIVE_THRESHOLD 96
+
+#define __ESR_TWRN_INT (0x1 << 17)
+#define __ESR_RWRN_INT (0x1 << 16)
+#define __ESR_BIT1_ERR (0x1 << 15)
+#define __ESR_BIT0_ERR (0x1 << 14)
+#define __ESR_ACK_ERR (0x1 << 13)
+#define __ESR_CRC_ERR (0x1 << 12)
+#define __ESR_FRM_ERR (0x1 << 11)
+#define __ESR_STF_ERR (0x1 << 10)
+#define __ESR_TX_WRN (0x1 << 9)
+#define __ESR_RX_WRN (0x1 << 8)
+#define __ESR_IDLE (0x1 << 7)
+#define __ESR_TXRX (0x1 << 6)
+#define __ESR_FLT_CONF_OFF 4
+#define __ESR_FLT_CONF_MASK (0x3 << __ESR_FLT_CONF_OFF)
+#define __ESR_BOFF_INT (0x1 << 2)
+#define __ESR_ERR_INT (0x1 << 1)
+#define __ESR_WAK_INT (0x1)
+
+#define __ESR_INTERRUPTS (__ESR_WAK_INT | __ESR_ERR_INT | \
+ __ESR_BOFF_INT | __ESR_TWRN_INT | \
+ __ESR_RWRN_INT)
+
+#define __FIFO_OV_INT 0x0080
+#define __FIFO_WARN_INT 0x0040
+#define __FIFO_RDY_INT 0x0020
+
+struct flexcan_device {
+ struct mutex mutex;
+ void *io_base;
+ struct can_hw_mb *hwmb;
+ unsigned int *rx_mask;
+ unsigned int xmit_mb;
+ unsigned int bitrate;
+ /* word 1 */
+ unsigned int br_presdiv:8;
+ unsigned int br_rjw:2;
+ unsigned int br_propseg:3;
+ unsigned int br_pseg1:3;
+ unsigned int br_pseg2:3;
+ unsigned int maxmb:6;
+ unsigned int xmit_maxmb:6;
+ unsigned int wd1_resv:1;
+
+ /* word 2 */
+ unsigned int fifo:1;
+ unsigned int wakeup:1;
+ unsigned int srx_dis:1;
+ unsigned int wak_src:1;
+ unsigned int bcc:1;
+ unsigned int lprio:1;
+ unsigned int abort:1;
+ unsigned int br_clksrc:1;
+ unsigned int loopback:1;
+ unsigned int smp:1;
+ unsigned int boff_rec:1;
+ unsigned int tsyn:1;
+ unsigned int listen:1;
+
+ unsigned int ext_msg:1;
+ unsigned int std_msg:1;
+
+ struct timer_list timer;
+ struct platform_device *dev;
+ struct regulator *core_reg;
+ struct regulator *io_reg;
+ struct clk *clk;
+ int irq;
+};
+
+#define FLEXCAN_MAX_FIFO_MB 8
+#define FLEXCAN_MAX_MB 64
+#define FLEXCAN_MAX_PRESDIV 256
+#define FLEXCAN_MAX_RJW 4
+#define FLEXCAN_MAX_PSEG1 8
+#define FLEXCAN_MAX_PSEG2 8
+#define FLEXCAN_MAX_PROPSEG 8
+#define FLEXCAN_MAX_BITRATE 1000000
+
+extern struct net_device *flexcan_device_alloc(struct platform_device *pdev,
+ void (*setup) (struct net_device
+ *dev));
+extern void flexcan_device_free(struct platform_device *pdev);
+
+extern void flexcan_mbm_init(struct flexcan_device *flexcan);
+extern void flexcan_mbm_isr(struct net_device *dev);
+extern int flexcan_mbm_xmit(struct flexcan_device *flexcan,
+ struct can_frame *frame);
+#endif /* __CAN_FLEXCAN_H__ */
diff --git a/drivers/net/can/flexcan/mbm.c b/drivers/net/can/flexcan/mbm.c
new file mode 100644
index 000000000000..ca1300503e02
--- /dev/null
+++ b/drivers/net/can/flexcan/mbm.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mbm.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "flexcan.h"
+
+#define flexcan_swab32(x) \
+ (((x) << 24) | ((x) >> 24) |\
+ (((x) & (__u32)0x0000ff00UL) << 8) |\
+ (((x) & (__u32)0x00ff0000UL) >> 8))
+
+static inline void flexcan_memcpy(void *dst, void *src, int len)
+{
+ int i;
+ unsigned int *d = (unsigned int *)dst, *s = (unsigned int *)src;
+ len = (len + 3) >> 2;
+ for (i = 0; i < len; i++, s++, d++)
+ *d = flexcan_swab32(*s);
+}
+
+static void flexcan_mb_bottom(struct net_device *dev, int index)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct can_hw_mb *hwmb;
+ struct can_frame *frame;
+ struct sk_buff *skb;
+ unsigned int tmp;
+
+ hwmb = flexcan->hwmb + index;
+ if (flexcan->fifo || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+ if (hwmb->mb_cs.cs.code == CAN_MB_TX_ABORT)
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+
+ if (hwmb->mb_cs.cs.code & CAN_MB_TX_INACTIVE) {
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ return;
+ }
+ }
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ if (hwmb->mb_cs.cs.ide)
+ frame->can_id =
+ (hwmb->mb_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = (hwmb->mb_id >> 18) & CAN_SFF_MASK;
+
+ if (hwmb->mb_cs.cs.rtr)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ frame->can_dlc = hwmb->mb_cs.cs.length;
+
+ if (frame->can_dlc && frame->can_dlc)
+ flexcan_memcpy(frame->data, hwmb->mb_data,
+ frame->can_dlc);
+
+ if (flexcan->fifo
+ || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ }
+
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ } else {
+ tmp = hwmb->mb_cs.data;
+ tmp = hwmb->mb_id;
+ tmp = hwmb->mb_data[0];
+ if (flexcan->fifo
+ || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ }
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+ stats->rx_dropped++;
+ }
+}
+
+static void flexcan_fifo_isr(struct net_device *dev, unsigned int iflag1)
+{
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct sk_buff *skb;
+ struct can_hw_mb *hwmb = flexcan->hwmb;
+ struct can_frame *frame;
+ unsigned int tmp;
+
+ if (iflag1 & __FIFO_RDY_INT) {
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame =
+ (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ if (hwmb->mb_cs.cs.ide)
+ frame->can_id =
+ (hwmb->mb_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id =
+ (hwmb->mb_id >> 18) & CAN_SFF_MASK;
+
+ if (hwmb->mb_cs.cs.rtr)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ frame->can_dlc = hwmb->mb_cs.cs.length;
+
+ if (frame->can_dlc && (frame->can_dlc <= 8))
+ flexcan_memcpy(frame->data, hwmb->mb_data,
+ frame->can_dlc);
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+
+ dev->last_rx = jiffies;
+
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ } else {
+ tmp = hwmb->mb_cs.data;
+ tmp = hwmb->mb_id;
+ tmp = hwmb->mb_data[0];
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+ }
+ }
+
+ if (iflag1 & (__FIFO_OV_INT | __FIFO_WARN_INT)) {
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame =
+ (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ frame->can_dlc = CAN_ERR_DLC;
+ if (iflag1 & __FIFO_WARN_INT)
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING |
+ CAN_ERR_CRTL_RX_WARNING;
+ if (iflag1 & __FIFO_OV_INT)
+ frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ }
+ }
+}
+
+/*!
+ * @brief The function call by CAN ISR to handle mb events.
+ *
+ * @param dev the pointer of network device.
+ *
+ * @return none
+ */
+void flexcan_mbm_isr(struct net_device *dev)
+{
+ int i, iflag1, iflag2, maxmb;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+
+ if (flexcan->maxmb > 31) {
+ maxmb = flexcan->maxmb + 1 - 32;
+ iflag1 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ iflag2 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG2) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK2);
+ iflag2 &= (1 << maxmb) - 1;
+ maxmb = 32;
+ } else {
+ maxmb = flexcan->maxmb + 1;
+ iflag1 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ iflag1 &= (1 << maxmb) - 1;
+ iflag2 = 0;
+ }
+
+ __raw_writel(iflag1, flexcan->io_base + CAN_HW_REG_IFLAG1);
+ __raw_writel(iflag2, flexcan->io_base + CAN_HW_REG_IFLAG2);
+
+ if (flexcan->fifo) {
+ flexcan_fifo_isr(dev, iflag1);
+ iflag1 &= 0xFFFFFF00;
+ }
+ for (i = 0; iflag1 && (i < maxmb); i++) {
+ if (iflag1 & (1 << i)) {
+ iflag1 &= ~(1 << i);
+ flexcan_mb_bottom(dev, i);
+ }
+ }
+
+ for (i = maxmb; iflag2 && (i <= flexcan->maxmb); i++) {
+ if (iflag2 & (1 << (i - 32))) {
+ iflag2 &= ~(1 << (i - 32));
+ flexcan_mb_bottom(dev, i);
+ }
+ }
+}
+
+/*!
+ * @brief function to xmit message buffer
+ *
+ * @param flexcan the pointer of can hardware device.
+ * @param frame the pointer of can message frame.
+ *
+ * @return Returns 0 if xmit is success. otherwise returns non-zero.
+ */
+int flexcan_mbm_xmit(struct flexcan_device *flexcan, struct can_frame *frame)
+{
+ int i = flexcan->xmit_mb;
+ struct can_hw_mb *hwmb = flexcan->hwmb;
+
+ do {
+ if (hwmb[i].mb_cs.cs.code == CAN_MB_TX_INACTIVE)
+ break;
+ if ((++i) > flexcan->maxmb) {
+ if (flexcan->fifo)
+ i = FLEXCAN_MAX_FIFO_MB;
+ else
+ i = flexcan->xmit_maxmb + 1;
+ }
+ if (i == flexcan->xmit_mb)
+ return -1;
+ } while (1);
+
+ flexcan->xmit_mb = i + 1;
+ if (flexcan->xmit_mb > flexcan->maxmb) {
+ if (flexcan->fifo)
+ flexcan->xmit_mb = FLEXCAN_MAX_FIFO_MB;
+ else
+ flexcan->xmit_mb = flexcan->xmit_maxmb + 1;
+ }
+
+ if (frame->can_id & CAN_RTR_FLAG)
+ hwmb[i].mb_cs.cs.rtr = 1;
+ else
+ hwmb[i].mb_cs.cs.rtr = 0;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ hwmb[i].mb_cs.cs.ide = 1;
+ hwmb[i].mb_cs.cs.srr = 1;
+ hwmb[i].mb_id = frame->can_id & CAN_EFF_MASK;
+ } else {
+ hwmb[i].mb_cs.cs.ide = 0;
+ hwmb[i].mb_id = (frame->can_id & CAN_SFF_MASK) << 18;
+ }
+
+ hwmb[i].mb_cs.cs.length = frame->can_dlc;
+ flexcan_memcpy(hwmb[i].mb_data, frame->data, frame->can_dlc);
+ hwmb[i].mb_cs.cs.code = CAN_MB_TX_ONCE;
+ return 0;
+}
+
+/*!
+ * @brief function to initial message buffer
+ *
+ * @param flexcan the pointer of can hardware device.
+ *
+ * @return none
+ */
+void flexcan_mbm_init(struct flexcan_device *flexcan)
+{
+ struct can_hw_mb *hwmb;
+ int rx_mb, i;
+
+ /* Set global mask to receive all messages */
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RXGMASK);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RX14MASK);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RX15MASK);
+
+ memset(flexcan->hwmb, 0, sizeof(*hwmb) * FLEXCAN_MAX_MB);
+ /* Set individual mask to receive all messages */
+ memset(flexcan->rx_mask, 0, sizeof(unsigned int) * FLEXCAN_MAX_MB);
+
+ if (flexcan->fifo)
+ rx_mb = FLEXCAN_MAX_FIFO_MB;
+ else
+ rx_mb = flexcan->maxmb - flexcan->xmit_maxmb;
+
+ hwmb = flexcan->hwmb;
+ if (flexcan->fifo) {
+ unsigned long *id_table = flexcan->io_base + CAN_FIFO_BASE;
+ for (i = 0; i < rx_mb; i++)
+ id_table[i] = 0;
+ } else {
+ for (i = 0; i < rx_mb; i++) {
+ hwmb[i].mb_cs.cs.code = CAN_MB_RX_EMPTY;
+ /*
+ * IDE bit can not control by mask registers
+ * So set message buffer to receive extend
+ * or standard message.
+ */
+ if (flexcan->ext_msg && flexcan->std_msg)
+ hwmb[i].mb_cs.cs.ide = i & 1;
+ else {
+ if (flexcan->ext_msg)
+ hwmb[i].mb_cs.cs.ide = 1;
+ }
+ }
+ }
+
+ for (; i <= flexcan->maxmb; i++)
+ hwmb[i].mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+
+ flexcan->xmit_mb = rx_mb;
+}
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
new file mode 100644
index 000000000000..1dcda35e0d7d
--- /dev/null
+++ b/drivers/net/can/mcp251x.c
@@ -0,0 +1,1239 @@
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ * MCP2510 support and bug fixes by Christian Pellegrin
+ * <chripell@evolware.org>
+ *
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Written under contract by:
+ * Chris Elston, Katalix Systems, Ltd.
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ *
+ * Your platform definition file should specify something like:
+ *
+ * static struct mcp251x_platform_data mcp251x_info = {
+ * .oscillator_frequency = 8000000,
+ * .board_specific_setup = &mcp251x_setup,
+ * .model = CAN_MCP251X_MCP2510,
+ * .power_enable = mcp251x_power_enable,
+ * .transceiver_enable = NULL,
+ * };
+ *
+ * static struct spi_board_info spi_board_info[] = {
+ * {
+ * .modalias = "mcp251x",
+ * .platform_data = &mcp251x_info,
+ * .irq = IRQ_EINT13,
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 2,
+ * },
+ * };
+ *
+ * Please see mcp251x.h for a description of the fields in
+ * struct mcp251x_platform_data.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/spi/spi.h>
+#include <linux/can/dev.h>
+#include <linux/can/core.h>
+#include <linux/if_arp.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/can/platform/mcp251x.h>
+
+/* SPI interface instruction set */
+#define INSTRUCTION_WRITE 0x02
+#define INSTRUCTION_READ 0x03
+#define INSTRUCTION_BIT_MODIFY 0x05
+#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
+#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
+#define INSTRUCTION_RESET 0xC0
+
+/* MPC251x registers */
+#define CANSTAT 0x0e
+#define CANCTRL 0x0f
+# define CANCTRL_REQOP_MASK 0xe0
+# define CANCTRL_REQOP_CONF 0x80
+# define CANCTRL_REQOP_LISTEN_ONLY 0x60
+# define CANCTRL_REQOP_LOOPBACK 0x40
+# define CANCTRL_REQOP_SLEEP 0x20
+# define CANCTRL_REQOP_NORMAL 0x00
+# define CANCTRL_OSM 0x08
+# define CANCTRL_ABAT 0x10
+#define TEC 0x1c
+#define REC 0x1d
+#define CNF1 0x2a
+#define CNF2 0x29
+# define CNF2_BTLMODE 0x80
+#define CNF3 0x28
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
+# define CNF3_PHSEG2_MASK 0x07
+#define CANINTE 0x2b
+# define CANINTE_MERRE 0x80
+# define CANINTE_WAKIE 0x40
+# define CANINTE_ERRIE 0x20
+# define CANINTE_TX2IE 0x10
+# define CANINTE_TX1IE 0x08
+# define CANINTE_TX0IE 0x04
+# define CANINTE_RX1IE 0x02
+# define CANINTE_RX0IE 0x01
+#define CANINTF 0x2c
+# define CANINTF_MERRF 0x80
+# define CANINTF_WAKIF 0x40
+# define CANINTF_ERRIF 0x20
+# define CANINTF_TX2IF 0x10
+# define CANINTF_TX1IF 0x08
+# define CANINTF_TX0IF 0x04
+# define CANINTF_RX1IF 0x02
+# define CANINTF_RX0IF 0x01
+#define EFLG 0x2d
+# define EFLG_EWARN 0x01
+# define EFLG_RXWAR 0x02
+# define EFLG_TXWAR 0x04
+# define EFLG_RXEP 0x08
+# define EFLG_TXEP 0x10
+# define EFLG_TXBO 0x20
+# define EFLG_RX0OVR 0x40
+# define EFLG_RX1OVR 0x80
+#define TXBCTRL(n) ((n * 0x10) + 0x30)
+# define TXBCTRL_ABTF 0x40
+# define TXBCTRL_MLOA 0x20
+# define TXBCTRL_TXERR 0x10
+# define TXBCTRL_TXREQ 0x08
+#define RXBCTRL(n) ((n * 0x10) + 0x60)
+# define RXBCTRL_BUKT 0x04
+# define RXBCTRL_RXM0 0x20
+# define RXBCTRL_RXM1 0x40
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define SPI_TRANSFER_BUF_LEN (2*(6 + CAN_FRAME_MAX_DATA_LEN))
+#define CAN_FRAME_MAX_BITS 128
+
+#define DEVICE_NAME "mcp251x"
+
+static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+module_param(mcp251x_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+
+static struct can_bittiming_const mcp251x_bittiming_const = {
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mcp251x_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+
+ struct mutex spi_lock; /* SPI buffer lock */
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct completion awake;
+ int wake;
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+ int restart_tx;
+};
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ u8 val = 0;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ else
+ val = priv->spi_rx_buf[2];
+
+ mutex_unlock(&priv->spi_lock);
+
+ dev_dbg(&spi->dev, "%s: read %02x = %02x\n", __func__, reg, val);
+ return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
+ u8 mask, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 4,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = mask;
+ priv->spi_tx_buf[3] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static int mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
+ int tx_buf_idx)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u32 sid, eid, exide, rtr;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */
+ if (exide)
+ sid = (frame->can_id & CAN_EFF_MASK) >> 18;
+ else
+ sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */
+ eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */
+ rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 1, sid >> 3);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 2,
+ ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3));
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 3,
+ (eid >> 8) & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 4, eid & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 5,
+ (rtr << 6) | frame->can_dlc);
+
+ for (i = 0; i < frame->can_dlc ; i++) {
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 6 + i,
+ frame->data[i]);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 6 + CAN_FRAME_MAX_DATA_LEN,
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+ tx_buf[1] = sid >> 3;
+ tx_buf[2] = ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3);
+ tx_buf[3] = (eid >> 8) & 0xff;
+ tx_buf[4] = eid & 0xff;
+ tx_buf[5] = (rtr << 6) | frame->can_dlc;
+
+ memcpy(tx_buf + 6, frame->data, frame->can_dlc);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__,
+ ret);
+ return -1;
+ }
+ }
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+ return 0;
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ dev_dbg(&spi->dev, "%s: out of memory for Rx'd frame\n",
+ __func__);
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = priv->net;
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+ u8 rx_buf[6];
+
+ rx_buf[1] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 1);
+ rx_buf[2] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 2);
+ rx_buf[3] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 3);
+ rx_buf[4] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 4);
+ rx_buf[5] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 5);
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = mcp251x_read_reg(spi,
+ RXBCTRL(buf_idx) +
+ 6 + i);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 14, /* RX buffer: RXBnCTRL to RXBnD7 */
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+ u8 *rx_buf = priv->spi_rx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_READ_RXB(buf_idx);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n",
+ __func__, ret);
+ priv->net->stats.rx_errors++;
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ memcpy(frame->data, rx_buf + 6, CAN_FRAME_MAX_DATA_LEN);
+
+ mutex_unlock(&priv->spi_lock);
+ }
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+}
+
+static void mcp251x_hw_sleep(struct spi_device *spi)
+{
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
+}
+
+static void mcp251x_hw_wakeup(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ priv->wake = 1;
+
+ /* Can only wake up by generating a wake-up interrupt. */
+ mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE);
+ mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF);
+
+ /* Wait until the device is awake */
+ if (!wait_for_completion_timeout(&priv->awake, HZ))
+ dev_err(&spi->dev, "MCP251x didn't wake-up\n");
+}
+
+static int mcp251x_hard_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ dev_warn(&spi->dev, "hard_xmit called with not null tx_skb\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->len != sizeof(struct can_frame)) {
+ dev_dbg(&spi->dev, "dropping packet - bad length\n");
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return 0;
+ }
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ net->trans_start = jiffies;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s (unimplemented)\n", __func__);
+
+ switch (mode) {
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void mcp251x_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ unsigned long timeout;
+
+ /* Enable interrupts */
+ mcp251x_write_reg(spi, CANINTE,
+ CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE |
+ CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK);
+ } else {
+ /* Put device into normal mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+ /* Wait for the device to enter normal mode */
+ timeout = jiffies + HZ;
+ while (mcp251x_read_reg(spi, CANSTAT) & 0xE0) {
+ udelay(10);
+ if (time_after(jiffies, timeout)) {
+ dev_err(&spi->dev, "MCP251x didn't"
+ " enter in normal mode\n");
+ break;
+ }
+ }
+ }
+}
+
+static int mcp251x_do_set_bittiming(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+ u8 state;
+
+ dev_dbg(&spi->dev, "%s: BRP = %d, PropSeg = %d, PS1 = %d,"
+ " PS2 = %d, SJW = %d\n", __func__, bt->brp,
+ bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+ bt->sjw);
+
+ /* Store original mode and set mode to config */
+ state = mcp251x_read_reg(spi, CANCTRL);
+ state = mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK;
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK,
+ CANCTRL_REQOP_CONF);
+
+ mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << 6) | (bt->brp - 1));
+ mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE |
+ ((bt->phase_seg1 - 1) << 3) |
+ (bt->prop_seg - 1));
+ mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
+ (bt->phase_seg2 - 1));
+
+ /* Restore original state */
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, state);
+
+ return 0;
+}
+
+static void mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+ struct spi_device *spi)
+{
+ int ret;
+
+ /* Set initial baudrate. Make sure that registers are updated
+ always by explicitly calling mcp251x_do_set_bittiming */
+ ret = can_set_bittiming(net);
+ if (ret)
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ else
+ mcp251x_do_set_bittiming(net);
+
+ /* Enable RX0->RX1 buffer roll over and disable filters */
+ mcp251x_write_bits(spi, RXBCTRL(0),
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ mcp251x_write_bits(spi, RXBCTRL(1),
+ RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_RXM0 | RXBCTRL_RXM1);
+
+ dev_dbg(&spi->dev, "%s RXBCTL 0 and 1: %02x %02x\n", __func__,
+ mcp251x_read_reg(spi, RXBCTRL(0)),
+ mcp251x_read_reg(spi, RXBCTRL(1)));
+}
+
+static void mcp251x_hw_reset(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_RESET;
+
+ ret = spi_write(spi, priv->spi_tx_buf, 1);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ /* wait for reset to finish */
+ mdelay(10);
+}
+
+static int mcp251x_hw_probe(struct spi_device *spi)
+{
+ int st1, st2;
+
+ mcp251x_hw_reset(spi);
+
+ st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
+ st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+
+ dev_dbg(&spi->dev, "%s: 0x%02x - 0x%02x\n", __func__,
+ st1, st2);
+
+ /* check for power up default values */
+ return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+}
+
+static int mcp251x_open(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ enable_irq(spi->irq);
+ mcp251x_hw_wakeup(spi);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ mcp251x_set_normal_mode(spi);
+ netif_wake_queue(net);
+
+ return 0;
+}
+
+static int mcp251x_stop(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ /* Disable and clear pending interrupts */
+ mcp251x_write_reg(spi, CANINTE, 0x00);
+ mcp251x_write_reg(spi, CANINTF, 0x00);
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+ flush_workqueue(priv->wq);
+
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ return 0;
+}
+
+static int mcp251x_do_get_state(struct net_device *net, enum can_state *state)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u8 eflag;
+
+ eflag = mcp251x_read_reg(spi, EFLG);
+
+ if (eflag & EFLG_TXBO)
+ *state = CAN_STATE_BUS_OFF;
+ else if (eflag & (EFLG_RXEP | EFLG_TXEP))
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (eflag & EFLG_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else
+ *state = CAN_STATE_ACTIVE;
+
+ return 0;
+}
+
+static void mcp251x_tx_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+ mcp251x_hw_tx(spi, frame, 0);
+ }
+}
+
+static void mcp251x_irq_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ irq_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ u8 intf;
+ u8 txbnctrl;
+
+ if (priv->after_suspend) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ /* clear since we lost tx buffer */
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ netif_wake_queue(net);
+ }
+ mcp251x_set_normal_mode(spi);
+ } else
+ mcp251x_hw_sleep(spi);
+ priv->after_suspend = 0;
+ return;
+ }
+
+ while (!priv->force_quit && !freezing(current)) {
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ dev_warn(&spi->dev,
+ "timeout in txing a packet, restarting\n");
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (priv->wake) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ priv->wake = 0;
+ }
+
+ intf = mcp251x_read_reg(spi, CANINTF);
+ if (intf == 0x00)
+ break;
+ mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+
+ dev_dbg(&spi->dev, "interrupt:%s%s%s%s%s%s%s%s\n",
+ (intf & CANINTF_MERRF) ? " MERR" : "",
+ (intf & CANINTF_WAKIF) ? " WAK" : "",
+ (intf & CANINTF_ERRIF) ? " ERR" : "",
+ (intf & CANINTF_TX2IF) ? " TX2" : "",
+ (intf & CANINTF_TX1IF) ? " TX1" : "",
+ (intf & CANINTF_TX0IF) ? " TX0" : "",
+ (intf & CANINTF_RX1IF) ? " RX1" : "",
+ (intf & CANINTF_RX0IF) ? " RX0" : "");
+
+ if (intf & CANINTF_WAKIF)
+ complete(&priv->awake);
+
+ if (intf & CANINTF_MERRF) {
+ /* if there are no pending Tx buffers, restart queue */
+ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+ if (!(txbnctrl & TXBCTRL_TXREQ)) {
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+ }
+
+ if (intf & CANINTF_ERRIF) {
+ struct sk_buff *skb;
+ struct can_frame *frame = NULL;
+ u8 eflag = mcp251x_read_reg(spi, EFLG);
+
+ dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflag);
+
+ /* Create error frame */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)
+ skb_put(skb, sizeof(struct can_frame));
+ *(unsigned long long *)&frame->data = 0ULL;
+ frame->can_id = CAN_ERR_FLAG;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = net;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Set error frame flags based on bus state */
+ if (eflag & EFLG_TXBO) {
+ frame->can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+ }
+ }
+
+ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+ if (eflag & EFLG_RX0OVR)
+ net->stats.rx_over_errors++;
+ if (eflag & EFLG_RX1OVR)
+ net->stats.rx_over_errors++;
+ if (frame) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] =
+ CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+ mcp251x_write_reg(spi, EFLG, 0x00);
+
+ if (skb)
+ netif_rx(skb);
+ }
+
+ if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+ if (priv->tx_skb) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes +=
+ ((struct can_frame *)
+ (priv->tx_skb->data))->can_dlc;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (intf & CANINTF_RX0IF)
+ mcp251x_hw_rx(spi, 0);
+
+ if (intf & CANINTF_RX1IF)
+ mcp251x_hw_rx(spi, 1);
+
+ }
+
+ mcp251x_read_reg(spi, CANSTAT);
+
+ dev_dbg(&spi->dev, "interrupt ended\n");
+}
+
+static irqreturn_t mcp251x_can_isr(int irq, void *dev_id)
+{
+ struct net_device *net = (struct net_device *)dev_id;
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ dev_dbg(&priv->spi->dev, "%s: irq\n", __func__);
+ /* Schedule bottom half */
+ if (!work_pending(&priv->irq_work))
+ queue_work(priv->wq, &priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mcp251x_tx_timeout(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ priv->restart_tx = 1;
+ queue_work(priv->wq, &priv->irq_work);
+}
+
+static struct net_device *alloc_mcp251x_netdev(int sizeof_priv)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+
+ net = alloc_candev(sizeof_priv);
+ if (!net)
+ return NULL;
+
+ priv = netdev_priv(net);
+
+ net->open = mcp251x_open;
+ net->stop = mcp251x_stop;
+ net->hard_start_xmit = mcp251x_hard_start_xmit;
+ net->tx_timeout = mcp251x_tx_timeout;
+ net->watchdog_timeo = HZ;
+
+ priv->can.bittiming_const = &mcp251x_bittiming_const;
+ priv->can.do_get_state = mcp251x_do_get_state;
+ priv->can.do_set_mode = mcp251x_do_set_mode;
+
+ priv->net = net;
+
+ return net;
+}
+
+static int __devinit mcp251x_can_probe(struct spi_device *spi)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ /* Platform data is required for osc freq */
+ goto error_out;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv));
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ priv = netdev_priv(net);
+ dev_set_drvdata(&spi->dev, priv);
+
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+
+ priv->can.bittiming.clock = pdata->oscillator_frequency / 2;
+
+ /* If requested, allocate DMA buffers */
+ if (mcp251x_enable_dma) {
+ spi->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ that much and share it between Tx and Rx DMA buffers. */
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE, &priv->spi_tx_dma, GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ mcp251x_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!mcp251x_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(1);
+
+ /* Call out to platform specific setup */
+ if (pdata->board_specific_setup)
+ pdata->board_specific_setup(spi);
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ priv->wq = create_freezeable_workqueue("mcp251x_wq");
+
+ INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
+ INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler);
+
+ init_completion(&priv->awake);
+
+ /* Configure the SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* Register IRQ */
+ if (request_irq(spi->irq, mcp251x_can_isr,
+ IRQF_TRIGGER_FALLING, DEVICE_NAME, net) < 0) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto error_irq;
+ }
+ disable_irq(spi->irq);
+
+ if (!mcp251x_hw_probe(spi)) {
+ dev_info(&spi->dev, "Probe failed\n");
+ goto error_probe;
+ }
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ ret = register_candev(net);
+ if (ret >= 0) {
+ dev_info(&spi->dev, "probed\n");
+ return ret;
+ }
+error_probe:
+ free_irq(spi->irq, net);
+error_irq:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ free_candev(net);
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ dev_err(&spi->dev, "probe failed\n");
+error_out:
+ return ret;
+}
+
+static int __devexit mcp251x_can_remove(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+ free_candev(net);
+
+ free_irq(spi->irq, net);
+ priv->force_quit = 1;
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ } else {
+ kfree(priv->spi_tx_buf);
+ kfree(priv->spi_rx_buf);
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+
+ if (pdata->power_enable) {
+ pdata->power_enable(0);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int mcp251x_can_resume(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+ pdata->power_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else
+ priv->after_suspend = 0;
+ }
+ return 0;
+}
+#else
+#define mcp251x_can_suspend NULL
+#define mcp251x_can_resume NULL
+#endif
+
+static struct spi_driver mcp251x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mcp251x_can_probe,
+ .remove = __devexit_p(mcp251x_can_remove),
+ .suspend = mcp251x_can_suspend,
+ .resume = mcp251x_can_resume,
+};
+
+static int __init mcp251x_can_init(void)
+{
+ return spi_register_driver(&mcp251x_can_driver);
+}
+
+static void __exit mcp251x_can_exit(void)
+{
+ spi_unregister_driver(&mcp251x_can_driver);
+}
+
+module_init(mcp251x_can_init);
+module_exit(mcp251x_can_exit);
+
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
+ "Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("Microchip 251x CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile
new file mode 100644
index 000000000000..9f61b13db90e
--- /dev/null
+++ b/drivers/net/can/mscan/Makefile
@@ -0,0 +1,23 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_MPC52XX) += mscan-mpc52xx.o
+
+mscan-mpc52xx-objs := mscan.o mpc52xx_can.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
diff --git a/drivers/net/can/mscan/mpc52xx_can.c b/drivers/net/can/mscan/mpc52xx_can.c
new file mode 100644
index 000000000000..8878a5901aa5
--- /dev/null
+++ b/drivers/net/can/mscan/mpc52xx_can.c
@@ -0,0 +1,293 @@
+/*
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+
+#define DRV_NAME "mpc52xx_can"
+
+static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = {
+ { .compatible = "fsl,mpc5200-cdm", },
+ { .compatible = "fsl,mpc5200b-cdm", },
+ {}
+};
+
+/*
+ * Get the frequency of the external oscillator clock connected
+ * to the SYS_XTAL_IN pin, or retrun 0 if it cannot be determined.
+ */
+static unsigned int __devinit mpc52xx_can_xtal_freq(struct device_node *np)
+{
+ struct mpc52xx_cdm __iomem *cdm;
+ struct device_node *np_cdm;
+ unsigned int freq;
+ u32 val;
+
+ freq = mpc52xx_find_ipb_freq(np);
+ if (!freq)
+ return 0;
+
+ /*
+ * Detemine SYS_XTAL_IN frequency from the clock domain settings
+ */
+ np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids);
+ cdm = of_iomap(np_cdm, 0);
+ of_node_put(np_cdm);
+ if (!np_cdm) {
+ printk(KERN_ERR "%s() failed abnormally\n", __func__);
+ return 0;
+ }
+
+ if (in_8(&cdm->ipb_clk_sel) & 0x1)
+ freq *= 2;
+ val = in_be32(&cdm->rstcfg);
+ if (val & (1 << 5))
+ freq *= 8;
+ else
+ freq *= 4;
+ if (val & (1 << 6))
+ freq /= 12;
+ else
+ freq /= 16;
+
+ iounmap(cdm);
+
+ return freq;
+}
+
+/*
+ * Get frequency of the MSCAN clock source
+ *
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK)
+ * can be selected. According to the MPC5200 user's manual, the oscillator
+ * clock is the better choice as it has less jitter but due to a hardware
+ * bug, it can not be selected for the old MPC5200 Rev. A chips.
+ */
+
+static unsigned int __devinit mpc52xx_can_clock_freq(struct device_node *np,
+ int clock_src)
+{
+ unsigned int pvr;
+
+ pvr = mfspr(SPRN_PVR);
+
+ if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
+ return mpc52xx_find_ipb_freq(np);
+
+ return mpc52xx_can_xtal_freq(np);
+}
+
+static int __devinit mpc52xx_can_probe(struct of_device *ofdev,
+ const struct of_device_id *id)
+{
+ struct device_node *np = ofdev->node;
+ struct net_device *dev;
+ struct can_priv *priv;
+ struct resource res;
+ void __iomem *base;
+ int err, irq, res_size, clock_src;
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err) {
+ dev_err(&ofdev->dev, "invalid address\n");
+ return err;
+ }
+
+ res_size = res.end - res.start + 1;
+
+ if (!request_mem_region(res.start, res_size, DRV_NAME)) {
+ dev_err(&ofdev->dev, "couldn't request %#x..%#x\n",
+ res.start, res.end);
+ return -EBUSY;
+ }
+
+ base = ioremap_nocache(res.start, res_size);
+ if (!base) {
+ dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n",
+ res.start, res.end);
+ err = -ENOMEM;
+ goto exit_release_mem;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&ofdev->dev, "no irq found\n");
+ err = -ENODEV;
+ goto exit_unmap_mem;
+ }
+
+ dev = alloc_mscandev();
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_dispose_irq;
+ }
+
+ dev->base_addr = (unsigned long)base;
+ dev->irq = irq;
+
+ priv = netdev_priv(dev);
+
+ /*
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock
+ * (IP_CLK) can be selected as MSCAN clock source. According to
+ * the MPC5200 user's manual, the oscillator clock is the better
+ * choice as it has less jitter. For this reason, it is selected
+ * by default.
+ */
+ if (of_get_property(np, "clock-ipb", NULL))
+ clock_src = MSCAN_CLKSRC_BUS;
+ else
+ clock_src = MSCAN_CLKSRC_XTAL;
+ priv->bittiming.clock = mpc52xx_can_clock_freq(np, clock_src);
+ if (!priv->bittiming.clock) {
+ dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n");
+ err = -ENODEV;
+ goto exit_free_mscan;
+ }
+
+ SET_NETDEV_DEV(dev, &ofdev->dev);
+
+ err = register_mscandev(dev, clock_src);
+ if (err) {
+ dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free_mscan;
+ }
+
+ dev_set_drvdata(&ofdev->dev, dev);
+
+ dev_info(&ofdev->dev, "MSCAN at 0x%lx, irq %d, clock %d Hz\n",
+ dev->base_addr, dev->irq, priv->bittiming.clock);
+
+ return 0;
+
+exit_free_mscan:
+ free_candev(dev);
+exit_dispose_irq:
+ irq_dispose_mapping(irq);
+exit_unmap_mem:
+ iounmap(base);
+exit_release_mem:
+ release_mem_region(res.start, res_size);
+
+ return err;
+}
+
+static int __devexit mpc52xx_can_remove(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct device_node *np = ofdev->node;
+ struct resource res;
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ unregister_mscandev(dev);
+ iounmap((void __iomem *)dev->base_addr);
+ irq_dispose_mapping(dev->irq);
+ free_candev(dev);
+
+ of_address_to_resource(np, 0, &res);
+ release_mem_region(res.start, res.end - res.start + 1);
+
+ return 0;
+}
+
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+
+static struct of_device_id __devinitdata mpc52xx_can_table[] = {
+ {.compatible = "fsl,mpc5200-mscan"},
+ {.compatible = "fsl,mpc5200b-mscan"},
+ {},
+};
+
+static struct of_platform_driver mpc52xx_can_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc52xx_can",
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+ .match_table = mpc52xx_can_table,
+};
+
+static int __init mpc52xx_can_init(void)
+{
+ return of_register_platform_driver(&mpc52xx_can_driver);
+}
+module_init(mpc52xx_can_init);
+
+static void __exit mpc52xx_can_exit(void)
+{
+ return of_unregister_platform_driver(&mpc52xx_can_driver);
+};
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
new file mode 100644
index 000000000000..fe42b9e7dfb7
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.c
@@ -0,0 +1,679 @@
+/*
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/list.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/io.h>
+
+#include "mscan.h"
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+static struct can_bittiming_const mscan_bittiming_const = {
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+struct tx_queue_entry {
+ struct list_head list;
+ u8 mask;
+ u8 id;
+};
+
+struct mscan_priv {
+ struct can_priv can;
+ long open_time;
+ unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
+ struct napi_struct napi;
+ struct net_device *dev;
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static enum can_state state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+
+ if (priv->tx_active) {
+ /* Abort transfers before going to sleep */
+ out_8(&regs->cantier, 0);
+ out_8(&regs->cantarq, priv->tx_active);
+ out_8(&regs->cantier, priv->tx_active);
+ }
+
+ canctl1 = in_8(&regs->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_STOPPED;
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(&regs->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(&regs->canctl0, in_8(&regs->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(&regs->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ else
+ priv->can.state = CAN_STATE_ACTIVE;
+ }
+ }
+ return ret;
+}
+
+static int mscan_start(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 canrflg;
+ int err;
+
+ out_8(&regs->canrier, 0);
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+ priv->shadow_canrier = 0;
+ priv->flags = 0;
+
+ err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ if (err)
+ return err;
+
+ canrflg = in_8(&regs->canrflg);
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ out_8(&regs->cantier, 0);
+
+ /* Enable receive interrupts. */
+ out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ return 0;
+}
+
+static int mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ out_8(&regs->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(&regs->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(&regs->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(&regs->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ void __iomem *data = &regs->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->tx.dlr, frame->can_dlc);
+ out_8(&regs->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(&regs->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ can_put_echo_skb(skb, dev, buf_id);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(&regs->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = &dev->stats;
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(&regs->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(&regs->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(&regs->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(&regs->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ void __iomem *data = &regs->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ dev_dbg(ND2D(dev), "error interrupt (canrflg=%#x)\n",
+ canrflg);
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(&regs->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+ if (!(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+ netif_rx_complete(dev, &priv->napi);
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ if (priv->can.state < CAN_STATE_BUS_OFF)
+ out_8(&regs->canrier, priv->shadow_canrier);
+ ret = 0;
+ }
+ return ret;
+}
+
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = &dev->stats;
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ cantier = in_8(&regs->cantier) & MSCAN_TXE;
+ cantflg = in_8(&regs->cantflg) & cantier;
+
+ if (cantier && cantflg) {
+
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ struct tx_queue_entry *entry =
+ list_entry(pos, struct tx_queue_entry, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ out_8(&regs->cantbsel, mask);
+ stats->tx_bytes += in_8(&regs->tx.dlr);
+ stats->tx_packets++;
+ can_get_echo_skb(dev, entry->id);
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(&regs->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ canrflg = in_8(&regs->canrflg);
+ if ((canrflg & ~MSCAN_STAT_MSK) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(&regs->canrier);
+ out_8(&regs->canrier, 0);
+ netif_rx_schedule(dev, &priv->napi);
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
+{
+
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ if (priv->can.state <= CAN_STATE_BUS_OFF)
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ ret = mscan_start(dev);
+ if (ret)
+ break;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int mscan_do_set_bittiming(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
+ btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
+ BTR1_SET_TSEG2(bt->phase_seg2) |
+ BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
+
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ out_8(&regs->canbtr0, btr0);
+ out_8(&regs->canbtr1, btr1);
+
+ return 0;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ /* determine and set bittime */
+ ret = can_set_bittiming(dev);
+ if (ret)
+ return ret;
+
+ napi_enable(&priv->napi);
+ ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
+
+ if (ret < 0) {
+ napi_disable(&priv->napi);
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ priv->open_time = jiffies;
+
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+
+ ret = mscan_start(dev);
+ if (ret)
+ return ret;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ napi_disable(&priv->napi);
+
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ free_irq(dev->irq, dev);
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ can_close_cleanup(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(&regs->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(&regs->canctl1, ctl1);
+ udelay(100);
+
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(&regs->canidar1_0, 0);
+ out_be16(&regs->canidar3_2, 0);
+ out_be16(&regs->canidar5_4, 0);
+ out_be16(&regs->canidar7_6, 0);
+
+ out_be16(&regs->canidmr1_0, 0xffff);
+ out_be16(&regs->canidmr3_2, 0xffff);
+ out_be16(&regs->canidmr5_4, 0xffff);
+ out_be16(&regs->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_candev(dev);
+}
+EXPORT_SYMBOL_GPL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_CANE);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_start_xmit;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+
+ priv->can.bittiming_const = &mscan_bittiming_const;
+ priv->can.do_set_bittiming = mscan_do_set_bittiming;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++) {
+ priv->tx_queue[i].id = i;
+ priv->tx_queue[i].mask = 1 << i;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips");
diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h
new file mode 100644
index 000000000000..81a2eb5a19e5
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.h
@@ -0,0 +1,237 @@
+/*
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+/* Use the MPC5200 MSCAN variant? */
+#ifdef CONFIG_PPC
+#define MSCAN_FOR_MPC5200
+#endif
+
+#ifdef MSCAN_FOR_MPC5200
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef MSCAN_FOR_MPC5200
+#define _MSCAN_RESERVED_(n, num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n, num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef MSCAN_FOR_MPC5200
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+struct net_device *alloc_mscandev(void);
+/* clock_src:
+ * 1 = The MSCAN clock source is the onchip Bus Clock.
+ * 0 = The MSCAN clock source is the chip Oscillator Clock.
+ */
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
diff --git a/drivers/net/can/old/Kconfig b/drivers/net/can/old/Kconfig
new file mode 100644
index 000000000000..4b8567a20d03
--- /dev/null
+++ b/drivers/net/can/old/Kconfig
@@ -0,0 +1,67 @@
+
+config CAN_SJA1000_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Philips SJA1000 (old)"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+ REMARK: This is the 'old' driver originally written by Matthias
+ Brukner and Oliver Hartkopp which uses a non-standard hardware
+ abstaction layer (HAL) inspired by the OCAN driver.
+
+config CAN_I82527_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Intel 82527 (old)"
+ ---help---
+ The i82527 is a complex CAN controller that can handle RTR
+ frame replies on it's own. This feature (and diffent RX filters)
+ lead to an amount of 15 message objects (for RX & TX). Message
+ object 15 has (as only) a shadow register for a reliable
+ receiption of EFF or(!) SFF frames at high CAN traffic.
+ This driver can send each type of CAN frames (SFF/EFF/RTR).
+ Using 4 message objects it can also receive each type of CAN
+ frames. But due to the onchip filter matching trigger method
+ it is not possible to determine the received RTR CAN-ID.
+ The reliable message object 15 receives SFF frames by default.
+ This message object 15 usage maybe changed with the mo15 param.
+ REMARK: This is the 'old' driver originally written by Oliver
+ Hartkopp which uses a non-standard hardware abstaction layer (HAL)
+ inspired by the OCAN driver. http://ar.linux.it/software/#ocan
+
+config CAN_MSCAN_OLD
+ depends on CAN_OLD_DRIVERS && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips (old)"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX_OLD
+ tristate "Freescale MPC5200 onboard CAN controller (old)"
+ depends on CAN_MSCAN_OLD && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_CCAN_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Bosch CCAN driver (old)"
+ ---help---
+ This is a driver for the Bosch CCAN controller found for example
+ on the hynix h7202 chip.
+
+config CAN_H7202_OLD
+ tristate "Hynix H7202 onboard CAN controller (old)"
+ depends on CAN_CCAN_OLD
+ ---help---
+ This is a driver for the hynix h7202 can controller.
+
diff --git a/drivers/net/can/old/ccan/Makefile b/drivers/net/can/old/ccan/Makefile
new file mode 100644
index 000000000000..c3db1800e6d7
--- /dev/null
+++ b/drivers/net/can/old/ccan/Makefile
@@ -0,0 +1,20 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_CCAN) += ccan.o
+obj-$(CONFIG_CAN_H7202) += h7202_can.o
+
+endif
diff --git a/drivers/net/can/old/ccan/ccan.c b/drivers/net/can/old/ccan/ccan.c
new file mode 100644
index 000000000000..6a908a14eee4
--- /dev/null
+++ b/drivers/net/can/old/ccan/ccan.c
@@ -0,0 +1,557 @@
+/*
+ * drivers/can/c_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "ccan.h"
+
+static u32 ccan_read_reg32(struct net_device *dev, enum c_regs reg)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ u32 val = priv->read_reg(dev, reg);
+ val |= ((u32) priv->read_reg(dev, reg + 2)) << 16;
+
+ return val;
+}
+
+static void ccan_write_reg32(struct net_device *dev, enum c_regs reg, u32 val)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, reg, val & 0xffff);
+ priv->write_reg(dev, reg + 2, val >> 16);
+}
+
+static inline void ccan_object_get(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static inline void ccan_object_put(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), IF_COMM_WR | mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static int ccan_write_object(struct net_device *dev,
+ int iface, struct can_frame *frame, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val;
+
+ if (frame->can_id & CAN_EFF_FLAG)
+ val = IF_ARB_MSGXTD | (frame->can_id & CAN_EFF_MASK);
+ else
+ val = ((frame->can_id & CAN_SFF_MASK) << 18);
+
+ if (!(frame->can_id & CAN_RTR_FLAG))
+ val |= IF_ARB_TRANSMIT;
+
+ val |= IF_ARB_MSGVAL;
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), val);
+
+ memcpy(&val, &frame->data[0], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAA(iface), val);
+ memcpy(&val, &frame->data[4], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAB(iface), val);
+ priv->write_reg(dev, CAN_IF_MCONT(iface),
+ IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB |
+ (frame->can_dlc & 0xf));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL);
+
+ return 0;
+}
+
+static int ccan_read_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val, ctrl, data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb->dev = dev;
+
+ ccan_object_get(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+#ifdef CCAN_DEBUG
+ priv->bufstat[objno]++;
+#endif
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ ctrl = priv->read_reg(dev, CAN_IF_MCONT(iface));
+
+ if (ctrl & IF_MCONT_MSGLST) {
+ priv->can.net_stats.rx_errors++;
+ DBG("%s: msg lost in buffer %d\n", __func__, objno);
+ }
+
+ frame->can_dlc = ctrl & 0xf;
+
+ val = ccan_read_reg32(dev, CAN_IF_ARB(iface));
+
+ data = ccan_read_reg32(dev, CAN_IF_DATAA(iface));
+ memcpy(&frame->data[0], &data, 4);
+ data = ccan_read_reg32(dev, CAN_IF_DATAB(iface));
+ memcpy(&frame->data[4], &data, 4);
+
+ if (val & IF_ARB_MSGXTD)
+ frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = (val >> 18) & CAN_SFF_MASK;
+
+ if (val & IF_ARB_TRANSMIT)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), ctrl &
+ ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_CONTROL);
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ netif_rx(skb);
+
+ priv->can.net_stats.rx_packets++;
+ priv->can.net_stats.rx_bytes += frame->can_dlc;
+
+ return 0;
+}
+
+static int ccan_setup_receive_object(struct net_device *dev, int iface,
+ int objno, unsigned int mask,
+ unsigned int id, unsigned int mcont)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_MASK(iface), mask);
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), IF_ARB_MSGVAL | id);
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), mcont);
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_inval_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), 0);
+ priv->write_reg(dev, CAN_IF_MCONT(iface), 0);
+ ccan_object_put(dev, 0, objno, IF_COMM_ARB | IF_COMM_CONTROL);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame = (struct can_frame *)skb->data;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ccan_write_object(dev, 0, frame, priv->tx_object);
+#ifdef CCAN_DEBUG
+ priv->bufstat[priv->tx_object]++;
+#endif
+ priv->tx_object++;
+ if (priv->tx_object > 5)
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ priv->can.net_stats.tx_packets++;
+ priv->can.net_stats.tx_bytes += frame->can_dlc;
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static void ccan_tx_timeout(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->can.net_stats.tx_errors++;
+}
+
+static int ccan_set_bittime(struct net_device *dev, struct can_bittime *br)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int reg_timing, ctrl_save;
+ u8 brp, sjw, tseg1, tseg2;
+
+ if (br->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ brp = br->std.brp - 1;
+ sjw = br->std.sjw - 1;
+ tseg1 = br->std.prop_seg + br->std.phase_seg1 - 1;
+ tseg2 = br->std.phase_seg2 - 1;
+
+ reg_timing = (brp & BTR_BRP_MASK) |
+ ((sjw << BTR_SJW_SHIFT) & BTR_SJW_MASK) |
+ ((tseg1 << BTR_TSEG1_SHIFT) & BTR_TSEG1_MASK) |
+ ((tseg2 << BTR_TSEG2_SHIFT) & BTR_TSEG2_MASK);
+
+ DBG("%s: brp = %d sjw = %d seg1 = %d seg2 = %d\n", __func__,
+ brp, sjw, tseg1, tseg2);
+ DBG("%s: setting BTR to %04x\n", __func__, reg_timing);
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ctrl_save = priv->read_reg(dev, CAN_CONTROL);
+ priv->write_reg(dev, CAN_CONTROL,
+ ctrl_save | CONTROL_CCE | CONTROL_INIT);
+ priv->write_reg(dev, CAN_BTR, reg_timing);
+ priv->write_reg(dev, CAN_CONTROL, ctrl_save);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ return 0;
+}
+
+static int ccan_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ DBG("%s: CAN_MODE_START requested\n", __func__);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ccan_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ u32 reg_status;
+#ifdef CCAN_DEBUG
+ int i;
+#endif
+
+ reg_status = priv->read_reg(dev, CAN_STATUS);
+
+ if (reg_status & STATUS_EPASS)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (reg_status & STATUS_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else if (reg_status & STATUS_BOFF)
+ *state = CAN_STATE_BUS_OFF;
+ else
+ *state = CAN_STATE_ACTIVE;
+#ifdef CCAN_DEBUG
+ DBG("buffer statistic:\n");
+ for (i = 0; i <= MAX_OBJECT; i++)
+ DBG("%d: %d\n", i, priv->bufstat[i]);
+#endif
+ return 0;
+}
+
+static int ccan_do_status_irq(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int status, diff;
+
+ status = priv->read_reg(dev, CAN_STATUS);
+ status &= ~(STATUS_TXOK | STATUS_RXOK);
+ diff = status ^ priv->last_status;
+
+ if (diff & STATUS_EPASS) {
+ if (status & STATUS_EPASS)
+ dev_info(ND2D(dev), "entered error passive state\n");
+ else
+ dev_info(ND2D(dev), "left error passive state\n");
+ }
+ if (diff & STATUS_EWARN) {
+ if (status & STATUS_EWARN)
+ dev_info(ND2D(dev), "entered error warning state\n");
+ else
+ dev_info(ND2D(dev), "left error warning state\n");
+ }
+ if (diff & STATUS_BOFF) {
+ if (status & STATUS_BOFF)
+ dev_info(ND2D(dev), "entered busoff state\n");
+ else
+ dev_info(ND2D(dev), "left busoff state\n");
+ }
+
+ if (diff & STATUS_LEC_MASK) {
+ switch (status & STATUS_LEC_MASK) {
+ case LEC_STUFF_ERROR:
+ dev_info(ND2D(dev), "suffing error\n");
+ break;
+ case LEC_FORM_ERROR:
+ dev_info(ND2D(dev), "form error\n");
+ break;
+ case LEC_ACK_ERROR:
+ dev_info(ND2D(dev), "ack error\n");
+ break;
+ case LEC_BIT1_ERROR:
+ dev_info(ND2D(dev), "bit1 error\n");
+ break;
+ }
+ }
+
+ priv->write_reg(dev, CAN_STATUS, 0);
+ priv->last_status = status;
+
+ return diff ? 1 : 0;
+}
+
+static void ccan_do_object_irq(struct net_device *dev, u16 irqstatus)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+ u32 val;
+
+ if (irqstatus > MAX_TRANSMIT_OBJECT) {
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ while (val & RECEIVE_OBJECT_BITS) {
+ for (i = MAX_TRANSMIT_OBJECT + 1; i <= MAX_OBJECT; i++)
+ if (val & (1<<i))
+ ccan_read_object(dev, 0, i);
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ }
+ } else {
+ ccan_inval_object(dev, 0, irqstatus - 1);
+ val = ccan_read_reg32(dev, CAN_TXRQST);
+ if (!val) {
+ priv->tx_object = 0;
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+static void do_statuspoll(struct work_struct *work)
+{
+ struct ccan_priv *priv = container_of(((struct delayed_work *) work),
+ struct ccan_priv, work);
+
+ priv->write_reg(priv->dev, CAN_CONTROL,
+ CONTROL_SIE | CONTROL_EIE | CONTROL_IE);
+}
+
+static irqreturn_t ccan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct ccan_priv *priv = netdev_priv(dev);
+ u16 irqstatus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ while (irqstatus) {
+ if (irqstatus == 0x8000) {
+ if (ccan_do_status_irq(dev)) {
+ /* The c_can core tends to flood us with
+ * interrupts when certain error states don't
+ * disappear. Disable interrupts and see if it's
+ * getting better later. This is at least the
+ * case on the Magnachip h7202.
+ */
+ priv->write_reg(dev, CAN_CONTROL, CONTROL_EIE |
+ CONTROL_IE);
+ schedule_delayed_work(&priv->work, HZ / 10);
+ goto exit;
+ }
+ } else {
+ ccan_do_object_irq(dev, irqstatus);
+ }
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ }
+
+exit:
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int ccan_open(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ if (request_irq(dev->irq, &ccan_isr, 0, dev->name, dev)) {
+ dev_err(ND2D(dev), "failed to attach interrupt\n");
+ return -EAGAIN;
+ }
+
+ priv->write_reg(dev, CAN_CONTROL,
+ CONTROL_EIE | CONTROL_SIE | CONTROL_IE);
+
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int ccan_stop(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ /* mask all IRQs */
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+ priv->write_reg(dev, CAN_CONTROL, 0);
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+static int ccan_chip_config(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+
+ /* setup message objects */
+ for (i = 0; i <= MAX_OBJECT; i++)
+ ccan_inval_object(dev, 0, i);
+
+ for (i = MAX_TRANSMIT_OBJECT + 1; i < MAX_OBJECT; i++)
+ ccan_setup_receive_object(dev, 0, i, 0, 0,
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+ ccan_setup_receive_object(dev, 0, MAX_OBJECT, 0, 0, IF_MCONT_EOB |
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+#ifdef CCAN_DEBUG
+ for (i = 0; i <= MAX_OBJECT; i++)
+ priv->bufstat[i] = 0;
+#endif
+
+ return 0;
+}
+
+struct net_device *alloc_ccandev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+
+ dev = alloc_candev(sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ dev->open = ccan_open;
+ dev->stop = ccan_stop;
+ dev->hard_start_xmit = ccan_hard_start_xmit;
+ dev->tx_timeout = ccan_tx_timeout;
+
+ priv->can.bitrate = 500000;
+
+ priv->can.do_set_bittime = ccan_set_bittime;
+ priv->can.do_get_state = ccan_get_state;
+ priv->can.do_set_mode = ccan_set_mode;
+
+ priv->dev = dev;
+ priv->tx_object = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL(alloc_ccandev);
+
+void free_ccandev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL(free_ccandev);
+
+int register_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ ccan_chip_config(dev);
+ INIT_DELAYED_WORK(&priv->work, do_statuspoll);
+
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL(register_ccandev);
+
+void unregister_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_ccandev);
+
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for C_CAN based chips");
diff --git a/drivers/net/can/old/ccan/ccan.h b/drivers/net/can/old/ccan/ccan.h
new file mode 100644
index 000000000000..b7a5460195e1
--- /dev/null
+++ b/drivers/net/can/old/ccan/ccan.h
@@ -0,0 +1,140 @@
+/*
+ * drivers/can/c_can.h
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CCAN_H__
+#define __CCAN_H__
+
+#include <linux/can.h>
+#include <linux/platform_device.h>
+
+#undef CCAN_DEBUG
+
+enum c_regs {
+ CAN_CONTROL = 0x00,
+ CAN_STATUS = 0x02,
+ CAN_ERROR = 0x04,
+ CAN_BTR = 0x06,
+ CAN_IR = 0x08,
+ CAN_TEST = 0x0a,
+ CAN_BRP_EXT = 0x0c,
+ CAN_IF1 = 0x10,
+ CAN_IF2 = 0x40,
+ CAN_TXRQST = 0x80, /* 32bit */
+ CAN_NEWDAT = 0x90, /* 32bit */
+ CAN_INTPND = 0xa0, /* 32bit */
+ CAN_MSGVAL = 0xb0, /* 32bit */
+};
+
+#define CAN_IF_COMR(x) (CAN_IF1 + (x) * 0x30 + 0x00)
+#define CAN_IF_COMM(x) (CAN_IF1 + (x) * 0x30 + 0x02)
+#define CAN_IF_MASK(x) (CAN_IF1 + (x) * 0x30 + 0x04) /* 32bit */
+#define CAN_IF_ARB(x) (CAN_IF1 + (x) * 0x30 + 0x08) /* 32bit */
+#define CAN_IF_MCONT(x) (CAN_IF1 + (x) * 0x30 + 0x0c)
+#define CAN_IF_DATAA(x) (CAN_IF1 + (x) * 0x30 + 0x0e) /* 32bit */
+#define CAN_IF_DATAB(x) (CAN_IF1 + (x) * 0x30 + 0x12) /* 32bit */
+
+#define CONTROL_TEST (1<<7)
+#define CONTROL_CCE (1<<6)
+#define CONTROL_DAR (1<<5)
+#define CONTROL_EIE (1<<3)
+#define CONTROL_SIE (1<<2)
+#define CONTROL_IE (1<<1)
+#define CONTROL_INIT (1<<0)
+
+#define TEST_RX (1<<7)
+#define TEST_TX1 (1<<6)
+#define TEST_TX2 (1<<5)
+#define TEST_LBACK (1<<4)
+#define TEST_SILENT (1<<3)
+#define TEST_BASIC (1<<2)
+
+#define STATUS_BOFF (1<<7)
+#define STATUS_EWARN (1<<6)
+#define STATUS_EPASS (1<<5)
+#define STATUS_RXOK (1<<4)
+#define STATUS_TXOK (1<<3)
+#define STATUS_LEC_MASK (1<<2)
+#define LEC_STUFF_ERROR 1
+#define LEC_FORM_ERROR 2
+#define LEC_ACK_ERROR 3
+#define LEC_BIT1_ERROR 4
+
+#define BTR_BRP_MASK 0x3f
+#define BTR_BRP_SHIFT 0
+#define BTR_SJW_SHIFT 6
+#define BTR_SJW_MASK (0x3 << BTR_SJW_SHIFT)
+#define BTR_TSEG1_SHIFT 8
+#define BTR_TSEG1_MASK (0xf << BTR_TSEG1_SHIFT)
+#define BTR_TSEG2_SHIFT 12
+#define BTR_TSEG2_MASK (0x7 << BTR_TSEG2_SHIFT)
+
+#define IF_COMR_BUSY (1<<15)
+
+#define IF_COMM_WR (1<<7)
+#define IF_COMM_MASK (1<<6)
+#define IF_COMM_ARB (1<<5)
+#define IF_COMM_CONTROL (1<<4)
+#define IF_COMM_CLR_INT_PND (1<<3)
+#define IF_COMM_TXRQST (1<<2)
+#define IF_COMM_DATAA (1<<1)
+#define IF_COMM_DATAB (1<<0)
+
+#define IF_COMM_ALL (IF_COMM_MASK | IF_COMM_ARB | IF_COMM_CONTROL | \
+ IF_COMM_TXRQST | IF_COMM_DATAA | IF_COMM_DATAB)
+
+#define IF_ARB_MSGVAL (1<<31)
+#define IF_ARB_MSGXTD (1<<30)
+#define IF_ARB_TRANSMIT (1<<29)
+
+#define IF_MCONT_NEWDAT (1<<15)
+#define IF_MCONT_MSGLST (1<<14)
+#define IF_MCONT_INTPND (1<<13)
+#define IF_MCONT_UMASK (1<<12)
+#define IF_MCONT_TXIE (1<<11)
+#define IF_MCONT_RXIE (1<<10)
+#define IF_MCONT_RMTEN (1<<9)
+#define IF_MCONT_TXRQST (1<<8)
+#define IF_MCONT_EOB (1<<7)
+
+#define MAX_OBJECT 31
+#define MAX_TRANSMIT_OBJECT 15
+#define RECEIVE_OBJECT_BITS 0xffff0000
+
+struct ccan_priv {
+ struct can_priv can;
+ struct net_device *dev;
+ int tx_object;
+ int last_status;
+ struct delayed_work work;
+ u16 (*read_reg)(struct net_device *dev, enum c_regs reg);
+ void (*write_reg)(struct net_device *dev, enum c_regs reg, u16 val);
+#ifdef CCAN_DEBUG
+ unsigned int bufstat[MAX_OBJECT + 1];
+#endif
+};
+
+extern struct net_device *alloc_ccandev(int sizeof_priv);
+extern void free_ccandev(struct net_device *dev);
+extern int register_ccandev(struct net_device *dev);
+extern void unregister_ccandev(struct net_device *dev);
+
+#endif /* __CCAN_H__ */
diff --git a/drivers/net/can/old/ccan/h7202_can.c b/drivers/net/can/old/ccan/h7202_can.c
new file mode 100644
index 000000000000..7dcc3e749248
--- /dev/null
+++ b/drivers/net/can/old/ccan/h7202_can.c
@@ -0,0 +1,199 @@
+/*
+ * drivers/can/h7202_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+#include <asm/hardware.h>
+
+#include "ccan.h"
+
+#define DRV_NAME "h7202can"
+#define DELAY 5
+#define CAN_ENABLE 0x0e
+
+static u16 h7202can_read_reg(struct net_device *dev, enum c_regs reg)
+{
+ u16 val;
+ volatile int i;
+
+ /* The big kernel lock is used to prevent any other AMBA devices from
+ * interfering with the current register read operation. The register
+ * is read twice because of braindamaged hynix cpu.
+ */
+ lock_kernel();
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+
+ return val;
+}
+
+static void h7202can_write_reg(struct net_device *dev, enum c_regs reg, u16 val)
+{
+ volatile int i;
+
+ lock_kernel();
+ outw(val, dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+}
+
+static int h7202can_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+ struct resource *mem;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ dev = alloc_ccandev(sizeof(struct ccan_priv));
+ if (!dev)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ priv = netdev_priv(dev);
+ priv->can.can_sys_clock = 8000000;
+ priv->read_reg = h7202can_read_reg;
+ priv->write_reg = h7202can_write_reg;
+
+ platform_set_drvdata(pdev, dev);
+
+ /* configure ports */
+ switch (mem->start) {
+ case CAN0_PHYS:
+ CPU_REG(GPIO_C_VIRT, GPIO_EN) &= ~(3<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) &= ~(1<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) |= (1<<2);
+ break;
+ case CAN1_PHYS:
+ CPU_REG(GPIO_E_VIRT, GPIO_EN) &= ~(3<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) |= (1<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) &= ~(1<<17);
+ break;
+ }
+
+ /* enable can */
+ h7202can_write_reg(dev, CAN_ENABLE, 1);
+
+ ret = register_ccandev(dev);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+fail_map:
+ release_mem_region(mem->start, mem_size);
+req_error:
+ free_ccandev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int h7202can_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_ccandev(dev);
+
+ iounmap((volatile void __iomem *)(dev->base_addr));
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_ccandev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int h7202can_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int h7202can_drv_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver h7202can_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = h7202can_drv_probe,
+ .remove = h7202can_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = h7202can_drv_suspend,
+ .resume = h7202can_drv_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init h7202can_init(void)
+{
+ printk(KERN_INFO "%s initializing\n", h7202can_driver.driver.name);
+ return platform_driver_register(&h7202can_driver);
+}
+
+static void __exit h7202can_cleanup(void)
+{
+ platform_driver_unregister(&h7202can_driver);
+ printk(KERN_INFO "%s unloaded\n", h7202can_driver.driver.name);
+}
+
+module_init(h7202can_init);
+module_exit(h7202can_cleanup);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver Hynix H7202 processor");
diff --git a/drivers/net/can/old/hal/c200.c b/drivers/net/can/old/hal/c200.c
new file mode 100644
index 000000000000..47ea74ca51b0
--- /dev/null
+++ b/drivers/net/can/old/hal/c200.c
@@ -0,0 +1,206 @@
+/*
+ * c200.c - low cost parallelport CAN adaptor hardware abstraction layer
+ * ( direct register access without parport subsystem support )
+ *
+ * CAN200 project homepage http://private.addcom.de/horo/can200
+ *
+ * This hal is based on a patch from Uwe Bonnes.
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "c200"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x378UL;
+ irq[0] = 7;
+}
+
+#define ECR_REGS_OFFSET 0x400
+#define ECR_CTRL_OFFSET (ECR_REGS_OFFSET + 2)
+
+static u8 ecr_crtl_save;
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* grab ECR control registers and set parport to 'byte mode' */
+ if (request_region(rbase[dev_num] + ECR_REGS_OFFSET, 3, drv_name)) {
+
+ ecr_crtl_save = inb(rbase[dev_num] + ECR_CTRL_OFFSET);
+
+ outb((ecr_crtl_save & 0x1F) | 0x20,
+ rbase[dev_num] + ECR_CTRL_OFFSET);
+ } else
+ return 0;
+
+ if (request_region(rbase[dev_num], 4, drv_name))
+ return 1;
+
+ release_region(rbase[dev_num] + ECR_REGS_OFFSET, 3);
+
+ return 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], 4);
+
+ /* restore original ECR control register value */
+ outb(ecr_crtl_save, base[dev_num] + ECR_CTRL_OFFSET);
+ release_region(base[dev_num] + ECR_REGS_OFFSET, 3);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* enable irq */
+ outb(inb(pc) | 0x10, pc);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* disable irq */
+ outb(inb(pc) & ~0x10, pc);
+
+ return 0;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+#define WRITEP 0x01 /* inverted at port */
+#define DATASTB 0x02 /* inverted at port and at device*/
+#define ADDRSTB 0x08 /* inverted at port and at device*/
+#define PORTREAD 0x20
+
+static DEFINE_SPINLOCK(c200_lock);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+ u8 val;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb((reg & 0x1F) | 0x80, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | PORTREAD, pc);
+ outb(irqstatus | DATASTB | PORTREAD, pc);
+ val = inb(pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb(reg & 0x1F, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | WRITEP, pc);
+ outb(irqstatus | DATASTB | WRITEP, pc);
+ outb(val, pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
diff --git a/drivers/net/can/old/hal/esdio.c b/drivers/net/can/old/hal/esdio.c
new file mode 100644
index 000000000000..a0132e2ed545
--- /dev/null
+++ b/drivers/net/can/old/hal/esdio.c
@@ -0,0 +1,186 @@
+/*
+ * esdio.c - multiplex register access CAN hardware abstraction layer
+ * for the esd 3xCAN pc104 board
+ * http://www.esd-electronics.de/products/CAN/can-pc104-200_e.htm
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2007 Fraunhofer FOKUS
+ *
+ * Provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ * History:
+ * 2007-05-22 Bjoern Riemer: initial release
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+//#define DBG(args...) printk(args)
+
+int esd_ale_offset = 1; //default for the sja1000 chip
+int esd_cs_offset = 0; //default for the sja1000 chip
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "esdio"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1e8UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ if (!memcmp(drv_name,"i82527-esdio",sizeof("i82527-esdio"))){
+ esd_ale_offset = 7;
+ esd_cs_offset = 4;
+ } else if (!memcmp(drv_name,"sja1000-esdio",sizeof("sja1000-esdio"))){
+ esd_ale_offset = 1;
+ esd_cs_offset = 0;
+ }
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ if (request_region(base[dev_num] + esd_ale_offset, 1, drv_name)){
+ if (request_region(base[dev_num] + esd_cs_offset, 1,drv_name)){
+ return 1;
+ } else {
+ release_region(base[dev_num]+esd_ale_offset, 1);
+ return 0; // error
+ }
+ }
+
+ return 0; // error
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ release_region(base[dev_num] + esd_cs_offset, 1);
+ release_region(base[dev_num] + esd_ale_offset, 1);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ int i, stat, i1;
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ i1 = irq[dev_num]; //get IRQ number
+ DBG(KERN_INFO "esdio.c: enabling IRQ %d for dev_num %d\n",i1,dev_num);
+
+ for (i=0; i<4; i++){
+ stat=i; // bit 0,1 selects the latch bit to write
+ if (i1 & 0x01){
+ stat |= 0x80; //bit7 carrys the value of the latch bit
+ }
+ outb(stat,base[dev_num]+3);
+ i1 = i1>>1;
+ }
+
+ outb(0x87,base[dev_num]+3); //enable irq selection
+ outb(0x86,base[dev_num]+3); //enable irq tristate buffer
+
+ return 1;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ int i;
+ extern unsigned long base[];
+
+ DBG(KERN_INFO "esdio.c: diabling IRQ for dev_num %d\n",dev_num);
+
+ outb(0x07,base[dev_num]+3); //disable irq selection
+ outb(0x06,base[dev_num]+3); //disable irq tristate buffer
+
+ for (i=0; i<4; i++)
+ outb(i,base[dev_num]+3);
+
+ return 1;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ return inb(base + esd_cs_offset); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ outb(val, base + esd_cs_offset); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) {
+
+ outb(0x86,dev->base_addr+3); //enable irq tristate buffer
+ return;
+}
diff --git a/drivers/net/can/old/hal/gw2.c b/drivers/net/can/old/hal/gw2.c
new file mode 100644
index 000000000000..893b983e2ec2
--- /dev/null
+++ b/drivers/net/can/old/hal/gw2.c
@@ -0,0 +1,161 @@
+/*
+ * gw2.c - Trajet GW2 register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#define ADDR_GAP 1
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "gw2"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+ extern unsigned int speed[];
+
+ base[0] = 0xF0100200UL;
+ irq[0] = 26;
+ speed[0] = 500;
+
+ base[1] = 0xF0100300UL;
+ irq[1] = 26;
+ speed[1] = 100;
+
+ base[2] = 0xF0100400UL;
+ irq[2] = 26;
+ speed[2] = 100;
+
+ base[3] = 0xF0100500UL;
+ irq[3] = 26;
+ speed[3] = 500;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], gw2_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], gw2_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], gw2_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], gw2_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ val = (u8)readw(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ writew(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/hal.h b/drivers/net/can/old/hal/hal.h
new file mode 100644
index 000000000000..1c59b54cdb6a
--- /dev/null
+++ b/drivers/net/can/old/hal/hal.h
@@ -0,0 +1,99 @@
+/*
+ * hal.h - definitions for CAN controller hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_HAL_H
+#define CAN_HAL_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+/* Number of supported CAN devices for each HAL (default) */
+#define MAXDEV 8
+
+/* general function prototypes for CAN HAL */
+
+/* init the HAL - call at driver module init */
+int hal_init(void);
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void);
+
+/* get name of this CAN HAL */
+char *hal_name(void);
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void);
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name);
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs);
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num);
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num);
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg);
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val);
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev);
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev);
+
+#endif /* CAN_HAL_H */
diff --git a/drivers/net/can/old/hal/io.c b/drivers/net/can/old/hal/io.c
new file mode 100644
index 000000000000..85e3b1edc1bc
--- /dev/null
+++ b/drivers/net/can/old/hal/io.c
@@ -0,0 +1,123 @@
+/*
+ * io.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x2C0UL;
+ irq[0] = 10;
+
+ base[1] = 0x320UL;
+ irq[1] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/iomem.c b/drivers/net/can/old/hal/iomem.c
new file mode 100644
index 000000000000..6a89a9cc2fe0
--- /dev/null
+++ b/drivers/net/can/old/hal/iomem.c
@@ -0,0 +1,142 @@
+/*
+ * iomem.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomem"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0xd8000UL;
+ irq[0] = 5;
+
+ base[1] = 0xd8100UL;
+ irq[1] = 15;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], num_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], num_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], num_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ val = (u8)readb(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ writeb(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/iomux.c b/drivers/net/can/old/hal/iomux.c
new file mode 100644
index 000000000000..a4e8fba82ddd
--- /dev/null
+++ b/drivers/net/can/old/hal/iomux.c
@@ -0,0 +1,125 @@
+/*
+ * iomux.c - multiplex register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomux"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x300UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ return (request_region(base[dev_num], 2, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ release_region(base[dev_num], 2);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base); /* address */
+ return inb(base + 1); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base); /* address */
+ outb(val, base + 1); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/pc7io.c b/drivers/net/can/old/hal/pc7io.c
new file mode 100644
index 000000000000..b223efd25f9f
--- /dev/null
+++ b/drivers/net/can/old/hal/pc7io.c
@@ -0,0 +1,126 @@
+/*
+ * pc7io.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "pc7io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1000UL;
+ irq[0] = 9;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) {
+
+ /* Unlock special function register */
+ outb(5, 0x169);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/i82527/Makefile b/drivers/net/can/old/i82527/Makefile
new file mode 100644
index 000000000000..eb025a8a10c5
--- /dev/null
+++ b/drivers/net/can/old/i82527/Makefile
@@ -0,0 +1,24 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := i82527-pc7io.o i82527-iomem.o i82527-esdio.o
+
+i82527-pc7io-objs := i82527.o proc.o ../hal/pc7io.o
+i82527-iomem-objs := i82527.o proc.o ../hal/iomem.o
+i82527-esdio-objs := i82527.o proc.o ../hal/esdio.o
+
+endif
diff --git a/drivers/net/can/old/i82527/i82527.c b/drivers/net/can/old/i82527/i82527.c
new file mode 100644
index 000000000000..d45c4fab22de
--- /dev/null
+++ b/drivers/net/can/old/i82527/i82527.c
@@ -0,0 +1,1251 @@
+/*
+ * i82527.c - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "hal.h"
+#include "i82527.h"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.0.4";
+static const char *drv_reldate = "2007-08-03";
+
+static const canid_t rxobjflags[] = {0, CAN_EFF_FLAG,
+ CAN_RTR_FLAG, CAN_RTR_FLAG | CAN_EFF_FLAG,
+ 0, CAN_EFF_FLAG};
+#define RXOBJBASE 10
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+unsigned int bcr[MAXDEV] = { 0 }; /* bus configuration register */
+unsigned int cdv[MAXDEV] = { 0 }; /* CLKOUT clock divider */
+unsigned int mo15[MAXDEV] = { MO15_DEFLT, MO15_DEFLT }; /* msg obj 15 */
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int force_dmc = DEFAULT_FORCE_DMC;
+static int irq_mode = DEFAULT_IRQ_MODE;
+static int debug = 0;
+static int restart_ms = 100;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int bcr_n;
+static int cdv_n;
+static int mo15_n;
+static int rx_probe_n;
+
+static u8 dsc; /* devide system clock */
+static u8 dmc; /* devide memory clock */
+static unsigned long irqflags; /* for shared / disabled local interrupts */
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(bcr, int, &bcr_n, 0);
+module_param_array(cdv, int, &cdv_n, 0);
+module_param_array(mo15, int, &mo15_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(force_dmc, int, 0);
+module_param(irq_mode, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(bcr, "i82527 bus configuration register value (default: 0)");
+MODULE_PARM_DESC(cdv, "clockout devider value (0-14) (default: 0)");
+MODULE_PARM_DESC(mo15, "rx message object 15 usage. 0:none 1:sff(default) 2:eff");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(force_dmc, "set i82527 DMC bit (default: calculate from clk)");
+MODULE_PARM_DESC(irq_mode, "specify irq setup bits (1:shared 2:disable local irqs while processing) (default: 1)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+
+/* function declarations */
+
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+static int i82527_probe_chip(unsigned long base)
+{
+ // Check if hardware reset is still inactive OR
+ // maybe there is no chip in this address space
+ if (CANin(base, cpuInterfaceReg) & iCPU_RST) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (reset)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ // Write test pattern
+ CANout(base, message1Reg.dataReg[1], 0x25);
+ CANout(base, message2Reg.dataReg[3], 0x52);
+ CANout(base, message10Reg.dataReg[6], 0xc3);
+
+ // Read back test pattern
+ if ((CANin(base, message1Reg.dataReg[1]) != 0x25 ) ||
+ (CANin(base, message2Reg.dataReg[3]) != 0x52 ) ||
+ (CANin(base, message10Reg.dataReg[6]) != 0xc3 )) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (pattern)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ CANout(base, bitTiming0Reg, btr0);
+ CANout(base, bitTiming1Reg, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ if (dsc) /* devide system clock */
+ clock >>= 1; /* calculate BTR with this value */
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+static inline int obj2rxo(int obj)
+{
+ /* obj4 = obj15 SFF, obj5 = obj15 EFF */
+ if (obj < 4)
+ return RXOBJBASE + obj;
+ else
+ return 15;
+}
+
+void enable_rx_obj(unsigned long base, int obj)
+{
+ u8 mcfg = 0;
+ int rxo = obj2rxo(obj);
+
+ // Configure message object for receiption
+ if (rxobjflags[obj] & CAN_EFF_FLAG)
+ mcfg = MCFG_XTD;
+
+ if (rxobjflags[obj] & CAN_RTR_FLAG) {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg,
+ mcfg | MCFG_DIR);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | CPUU_SET | TXRQ_RES | RMPD_RES);
+ } else {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg, mcfg);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ }
+}
+
+void disable_rx_obj(unsigned long base, int obj)
+{
+ int rxo = obj2rxo(obj);
+
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Configure cpu interface
+ CANout(base, cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, iCTL_CCE | iCTL_INI);
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Clear message objects for receiption
+ if (priv->mo15 == MO15_SFF)
+ disable_rx_obj(base, 4); /* rx via obj15 SFF */
+ else
+ disable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF)
+ disable_rx_obj(base, 5); /* rx via obj15 EFF */
+ else
+ disable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ disable_rx_obj(base, 2);
+ disable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ return 0;
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Configure message objects for receiption
+ if (priv->mo15 == MO15_SFF) {
+ enable_rx_obj(base, 4); /* rx via obj15 SFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "SFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF) {
+ enable_rx_obj(base, 5); /* rx via obj15 EFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "EFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ enable_rx_obj(base, 2);
+ enable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ return 0;
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ return set_normal_mode(dev); /* for now */
+}
+
+/*
+ * Clear and invalidate message objects
+ */
+int i82527_clear_msg_objects(unsigned long base)
+{
+ int i;
+ int id;
+ int data;
+
+ for (i = 1; i <= 15; i++) {
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_UNC | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_RES | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ for (data = 0; data < 8; data++)
+ CANout(base, msgArr[i].messageReg.dataReg[data], 0);
+ for (id = 0; id < 4; id++)
+ CANout(base, msgArr[i].messageReg.idReg[id], 0);
+ CANout(base, msgArr[i].messageReg.messageConfigReg, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * initialize I82527 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, (iCTL_CCE | iCTL_INI));
+
+ // Set CLKOUT devider and slew rates is was done in i82527_init_module
+
+ // Bus configuration was done in i82527_init_module
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ i82527_clear_msg_objects(base);
+
+ // Set all global ID masks to "don't care"
+ CANout(base, globalMaskStandardReg[0], 0);
+ CANout(base, globalMaskStandardReg[1], 0);
+ CANout(base, globalMaskExtendedReg[0], 0);
+ CANout(base, globalMaskExtendedReg[1], 0);
+ CANout(base, globalMaskExtendedReg[2], 0);
+ CANout(base, globalMaskExtendedReg[3], 0);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ // Note: At this stage the CAN ship is still in bus-off condition
+ // and must be started using StartChip()
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t dlc;
+ uint8_t rtr;
+ canid_t id;
+ int i;
+
+ if ((CANin(base, message1Reg.msgCtrl1Reg) & TXRQ_UNC) == TXRQ_SET) {
+ printk(KERN_ERR "%s: %s: TX register is occupied!\n",
+ dev->name, drv_name);
+ return 0;
+ }
+
+ netif_stop_queue(dev);
+
+ dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if ( cf->can_id & CAN_RTR_FLAG )
+ rtr = 0;
+ else
+ rtr = MCFG_DIR;
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_SET | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_SET | TXIE_SET | RXIE_RES | INTPD_RES);
+
+ if (id & CAN_EFF_FLAG) {
+ id &= CAN_EFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ (dlc << 4) + rtr + MCFG_XTD);
+ CANout(base, message1Reg.idReg[3], (id << 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[2], (id >> 5) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id >> 13) & 0xFFU);
+ CANout(base, message1Reg.idReg[0], (id >> 21) & 0xFFU);
+ }
+ else {
+ id &= CAN_SFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ ( dlc << 4 ) + rtr);
+ CANout(base, message1Reg.idReg[0], (id >> 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id << 5) & 0xFFU);
+ }
+
+ dlc &= 0x0f; //restore length only
+ for ( i=0; i < dlc; i++ ) {
+ CANout(base, message1Reg.dataReg[i],
+ cf->data[i]);
+ }
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ (RMPD_RES | TXRQ_SET | CPUU_RES | NEWD_UNC));
+
+ // HM: We had some cases of repeated IRQs
+ // so make sure the INT is acknowledged
+ // I know it's already further up, but doing again fixed the issue
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+# if 0
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+/*
+ * Subroutine of ISR for RX interrupts.
+ *
+ */
+static void can_rx(struct net_device *dev, int obj)
+{
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t msgctlreg;
+ uint8_t ctl1reg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+ int rxo = obj2rxo(obj);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ msgctlreg = CANin(base, msgArr[rxo].messageReg.messageConfigReg);
+
+ if( msgctlreg & MCFG_XTD ) {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[3])
+ | (CANin(base, msgArr[rxo].messageReg.idReg[2]) << 8)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[1]) << 16)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[0]) << 24);
+ id >>= 3;
+ id |= CAN_EFF_FLAG;
+ } else {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[1])
+ |(CANin(base, msgArr[rxo].messageReg.idReg[0]) << 8);
+ id >>= 5;
+ }
+
+ if (ctl1reg & RMPD_SET) {
+ id |= CAN_RTR_FLAG;
+ }
+
+ msgctlreg &= 0xf0;/* strip length code */
+ dlc = msgctlreg >> 4;
+ dlc %= 9; /* limit count to 8 bytes */
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = CANin(base, msgArr[rxo].messageReg.dataReg[i]);
+ }
+
+ // Make the chip ready to receive the next message
+ enable_rx_obj(base, obj);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * I82527 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ uint8_t irqreg;
+ uint8_t lastIrqreg;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ //chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode!\n",
+ dev->name, __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+
+ // Read the highest pending interrupt request
+ irqreg = CANin(base, interruptReg);
+ lastIrqreg = irqreg;
+
+ while ( irqreg ) {
+ n++;
+ switch (irqreg) {
+
+ case 1: // Status register
+ {
+ uint8_t status;
+
+ // Read the STATUS reg
+ status = CANin(base, statusReg);
+ CANout (base, statusReg, 0);
+
+ if ( status & iSTAT_RXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_RXOK);
+ }
+ if ( status & iSTAT_TXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_TXOK);
+ }
+ if ( status & iSTAT_WARN ) {
+ // Note: status bit is read-only, don't clear
+ /* error warning interrupt */
+ iDBG(KERN_INFO "%s: error warning\n",
+ dev->name);
+ priv->can_stats.error_warning++;
+ }
+ if ( status & iSTAT_BOFF ) {
+ uint8_t flags;
+
+ // Note: status bit is read-only, don't clear
+
+ priv->can_stats.bus_error++;
+
+ // Clear init flag and reenable interrupts
+ flags = CANin(base, controlReg) |
+ ( iCTL_IE | iCTL_EIE );
+
+ flags &= ~iCTL_INI; // Reset init flag
+ CANout(base, controlReg, flags);
+ }
+ }
+ break;
+
+ case 0x2: // Receiption, message object 15
+ {
+ uint8_t ctl1reg;
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+
+ if (priv->mo15 == MO15_SFF)
+ can_rx(dev, 4); /* rx via obj15 SFF */
+ else
+ can_rx(dev, 5); /* rx via obj15 EFF */
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xC: // Receiption, message object 10
+ case 0xD: // Receiption, message object 11
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl1reg;
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_UNC | RMPD_UNC);
+ can_rx(dev, obj);
+ ctl1reg = CANin(base,
+ msgArr[rxo].messageReg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xE: // Receiption, message object 12 (RTR)
+ case 0xF: // Receiption, message object 13 (RTR)
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl0reg;
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ while (ctl0reg & INTPD_SET) {
+ can_rx(dev, obj);
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 3: // Message object 1 (our write object)
+ /* transmission complete interrupt */
+
+ // Nothing more to send, switch off interrupts
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES));
+ // We had some cases of repeated IRQ
+ // so make sure the INT is acknowledged
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_packets++;
+ netif_wake_queue(dev);
+ break;
+
+ default: // Unexpected
+ iDBG(KERN_INFO "%s: Unexpected i82527 interrupt: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+ break;
+ }
+
+ // Get irq status again for next loop iteration
+ irqreg = CANin(base, interruptReg);
+ if (irqreg == lastIrqreg)
+ iDBG(KERN_INFO "%s: i82527 interrupt repeated: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+
+ lastIrqreg = irqreg;
+ } /* end while (irqreq) */
+
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, irqflags,
+ dev->name, (void*)dev))
+ return -EAGAIN;
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+ dev->header_ops = NULL;
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ const char mo15mode [3][6] = {"none", "sff", "eff"};
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d / mo15 %s\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num],
+ mo15mode[mo15[dev_num]]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->mo15 = mo15[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void i82527_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int i82527_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((sizeof(canmessage_t) != 15) || (sizeof(canregs_t) != 256)) {
+ printk(KERN_WARNING "%s sizes: canmessage_t %d canregs_t %d\n",
+ CHIP_NAME, (int)sizeof(canmessage_t),
+ (int)sizeof(canregs_t));
+ return -EBUSY;
+ }
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+ printk(KERN_INFO "%s - options [force_dmc %d] [irq_mode %d]\n",
+ drv_name, force_dmc, irq_mode);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ /* to ensure the proper access to the i82527 registers */
+ /* the timing dependend settings have to be done first */
+ if (clk > 10000000)
+ dsc = iCPU_DSC; /* devide system clock => MCLK is 8MHz save */
+ else if (clk > 8000000) /* 8MHz < clk <= 10MHz */
+ dmc = iCPU_DMC; /* devide memory clock */
+
+ /* devide memory clock even if it's not needed (regarding the spec) */
+ if (force_dmc)
+ dmc = iCPU_DMC;
+
+ if (irq_mode & IRQ_MODE_SHARED)
+ irqflags |= IRQF_SHARED;
+ if (irq_mode & IRQ_MODE_DISABLE_LOCAL_IRQS)
+ irqflags |= IRQF_DISABLED;
+
+ for (i = 0; base[i]; i++) {
+ int clkout;
+ u8 clockdiv;
+
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, I82527_IO_SIZE, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ i82527_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ // Enable configuration, put chip in bus-off, disable ints
+ CANout(rbase[i], controlReg, iCTL_CCE | iCTL_INI);
+
+ // Configure cpu interface / CLKOUT disable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc));
+
+ if (!i82527_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ i82527_exit_module();
+ return -ENODEV;
+ }
+
+ /* CLKOUT devider and slew rate calculation */
+ if ((cdv[i] < 0) || (cdv[i] > 14)) {
+ printk(KERN_WARNING "%s: adjusted cdv[%d]=%d to 0.\n",
+ drv_name, i, cdv[i]);
+ cdv[i] = 0;
+ }
+
+ clkout = clk / (cdv[i] + 1); /* CLKOUT frequency */
+ clockdiv = (u8)cdv[i]; /* devider value (see i82527 spec) */
+
+ if (clkout <= 16000000) {
+ clockdiv |= iCLK_SL1;
+ if (clkout <= 8000000)
+ clockdiv |= iCLK_SL0;
+ } else if (clkout <= 24000000)
+ clockdiv |= iCLK_SL0;
+
+ // Set CLKOUT devider and slew rates
+ CANout(rbase[i], clkOutReg, clockdiv);
+
+ // Configure cpu interface / CLKOUT enable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ CANout(rbase[i], busConfigReg, bcr[i]);
+
+ dev = can_create_netdev(i, I82527_IO_SIZE);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ }
+ }
+ return 0;
+}
+
+module_init(i82527_init_module);
+module_exit(i82527_exit_module);
+
diff --git a/drivers/net/can/old/i82527/i82527.h b/drivers/net/can/old/i82527/i82527.h
new file mode 100644
index 000000000000..0484b21406f9
--- /dev/null
+++ b/drivers/net/can/old/i82527/i82527.h
@@ -0,0 +1,300 @@
+/*
+ * i82527.h - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Original version Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ *
+ * Major Refactoring and Integration into can4linux version 3.1 by
+ * Henrik W Maier of FOCUS Software Engineering Pty Ltd <www.focus-sw.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef I82527_H
+#define I82527_H
+
+#define I82527_IO_SIZE 0x100
+
+#define CHIP_NAME "i82527"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+#define DEFAULT_FORCE_DMC 0 /* for critical register access, e.g. ser1274 */
+
+#define IRQ_MODE_SHARED 1 /* enable shared interrupts */
+#define IRQ_MODE_DISABLE_LOCAL_IRQS 2 /* when processing the irq handler */
+#define DEFAULT_IRQ_MODE IRQ_MODE_SHARED
+
+/* The message object 15 has a shadow register for reliable data receiption */
+/* under heavy bus load. Therefore it makes sense to use this message object */
+/* (mo15) for the needed use case. The frame type (EFF/SFF) for the mo15 can */
+/* be defined on the module command line. The default is 11 bit SFF format. */
+
+#define MO15_NONE 0
+#define MO15_SFF 1
+#define MO15_EFF 2
+
+#define MO15_DEFLT MO15_SFF /* the default */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 62
+#define JUMPWIDTH 0x40
+
+typedef struct canmessage {
+ uint8_t msgCtrl0Reg;
+ uint8_t msgCtrl1Reg;
+ uint8_t idReg[4];
+ uint8_t messageConfigReg;
+ uint8_t dataReg[8];
+} canmessage_t; // __attribute__ ((packed));
+
+typedef struct canregs {
+ union
+ {
+ struct
+ {
+ canmessage_t messageReg;
+ uint8_t someOtherReg; // padding
+ } msgArr[16];
+ struct
+ {
+ uint8_t controlReg; // Control Register
+ uint8_t statusReg; // Status Register
+ uint8_t cpuInterfaceReg; // CPU Interface Register
+ uint8_t reserved1Reg;
+ uint8_t highSpeedReadReg[2]; // High Speed Read
+ uint8_t globalMaskStandardReg[2]; // Standard Global Mask byte 0
+ uint8_t globalMaskExtendedReg[4]; // Extended Global Mask bytes
+ uint8_t message15MaskReg[4]; // Message 15 Mask bytes
+ canmessage_t message1Reg;
+ uint8_t clkOutReg; // Clock Out Register
+ canmessage_t message2Reg;
+ uint8_t busConfigReg; // Bus Configuration Register
+ canmessage_t message3Reg;
+ uint8_t bitTiming0Reg; // Bit Timing Register byte 0
+ canmessage_t message4Reg;
+ uint8_t bitTiming1Reg; // Bit Timing Register byte 1
+ canmessage_t message5Reg;
+ uint8_t interruptReg; // Interrupt Register
+ canmessage_t message6Reg;
+ uint8_t reserved2Reg;
+ canmessage_t message7Reg;
+ uint8_t reserved3Reg;
+ canmessage_t message8Reg;
+ uint8_t reserved4Reg;
+ canmessage_t message9Reg;
+ uint8_t p1ConfReg;
+ canmessage_t message10Reg;
+ uint8_t p2ConfReg;
+ canmessage_t message11Reg;
+ uint8_t p1InReg;
+ canmessage_t message12Reg;
+ uint8_t p2InReg;
+ canmessage_t message13Reg;
+ uint8_t p1OutReg;
+ canmessage_t message14Reg;
+ uint8_t p2OutReg;
+ canmessage_t message15Reg;
+ uint8_t serialResetAddressReg;
+ };
+ };
+} canregs_t; // __attribute__ ((packed));
+
+/* Control Register (0x00) */
+enum i82527_iCTL {
+ iCTL_INI = 1, // Initialization
+ iCTL_IE = 1<<1, // Interrupt Enable
+ iCTL_SIE = 1<<2, // Status Interrupt Enable
+ iCTL_EIE = 1<<3, // Error Interrupt Enable
+ iCTL_CCE = 1<<6 // Change Configuration Enable
+};
+
+/* Status Register (0x01) */
+enum i82527_iSTAT {
+ iSTAT_TXOK = 1<<3, // Transmit Message Successfully
+ iSTAT_RXOK = 1<<4, // Receive Message Successfully
+ iSTAT_WAKE = 1<<5, // Wake Up Status
+ iSTAT_WARN = 1<<6, // Warning Status
+ iSTAT_BOFF = 1<<7 // Bus Off Status
+};
+
+/* CPU Interface Register (0x02) */
+enum i82527_iCPU {
+ iCPU_CEN = 1, // Clock Out Enable
+ iCPU_MUX = 1<<2, // Multiplex
+ iCPU_SLP = 1<<3, // Sleep
+ iCPU_PWD = 1<<4, // Power Down Mode
+ iCPU_DMC = 1<<5, // Divide Memory Clock
+ iCPU_DSC = 1<<6, // Divide System Clock
+ iCPU_RST = 1<<7, // Hardware Reset Status
+};
+
+/* Clock Out Register (0x1f) */
+enum i82527_iCLK {
+ iCLK_CD0 = 1, // Clock Divider bit 0
+ iCLK_CD1 = 1<<1,
+ iCLK_CD2 = 1<<2,
+ iCLK_CD3 = 1<<3,
+ iCLK_SL0 = 1<<4, // Slew Rate bit 0
+ iCLK_SL1 = 1<<5
+};
+
+/* Bus Configuration Register (0x2f) */
+enum i82527_iBUS {
+ iBUS_DR0 = 1, // Disconnect RX0 Input
+ iBUS_DR1 = 1<<1, // Disconnect RX1 Input
+ iBUS_DT1 = 1<<3, // Disconnect TX1 Output
+ iBUS_POL = 1<<5, // Polarity
+ iBUS_CBY = 1<<6 // Comparator Bypass
+};
+
+#define RESET 1 // Bit Pair Reset Status
+#define SET 2 // Bit Pair Set Status
+#define UNCHANGED 3 // Bit Pair Unchanged
+
+/* Message Control Register 0 (Base Address + 0x0) */
+enum i82527_iMSGCTL0 {
+ INTPD_SET = SET, // Interrupt pending
+ INTPD_RES = RESET, // No Interrupt pending
+ INTPD_UNC = UNCHANGED,
+ RXIE_SET = SET<<2, // Receive Interrupt Enable
+ RXIE_RES = RESET<<2, // Receive Interrupt Disable
+ RXIE_UNC = UNCHANGED<<2,
+ TXIE_SET = SET<<4, // Transmit Interrupt Enable
+ TXIE_RES = RESET<<4, // Transmit Interrupt Disable
+ TXIE_UNC = UNCHANGED<<4,
+ MVAL_SET = SET<<6, // Message Valid
+ MVAL_RES = RESET<<6, // Message Invalid
+ MVAL_UNC = UNCHANGED<<6
+};
+
+/* Message Control Register 1 (Base Address + 0x01) */
+enum i82527_iMSGCTL1 {
+ NEWD_SET = SET, // New Data
+ NEWD_RES = RESET, // No New Data
+ NEWD_UNC = UNCHANGED,
+ MLST_SET = SET<<2, // Message Lost
+ MLST_RES = RESET<<2, // No Message Lost
+ MLST_UNC = UNCHANGED<<2,
+ CPUU_SET = SET<<2, // CPU Updating
+ CPUU_RES = RESET<<2, // No CPU Updating
+ CPUU_UNC = UNCHANGED<<2,
+ TXRQ_SET = SET<<4, // Transmission Request
+ TXRQ_RES = RESET<<4, // No Transmission Request
+ TXRQ_UNC = UNCHANGED<<4,
+ RMPD_SET = SET<<6, // Remote Request Pending
+ RMPD_RES = RESET<<6, // No Remote Request Pending
+ RMPD_UNC = UNCHANGED<<6
+};
+
+/* Message Configuration Register (Base Address + 0x06) */
+enum i82527_iMSGCFG {
+ MCFG_XTD = 1<<2, // Extended Identifier
+ MCFG_DIR = 1<<3 // Direction is Transmit
+};
+
+#undef IOPRINT
+#undef IODEBUG
+
+#ifdef IOPRINT
+#define CANout(base,adr,v) \
+ printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v)
+
+#define CANin(base,adr) \
+ printk("CANin: (%lx+%x)\n", base, (int)(long)&((canregs_t *)0)->adr)
+
+#else /* IOPRINT */
+
+#ifdef IODEBUG
+#define CANout(base,adr,v) \
+ (printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v),\
+ hw_writereg(base, (int)(long)&((canregs_t *)0)->adr, v))
+#else
+#define CANout(base,adr,v) hw_writereg(base,\
+ (int)(long)&((canregs_t *)0)->adr, v)
+#endif
+
+#define CANin(base,adr) hw_readreg(base, (int)(long)&((canregs_t *)0)->adr)
+
+#endif /* IOPRINT */
+
+/* CAN private data structure */
+
+struct can_priv {
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ int mo15;
+ struct timer_list timer;
+ int state;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* I82527_H */
diff --git a/drivers/net/can/old/i82527/proc.c b/drivers/net/can/old/i82527/proc.c
new file mode 100644
index 000000000000..e11628fe387c
--- /dev/null
+++ b/drivers/net/can/old/i82527/proc.c
@@ -0,0 +1,209 @@
+/*
+ * proc.c - proc file system functions for I82527 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "i82527.h"
+#include "hal.h"
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
diff --git a/drivers/net/can/old/mscan/Makefile b/drivers/net/can/old/mscan/Makefile
new file mode 100644
index 000000000000..854012e9174d
--- /dev/null
+++ b/drivers/net/can/old/mscan/Makefile
@@ -0,0 +1,21 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_MPC52XX_OLD) += mscan-mpc52xx-old.o
+
+mscan-mpc52xx-old-objs := mscan.o mpc52xx_can.o
+
+endif
diff --git a/drivers/net/can/old/mscan/mpc52xx_can.c b/drivers/net/can/old/mscan/mpc52xx_can.c
new file mode 100644
index 000000000000..da1278fa630a
--- /dev/null
+++ b/drivers/net/can/old/mscan/mpc52xx_can.c
@@ -0,0 +1,248 @@
+/*
+ * DESCRIPTION:
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2005, Varma Electronics Oy
+ *
+ * LICENCE:
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * HISTORY:
+ * 2005-02-03 created
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+
+#define PDEV_MAX 2
+
+struct platform_device *pdev[PDEV_MAX];
+
+static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct net_device *dev;
+ struct mscan_platform_data *pdata = pdev->dev.platform_data;
+ struct can_priv *can;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ if (!pdata)
+ return ret;
+
+ dev = alloc_mscandev();
+ if (!dev)
+ return -ENOMEM;
+ can = netdev_priv(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ can->can_sys_clock = pdata->clock_frq;
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = register_mscandev(dev, pdata->clock_src);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+ fail_map:
+ release_mem_region(mem->start, mem_size);
+ req_error:
+ free_candev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_mscandev(dev);
+
+ iounmap((volatile void __iomem *)dev->base_addr);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_candev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+#endif
+
+static struct platform_driver mpc52xx_can_driver = {
+ .driver = {
+ .name = "mpc52xx-mscan",
+ },
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+#endif
+};
+
+#ifdef CONFIG_PPC_MERGE
+static int __init mpc52xx_of_to_pdev(void)
+{
+ struct device_node *np = NULL;
+ unsigned int i;
+ int err = -ENODEV;
+
+ for (i = 0;
+ (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
+ i++) {
+ struct resource r[2] = { };
+ struct mscan_platform_data pdata;
+
+ if (i >= PDEV_MAX) {
+ printk(KERN_WARNING "%s: increase PDEV_MAX for more "
+ "than %i devices\n", __func__, PDEV_MAX);
+ break;
+ }
+
+ err = of_address_to_resource(np, 0, &r[0]);
+ if (err)
+ break;
+
+ of_irq_to_resource(np, 0, &r[1]);
+
+ pdev[i] =
+ platform_device_register_simple("mpc52xx-mscan", i, r, 2);
+ if (IS_ERR(pdev[i])) {
+ err = PTR_ERR(pdev[i]);
+ break;
+ }
+
+ pdata.clock_src = MSCAN_CLKSRC_BUS;
+ pdata.clock_frq = mpc52xx_find_ipb_freq(np);
+ err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
+ if (err)
+ break;
+ }
+ return err;
+}
+#else
+#define mscan_of_to_pdev()
+#endif
+
+int __init mpc52xx_can_init(void)
+{
+ printk(KERN_WARNING
+ "This %s driver is DEPRECATED, please switch!\n",
+ mpc52xx_can_driver.driver.name);
+#ifdef CONFIG_PPC_MERGE
+ int err = mpc52xx_of_to_pdev();
+
+ if (err) {
+ printk(KERN_ERR "%s init failed with err=%d\n",
+ mpc52xx_can_driver.driver.name, err);
+ return err;
+ }
+#endif
+ return platform_driver_register(&mpc52xx_can_driver);
+}
+
+void __exit mpc52xx_can_exit(void)
+{
+ int i;
+ platform_driver_unregister(&mpc52xx_can_driver);
+ for (i = 0; i < PDEV_MAX; i++)
+ platform_device_unregister(pdev[i]);
+ printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
+}
+
+module_init(mpc52xx_can_init);
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/old/mscan/mscan.c b/drivers/net/can/old/mscan/mscan.c
new file mode 100644
index 000000000000..05afeae1e2fe
--- /dev/null
+++ b/drivers/net/can/old/mscan/mscan.c
@@ -0,0 +1,708 @@
+/*
+ * mscan.c
+ *
+ * DESCRIPTION:
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2005-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <asm/io.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "mscan.h"
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+typedef struct {
+ struct list_head list;
+ u8 mask;
+} tx_queue_entry_t;
+
+struct mscan_priv {
+ struct can_priv can;
+ volatile unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ tx_queue_entry_t tx_queue[TX_QUEUE_SIZE];
+ struct napi_struct napi;
+ struct net_device *dev;
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+ canctl1 = in_8(&regs->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(&regs->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(&regs->canctl0, in_8(&regs->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(&regs->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ }
+ return ret;
+}
+
+static void mscan_push_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ state->mode = in_8(&regs->canctl0) & (MSCAN_SLPRQ | MSCAN_INITRQ |
+ MSCAN_CSWAI);
+ state->canrier = in_8(&regs->canrier);
+ state->cantier = in_8(&regs->cantier);
+}
+
+static int mscan_pop_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ int ret;
+ ret = mscan_set_mode(dev, state->mode);
+ if (!ret) {
+ out_8(&regs->canrier, state->canrier);
+ out_8(&regs->cantier, state->cantier);
+ }
+ return ret;
+}
+
+static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ dev_dbg(ND2D(dev), "%s\n", __FUNCTION__);
+ out_8(&regs->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(&regs->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ dev_dbg(ND2D(dev), "sending extended frame\n");
+
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(&regs->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ dev_dbg(ND2D(dev), "sending standard frame\n");
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(&regs->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ volatile void __iomem *data = &regs->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->tx.dlr, frame->can_dlc);
+ out_8(&regs->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(&regs->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ kfree_skb(skb);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(&regs->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static void mscan_tx_timeout(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame;
+ u8 mask;
+
+ printk("%s\n", __FUNCTION__);
+
+ out_8(&regs->cantier, 0);
+
+ mask = list_entry(priv->tx_head.next, tx_queue_entry_t, list)->mask;
+ dev->trans_start = jiffies;
+ out_8(&regs->cantarq, mask);
+ out_8(&regs->cantier, priv->tx_active);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "TIMEOUT packet dropped\n");
+ return;
+ }
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_TX_TIMEOUT;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ netif_rx(skb);
+
+}
+
+static can_state_t state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ can_state_t state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state =
+ state_map[max(MSCAN_STATE_RX(canrflg), MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ netif_carrier_off(dev);
+ else if (priv->can.state == CAN_STATE_BUS_OFF
+ && state != CAN_STATE_BUS_OFF)
+ netif_carrier_on(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(&regs->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(&regs->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb,
+ sizeof(struct can_frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(&regs->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(&regs->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ volatile void __iomem *data = &regs->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ dev_dbg(ND2D(dev),
+ "received pkt: id: %u dlc: %u data: ",
+ frame->can_id, frame->can_dlc);
+#ifdef DEBUG
+ for (i = 0;
+ i < frame->can_dlc && !(frame->can_id &
+ CAN_FLAG_RTR); i++)
+ printk("%2x ", frame->data[i]);
+ printk("\n");
+#endif
+
+ out_8(&regs->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(&regs->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+ if (!(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+ netif_rx_complete(dev, &priv->napi);
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ out_8(&regs->canrier,
+ in_8(&regs->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+ ret = 0;
+ }
+ return ret;
+}
+
+
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ if ((cantier = in_8(&regs->cantier) & MSCAN_TXE) &&
+ (cantflg = in_8(&regs->cantflg) & cantier)) {
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ tx_queue_entry_t *entry =
+ list_entry(pos, tx_queue_entry_t, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ if (in_8(&regs->cantaak) & mask) {
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+ } else {
+ out_8(&regs->cantbsel, mask);
+ stats->tx_bytes +=
+ in_8(&regs->tx.dlr);
+ stats->tx_packets++;
+ }
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(&regs->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ if ((((canrflg = in_8(&regs->canrflg)) & ~MSCAN_STAT_MSK)) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (check_set_state(dev, canrflg)) {
+ out_8(&regs->canrflg, MSCAN_CSCIF);
+ ret = IRQ_HANDLED;
+ }
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(&regs->canrier);
+ out_8(&regs->canrier, 0);
+ netif_rx_schedule(dev, &priv->napi);
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, can_mode_t mode)
+{
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ printk("%s: CAN_MODE_START requested\n", __FUNCTION__);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int mscan_do_set_bit_time(struct net_device *dev,
+ struct can_bittime *bt)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ u8 reg;
+ struct mscan_state state;
+
+ if (bt->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ mscan_push_state(dev, &state);
+ ret = mscan_set_mode(dev, MSCAN_INIT_MODE);
+ if (!ret) {
+ reg = BTR0_SET_BRP(bt->std.brp) | BTR0_SET_SJW(bt->std.sjw);
+ out_8(&regs->canbtr0, reg);
+
+ reg = (BTR1_SET_TSEG1(bt->std.prop_seg + bt->std.phase_seg1) |
+ BTR1_SET_TSEG2(bt->std.phase_seg2) |
+ BTR1_SET_SAM(bt->std.sam));
+ out_8(&regs->canbtr1, reg);
+
+ ret = mscan_pop_state(dev, &state);
+ }
+
+ spin_unlock_irq(&priv->can.irq_lock);
+ return ret;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ napi_enable(&priv->napi);
+ ret = request_irq(dev->irq, mscan_isr, IRQF_SHARED, dev->name, dev);
+
+ if (ret < 0) {
+ napi_disable(&priv->napi);
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(&regs->canidar1_0, 0);
+ out_be16(&regs->canidar3_2, 0);
+ out_be16(&regs->canidar5_4, 0);
+ out_be16(&regs->canidar7_6, 0);
+
+ out_be16(&regs->canidmr1_0, 0xffff);
+ out_be16(&regs->canidmr3_2, 0xffff);
+ out_be16(&regs->canidmr5_4, 0xffff);
+ out_be16(&regs->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+
+ priv->shadow_statflg = in_8(&regs->canrflg) & MSCAN_STAT_MSK;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+
+ out_8(&regs->cantier, 0);
+ /* Enable receive interrupts. */
+ out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ /* disable interrupts */
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ free_irq(dev->irq, dev);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(&regs->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(&regs->canctl1, ctl1);
+ udelay(100);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_netdev(dev);
+}
+
+EXPORT_SYMBOL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_CANE);
+ unregister_netdev(dev);
+}
+
+EXPORT_SYMBOL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->watchdog_timeo = MSCAN_WATCHDOG_TIMEOUT;
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_hard_start_xmit;
+ dev->tx_timeout = mscan_tx_timeout;
+
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+
+ priv->can.do_set_bittime = mscan_do_set_bit_time;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++)
+ priv->tx_queue[i].mask = 1 << i;
+
+ return dev;
+}
+
+EXPORT_SYMBOL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
diff --git a/drivers/net/can/old/mscan/mscan.h b/drivers/net/can/old/mscan/mscan.h
new file mode 100644
index 000000000000..807ec3fae437
--- /dev/null
+++ b/drivers/net/can/old/mscan/mscan.h
@@ -0,0 +1,247 @@
+/*
+ * DESCRIPTION:
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/autoconf.h>
+#include <asm/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define _MSCAN_RESERVED_(n,num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n,num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef CONFIG_PPC_MPC52xx
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+#define MSCAN_WATCHDOG_TIMEOUT ((500*HZ)/1000)
+
+struct mscan_platform_data {
+ u8 clock_src; /* MSCAN_CLKSRC_BUS or MSCAN_CLKSRC_XTAL */
+ u32 clock_frq; /* can ref. clock, in Hz */
+};
+
+struct net_device *alloc_mscandev(void);
+/* @clock_src:
+ 1 = The MSCAN clock source is the onchip Bus Clock.
+ 0 = The MSCAN clock source is the chip Oscillator Clock.
+*/
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
diff --git a/drivers/net/can/old/sja1000/Makefile b/drivers/net/can/old/sja1000/Makefile
new file mode 100644
index 000000000000..cc8f1f917e99
--- /dev/null
+++ b/drivers/net/can/old/sja1000/Makefile
@@ -0,0 +1,27 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := sja1000-io.o sja1000-iomem.o sja1000-iomux.o sja1000-gw2.o sja1000-esdio.o sja1000-c200.o
+
+sja1000-io-objs := sja1000.o proc.o ../hal/io.o
+sja1000-iomem-objs := sja1000.o proc.o ../hal/iomem.o
+sja1000-iomux-objs := sja1000.o proc.o ../hal/iomux.o
+sja1000-gw2-objs := sja1000.o proc.o ../hal/gw2.o
+sja1000-esdio-objs := sja1000.o proc.o ../hal/esdio.o
+sja1000-c200-objs := sja1000.o proc.o ../hal/c200.o
+
+endif
diff --git a/drivers/net/can/old/sja1000/proc.c b/drivers/net/can/old/sja1000/proc.c
new file mode 100644
index 000000000000..b98aa6180784
--- /dev/null
+++ b/drivers/net/can/old/sja1000/proc.c
@@ -0,0 +1,230 @@
+/*
+ * proc.c - proc file system functions for SJA1000 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "sja1000.h"
+#include "hal.h"
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+#ifdef SJA1000_H
+ u8 stat = hw_readreg(dev->base_addr, REG_SR);
+
+ if (stat & 0x80) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: "
+ "BUS OFF, ", dev->name);
+ } else if (stat & 0x40) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: ERROR "
+ "PASSIVE, ", dev->name);
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: OK, ",
+ dev->name);
+ }
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "RXERR: %d, TXERR: %d\n",
+ hw_readreg(dev->base_addr, REG_RXERR),
+ hw_readreg(dev->base_addr, REG_TXERR));
+#endif
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
diff --git a/drivers/net/can/old/sja1000/sja1000.c b/drivers/net/can/old/sja1000/sja1000.c
new file mode 100644
index 000000000000..8d7185a2962b
--- /dev/null
+++ b/drivers/net/can/old/sja1000/sja1000.c
@@ -0,0 +1,1140 @@
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "sja1000.h"
+#include "hal.h"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.1.1";
+static const char *drv_reldate = "2007-04-13";
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int debug = 0;
+static int restart_ms = 100;
+static int echo = 1;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int rx_probe_n;
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+module_param(echo, int, S_IRUGO);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+MODULE_PARM_DESC(echo, "Echo sent frames. default: 1 (On)");
+
+/*
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented inside this sja1000 driver. In the case that the driver does
+ * not support the echo the IFF_ECHO remains clear in dev->flags.
+ * This causes the PF_CAN core to perform the echo as a fallback solution.
+ */
+
+/* function declarations */
+
+static void can_restart_dev(unsigned long data);
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+static int sja1000_probe_chip(unsigned long base)
+{
+ if (base && (hw_readreg(base, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ drv_name, base);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ hw_writereg(dev->base_addr, REG_BTR0, btr0);
+ hw_writereg(dev->base_addr, REG_BTR1, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ clock >>= 1;
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ priv->can_stats.bus_error_at_init = priv->can_stats.bus_error;
+
+ /* disable interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+ priv->state = STATE_RESET_MODE;
+ return 0;
+ }
+
+ hw_writereg(dev->base_addr, REG_MOD, MOD_RM); /* reset chip */
+ status = hw_readreg(dev->base_addr, REG_MOD);
+
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into reset mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ hw_writereg(dev->base_addr, REG_MOD, 0x00);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into normal mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset mode bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set listen only mode, clear reset */
+ hw_writereg(dev->base_addr, REG_MOD, MOD_LOM);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into listen mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* go into Pelican mode, disable clkout, disable comparator */
+ hw_writereg(base, REG_CDR, 0xCF);
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+
+ /* set acceptance filter (accept all) */
+ hw_writereg(base, REG_ACCC0, 0x00);
+ hw_writereg(base, REG_ACCC1, 0x00);
+ hw_writereg(base, REG_ACCC2, 0x00);
+ hw_writereg(base, REG_ACCC3, 0x00);
+
+ hw_writereg(base, REG_ACCM0, 0xFF);
+ hw_writereg(base, REG_ACCM1, 0xFF);
+ hw_writereg(base, REG_ACCM2, 0xFF);
+ hw_writereg(base, REG_ACCM3, 0xFF);
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev)) {
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev);
+ }
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ /* enable receive and error interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_RI | IRQ_EI);
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ /* enable all interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_ALL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int loop;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ hw_writereg(base, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ hw_writereg(base, REG_ID3, (id & 0x00001fe0) >> 5);
+ hw_writereg(base, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x000007f8) >> 3);
+ hw_writereg(base, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++) {
+ hw_writereg(base, dreg++, cf->data[i]);
+ }
+
+ hw_writereg(base, REG_CMR, CMD_TR);
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ /* set flag whether this packet has to be looped back */
+ loop = skb->pkt_type == PACKET_LOOPBACK;
+
+ if (!echo || !loop) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!priv->echo_skb) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ DBG(KERN_INFO "%s: %s: freeing old skbuff %p, "
+ "using new skbuff %p\n",
+ dev->name, __FUNCTION__, old_skb, skb);
+ kfree_skb(old_skb);
+ if (!skb) {
+ return 0;
+ }
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb = skb;
+
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: occupied echo_skb!\n",
+ dev->name, __FUNCTION__ );
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+static void can_rx(struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = hw_readreg(base, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << (5+16))
+ | (hw_readreg(base, REG_ID2) << (5+8))
+ | (hw_readreg(base, REG_ID3) << 5)
+ | (hw_readreg(base, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << 3)
+ | (hw_readreg(base, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = hw_readreg(base, dreg++);
+ }
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ hw_writereg(base, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * SJA1000 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ uint8_t isrc, status, ecc, alc;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __FUNCTION__, hw_readreg(base, REG_MOD),
+ hw_readreg(base, REG_IER), hw_readreg(base, REG_IR),
+ hw_readreg(base, REG_SR));
+ return IRQ_NONE;
+ }
+
+ while ((isrc = hw_readreg(base, REG_IR)) && (n < 20)) {
+ n++;
+ status = hw_readreg(base, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+
+ if (echo && priv->echo_skb) {
+ netif_rx(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+
+ while (status & SR_RBS) {
+ can_rx(dev);
+ status = hw_readreg(base, REG_SR);
+ }
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ iDBG(KERN_INFO "%s: RI #%d#\n", dev->name, n);
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ priv->can_stats.data_overrun++;
+ hw_writereg(base, REG_CMR, CMD_CDO); /* clear bit */
+ }
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can_stats.error_warning++;
+ if (status & SR_BS) {
+ printk(KERN_INFO "%s: BUS OFF, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ }
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can_stats.bus_error -
+ priv->can_stats.bus_error_at_init);
+ priv->can_stats.bus_error++;
+ ecc = hw_readreg(base, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ /* when the bus errors flood the system, */
+ /* restart the controller */
+ if (priv->can_stats.bus_error_at_init +
+ MAX_BUS_ERRORS < priv->can_stats.bus_error) {
+ iDBG(KERN_INFO "%s: heavy bus errors,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#if 1
+ /* don't know, if this is a good idea, */
+ /* but it works fine ... */
+ if (hw_readreg(base, REG_RXERR) > 128) {
+ iDBG(KERN_INFO "%s: RX_ERR > 128,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#endif
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: -> ERROR PASSIVE, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else {
+ iDBG(KERN_INFO "%s: -> ERROR ACTIVE\n",
+ dev->name);
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ priv->can_stats.arbitration_lost++;
+ alc = hw_readreg(base, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ }
+ }
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+out:
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, IRQF_SHARED,
+ dev->name, (void*)dev)) {
+ return -EAGAIN;
+ }
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+#if 0
+static void test_if(struct net_device *dev)
+{
+ int i;
+ int j;
+ int x;
+
+ hw_writereg(base, REG_CDR, 0xCF);
+ for (i = 0; i < 10000; i++) {
+ for (j = 0; j < 256; j++) {
+ hw_writereg(base, REG_EWL, j);
+ x = hw_readreg(base, REG_EWL);
+ if (x != j) {
+ printk(KERN_INFO "%s: is: %02X expected: "
+ "%02X (%d)\n", dev->name, x, j, i);
+ }
+ }
+ }
+}
+#endif
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+ dev->header_ops = NULL;
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+
+ /* set flags according to driver capabilities */
+ if (echo)
+ dev->flags |= IFF_ECHO;
+
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void sja1000_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int sja1000_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ for (i = 0; base[i]; i++) {
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, SJA1000_IO_SIZE_BASIC, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ sja1000_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ if (!sja1000_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ sja1000_exit_module();
+ return -ENODEV;
+ }
+
+ dev = can_create_netdev(i, SJA1000_IO_SIZE_BASIC);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ }
+ }
+ return 0;
+}
+
+module_init(sja1000_init_module);
+module_exit(sja1000_exit_module);
+
diff --git a/drivers/net/can/old/sja1000/sja1000.h b/drivers/net/can/old/sja1000/sja1000.h
new file mode 100644
index 000000000000..a47b2f629136
--- /dev/null
+++ b/drivers/net/can/old/sja1000/sja1000.h
@@ -0,0 +1,187 @@
+/*
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000_H
+#define SJA1000_H
+
+#define SJA1000_IO_SIZE_BASIC 0x20
+#define SJA1000_IO_SIZE_PELICAN 0x80 /* unused */
+
+#define CHIP_NAME "sja1000"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_DIR 0x20
+#define ECC_SEG 0x1F
+#define ECC_ERR 6
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 75
+#define JUMPWIDTH 0x40
+
+/* CAN private data structure */
+
+struct can_priv {
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ struct timer_list timer;
+ int state;
+ struct sk_buff *echo_skb;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* SJA1000_H */
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
new file mode 100644
index 000000000000..176393a0001d
--- /dev/null
+++ b/drivers/net/can/sja1000/Makefile
@@ -0,0 +1,30 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+#EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/hal
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000.o
+obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
+obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o
+obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
+obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
+obj-$(CONFIG_CAN_IXXAT_PCI) += ixxat_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
+obj-$(CONFIG_CAN_PIPCAN) += pipcan.o
+obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c
new file mode 100644
index 000000000000..6076375e2a96
--- /dev/null
+++ b/drivers/net/can/sja1000/ems_pci.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pci"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_MAX_CHAN 2
+
+struct ems_pci_card {
+ int channels;
+
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev[EMS_PCI_MAX_CHAN];
+
+ void __iomem *conf_addr;
+ void __iomem *base_addr;
+};
+
+#define EMS_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * Register definitions and descriptions are from LinCAN 0.3.3.
+ *
+ * PSB4610 PITA-2 bridge control registers
+ */
+#define PITA2_ICR 0x00 /* Interrupt Control Register */
+#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */
+#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */
+
+#define PITA2_MISC 0x1c /* Miscellaneous Register */
+#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCI_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
+#define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */
+
+#define EMS_PCI_PORT_BYTES 0x4 /* Each register occupies 4 bytes */
+
+#define EMS_PCI_VENDOR_ID 0x110a /* PCI device and vendor ID */
+#define EMS_PCI_DEVICE_ID 0x2104
+
+static struct pci_device_id ems_pci_tbl[] = {
+ {EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
+
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_readb(struct ems_pci_card *card, unsigned int port)
+{
+ return readb((void __iomem *)card->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static u8 ems_pci_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct ems_pci_card *card = (struct ems_pci_card *)priv->priv;
+
+ /* reset int flag of pita */
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, card->conf_addr
+ + PITA2_ICR);
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pci_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pci_write_reg(dev, REG_MOD, 1);
+
+ ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pci_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+ struct ems_pci_card *card = pci_get_drvdata(pdev);
+ struct net_device *dev;
+ int i = 0;
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ if (card->base_addr != NULL)
+ pci_iounmap(card->pci_dev, card->base_addr);
+
+ if (card->conf_addr != NULL)
+ pci_iounmap(card->pci_dev, card->conf_addr);
+
+ kfree(card);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+ /* Request board reset */
+ writeb(0, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pci_add_card(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pci_card *card;
+ int err, i;
+
+ /* Enabling PCI device */
+ if (pci_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "Enabling PCI device failed\n");
+ return -ENODEV;
+ }
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL);
+ if (card == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ pci_disable_device(pdev);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, card);
+
+ card->pci_dev = pdev;
+
+ card->channels = 0;
+
+ /* Remap PITA configuration space, and controller memory area */
+ card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE);
+ if (card->conf_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ /* Configure PITA-2 parallel interface (enable MUX) */
+ writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC);
+
+ /* Check for unique EMS CAN signature */
+ if (ems_pci_readb(card, 0) != 0x55 ||
+ ems_pci_readb(card, 1) != 0xAA ||
+ ems_pci_readb(card, 2) != 0x01 ||
+ ems_pci_readb(card, 3) != 0xCB ||
+ ems_pci_readb(card, 4) != 0x11) {
+ dev_err(&pdev->dev, "Not EMS Dr. Thomas Wuensche interface\n");
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pci_card_reset(card);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCI_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+
+ dev->irq = pdev->irq;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCI_CAN_BASE_OFFSET
+ + (i * EMS_PCI_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pci_check_chan(dev)) {
+ priv->read_reg = ems_pci_read_reg;
+ priv->write_reg = ems_pci_write_reg;
+ priv->post_irq = ems_pci_post_irq;
+ priv->can.bittiming.clock = EMS_PCI_CAN_CLOCK;
+ priv->ocr = EMS_PCI_OCR;
+ priv->cdr = EMS_PCI_CDR;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Enable interrupts from card */
+ writel(PITA2_ICR_INT0_EN, card->conf_addr + PITA2_ICR);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed "
+ "(err=%d)\n", err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ dev_info(&pdev->dev, "Channel #%d at %#lX, irq %d\n",
+ i + 1, dev->base_addr,
+ dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ return 0;
+
+failure_cleanup:
+ dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
+
+ ems_pci_del_card(pdev);
+
+ return err;
+}
+
+static struct pci_driver ems_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ems_pci_tbl,
+ .probe = ems_pci_add_card,
+ .remove = ems_pci_del_card,
+};
+
+static int __init ems_pci_init(void)
+{
+ return pci_register_driver(&ems_pci_driver);
+}
+
+static void __exit ems_pci_exit(void)
+{
+ pci_unregister_driver(&ems_pci_driver);
+}
+
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
+
diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c
new file mode 100644
index 000000000000..7c9b88b30301
--- /dev/null
+++ b/drivers/net/can/sja1000/ems_pcmcia.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pcmcia"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-CARD cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-CARD CAN card");
+MODULE_LICENSE("GPL v2");
+
+static int debug;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug, "Set debug level (default: 0)");
+
+#define EMS_PCMCIA_MAX_CHAN 2
+
+struct ems_pcmcia_card {
+ int channels;
+
+ struct pcmcia_device *pcmcia_dev;
+ struct net_device *net_dev[EMS_PCMCIA_MAX_CHAN];
+
+ void __iomem *base_addr;
+};
+
+#define EMS_PCMCIA_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCMCIA_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCMCIA_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCMCIA_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCMCIA_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */
+#define EMS_PCMCIA_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */
+
+#define EMS_CMD_RESET 0x00 /* Perform a reset of the card */
+#define EMS_CMD_MAP 0x03 /* Map CAN controllers into card' memory */
+#define EMS_CMD_UMAP 0x02 /* Unmap CAN controllers from card' memory */
+
+static struct pcmcia_device_id ems_pcmcia_tbl[] = {
+ PCMCIA_DEVICE_PROD_ID123("EMS_T_W", "CPC-Card", "V2.0", 0xeab1ea23,
+ 0xa338573f, 0xe4575800),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE (pcmcia, ems_pcmcia_tbl);
+
+static void ems_pcmcia_config(struct pcmcia_device *dev);
+
+static u8 ems_pcmcia_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr + port);
+}
+
+static void ems_pcmcia_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr + port);
+}
+
+static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
+{
+ struct ems_pcmcia_card *card = dev_id;
+ struct net_device *dev;
+ irqreturn_t retval = IRQ_NONE;
+ int i, again;
+
+ do {
+ again = 0;
+
+ /* Check interrupt for each channel */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+ if (sja1000_interrupt(irq, dev) == IRQ_HANDLED)
+ again = 1;
+ }
+ /* At least one channel handled the interrupt */
+ if (again)
+ retval = IRQ_HANDLED;
+
+ } while (again);
+
+ return retval;
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pcmcia_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pcmcia_write_reg(dev, REG_MOD, 1);
+
+ ems_pcmcia_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pcmcia_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pcmcia_del_card(struct pcmcia_device *pdev)
+{
+ struct ems_pcmcia_card *card = pdev->priv;
+ struct net_device *dev;
+ int i = 0;
+
+ if (!card)
+ return;
+
+ free_irq(pdev->irq.AssignedIRQ, card);
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ printk(KERN_INFO "%s: removing %s on channel #%d\n",
+ DRV_NAME, dev->name, i);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ writeb(EMS_CMD_UMAP, card->base_addr);
+
+ if (card->base_addr != NULL )
+ iounmap(card->base_addr);
+
+ kfree(card);
+
+ pdev->priv = NULL;
+}
+
+static void ems_pcmcia_card_reset(struct ems_pcmcia_card *card)
+{
+ /* Request board reset */
+ writeb(EMS_CMD_RESET, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pcmcia_add_card(struct pcmcia_device *pdev,
+ unsigned long base)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pcmcia_card *card;
+ int err, i;
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pcmcia_card), GFP_KERNEL);
+ if (card == NULL) {
+ printk(KERN_ERR "%s: unable to allocate memory\n", DRV_NAME);
+ return -ENOMEM;
+ }
+
+ pdev->priv = card;
+
+ card->channels = 0;
+
+ card->base_addr = ioremap(base, EMS_PCMCIA_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ /* Check for unique EMS CAN signature */
+ if (readw(card->base_addr) != 0xAA55) {
+ printk(KERN_ERR "%s: No EMS CPC Card hardware found.\n",
+ DRV_NAME);
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pcmcia_card_reset(card);
+
+ /* Make sure CAN controllers are mapped into card's memory space */
+ writeb(EMS_CMD_MAP, card->base_addr);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->irq = pdev->irq.AssignedIRQ;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCMCIA_CAN_BASE_OFFSET
+ + (i * EMS_PCMCIA_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pcmcia_check_chan(dev)) {
+ priv->read_reg = ems_pcmcia_read_reg;
+ priv->write_reg = ems_pcmcia_write_reg;
+ priv->can.bittiming.clock = EMS_PCMCIA_CAN_CLOCK;
+ priv->ocr = EMS_PCMCIA_OCR;
+ priv->cdr = EMS_PCMCIA_CDR;
+ priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_INFO "%s: registering device "
+ "failed (err=%d)\n", DRV_NAME, err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ printk(KERN_INFO "%s: registered %s on channel "
+ "#%d at %lX, irq %d\n", DRV_NAME, dev->name,
+ i, dev->base_addr, dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED,
+ DRV_NAME, (void *)card);
+ if (err) {
+ printk(KERN_INFO "Registering device failed (err=%d)\n", err);
+
+ goto failure_cleanup;
+ }
+
+ return 0;
+
+failure_cleanup:
+ printk(KERN_ERR "Error: %d. Cleaning Up.\n", err);
+
+ ems_pcmcia_del_card(pdev);
+
+ return err;
+}
+
+/*
+ * Setup PCMCIA socket and probe for EMS CPC-CARD
+ */
+static int __devinit ems_pcmcia_probe(struct pcmcia_device *dev)
+{
+ /* The io structure describes IO port mapping */
+ dev->io.NumPorts1 = 16;
+ dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ dev->io.NumPorts2 = 16;
+ dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+ dev->io.IOAddrLines = 5;
+
+ /* Interrupt setup */
+ dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* General socket configuration */
+ dev->conf.Attributes = CONF_ENABLE_IRQ;
+ dev->conf.IntType = INT_MEMORY_AND_IO;
+ dev->conf.ConfigIndex = 1;
+ dev->conf.Present = PRESENT_OPTION;
+
+ dev->win = NULL;
+
+ ems_pcmcia_config(dev);
+
+ return 0;
+}
+
+/*
+ * Configure PCMCIA socket
+ */
+static void ems_pcmcia_config(struct pcmcia_device *dev)
+{
+ win_req_t req;
+ memreq_t mem;
+
+ int csval;
+
+ /* Allocate a memory window */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ req.Base = req.Size = 0;
+ req.AccessSpeed = 0;
+
+ csval = pcmcia_request_window(&dev, &req, &dev->win);
+ if (csval) {
+ cs_error(dev, RequestWindow, csval);
+ return;
+ }
+
+ mem.CardOffset = mem.Page = 0;
+ mem.CardOffset = dev->conf.ConfigBase;
+
+ csval = pcmcia_map_mem_page(dev->win, &mem);
+ if (csval) {
+ cs_error(dev, MapMemPage, csval);
+ return;
+ }
+
+ csval = pcmcia_request_irq(dev, &dev->irq);
+ if (csval) {
+ cs_error(dev, RequestIRQ, csval);
+ return;
+ }
+
+ /* This actually configures the PCMCIA socket */
+ csval = pcmcia_request_configuration(dev, &dev->conf);
+ if (csval) {
+ cs_error(dev, RequestConfiguration, csval);
+ return;
+ }
+
+ ems_pcmcia_add_card(dev, req.Base);
+}
+
+/*
+ * Release claimed resources
+ */
+static void ems_pcmcia_remove(struct pcmcia_device *dev)
+{
+ ems_pcmcia_del_card(dev);
+
+ pcmcia_disable_device(dev);
+}
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+static dev_info_t dev_info = "can-ems-pcmcia";
+
+static struct pcmcia_driver ems_pcmcia_driver = {
+ .drv = {
+ .name = dev_info,
+ },
+
+ .probe = ems_pcmcia_probe,
+ .remove = ems_pcmcia_remove,
+
+ .id_table = ems_pcmcia_tbl,
+};
+
+static int __init ems_pcmcia_init(void)
+{
+ return pcmcia_register_driver(&ems_pcmcia_driver);
+}
+
+static void __exit ems_pcmcia_exit(void)
+{
+ pcmcia_unregister_driver(&ems_pcmcia_driver);
+}
+
+module_init(ems_pcmcia_init);
+module_exit(ems_pcmcia_exit);
+
diff --git a/drivers/net/can/sja1000/ixxat_pci.c b/drivers/net/can/sja1000/ixxat_pci.c
new file mode 100644
index 000000000000..ab98a9d160ec
--- /dev/null
+++ b/drivers/net/can/sja1000/ixxat_pci.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ixxat_pci"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Socket-CAN driver for IXXAT PC-I 04/PCI PCI cards");
+MODULE_SUPPORTED_DEVICE("IXXAT PC-I 04/PCI card");
+MODULE_LICENSE("GPL v2");
+
+/* Maximum number of interfaces supported on one card. Currently
+ * we only support a maximum of two interfaces, which is the maximum
+ * of what Ixxat sells anyway.
+ */
+#define IXXAT_PCI_MAX_CAN 2
+
+struct ixxat_pci {
+ struct pci_dev *pci_dev;
+ struct net_device *dev[IXXAT_PCI_MAX_CAN];
+ int conf_addr;
+ void __iomem *base_addr;
+};
+
+#define IXXAT_PCI_CAN_CLOCK (16000000 / 2)
+
+#define IXXAT_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX0_INVERT | \
+ OCR_TX1_PUSHPULL)
+#define IXXAT_PCI_CDR 0
+
+#define CHANNEL_RESET_OFFSET 0x110
+#define CHANNEL_OFFSET 0x200
+
+#define INTCSR_OFFSET 0x4c /* Offset in PLX9050 conf registers */
+#define INTCSR_LINTI1 (1 << 0)
+#define INTCSR_LINTI2 (1 << 3)
+#define INTCSR_PCI (1 << 6)
+
+/* PCI vender, device and sub-device ID */
+#define IXXAT_PCI_VENDOR_ID 0x10b5
+#define IXXAT_PCI_DEVICE_ID 0x9050
+#define IXXAT_PCI_SUB_SYS_ID 0x2540
+
+#define IXXAT_PCI_BASE_SIZE 0x400
+
+static struct pci_device_id ixxat_pci_tbl[] = {
+ {IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ixxat_pci_tbl);
+
+static u8 ixxat_pci_read_reg(struct net_device *ndev, int port)
+{
+ u8 val;
+ val = readb((void __iomem *)(ndev->base_addr + port));
+ return val;
+}
+
+static void ixxat_pci_write_reg(struct net_device *ndev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)(ndev->base_addr + port));
+}
+
+static void ixxat_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev)
+{
+ dev_info(&pdev->dev, "Removing device %s\n", ndev->name);
+
+ unregister_sja1000dev(ndev);
+
+ free_sja1000dev(ndev);
+}
+
+static struct net_device *ixxat_pci_add_chan(struct pci_dev *pdev,
+ void __iomem *base_addr)
+{
+ struct net_device *ndev;
+ struct sja1000_priv *priv;
+ int err;
+
+ ndev = alloc_sja1000dev(0);
+ if (ndev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ priv = netdev_priv(ndev);
+
+ ndev->base_addr = (unsigned long)base_addr;
+
+ priv->read_reg = ixxat_pci_read_reg;
+ priv->write_reg = ixxat_pci_write_reg;
+
+ priv->can.bittiming.clock = IXXAT_PCI_CAN_CLOCK;
+
+ priv->ocr = IXXAT_PCI_OCR;
+ priv->cdr = IXXAT_PCI_CDR;
+
+ /* Set and enable PCI interrupts */
+ ndev->irq = pdev->irq;
+
+ dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
+ ndev->base_addr, ndev->irq);
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ err = register_sja1000dev(ndev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register (err=%d)\n", err);
+ goto failure;
+ }
+
+ return ndev;
+
+failure:
+ free_sja1000dev(ndev);
+ return ERR_PTR(err);
+}
+
+static int __devinit ixxat_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct ixxat_pci *board;
+ int err, intcsr = INTCSR_LINTI1 | INTCSR_PCI;
+ u16 sub_sys_id;
+ void __iomem *base_addr;
+
+ dev_info(&pdev->dev, "Initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_release_pci;
+
+ if (sub_sys_id != IXXAT_PCI_SUB_SYS_ID)
+ return -ENODEV;
+
+ /* Enable memory and I/O space */
+ err = pci_write_config_word(pdev, 0x04, 0x3);
+ if (err)
+ goto failure_release_pci;
+
+ board->conf_addr = pci_resource_start(pdev, 1);
+
+ base_addr = pci_iomap(pdev, 2, IXXAT_PCI_BASE_SIZE);
+ if (base_addr == NULL) {
+ err = -ENODEV;
+ goto failure_release_pci;
+ }
+
+ board->base_addr = base_addr;
+
+ writeb(0x1, base_addr + CHANNEL_RESET_OFFSET);
+ writeb(0x1, base_addr + CHANNEL_OFFSET + CHANNEL_RESET_OFFSET);
+ udelay(100);
+
+ board->dev[0] = ixxat_pci_add_chan(pdev, base_addr);
+ if (IS_ERR(board->dev[0]))
+ goto failure_iounmap;
+
+ /* Check if second channel is available */
+ if ((readb(base_addr + CHANNEL_OFFSET + REG_MOD) & 0xa1) == 0x21 &&
+ readb(base_addr + CHANNEL_OFFSET + REG_SR) == 0x0c &&
+ readb(base_addr + CHANNEL_OFFSET + REG_IR) == 0xe0) {
+ board->dev[1] = ixxat_pci_add_chan(pdev,
+ base_addr + CHANNEL_OFFSET);
+ if (IS_ERR(board->dev[1]))
+ goto failure_unreg_dev0;
+
+ intcsr |= INTCSR_LINTI2;
+ }
+
+ /* enable interrupt(s) in PLX9050 */
+ outb(intcsr, board->conf_addr + INTCSR_OFFSET);
+
+ pci_set_drvdata(pdev, board);
+
+ return 0;
+
+failure_unreg_dev0:
+ ixxat_pci_del_chan(pdev, board->dev[0]);
+
+failure_iounmap:
+ pci_iounmap(pdev, board->base_addr);
+
+failure_release_pci:
+ pci_release_regions(pdev);
+
+failure:
+ kfree(board);
+
+ return err;
+}
+
+static void __devexit ixxat_pci_remove_one(struct pci_dev *pdev)
+{
+ struct ixxat_pci *board = pci_get_drvdata(pdev);
+ int i;
+
+ /* Disable interrupts in PLX9050*/
+ outb(0, board->conf_addr + INTCSR_OFFSET);
+
+ for (i = 0; i < IXXAT_PCI_MAX_CAN; i++) {
+ if (!board->dev[i])
+ break;
+ ixxat_pci_del_chan(pdev, board->dev[i]);
+ }
+
+ pci_iounmap(pdev, board->base_addr);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(board);
+}
+
+static struct pci_driver ixxat_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ixxat_pci_tbl,
+ .probe = ixxat_pci_init_one,
+ .remove = __devexit_p(ixxat_pci_remove_one),
+};
+
+static int __init ixxat_pci_init(void)
+{
+ return pci_register_driver(&ixxat_pci_driver);
+}
+
+static void __exit ixxat_pci_exit(void)
+{
+ pci_unregister_driver(&ixxat_pci_driver);
+}
+
+module_init(ixxat_pci_init);
+module_exit(ixxat_pci_exit);
diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c
new file mode 100644
index 000000000000..0b01ea3d5560
--- /dev/null
+++ b/drivers/net/can/sja1000/kvaser_pci.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se>
+ *
+ * Parts of this software are based on (derived) the following:
+ *
+ * - Kvaser linux driver, version 4.72 BETA
+ * Copyright (C) 2002-2007 KVASER AB
+ *
+ * - Lincan driver, version 0.3.3, OCERA project
+ * Copyright (C) 2004 Pavel Pisa
+ * Copyright (C) 2001 Arnaud Westenberg
+ *
+ * - Socketcan SJA1000 drivers
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "kvaser_pci"
+
+MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>");
+MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define MAX_NO_OF_CHANNELS 4 /* max no of channels on
+ a single card */
+
+struct kvaser_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1];
+ void __iomem *conf_addr;
+ void __iomem *res_addr;
+ int no_channels;
+ u8 xilinx_ver;
+};
+
+#define KVASER_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 0
+ * (meaning divide-by-2), the Pelican bit, and the clock-off bit
+ * (you will have no need for CLKOUT anyway).
+ */
+#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+
+/*
+ * These register values are valid for revision 14 of the Xilinx logic.
+ */
+#define XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define XILINX_PRESUMED_VERSION 14
+
+/*
+ * Important S5920 registers
+ */
+#define S5920_INTCSR 0x38
+#define S5920_PTCR 0x60
+#define INTCSR_ADDON_INTENABLE_M 0x2000
+
+
+#define KVASER_PCI_PORT_BYTES 0x20
+
+#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */
+#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */
+
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID1 0x8406
+
+#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID2 0x0008
+
+static struct pci_device_id kvaser_pci_tbl[] = {
+ {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,},
+ {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl);
+
+static u8 kvaser_pci_read_reg(struct net_device *dev, int port)
+{
+ return ioread8((void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_disable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Disable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp &= ~INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static void kvaser_pci_enable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Enable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp |= INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static int number_of_sja1000_chip(void __iomem *base_addr)
+{
+ u8 status;
+ int i;
+
+ for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
+ /* reset chip */
+ iowrite8(MOD_RM, base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ status = ioread8(base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ udelay(10);
+ /* check reset bit */
+ if (!(status & MOD_RM))
+ break;
+ }
+
+ return i;
+}
+
+static void kvaser_pci_del_chan(struct net_device *dev)
+{
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int i;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ dev->name);
+
+ for (i = 0; i < board->no_channels - 1; i++) {
+ if (board->slave_dev[i]) {
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ board->slave_dev[i]->name);
+ unregister_sja1000dev(board->slave_dev[i]);
+ free_sja1000dev(board->slave_dev[i]);
+ }
+ }
+ unregister_sja1000dev(dev);
+
+ /* Disable PCI interrupts */
+ kvaser_pci_disable_irq(dev);
+
+ pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr);
+ pci_iounmap(board->pci_dev, board->conf_addr);
+ pci_iounmap(board->pci_dev, board->res_addr);
+
+ free_sja1000dev(dev);
+}
+
+static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev,
+ void __iomem *conf_addr,
+ void __iomem *res_addr,
+ unsigned long base_addr)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ /*S5920*/
+ board->conf_addr = conf_addr;
+
+ /*XILINX board wide address*/
+ board->res_addr = res_addr;
+
+ if (channel == 0) {
+ board->xilinx_ver =
+ ioread8(board->res_addr + XILINX_VERINT) >> 4;
+ init_step = 2;
+
+ /* Assert PTADR# - we're in passive mode so the other bits are
+ not important */
+ iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR);
+
+ /* Disable interrupts from card */
+ kvaser_pci_disable_irq(dev);
+ /* Enable interrupts from card */
+ kvaser_pci_enable_irq(dev);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct kvaser_pci *master_board = master_priv->priv;
+ master_board->slave_dev[channel - 1] = dev;
+ master_board->no_channels = channel + 1;
+ board->xilinx_ver = master_board->xilinx_ver;
+ }
+
+ dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES;
+
+ priv->read_reg = kvaser_pci_read_reg;
+ priv->write_reg = kvaser_pci_write_reg;
+
+ priv->can.bittiming.clock = KVASER_PCI_CAN_CLOCK;
+
+ priv->ocr = KVASER_PCI_OCR;
+ priv->cdr = KVASER_PCI_CDR;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ init_step = 4;
+
+ dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n",
+ dev->base_addr, board->conf_addr, dev->irq);
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed (err=%d)\n",
+ err);
+ goto failure;
+ }
+
+ if (channel == 0)
+ *master_dev = dev;
+
+ return 0;
+
+failure:
+ kvaser_pci_del_chan(dev);
+ return err;
+}
+
+static int __devinit kvaser_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ struct net_device *master_dev = NULL;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int no_channels;
+ void __iomem *base_addr = NULL;
+ void __iomem *conf_addr = NULL;
+ void __iomem *res_addr = NULL;
+ int i;
+
+ dev_info(&pdev->dev, "initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure_release_pci;
+
+ /*S5920*/
+ conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE);
+ if (conf_addr == NULL) {
+ err = -ENODEV;
+ goto failure_iounmap;
+ }
+
+ /*XILINX board wide address*/
+ res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE);
+ if (res_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE);
+ if (base_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ no_channels = number_of_sja1000_chip(base_addr);
+ if (no_channels == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ for (i = 0; i < no_channels; i++) {
+ err = kvaser_pci_add_chan(pdev, i, &master_dev,
+ conf_addr, res_addr,
+ (unsigned long)base_addr);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ priv = netdev_priv(master_dev);
+ board = priv->priv;
+
+ dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n",
+ board->xilinx_ver, board->no_channels);
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ kvaser_pci_del_chan(master_dev);
+
+failure_iounmap:
+ if (conf_addr == NULL)
+ pci_iounmap(pdev, conf_addr);
+ if (res_addr == NULL)
+ pci_iounmap(pdev, res_addr);
+ if (base_addr == NULL)
+ pci_iounmap(pdev, base_addr);
+
+ pci_release_regions(pdev);
+
+failure_release_pci:
+ pci_disable_device(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ kvaser_pci_del_chan(dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver kvaser_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = kvaser_pci_tbl,
+ .probe = kvaser_pci_init_one,
+ .remove = __devexit_p(kvaser_pci_remove_one),
+};
+
+static int __init kvaser_pci_init(void)
+{
+ return pci_register_driver(&kvaser_pci_driver);
+}
+
+static void __exit kvaser_pci_exit(void)
+{
+ pci_unregister_driver(&kvaser_pci_driver);
+}
+
+module_init(kvaser_pci_init);
+module_exit(kvaser_pci_exit);
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
new file mode 100644
index 000000000000..d1089a3d62e0
--- /dev/null
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from the PCAN project file driver/src/pcan_pci.c:
+ *
+ * Copyright (C) 2001-2006 PEAK System-Technik GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "peak_pci"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+struct peak_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev;
+ volatile void __iomem *conf_addr;
+};
+
+#define PEAK_PCI_SINGLE 0 /* single channel device */
+#define PEAK_PCI_MASTER 1 /* multi channel master device */
+#define PEAK_PCI_SLAVE 2 /* multi channel slave device */
+
+#define PEAK_PCI_CAN_CLOCK (16000000 / 2)
+
+#define PEAK_PCI_CDR_SINGLE (CDR_CBP | CDR_CLKOUT_MASK | CDR_CLK_OFF)
+#define PEAK_PCI_CDR_MASTER (CDR_CBP | CDR_CLKOUT_MASK)
+
+#define PEAK_PCI_OCR OCR_TX0_PUSHPULL
+
+/*
+ * Important PITA registers
+ */
+#define PITA_ICR 0x00 /* interrupt control register */
+#define PITA_GPIOICR 0x18 /* general purpose I/O interface
+ control register */
+#define PITA_MISC 0x1C /* miscellanoes register */
+
+#define PCI_CONFIG_PORT_SIZE 0x1000 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x0400 /* size of a channel io-memory */
+
+#define PEAK_PCI_VENDOR_ID 0x001C /* the PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID 0x0001
+
+static struct pci_device_id peak_pci_tbl[] = {
+ {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(struct net_device *dev, int port)
+{
+ u8 val;
+ val = readb((const volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+ return val;
+}
+
+static void peak_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+}
+
+static void peak_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+ u16 icr_low;
+
+ /* Select and clear in Pita stored interrupt */
+ icr_low = readw(board->conf_addr + PITA_ICR);
+ if (board->channel == PEAK_PCI_SLAVE) {
+ if (icr_low & 0x0001)
+ writew(0x0001, board->conf_addr + PITA_ICR);
+ } else {
+ if (icr_low & 0x0002)
+ writew(0x0002, board->conf_addr + PITA_ICR);
+ }
+}
+
+static void peak_pci_del_chan(struct net_device *dev, int init_step)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board;
+ u16 icr_high;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ switch (init_step) {
+ case 0: /* Full cleanup */
+ printk(KERN_INFO "Removing %s device %s\n",
+ DRV_NAME, dev->name);
+ unregister_sja1000dev(dev);
+ case 4:
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (board->channel == PEAK_PCI_SLAVE)
+ icr_high &= ~0x0001;
+ else
+ icr_high &= ~0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ case 3:
+ iounmap((void *)dev->base_addr);
+ case 2:
+ if (board->channel != PEAK_PCI_SLAVE)
+ iounmap((void *)board->conf_addr);
+ case 1:
+ free_sja1000dev(dev);
+ break;
+ }
+
+}
+
+static int peak_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct peak_pci *board;
+ u16 icr_high;
+ unsigned long addr;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct peak_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+ init_step = 1;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ if (channel != PEAK_PCI_SLAVE) {
+
+ addr = pci_resource_start(pdev, 0);
+ board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE);
+ if (board->conf_addr == 0) {
+ err = -ENODEV;
+ goto failure;
+ }
+ init_step = 2;
+
+ /* Set GPIO control register */
+ writew(0x0005, board->conf_addr + PITA_GPIOICR + 2);
+
+ /* Enable single or dual channel */
+ if (channel == PEAK_PCI_MASTER)
+ writeb(0x00, board->conf_addr + PITA_GPIOICR);
+ else
+ writeb(0x04, board->conf_addr + PITA_GPIOICR);
+ /* Toggle reset */
+ writeb(0x05, board->conf_addr + PITA_MISC + 3);
+ mdelay(5);
+ /* Leave parport mux mode */
+ writeb(0x04, board->conf_addr + PITA_MISC + 3);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct peak_pci *master_board = master_priv->priv;
+ master_board->slave_dev = dev;
+ board->conf_addr = master_board->conf_addr;
+ }
+
+ addr = pci_resource_start(pdev, 1);
+ if (channel == PEAK_PCI_SLAVE)
+ addr += PCI_PORT_SIZE;
+
+ dev->base_addr = (unsigned long)ioremap(addr, PCI_PORT_SIZE);
+ if (dev->base_addr == 0) {
+ err = -ENOMEM;
+ goto failure;
+ }
+ init_step = 3;
+
+ priv->read_reg = peak_pci_read_reg;
+ priv->write_reg = peak_pci_write_reg;
+ priv->post_irq = peak_pci_post_irq;
+
+ priv->can.bittiming.clock = PEAK_PCI_CAN_CLOCK;
+
+ priv->ocr = PEAK_PCI_OCR;
+
+ if (channel == PEAK_PCI_MASTER)
+ priv->cdr = PEAK_PCI_CDR_MASTER;
+ else
+ priv->cdr = PEAK_PCI_CDR_SINGLE;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (channel == PEAK_PCI_SLAVE)
+ icr_high |= 0x0001;
+ else
+ icr_high |= 0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ init_step = 4;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_ERR "Registering %s device failed (err=%d)\n",
+ DRV_NAME, err);
+ goto failure;
+ }
+
+ if (channel != PEAK_PCI_SLAVE)
+ *master_dev = dev;
+
+ printk(KERN_INFO "%s: %s at base_addr=%#lx conf_addr=%p irq=%d\n",
+ DRV_NAME, dev->name, dev->base_addr, board->conf_addr, dev->irq);
+
+ return 0;
+
+failure:
+ peak_pci_del_chan(dev, init_step);
+ return err;
+}
+
+static int __devinit peak_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ u16 sub_sys_id;
+ struct net_device *master_dev = NULL;
+
+ printk(KERN_INFO "%s: initializing device %04x:%04x\n",
+ DRV_NAME, pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_cleanup;
+
+ err = pci_write_config_word(pdev, 0x44, 0);
+ if (err)
+ goto failure_cleanup;
+
+ if (sub_sys_id > 3) {
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_MASTER, &master_dev);
+ if (err)
+ goto failure_cleanup;
+
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_SLAVE, &master_dev);
+ if (err)
+ goto failure_cleanup;
+ } else {
+ err = peak_pci_add_chan(pdev, PEAK_PCI_SINGLE,
+ &master_dev);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ if (master_dev)
+ peak_pci_del_chan(master_dev, 0);
+
+ pci_release_regions(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit peak_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+
+ if (board->slave_dev)
+ peak_pci_del_chan(board->slave_dev, 0);
+ peak_pci_del_chan(dev, 0);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver peak_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = peak_pci_tbl,
+ .probe = peak_pci_init_one,
+ .remove = __devexit_p(peak_pci_remove_one),
+};
+
+static int __init peak_pci_init(void)
+{
+ return pci_register_driver(&peak_pci_driver);
+}
+
+static void __exit peak_pci_exit(void)
+{
+ pci_unregister_driver(&peak_pci_driver);
+}
+
+module_init(peak_pci_init);
+module_exit(peak_pci_exit);
diff --git a/drivers/net/can/sja1000/pipcan.c b/drivers/net/can/sja1000/pipcan.c
new file mode 100644
index 000000000000..b7414b218697
--- /dev/null
+++ b/drivers/net/can/sja1000/pipcan.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 David Müller, <d.mueller@elsoft.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "pipcan"
+
+MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
+MODULE_DESCRIPTION("Socket-CAN driver for MPL PIPCAN module");
+MODULE_SUPPORTED_DEVICE("MPL PIPCAN module");
+MODULE_LICENSE("GPL v2");
+
+#define PIPCAN_CAN_CLOCK (16000000 / 2)
+
+#define PIPCAN_OCR (OCR_TX1_PUSHPULL)
+#define PIPCAN_CDR (CDR_CBP | CDR_CLK_OFF)
+
+#define PIPCAN_IOSIZE (0x100)
+
+#define PIPCAN_RES (0x804)
+#define PIPCAN_RST (0x805)
+
+static u8 pc_read_reg(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static void pc_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static int __init pc_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res;
+ int rc, irq;
+
+ rc = -ENODEV;
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || !irq)
+ goto exit;
+
+ rc = -EBUSY;
+ if (!request_region(res->start, res->end - res->start + 1, DRV_NAME))
+ goto exit;
+
+ rc = -ENOMEM;
+ dev = alloc_sja1000dev(0);
+ if (!dev)
+ goto exit_release;
+
+ priv = netdev_priv(dev);
+
+ priv->read_reg = pc_read_reg;
+ priv->write_reg = pc_write_reg;
+ priv->can.bittiming.clock = PIPCAN_CAN_CLOCK;
+ priv->ocr = PIPCAN_OCR;
+ priv->cdr = PIPCAN_CDR;
+
+ dev->irq = irq;
+ dev->base_addr = res->start;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* deactivate RST */
+ outb(inb(PIPCAN_RST) & ~0x01, PIPCAN_RST);
+
+ rc = register_sja1000dev(dev);
+ if (rc) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, rc);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "device registered (base_addr=%#lx, irq=%d)\n",
+ dev->base_addr, dev->irq);
+ return 0;
+
+exit_free:
+ free_sja1000dev(dev);
+
+exit_release:
+ release_region(res->start, res->end - res->start + 1);
+
+exit:
+ return rc;
+}
+
+static int __exit pc_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ unregister_sja1000dev(dev);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ free_sja1000dev(dev);
+
+ release_region(res->start, res->end - res->start + 1);
+
+ /* activate RST */
+ outb(inb(PIPCAN_RST) | 0x01, PIPCAN_RST);
+
+ return 0;
+}
+
+static struct platform_driver pc_driver = {
+ .remove = __exit_p(pc_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *pc_pdev;
+static const u16 pipcan_ioport[] = {0x1000, 0x8000, 0xE000};
+
+static int __init pc_init(void)
+{
+ struct resource r[2];
+ int rc, addr, irq, idx;
+ u8 pc_res;
+
+ /* get PIPCAN resources from EPLD */
+ pc_res = inb(PIPCAN_RES);
+
+ idx = (pc_res & 0x0F);
+ if ((idx <= 0) || (idx > ARRAY_SIZE(pipcan_ioport))) {
+ printk(KERN_ERR DRV_NAME " invalid base address\n");
+ return -EINVAL;
+ }
+ addr = pipcan_ioport[idx-1];
+
+ irq = (pc_res >> 4) & 0x0F;
+ if ((irq < 3) || (irq == 8) || (irq == 13)) {
+ printk(KERN_ERR DRV_NAME " invalid IRQ\n");
+ return -EINVAL;
+ }
+
+ /* fill in resources */
+ memset(&r, 0, sizeof(r));
+ r[0].start = addr;
+ r[0].end = addr + PIPCAN_IOSIZE - 1;
+ r[0].name = DRV_NAME;
+ r[0].flags = IORESOURCE_IO;
+ r[1].start = r[1].end = irq;
+ r[1].name = DRV_NAME;
+ r[1].flags = IORESOURCE_IRQ;
+
+ pc_pdev = platform_device_register_simple(DRV_NAME, 0, r,
+ ARRAY_SIZE(r));
+ if (IS_ERR(pc_pdev))
+ return PTR_ERR(pc_pdev);
+
+ rc = platform_driver_probe(&pc_driver, pc_probe);
+ if (rc) {
+ platform_device_unregister(pc_pdev);
+ printk(KERN_ERR DRV_NAME
+ " platform_driver_probe() failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static void __exit pc_exit(void)
+{
+ platform_driver_unregister(&pc_driver);
+ platform_device_unregister(pc_pdev);
+}
+
+module_init(pc_init);
+module_exit(pc_exit);
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
new file mode 100644
index 000000000000..02f49fa703ce
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -0,0 +1,677 @@
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/dev.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver");
+
+static struct can_bittiming_const sja1000_bittiming_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static int sja1000_probe_chip(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ if (dev->base_addr && (priv->read_reg(dev, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ DRV_NAME, dev->base_addr);
+ return 0;
+ }
+ return 1;
+}
+
+static int set_reset_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ /* disable interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ priv->can.state = CAN_STATE_STOPPED;
+ return 0;
+ }
+
+ priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n");
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+ priv->can.state = CAN_STATE_ACTIVE;
+ /* enable all interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_ALL);
+
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ priv->write_reg(dev, REG_MOD, 0x00);
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n");
+ return 1;
+
+}
+
+static void sja1000_start(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* Clear error counters and error code capture */
+ priv->write_reg(dev, REG_TXERR, 0x0);
+ priv->write_reg(dev, REG_RXERR, 0x0);
+ priv->read_reg(dev, REG_ECC);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int sja1000_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ if (!priv->open_time)
+ return -EINVAL;
+
+ sja1000_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sja1000_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ u8 status;
+
+ /* FIXME: inspecting the status register to get the current state
+ * is not really necessary, because state changes are handled by
+ * in the ISR and the variable priv->can.state gets updated. The
+ * CAN devicde interface needs fixing!
+ */
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ *state = CAN_STATE_STOPPED;
+ } else {
+ status = priv->read_reg(dev, REG_SR);
+ if (status & SR_BS)
+ *state = CAN_STATE_BUS_OFF;
+ else if (status & SR_ES) {
+ if (priv->read_reg(dev, REG_TXERR) > 127 ||
+ priv->read_reg(dev, REG_RXERR) > 127)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_WARNING;
+ } else
+ *state = CAN_STATE_ACTIVE;
+ }
+ /* Check state */
+ if (*state != priv->can.state)
+ dev_err(ND2D(dev),
+ "Oops, state mismatch: hard %d != soft %d\n",
+ *state, priv->can.state);
+ spin_unlock_irq(&priv->can.irq_lock);
+ return 0;
+}
+
+static int sja1000_set_bittiming(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+ btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+ (((bt->phase_seg2 - 1) & 0x7) << 4) |
+ ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 7);
+
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ priv->write_reg(dev, REG_BTR0, btr0);
+ priv->write_reg(dev, REG_BTR1, btr1);
+
+ return 0;
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* set clock divider and output control register */
+ priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN);
+
+ /* set acceptance filter (accept all) */
+ priv->write_reg(dev, REG_ACCC0, 0x00);
+ priv->write_reg(dev, REG_ACCC1, 0x00);
+ priv->write_reg(dev, REG_ACCC2, 0x00);
+ priv->write_reg(dev, REG_ACCC3, 0x00);
+
+ priv->write_reg(dev, REG_ACCM0, 0xFF);
+ priv->write_reg(dev, REG_ACCM1, 0xFF);
+ priv->write_reg(dev, REG_ACCM2, 0xFF);
+ priv->write_reg(dev, REG_ACCM3, 0xFF);
+
+ priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5);
+ priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3);
+ priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++)
+ priv->write_reg(dev, dreg++, cf->data[i]);
+
+ stats->tx_bytes += dlc;
+ dev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, dev, 0);
+
+ priv->write_reg(dev, REG_CMR, CMD_TR);
+
+ return 0;
+}
+
+static void sja1000_rx(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = priv->read_reg(dev, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << (5 + 16))
+ | (priv->read_reg(dev, REG_ID2) << (5 + 8))
+ | (priv->read_reg(dev, REG_ID3) << 5)
+ | (priv->read_reg(dev, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << 3)
+ | (priv->read_reg(dev, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++)
+ cf->data[i] = priv->read_reg(dev, dreg++);
+
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ priv->write_reg(dev, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state state = priv->can.state;
+ uint8_t ecc, alc;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ dev_dbg(ND2D(dev), "data overrun interrupt\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ priv->can.can_stats.data_overrun++;
+ priv->write_reg(dev, REG_CMR, CMD_CDO); /* clear bit */
+ }
+
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ priv->can.can_stats.error_warning++;
+ dev_dbg(ND2D(dev), "error warning interrupt\n");
+
+ if (status & SR_BS) {
+ state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(dev);
+ } else if (status & SR_ES) {
+ state = CAN_STATE_BUS_WARNING;
+ } else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ priv->can.can_stats.bus_error++;
+ ecc = priv->read_reg(dev, REG_ECC);
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & ECC_MASK) {
+ case ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & ECC_SEG;
+ break;
+ }
+ /* Error occured during transmission? */
+ if ((ecc & ECC_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ dev_dbg(ND2D(dev), "error passive interrupt\n");
+ priv->can.can_stats.error_passive++;
+ if (status & SR_ES)
+ state = CAN_STATE_BUS_PASSIVE;
+ else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ dev_dbg(ND2D(dev), "arbitration lost interrupt\n");
+ alc = priv->read_reg(dev, REG_ALC);
+ priv->can.can_stats.arbitration_lost++;
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = alc & 0x1f;
+ }
+
+ if (state != priv->can.state && (state == CAN_STATE_BUS_WARNING ||
+ state == CAN_STATE_BUS_PASSIVE)) {
+ uint8_t rxerr = priv->read_reg(dev, REG_RXERR);
+ uint8_t txerr = priv->read_reg(dev, REG_TXERR);
+ cf->can_id |= CAN_ERR_CRTL;
+ if (state == CAN_STATE_BUS_WARNING)
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ else
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+
+ priv->can.state = state;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+irqreturn_t sja1000_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ uint8_t isrc, status;
+ int n = 0;
+
+ /* Shared interrupts and IRQ off? */
+ if (priv->read_reg(dev, REG_IER) == IRQ_OFF)
+ return IRQ_NONE;
+
+ if (priv->pre_irq)
+ priv->pre_irq(dev);
+
+ while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
+ n++;
+ status = priv->read_reg(dev, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can.can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+ while (status & SR_RBS) {
+ sja1000_rx(dev);
+ status = priv->read_reg(dev, REG_SR);
+ }
+ }
+ if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
+ /* error interrupt */
+ if (sja1000_err(dev, isrc, status))
+ break;
+ }
+ }
+
+ if (priv->post_irq)
+ priv->post_irq(dev);
+
+ if (n >= SJA1000_MAX_IRQ)
+ dev_dbg(ND2D(dev), "%d messages handled in ISR", n);
+
+ return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL_GPL(sja1000_interrupt);
+
+static int sja1000_open(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* determine and set bittime */
+ err = can_set_bittiming(dev);
+ if (err)
+ return err;
+
+ /* register interrupt handler, if not done by the device driver */
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
+ err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED,
+ dev->name, (void *)dev);
+ if (err)
+ return -EAGAIN;
+ }
+
+ /* init and start chi */
+ sja1000_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int sja1000_close(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ set_reset_mode(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+ can_close_cleanup(dev);
+
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER))
+ free_irq(dev->irq, (void *)dev);
+
+ return 0;
+}
+
+struct net_device *alloc_sja1000dev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+
+ dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct sja1000_priv);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_sja1000dev);
+
+void free_sja1000dev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL_GPL(free_sja1000dev);
+
+int register_sja1000dev(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!sja1000_probe_chip(dev))
+ return -ENODEV;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ dev->open = sja1000_open;
+ dev->stop = sja1000_close;
+ dev->hard_start_xmit = sja1000_start_xmit;
+
+ priv->can.bittiming_const = &sja1000_bittiming_const;
+ priv->can.do_set_bittiming = sja1000_set_bittiming;
+ priv->can.do_get_state = sja1000_get_state;
+ priv->can.do_set_mode = sja1000_set_mode;
+ priv->dev = dev;
+
+ err = register_candev(dev);
+ if (err) {
+ printk(KERN_INFO
+ "%s: registering netdev failed\n", DRV_NAME);
+ free_netdev(dev);
+ return err;
+ }
+
+ set_reset_mode(dev);
+ chipset_init(dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_sja1000dev);
+
+void unregister_sja1000dev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_sja1000dev);
+
+static __init int sja1000_init(void)
+{
+ printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME);
+
+ return 0;
+}
+
+module_init(sja1000_init);
+
+static __exit void sja1000_exit(void)
+{
+ printk(KERN_INFO "%s: driver removed\n", DRV_NAME);
+}
+
+module_exit(sja1000_exit);
diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h
new file mode 100644
index 000000000000..60d4cd690a24
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000.h
@@ -0,0 +1,177 @@
+/*
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000_DEV_H
+#define SJA1000_DEV_H
+
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+
+#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG 0x1F
+#define ECC_DIR 0x20
+#define ECC_ERR 6
+#define ECC_BIT 0x00
+#define ECC_FORM 0x40
+#define ECC_STUFF 0x80
+#define ECC_MASK 0xc0
+
+/*
+ * Flags for sja1000priv.flags
+ */
+#define SJA1000_CUSTOM_IRQ_HANDLER 0x1
+
+/*
+ * SJA1000 private data structure
+ */
+struct sja1000_priv {
+ struct can_priv can; /* must be the first member! */
+ long open_time;
+ struct sk_buff *echo_skb;
+
+ u8 (*read_reg) (struct net_device *dev, int reg);
+ void (*write_reg) (struct net_device *dev, int reg, u8 val);
+ void (*pre_irq) (struct net_device *dev);
+ void (*post_irq) (struct net_device *dev);
+
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+
+ u8 ocr;
+ u8 cdr;
+ u32 flags;
+};
+
+struct net_device *alloc_sja1000dev(int sizeof_priv);
+void free_sja1000dev(struct net_device *dev);
+int register_sja1000dev(struct net_device *dev);
+void unregister_sja1000dev(struct net_device *dev);
+
+irqreturn_t sja1000_interrupt(int irq, void *dev_id);
+
+#endif /* SJA1000_DEV_H */
diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c
new file mode 100644
index 000000000000..a6c5d760895f
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000_platform.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2005 Sascha Hauer, Pengutronix
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000_platform"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
+MODULE_LICENSE("GPL v2");
+
+static u8 sp_read_reg(struct net_device *dev, int reg)
+{
+ return ioread8((void __iomem *)(dev->base_addr + reg));
+}
+
+static void sp_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + reg));
+}
+
+static int sp_probe(struct platform_device *pdev)
+{
+ int err, irq;
+ void __iomem *addr;
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res_mem, *res_irq;
+ struct sja1000_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data provided!\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_mem || !res_irq) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (!request_mem_region(res_mem->start,
+ res_mem->end - res_mem->start + 1,
+ DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ addr = ioremap_nocache(res_mem->start,
+ res_mem->end - res_mem->start + 1);
+ if (!addr) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+
+ irq = res_irq->start;
+ if (res_irq->flags & IRQF_TRIGGER_MASK)
+ set_irq_type(irq, res_irq->flags & IRQF_TRIGGER_MASK);
+
+ dev = alloc_sja1000dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_iounmap;
+ }
+ priv = netdev_priv(dev);
+
+ priv->read_reg = sp_read_reg;
+ priv->write_reg = sp_write_reg;
+ priv->can.bittiming.clock = pdata->clock;
+ priv->ocr = pdata->ocr;
+ priv->cdr = pdata->cdr;
+
+ dev->irq = irq;
+ dev->base_addr = (unsigned long)addr;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n",
+ DRV_NAME, dev->base_addr, dev->irq);
+ return 0;
+
+ exit_free:
+ free_sja1000dev(dev);
+ exit_iounmap:
+ iounmap(addr);
+ exit_release:
+ release_mem_region(res_mem->start, res_mem->end - res_mem->start + 1);
+ exit:
+ return err;
+}
+
+static int sp_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ unregister_sja1000dev(dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ if (dev->base_addr)
+ iounmap((void __iomem *)dev->base_addr);
+
+ free_sja1000dev(dev);
+
+ return 0;
+}
+
+static struct platform_driver sp_driver = {
+ .probe = sp_probe,
+ .remove = sp_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sp_init(void)
+{
+ return platform_driver_register(&sp_driver);
+}
+
+static void __exit sp_exit(void)
+{
+ platform_driver_unregister(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
new file mode 100644
index 000000000000..80fcff3e69d8
--- /dev/null
+++ b/drivers/net/can/slcan.c
@@ -0,0 +1,904 @@
+/*
+ * slcan.c - serial line CAN interface driver (using tty line discipline)
+ *
+ * Copyright (c) 2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+/*
+ * This file is derived from linux/drivers/net/slip.c
+ *
+ * Therefore it has the same (strange?) behaviour not to unregister the
+ * netdevice when detaching the tty. Is there any better solution?
+ *
+ * Do not try to attach, detach and re-attach a tty for this reason ...
+ *
+ * slip.c Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/if_slip.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <linux/can.h>
+
+static __initdata const char banner[] =
+ KERN_INFO "slcan: serial line CAN interface driver\n";
+
+MODULE_ALIAS_LDISC(N_SLCAN);
+MODULE_DESCRIPTION("serial line CAN interface");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int debug;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "slcan %s: ", __func__), \
+ printk(args)) : 0)
+#else
+#define DBG(args...)
+#endif
+
+#ifndef N_SLCAN
+#error Your kernel does not support tty line discipline N_SLCAN
+#endif
+/*
+ * As there is currently no line discipline N_SLCAN in the mainstream kernel
+ * you will have to modify two kernel includes & recompile the kernel.
+ *
+ * Add this in include/asm/termios.h after the definition of N_HCI:
+ * #define N_SLCAN 16
+ *
+ * Increment NR_LDICS in include/linux/tty.h from 16 to 17
+ *
+ * NEW: Since Kernel 2.6.21 you only have to change include/linux/tty.h
+ *
+ */
+
+#define SLC_CHECK_TRANSMIT
+#define SLCAN_MAGIC 0x53CA
+
+static int maxdev = 10; /* MAX number of SLCAN channels;
+ This can be overridden with
+ insmod slcan.ko maxdev=nnn */
+module_param(maxdev, int, 0);
+MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
+
+/* maximum rx buffer len: extended CAN frame with timestamp */
+#define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
+
+struct slcan {
+ int magic;
+
+ /* Various fields. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct net_device *dev; /* easy for intr handling */
+ spinlock_t lock;
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char rbuff[SLC_MTU]; /* receiver buffer */
+ int rcount; /* received chars counter */
+ unsigned char xbuff[SLC_MTU]; /* transmitter buffer */
+ unsigned char *xhead; /* pointer to next XMIT byte */
+ int xleft; /* bytes left in XMIT queue */
+
+ unsigned long flags; /* Flag values/ mode etc */
+#define SLF_INUSE 0 /* Channel in use */
+#define SLF_ERROR 1 /* Parity, etc. error */
+
+ unsigned char leased;
+ dev_t line;
+ pid_t pid;
+};
+
+static struct net_device **slcan_devs;
+
+
+ /************************************************************************
+ * SLCAN ENCAPSULATION FORMAT *
+ ************************************************************************/
+
+/*
+ * A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended
+ * frame format) a data length code (can_dlc) which can be from 0 to 8
+ * and up to <can_dlc> data bytes as payload.
+ * Additionally a CAN frame may become a remote transmission frame if the
+ * RTR-bit is set. This causes another ECU to send a CAN frame with the
+ * given can_id.
+ *
+ * The SLCAN ASCII representation of these different frame types is:
+ * <type> <id> <dlc> <data>*
+ *
+ * Extended frames (29 bit) are defined by capital characters in the type.
+ * RTR frames are defined as 'r' types - normal frames have 't' type:
+ * t => 11 bit data frame
+ * r => 11 bit RTR frame
+ * T => 29 bit data frame
+ * R => 29 bit RTR frame
+ *
+ * The <id> is 3 (standard) or 8 (extended) bytes in ASCII Hex (base64).
+ * The <dlc> is a one byte ASCII number ('0' - '8')
+ * The <data> section has at much ASCII Hex bytes as defined by the <dlc>
+ *
+ * Examples:
+ *
+ * t1230 : can_id 0x123, can_dlc 0, no data
+ * t4563112233 : can_id 0x456, can_dlc 3, data 0x11 0x22 0x33
+ * T12ABCDEF2AA55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55
+ * r1230 : can_id 0x123, can_dlc 0, no data, remote transmission request
+ *
+ */
+
+ /************************************************************************
+ * STANDARD SLCAN DECAPSULATION *
+ ************************************************************************/
+
+static int asc2nibble(char c)
+{
+
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+
+ return 16; /* error */
+}
+
+/* Send one completely decapsulated can_frame to the network layer */
+static void slc_bump(struct slcan *sl)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+ struct sk_buff *skb;
+ struct can_frame cf;
+ int i, dlc_pos, tmp;
+ unsigned long ultmp;
+ char cmd = sl->rbuff[0];
+
+ if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R'))
+ return;
+
+ if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */
+ dlc_pos = 4; /* dlc position tiiid */
+ else
+ dlc_pos = 9; /* dlc position Tiiiiiiiid */
+
+ if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9')))
+ return;
+
+ cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */
+
+ sl->rbuff[dlc_pos] = 0; /* terminate can_id string */
+
+ if (strict_strtoul(sl->rbuff+1, 16, &ultmp))
+ return;
+ cf.can_id = ultmp;
+
+ if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */
+ cf.can_id |= CAN_EFF_FLAG;
+
+ if ((cmd | 0x20) == 'r') /* RTR frame */
+ cf.can_id |= CAN_RTR_FLAG;
+
+ *(u64 *) (&cf.data) = 0; /* clear payload */
+
+ for (i = 0, dlc_pos++; i < cf.can_dlc; i++) {
+
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] = (tmp << 4);
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] |= tmp;
+ }
+
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb)
+ return;
+
+ skb->dev = sl->dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(struct can_frame)),
+ &cf, sizeof(struct can_frame));
+ netif_rx(skb);
+
+ sl->dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf.can_dlc;
+}
+
+/* parse tty input stream */
+static void slcan_unesc(struct slcan *sl, unsigned char s)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ (sl->rcount > 4)) {
+ slc_bump(sl);
+ }
+ sl->rcount = 0;
+ } else {
+ if (!test_bit(SLF_ERROR, &sl->flags)) {
+ if (sl->rcount < SLC_MTU) {
+ sl->rbuff[sl->rcount++] = s;
+ return;
+ } else {
+ stats->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
+ }
+ }
+}
+
+ /************************************************************************
+ * STANDARD SLCAN ENCAPSULATION *
+ ************************************************************************/
+
+/* Encapsulate one can_frame and stuff into a TTY queue. */
+static void slc_encaps(struct slcan *sl, struct can_frame *cf)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+ int actual, idx, i;
+ char cmd;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ cmd = 'R'; /* becomes 'r' in standard frame format */
+ else
+ cmd = 'T'; /* becomes 't' in standard frame format */
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(sl->xbuff, "%c%08X%d", cmd,
+ cf->can_id & CAN_EFF_MASK, cf->can_dlc);
+ else
+ sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20,
+ cf->can_id & CAN_SFF_MASK, cf->can_dlc);
+
+ idx = strlen(sl->xbuff);
+
+ for (i = 0; i < cf->can_dlc; i++)
+ sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]);
+
+ DBG("ASCII frame = '%s'\n", sl->xbuff);
+
+ strcat(sl->xbuff, "\r"); /* add terminating character */
+
+ /* Order of next two lines is *very* important.
+ * When we are sending a little amount of data,
+ * the transfer may be completed inside driver.write()
+ * routine, because it's running with interrupts enabled.
+ * In this case we *never* got WRITE_WAKEUP event,
+ * if we did not request it before write operation.
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff));
+#ifdef SLC_CHECK_TRANSMIT
+ sl->dev->trans_start = jiffies;
+#endif
+ sl->xleft = strlen(sl->xbuff) - actual;
+ sl->xhead = sl->xbuff + actual;
+ stats->tx_bytes += cf->can_dlc;
+}
+
+/*
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void slcan_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev))
+ return;
+
+ if (sl->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ stats->tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+ return;
+ }
+
+ actual = tty->ops->write(tty, sl->xhead, sl->xleft);
+ sl->xleft -= actual;
+ sl->xhead += actual;
+}
+
+static void slc_tx_timeout(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock(&sl->lock);
+
+ if (netif_queue_stopped(dev)) {
+ if (!netif_running(dev))
+ goto out;
+
+ /* May be we must check transmitter timeout here ?
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+#ifdef SLC_CHECK_TRANSMIT
+ if (time_before(jiffies, dev->trans_start + 20 * HZ)) {
+ /* 20 sec timeout not reached */
+ goto out;
+ }
+ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+ (tty_chars_in_buffer(sl->tty) || sl->xleft)
+ ? "bad line quality" : "driver error");
+ sl->xleft = 0;
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+#endif
+ }
+out:
+ spin_unlock(&sl->lock);
+}
+
+
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Send a can_frame to a TTY queue. */
+static int slc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (skb->len != sizeof(struct can_frame))
+ goto out;
+
+ spin_lock(&sl->lock);
+ if (!netif_running(dev)) {
+ spin_unlock(&sl->lock);
+ printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
+ goto out;
+ }
+
+ if (sl->tty == NULL) {
+ spin_unlock(&sl->lock);
+ goto out;
+ }
+
+ netif_stop_queue(sl->dev);
+ slc_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */
+ spin_unlock(&sl->lock);
+
+out:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/* Netdevice UP -> DOWN routine */
+static int slc_close(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ /* TTY discipline is running. */
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ }
+ netif_stop_queue(dev);
+ sl->rcount = 0;
+ sl->xleft = 0;
+ spin_unlock_bh(&sl->lock);
+
+ return 0;
+}
+
+/* Netdevice DOWN -> UP routine */
+static int slc_open(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (sl->tty == NULL)
+ return -ENODEV;
+
+ sl->flags &= (1 << SLF_INUSE);
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Netdevice register callback */
+static void slc_setup(struct net_device *dev)
+{
+ dev->open = slc_open;
+ dev->destructor = free_netdev;
+ dev->stop = slc_close;
+ dev->hard_start_xmit = slc_xmit;
+
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->mtu = sizeof(struct can_frame);
+ dev->type = ARPHRD_CAN;
+#ifdef SLC_CHECK_TRANSMIT
+ dev->tx_timeout = slc_tx_timeout;
+ dev->watchdog_timeo = 20*HZ;
+#endif
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+}
+
+/******************************************
+ * Routines looking at TTY side.
+ ******************************************/
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLCAN data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing. This will not
+ * be re-entered while running but other ldisc functions may be called
+ * in parallel
+ */
+
+static void slcan_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ if (!sl || sl->magic != SLCAN_MAGIC ||
+ !netif_running(sl->dev))
+ return;
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ if (fp && *fp++) {
+ if (!test_and_set_bit(SLF_ERROR, &sl->flags))
+ stats->rx_errors++;
+ cp++;
+ continue;
+ }
+ slcan_unesc(sl, *cp++);
+ }
+}
+
+/************************************
+ * slcan_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void slc_sync(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->tty || sl->leased)
+ continue;
+ if (dev->flags&IFF_UP)
+ dev_close(dev);
+ }
+}
+
+
+/* Find a free SLCAN channel, and link in this `tty' line. */
+static struct slcan *slc_alloc(dev_t line)
+{
+ int i;
+ int sel = -1;
+ int score = -1;
+ struct net_device *dev = NULL;
+ struct slcan *sl;
+
+ if (slcan_devs == NULL)
+ return NULL; /* Master array missing ! */
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->leased) {
+ if (sl->line != line)
+ continue;
+ if (sl->tty)
+ return NULL;
+
+ /* Clear ESCAPE & ERROR flags */
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+
+ if (sl->tty)
+ continue;
+
+ if (current->pid == sl->pid) {
+ if (sl->line == line && score < 3) {
+ sel = i;
+ score = 3;
+ continue;
+ }
+ if (score < 2) {
+ sel = i;
+ score = 2;
+ }
+ continue;
+ }
+ if (sl->line == line && score < 1) {
+ sel = i;
+ score = 1;
+ continue;
+ }
+ if (score < 0) {
+ sel = i;
+ score = 0;
+ }
+ }
+
+ if (sel >= 0) {
+ i = sel;
+ dev = slcan_devs[i];
+ if (score > 1) {
+ sl = netdev_priv(dev);
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= maxdev)
+ return NULL;
+
+ if (dev) {
+ sl = netdev_priv(dev);
+ if (test_bit(SLF_INUSE, &sl->flags)) {
+ unregister_netdevice(dev);
+ dev = NULL;
+ slcan_devs[i] = NULL;
+ }
+ }
+
+ if (!dev) {
+ char name[IFNAMSIZ];
+ sprintf(name, "slc%d", i);
+
+ dev = alloc_netdev(sizeof(*sl), name, slc_setup);
+ if (!dev)
+ return NULL;
+ dev->base_addr = i;
+ }
+
+ sl = netdev_priv(dev);
+
+ /* Initialize channel control data */
+ sl->magic = SLCAN_MAGIC;
+ sl->dev = dev;
+ spin_lock_init(&sl->lock);
+ slcan_devs[i] = dev;
+
+ return sl;
+}
+
+/*
+ * Open the high-level part of the SLCAN channel.
+ * This function is called by the TTY module when the
+ * SLCAN line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLCAN channel...
+ *
+ * Called in process context serialized from other ldisc calls.
+ */
+
+static int slcan_open(struct tty_struct *tty)
+{
+ struct slcan *sl;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+
+ /* RTnetlink lock is misused here to serialize concurrent
+ opens of slcan channels. There are better ways, but it is
+ the simplest one.
+ */
+ rtnl_lock();
+
+ /* Collect hanged up channels. */
+ slc_sync();
+
+ sl = (struct slcan *) tty->disc_data;
+
+ err = -EEXIST;
+ /* First make sure we're not already connected. */
+ if (sl && sl->magic == SLCAN_MAGIC)
+ goto err_exit;
+
+ /* OK. Find a free SLCAN channel to use. */
+ err = -ENFILE;
+ sl = slc_alloc(tty_devnum(tty));
+ if (sl == NULL)
+ goto err_exit;
+
+ sl->tty = tty;
+ tty->disc_data = sl;
+ sl->line = tty_devnum(tty);
+ sl->pid = current->pid;
+
+ if (!test_bit(SLF_INUSE, &sl->flags)) {
+ /* Perform the low-level SLCAN initialization. */
+ sl->rcount = 0;
+ sl->xleft = 0;
+
+ set_bit(SLF_INUSE, &sl->flags);
+
+ err = register_netdevice(sl->dev);
+ if (err)
+ goto err_free_chan;
+ }
+
+ /* Done. We have linked the TTY line to a channel. */
+ rtnl_unlock();
+
+ tty->receive_room = 65536; /* We don't flow control */
+
+ return sl->dev->base_addr;
+
+err_free_chan:
+ sl->tty = NULL;
+ tty->disc_data = NULL;
+ clear_bit(SLF_INUSE, &sl->flags);
+
+err_exit:
+ rtnl_unlock();
+
+ /* Count references from TTY module */
+ return err;
+}
+
+/*
+
+ FIXME: 1,2 are fixed 3 was never true anyway.
+
+ Let me to blame a bit.
+ 1. TTY module calls this funstion on soft interrupt.
+ 2. TTY module calls this function WITH MASKED INTERRUPTS!
+ 3. TTY module does not notify us about line discipline
+ shutdown,
+
+ Seems, now it is clean. The solution is to consider netdevice and
+ line discipline sides as two independent threads.
+
+ By-product (not desired): slc? does not feel hangups and remains open.
+ It is supposed, that user level program (dip, diald, slattach...)
+ will catch SIGHUP and make the rest of work.
+
+ I see no way to make more with current tty code. --ANK
+ */
+
+/*
+ * Close down a SLCAN channel.
+ * This means flushing out any pending queues, and then returning. This
+ * call is serialized against other ldisc functions.
+ */
+static void slcan_close(struct tty_struct *tty)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty)
+ return;
+
+ tty->disc_data = NULL;
+ sl->tty = NULL;
+ if (!sl->leased)
+ sl->line = 0;
+
+ /* Count references from TTY module */
+}
+
+/* Perform I/O control on an active SLCAN channel. */
+static int slcan_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGIFNAME:
+ tmp = strlen(sl->dev->name) + 1;
+ if (copy_to_user((void __user *)arg, sl->dev->name, tmp))
+ return -EFAULT;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return -EINVAL;
+
+ default:
+ return tty_mode_ioctl(tty, file, cmd, arg);
+ }
+}
+
+static struct tty_ldisc_ops slc_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "slcan",
+ .open = slcan_open,
+ .close = slcan_close,
+ .ioctl = slcan_ioctl,
+ .receive_buf = slcan_receive_buf,
+ .write_wakeup = slcan_write_wakeup,
+};
+
+/************************************
+ * general slcan module init/exit
+ ************************************/
+
+static int __init slcan_init(void)
+{
+ int status;
+
+ if (maxdev < 4)
+ maxdev = 4; /* Sanity */
+
+ printk(banner);
+ printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev);
+
+ slcan_devs = kmalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL);
+ if (!slcan_devs) {
+ printk(KERN_ERR "slcan: can't allocate slcan device array!\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(slcan_devs, 0, sizeof(struct net_device *)*maxdev);
+
+ /* Fill in our line protocol discipline, and register it */
+ status = tty_register_ldisc(N_SLCAN, &slc_ldisc);
+ if (status != 0) {
+ printk(KERN_ERR "slcan: can't register line discipline\n");
+ kfree(slcan_devs);
+ }
+ return status;
+}
+
+static void __exit slcan_exit(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+ unsigned long timeout = jiffies + HZ;
+ int busy = 0;
+
+ if (slcan_devs == NULL)
+ return;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy)
+ msleep_interruptible(100);
+
+ busy = 0;
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ sl = netdev_priv(dev);
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ busy++;
+ tty_hangup(sl->tty);
+ }
+ spin_unlock_bh(&sl->lock);
+ }
+ } while (busy && time_before(jiffies, timeout));
+
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ slcan_devs[i] = NULL;
+
+ sl = netdev_priv(dev);
+ if (sl->tty) {
+ printk(KERN_ERR "%s: tty discipline still running\n",
+ dev->name);
+ /* Intentionally leak the control block. */
+ dev->destructor = NULL;
+ }
+
+ unregister_netdev(dev);
+ }
+
+ kfree(slcan_devs);
+ slcan_devs = NULL;
+
+ i = tty_unregister_ldisc(N_SLCAN);
+ if (i)
+ printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i);
+}
+
+module_init(slcan_init);
+module_exit(slcan_exit);
diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile
new file mode 100644
index 000000000000..df3fceca7bd0
--- /dev/null
+++ b/drivers/net/can/softing/Makefile
@@ -0,0 +1,20 @@
+# Makefile for softing CAN driver
+
+ifeq ($(KERNELRELEASE),)
+# necessary when used outside kernel
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+softing-y := softing_main.o softing_fw.o
+obj-$(CONFIG_CAN_SOFTING) += softing.o
+obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o
+
+endif
diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h
new file mode 100644
index 000000000000..e43c7f116799
--- /dev/null
+++ b/drivers/net/can/softing/softing.h
@@ -0,0 +1,268 @@
+/*
+ * softing common interfaces
+ *
+ * by Kurt Van Dijck, 06-2008
+ */
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+struct softing;
+struct sofing_desc;
+
+/* special attribute, so we should not rely on the ->priv pointers
+ * before knowing how to interpret these
+ */
+struct softing_attribute;
+
+struct softing_priv {
+ struct can_priv can; /* must be the first member! */
+ struct net_device *netdev;
+ struct softing *card;
+ struct {
+ int pending;
+ /* variables wich hold the circular buffer */
+ int echo_put;
+ int echo_get;
+ } tx;
+ struct can_bittiming_const btr_const;
+ int index;
+ u8 output;
+ u16 chip;
+ struct attribute_group sysfs;
+};
+#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev))
+
+/* the 'all cards have the same' fields definition */
+extern const struct can_bittiming_const softing_btr_const;
+
+struct softing_desc {
+ unsigned int manf;
+ unsigned int prod;
+ /* generation
+ * 1st with NEC or SJA1000
+ * 8bit, exclusive interrupt, ...
+ * 2nd only SJA11000
+ * 16bit, shared interrupt
+ */
+ int generation;
+ unsigned int freq; /*crystal in MHz */
+ unsigned int max_brp;
+ unsigned int max_sjw;
+ unsigned long dpram_size;
+ char name[32];
+ struct {
+ unsigned long offs;
+ unsigned long addr;
+ char fw[32];
+ } boot, load, app;
+};
+
+struct softing {
+ int nbus;
+ struct softing_priv *bus[2];
+ spinlock_t spin; /* protect this structure & DPRAM access */
+
+ struct {
+ /* indication of firmware status */
+ int up;
+ /* protection of the 'up' variable */
+ struct mutex lock;
+ } fw;
+ struct {
+ int nr;
+ int requested;
+ struct tasklet_struct bh;
+ int svc_count;
+ } irq;
+ struct {
+ int pending;
+ int last_bus;
+ /* keep the bus that last tx'd a message,
+ * in order to let every netdev queue resume
+ */
+ } tx;
+ struct {
+ unsigned long phys;
+ unsigned long size;
+ unsigned char *virt;
+ unsigned char *end;
+ struct softing_fct *fct;
+ struct softing_info *info;
+ struct softing_rx *rx;
+ struct softing_tx *tx;
+ struct softing_irq *irq;
+ unsigned short *command;
+ unsigned short *receipt;
+ } dpram;
+ struct {
+ unsigned short manf;
+ unsigned short prod;
+ u32 serial, fw, hw, lic;
+ u16 chip [2];
+ u32 freq;
+ const char *name;
+ } id;
+ const struct softing_desc *desc;
+ struct {
+ int (*reset) (struct softing *, int);
+ int (*enable_irq)(struct softing *, int);
+ } fn;
+ struct device *dev;
+ /* sysfs */
+ struct attribute_group sysfs;
+ struct softing_attribute *attr;
+ struct attribute **grp;
+};
+
+extern int mk_softing(struct softing *);
+/* fields that must be set already are :
+ * ncan
+ * id.manf
+ * id.prod
+ * fn.reset
+ * fn.enable_irq
+ */
+extern void rm_softing(struct softing *);
+/* usefull functions during operation */
+
+extern const struct softing_desc *
+ softing_lookup_desc(unsigned int manf, unsigned int prod);
+
+extern int softing_default_output(struct softing *card
+ , struct softing_priv *priv);
+extern u32 softing_time2usec(struct softing *card, u32 raw);
+
+extern int softing_fct_cmd(struct softing *card
+ , int cmd, int vector, const char *msg);
+
+extern int softing_bootloader_command(struct softing *card
+ , int command, const char *msg);
+
+/* Load firmware after reset */
+extern int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset);
+
+/* Load final application firmware after bootloader */
+extern int softing_load_app_fw(const char *file, struct softing *card);
+
+extern int softing_reset_chip(struct softing *card);
+
+/* enable or disable irq
+ * only called with fw.lock locked
+ */
+extern int softing_card_irq(struct softing *card, int enable);
+
+/* called when tx queue is flushed */
+extern void softing_flush_echo_skb(struct softing_priv *priv);
+
+/* reinitaliase the card, apply -1 for bus[01] for 'no change' */
+extern int softing_reinit(struct softing *card, int bus0, int bus1);
+
+/* SOFTING DPRAM mappings */
+struct softing_rx {
+ u8 fifo[16][32];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u16 dummy3;
+ u16 lost_msg;
+} __attribute__((packed));
+
+#define TXMAX 31
+struct softing_tx {
+ u8 fifo[32][16];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u8 dummy3;
+} __attribute__((packed));
+
+struct softing_irq {
+ u8 to_host;
+ u8 to_card;
+} __attribute__((packed));
+
+struct softing_fct {
+ s16 param[20]; /* 0 is index */
+ s16 returned;
+ u8 dummy;
+ u16 host_access;
+} __attribute__((packed));
+
+struct softing_info {
+ u8 dummy1;
+ u16 bus_state;
+ u16 dummy2;
+ u16 bus_state2;
+ u16 dummy3;
+ u16 error_state;
+ u16 dummy4;
+ u16 error_state2;
+ u16 dummy5;
+ u16 reset;
+ u16 dummy6;
+ u16 clear_rcv_fifo;
+ u16 dummy7;
+ u16 dummyxx;
+ u16 dummy8;
+ u16 time_reset;
+ u8 dummy9;
+ u32 time;
+ u32 time_wrap;
+ u8 wr_start;
+ u8 wr_end;
+ u8 dummy10;
+ u16 dummy12;
+ u16 dummy12x;
+ u16 dummy13;
+ u16 reset_rcv_fifo;
+ u8 dummy14;
+ u8 reset_xmt_fifo;
+ u8 read_fifo_levels;
+ u16 rcv_fifo_level;
+ u16 xmt_fifo_level;
+} __attribute__((packed));
+
+/* DPRAM return codes */
+#define RES_NONE 0
+#define RES_OK 1
+#define RES_NOK 2
+#define RES_UNKNOWN 3
+/* DPRAM flags */
+#define CMD_TX 0x01
+#define CMD_ACK 0x02
+#define CMD_XTD 0x04
+#define CMD_RTR 0x08
+#define CMD_ERR 0x10
+#define CMD_BUS2 0x80
+
+/* debug */
+extern int softing_debug;
+
+#define mod_alert(fmt,arg...) { \
+ if (softing_debug >= 0) \
+ printk(KERN_ALERT "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_info(fmt,arg...) { \
+ if (softing_debug >= 1) \
+ printk(KERN_INFO "[%s] %s:" fmt "\n"\
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_trace(fmt,arg...) { \
+ if (softing_debug >= 2) \
+ printk(KERN_DEBUG "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+
diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c
new file mode 100644
index 000000000000..e33b614de28f
--- /dev/null
+++ b/drivers/net/can/softing/softing_cs.c
@@ -0,0 +1,427 @@
+/*
+* drivers/net/can/softing/softing_cs.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the version 2 of the GNU General Public License
+* as published by the Free Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/system.h>
+
+#include "softing.h"
+
+struct softing_cs {
+ struct softing softing;
+ win_req_t win;
+};
+#define softing2cs(x) container_of((x), struct softing_cs, softing)
+
+struct lookup {
+ int i;
+ const char *a;
+};
+
+static const char __devinit *lookup_mask(const struct lookup *lp, int *i)
+{
+ for (; lp->a; ++lp) {
+ if (lp->i & *i) {
+ *i &= ~lp->i;
+ return lp->a;
+ }
+ }
+ return 0;
+}
+
+static int card_reset_via_pcmcia(struct softing *sdev, int v)
+{
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 2;
+ reg.Value = v ? 0 : 0x20;
+ return pcmcia_access_configuration_register(pcmcia, &reg);
+}
+
+static int card_reset_via_dpram(struct softing *sdev, int v)
+{
+ if (v) {
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] &= ~1;
+ spin_unlock_bh(&sdev->spin);
+ card_reset_via_pcmcia(sdev, v);
+ } else {
+ card_reset_via_pcmcia(sdev, v);
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] |= 1;
+ spin_unlock_bh(&sdev->spin);
+ }
+ return 0;
+}
+
+static int card_enable_irq_via_pcmcia(struct softing *sdev, int v)
+{
+ int ret;
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ memset(&reg, 0, sizeof(reg));
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 0;
+ reg.Value = v ? 0x60 : 0;
+ ret = pcmcia_access_configuration_register(pcmcia, &reg);
+ if (ret)
+ mod_alert("failed %u", ret);
+ return ret;
+}
+
+/* TODO: in 2.6.26, __devinitconst works*/
+static const __devinitdata struct lookup pcmcia_io_attr[] = {
+ { IO_DATA_PATH_WIDTH_AUTO , "[auto]" , },
+ { IO_DATA_PATH_WIDTH_8 , "8bit" , },
+ { IO_DATA_PATH_WIDTH_16 , "16bit" , },
+ { 0, 0, },
+};
+
+static const __devinitdata struct lookup pcmcia_mem_attr[] = {
+ { WIN_ADDR_SPACE_IO , "IO" , },
+ { WIN_MEMORY_TYPE_AM , "typeAM" , },
+ { WIN_ENABLE , "enable" , },
+ { WIN_DATA_WIDTH_8 , "8bit" , },
+ { WIN_DATA_WIDTH_16 , "16bit" , },
+ { WIN_DATA_WIDTH_32 , "32bit" , },
+ { WIN_PAGED , "paged" , },
+ { WIN_SHARED , "shared" , },
+ { WIN_FIRST_SHARED , "first_shared", },
+ { WIN_USE_WAIT , "wait" , },
+ { WIN_STRICT_ALIGN , "strict_align", },
+ { WIN_MAP_BELOW_1MB , "below_1MB" , },
+ { WIN_PREFETCH , "prefetch" , },
+ { WIN_CACHEABLE , "cacheable" , },
+ { 0, 0, },
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+/* backported */
+struct pcmcia_cfg_mem {
+ tuple_t tuple;
+ cisparse_t parse;
+ u8 buf[256];
+ cistpl_cftable_entry_t dflt;
+};
+static int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_cfg_mem *cfg_mem;
+
+ tuple_t *tuple;
+ int ret = -ENODEV;
+ unsigned int vcc;
+
+ cfg_mem = kzalloc(sizeof(*cfg_mem), GFP_KERNEL);
+ if (cfg_mem == NULL)
+ return -ENOMEM;
+
+ /* get the current Vcc setting */
+ vcc = p_dev->socket->socket.Vcc;
+
+ tuple = &cfg_mem->tuple;
+ tuple->TupleData = cfg_mem->buf;
+ tuple->TupleDataMax = sizeof(cfg_mem->buf)-1;
+ tuple->TupleOffset = 0;
+ tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple->Attributes = 0;
+
+ ret = pcmcia_get_first_tuple(p_dev, tuple);
+ while (!ret) {
+ cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+ if (pcmcia_get_tuple_data(p_dev, tuple))
+ goto next_entry;
+
+ if (pcmcia_parse_tuple(p_dev, tuple, &cfg_mem->parse))
+ goto next_entry;
+
+ /* default values */
+ p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pcmcia_get_next_tuple(p_dev, tuple);
+ }
+ kfree(cfg_mem);
+ return ret;
+}
+#endif
+
+static int dev_conf_check(struct pcmcia_device *pdev,
+ cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *def_cf,
+ unsigned int vcc, void *priv_data)
+{
+ struct softing_cs *csdev = priv_data;
+ struct softing *sdev = &csdev->softing;
+ int ret;
+
+ if (!cf->index)
+ goto do_next;
+ /* power settings (Vcc & Vpp) */
+ if (cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ } else if (def_cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != def_cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ }
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ else if (def_cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = def_cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* interrupt ? */
+ if (cf->irq.IRQInfo1 || def_cf->irq.IRQInfo1)
+ pdev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window */
+ pdev->io.NumPorts1
+ = pdev->io.NumPorts2
+ = 0;
+ /* Memory window */
+ if ((cf->mem.nwin > 0) || (def_cf->mem.nwin > 0)) {
+ memreq_t map;
+ cistpl_mem_t *mem
+ = (cf->mem.nwin) ? &cf->mem : &def_cf->mem;
+ /* softing specific: choose 8 or 16bit access */
+ csdev->win.Attributes = ((sdev->desc->generation >= 2)
+ ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8)
+ | WIN_MEMORY_TYPE_CM
+ | WIN_ENABLE;
+ csdev->win.Base = mem->win[0].host_addr;
+ csdev->win.Size = mem->win[0].len;
+ csdev->win.AccessSpeed = 0;
+ ret = pcmcia_request_window(&pdev, &csdev->win, &pdev->win);
+ if (ret) {
+ mod_alert("pcmcia_request_window() mismatch\n");
+ goto do_next;
+ }
+ /* softing specific: choose slower access for old cards */
+ if (sdev->desc->generation < 2) {
+ pdev->win->ctl.flags
+ = MAP_ACTIVE | MAP_USE_WAIT;
+ pdev->win->ctl.speed = 3;
+ }
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(pdev->win, &map)) {
+ mod_alert("pcmcia_map_mem_page() mismatch\n");
+ goto do_next_win;
+ }
+ } else {
+ mod_info("no memory window in tuple %u", cf->index);
+ goto do_next;
+ }
+ return 0;
+do_next_win:
+do_next:
+ pcmcia_disable_device(pdev);
+ return -ENODEV;
+}
+
+static void driver_remove(struct pcmcia_device *pcmcia)
+{
+ struct softing *card = (struct softing *)pcmcia->priv;
+ struct softing_cs *cs = softing2cs(card);
+ mod_trace("%s,device'%s'", card->id.name, pcmcia->devname);
+ rm_softing(card);
+ /* release pcmcia stuff */
+ pcmcia_disable_device(pcmcia);
+ /* free bits */
+ kfree(cs);
+}
+
+static int __devinit driver_probe(struct pcmcia_device *pcmcia)
+{
+ struct softing_cs *cs;
+ struct softing *card;
+
+ mod_trace("on %s", pcmcia->devname);
+
+ /* Create new softing device */
+ cs = kzalloc(sizeof(*cs), GFP_KERNEL);
+ if (!cs)
+ goto no_mem;
+ /* setup links */
+ card = &cs->softing;
+ pcmcia->priv = card;
+ card->dev = &pcmcia->dev;
+ /* properties */
+ card->id.manf = pcmcia->manf_id;
+ card->id.prod = pcmcia->card_id;
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (card->desc->generation >= 2) {
+ card->fn.reset = card_reset_via_dpram;
+ } else {
+ card->fn.reset = card_reset_via_pcmcia;
+ card->fn.enable_irq = card_enable_irq_via_pcmcia;
+ }
+
+ card->nbus = 2;
+ /* pcmcia presets */
+ pcmcia->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ pcmcia->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ pcmcia->irq.Handler = 0;
+ pcmcia->conf.Attributes = 0;
+ pcmcia->conf.IntType = INT_MEMORY_AND_IO;
+
+ if (pcmcia_loop_config(pcmcia, dev_conf_check, cs))
+ goto config_failed;
+
+ if (pcmcia_request_irq(pcmcia, &pcmcia->irq))
+ goto config_failed;
+
+ if (pcmcia_request_configuration(pcmcia, &pcmcia->conf))
+ goto config_failed;
+
+ card->dpram.phys = cs->win.Base;
+ card->dpram.size = cs->win.Size;
+
+ if (card->dpram.size != 0x1000) {
+ mod_alert("dpram size 0x%lx mismatch\n", card->dpram.size);
+ goto wrong_dpram;
+ }
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "[%s] %s: index 0x%02x",
+ THIS_MODULE->name,
+ pcmcia->devname,
+ pcmcia->conf.ConfigIndex);
+ if (pcmcia->conf.Vpp)
+ printk(", Vpp %d.%d", pcmcia->conf.Vpp/10, pcmcia->conf.Vpp%10);
+ if (pcmcia->conf.Attributes & CONF_ENABLE_IRQ) {
+ printk(", irq %d", pcmcia->irq.AssignedIRQ);
+ card->irq.nr = pcmcia->irq.AssignedIRQ;
+ }
+ if (pcmcia->win) {
+ int tmp;
+ const char *p;
+ printk(", mem 0x%08lx-0x%08lx"
+ , card->dpram.phys
+ , card->dpram.phys + card->dpram.size-1);
+ tmp = cs->win.Attributes;
+ while (tmp) {
+ p = lookup_mask(pcmcia_mem_attr, &tmp);
+ if (p)
+ printk(" %s", p);
+ }
+ }
+ printk("\n");
+
+ if (mk_softing(card))
+ goto softing_failed;
+ return 0;
+
+softing_failed:
+wrong_dpram:
+config_failed:
+ kfree(cs);
+no_mem:
+ pcmcia_disable_device(pcmcia);
+ return -ENODEV;
+}
+
+static struct pcmcia_device_id driver_ids[] = {
+ /* softing */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005),
+ /* vector , manufacturer? */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085),
+ /* EDIC */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, driver_ids);
+
+static struct pcmcia_driver softing_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "softing_cs",
+ },
+ .probe = driver_probe,
+ .remove = driver_remove,
+ .id_table = driver_ids,
+};
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return pcmcia_register_driver(&softing_cs_driver);
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+ pcmcia_unregister_driver(&softing_cs_driver);
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("softing CANcard driver"
+ ", links PCMCIA card to softing driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("softing CANcard2");
+
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
new file mode 100644
index 000000000000..4d20774c9cb9
--- /dev/null
+++ b/drivers/net/can/softing/softing_fw.c
@@ -0,0 +1,685 @@
+/*
+* drivers/net/can/softing/softing_fw.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the version 2 of the GNU General Public License
+* as published by the Free Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+#define fw_dir "softing-4.6/"
+
+const struct can_bittiming_const softing_btr_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4, /* overruled */
+ .brp_min = 1,
+ .brp_max = 32, /* overruled */
+ .brp_inc = 1,
+};
+
+static const struct softing_desc carddescs[] = {
+{
+ .name = "CANcard",
+ .manf = 0x0168, .prod = 0x001,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-NEC",
+ .manf = 0x0168, .prod = 0x002,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-SJA",
+ .manf = 0x0168, .prod = 0x004,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "CANcard-2",
+ .manf = 0x0168, .prod = 0x005,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "Vector-CANcard",
+ .manf = 0x0168, .prod = 0x081,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "Vector-CANcard-SJA",
+ .manf = 0x0168, .prod = 0x084,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "Vector-CANcard-2",
+ .manf = 0x0168, .prod = 0x085,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "EDICcard-NEC",
+ .manf = 0x0168, .prod = 0x102,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "EDICcard-2",
+ .manf = 0x0168, .prod = 0x105,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+ },
+
+/* never tested, but taken from original softing */
+{ .name = "CAN-AC2-104",
+ .manf = 0x0000, .prod = 0x009,
+ .generation = 1,
+ .freq = 25, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x1000,
+ .boot = {0x0000, 0x000000, fw_dir "boot104.bin",},
+ .load = {0x0800, 0x035000, fw_dir "ld104.bin",},
+ .app = {0x0010, 0x120000, fw_dir "canpc104.bin",},
+ },
+{0, 0,},
+};
+
+const struct softing_desc *softing_lookup_desc
+ (unsigned int manf, unsigned int prod)
+{
+ const struct softing_desc *lp = carddescs;
+ for (; lp->name; ++lp) {
+ if ((lp->manf == manf) && (lp->prod == prod))
+ return lp;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(softing_lookup_desc);
+
+int softing_fct_cmd(struct softing *card, int cmd, int vector, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ if (vector == RES_OK)
+ vector = RES_NONE;
+ card->dpram.fct->param[0] = cmd;
+ card->dpram.fct->host_access = vector;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.fct->host_access;
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK) {
+ /*don't read return-value now */
+ ret = card->dpram.fct->returned;
+ if (ret)
+ mod_alert("%s returned %u", msg, ret);
+ return 0;
+ }
+ if ((jiffies - stamp) >= 1 * HZ)
+ break;
+ if (in_interrupt())
+ /* go as fast as possible */
+ continue;
+ /* process context => relax */
+ schedule();
+ } while (!signal_pending(current));
+
+ if (ret == RES_NONE) {
+ mod_alert("%s, no response from card on %u/0x%02x"
+ , msg, cmd, vector);
+ return 1;
+ } else {
+ mod_alert("%s, bad response from card on %u/0x%02x, 0x%04x"
+ , msg, cmd, vector, ret);
+ /*make sure to return something not 0 */
+ return ret ? ret : 1;
+ }
+}
+
+int softing_bootloader_command(struct softing *card
+ , int command, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ card->dpram.receipt[0] = RES_NONE;
+ card->dpram.command[0] = command;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.receipt[0];
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK)
+ return 0;
+ if ((jiffies - stamp) >= (3 * HZ))
+ break;
+ schedule();
+ } while (!signal_pending(current));
+
+ switch (ret) {
+ case RES_NONE:
+ mod_alert("%s: no response from card", msg);
+ break;
+ case RES_NOK:
+ mod_alert("%s: response from card nok", msg);
+ break;
+ case RES_UNKNOWN:
+ mod_alert("%s: command 0x%04x unknown", msg, command);
+ break;
+ default:
+ mod_alert("%s: bad response from card (%u)]", msg, ret);
+ break;
+ }
+ return ret ? ret : 1;
+}
+
+struct fw_hdr {
+ u16 type;
+ u32 addr;
+ u16 len;
+ u16 checksum;
+ const unsigned char *base;
+} __attribute__ ((packed));
+
+static int fw_parse(const unsigned char **pmem, struct fw_hdr *hdr)
+{
+ u16 tmp;
+ const unsigned char *mem;
+ const unsigned char *end;
+ mem = *pmem;
+ hdr->type = (mem[0] << 0) | (mem[1] << 8);
+ hdr->addr = (mem[2] << 0) | (mem[3] << 8)
+ | (mem[4] << 16) | (mem[5] << 24);
+ hdr->len = (mem[6] << 0) | (mem[7] << 8);
+ hdr->base = &mem[8];
+ hdr->checksum =
+ (hdr->base[hdr->len] << 0) | (hdr->base[hdr->len + 1] << 8);
+ for (tmp = 0, mem = *pmem, end = &hdr->base[hdr->len]; mem < end; ++mem)
+ tmp += *mem;
+ if (tmp != hdr->checksum)
+ return EINVAL;
+ *pmem += 10 + hdr->len;
+ return 0;
+}
+
+int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ u32 start_addr;
+ struct fw_hdr rec;
+ int ok = 0;
+ unsigned char buf[256];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %u bytes, offset %c0x%04x"
+ , card->id.name, file, (unsigned int)fw->size,
+ (offset >= 0) ? '+' : '-', abs(offset));
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+
+ if ((rec.addr + rec.len + offset) > size) {
+ mod_alert("firmware out of range (0x%08x / 0x%08x)"
+ , (rec.addr + rec.len + offset), size);
+ goto fw_end;
+ }
+ memcpy_toio(&virt[rec.addr + offset],
+ rec.base, rec.len);
+ /* be sure to flush caches from IO space */
+ mb();
+ if (rec.len > sizeof(buf)) {
+ mod_info("record is big (%u bytes), not verifying"
+ , rec.len);
+ continue;
+ }
+ /* verify record data */
+ memcpy_fromio(buf, &virt[rec.addr + offset], rec.len);
+ if (!memcmp(buf, rec.base, rec.len))
+ /* is ok */
+ continue;
+ mod_alert("0x%08x:0x%03x at 0x%p failed", rec.addr, rec.len
+ , &virt[rec.addr + offset]);
+ goto fw_end;
+ }
+fw_end:
+ release_firmware(fw);
+ if (0x5 == (ok & 0x5)) {
+ /*got eof & start */
+ return 0;
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_load_app_fw(const char *file, struct softing *card)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ struct fw_hdr rec;
+ int ok = 0;
+ u32 start_addr = 0;
+ u16 rx_sum;
+ unsigned int sum;
+ const unsigned char *mem_lp;
+ const unsigned char *mem_end;
+ struct cpy {
+ u32 src;
+ u32 dst;
+ u16 len;
+ u8 do_cs;
+ } __attribute__((packed)) *pcpy =
+ (struct cpy *)&card->dpram.command[1];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %lu bytes", card->id.name, file,
+ (unsigned long)fw->size);
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+ /* regualar data */
+ for (sum = 0, mem_lp = rec.base, mem_end = &mem_lp[rec.len];
+ mem_lp < mem_end; ++mem_lp)
+ sum += *mem_lp;
+
+ memcpy_toio(&card->dpram. virt[card->desc->app.offs],
+ rec.base, rec.len);
+ pcpy->src = card->desc->app.offs + card->desc->app.addr;
+ pcpy->dst = rec.addr;
+ pcpy->len = rec.len;
+ pcpy->do_cs = 1;
+ if (softing_bootloader_command(card, 1, "loading app."))
+ goto fw_end;
+ /*verify checksum */
+ rx_sum = card->dpram.receipt[1];
+ if (rx_sum != (sum & 0xffff)) {
+ mod_alert("SRAM seems to be damaged"
+ ", wanted 0x%04x, got 0x%04x", sum, rx_sum);
+ goto fw_end;
+ }
+ }
+fw_end:
+ release_firmware(fw);
+ if (ok == 7) {
+ /*got start, start_addr, & eof */
+ struct cmd {
+ u32 start;
+ u8 autorestart;
+ } *pcmd = (struct cmd *)&card->dpram.command[1];
+ pcmd->start = start_addr;
+ pcmd->autorestart = 1;
+ if (!softing_bootloader_command(card, 3, "start app.")) {
+ mod_trace("%s: card app. run at 0x%06x"
+ , card->id.name, start_addr);
+ return 0;
+ }
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_reset_chip(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ do {
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ if (!softing_fct_cmd(card, 0, 0, "reset_chip"))
+ break;
+ if (signal_pending(current))
+ goto failed;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto failed;
+ } while (1);
+ card->tx.pending = 0;
+ return 0;
+failed:
+ return -EIO;
+}
+
+int softing_reinit(struct softing *card, int bus0, int bus1)
+{
+ int ret;
+ int restarted_bus = -1;
+ mod_trace("%s", card->id.name);
+ if (!card->fw.up)
+ return -EIO;
+ if (bus0 < 0) {
+ bus0 = (card->bus[0]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus0)
+ restarted_bus = 0;
+ } else if (bus1 < 0) {
+ bus1 = (card->bus[1]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus1)
+ restarted_bus = 1;
+ }
+ /* collect info */
+ if (card->bus[0]) {
+ card->bus[0]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[0]);
+ }
+ if (card->bus[1]) {
+ card->bus[1]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[1]);
+ }
+
+ /* start acting */
+ if (!bus0 && !bus1) {
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return 0;
+ }
+ ret = softing_reset_chip(card);
+ if (ret) {
+ softing_card_irq(card, 0);
+ return ret;
+ }
+ if (bus0) {
+ /*init chip */
+ card->dpram.fct->param[1] = card->bus[0]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[0]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[0]->can.bittiming.phase_seg1 +
+ card->bus[0]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[0]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[0]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 1, 0, "initialize_chip[0]"))
+ goto failed;
+ /*set mode */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 3, 0, "set_mode[0]"))
+ goto failed;
+ /*set filter */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[0].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[0].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[0].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[0].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[0].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[0].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 7, 0, "set_filter[0]"))
+ goto failed;
+ /*set output control */
+ card->dpram.fct->param[1] = card->bus[0]->output;
+ if (softing_fct_cmd(card, 5, 0, "set_output[0]"))
+ goto failed;
+ }
+ if (bus1) {
+ /*init chip2 */
+ card->dpram.fct->param[1] = card->bus[1]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[1]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[1]->can.bittiming.phase_seg1 +
+ card->bus[1]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[1]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[1]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 2, 0, "initialize_chip[1]"))
+ goto failed;
+ /*set mode2 */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 4, 0, "set_mode[1]"))
+ goto failed;
+ /*set filter2 */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[1].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[1].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[1].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[1].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[1].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[1].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 8, 0, "set_filter[1]"))
+ goto failed;
+ /*set output control2 */
+ card->dpram.fct->param[1] = card->bus[1]->output;
+ if (softing_fct_cmd(card, 6, 0, "set_output[1]"))
+ goto failed;
+ }
+ /*set interrupt */
+ /*enable_error_frame */
+ if (softing_fct_cmd(card, 51, 0, "enable_error_frame"))
+ goto failed;
+ /*initialize interface */
+ card->dpram.fct->param[1] = 1;
+ card->dpram.fct->param[2] = 1;
+ card->dpram.fct->param[3] = 1;
+ card->dpram.fct->param[4] = 1;
+ card->dpram.fct->param[5] = 1;
+ card->dpram.fct->param[6] = 1;
+ card->dpram.fct->param[7] = 1;
+ card->dpram.fct->param[8] = 1;
+ card->dpram.fct->param[9] = 1;
+ card->dpram.fct->param[10] = 1;
+ if (softing_fct_cmd(card, 17, 0, "initialize_interface"))
+ goto failed;
+ /*enable_fifo */
+ if (softing_fct_cmd(card, 36, 0, "enable_fifo"))
+ goto failed;
+ /*enable fifo tx ack */
+ if (softing_fct_cmd(card, 13, 0, "fifo_tx_ack[0]"))
+ goto failed;
+ /*enable fifo tx ack2 */
+ if (softing_fct_cmd(card, 14, 0, "fifo_tx_ack[1]"))
+ goto failed;
+ /*enable timestamps */
+ /*is default, no code found */
+ /*start_chip */
+ if (softing_fct_cmd(card, 11, 0, "start_chip"))
+ goto failed;
+ card->dpram.info->bus_state = 0;
+ card->dpram.info->bus_state2 = 0;
+ mod_info("ok for %s, %s/%s\n", card->bus[0]->netdev->name,
+ card->bus[1]->netdev->name, card->id.name);
+ if (card->desc->generation < 2) {
+ card->dpram.irq->to_host = 0;
+ /* flush the DPRAM caches */
+ wmb();
+ }
+ /*run once */
+ /*the bottom halve will start flushing the tx-queue too */
+ tasklet_schedule(&card->irq.bh);
+
+ ret = softing_card_irq(card, 1);
+ if (ret)
+ goto failed;
+
+ /*TODO: generate RESTARTED messages */
+
+ if (card->bus[0] && bus0) {
+ card->bus[0]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[0]->netdev);
+ }
+ if (card->bus[1] && bus1) {
+ card->bus[1]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[1]->netdev);
+ }
+ return 0;
+failed:
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return -EIO;
+}
+
+
+int softing_default_output(struct softing *card, struct softing_priv *priv)
+{
+ switch (priv->chip) {
+ case 1000:
+ if (card->desc->generation < 2)
+ return 0xfb;
+ return 0xfa;
+ case 5:
+ return 0x60;
+ default:
+ return 0x40;
+ }
+}
+
+u32 softing_time2usec(struct softing *card, u32 raw)
+{
+ /*TODO : don't loose higher order bits in computation */
+ switch (card->desc->freq) {
+ case 20:
+ return raw * 4 / 5;
+ case 24:
+ return raw * 2 / 3;
+ case 25:
+ return raw * 16 / 25;
+ case 0:
+ case 16:
+ default:
+ return raw;
+ }
+}
+
+
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
new file mode 100644
index 000000000000..955f9c85a3a4
--- /dev/null
+++ b/drivers/net/can/softing/softing_main.c
@@ -0,0 +1,1058 @@
+/*
+* drivers/net/can/softing/softing_main.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the version 2 of the GNU General Public License
+* as published by the Free Software Foundation
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+/* this is the worst thing on the softing API
+ * 2 busses are driven together, I don't know how
+ * to recover a single of them.
+ * Therefore, when one bus is modified, the other
+ * is flushed too
+ */
+void softing_flush_echo_skb(struct softing_priv *priv)
+{
+ can_close_cleanup(priv->netdev);
+ priv->tx.pending = 0;
+ priv->tx.echo_put = 0;
+ priv->tx.echo_get = 0;
+}
+
+/*softing_unlocked_tx_run:*/
+/*trigger the tx queue-ing*/
+/*no locks are grabbed, so be sure to have the spin spinlock*/
+static int netdev_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct softing_priv *priv = (struct softing_priv *)netdev_priv(dev);
+ struct softing *card = priv->card;
+ int ret;
+ int bhlock;
+ u8 *ptr;
+ u8 cmd;
+ unsigned int fifo_wr;
+ struct can_frame msg;
+
+ ret = -ENOTTY;
+ if (in_interrupt()) {
+ bhlock = 0;
+ spin_lock(&card->spin);
+ } else {
+ bhlock = 1;
+ spin_lock_bh(&card->spin);
+ }
+ if (!card->fw.up) {
+ ret = -EIO;
+ goto xmit_done;
+ }
+ if (netif_carrier_ok(priv->netdev) <= 0) {
+ ret = -EBADF;
+ goto xmit_done;
+ }
+ if (card->tx.pending >= TXMAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ if (priv->tx.pending >= CAN_ECHO_SKB_MAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ fifo_wr = card->dpram.tx->wr;
+ if (fifo_wr == card->dpram.tx->rd) {
+ /*fifo full */
+ ret = -EAGAIN;
+ goto xmit_done;
+ }
+ memcpy(&msg, skb->data, sizeof(msg));
+ ptr = &card->dpram.tx->fifo[fifo_wr][0];
+ cmd = CMD_TX;
+ if (msg.can_id & CAN_RTR_FLAG)
+ cmd |= CMD_RTR;
+ if (msg.can_id & CAN_EFF_FLAG)
+ cmd |= CMD_XTD;
+ if (priv->index)
+ cmd |= CMD_BUS2;
+ *ptr++ = cmd;
+ *ptr++ = msg.can_dlc;
+ *ptr++ = (msg.can_id >> 0);
+ *ptr++ = (msg.can_id >> 8);
+ if (msg.can_id & CAN_EFF_FLAG) {
+ *ptr++ = (msg.can_id >> 16);
+ *ptr++ = (msg.can_id >> 24);
+ } else {
+ /*increment 1, not 2 as you might think */
+ ptr += 1;
+ }
+ if (!(msg.can_id & CAN_RTR_FLAG))
+ memcpy_toio(ptr, &msg.data[0], msg.can_dlc);
+ if (++fifo_wr >=
+ sizeof(card->dpram.tx->fifo) /
+ sizeof(card->dpram.tx->fifo[0]))
+ fifo_wr = 0;
+ card->dpram.tx->wr = fifo_wr;
+ ret = 0;
+ ++card->tx.pending;
+ ++priv->tx.pending;
+ can_put_echo_skb(skb, dev, priv->tx.echo_put);
+ ++priv->tx.echo_put;
+ if (priv->tx.echo_put >= CAN_ECHO_SKB_MAX)
+ priv->tx.echo_put = 0;
+ /* clear pointer, so don't erase later */
+ skb = 0;
+xmit_done:
+ if (bhlock)
+ spin_unlock_bh(&card->spin);
+ else
+ spin_unlock(&card->spin);
+ if (card->tx.pending >= TXMAX) {
+ struct softing_priv *bus;
+ int j;
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ netif_stop_queue(bus->netdev);
+ }
+ }
+
+ /* free skb, if not handled by the driver */
+ if (skb)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int softing_dev_svc_once(struct softing *card)
+{
+ int j;
+ struct softing_priv *bus;
+ struct sk_buff *skb;
+ struct can_frame msg;
+
+ unsigned int fifo_rd;
+ unsigned int cnt = 0;
+ struct net_device_stats *stats;
+
+ memset(&msg, 0, sizeof(msg));
+ if (card->dpram.rx->lost_msg) {
+ /*reset condition */
+ card->dpram.rx->lost_msg = 0;
+ /* prepare msg */
+ msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ msg.can_dlc = CAN_ERR_DLC;
+ msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ /*service to both busses, we don't know which one generated */
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ if (!netif_carrier_ok(bus->netdev))
+ continue;
+ ++bus->can.can_stats.data_overrun;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg, sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ memset(&msg, 0, sizeof(msg));
+ ++cnt;
+ }
+
+ fifo_rd = card->dpram.rx->rd;
+ if (++fifo_rd >=
+ sizeof(card->dpram.rx->fifo) / sizeof(card->dpram.rx->fifo[0]))
+ fifo_rd = 0;
+ if (card->dpram.rx->wr != fifo_rd) {
+ u8 *ptr;
+ u32 tmp;
+ u8 cmd;
+ int do_skb;
+
+ ptr = &card->dpram.rx->fifo[fifo_rd][0];
+
+ cmd = *ptr++;
+ if (cmd == 0xff) {
+ /*not quite usefull, probably the card has got out */
+ mod_alert("got cmd 0x%02x, I suspect the card is lost"
+ , cmd);
+ }
+ /*mod_trace("0x%02x", cmd);*/
+ bus = card->bus[0];
+ if (cmd & CMD_BUS2)
+ bus = card->bus[1];
+
+ stats = &bus->netdev->stats;
+ if (cmd & CMD_ERR) {
+ u8 can_state;
+ u8 state;
+ state = *ptr++;
+
+ msg.can_id = CAN_ERR_FLAG;
+ msg.can_dlc = CAN_ERR_DLC;
+
+ if (state & 0x80) {
+ can_state = CAN_STATE_BUS_OFF;
+ msg.can_id |= CAN_ERR_BUSOFF;
+ state = 2;
+ } else if (state & 0x60) {
+ can_state = CAN_STATE_BUS_PASSIVE;
+ msg.can_id |= CAN_ERR_BUSERROR;
+ state = 1;
+ } else {
+ can_state = CAN_STATE_ACTIVE;
+ state = 0;
+ do_skb = 0;
+ }
+ /*update DPRAM */
+ if (!bus->index)
+ card->dpram.info->bus_state = state;
+ else
+ card->dpram.info->bus_state2 = state;
+ /*timestamp */
+ tmp = (ptr[0] << 0)
+ |(ptr[1] << 8)
+ |(ptr[2] << 16)
+ |(ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update internal status */
+ if (can_state != bus->can.state) {
+ bus->can.state = can_state;
+ if (can_state == 1)
+ bus->can.can_stats.error_passive += 1;
+ }
+ bus->can.can_stats.bus_error += 1;
+
+ /*trigger socketcan */
+ if (state == 2) {
+ /* this calls can_close_cleanup() */
+ softing_flush_echo_skb(bus);
+ can_bus_off(bus->netdev);
+ netif_stop_queue(bus->netdev);
+ }
+ if ((state == CAN_STATE_BUS_OFF)
+ || (state == CAN_STATE_BUS_PASSIVE)) {
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ } else {
+ if (cmd & CMD_RTR)
+ msg.can_id |= CAN_RTR_FLAG;
+ /* acknowledge, was tx msg
+ * no real tx flag to set
+ if (cmd & CMD_ACK) {
+ }
+ */
+ msg.can_dlc = *ptr++;
+ if (msg.can_dlc > 8)
+ msg.can_dlc = 8;
+ if (cmd & CMD_XTD) {
+ msg.can_id |= CAN_EFF_FLAG;
+ msg.can_id |=
+ (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ } else {
+ msg.can_id |= (ptr[0] << 0) | (ptr[1] << 8);
+ ptr += 2;
+ }
+ tmp = (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ memcpy_fromio(&msg.data[0], ptr, 8);
+ ptr += 8;
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update socket */
+ if (cmd & CMD_ACK) {
+ can_get_echo_skb(bus->netdev, bus->tx.echo_get);
+ ++bus->tx.echo_get;
+ if (bus->tx.echo_get >= CAN_ECHO_SKB_MAX)
+ bus->tx.echo_get = 0;
+ if (bus->tx.pending)
+ --bus->tx.pending;
+ if (card->tx.pending)
+ --card->tx.pending;
+ stats->tx_packets += 1;
+ stats->tx_bytes += msg.can_dlc;
+ } else {
+ stats->rx_packets += 1;
+ stats->rx_bytes += msg.can_dlc;
+ bus->netdev->last_rx = jiffies;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (skb) {
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ }
+ ++cnt;
+ }
+ return cnt;
+}
+
+static void softing_dev_svc(unsigned long param)
+{
+ struct softing *card = (struct softing *)param;
+ struct softing_priv *bus;
+ int j;
+ int offset;
+
+ spin_lock(&card->spin);
+ while (softing_dev_svc_once(card) > 0)
+ ++card->irq.svc_count;
+ /*resume tx queue's */
+ offset = card->tx.last_bus;
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->tx.pending >= TXMAX)
+ break;
+ bus = card->bus[(j + offset) % card->nbus];
+ if (netif_carrier_ok(bus->netdev))
+ netif_wake_queue(bus->netdev);
+ }
+ spin_unlock(&card->spin);
+}
+
+static void card_seems_down(struct softing *card)
+{
+ /* free interrupt, but probably
+ * in wrong (interrupt) context
+ if (card->irq.requested) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ card->fw.up = 0;
+ }
+ */
+ mod_alert("I think the card is vanished");
+}
+
+static
+irqreturn_t dev_interrupt_shared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char ir;
+ ir = card->dpram.virt[0xe02];
+ card->dpram.virt[0xe02] = 0;
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ if (ir == 1) {
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+ } else if (ir == 0x10) {
+ return IRQ_NONE;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static
+irqreturn_t dev_interrupt_nshared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char irq_host;
+ irq_host = card->dpram.irq->to_host;
+ /* make sure we have a copy, before clearing the variable in DPRAM */
+ rmb();
+ card->dpram.irq->to_host = 0;
+ /* make sure we cleared it */
+ wmb();
+ mod_trace("0x%02x", irq_host);
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+}
+
+static int netdev_open(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+ int ret;
+
+ mod_trace("%s", ndev->name);
+ /* determine and set bittime */
+ ret = can_set_bittiming(ndev);
+ if (ret)
+ return ret;
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 1 : -1
+ , (card->bus[1] == priv) ? 1 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int netdev_stop(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+
+ mod_trace("%s", ndev->name);
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+ softing_flush_echo_skb(priv);
+ can_close_cleanup(ndev);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 0 : -1
+ , (card->bus[1] == priv) ? 0 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ return 0;
+}
+
+static int candev_get_state(struct net_device *ndev, enum can_state *state)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ mod_trace("%s", ndev->name);
+ if (priv->netdev->flags & IFF_UP)
+ *state = CAN_STATE_STOPPED;
+ else if (priv->can.state == CAN_STATE_STOPPED)
+ *state = CAN_STATE_STOPPED;
+ else
+ *state = CAN_STATE_ACTIVE;
+ return 0;
+}
+
+static int candev_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ mod_trace("%s %u", ndev->name, mode);
+ switch (mode) {
+ case CAN_MODE_START:
+ /*recovery from busoff? */
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ softing_reinit(card, -1, -1);
+ mutex_unlock(&card->fw.lock);
+ break;
+ case CAN_MODE_STOP:
+ case CAN_MODE_SLEEP:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*assume the card->lock is held*/
+
+int softing_card_irq(struct softing *card, int enable)
+{
+ int ret;
+ if (!enable) {
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ return 0;
+ }
+ if (!card->irq.requested && (card->irq.nr)) {
+ irqreturn_t(*fn) (int, void *);
+ unsigned int flags;
+ flags = IRQF_DISABLED | IRQF_SHARED;/*| IRQF_TRIGGER_LOW; */
+ fn = dev_interrupt_nshared;
+ if (card->desc->generation >= 2)
+ fn = dev_interrupt_shared;
+ ret = request_irq(card->irq.nr, fn, flags, card->id.name, card);
+ if (ret) {
+ mod_alert("%s, request_irq(%u) failed"
+ , card->id.name, card->irq.nr
+ );
+ return ret;
+ }
+ card->irq.requested = 1;
+ }
+ return 0;
+}
+
+static void shutdown_card(struct softing *card)
+{
+ int fw_up = 0;
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ /* return -ERESTARTSYS*/;
+ fw_up = card->fw.up;
+ card->fw.up = 0;
+
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ if (fw_up) {
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ }
+ mutex_unlock(&card->fw.lock);
+ tasklet_kill(&card->irq.bh);
+}
+
+static int boot_card(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (card->fw.up) {
+ mutex_unlock(&card->fw.lock);
+ return 0;
+ }
+ /*reset board */
+
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 1);
+ /*boot card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ /*test dp ram */
+ if (card->dpram.virt) {
+ unsigned char *lp;
+ static const unsigned char stream[]
+ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, };
+ unsigned char back[sizeof(stream)];
+ for (lp = card->dpram.virt;
+ &lp[sizeof(stream)] <= card->dpram.end;
+ lp += sizeof(stream)) {
+ memcpy_toio(lp, stream, sizeof(stream));
+ /* flush IO cache */
+ mb();
+ memcpy_fromio(back, lp, sizeof(stream));
+
+ if (memcmp(back, stream, sizeof(stream))) {
+ char line[3 * sizeof(stream)
+ / sizeof(stream[0]) + 1];
+ char *pline = line;
+ unsigned char *addr = lp;
+ for (lp = back; lp < &back[sizeof(stream)
+ / sizeof(stream[0])]; ++lp)
+ pline += sprintf(pline, " %02x", *lp);
+
+ mod_alert("write to dpram failed at 0x%p, %s"
+ , addr, line);
+ goto open_failed;
+ }
+ }
+ /*fill dpram with 0x55 */
+ /*for (lp = card->dpram.virt; lp <= card->dpram.end; ++lp) {
+ *lp = 0x55;
+ }*/
+ wmb();
+ } else {
+ goto open_failed;
+ }
+ /*load boot firmware */
+ if (softing_load_fw(card->desc->boot.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->boot.offs -
+ card->desc->boot.addr))
+ goto open_failed;
+ /*load load firmware */
+ if (softing_load_fw(card->desc->load.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->load.offs -
+ card->desc->load.addr))
+ goto open_failed;
+
+ if (card->fn.reset)
+ card->fn.reset(card, 0);
+ if (softing_bootloader_command(card, 0, "card boot"))
+ goto open_failed;
+ if (softing_load_app_fw(card->desc->app.fw, card))
+ goto open_failed;
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto open_failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto open_failed;
+ /*reset chip */
+ if (softing_fct_cmd(card, 0, 0, "reset_chip"))
+ goto open_failed;
+ /*get_serial */
+ if (softing_fct_cmd(card, 43, 0, "get_serial_number"))
+ goto open_failed;
+ card->id.serial =
+ (u16) card->dpram.fct->param[1] +
+ (((u16) card->dpram.fct->param[2]) << 16);
+ /*get_version */
+ if (softing_fct_cmd(card, 12, 0, "get_version"))
+ goto open_failed;
+ card->id.fw = (u16) card->dpram.fct->param[1];
+ card->id.hw = (u16) card->dpram.fct->param[2];
+ card->id.lic = (u16) card->dpram.fct->param[3];
+ card->id.chip[0] = (u16) card->dpram.fct->param[4];
+ card->id.chip[1] = (u16) card->dpram.fct->param[5];
+
+ mod_info("%s, card booted, "
+ "serial %u, fw %u, hw %u, lic %u, chip (%u,%u)",
+ card->id.name, card->id.serial, card->id.fw, card->id.hw,
+ card->id.lic, card->id.chip[0], card->id.chip[1]);
+
+ card->fw.up = 1;
+ mutex_unlock(&card->fw.lock);
+ return 0;
+open_failed:
+ card->fw.up = 0;
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ mutex_unlock(&card->fw.lock);
+ return EINVAL;
+}
+
+/*sysfs stuff*/
+
+/* Because the struct softing may be used by pcmcia devices
+ * as well as pci devices, * we have no clue how to get
+ * from a struct device * towards the struct softing *.
+ * It may go over a pci_device->priv or over a pcmcia_device->priv.
+ * Therefore, provide the struct softing pointer within the attribute.
+ * Then we don't need driver/bus specific things in these attributes
+ */
+struct softing_attribute {
+ struct device_attribute dev;
+ ssize_t (*show) (struct softing *card, char *buf);
+ ssize_t (*store)(struct softing *card, const char *buf, size_t count);
+ struct softing *card;
+};
+
+static ssize_t rd_card_attr(struct device *dev, struct device_attribute *attr
+ , char *buf) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->show ? cattr->show(cattr->card, buf) : 0;
+}
+static ssize_t wr_card_attr(struct device *dev, struct device_attribute *attr
+ , const char *buf, size_t count) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->store ? cattr->store(cattr->card, buf, count) : 0;
+}
+
+#define declare_attr(_name, _mode, _show, _store) { \
+ .dev = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = _mode, \
+ }, \
+ .show = rd_card_attr, \
+ .store = wr_card_attr, \
+ }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define CARD_SHOW(name, member) \
+static ssize_t show_##name(struct softing *card, char *buf) { \
+ return sprintf(buf, "%u\n", card->member); \
+}
+CARD_SHOW(serial , id.serial);
+CARD_SHOW(firmware , id.fw);
+CARD_SHOW(hardware , id.hw);
+CARD_SHOW(license , id.lic);
+CARD_SHOW(freq , id.freq);
+CARD_SHOW(txpending , tx.pending);
+
+static const struct softing_attribute card_attr_proto [] = {
+ declare_attr(serial , 0444, show_serial , 0),
+ declare_attr(firmware , 0444, show_firmware , 0),
+ declare_attr(hardware , 0444, show_hardware , 0),
+ declare_attr(license , 0444, show_license , 0),
+ declare_attr(freq , 0444, show_freq , 0),
+ declare_attr(txpending , 0644, show_txpending , 0),
+};
+
+static int mk_card_sysfs(struct softing *card)
+{
+ int size;
+ int j;
+
+ size = sizeof(card_attr_proto)/sizeof(card_attr_proto[0]);
+ card->attr = kmalloc((size+1)*sizeof(card->attr[0]), GFP_KERNEL);
+ if (!card->attr)
+ goto attr_mem_failed;
+ memcpy(card->attr, card_attr_proto, size * sizeof(card->attr[0]));
+ memset(&card->attr[size], 0, sizeof(card->attr[0]));
+
+ card->grp = kmalloc((size+1)*sizeof(card->grp [0]), GFP_KERNEL);
+ if (!card->grp)
+ goto grp_mem_failed;
+
+ for (j = 0; j < size; ++j) {
+ card->attr[j].card = card;
+ card->grp[j] = &card->attr[j].dev.attr;
+ if (!card->attr[j].show)
+ card->attr[j].dev.attr.mode &= ~(S_IRUGO);
+ if (!card->attr[j].store)
+ card->attr[j].dev.attr.mode &= ~(S_IWUGO);
+ }
+ card->grp[size] = 0;
+ card->sysfs.name = "softing";
+ card->sysfs.attrs = card->grp;
+ if (sysfs_create_group(&card->dev->kobj, &card->sysfs) < 0)
+ goto sysfs_failed;
+
+ return 0;
+
+sysfs_failed:
+ kfree(card->grp);
+grp_mem_failed:
+ kfree(card->attr);
+attr_mem_failed:
+ return -1;
+}
+static void rm_card_sysfs(struct softing *card)
+{
+ sysfs_remove_group(&card->dev->kobj, &card->sysfs);
+ kfree(card->grp);
+ kfree(card->attr);
+}
+
+static ssize_t show_chip(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "%i\n", priv->chip);
+}
+
+static ssize_t show_output(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "0x%02x\n", priv->output);
+}
+
+static ssize_t store_output(struct device *dev
+ , struct device_attribute *attr
+ , const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ struct softing *card = priv->card;
+
+ u8 v = simple_strtoul(buf, NULL, 10) & 0xFFU;
+
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (ndev->flags & IFF_UP) {
+ int j;
+ /* we will need a restart */
+ for (j = 0; j < card->nbus; ++j) {
+ if (j == priv->index)
+ /* me, myself & I */
+ continue;
+ if (card->bus[j]->netdev->flags & IFF_UP) {
+ mutex_unlock(&card->fw.lock);
+ return -EBUSY;
+ }
+ }
+ priv->output = v;
+ softing_reinit(card, -1, -1);
+ } else {
+ priv->output = v;
+ }
+ mutex_unlock(&card->fw.lock);
+ return count;
+}
+/* TODO
+ * the latest softing cards support sleep mode too
+ */
+
+static const DEVICE_ATTR(chip, S_IRUGO, show_chip, 0);
+static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
+
+static const struct attribute *const netdev_sysfs_entries [] = {
+ &dev_attr_chip .attr,
+ &dev_attr_output .attr,
+ 0,
+};
+static const struct attribute_group netdev_sysfs = {
+ .name = 0,
+ .attrs = (struct attribute **)netdev_sysfs_entries,
+};
+
+static int mk_netdev_sysfs(struct softing_priv *priv)
+{
+ if (!priv->netdev->dev.kobj.sd) {
+ mod_alert("sysfs_create_group would fail");
+ return ENODEV;
+ }
+ return sysfs_create_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+static void rm_netdev_sysfs(struct softing_priv *priv)
+{
+ sysfs_remove_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+
+static struct softing_priv *mk_netdev(struct softing *card, u16 chip_id)
+{
+ struct net_device *ndev;
+ struct softing_priv *priv;
+
+ ndev = alloc_candev(sizeof(*priv));
+ if (!ndev) {
+ mod_alert("alloc_candev failed");
+ return 0;
+ }
+ priv = netdev_priv(ndev);
+ priv->netdev = ndev;
+ priv->card = card;
+ memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const));
+ priv->btr_const.brp_max = card->desc->max_brp;
+ priv->btr_const.sjw_max = card->desc->max_sjw;
+ priv->can.bittiming_const = &priv->btr_const;
+ priv->can.bittiming.clock = 8000000;
+ priv->chip = chip_id;
+ priv->output = softing_default_output(card, priv);
+ SET_NETDEV_DEV(ndev, card->dev);
+
+ ndev->flags |= IFF_ECHO;
+ ndev->open = netdev_open;
+ ndev->stop = netdev_stop;
+ ndev->hard_start_xmit = netdev_start_xmit;
+ priv->can.do_get_state = candev_get_state;
+ priv->can.do_set_mode = candev_set_mode;
+
+ return priv;
+}
+
+static void rm_netdev(struct softing_priv *priv)
+{
+ free_candev(priv->netdev);
+}
+
+static int reg_netdev(struct softing_priv *priv)
+{
+ int ret;
+ netif_carrier_off(priv->netdev);
+ ret = register_candev(priv->netdev);
+ if (ret) {
+ mod_alert("%s, register failed", priv->card->id.name);
+ goto reg_failed;
+ }
+ ret = mk_netdev_sysfs(priv);
+ if (ret) {
+ mod_alert("%s, sysfs failed", priv->card->id.name);
+ goto sysfs_failed;
+ }
+ return 0;
+sysfs_failed:
+ unregister_candev(priv->netdev);
+reg_failed:
+ return EINVAL;
+}
+
+static void unreg_netdev(struct softing_priv *priv)
+{
+ rm_netdev_sysfs(priv);
+ unregister_candev(priv->netdev);
+}
+
+void rm_softing(struct softing *card)
+{
+ int j;
+
+ /*first, disable card*/
+ shutdown_card(card);
+
+ for (j = 0; j < card->nbus; ++j) {
+ unreg_netdev(card->bus[j]);
+ rm_netdev(card->bus[j]);
+ }
+
+ rm_card_sysfs(card);
+
+ iounmap(card->dpram.virt);
+}
+EXPORT_SYMBOL(rm_softing);
+
+int mk_softing(struct softing *card)
+{
+ int j;
+
+ /* try_module_get(THIS_MODULE); */
+ mutex_init(&card->fw.lock);
+ spin_lock_init(&card->spin);
+ tasklet_init(&card->irq.bh, softing_dev_svc, (unsigned long)card);
+
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (!card->desc) {
+ mod_alert("0x%04x:0x%04x not supported\n", card->id.manf,
+ card->id.prod);
+ goto lookup_failed;
+ }
+ card->id.name = card->desc->name;
+ mod_trace("can (%s)", card->id.name);
+
+ card->dpram.virt = ioremap(card->dpram.phys, card->dpram.size);
+ if (!card->dpram.virt) {
+ mod_alert("dpram ioremap failed\n");
+ goto ioremap_failed;
+ }
+
+ card->dpram.size = card->desc->dpram_size;
+ card->dpram.end = &card->dpram.virt[card->dpram.size];
+ /*initialize_board */
+ card->dpram.rx = (struct softing_rx *)&card->dpram.virt[0x0000];
+ card->dpram.tx = (struct softing_tx *)&card->dpram.virt[0x0400];
+ card->dpram.fct = (struct softing_fct *)&card->dpram.virt[0x0300];
+ card->dpram.info = (struct softing_info *)&card->dpram.virt[0x0330];
+ card->dpram.command = (unsigned short *)&card->dpram.virt[0x07e0];
+ card->dpram.receipt = (unsigned short *)&card->dpram.virt[0x07f0];
+ card->dpram.irq = (struct softing_irq *)&card->dpram.virt[0x07fe];
+
+ /*reset card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ if (boot_card(card)) {
+ mod_alert("%s, failed to boot", card->id.name);
+ goto boot_failed;
+ }
+
+ /*only now, the chip's are known */
+ card->id.freq = card->desc->freq * 1000000UL;
+
+ if (mk_card_sysfs(card)) {
+ mod_alert("%s, sysfs failed", card->id.name);
+ goto sysfs_failed;
+ }
+
+ if (card->nbus > (sizeof(card->bus) / sizeof(card->bus[0]))) {
+ card->nbus = sizeof(card->bus) / sizeof(card->bus[0]);
+ mod_alert("%s, going for %u busses", card->id.name, card->nbus);
+ }
+
+ for (j = 0; j < card->nbus; ++j) {
+ card->bus[j] = mk_netdev(card, card->id.chip[j]);
+ if (!card->bus[j]) {
+ mod_alert("%s: failed to make can[%i]", card->id.name,
+ j);
+ goto netdev_failed;
+ }
+ card->bus[j]->index = j;
+ }
+ for (j = 0; j < card->nbus; ++j) {
+ if (reg_netdev(card->bus[j])) {
+ mod_alert("%s: failed to register can[%i]",
+ card->id.name, j);
+ goto reg_failed;
+ }
+ }
+ mod_trace("card initialised");
+ return 0;
+
+reg_failed:
+ for (j = 0; j < card->nbus; ++j)
+ unreg_netdev(card->bus[j]);
+netdev_failed:
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->bus[j])
+ rm_netdev(card->bus[j]);
+ }
+ rm_card_sysfs(card);
+sysfs_failed:
+ shutdown_card(card);
+boot_failed:
+ iounmap(card->dpram.virt);
+ card->dpram.virt = 0;
+ card->dpram.end = 0;
+ioremap_failed:
+lookup_failed:
+ tasklet_kill(&card->irq.bh);
+ return EINVAL;
+}
+EXPORT_SYMBOL(mk_softing);
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return 0;
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("socketcan softing driver");
+MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>");
+MODULE_LICENSE("GPL");
+
+int softing_debug = 1;
+EXPORT_SYMBOL(softing_debug);
+module_param(softing_debug, int , S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(softing_debug, "trace softing functions");
diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c
new file mode 100644
index 000000000000..746f6410cbeb
--- /dev/null
+++ b/drivers/net/can/sysfs.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2007-2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sysfs.h"
+
+#ifdef CONFIG_SYSFS
+
+/*
+ * SYSFS access functions and attributes. Use same locking as
+ * net/core/net-sysfs.c does.
+ */
+static inline int dev_isalive(const struct net_device *dev)
+{
+ return dev->reg_state <= NETREG_REGISTERED;
+}
+
+/* use same locking rules as GIF* ioctl's */
+static ssize_t can_dev_show(struct device *d,
+ struct device_attribute *attr, char *buf,
+ ssize_t (*fmt)(struct net_device *, char *))
+{
+ struct net_device *dev = to_net_dev(d);
+ ssize_t ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = (*fmt)(dev, buf);
+ read_unlock(&dev_base_lock);
+
+ return ret;
+}
+
+/* generate a show function for simple field */
+#define CAN_DEV_SHOW(field, fmt_string) \
+static ssize_t fmt_can_##field(struct net_device *dev, char *buf) \
+{ \
+ struct can_priv *priv = netdev_priv(dev); \
+ return sprintf(buf, fmt_string, priv->field); \
+} \
+static ssize_t show_can_##field(struct device *d, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return can_dev_show(d, attr, buf, fmt_can_##field); \
+}
+
+/* use same locking and permission rules as SIF* ioctl's */
+static ssize_t can_dev_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t len,
+ int (*set)(struct net_device *, unsigned long))
+{
+ struct net_device *dev = to_net_dev(d);
+ unsigned long new;
+ int ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ ret = (*set)(dev, new);
+ if (!ret)
+ ret = len;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_CREATE_FILE(_dev, _name) \
+ if (device_create_file(&_dev->dev, &dev_attr_##_name)) \
+ dev_err(ND2D(_dev), \
+ "Couldn't create device file for ##_name\n")
+
+#define CAN_REMOVE_FILE(_dev, _name) \
+ device_remove_file(&_dev->dev, &dev_attr_##_name) \
+
+CAN_DEV_SHOW(ctrlmode, "0x%x\n");
+
+static int change_can_ctrlmode(struct net_device *dev, unsigned long ctrlmode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err = 0;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ if (priv->do_set_ctrlmode)
+ err = priv->do_set_ctrlmode(dev, ctrlmode);
+
+ if (!err)
+ priv->ctrlmode = ctrlmode;
+
+ return err;
+}
+
+static ssize_t store_can_ctrlmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_ctrlmode);
+}
+
+static DEVICE_ATTR(can_ctrlmode, S_IRUGO | S_IWUSR,
+ show_can_ctrlmode, store_can_ctrlmode);
+
+static const char *can_state_names[] = {
+ "active", "bus-warn", "bus-pass" , "bus-off",
+ "stopped", "sleeping", "unkown"
+};
+
+static ssize_t printf_can_state(struct net_device *dev, char *buf)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int err = 0;
+
+ if (priv->do_get_state) {
+ err = priv->do_get_state(dev, &state);
+ if (err)
+ goto out;
+ priv->state = state;
+ } else
+ state = priv->state;
+
+ if (state >= ARRAY_SIZE(can_state_names))
+ state = ARRAY_SIZE(can_state_names) - 1;
+ err = sprintf(buf, "%s\n", can_state_names[state]);
+out:
+ return err;
+}
+
+static ssize_t show_can_state(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_state);
+}
+
+static DEVICE_ATTR(can_state, S_IRUGO, show_can_state, NULL);
+
+CAN_DEV_SHOW(restart_ms, "%d\n");
+
+static int change_can_restart_ms(struct net_device *dev, unsigned long ms)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->restart_ms < 0)
+ return -EOPNOTSUPP;
+ priv->restart_ms = ms;
+ return 0;
+}
+
+static ssize_t store_can_restart_ms(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart_ms);
+}
+
+static DEVICE_ATTR(can_restart_ms, S_IRUGO | S_IWUSR,
+ show_can_restart_ms, store_can_restart_ms);
+
+static ssize_t printf_can_echo(struct net_device *dev, char *buf)
+{
+ return sprintf(buf, "%d\n", dev->flags & IFF_ECHO ? 1 : 0);
+}
+
+static ssize_t show_can_echo(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_echo);
+}
+
+static int change_can_echo(struct net_device *dev, unsigned long on)
+{
+ if (on)
+ dev->flags |= IFF_ECHO;
+ else
+ dev->flags &= ~IFF_ECHO;
+ return 0;
+}
+
+static ssize_t store_can_echo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_echo);
+}
+
+static DEVICE_ATTR(can_echo, S_IRUGO | S_IWUSR, show_can_echo, store_can_echo);
+
+static int change_can_restart(struct net_device *dev, unsigned long on)
+{
+ return can_restart_now(dev);
+}
+
+static ssize_t store_can_restart(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart);
+}
+
+static DEVICE_ATTR(can_restart, S_IWUSR, NULL, store_can_restart);
+
+/* Show a given attribute if the CAN bittiming group */
+static ssize_t can_btc_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming_const *btc = priv->bittiming_const;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming_const) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev) && btc)
+ ret = sprintf(buf, "%d\n",
+ *(u32 *)(((u8 *)btc) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only bittiming const attribute */
+#define CAN_BT_CONST_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_btc_show(d, attr, buf, \
+ offsetof(struct can_bittiming_const, name));\
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_CONST_ENTRY(tseg1_min);
+CAN_BT_CONST_ENTRY(tseg1_max);
+CAN_BT_CONST_ENTRY(tseg2_min);
+CAN_BT_CONST_ENTRY(tseg2_max);
+CAN_BT_CONST_ENTRY(sjw_max);
+CAN_BT_CONST_ENTRY(brp_min);
+CAN_BT_CONST_ENTRY(brp_max);
+CAN_BT_CONST_ENTRY(brp_inc);
+
+static ssize_t can_bt_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ ssize_t ret = -EINVAL;
+ u32 *ptr, val;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev)) {
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ if (ptr == &bt->sample_point &&
+ priv->state != CAN_STATE_STOPPED)
+ val = can_sample_point(bt);
+ else
+ val = *ptr;
+ ret = sprintf(buf, "%d\n", val);
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static ssize_t can_bt_store(const struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ unsigned long new;
+ ssize_t ret = -EINVAL;
+ u32 *ptr;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ *ptr = (u32)new;
+
+ if ((ptr == &bt->bitrate) || (ptr == &bt->sample_point)) {
+ bt->tq = 0;
+ bt->brp = 0;
+ bt->sjw = 0;
+ bt->prop_seg = 0;
+ bt->phase_seg1 = 0;
+ bt->phase_seg2 = 0;
+ } else {
+ bt->bitrate = 0;
+ bt->sample_point = 0;
+ }
+ ret = count;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_BT_ENTRY_RO(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_ENTRY_RO(clock);
+
+#define CAN_BT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static ssize_t store_##name(struct device *d, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ return can_bt_store(d, attr, buf, count, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name)
+
+CAN_BT_ENTRY(bitrate);
+CAN_BT_ENTRY(sample_point);
+CAN_BT_ENTRY(tq);
+CAN_BT_ENTRY(prop_seg);
+CAN_BT_ENTRY(phase_seg1);
+CAN_BT_ENTRY(phase_seg2);
+CAN_BT_ENTRY(sjw);
+
+static struct attribute *can_bittiming_attrs[] = {
+ &dev_attr_hw_tseg1_min.attr,
+ &dev_attr_hw_tseg1_max.attr,
+ &dev_attr_hw_tseg2_max.attr,
+ &dev_attr_hw_tseg2_min.attr,
+ &dev_attr_hw_sjw_max.attr,
+ &dev_attr_hw_brp_min.attr,
+ &dev_attr_hw_brp_max.attr,
+ &dev_attr_hw_brp_inc.attr,
+ &dev_attr_hw_clock.attr,
+ &dev_attr_bitrate.attr,
+ &dev_attr_sample_point.attr,
+ &dev_attr_tq.attr,
+ &dev_attr_prop_seg.attr,
+ &dev_attr_phase_seg1.attr,
+ &dev_attr_phase_seg2.attr,
+ &dev_attr_sjw.attr,
+ NULL
+};
+
+static struct attribute_group can_bittiming_group = {
+ .name = "can_bittiming",
+ .attrs = can_bittiming_attrs,
+};
+
+/* Show a given attribute in the CAN statistics group */
+static ssize_t can_stat_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_device_stats *stats = &priv->can_stats;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_device_stats) ||
+ offset % sizeof(unsigned long) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = sprintf(buf, "%ld\n",
+ *(unsigned long *)(((u8 *)stats) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only CAN statistics attribute */
+#define CAN_STAT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_stat_show(d, attr, buf, \
+ offsetof(struct can_device_stats, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+
+CAN_STAT_ENTRY(error_warning);
+CAN_STAT_ENTRY(error_passive);
+CAN_STAT_ENTRY(bus_error);
+CAN_STAT_ENTRY(arbitration_lost);
+CAN_STAT_ENTRY(data_overrun);
+CAN_STAT_ENTRY(wakeup);
+CAN_STAT_ENTRY(restarts);
+
+static struct attribute *can_statistics_attrs[] = {
+ &dev_attr_error_warning.attr,
+ &dev_attr_error_passive.attr,
+ &dev_attr_bus_error.attr,
+ &dev_attr_arbitration_lost.attr,
+ &dev_attr_data_overrun.attr,
+ &dev_attr_wakeup.attr,
+ &dev_attr_restarts.attr,
+ NULL
+};
+
+static struct attribute_group can_statistics_group = {
+ .name = "can_statistics",
+ .attrs = can_statistics_attrs,
+};
+
+void can_create_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ CAN_CREATE_FILE(dev, can_ctrlmode);
+ CAN_CREATE_FILE(dev, can_echo);
+ CAN_CREATE_FILE(dev, can_restart);
+ CAN_CREATE_FILE(dev, can_state);
+ CAN_CREATE_FILE(dev, can_restart_ms);
+
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_statistics_group);
+ if (err) {
+ printk(KERN_EMERG
+ "couldn't create sysfs group for CAN statistics\n");
+ }
+
+ if (priv->bittiming_const) {
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_bittiming_group);
+ if (err) {
+ printk(KERN_EMERG "couldn't create sysfs "
+ "group for CAN bittiming\n");
+ }
+ }
+}
+
+void can_remove_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ CAN_REMOVE_FILE(dev, can_ctrlmode);
+ CAN_REMOVE_FILE(dev, can_echo);
+ CAN_REMOVE_FILE(dev, can_state);
+ CAN_REMOVE_FILE(dev, can_restart);
+ CAN_REMOVE_FILE(dev, can_restart_ms);
+
+ sysfs_remove_group(&(dev->dev.kobj), &can_statistics_group);
+ if (priv->bittiming_const)
+ sysfs_remove_group(&(dev->dev.kobj), &can_bittiming_group);
+}
+
+#endif /* CONFIG_SYSFS */
+
+
+
diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h
new file mode 100644
index 000000000000..e21f2fa4b158
--- /dev/null
+++ b/drivers/net/can/sysfs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CAN_SYSFS_H
+#define CAN_SYSFS_H
+
+void can_create_sysfs(struct net_device *dev);
+void can_remove_sysfs(struct net_device *dev);
+
+#endif /* CAN_SYSFS_H */
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index 103f0f1df280..69e6b804c477 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -128,26 +128,27 @@ static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+
static void vcan_setup(struct net_device *dev)
{
- dev->type = ARPHRD_CAN;
- dev->mtu = sizeof(struct can_frame);
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->tx_queue_len = 0;
- dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_NOARP;
/* set flags according to driver capabilities */
if (echo)
dev->flags |= IFF_ECHO;
- dev->hard_start_xmit = vcan_tx;
- dev->destructor = free_netdev;
+ dev->hard_start_xmit = vcan_tx;
+ dev->destructor = free_netdev;
}
static struct rtnl_link_ops vcan_link_ops __read_mostly = {
- .kind = "vcan",
- .setup = vcan_setup,
+ .kind = "vcan",
+ .setup = vcan_setup,
};
static __init int vcan_init_module(void)
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 7107620f615d..d56846d8d5b4 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -193,12 +193,17 @@ static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0};
#define CIRRUS_DEFAULT_IRQ VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */
static unsigned int netcard_portlist[] __used __initdata = {CIRRUS_DEFAULT_BASE, 0};
static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0};
-#elif defined(CONFIG_MACH_MX31ADS)
-#include <mach/board-mx31ads.h>
-static unsigned int netcard_portlist[] __used __initdata = {
- PBC_BASE_ADDRESS + PBC_CS8900A_IOBASE + 0x300, 0
-};
-static unsigned cs8900_irq_map[] = {EXPIO_INT_ENET_INT, 0, 0, 0};
+#elif defined(CONFIG_ARCH_MXC)
+/*! Null terminated portlist used to probe for the CS8900A device on ISA Bus
+ * Add 3 to reset the page window before probing (fixes eth probe when deployed
+ * using nand_boot)
+ */
+extern unsigned int netcard_portlist[2];
+/*!
+ * The CS8900A has 4 IRQ pins, which is software selectable, CS8900A interrupt
+ * pin 0 is used for interrupt generation.
+ */
+extern unsigned int cs8900_irq_map[4];
#else
static unsigned int netcard_portlist[] __used __initdata =
{ 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
@@ -1034,7 +1039,7 @@ skip_this_frame:
void __init reset_chip(struct net_device *dev)
{
-#if !defined(CONFIG_MACH_MX31ADS)
+#if !defined(CONFIG_ARCH_MXC)
#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
struct net_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
@@ -1063,7 +1068,7 @@ void __init reset_chip(struct net_device *dev)
reset_start_time = jiffies;
while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
;
-#endif /* !CONFIG_MACH_MX31ADS */
+#endif /* !CONFIG_ARCH_MXC */
}
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index 36cb6e95b465..98c07af8a2f9 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -36,6 +36,7 @@
#define DRV_VERSION "1.01"
#define SPI_OPLEN 1
+#define MAX_ENC_CARDS 1
#define ENC28J60_MSG_DEFAULT \
(NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
@@ -49,6 +50,10 @@
/* Max TX retries in case of collision as suggested by errata datasheet */
#define MAX_TX_RETRYCOUNT 16
+#ifdef CONFIG_ARCH_STMP3XXX
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ocotp.h>
+#endif
enum {
RXFILTER_NORMAL,
RXFILTER_MULTI,
@@ -81,6 +86,69 @@ static struct {
u32 msg_enable;
} debug = { -1 };
+static int random_mac; /* = 0 */
+static char *mac[MAX_ENC_CARDS];
+
+static int enc28j60_get_mac(unsigned char *dev_addr, int idx)
+{
+ int i, r;
+ char *p, *item;
+ unsigned long v;
+ unsigned char sv[10];
+
+ if (idx < 0)
+ idx = 0;
+ if (idx > MAX_ENC_CARDS)
+ return false;
+
+ if (!mac[idx]) {
+#ifdef CONFIG_ARCH_STMP3XXX
+ if (get_evk_board_version() >= 1) {
+ int mac1 , mac2 , retry = 0;
+
+ HW_OCOTP_CTRL_SET(BM_OCOTP_CTRL_RD_BANK_OPEN);
+ while (HW_OCOTP_CTRL_RD() & BM_OCOTP_CTRL_BUSY) {
+ msleep(10);
+ retry++;
+ if (retry > 10)
+ return false;
+ }
+
+ mac1 = HW_OCOTP_CUSTn_RD(0);
+ mac2 = HW_OCOTP_CUSTn_RD(1);
+ if (MAX_ADDR_LEN < 6)
+ return false;
+
+ dev_addr[0] = (mac1 >> 24) & 0xFF;
+ dev_addr[1] = (mac1 >> 16) & 0xFF;
+ dev_addr[2] = (mac1 >> 8) & 0xFF;
+ dev_addr[3] = (mac1 >> 0) & 0xFF;
+ dev_addr[4] = (mac2 >> 8) & 0xFF;
+ dev_addr[5] = (mac2 >> 0) & 0xFF;
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ item = mac[idx];
+ for (i = 0; i < MAX_ADDR_LEN; i++) {
+ p = strchr(item, ':');
+ if (!p)
+ sprintf(sv, "0x%s", item);
+ else
+ sprintf(sv, "0x%*.*s", p - item, p-item, item);
+ r = strict_strtoul(sv, 0, &v);
+ dev_addr[i] = v;
+ if (p)
+ item = p + 1;
+ else
+ break;
+ if (r < 0)
+ return false;
+ }
+ return true;
+}
/*
* SPI read buffer
* wait for the SPI transfer and copy received data to destination
@@ -90,10 +158,13 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
{
u8 *rx_buf = priv->spi_transfer_buf + 4;
u8 *tx_buf = priv->spi_transfer_buf;
- struct spi_transfer t = {
- .tx_buf = tx_buf,
- .rx_buf = rx_buf,
- .len = SPI_OPLEN + len,
+ struct spi_transfer tt = {
+ .tx_buf = tx_buf,
+ .len = SPI_OPLEN,
+ };
+ struct spi_transfer tr = {
+ .rx_buf = rx_buf,
+ .len = len,
};
struct spi_message msg;
int ret;
@@ -102,10 +173,11 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
spi_message_init(&msg);
- spi_message_add_tail(&t, &msg);
+ spi_message_add_tail(&tt, &msg);
+ spi_message_add_tail(&tr, &msg);
ret = spi_sync(priv->spi, &msg);
if (ret == 0) {
- memcpy(data, &rx_buf[SPI_OPLEN], len);
+ memcpy(data, rx_buf, len);
ret = msg.status;
}
if (ret && netif_msg_drv(priv))
@@ -196,16 +268,32 @@ static void enc28j60_soft_reset(struct enc28j60_net *priv)
*/
static void enc28j60_set_bank(struct enc28j60_net *priv, u8 addr)
{
- if ((addr & BANK_MASK) != priv->bank) {
- u8 b = (addr & BANK_MASK) >> 5;
+ u8 b = (addr & BANK_MASK) >> 5;
+
+ /* These registers (EIE, EIR, ESTAT, ECON2, ECON1)
+ * are present in all banks, no need to switch bank
+ */
+ if (addr >= EIE && addr <= ECON1)
+ return;
- if (b != (ECON1_BSEL1 | ECON1_BSEL0))
+ /* Clear or set each bank selection bit as needed */
+ if ((b & ECON1_BSEL0) != (priv->bank & ECON1_BSEL0)) {
+ if (b & ECON1_BSEL0)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL0);
+ else
+ spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
+ ECON1_BSEL0);
+ }
+ if ((b & ECON1_BSEL1) != (priv->bank & ECON1_BSEL1)) {
+ if (b & ECON1_BSEL1)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL1);
+ else
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
- ECON1_BSEL1 | ECON1_BSEL0);
- if (b != 0)
- spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, b);
- priv->bank = (addr & BANK_MASK);
+ ECON1_BSEL1);
}
+ priv->bank = b;
}
/*
@@ -930,7 +1018,7 @@ static void enc28j60_hw_rx(struct net_device *ndev)
if (netif_msg_rx_status(priv))
enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat);
- if (!RSV_GETBIT(rxstat, RSV_RXOK)) {
+ if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
ndev->stats.rx_errors++;
@@ -938,6 +1026,8 @@ static void enc28j60_hw_rx(struct net_device *ndev)
ndev->stats.rx_crc_errors++;
if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
ndev->stats.rx_frame_errors++;
+ if (len > MAX_FRAMELEN)
+ ndev->stats.rx_over_errors++;
} else {
skb = dev_alloc_skb(len + NET_IP_ALIGN);
if (!skb) {
@@ -1093,8 +1183,24 @@ static int enc28j60_rx_interrupt(struct net_device *ndev)
priv->max_pk_counter);
}
ret = pk_counter;
- while (pk_counter-- > 0)
+ while (pk_counter-- > 0) {
+ if (!priv->full_duplex) {
+ /*
+ * This works only in HALF DUPLEX mode:
+ * when more than 2 packets are available, start
+ * transmission of 11111.. frame by setting
+ * FCON0 (0x01) in EFLOCON
+ *
+ * This bit can be cleared either explicitly, or by
+ * trasmitting the packet in enc28j60_hw_tx.
+ */
+ if (pk_counter > 2)
+ locked_reg_bfset(priv, EFLOCON, 0x01);
+ if (pk_counter == 1)
+ locked_reg_bfclr(priv, EFLOCON, 0x01);
+ }
enc28j60_hw_rx(ndev);
+ }
return ret;
}
@@ -1220,6 +1326,11 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
*/
static void enc28j60_hw_tx(struct enc28j60_net *priv)
{
+ if (!priv->tx_skb) {
+ enc28j60_tx_clear(priv->netdev, false);
+ return;
+ }
+
if (netif_msg_tx_queued(priv))
printk(KERN_DEBUG DRV_NAME
": Tx Packet Len:%d\n", priv->tx_skb->len);
@@ -1523,6 +1634,7 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
struct net_device *dev;
struct enc28j60_net *priv;
int ret = 0;
+ int set;
if (netif_msg_drv(&debug))
dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
@@ -1556,7 +1668,11 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
ret = -EIO;
goto error_irq;
}
- random_ether_addr(dev->dev_addr);
+
+ /* need a counter here, to count instances of enc28j60 devices */
+ set = enc28j60_get_mac(dev->dev_addr, -1);
+ if (!set || random_mac)
+ random_ether_addr(dev->dev_addr);
enc28j60_set_hw_macaddr(dev);
/* Board setup must set the relevant edge trigger type;
@@ -1616,6 +1732,40 @@ static int __devexit enc28j60_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int
+enc28j60_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ netif_stop_queue(net_dev);
+ netif_device_detach(net_dev);
+ disable_irq(spi->irq);
+ }
+ return 0;
+}
+
+static int
+enc28j60_resume(struct spi_device *spi)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ enable_irq(spi->irq);
+ netif_device_attach(net_dev);
+ netif_start_queue(net_dev);
+ schedule_work(&priv->restart_work);
+ }
+ return 0;
+}
+#else
+#define enc28j60_resume NULL
+#define enc28j60_suspend NULL
+#endif
+
static struct spi_driver enc28j60_driver = {
.driver = {
.name = DRV_NAME,
@@ -1623,6 +1773,8 @@ static struct spi_driver enc28j60_driver = {
},
.probe = enc28j60_probe,
.remove = __devexit_p(enc28j60_remove),
+ .suspend = enc28j60_suspend,
+ .resume = enc28j60_resume,
};
static int __init enc28j60_init(void)
@@ -1645,4 +1797,6 @@ MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_LICENSE("GPL");
module_param_named(debug, debug.msg_enable, int, 0);
+module_param(random_mac, int, 0444);
+module_param_array(mac, charp, NULL, 0);
MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index ecd5c71a7a8a..94d55a17d11c 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -18,6 +18,9 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
*/
+/*
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -36,16 +39,29 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
+#include <linux/clk.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include "fec.h"
+#define FEC_ALIGNMENT (0x03) /*FEC needs 4bytes alignment*/
+#elif defined(CONFIG_ARCH_MXC)
+#include <mach/hardware.h>
+#include <mach/iim.h>
+#include "fec.h"
+#define FEC_ALIGNMENT (0x0F) /*FEC needs 128bits(32bytes) alignment*/
+#endif
+
+#define FEC_ADDR_ALIGNMENT(x) ((unsigned char *)(((unsigned long )(x) + (FEC_ALIGNMENT)) & (~FEC_ALIGNMENT)))
#if defined(CONFIG_FEC2)
#define FEC_MAX_PORTS 2
@@ -53,7 +69,7 @@
#define FEC_MAX_PORTS 1
#endif
-#if defined(CONFIG_M5272)
+#if defined(CONFIG_M5272) || defined(CONFIG_ARCH_MXC)
#define HAVE_mii_link_interrupt
#endif
@@ -72,6 +88,8 @@ static unsigned int fec_hw[] = {
(MCF_MBAR+0x30000),
#elif defined(CONFIG_M532x)
(MCF_MBAR+0xfc030000),
+#elif defined(CONFIG_ARCH_MXC)
+ (IO_ADDRESS(FEC_BASE_ADDR)),
#else
&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec),
#endif
@@ -149,6 +167,12 @@ typedef struct {
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+#ifndef CONFIG_ARCH_MXC
+#define FEC_ENET_MASK ((uint)0xffc00000)
+#else
+#define FEC_ENET_MASK ((uint)0xfff80000)
+#endif
+
/* The FEC stores dest/src/type, data, and checksum for receive packets.
*/
#define PKT_MAXBUF_SIZE 1518
@@ -162,7 +186,7 @@ typedef struct {
* account when setting it.
*/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
#else
#define OPT_FRAME_SIZE 0
@@ -185,11 +209,13 @@ struct fec_enet_private {
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
ushort skb_cur;
ushort skb_dirty;
/* CPM dual port RAM relative addresses.
*/
+ void * cbd_mem_base; /* save the virtual base address of rx&tx buffer descripter */
cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */
cbd_t *tx_bd_base;
cbd_t *cur_rx, *cur_tx; /* The next free ring entry */
@@ -206,6 +232,7 @@ struct fec_enet_private {
uint phy_speed;
phy_info_t const *phy;
struct work_struct phy_task;
+ struct net_device *net;
uint sequence_done;
uint mii_phy_task_queued;
@@ -217,6 +244,8 @@ struct fec_enet_private {
int link;
int old_link;
int full_duplex;
+
+ struct clk *clk;
};
static int fec_enet_open(struct net_device *dev);
@@ -231,6 +260,17 @@ static void fec_restart(struct net_device *dev, int duplex);
static void fec_stop(struct net_device *dev);
static void fec_set_mac_address(struct net_device *dev);
+static void __inline__ fec_dcache_inv_range(void * start, void * end);
+static void __inline__ fec_dcache_flush_range(void * start, void * end);
+
+/*
+ * fec_copy_threshold controls the copy when recieving ethernet frame.
+ * If ethernet header aligns 4bytes, the ip header and upper header will not aligns 4bytes.
+ * The resean is ethernet header is 14bytes.
+ * And the max size of tcp & ip header is 128bytes. Normally it is 40bytes.
+ * So I set the default value between 128 to 256.
+ */
+static int fec_copy_threshold = -1;
/* MII processing. We keep this as simple as possible. Requests are
* placed on the list (if there is room). When the request is finished
@@ -342,10 +382,10 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
* 4-byte boundaries. Use bounce buffers to copy data
* and get it aligned. Ugh.
*/
- if (bdp->cbd_bufaddr & 0x3) {
+ if ((bdp->cbd_bufaddr) & FEC_ALIGNMENT) {
unsigned int index;
index = bdp - fep->tx_bd_base;
- memcpy(fep->tx_bounce[index], (void *) bdp->cbd_bufaddr, bdp->cbd_datlen);
+ memcpy(fep->tx_bounce[index], (void *) skb->data, skb->len);
bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]);
}
@@ -359,8 +399,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Push the data cache so the CPM does not get stale memory
* data.
*/
- flush_dcache_range((unsigned long)skb->data,
- (unsigned long)skb->data + skb->len);
+ fec_dcache_flush_range(__va(bdp->cbd_bufaddr), __va(bdp->cbd_bufaddr) +
+ bdp->cbd_datlen);
/* Send it on its way. Tell FEC it's ready, interrupt when done,
* it's the last BD of the frame, and to put the CRC on the end.
@@ -373,7 +413,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
/* Trigger transmission start */
- fecp->fec_x_des_active = 0;
+ fecp->fec_x_des_active = 0x01000000;
/* If this was the last BD in the ring, start at the beginning again.
*/
@@ -460,7 +500,7 @@ fec_enet_interrupt(int irq, void * dev_id)
/* Handle receive event in its own function.
*/
- if (int_events & FEC_ENET_RXF) {
+ if (int_events & (FEC_ENET_RXF | FEC_ENET_RXB)) {
ret = IRQ_HANDLED;
fec_enet_rx(dev);
}
@@ -469,7 +509,7 @@ fec_enet_interrupt(int irq, void * dev_id)
descriptors. FEC handles all errors, we just discover
them as part of the transmit process.
*/
- if (int_events & FEC_ENET_TXF) {
+ if (int_events & (FEC_ENET_TXF | FEC_ENET_TXB)) {
ret = IRQ_HANDLED;
fec_enet_tx(dev);
}
@@ -572,6 +612,7 @@ fec_enet_rx(struct net_device *dev)
struct sk_buff *skb;
ushort pkt_len;
__u8 *data;
+ int rx_index ;
#ifdef CONFIG_M532x
flush_cache_all();
@@ -588,7 +629,7 @@ fec_enet_rx(struct net_device *dev)
bdp = fep->cur_rx;
while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
-
+ rx_index = bdp - fep->rx_bd_base;
#ifndef final_version
/* Since we have allocated space to hold a complete frame,
* the last indicator should be set.
@@ -638,14 +679,31 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
* include that when passing upstream as it messes up
* bridging applications.
*/
- skb = dev_alloc_skb(pkt_len-4);
+ if ((pkt_len - 4) < fec_copy_threshold) {
+ skb = dev_alloc_skb(pkt_len);
+ } else {
+ skb = dev_alloc_skb(FEC_ENET_RX_FRSIZE);
+ }
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
dev->stats.rx_dropped++;
} else {
- skb_put(skb,pkt_len-4); /* Make room */
- skb_copy_to_linear_data(skb, data, pkt_len-4);
+ if ((pkt_len - 4) < fec_copy_threshold) {
+ skb_reserve(skb, 2); /*skip 2bytes, so ipheader is align 4bytes*/
+ skb_put(skb,pkt_len-4); /* Make room */
+ skb_copy_to_linear_data(skb, data, pkt_len-4);
+ } else {
+ struct sk_buff * pskb = fep->rx_skbuff[rx_index];
+
+ fec_dcache_inv_range(skb->data, skb->data +
+ FEC_ENET_RX_FRSIZE);
+ fep->rx_skbuff[rx_index] = skb;
+ skb->data = FEC_ADDR_ALIGNMENT(skb->data);
+ bdp->cbd_bufaddr = __pa(skb->data);
+ skb_put(pskb,pkt_len-4); /* Make room */
+ skb = pskb;
+ }
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
}
@@ -672,7 +730,7 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
* incoming frames. On a heavily loaded network, we should be
* able to keep up at the expense of system resources.
*/
- fecp->fec_r_des_active = 0;
+ fecp->fec_r_des_active = 0x01000000;
#endif
} /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */
fep->cur_rx = (cbd_t *)bdp;
@@ -1207,6 +1265,48 @@ static phy_info_t phy_info_dp83848= {
},
};
+static phy_info_t phy_info_lan8700 = {
+ 0x0007C0C,
+ "LAN8700",
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* act_int */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+
+static phy_info_t phy_info_lan8710 = {
+ 0x0007C0F,
+ "LAN8710",
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* act_int */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+
/* ------------------------------------------------------------------------- */
static phy_info_t const * const phy_info[] = {
@@ -1216,6 +1316,8 @@ static phy_info_t const * const phy_info[] = {
&phy_info_am79c874,
&phy_info_ks8721bl,
&phy_info_dp83848,
+ &phy_info_lan8700,
+ &phy_info_lan8710,
NULL
};
@@ -1227,6 +1329,21 @@ mii_link_interrupt(int irq, void * dev_id);
#if defined(CONFIG_M5272)
/*
+ * * do some initializtion based architecture of this chip
+ * */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * * do some cleanup based architecture of this chip
+ * */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
+/*
* Code specific to Coldfire 5272 setup.
*/
static void __inline__ fec_request_intrs(struct net_device *dev)
@@ -1327,15 +1444,40 @@ static void __inline__ fec_phy_ack_intr(void)
*icrp = 0x0d000000;
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
+ return addr;
}
/*
- * Do not need to make region uncached on 5272.
+ * unmap memory erea started with addr from uncachable erea.
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_unmap_uncache(void * addr)
{
+ return ;
}
/* ------------------------------------------------------------------------- */
@@ -1343,6 +1485,22 @@ static void __inline__ fec_uncache(unsigned long addr)
#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
+/*
* Code specific to Coldfire 5230/5231/5232/5234/5235,
* the 5270/5271/5274/5275 and 5280/5282 setups.
*/
@@ -1489,20 +1647,58 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
}
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
/*
- * Do not need to make region uncached on 5272.
+ * flush dcache related with the virtual memory range(start, end)
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return addr;
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
/* ------------------------------------------------------------------------- */
#elif defined(CONFIG_M520x)
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
/*
* Code specific to Coldfire 520x
@@ -1610,17 +1806,63 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
{
+ return ;
}
-static void __inline__ fec_uncache(unsigned long addr)
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
+ return addr;
}
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
+}
+
+
/* ------------------------------------------------------------------------- */
#elif defined(CONFIG_M532x)
+
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
/*
* Code specific for M532x
*/
@@ -1749,21 +1991,297 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
}
/*
- * Do not need to make region uncached on 532x.
+ * invalidate dcache related with the virtual memory range(start, end)
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return addr;
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
/* ------------------------------------------------------------------------- */
+#elif defined(CONFIG_ARCH_MXC)
+
+extern void gpio_fec_active(void);
+extern void gpio_fec_inactive(void);
+extern unsigned int expio_intr_fec;
+
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ struct clk *clk;
+ gpio_fec_active();
+ clk = clk_get(NULL, "fec_clk");
+ clk_enable(clk);
+ clk_put(clk);
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ struct clk *clk;
+ clk = clk_get(NULL, "fec_clk");
+ clk_disable(clk);
+ clk_put(clk);
+ gpio_fec_inactive();
+ return;
+}
+
+/*
+ * Code specific to Freescale i.MXC
+ */
+static void __inline__ fec_request_intrs(struct net_device *dev)
+{
+ /* Setup interrupt handlers. */
+ if (request_irq(MXC_INT_FEC, fec_enet_interrupt, 0, "fec", dev) != 0)
+ panic("FEC: Could not allocate FEC IRQ(%d)!\n", MXC_INT_FEC);
+ /* TODO: disable now due to CPLD issue */
+ if ((expio_intr_fec > 0) &&
+ (request_irq(expio_intr_fec, mii_link_interrupt, 0, "fec(MII)", dev) != 0))
+ panic("FEC: Could not allocate FEC(MII) IRQ(%d)!\n", expio_intr_fec);
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+{
+ u32 rate;
+ struct clk *clk;
+ volatile fec_t *fecp;
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
+ fecp->fec_x_cntrl = 0x00;
+
+ /*
+ * Set MII speed to 2.5 MHz
+ */
+ clk = clk_get(NULL, "fec_clk");
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ fep->phy_speed =
+ ((((rate / 2 + 4999999) / 2500000) / 2) & 0x3F) << 1;
+ fecp->fec_mii_speed = fep->phy_speed;
+ fec_restart(dev, 0);
+}
+
+#define FEC_IIM_BASE IO_ADDRESS(IIM_BASE_ADDR)
+static void __inline__ fec_get_mac(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+ int i;
+ unsigned long fec_mac_base = FEC_IIM_BASE + MXC_IIMKEY0;
+ fecp = fep->hwp;
+
+ if (fecp->fec_addr_low || fecp->fec_addr_high) {
+ *((unsigned long *) &tmpaddr[0]) =
+ be32_to_cpu(fecp->fec_addr_low);
+ *((unsigned short *) &tmpaddr[4]) =
+ be32_to_cpu(fecp->fec_addr_high);
+ iap = &tmpaddr[0];
+ } else {
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ fec_mac_base = FEC_IIM_BASE + MXC_IIMMAC;
+
+ memset(tmpaddr, 0, ETH_ALEN);
+ if (!(machine_is_mx35_3ds() || cpu_is_mx51())) {
+ /*
+ * Get MAC address from IIM.
+ * If it is all 1's or 0's, use the default.
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ tmpaddr[ETH_ALEN-1-i] =
+ __raw_readb(fec_mac_base + i * 4);
+ }
+ iap = &tmpaddr[0];
+
+ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+ iap = fec_mac_default;
+ if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+ (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+ iap = fec_mac_default;
+ }
+
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+
+#ifndef MODULE
+static int fec_mac_setup(char *new_mac)
+{
+ char *ptr, *p = new_mac;
+ int i = 0;
+
+ while (p && (*p) && i < 6) {
+ ptr = strchr(p, ':');
+ if (ptr)
+ *ptr++ = '\0';
+
+ if (strlen(p)) {
+ unsigned long tmp = simple_strtoul(p, NULL, 16);
+ if (tmp > 0xff)
+ break;
+ fec_mac_default[i++] = tmp;
+ }
+ p = ptr;
+ }
+
+ return 0;
+}
+
+__setup("fec_mac=", fec_mac_setup);
+#endif
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+ if (expio_intr_fec > 0)
+ enable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+ if (expio_intr_fec > 0)
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+ if (expio_intr_fec > 0)
+ disable_irq(expio_intr_fec);
+}
+
+#ifdef CONFIG_ARCH_MX25
+/*
+ * i.MX25 allows RMII mode to be configured via a gasket
+ */
+#define FEC_MIIGSK_CFGR_FRCONT (1 << 6)
+#define FEC_MIIGSK_CFGR_LBMODE (1 << 4)
+#define FEC_MIIGSK_CFGR_EMODE (1 << 3)
+#define FEC_MIIGSK_CFGR_IF_MODE_MASK (3 << 0)
+#define FEC_MIIGSK_CFGR_IF_MODE_MII (0 << 0)
+#define FEC_MIIGSK_CFGR_IF_MODE_RMII (1 << 0)
+
+#define FEC_MIIGSK_ENR_READY (1 << 2)
+#define FEC_MIIGSK_ENR_EN (1 << 1)
+
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp = fep->hwp;
+ /*
+ * Set up the MII gasket for RMII mode
+ */
+ printk("%s: enable RMII gasket\n", dev->name);
+ /* disable the gasket and wait */
+ fecp->fec_miigsk_enr = 0;
+ while (fecp->fec_miigsk_enr & FEC_MIIGSK_ENR_READY)
+ udelay(1);
+
+ /* configure the gasket for RMII, 50 MHz, no loopback, no echo */
+ fecp->fec_miigsk_cfgr = FEC_MIIGSK_CFGR_IF_MODE_RMII;
+
+ /* re-enable the gasket */
+ fecp->fec_miigsk_enr = FEC_MIIGSK_ENR_EN;
+}
#else
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+#endif
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ dma_sync_single_for_device(NULL, (unsigned long)__pa(start),
+ (unsigned long)(end - start),
+ DMA_FROM_DEVICE);
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ dma_sync_single_for_device(NULL, (unsigned long)__pa(start),
+ (unsigned long)(end - start), DMA_TO_DEVICE);
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return (unsigned long)ioremap(__pa(addr), size);
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return iounmap(addr);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#else
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
/*
* Code specific to the MPC860T setup.
@@ -1831,7 +2349,7 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
volatile fec_t *fecp;
@@ -1842,12 +2360,40 @@ static void __inline__ fec_localhw_setup(void)
fecp->fec_fun_code = 0x78000000;
}
-static void __inline__ fec_uncache(unsigned long addr)
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
pte_t *pte;
pte = va_to_pte(mem_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
flush_tlb_page(init_mm.mmap, mem_addr);
+ return addr;
+}
+
+/*
+ * * unmap memory erea started with addr from uncachable erea.
+ * */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
#endif
@@ -2073,10 +2619,14 @@ mii_link_interrupt(int irq, void * dev_id)
#if 0
disable_irq(fep->mii_irq); /* disable now, enable later */
#endif
-
- mii_do_cmd(dev, fep->phy->ack_int);
- mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
-
+ /*
+ * Some board will trigger phy interrupt before phy enable.
+ * And at that moment , fep->phy is not initialized.
+ */
+ if (fep->phy) {
+ mii_do_cmd(dev, fep->phy->ack_int);
+ mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
+ }
return IRQ_HANDLED;
}
#endif
@@ -2086,6 +2636,7 @@ fec_enet_open(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ fec_arch_init();
/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
@@ -2094,6 +2645,8 @@ fec_enet_open(struct net_device *dev)
fep->sequence_done = 0;
fep->link = 0;
+ fec_restart(dev, 1);
+
if (fep->phy) {
mii_do_cmd(dev, fep->phy->ack_int);
mii_do_cmd(dev, fep->phy->config);
@@ -2110,18 +2663,14 @@ fec_enet_open(struct net_device *dev)
schedule();
mii_do_cmd(dev, fep->phy->startup);
-
- /* Set the initial link state to true. A lot of hardware
- * based on this device does not implement a PHY interrupt,
- * so we are never notified of link change.
- */
- fep->link = 1;
- } else {
- fep->link = 1; /* lets just try it and see */
- /* no phy, go full duplex, it's most likely a hub chip */
- fec_restart(dev, 1);
}
+ /* Set the initial link state to true. A lot of hardware
+ * based on this device does not implement a PHY interrupt,
+ * so we are never notified of link change.
+ */
+ fep->link = 1;
+
netif_start_queue(dev);
fep->opened = 1;
return 0; /* Success */
@@ -2135,9 +2684,10 @@ fec_enet_close(struct net_device *dev)
/* Don't know what to do yet.
*/
fep->opened = 0;
- netif_stop_queue(dev);
- fec_stop(dev);
-
+ if (fep->link) {
+ fec_stop(dev);
+ }
+ fec_arch_exit();
return 0;
}
@@ -2248,6 +2798,7 @@ int __init fec_enet_init(struct net_device *dev)
unsigned long mem_addr;
volatile cbd_t *bdp;
cbd_t *cbd_base;
+ struct sk_buff* pskb;
volatile fec_t *fecp;
int i, j;
static int index = 0;
@@ -2256,6 +2807,8 @@ int __init fec_enet_init(struct net_device *dev)
if (index >= FEC_MAX_PORTS)
return -ENXIO;
+ fep->net = dev;
+
/* Allocate memory for buffer descriptors.
*/
mem_addr = __get_free_page(GFP_KERNEL);
@@ -2264,6 +2817,7 @@ int __init fec_enet_init(struct net_device *dev)
return -ENOMEM;
}
+ fep->cbd_mem_base = (void *)mem_addr;
spin_lock_init(&fep->hw_lock);
spin_lock_init(&fep->mii_lock);
@@ -2288,10 +2842,14 @@ int __init fec_enet_init(struct net_device *dev)
*/
fec_get_mac(dev);
- cbd_base = (cbd_t *)mem_addr;
- /* XXX: missing check for allocation failure */
+ cbd_base = (cbd_t *)fec_map_uncache(mem_addr, PAGE_SIZE);
+ if (cbd_base == NULL) {
+ free_page(mem_addr);
+ printk("FEC: map descriptor memory to uncacheable failed?\n");
+ return -ENOMEM;
+ }
- fec_uncache(mem_addr);
+ /* XXX: missing check for allocation failure */
/* Set receive and transmit descriptor base.
*/
@@ -2306,25 +2864,26 @@ int __init fec_enet_init(struct net_device *dev)
/* Initialize the receive buffer descriptors.
*/
bdp = fep->rx_bd_base;
- for (i=0; i<FEC_ENET_RX_PAGES; i++) {
-
- /* Allocate a page.
- */
- mem_addr = __get_free_page(GFP_KERNEL);
- /* XXX: missing check for allocation failure */
-
- fec_uncache(mem_addr);
-
- /* Initialize the BD for every fragment in the page.
- */
- for (j=0; j<FEC_ENET_RX_FRPPG; j++) {
- bdp->cbd_sc = BD_ENET_RX_EMPTY;
- bdp->cbd_bufaddr = __pa(mem_addr);
- mem_addr += FEC_ENET_RX_FRSIZE;
- bdp++;
+ for (i=0; i<RX_RING_SIZE; i++, bdp++) {
+ pskb = dev_alloc_skb(FEC_ENET_RX_FRSIZE);
+ if(pskb == NULL) {
+ for(; i>0; i--) {
+ if( fep->rx_skbuff[i-1] ) {
+ kfree_skb(fep->rx_skbuff[i-1]);
+ fep->rx_skbuff[i-1] = NULL;
+ }
+ }
+ printk("FEC: allocate skb fail when initializing rx buffer \n");
+ free_page(mem_addr);
+ return -ENOMEM;
}
+ fep->rx_skbuff[i] = pskb;
+ fec_dcache_inv_range(pskb->data, pskb->data +
+ FEC_ENET_RX_FRSIZE);
+ pskb->data = FEC_ADDR_ALIGNMENT(pskb->data);
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
+ bdp->cbd_bufaddr = __pa(pskb->data);
}
-
/* Set the last buffer to wrap.
*/
bdp--;
@@ -2357,19 +2916,23 @@ int __init fec_enet_init(struct net_device *dev)
/* Set receive and transmit descriptor base.
*/
- fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
- fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
+ fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base + RX_RING_SIZE*sizeof(cbd_t)));
/* Install our interrupt handlers. This varies depending on
* the architecture.
*/
fec_request_intrs(dev);
+ /* Clear and enable interrupts */
+ fecp->fec_ievent = FEC_ENET_MASK;
+ fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII;
+
fecp->fec_grp_hash_table_high = 0;
fecp->fec_grp_hash_table_low = 0;
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0;
+ fecp->fec_r_des_active = 0x01000000;
#ifndef CONFIG_M5272
fecp->fec_hash_table_high = 0;
fecp->fec_hash_table_low = 0;
@@ -2392,10 +2955,6 @@ int __init fec_enet_init(struct net_device *dev)
/* setup MII interface */
fec_set_mii(dev, fep);
- /* Clear and enable interrupts */
- fecp->fec_ievent = 0xffc00000;
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
-
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
@@ -2427,9 +2986,15 @@ fec_restart(struct net_device *dev, int duplex)
fecp->fec_ecntrl = 1;
udelay(10);
+ /* Enable interrupts we wish to service.
+ */
+ fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII;
+
/* Clear any outstanding interrupt.
- */
- fecp->fec_ievent = 0xffc00000;
+ *
+ */
+ fecp->fec_ievent = FEC_ENET_MASK;
+
fec_enable_phy_intr();
/* Set station address.
@@ -2445,12 +3010,12 @@ fec_restart(struct net_device *dev, int duplex)
*/
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
- fec_localhw_setup();
+ fec_localhw_setup(dev);
/* Set receive and transmit descriptor base.
*/
- fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
- fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
+ fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base + RX_RING_SIZE*sizeof(cbd_t)));
fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
fep->cur_rx = fep->rx_bd_base;
@@ -2517,11 +3082,7 @@ fec_restart(struct net_device *dev, int duplex)
/* And last, enable the transmit and receive processing.
*/
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0;
-
- /* Enable interrupts we wish to service.
- */
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
+ fecp->fec_r_des_active = 0x01000000;
}
static void
@@ -2530,6 +3091,8 @@ fec_stop(struct net_device *dev)
volatile fec_t *fecp;
struct fec_enet_private *fep;
+ netif_stop_queue(dev);
+
fep = netdev_priv(dev);
fecp = fep->hwp;
@@ -2561,15 +3124,18 @@ fec_stop(struct net_device *dev)
static int __init fec_enet_module_init(void)
{
struct net_device *dev;
- int i, err;
+ int i, err, ret = 0;
DECLARE_MAC_BUF(mac);
printk("FEC ENET Version 0.2\n");
+ fec_arch_init();
for (i = 0; (i < FEC_MAX_PORTS); i++) {
dev = alloc_etherdev(sizeof(struct fec_enet_private));
- if (!dev)
- return -ENOMEM;
+ if (!dev) {
+ ret = -ENOMEM;
+ goto exit;
+ }
err = fec_enet_init(dev);
if (err) {
free_netdev(dev);
@@ -2578,13 +3144,19 @@ static int __init fec_enet_module_init(void)
if (register_netdev(dev) != 0) {
/* XXX: missing cleanup here */
free_netdev(dev);
- return -EIO;
+ ret = -EIO;
+ goto exit;
}
printk("%s: ethernet %s\n",
dev->name, print_mac(mac, dev->dev_addr));
}
- return 0;
+
+exit:
+ fec_arch_exit();
+ return ret;
+
+
}
module_init(fec_enet_module_init);
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 292719daceff..cb21ef1a8052 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/fec.h
@@ -14,7 +14,7 @@
/****************************************************************************/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
/*
* Just figures, Motorola would have to change the offsets for
* registers in the same peripheral device on different models
@@ -56,6 +56,10 @@ typedef struct fec {
unsigned long fec_r_des_start; /* Receive descriptor ring */
unsigned long fec_x_des_start; /* Transmit descriptor ring */
unsigned long fec_r_buff_size; /* Maximum receive buff size */
+ unsigned long fec_reserved12[93];
+ unsigned long fec_miigsk_cfgr; /* MIIGSK config register */
+ unsigned long fec_reserved13;
+ unsigned long fec_miigsk_enr; /* MIIGSK enable register */
} fec_t;
#else
@@ -103,12 +107,22 @@ typedef struct fec {
/*
* Define the buffer descriptor structure.
*/
+/* Please see "Receive Buffer Descriptor Field Definitions" in Specification.
+ * It's LE.
+ */
+#ifdef CONFIG_ARCH_MXC
+typedef struct bufdesc {
+ unsigned short cbd_datlen; /* Data length */
+ unsigned short cbd_sc; /* Control and status info */
+ unsigned long cbd_bufaddr; /* Buffer address */
+} cbd_t;
+#else
typedef struct bufdesc {
unsigned short cbd_sc; /* Control and status info */
unsigned short cbd_datlen; /* Data length */
unsigned long cbd_bufaddr; /* Buffer address */
} cbd_t;
-
+#endif
/*
* The following definitions courtesy of commproc.h, which where
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e6317557a531..e372f35ae038 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -342,5 +342,9 @@ config MCS_FIR
To compile it as a module, choose M here: the module will be called
mcs7780.
+config MXC_FIR
+ tristate "Freescale MXC FIR driver"
+ depends on ARCH_MXC && IRDA
+
endmenu
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
index 5d20fde32a24..d32839cf9e73 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o
obj-$(CONFIG_VIA_FIR) += via-ircc.o
obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o
obj-$(CONFIG_MCS_FIR) += mcs7780.o
+obj-$(CONFIG_MXC_FIR) += mxc_ir.o
obj-$(CONFIG_AU1000_FIR) += au1k_ir.o
# SIR drivers
obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o
diff --git a/drivers/net/irda/mxc_ir.c b/drivers/net/irda/mxc_ir.c
new file mode 100644
index 000000000000..7adcf5363078
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.c
@@ -0,0 +1,1777 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * Based on sa1100_ir.c - Copyright 2000-2001 Russell King
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_ir.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC FIRI.
+ *
+ * This driver is based on drivers/net/irda/sa1100_ir.c, by Russell King.
+ *
+ * @ingroup FIRI
+ */
+
+/*
+ * Include Files
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include "mxc_ir.h"
+
+#define IS_SIR(mi) ( (mi)->speed <= 115200 )
+#define IS_MIR(mi) ( (mi)->speed < 4000000 && (mi)->speed >= 576000 )
+#define IS_FIR(mi) ( (mi)->speed >= 4000000 )
+
+#define SDMA_START_DELAY() { \
+ volatile int j,k;\
+ int i;\
+ for(i=0;i<10000;i++)\
+ k=j;\
+ }
+
+#define IRDA_FRAME_SIZE_LIMIT 2047
+#define UART_BUFF_SIZE 14384
+
+#define UART4_UFCR_TXTL 16
+#define UART4_UFCR_RXTL 1
+
+#define FIRI_SDMA_TX
+#define FIRI_SDMA_RX
+
+/*!
+ * This structure is a way for the low level driver to define their own
+ * \b mxc_irda structure. This structure includes SK buffers, DMA buffers.
+ * and has other elements that are specifically required by this driver.
+ */
+struct mxc_irda {
+ /*!
+ * This keeps track of device is running or not
+ */
+ unsigned char open;
+
+ /*!
+ * This holds current FIRI communication speed
+ */
+ int speed;
+
+ /*!
+ * This holds FIRI communication speed for next packet
+ */
+ int newspeed;
+
+ /*!
+ * SK buffer for transmitter
+ */
+ struct sk_buff *txskb;
+
+ /*!
+ * SK buffer for receiver
+ */
+ struct sk_buff *rxskb;
+
+#ifdef FIRI_SDMA_RX
+ /*!
+ * SK buffer for tasklet
+ */
+ struct sk_buff *tskb;
+#endif
+
+ /*!
+ * DMA address for transmitter
+ */
+ dma_addr_t dma_rx_buff_phy;
+
+ /*!
+ * DMA address for receiver
+ */
+ dma_addr_t dma_tx_buff_phy;
+
+ /*!
+ * DMA Transmit buffer length
+ */
+ unsigned int dma_tx_buff_len;
+
+ /*!
+ * DMA channel for transmitter
+ */
+ int txdma_ch;
+
+ /*!
+ * DMA channel for receiver
+ */
+ int rxdma_ch;
+
+ /*!
+ * IrDA network device statistics
+ */
+ struct net_device_stats stats;
+
+ /*!
+ * The device structure used to get FIRI information
+ */
+ struct device *dev;
+
+ /*!
+ * Resource structure for UART, which will maintain base addresses and IRQs.
+ */
+ struct resource *uart_res;
+
+ /*!
+ * Base address of UART, used in readl and writel.
+ */
+ void *uart_base;
+
+ /*!
+ * Resource structure for FIRI, which will maintain base addresses and IRQs.
+ */
+ struct resource *firi_res;
+
+ /*!
+ * Base address of FIRI, used in readl and writel.
+ */
+ void *firi_base;
+
+ /*!
+ * UART IRQ number.
+ */
+ int uart_irq;
+
+ /*!
+ * Second UART IRQ number in case the interrupt lines are not muxed.
+ */
+ int uart_irq1;
+
+ /*!
+ * UART clock needed for baud rate calculations
+ */
+ struct clk *uart_clk;
+
+ /*!
+ * UART clock needed for baud rate calculations
+ */
+ unsigned long uart_clk_rate;
+
+ /*!
+ * FIRI clock needed for baud rate calculations
+ */
+ struct clk *firi_clk;
+
+ /*!
+ * FIRI IRQ number.
+ */
+ int firi_irq;
+
+ /*!
+ * IrLAP layer instance
+ */
+ struct irlap_cb *irlap;
+
+ /*!
+ * Driver supported baudrate capabilities
+ */
+ struct qos_info qos;
+
+ /*!
+ * Temporary transmit buffer used by the driver
+ */
+ iobuff_t tx_buff;
+
+ /*!
+ * Temporary receive buffer used by the driver
+ */
+ iobuff_t rx_buff;
+
+ /*!
+ * Pointer to platform specific data structure.
+ */
+ struct mxc_ir_platform_data *mxc_ir_plat;
+
+ /*!
+ * This holds the power management status of this module.
+ */
+ int suspend;
+
+};
+
+extern void gpio_firi_active(void *, unsigned int);
+extern void gpio_firi_inactive(void);
+extern void gpio_firi_init(void);
+
+void mxc_irda_firi_init(struct mxc_irda *si);
+#ifdef FIRI_SDMA_RX
+static void mxc_irda_fir_dma_rx_irq(void *id, int error_status,
+ unsigned int count);
+#endif
+#ifdef FIRI_SDMA_TX
+static void mxc_irda_fir_dma_tx_irq(void *id, int error_status,
+ unsigned int count);
+#endif
+
+/*!
+ * This function allocates and maps the receive buffer,
+ * unless it is already allocated.
+ *
+ * @param si FIRI device specific structure.
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_rx_alloc(struct mxc_irda *si)
+{
+#ifdef FIRI_SDMA_RX
+ mxc_dma_requestbuf_t dma_request;
+#endif
+ if (si->rxskb) {
+ return 0;
+ }
+
+ si->rxskb = alloc_skb(IRDA_FRAME_SIZE_LIMIT + 1, GFP_ATOMIC);
+
+ if (!si->rxskb) {
+ dev_err(si->dev, "mxc_ir: out of memory for RX SKB\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Align any IP headers that may be contained
+ * within the frame.
+ */
+ skb_reserve(si->rxskb, 1);
+
+#ifdef FIRI_SDMA_RX
+ si->dma_rx_buff_phy =
+ dma_map_single(si->dev, si->rxskb->data, IRDA_FRAME_SIZE_LIMIT,
+ DMA_FROM_DEVICE);
+
+ dma_request.num_of_bytes = IRDA_FRAME_SIZE_LIMIT;
+ dma_request.dst_addr = si->dma_rx_buff_phy;
+ dma_request.src_addr = si->firi_res->start;
+
+ mxc_dma_config(si->rxdma_ch, &dma_request, 1, MXC_DMA_MODE_READ);
+#endif
+ return 0;
+}
+
+/*!
+ * This function is called to disable the FIRI dma
+ *
+ * @param si FIRI port specific structure.
+ */
+static void mxc_irda_disabledma(struct mxc_irda *si)
+{
+ /* Stop all DMA activity. */
+#ifdef FIRI_SDMA_TX
+ mxc_dma_disable(si->txdma_ch);
+#endif
+#ifdef FIRI_SDMA_RX
+ mxc_dma_disable(si->rxdma_ch);
+#endif
+}
+
+/*!
+ * This function is called to set the IrDA communications speed.
+ *
+ * @param si FIRI specific structure.
+ * @param speed new Speed to be configured for.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_set_speed(struct mxc_irda *si, int speed)
+{
+ unsigned long flags;
+ int ret = 0;
+ unsigned int num, denom, baud;
+ unsigned int cr;
+
+ dev_dbg(si->dev, "speed:%d\n", speed);
+ switch (speed) {
+ case 9600:
+ case 19200:
+ case 38400:
+ case 57600:
+ case 115200:
+ dev_dbg(si->dev, "starting SIR\n");
+ baud = speed;
+ if (IS_FIR(si)) {
+#ifdef FIRI_SDMA_RX
+ mxc_dma_disable(si->rxdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ }
+ local_irq_save(flags);
+
+ /* Disable Tx and Rx */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ gpio_firi_inactive();
+
+ num = baud / 100 - 1;
+ denom = si->uart_clk_rate / 1600 - 1;
+ if ((denom < 65536) && (si->uart_clk_rate > 1600)) {
+ writel(num, si->uart_base + MXC_UARTUBIR);
+ writel(denom, si->uart_base + MXC_UARTUBMR);
+ }
+
+ si->speed = speed;
+
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+
+ /* Enable Receive Overrun and Data Ready interrupts. */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr |= (MXC_UARTUCR4_OREN | MXC_UARTUCR4_DREN);
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr |= (MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ local_irq_restore(flags);
+ break;
+ case 4000000:
+ local_irq_save(flags);
+
+ /* Disable Receive Overrun and Data Ready interrupts. */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr &= ~(MXC_UARTUCR4_OREN | MXC_UARTUCR4_DREN);
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ /* Disable Tx and Rx */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ /*
+ * FIR configuration
+ */
+ mxc_irda_disabledma(si);
+
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ gpio_firi_active(si->firi_base + FIRITCR, FIRITCR_TPP);
+
+ si->speed = speed;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ dev_dbg(si->dev, "Going for fast IRDA ...\n");
+ ret = mxc_irda_rx_alloc(si);
+
+ /* clear RX status register */
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+#ifdef FIRI_SDMA_RX
+ if (si->rxskb) {
+ mxc_dma_enable(si->rxdma_ch);
+ }
+#endif
+ local_irq_restore(flags);
+
+ break;
+ default:
+ dev_err(si->dev, "speed not supported by FIRI\n");
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * This function is called to set the IrDA communications speed.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static inline int mxc_irda_fir_error(struct mxc_irda *si)
+{
+ struct sk_buff *skb = si->rxskb;
+ unsigned int dd_error, crc_error, overrun_error;
+ unsigned int sr;
+
+ if (!skb) {
+ dev_err(si->dev, "no skb!\n");
+ return -1;
+ }
+
+ sr = readl(si->firi_base + FIRIRSR);
+ dd_error = sr & FIRIRSR_DDE;
+ crc_error = sr & FIRIRSR_CRCE;
+ overrun_error = sr & FIRIRSR_RFO;
+
+ if (!(dd_error | crc_error | overrun_error)) {
+ return 0;
+ }
+ dev_err(si->dev, "dde,crce,rfo=%d,%d,%d.\n", dd_error, crc_error,
+ overrun_error);
+ si->stats.rx_errors++;
+ if (crc_error) {
+ si->stats.rx_crc_errors++;
+ }
+ if (dd_error) {
+ si->stats.rx_frame_errors++;
+ }
+ if (overrun_error) {
+ si->stats.rx_frame_errors++;
+ }
+ writel(sr, si->firi_base + FIRIRSR);
+
+ return -1;
+}
+
+#ifndef FIRI_SDMA_RX
+/*!
+ * FIR interrupt service routine to handle receive.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq_rx(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->rxskb;
+ unsigned int sr, len;
+ int i;
+ unsigned char *p = skb->data;
+
+ /*
+ * Deal with any receive errors.
+ */
+ if (mxc_irda_fir_error(si) != 0) {
+ return;
+ }
+
+ sr = readl(si->firi_base + FIRIRSR);
+
+ if (!(sr & FIRIRSR_RPE)) {
+ return;
+ }
+
+ /*
+ * Coming here indicates that fir rx packet has been successfully recieved.
+ * And No error happened so far.
+ */
+ writel(sr | FIRIRSR_RPE, si->firi_base + FIRIRSR);
+
+ len = (sr & FIRIRSR_RFP) >> 8;
+
+ /* 4 bytes of CRC */
+ len -= 4;
+
+ skb_put(skb, len);
+
+ for (i = 0; i < len; i++) {
+ *p++ = readb(si->firi_base + FIRIRXFIFO);
+ }
+
+ /* Discard the four CRC bytes */
+ for (i = 0; i < 4; i++) {
+ readb(si->firi_base + FIRIRXFIFO);
+ }
+
+ /*
+ * Deal with the case of packet complete.
+ */
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ si->stats.rx_packets++;
+ si->stats.rx_bytes += len;
+ netif_rx(skb);
+
+ si->rxskb = NULL;
+ mxc_irda_rx_alloc(si);
+
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+
+}
+#endif
+
+/*!
+ * FIR interrupt service routine to handle transmit.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq_tx(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->txskb;
+ unsigned int cr, sr;
+
+ sr = readl(si->firi_base + FIRITSR);
+ writel(sr, si->firi_base + FIRITSR);
+
+ if (sr & FIRITSR_TC) {
+
+#ifdef FIRI_SDMA_TX
+ mxc_dma_disable(si->txdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~(FIRITCR_TCIE | FIRITCR_TE);
+ writel(cr, si->firi_base + FIRITCR);
+
+ if (si->newspeed) {
+ mxc_irda_set_speed(si, si->newspeed);
+ si->newspeed = 0;
+ }
+ si->txskb = NULL;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+ /*
+ * Account and free the packet.
+ */
+ if (skb) {
+#ifdef FIRI_SDMA_TX
+ dma_unmap_single(si->dev, si->dma_tx_buff_phy, skb->len,
+ DMA_TO_DEVICE);
+#endif
+ si->stats.tx_packets++;
+ si->stats.tx_bytes += skb->len;
+ dev_kfree_skb_irq(skb);
+ }
+ /*
+ * Make sure that the TX queue is available for sending
+ * (for retries). TX has priority over RX at all times.
+ */
+ netif_wake_queue(dev);
+ }
+}
+
+/*!
+ * This is FIRI interrupt handler.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ unsigned int sr1, sr2;
+
+ sr1 = readl(si->firi_base + FIRIRSR);
+ sr2 = readl(si->firi_base + FIRITSR);
+
+ if (sr2 & FIRITSR_TC)
+ mxc_irda_fir_irq_tx(dev);
+#ifndef FIRI_SDMA_RX
+ if (sr1 & (FIRIRSR_RPE | FIRIRSR_RFO))
+ mxc_irda_fir_irq_rx(dev);
+#endif
+
+}
+
+/*!
+ * This is the SIR transmit routine.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @param dev pointer to the net_device structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_sir_txirq(struct mxc_irda *si, struct net_device *dev)
+{
+ unsigned int sr1, sr2, cr;
+ unsigned int status;
+
+ sr1 = readl(si->uart_base + MXC_UARTUSR1);
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+
+ /*
+ * Echo cancellation for IRDA Transmit chars
+ * Disable the receiver and enable Transmit complete.
+ */
+ cr &= ~MXC_UARTUCR2_RXEN;
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr |= MXC_UARTUCR4_TCEN;
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ while ((sr1 & MXC_UARTUSR1_TRDY) && si->tx_buff.len) {
+
+ writel(*si->tx_buff.data++, si->uart_base + MXC_UARTUTXD);
+ si->tx_buff.len -= 1;
+ sr1 = readl(si->uart_base + MXC_UARTUSR1);
+ }
+
+ if (si->tx_buff.len == 0) {
+ si->stats.tx_packets++;
+ si->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head;
+
+ /*Yoohoo...we are done...Lets stop Tx */
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr &= ~MXC_UARTUCR1_TRDYEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+
+ do {
+ status = readl(si->uart_base + MXC_UARTUSR2);
+ } while (!(status & MXC_UARTUSR2_TXDC));
+
+ if (si->newspeed) {
+ mxc_irda_set_speed(si, si->newspeed);
+ si->newspeed = 0;
+ }
+ /* I'm hungry! */
+ netif_wake_queue(dev);
+
+ /* Is the transmit complete to reenable the receiver? */
+ if (status & MXC_UARTUSR2_TXDC) {
+
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr |= MXC_UARTUCR2_RXEN;
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ /* Disable the Transmit complete interrupt bit */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr &= ~MXC_UARTUCR4_TCEN;
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * This is the SIR receive routine.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @param dev pointer to the net_device structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_sir_rxirq(struct mxc_irda *si, struct net_device *dev)
+{
+ unsigned int data, status;
+ volatile unsigned int sr2;
+
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+ while ((sr2 & MXC_UARTUSR2_RDR) == 1) {
+ data = readl(si->uart_base + MXC_UARTURXD);
+ status = data & 0xf400;
+ if (status & MXC_UARTURXD_ERR) {
+ dev_err(si->dev, "Receive an incorrect data =0x%x.\n",
+ data);
+ si->stats.rx_errors++;
+ if (status & MXC_UARTURXD_OVRRUN) {
+ si->stats.rx_fifo_errors++;
+ dev_err(si->dev, "Rx overrun.\n");
+ }
+ if (status & MXC_UARTURXD_FRMERR) {
+ si->stats.rx_frame_errors++;
+ dev_err(si->dev, "Rx frame error.\n");
+ }
+ if (status & MXC_UARTURXD_PRERR) {
+ dev_err(si->dev, "Rx parity error.\n");
+ }
+ /* Other: it is the Break char.
+ * Do nothing for it. throw out the data.
+ */
+ async_unwrap_char(dev, &si->stats, &si->rx_buff,
+ (data & 0xFF));
+ } else {
+ /* It is correct data. */
+ data &= 0xFF;
+ async_unwrap_char(dev, &si->stats, &si->rx_buff, data);
+
+ dev->last_rx = jiffies;
+ }
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+ } /*while */
+ return 0;
+
+}
+
+static irqreturn_t mxc_irda_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ if (IS_FIR(si)) {
+ mxc_irda_fir_irq(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (readl(si->uart_base + MXC_UARTUCR2) & MXC_UARTUCR2_RXEN) {
+ mxc_irda_sir_rxirq(si, dev);
+ }
+ if ((readl(si->uart_base + MXC_UARTUCR1) & MXC_UARTUCR1_TRDYEN) &&
+ (readl(si->uart_base + MXC_UARTUSR1) & MXC_UARTUSR1_TRDY)) {
+ mxc_irda_sir_txirq(si, dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_irda_tx_irq(int irq, void *dev_id)
+{
+
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ mxc_irda_sir_txirq(si, dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_irda_rx_irq(int irq, void *dev_id)
+{
+
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ /* Clear the aging timer bit */
+ writel(MXC_UARTUSR1_AGTIM, si->uart_base + MXC_UARTUSR1);
+
+ mxc_irda_sir_rxirq(si, dev);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FIRI_SDMA_RX
+struct tasklet_struct dma_rx_tasklet;
+
+static void mxc_irda_rx_task(unsigned long tparam)
+{
+ struct mxc_irda *si = (struct mxc_irda *)tparam;
+ struct sk_buff *lskb = si->tskb;
+
+ si->tskb = NULL;
+ if (lskb) {
+ lskb->mac_header = lskb->data;
+ lskb->protocol = htons(ETH_P_IRDA);
+ netif_rx(lskb);
+ }
+}
+
+/*!
+ * Receiver DMA callback routine.
+ *
+ * @param id pointer to network device structure
+ * @param error_status used to pass error status to this callback function
+ * @param count number of bytes received
+ */
+static void mxc_irda_fir_dma_rx_irq(void *id, int error_status,
+ unsigned int count)
+{
+ struct net_device *dev = id;
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->rxskb;
+ unsigned int cr;
+ unsigned int len;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ len = count - 4; /* remove 4 bytes for CRC */
+ skb_put(skb, len);
+ skb->dev = dev;
+ si->tskb = skb;
+ tasklet_schedule(&dma_rx_tasklet);
+
+ if (si->dma_rx_buff_phy != 0)
+ dma_unmap_single(si->dev, si->dma_rx_buff_phy,
+ IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE);
+
+ si->rxskb = NULL;
+ mxc_irda_rx_alloc(si);
+
+ SDMA_START_DELAY();
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+
+ if (si->rxskb) {
+ mxc_dma_enable(si->rxdma_ch);
+ }
+}
+#endif
+
+#ifdef FIRI_SDMA_TX
+/*!
+ * This function is called by SDMA Interrupt Service Routine to indicate
+ * requested DMA transfer is completed.
+ *
+ * @param id pointer to network device structure
+ * @param error_status used to pass error status to this callback function
+ * @param count number of bytes sent
+ */
+static void mxc_irda_fir_dma_tx_irq(void *id, int error_status,
+ unsigned int count)
+{
+ struct net_device *dev = id;
+ struct mxc_irda *si = dev->priv;
+
+ mxc_dma_disable(si->txdma_ch);
+}
+#endif
+
+/*!
+ * This function is called by Linux IrDA network subsystem to
+ * transmit the Infrared data packet. The TX DMA channel is configured
+ * to transfer SK buffer data to FIRI TX FIFO along with DMA transfer
+ * completion routine.
+ *
+ * @param skb The packet that is queued to be sent
+ * @param dev net_device structure.
+ *
+ * @return The function returns 0 on success and a negative value on
+ * failure.
+ */
+static int mxc_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ int speed = irda_get_next_speed(skb);
+ unsigned int cr;
+
+ /*
+ * Does this packet contain a request to change the interface
+ * speed? If so, remember it until we complete the transmission
+ * of this frame.
+ */
+ if (speed != si->speed && speed != -1) {
+ si->newspeed = speed;
+ }
+
+ /* If this is an empty frame, we can bypass a lot. */
+ if (skb->len == 0) {
+ if (si->newspeed) {
+ si->newspeed = 0;
+ mxc_irda_set_speed(si, speed);
+ }
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /* We must not be transmitting... */
+ netif_stop_queue(dev);
+ if (IS_SIR(si)) {
+
+ si->tx_buff.data = si->tx_buff.head;
+ si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data,
+ si->tx_buff.truesize);
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr |= MXC_UARTUCR1_TRDYEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+ dev_kfree_skb(skb);
+ } else {
+ unsigned int mtt = irda_get_mtt(skb);
+ unsigned char *p = skb->data;
+ unsigned int skb_len = skb->len;
+#ifdef FIRI_SDMA_TX
+ mxc_dma_requestbuf_t dma_request;
+#else
+ unsigned int i, sr;
+#endif
+
+ skb_len = skb_len + ((4 - (skb_len % 4)) % 4);
+
+ if (si->txskb) {
+ BUG();
+ }
+ si->txskb = skb;
+
+ /*
+ * If we have a mean turn-around time, impose the specified
+ * specified delay. We could shorten this by timing from
+ * the point we received the packet.
+ */
+ if (mtt) {
+ udelay(mtt);
+ }
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ writel(skb->len - 1, si->firi_base + FIRITCTR);
+
+#ifdef FIRI_SDMA_TX
+ /*
+ * Configure DMA Tx Channel for source and destination addresses,
+ * Number of bytes in SK buffer to transfer and Transfer complete
+ * callback function.
+ */
+ si->dma_tx_buff_len = skb_len;
+ si->dma_tx_buff_phy =
+ dma_map_single(si->dev, p, skb_len, DMA_TO_DEVICE);
+
+ dma_request.num_of_bytes = skb_len;
+ dma_request.dst_addr = si->firi_res->start + FIRITXFIFO;
+ dma_request.src_addr = si->dma_tx_buff_phy;
+
+ mxc_dma_config(si->txdma_ch, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+
+ mxc_dma_enable(si->txdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr |= FIRITCR_TCIE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ cr |= FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+#ifndef FIRI_SDMA_TX
+ for (i = 0; i < skb->len;) {
+ sr = readl(si->firi_base + FIRITSR);
+ /* TFP = number of bytes in the TX FIFO for the
+ * Transmitter
+ * */
+ if ((sr >> 8) < 128) {
+ writeb(*p, si->firi_base + FIRITXFIFO);
+ p++;
+ i++;
+ }
+ }
+#endif
+ }
+
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+/*!
+ * This function handles network interface ioctls passed to this driver..
+ *
+ * @param dev net device structure
+ * @param ifreq user request data
+ * @param cmd command issued
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+ struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+ struct mxc_irda *si = dev->priv;
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd) {
+ /* This function will be used by IrLAP to change the speed */
+ case SIOCSBANDWIDTH:
+ dev_dbg(si->dev, "%s:with cmd SIOCSBANDWIDTH\n", __FUNCTION__);
+ if (capable(CAP_NET_ADMIN)) {
+ /*
+ * We are unable to set the speed if the
+ * device is not running.
+ */
+ if (si->open) {
+ ret = mxc_irda_set_speed(si, rq->ifr_baudrate);
+ } else {
+ dev_err(si->dev, "mxc_ir_ioctl: SIOCSBANDWIDTH:\
+ !netif_running\n");
+ ret = 0;
+ }
+ }
+ break;
+ case SIOCSMEDIABUSY:
+ dev_dbg(si->dev, "%s:with cmd SIOCSMEDIABUSY\n", __FUNCTION__);
+ ret = -EPERM;
+ if (capable(CAP_NET_ADMIN)) {
+ irda_device_set_media_busy(dev, TRUE);
+ ret = 0;
+ }
+ break;
+ case SIOCGRECEIVING:
+ rq->ifr_receiving =
+ IS_SIR(si) ? si->rx_buff.state != OUTSIDE_FRAME : 0;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/*!
+ * Kernel interface routine to get current statistics of the device
+ * which includes the number bytes/packets transmitted/received,
+ * receive errors, CRC errors, framing errors etc.
+ *
+ * @param dev the net_device structure
+ *
+ * @return This function returns IrDA network statistics
+ */
+static struct net_device_stats *mxc_irda_stats(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ return &si->stats;
+}
+
+/*!
+ * FIRI init function
+ *
+ * @param si FIRI device specific structure.
+ */
+void mxc_irda_firi_init(struct mxc_irda *si)
+{
+ unsigned int firi_baud, osf = 6;
+ unsigned int tcr, rcr, cr;
+
+ si->firi_clk = clk_get(si->dev, "firi_clk");
+ firi_baud = clk_round_rate(si->firi_clk, 48004500);
+ if ((firi_baud < 47995500) ||
+ (clk_set_rate(si->firi_clk, firi_baud) < 0)) {
+ dev_err(si->dev, "Unable to set FIR clock to 48MHz.\n");
+ return;
+ }
+ clk_enable(si->firi_clk);
+
+ writel(0xFFFF, si->firi_base + FIRITSR);
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+ writel(0x00, si->firi_base + FIRITCR);
+ writel(0x00, si->firi_base + FIRIRCR);
+
+ /* set _BL & _OSF */
+ cr = (osf - 1) | (16 << 5);
+ writel(cr, si->firi_base + FIRICR);
+
+#ifdef FIRI_SDMA_TX
+ tcr =
+ FIRITCR_TDT_FIR | FIRITCR_TM_FIR | FIRITCR_TCIE |
+ FIRITCR_PCF | FIRITCR_PC;
+#else
+ tcr = FIRITCR_TM_FIR | FIRITCR_TCIE | FIRITCR_PCF | FIRITCR_PC;
+#endif
+
+#ifdef FIRI_SDMA_RX
+ rcr =
+ FIRIRCR_RPEDE | FIRIRCR_RM_FIR | FIRIRCR_RDT_FIR |
+ FIRIRCR_RPA | FIRIRCR_RPP;
+#else
+ rcr =
+ FIRIRCR_RPEDE | FIRIRCR_RM_FIR | FIRIRCR_RDT_FIR | FIRIRCR_RPEIE |
+ FIRIRCR_RPA | FIRIRCR_PAIE | FIRIRCR_RFOIE | FIRIRCR_RPP;
+#endif
+
+ writel(tcr, si->firi_base + FIRITCR);
+ writel(rcr, si->firi_base + FIRIRCR);
+ cr = 0;
+ writel(cr, si->firi_base + FIRITCTR);
+}
+
+/*!
+ * This function initialises the UART.
+ *
+ * @param si FIRI port specific structure.
+ *
+ * @return The function returns 0 on success.
+ */
+static int mxc_irda_uart_init(struct mxc_irda *si)
+{
+ unsigned int per_clk;
+ unsigned int num, denom, baud, ufcr = 0;
+ unsigned int cr;
+ int d = 1;
+ int uart_ir_mux = 0;
+
+ if (si->mxc_ir_plat)
+ uart_ir_mux = si->mxc_ir_plat->uart_ir_mux;
+ /*
+ * Clear Status Registers 1 and 2
+ **/
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+
+ /* Configure the IOMUX for the UART */
+ gpio_firi_init();
+
+ per_clk = clk_get_rate(si->uart_clk);
+ baud = per_clk / 16;
+ if (baud > 1500000) {
+ baud = 1500000;
+ d = per_clk / ((baud * 16) + 1000);
+ if (d > 6) {
+ d = 6;
+ }
+ }
+ clk_enable(si->uart_clk);
+
+ si->uart_clk_rate = per_clk / d;
+ writel(si->uart_clk_rate / 1000, si->uart_base + MXC_UARTONEMS);
+
+ writel(si->mxc_ir_plat->ir_rx_invert | MXC_UARTUCR4_IRSC,
+ si->uart_base + MXC_UARTUCR4);
+
+ if (uart_ir_mux) {
+ writel(MXC_UARTUCR3_RXDMUXSEL | si->mxc_ir_plat->ir_tx_invert |
+ MXC_UARTUCR3_DSR, si->uart_base + MXC_UARTUCR3);
+ } else {
+ writel(si->mxc_ir_plat->ir_tx_invert | MXC_UARTUCR3_DSR,
+ si->uart_base + MXC_UARTUCR3);
+ }
+
+ writel(MXC_UARTUCR2_IRTS | MXC_UARTUCR2_CTS | MXC_UARTUCR2_WS |
+ MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN,
+ si->uart_base + MXC_UARTUCR2);
+ /* Wait till we are out of software reset */
+ do {
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ } while (!(cr & MXC_UARTUCR2_SRST));
+
+ ufcr |= (UART4_UFCR_TXTL << MXC_UARTUFCR_TXTL_OFFSET) |
+ ((6 - d) << MXC_UARTUFCR_RFDIV_OFFSET) | UART4_UFCR_RXTL;
+ writel(ufcr, si->uart_base + MXC_UARTUFCR);
+
+ writel(MXC_UARTUCR1_UARTEN | MXC_UARTUCR1_IREN,
+ si->uart_base + MXC_UARTUCR1);
+
+ baud = 9600;
+ num = baud / 100 - 1;
+ denom = si->uart_clk_rate / 1600 - 1;
+
+ if ((denom < 65536) && (si->uart_clk_rate > 1600)) {
+ writel(num, si->uart_base + MXC_UARTUBIR);
+ writel(denom, si->uart_base + MXC_UARTUBMR);
+ }
+
+ writel(0x0000, si->uart_base + MXC_UARTUTS);
+ return 0;
+
+}
+
+/*!
+ * This function enables FIRI port.
+ *
+ * @param si FIRI port specific structure.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_startup(struct mxc_irda *si)
+{
+ int ret = 0;
+
+ mxc_irda_uart_init(si);
+ mxc_irda_firi_init(si);
+
+ /* configure FIRI device for speed */
+ ret = mxc_irda_set_speed(si, si->speed = 9600);
+
+ return ret;
+}
+
+/*!
+ * When an ifconfig is issued which changes the device flag to include
+ * IFF_UP this function is called. It is only called when the change
+ * occurs, not when the interface remains up. The function grabs the interrupt
+ * resources and registers FIRI interrupt service routines, requests for DMA
+ * channels, configures the DMA channel. It then initializes the IOMUX
+ * registers to configure the pins for FIRI signals and finally initializes the
+ * various FIRI registers and enables the port for reception.
+ *
+ * @param dev net device structure that is being opened
+ *
+ * @return The function returns 0 for a successful open and non-zero value
+ * on failure.
+ */
+static int mxc_irda_start(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ int err;
+ int ints_muxed = 0;
+ mxc_dma_device_t dev_id = 0;
+
+ if (si->uart_irq == si->uart_irq1)
+ ints_muxed = 1;
+
+ si->speed = 9600;
+
+ if (si->uart_irq == si->firi_irq) {
+ err =
+ request_irq(si->uart_irq, mxc_irda_irq, 0, dev->name, dev);
+ if (err) {
+ dev_err(si->dev, "%s:Failed to request the IRQ\n",
+ __FUNCTION__);
+ return err;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ } else {
+ err =
+ request_irq(si->firi_irq, mxc_irda_irq, 0, dev->name, dev);
+ if (err) {
+ dev_err(si->dev, "%s:Failed to request FIRI IRQ\n",
+ __FUNCTION__);
+ return err;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->firi_irq);
+ if (ints_muxed) {
+
+ err = request_irq(si->uart_irq, mxc_irda_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART IRQ\n",
+ __FUNCTION__);
+ goto err_irq1;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ } else {
+ err = request_irq(si->uart_irq, mxc_irda_tx_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART IRQ\n",
+ __FUNCTION__);
+ goto err_irq1;
+ }
+ err = request_irq(si->uart_irq1, mxc_irda_rx_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART1 IRQ\n",
+ __FUNCTION__);
+ goto err_irq2;
+ }
+ /*
+ * The interrupts must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ disable_irq(si->uart_irq1);
+ }
+ }
+#ifdef FIRI_SDMA_RX
+ dev_id = MXC_DMA_FIR_RX;
+ si->rxdma_ch = mxc_dma_request(dev_id, "MXC FIRI RX");
+ if (si->rxdma_ch < 0) {
+ dev_err(si->dev, "Cannot allocate FIR DMA channel\n");
+ goto err_rx_dma;
+ }
+ mxc_dma_callback_set(si->rxdma_ch, mxc_irda_fir_dma_rx_irq,
+ (void *)dev_get_drvdata(si->dev));
+#endif
+#ifdef FIRI_SDMA_TX
+
+ dev_id = MXC_DMA_FIR_TX;
+ si->txdma_ch = mxc_dma_request(dev_id, "MXC FIRI TX");
+ if (si->txdma_ch < 0) {
+ dev_err(si->dev, "Cannot allocate FIR DMA channel\n");
+ goto err_tx_dma;
+ }
+ mxc_dma_callback_set(si->txdma_ch, mxc_irda_fir_dma_tx_irq,
+ (void *)dev_get_drvdata(si->dev));
+#endif
+ /* Setup the serial port port for the initial speed. */
+ err = mxc_irda_startup(si);
+ if (err) {
+ goto err_startup;
+ }
+
+ /* Open a new IrLAP layer instance. */
+ si->irlap = irlap_open(dev, &si->qos, "mxc");
+ err = -ENOMEM;
+ if (!si->irlap) {
+ goto err_irlap;
+ }
+
+ /* Now enable the interrupt and start the queue */
+ si->open = 1;
+ si->suspend = 0;
+
+ if (si->uart_irq == si->firi_irq) {
+ enable_irq(si->uart_irq);
+ } else {
+ enable_irq(si->firi_irq);
+ if (ints_muxed == 1) {
+ enable_irq(si->uart_irq);
+ } else {
+ enable_irq(si->uart_irq);
+ enable_irq(si->uart_irq1);
+ }
+ }
+
+ netif_start_queue(dev);
+ return 0;
+
+ err_irlap:
+ si->open = 0;
+ mxc_irda_disabledma(si);
+ err_startup:
+#ifdef FIRI_SDMA_TX
+ mxc_dma_free(si->txdma_ch);
+ err_tx_dma:
+#endif
+#ifdef FIRI_SDMA_RX
+ mxc_dma_free(si->rxdma_ch);
+ err_rx_dma:
+#endif
+ if (si->uart_irq1 && !ints_muxed)
+ free_irq(si->uart_irq1, dev);
+ err_irq2:
+ if (si->uart_irq != si->firi_irq)
+ free_irq(si->uart_irq, dev);
+ err_irq1:
+ if (si->firi_irq)
+ free_irq(si->firi_irq, dev);
+ return err;
+}
+
+/*!
+ * This function is called when IFF_UP flag has been cleared by the user via
+ * the ifconfig irda0 down command. This function stops any further
+ * transmissions being queued, and then disables the interrupts.
+ * Finally it resets the device.
+ * @param dev the net_device structure
+ *
+ * @return int the function always returns 0 indicating a success.
+ */
+static int mxc_irda_stop(struct net_device *dev)
+{
+ struct mxc_irda *si = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Stop IrLAP */
+ if (si->irlap) {
+ irlap_close(si->irlap);
+ si->irlap = NULL;
+ }
+
+ netif_stop_queue(dev);
+
+ /*Save flags and disable the FIRI interrupts.. */
+ if (si->open) {
+ local_irq_save(flags);
+ disable_irq(si->uart_irq);
+ free_irq(si->uart_irq, dev);
+ if (si->uart_irq != si->firi_irq) {
+ disable_irq(si->firi_irq);
+ free_irq(si->firi_irq, dev);
+ if (si->uart_irq1 != si->uart_irq) {
+ disable_irq(si->uart_irq1);
+ free_irq(si->uart_irq1, dev);
+ }
+ }
+ local_irq_restore(flags);
+ si->open = 0;
+ }
+#ifdef FIRI_SDMA_RX
+ if (si->rxdma_ch) {
+ mxc_dma_disable(si->rxdma_ch);
+ mxc_dma_free(si->rxdma_ch);
+ if (si->dma_rx_buff_phy) {
+ dma_unmap_single(si->dev, si->dma_rx_buff_phy,
+ IRDA_FRAME_SIZE_LIMIT,
+ DMA_FROM_DEVICE);
+ si->dma_rx_buff_phy = 0;
+ }
+ si->rxdma_ch = 0;
+ }
+ tasklet_kill(&dma_rx_tasklet);
+#endif
+#ifdef FIRI_SDMA_TX
+ if (si->txdma_ch) {
+ mxc_dma_disable(si->txdma_ch);
+ mxc_dma_free(si->txdma_ch);
+ if (si->dma_tx_buff_phy) {
+ dma_unmap_single(si->dev, si->dma_tx_buff_phy,
+ si->dma_tx_buff_len, DMA_TO_DEVICE);
+ si->dma_tx_buff_phy = 0;
+ }
+ si->txdma_ch = 0;
+ }
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the FIRI in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(ndev);
+ unsigned int cr;
+ unsigned long flags;
+
+ if (!si) {
+ return 0;
+ }
+ if (si->suspend == 1) {
+ dev_err(si->dev,
+ " suspend - Device is already suspended ... \n");
+ return 0;
+ }
+ if (si->open) {
+
+ netif_device_detach(ndev);
+ mxc_irda_disabledma(si);
+
+ /*Save flags and disable the FIRI interrupts.. */
+ local_irq_save(flags);
+ disable_irq(si->uart_irq);
+ if (si->uart_irq != si->firi_irq) {
+ disable_irq(si->firi_irq);
+ if (si->uart_irq != si->uart_irq1) {
+ disable_irq(si->uart_irq1);
+ }
+ }
+ local_irq_restore(flags);
+
+ /* Disable Tx and Rx and then disable the UART clock */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr &= ~MXC_UARTUCR1_UARTEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+ clk_disable(si->uart_clk);
+
+ /*Disable Tx and Rx for FIRI and then disable the FIRI clock.. */
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ clk_disable(si->firi_clk);
+
+ gpio_firi_inactive();
+
+ si->suspend = 1;
+ si->open = 0;
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to bring the FIRI back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(ndev);
+ unsigned long flags;
+
+ if (!si) {
+ return 0;
+ }
+
+ if (si->suspend == 1 && !si->open) {
+
+ /*Initialise the UART first */
+ clk_enable(si->uart_clk);
+
+ /*Now init FIRI */
+ gpio_firi_active(si->firi_base + FIRITCR, FIRITCR_TPP);
+ mxc_irda_startup(si);
+
+ /* Enable the UART and FIRI interrupts.. */
+ local_irq_save(flags);
+ enable_irq(si->uart_irq);
+ if (si->uart_irq != si->firi_irq) {
+ enable_irq(si->firi_irq);
+ if (si->uart_irq != si->uart_irq1) {
+ enable_irq(si->uart_irq1);
+ }
+ }
+ local_irq_restore(flags);
+
+ /* Let the kernel know that we are alive and kicking.. */
+ netif_device_attach(ndev);
+
+ si->suspend = 0;
+ si->open = 1;
+ }
+ return 0;
+}
+#else
+#define mxc_irda_suspend NULL
+#define mxc_irda_resume NULL
+#endif
+
+static int mxc_irda_init_iobuf(iobuff_t * io, int size)
+{
+ io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
+ if (io->head != NULL) {
+ io->truesize = size;
+ io->in_frame = FALSE;
+ io->state = OUTSIDE_FRAME;
+ io->data = io->head;
+ }
+ return io->head ? 0 : -ENOMEM;
+
+}
+
+/*!
+ * This function is called during the driver binding process.
+ * This function requests for memory, initializes net_device structure and
+ * registers with kernel.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function returns 0 on success and a non-zero value on failure
+ */
+static int mxc_irda_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct mxc_irda *si;
+ struct resource *uart_res, *firi_res;
+ int uart_irq, firi_irq, uart_irq1;
+ unsigned int baudrate_mask = 0;
+ int err;
+
+ uart_res = &pdev->resource[0];
+ uart_irq = pdev->resource[1].start;
+
+ firi_res = &pdev->resource[2];
+ firi_irq = pdev->resource[3].start;
+
+ uart_irq1 = pdev->resource[4].start;
+
+ if (!uart_res || uart_irq == NO_IRQ || !firi_res || firi_irq == NO_IRQ) {
+ dev_err(&pdev->dev, "Unable to find resources\n");
+ return -ENXIO;
+ }
+
+ err =
+ request_mem_region(uart_res->start, SZ_16K,
+ "MXC_IRDA") ? 0 : -EBUSY;
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request UART memory region\n");
+ return -ENOMEM;
+ }
+
+ err =
+ request_mem_region(firi_res->start, SZ_16K,
+ "MXC_IRDA") ? 0 : -EBUSY;
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request FIRI memory region\n");
+ goto err_mem_1;
+ }
+
+ dev = alloc_irdadev(sizeof(struct mxc_irda));
+ if (!dev) {
+ goto err_mem_2;
+ }
+
+ si = netdev_priv(dev);
+ si->dev = &pdev->dev;
+
+ si->mxc_ir_plat = pdev->dev.platform_data;
+ si->uart_clk = si->mxc_ir_plat->uart_clk;
+
+ si->uart_res = uart_res;
+ si->firi_res = firi_res;
+ si->uart_irq = uart_irq;
+ si->firi_irq = firi_irq;
+ si->uart_irq1 = uart_irq1;
+
+ si->uart_base = ioremap(uart_res->start, SZ_16K);
+ si->firi_base = ioremap(firi_res->start, SZ_16K);
+
+ if (!(si->uart_base || si->firi_base)) {
+ err = -ENOMEM;
+ goto err_mem_3;
+ }
+
+ /*
+ * Initialise the SIR buffers
+ */
+ err = mxc_irda_init_iobuf(&si->rx_buff, UART_BUFF_SIZE);
+ if (err) {
+ goto err_mem_4;
+ }
+
+ err = mxc_irda_init_iobuf(&si->tx_buff, UART_BUFF_SIZE);
+ if (err) {
+ goto err_mem_5;
+ }
+
+ dev->hard_start_xmit = mxc_irda_hard_xmit;
+ dev->open = mxc_irda_start;
+ dev->stop = mxc_irda_stop;
+ dev->do_ioctl = mxc_irda_ioctl;
+ dev->get_stats = mxc_irda_stats;
+
+ irda_init_max_qos_capabilies(&si->qos);
+
+ /*
+ * We support
+ * SIR(9600, 19200,38400, 57600 and 115200 bps)
+ * FIR(4 Mbps)
+ * Min Turn Time set to 1ms or greater.
+ */
+ baudrate_mask |= IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
+ baudrate_mask |= IR_4000000 << 8;
+
+ si->qos.baud_rate.bits &= baudrate_mask;
+ si->qos.min_turn_time.bits = 0x7;
+
+ irda_qos_bits_to_value(&si->qos);
+
+#ifdef FIRI_SDMA_RX
+ si->tskb = NULL;
+ tasklet_init(&dma_rx_tasklet, mxc_irda_rx_task, (unsigned long)si);
+#endif
+ err = register_netdev(dev);
+ if (err == 0) {
+ platform_set_drvdata(pdev, dev);
+ } else {
+ kfree(si->tx_buff.head);
+ err_mem_5:
+ kfree(si->rx_buff.head);
+ err_mem_4:
+ iounmap(si->uart_base);
+ iounmap(si->firi_base);
+ err_mem_3:
+ free_netdev(dev);
+ err_mem_2:
+ release_mem_region(firi_res->start, SZ_16K);
+ err_mem_1:
+ release_mem_region(uart_res->start, SZ_16K);
+ }
+ return err;
+}
+
+/*!
+ * Dissociates the driver from the FIRI device. Removes the appropriate FIRI
+ * port structure from the kernel.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(dev);
+
+ if (si->uart_base)
+ iounmap(si->uart_base);
+ if (si->firi_base)
+ iounmap(si->firi_base);
+ if (si->firi_res->start)
+ release_mem_region(si->firi_res->start, SZ_16K);
+ if (si->uart_res->start)
+ release_mem_region(si->uart_res->start, SZ_16K);
+ if (si->tx_buff.head)
+ kfree(si->tx_buff.head);
+ if (si->rx_buff.head)
+ kfree(si->rx_buff.head);
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_netdev(dev);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcir_driver = {
+ .driver = {
+ .name = "mxcir",
+ },
+ .probe = mxc_irda_probe,
+ .remove = mxc_irda_remove,
+ .suspend = mxc_irda_suspend,
+ .resume = mxc_irda_resume,
+};
+
+/*!
+ * This function is used to initialize the FIRI driver module. The function
+ * registers the power management callback functions with the kernel and also
+ * registers the FIRI callback functions.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_irda_init(void)
+{
+ return platform_driver_register(&mxcir_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxc_irda_exit(void)
+{
+ platform_driver_unregister(&mxcir_driver);
+}
+
+module_init(mxc_irda_init);
+module_exit(mxc_irda_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("MXC IrDA(SIR/FIR) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/irda/mxc_ir.h b/drivers/net/irda/mxc_ir.h
new file mode 100644
index 000000000000..6b22ca129f27
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __MXC_FIRI_REG_H__
+#define __MXC_FIRI_REG_H__
+
+#include <mach/hardware.h>
+
+/*!
+ * @defgroup FIRI Fast IR Driver
+ */
+
+/*!
+ * @file mxc_ir.h
+ *
+ * @brief MXC FIRI header file
+ *
+ * This file defines base address and bits of FIRI registers
+ *
+ * @ingroup FIRI
+ */
+
+/*!
+ * FIRI maximum packet length
+ */
+#define FIR_MAX_RXLEN 2047
+
+/*
+ * FIRI Transmitter Control Register
+ */
+#define FIRITCR 0x00
+/*
+ * FIRI Transmitter Count Register
+ */
+#define FIRITCTR 0x04
+/*
+ * FIRI Receiver Control Register
+ */
+#define FIRIRCR 0x08
+/*
+ * FIRI Transmitter Status Register
+ */
+#define FIRITSR 0x0C
+/*
+ * FIRI Receiver Status Register
+ */
+#define FIRIRSR 0x10
+/*
+ * FIRI Transmitter FIFO
+ */
+#define FIRITXFIFO 0x14
+/*
+ * FIRI Receiver FIFO
+ */
+#define FIRIRXFIFO 0x18
+/*
+ * FIRI Control Register
+ */
+#define FIRICR 0x1C
+
+/*
+ * Bit definitions of Transmitter Controller Register
+ */
+#define FIRITCR_HAG (1<<24) /* H/W address generator */
+#define FIRITCR_SRF_FIR (0<<13) /* Start field repeat factor */
+#define FIRITCR_SRF_MIR (1<<13) /* Start field Repeat Factor */
+#define FIRITCR_TDT_MIR (2<<10) /* TX trigger for MIR is set to 32 bytes) */
+#define FIRITCR_TDT_FIR (1<<10) /* TX trigger for FIR is set to 16 bytes) */
+#define FIRITCR_TCIE (1<<9) /* TX Complete Interrupt Enable */
+#define FIRITCR_TPEIE (1<<8) /* TX Packet End Interrupt Enable */
+#define FIRITCR_TFUIE (1<<7) /* TX FIFO Under-run Interrupt Enable */
+#define FIRITCR_PCF (1<<6) /* Packet Complete by FIFO */
+#define FIRITCR_PC (1<<5) /* Packet Complete */
+#define FIRITCR_SIP (1<<4) /* TX Enable of SIP */
+#define FIRITCR_TPP (1<<3) /* TX Pulse Polarity bit */
+#define FIRITCR_TM_FIR (0<<1) /* TX Mode 4 Mbps */
+#define FIRITCR_TM_MIR1 (1<<1) /* TX Mode 0.576 Mbps */
+#define FIRITCR_TM_MIR2 (1<<2) /* TX Mode 1.152 Mbps */
+#define FIRITCR_TE (1<<0) /* TX Enable */
+
+/*
+ * Bit definitions of Transmitter Count Register
+ */
+#define FIRITCTR_TPL 511 /* TX Packet Length set to 512 bytes */
+
+/*
+ * Bit definitions of Receiver Control Register
+ */
+#define FIRIRCR_RAM (1<<24) /* RX Address Match */
+#define FIRIRCR_RPEDE (1<<11) /* Packet End DMA request Enable */
+#define FIRIRCR_RDT_MIR (2<<8) /* DMA Trigger level(64 bytes in RXFIFO) */
+#define FIRIRCR_RDT_FIR (1<<8) /* DMA Trigger level(16 bytes in RXFIFO) */
+#define FIRIRCR_RPA (1<<7) /* RX Packet Abort */
+#define FIRIRCR_RPEIE (1<<6) /* RX Packet End Interrupt Enable */
+#define FIRIRCR_PAIE (1<<5) /* Packet Abort Interrupt Enable */
+#define FIRIRCR_RFOIE (1<<4) /* RX FIFO Overrun Interrupt Enable */
+#define FIRIRCR_RPP (1<<3) /* RX Pulse Polarity bit */
+#define FIRIRCR_RM_FIR (0<<1) /* 4 Mbps */
+#define FIRIRCR_RM_MIR1 (1<<1) /* 0.576 Mbps */
+#define FIRIRCR_RM_MIR2 (1<<2) /* 1.152 Mbps */
+#define FIRIRCR_RE (1<<0) /* RX Enable */
+
+/* Transmitter Status Register */
+#define FIRITSR_TFP 0xFF00 /* Mask for available bytes in TX FIFO */
+#define FIRITSR_TC (1<<3) /* Transmit Complete bit */
+#define FIRITSR_SIPE (1<<2) /* SIP End bit */
+#define FIRITSR_TPE (1<<1) /* Transmit Packet End */
+#define FIRITSR_TFU (1<<0) /* TX FIFO Under-run */
+
+/* Receiver Status Register */
+#define FIRIRSR_RFP 0xFF00 /* mask for available bytes RX FIFO */
+#define FIRIRSR_PAS (1<<5) /* preamble search */
+#define FIRIRSR_RPE (1<<4) /* RX Packet End */
+#define FIRIRSR_RFO (1<<3) /* RX FIFO Overrun */
+#define FIRIRSR_BAM (1<<2) /* Broadcast Address Match */
+#define FIRIRSR_CRCE (1<<1) /* CRC error */
+#define FIRIRSR_DDE (1<<0) /* Address, control or data field error */
+
+/* FIRI Control Register */
+#define FIRICR_BL (32<<5) /* Burst Length is set to 32 */
+#define FIRICR_OSF (0<<1) /* Over Sampling Factor */
+
+#endif /* __MXC_FIRI_REG_H__ */
diff --git a/drivers/net/ns9xxx-eth.c b/drivers/net/ns9xxx-eth.c
new file mode 100755
index 000000000000..4fc84c7a6d41
--- /dev/null
+++ b/drivers/net/ns9xxx-eth.c
@@ -0,0 +1,1499 @@
+/*
+ * drivers/net/ns9xxx-eth.c
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/* open issues
+ * - use phy irq
+ * - VLAN
+ * - clk_enable only at open time?
+ * - PM for hibernate
+ */
+#include <linux/clk.h>
+#include <linux/crc32.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ns9xxx-eth.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/timer.h>
+#include <asm/gpio.h>
+
+#define DRIVER_NAME "ns9xxx-eth"
+
+#define ETH_EGCR1 0x0000
+#define ETH_EGCR1_ERX (1 << 31)
+#define ETH_EGCR1_ERXDMA (1 << 30)
+#define ETH_EGCR1_ERXSHT (1 << 28)
+#define ETH_EGCR1_ETX (1 << 23)
+#define ETH_EGCR1_ETXDMA (1 << 22)
+#define ETH_EGCR1_ERXINIT (1 << 19)
+#define ETH_EGCR1_PM (3 << 14)
+#define ETH_EGCR1_PM_MII 0
+#define ETH_EGCR1_MACHRST (1 << 9)
+#define ETH_EGCR1_ITXA (1 << 8)
+#define ETH_EGCR1_MB1 (1 << 21)
+
+#define ETH_EGCR2 0x0004
+/* TCLER is different comparing the ns9360 vs ns921x */
+/* FIXME TCLER should be used differently per processor */
+#define ETH_EGCR2_TCLER_NS921X (1 << 7)
+#define ETH_EGCR2_TCLER_NS9360 (1 << 3)
+#define ETH_EGCR2_TCLER (1 << 7)
+#define ETH_EGCR2_TKICK (1 << 3)
+#define ETH_EGCR2_AUTOZ (1 << 2)
+#define ETH_EGCR2_CLRCNT (1 << 1)
+#define ETH_EGCR2_STEN (1 << 0)
+
+#define ETH_EGSR 0x0008
+#define ETH_EGSR_RXINIT (1 << 20)
+
+#define ETH_TS 0x0018
+#define ETH_TS_OK ( 1 << 15)
+#define ETH_TS_BR ( 1 << 14)
+#define ETH_TS_MC ( 1 << 13)
+#define ETH_TS_AL ( 1 << 12)
+#define ETH_TS_AED ( 1 << 11)
+#define ETH_TS_AEC ( 1 << 10)
+#define ETH_TS_AUR ( 1 << 9)
+#define ETH_TS_AJ ( 1 << 8)
+#define ETH_TS_DEF ( 1 << 6)
+#define ETH_TS_CRC ( 1 << 5)
+#define ETH_TS_COLC (15 << 0)
+
+#define ETH_MAC1 0x0400
+#define ETH_MAC1_SRST (1 << 15)
+#define ETH_MAC1_RXEN (1 << 0)
+
+#define ETH_MAC2 0x0404
+#define ETH_MAC2_PADEN (1 << 5)
+#define ETH_MAC2_CRCEN (1 << 4)
+#define ETH_MAC2_FULLD (1 << 0)
+
+#define ETH_B2BIPG 0x0408
+
+#define ETH_EPSR 0x0418
+#define ETH_EPSR_SPEED_MASK (1 << 8)
+#define ETH_EPSR_SPEED_100 (1 << 8)
+#define ETH_EPSR_SPEED_10 (0 << 8)
+
+
+#define ETH_MIIMCFG 0x0420
+#define ETH_MIIMCFG_RMIIM (1 << 15)
+#define ETH_MIIMCFG_CLKS (7 << 2)
+#define ETH_MIIMCFG_CLKS_DIV40 (7 << 2)
+
+#define ETH_MIIMCMD 0x0424
+#define ETH_MIIMCMD_READ (1 << 0)
+#define ETH_MIIMADDR 0x0428
+#define ETH_MIIMWD 0x042c
+#define ETH_MIIMRD 0x0430
+#define ETH_MIIMIR 0x0434
+#define ETH_MIIMIR_LF (1 << 3)
+#define ETH_MIIMIR_BUSY (1 << 0)
+
+#define ETH_SA1 0x0440
+#define ETH_SA2 0x0444
+#define ETH_SA3 0x0448
+
+#define ETH_SAF 0x0500
+#define ETH_SAF_PRO (1 << 3)
+#define ETH_SAF_PRM (1 << 2)
+#define ETH_SAF_PRA (1 << 1)
+#define ETH_SAF_BROAD (1 << 0)
+
+#define ETH_HT1 0x0504
+#define ETH_HT2 0x0508
+
+#define ETH_STAT_TR64 0x0680
+#define ETH_STAT_TR127 0x0684
+#define ETH_STAT_TR255 0x0688
+#define ETH_STAT_TR511 0x068C
+#define ETH_STAT_TR1K 0x0690
+#define ETH_STAT_TRMAX 0x0694
+#define ETH_STAT_TRMGV 0x0698
+#define ETH_STAT_RBYT 0x069C
+#define ETH_STAT_RPKT 0x06A0
+#define ETH_STAT_RFCS 0x06A4
+#define ETH_STAT_RMCA 0x06A8
+#define ETH_STAT_RBCA 0x06AC
+#define ETH_STAT_RXCF 0x06B0
+#define ETH_STAT_RXPF 0x06B4
+#define ETH_STAT_RXUO 0x06B8
+#define ETH_STAT_RALN 0x06BC
+#define ETH_STAT_RFLR 0x06C0
+#define ETH_STAT_RCDE 0x06C4
+#define ETH_STAT_RCSE 0x06C8
+#define ETH_STAT_RUND 0x06CC
+#define ETH_STAT_ROVR 0x06D0
+#define ETH_STAT_RFRG 0x06D4
+#define ETH_STAT_RJBR 0x06D8
+#define ETH_STAT_TBYT 0x06E0
+#define ETH_STAT_TPKT 0x06E4
+#define ETH_STAT_TMCA 0x06E8
+#define ETH_STAT_TBCA 0x06EC
+#define ETH_STAT_TDFR 0x06F4
+#define ETH_STAT_TEDF 0x06F8
+#define ETH_STAT_TSCL 0x06FC
+#define ETH_STAT_TMCL 0x0700
+#define ETH_STAT_TLCL 0x0704
+#define ETH_STAT_TXCL 0x0708
+#define ETH_STAT_TNCL 0x070C
+#define ETH_STAT_TJBR 0x0718
+#define ETH_STAT_TFCS 0x071C
+#define ETH_STAT_TOVR 0x0724
+#define ETH_STAT_TUND 0x0728
+#define ETH_STAT_TFRG 0x072C
+
+#define ETH_RXABDP 0x0a00
+#define ETH_RXBBDP 0x0a04
+#define ETH_RXCBDP 0x0a08
+#define ETH_RXDBDP 0x0a0c
+
+#define ETH_IS 0x0a10
+#define ETH_IS_RXOVFLDATA (1 << 25)
+#define ETH_IS_RXOVFLSTAT (1 << 24)
+#define ETH_IS_RXDONEA (1 << 22)
+#define ETH_IS_RXDONEB (1 << 21)
+#define ETH_IS_RXDONEC (1 << 20)
+#define ETH_IS_RXDONED (1 << 19)
+#define ETH_IS_RXNOBUF (1 << 18)
+#define ETH_IS_RXBUFFUL (1 << 17)
+#define ETH_IS_RXBR (1 << 16)
+#define ETH_IS_TXDONE (1 << 2)
+#define ETH_IS_TXERR (1 << 1)
+#define ETH_IS_TXIDLE (1 << 0)
+
+#define ETH_IE 0x0a14
+#define ETH_IE_RXOVFLDATA (1 << 25)
+#define ETH_IE_RXOVFLSTAT (1 << 24)
+#define ETH_IE_RXDONEA (1 << 22)
+#define ETH_IE_RXDONEB (1 << 21)
+#define ETH_IE_RXDONEC (1 << 20)
+#define ETH_IE_RXDONED (1 << 19)
+#define ETH_IE_RXNOBUF (1 << 18)
+#define ETH_IE_RXBUFFUL (1 << 17)
+#define ETH_IE_RXBR (1 << 16)
+#define ETH_IE_TXDONE (1 << 2)
+#define ETH_IE_TXERR (1 << 1)
+#define ETH_IE_TXIDLE (1 << 0)
+
+#define ETH_RX_IRQS (ETH_IE_RXOVFLDATA | \
+ ETH_IE_RXDONEA | \
+ ETH_IE_RXDONEB | \
+ ETH_IE_RXDONEC | \
+ ETH_IE_RXDONED | \
+ ETH_IE_RXNOBUF | \
+ ETH_IE_RXBUFFUL)
+
+#define ETH_TX_IRQS (ETH_IE_TXDONE | ETH_IE_TXERR)
+
+#define ETH_TXBDP 0x0a18
+
+#define ETH_TRBDP 0x0a1c
+
+#define ETH_RXFREE 0x0a3c
+#define ETH_RXFREE_A (1 << 0)
+
+#define ETH_TXBDR 0x1000
+
+/* hardware limits sets from ethernet controller */
+#define HW_RX_RINGS (4)
+#define MAX_ETH_FRAME_LEN (1522)
+
+/* software limits sets by driver */
+#define TOTAL_NR_TXDESC (64)
+#define NR_RXDESC_PER_RING (45)
+#define TOTAL_NR_RXDESC (HW_RX_RINGS*NR_RXDESC_PER_RING) /* total number of descriptors */
+
+/* masks for DMA descriptors handling */
+#define DMADESC_WRAP (1 << 15)
+#define DMADESC_INTR (1 << 14)
+#define DMATXDESC_LAST (1 << 13)
+#define DMARXDESC_EN (1 << 13)
+#define DMADESC_FULL (1 << 12)
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ #define ACTIVITYLED_TOGGLE_TIMEOUT 2 /* in jiffies */
+ #define ACTIVITYLED_OFF_TIMEOUT 20 /* in jiffies */
+ unsigned int rxtx_activity = 0;
+ struct timer_list activityled_timer;
+#endif
+
+union ns9xxx_dma_desc {
+ struct {
+ u32 source;
+ u16 len;
+ u16 reserved;
+ u32 dest;
+ u16 status;
+ u16 flags;
+ };
+ u32 data[4];
+};
+
+struct ns9xxx_eth_priv {
+ unsigned char __iomem *membase;
+ resource_size_t mapbase;
+ spinlock_t lock;
+ struct net_device_stats stats;
+ struct clk *clk;
+ unsigned int irqtx;
+ unsigned int irqrx;
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ unsigned int activityled;
+#endif
+
+ /* phy management */
+ int lastlink;
+ int lastduplex;
+ int lastspeed;
+ struct mii_bus *mdiobus;
+ struct phy_device *phy;
+
+ /* rx stuff */
+ struct sk_buff **rxskb;
+ union ns9xxx_dma_desc *rxdesc;
+ dma_addr_t rxdeschandle;
+
+ /* tx stuff */
+ struct sk_buff **txskb;
+ u16 txbusy;
+ u16 txfree;
+
+ /* work to recover from rx stall condition that can happen at 100 Mbps HD */
+ struct delayed_work recover_from_rx_stall;
+};
+
+static inline u32 ethread32(struct net_device *dev, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 ret = ioread32(priv->membase + offset);
+
+ dev_vdbg(&dev->dev, "read 0x%p -> 0x%08x\n",
+ priv->membase + offset, ret);
+
+ return ret;
+}
+
+static inline void ethwrite32(struct net_device *dev,
+ u32 value, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_vdbg(&dev->dev, "write 0x%p <- 0x%08x\n",
+ priv->membase + offset, value);
+ iowrite32(value, priv->membase + offset);
+}
+
+static inline void ethupdate32(struct net_device *dev,
+ u32 and, u32 or, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 reg;
+
+ reg = ioread32(priv->membase + offset) & and;
+ iowrite32(reg | or, priv->membase + offset);
+ dev_vdbg(&dev->dev, "update 0x%p <- 0x%08x\n",
+ priv->membase + offset, reg | or);
+}
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+static void toggle_activityled(void)
+{
+ /* set activity flag */
+ rxtx_activity = 1;
+ /* run timer if not already running */
+ if(!timer_pending(&activityled_timer))
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+}
+
+static void activityled_timer_fn(unsigned long gpio)
+{
+ static int cnt = 0;
+
+ if(rxtx_activity) {
+ /* toggle RX/TX Ethernet activity LED */
+ gpio_set_value(gpio, !gpio_get_value(gpio));
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+ cnt = 0;
+ rxtx_activity = 0;
+ } else {
+ if(cnt++ < ACTIVITYLED_OFF_TIMEOUT / ACTIVITYLED_TOGGLE_TIMEOUT)
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+ else {
+ gpio_set_value(gpio, 1); /* switch LED off */
+ cnt = 0;
+ }
+ }
+
+}
+#endif
+
+static int ns9xxx_eth_miibus_pollbusy(struct mii_bus *bus)
+{
+ unsigned int timeout = 0x3000;
+ struct net_device *dev = bus->priv;
+
+ while (timeout--) {
+ u32 miimir;
+
+ miimir = ethread32(dev, ETH_MIIMIR);
+
+ if (!(miimir & ETH_MIIMIR_BUSY))
+ break;
+
+ cpu_relax();
+ }
+
+ return timeout ? 0 : -EBUSY;
+}
+
+static int ns9xxx_eth_mdiobus_read(struct mii_bus *bus, int phyid, int regnum)
+{
+ struct net_device *dev = bus->priv;
+ int ret;
+
+ if ((phyid & ~0x1f) || (regnum & ~0x1f))
+ return -EINVAL;
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ethwrite32(dev, phyid << 8 | regnum, ETH_MIIMADDR);
+ ethwrite32(dev, 0, ETH_MIIMCMD);
+ ethwrite32(dev, ETH_MIIMCMD_READ, ETH_MIIMCMD);
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ret = ethread32(dev, ETH_MIIMRD) & 0xffff;
+
+out:
+ dev_vdbg(bus->parent, "%s, phyid = %d, regnum = %d -> %04x\n",
+ __func__, phyid, regnum, ret);
+
+ return ret;
+}
+
+static int ns9xxx_eth_mdiobus_write(struct mii_bus *bus,
+ int phyid, int regnum, u16 val)
+{
+ struct net_device *dev = bus->priv;
+ int ret;
+
+ if ((phyid & ~0x1f) || (regnum & ~0x1f))
+ return -EINVAL;
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ethwrite32(dev, phyid << 8 | regnum, ETH_MIIMADDR);
+ ethwrite32(dev, val, ETH_MIIMWD);
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+
+out:
+ dev_vdbg(bus->parent, "%s: phyid = %d, regnum = %d, val = %04hx -> %04x\n",
+ __func__, phyid, regnum, val, ret);
+
+ return ret;
+}
+
+static int ns9xxx_eth_mdiobus_reset(struct mii_bus *bus)
+{
+ struct net_device *dev = bus->priv;
+
+ dev_dbg(bus->parent, "%s\n", __func__);
+
+ ethwrite32(dev, ETH_MIIMCFG_RMIIM, ETH_MIIMCFG);
+
+ /* TODO: currently the biggest divider (40) is used. This could be
+ * tuned depending on the PHY. phylib doesn't provide the needed
+ * information, though :-( */
+ ethwrite32(dev, ETH_MIIMCFG_CLKS_DIV40, ETH_MIIMCFG);
+
+ return 0;
+}
+
+static inline int ns9xxx_eth_create_skbuff(struct net_device* dev, const int descr)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+ int ret;
+
+ skb = dev_alloc_skb(MAX_ETH_FRAME_LEN);
+ if (likely(skb)) {
+ priv->rxskb[descr] = skb;
+ skb->dev = dev;
+
+ priv->rxdesc[descr].source = dma_map_single(&dev->dev, skb->data,
+ skb->len, DMA_FROM_DEVICE);
+
+ priv->rxdesc[descr].len = MAX_ETH_FRAME_LEN;
+ priv->rxdesc[descr].flags = DMADESC_INTR | DMARXDESC_EN |
+ (descr == (NR_RXDESC_PER_RING - 1) ? DMADESC_WRAP : 0);
+ ret = 0;
+ } else {
+ printk(KERN_ERR "%s: out of memory\n", __func__);
+ priv->rxdesc[descr].flags = 0;
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+
+static inline void ns9xxx_eth_rx_process_ring(struct net_device* dev, unsigned int ring)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ union ns9xxx_dma_desc *desc;
+ unsigned int i, endring;
+
+ endring = (ring + 1) * NR_RXDESC_PER_RING;
+ desc = &priv->rxdesc[ring * NR_RXDESC_PER_RING];
+
+ for (i = ring * NR_RXDESC_PER_RING; i < endring; i++, desc++) {
+ if (desc->flags & DMADESC_FULL) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(MAX_ETH_FRAME_LEN);
+ if (likely(skb)) {
+ skb_reserve(skb, 2); /* 16 byte IP header align */
+ memcpy(skb_put(skb, desc->len), (unsigned char *)priv->rxskb[i]->data,
+ desc->len);
+ skb->protocol = eth_type_trans(skb, dev);
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += desc->len;
+ dev->last_rx = jiffies;
+ netif_rx(skb);
+ } else {
+ printk(KERN_ERR "%s: out of memory, dropping packet\n", __func__);
+ dev->stats.rx_dropped++;
+ }
+ desc->len = MAX_ETH_FRAME_LEN;
+ desc->flags = DMADESC_INTR | DMARXDESC_EN | (i == (NR_RXDESC_PER_RING - 1) ? DMADESC_WRAP : 0);
+ }
+ }
+}
+
+static irqreturn_t ns9xxx_eth_rx_int(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ u32 is, rxdonemask, ring;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ is = ethread32(dev, ETH_IS);
+ /* Acknowledge interrupts */
+ ethwrite32(dev, is & ETH_RX_IRQS, ETH_IS);
+ dev_vdbg(&dev->dev, "%s: ETH_IS=%08x\n", __func__, is);
+
+ if (is & ETH_IS_RXOVFLDATA) {
+ if (!(is & (ETH_IS_RXDONEA | ETH_IS_RXDONEB |
+ ETH_IS_RXDONEC | ETH_IS_RXDONEA))) {
+ /* The ETH_IS_RXOVFLDATA bit is set, then the receiver front
+ * end has apparently locked up. We schedule a work that resets
+ * the interface. We check the DONE bits to try to empty the
+ * receive rings of packets before the receiver reset. Note
+ * that once we get into this lockup state, the ETH_IS_RXOVFLDATA
+ * interrupt will happen continously until we reset the receiver.
+ */
+ ethupdate32(dev, ~(ETH_EGCR1_ERX | ETH_EGCR1_ERXDMA), 0, ETH_EGCR1);
+ ethupdate32(dev, ~ETH_MAC1_RXEN, 0, ETH_MAC1);
+ ethupdate32(dev, ~ETH_IE_RXOVFLDATA, 0, ETH_IE);
+
+ schedule_delayed_work(&priv->recover_from_rx_stall, 0);
+ }
+ }
+
+ if (is & (ETH_IS_RXNOBUF | ETH_IS_RXBUFFUL)) {
+ priv->stats.rx_dropped++;
+ }
+
+ for (ring = 0, rxdonemask = ETH_IS_RXDONEA; ring < HW_RX_RINGS; ring++) {
+ if (is & rxdonemask) {
+ ns9xxx_eth_rx_process_ring(dev, ring);
+ ethwrite32(dev, 1 << ring, ETH_RXFREE);
+ }
+ rxdonemask >>= 1;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ toggle_activityled();
+#endif
+ return IRQ_HANDLED;
+}
+
+static void ns9xxx_eth_recover_from_rx_stall(struct work_struct *work)
+{
+ struct ns9xxx_eth_priv *priv =
+ container_of(work, struct ns9xxx_eth_priv,
+ recover_from_rx_stall.work);
+
+ struct net_device *dev =
+ container_of((void *)priv, struct net_device, priv);
+ unsigned long flags;
+ int i, timeout = 20;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ for (i = 0; i < HW_RX_RINGS; i++) {
+ ethwrite32(dev, priv->rxdeschandle + (i * NR_RXDESC_PER_RING),
+ ETH_RXABDP + (i * 4));
+ }
+
+ ethupdate32(dev, 0xffffffff, ETH_EGSR_RXINIT, ETH_EGSR);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR1_ERX, ETH_EGCR1);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR1_ERXINIT, ETH_EGCR1);
+
+ while (!(ethread32(dev, ETH_EGSR) & ETH_EGSR_RXINIT) && timeout--)
+ udelay(1);
+
+ ethupdate32(dev, ~ETH_EGCR1_ERXINIT, 0, ETH_EGCR1);
+
+ /* Re-enable the overflow interrupt */
+ ethupdate32(dev, 0xffffffff, ETH_IE_RXOVFLDATA, ETH_IE);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static inline int ns9xxx_eth_num_txbusy(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ return (TOTAL_NR_TXDESC + priv->txfree - priv->txbusy) %
+ TOTAL_NR_TXDESC;
+}
+
+static inline int ns9xxx_eth_num_txfree(struct net_device *dev)
+{
+ return TOTAL_NR_TXDESC - ns9xxx_eth_num_txbusy(dev);
+}
+
+static void ns9xxx_eth_read_txdesc(struct net_device *dev,
+ union ns9xxx_dma_desc *txbuffer)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < 4; ++i)
+ txbuffer->data[i] = ethread32(dev,
+ ETH_TXBDR + 16 * priv->txbusy + 4 * i);
+}
+
+static void ns9xxx_eth_start_tx_dma(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 egcr2, ie;
+ u32 start;
+
+ dev_vdbg(&dev->dev, "%s\n", __func__);
+
+ dev->trans_start = jiffies;
+
+ /* XXX: really kick TCLER? */
+
+ egcr2 = ethread32(dev, ETH_EGCR2);
+ if (egcr2 & (ETH_EGCR2_TCLER | ETH_EGCR2_TKICK)) {
+ egcr2 &= ~(ETH_EGCR2_TCLER | ETH_EGCR2_TKICK);
+ ethwrite32(dev, egcr2, ETH_EGCR2);
+ }
+
+ start = 4 * priv->txbusy;
+
+ ie = ethread32(dev, ETH_IE);
+ if ((ie & ETH_TX_IRQS) == 0) {
+ u32 egcr1 = ethread32(dev, ETH_EGCR1);
+ ethwrite32(dev, start, ETH_TXBDP);
+
+ ethwrite32(dev, ie | ETH_TX_IRQS, ETH_IE);
+ ethwrite32(dev, egcr1 | ETH_EGCR1_ETXDMA, ETH_EGCR1);
+ } else
+ ethwrite32(dev, start, ETH_TRBDP);
+
+ egcr2 |= ETH_EGCR2_TCLER | ETH_EGCR2_TKICK;
+ ethwrite32(dev, egcr2, ETH_EGCR2);
+}
+
+static irqreturn_t ns9xxx_eth_tx_int(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 is;
+ struct sk_buff *skb;
+ union ns9xxx_dma_desc txbuffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ is = ethread32(dev, ETH_IS);
+ dev_vdbg(&dev->dev, "%s: ETH_IS=%08x\n", __func__, is);
+
+ /* ack */
+ ethwrite32(dev, is & (ETH_IS_TXDONE | ETH_IS_TXERR), ETH_IS);
+
+ while (1) {
+ if (!ns9xxx_eth_num_txbusy(dev))
+ break;
+
+ skb = priv->txskb[priv->txbusy];
+
+ ns9xxx_eth_read_txdesc(dev, &txbuffer);
+
+ if (txbuffer.flags & DMADESC_FULL)
+ break;
+
+ dma_unmap_single(&dev->dev, txbuffer.source,
+ skb->len, DMA_TO_DEVICE);
+
+ priv->txbusy = (priv->txbusy + 1) % TOTAL_NR_TXDESC;
+
+ if (txbuffer.status & ETH_TS_OK) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ } else {
+ priv->stats.tx_errors++;
+
+ /* XXX: fill in tx_aborted_errors etc. */
+ /* kick TCLER? */
+ }
+
+ dev_kfree_skb_irq(skb);
+ }
+
+ if (ns9xxx_eth_num_txbusy(dev) &&
+ (is & ETH_IS_TXIDLE))
+ ns9xxx_eth_start_tx_dma(dev);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ toggle_activityled();
+#endif
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ns9xxx_eth_netpoll(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ns9xxx_eth_rx_int(priv->irqrx, dev);
+ ns9xxx_eth_tx_int(priv->irqtx, dev);
+ local_irq_restore(flags);
+}
+#endif
+
+static void ns9xxx_eth_adjust_link(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if ((phydev->link != priv->lastlink) ||
+ (priv->lastspeed != phydev->speed) ||
+ (priv->lastduplex != phydev->duplex)){
+
+ if (phydev->link) {
+ u32 mac2 = ethread32(dev, ETH_MAC2);
+ u32 epsr = ethread32(dev, ETH_EPSR);
+
+ /* Adjsut speed */
+ epsr &= ~ETH_EPSR_SPEED_MASK;
+ epsr |= (phydev->speed == SPEED_100) ?
+ ETH_EPSR_SPEED_100 : 0;
+ ethwrite32(dev, epsr, ETH_EPSR);
+ priv->lastspeed = phydev->speed;
+
+ /* Adjsut duplex */
+ mac2 &= ~ETH_MAC2_FULLD;
+ mac2 |= phydev->duplex ? ETH_MAC2_FULLD : 0;
+ ethwrite32(dev, mac2, ETH_MAC2);
+ ethwrite32(dev, phydev->duplex ? 0x15 : 0x12,
+ ETH_B2BIPG);
+ priv->lastduplex = phydev->duplex;
+
+ dev_info(&dev->dev, "link up (%d/%s)\n", phydev->speed,
+ (phydev->duplex == DUPLEX_FULL) ?
+ "full" : "half");
+ } else {
+ /* link down */
+ priv->lastspeed = 0;
+ priv->lastduplex = -1;
+ dev_info(&dev->dev, "link down\n");
+ }
+ priv->lastlink = phydev->link;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ns9xxx_eth_hwinit(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 egcr1, ie;
+ int timeout, i;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ /* disable everything */
+ ethwrite32(dev, ETH_EGCR1_PM_MII, ETH_EGCR1);
+ ethwrite32(dev, 0, ETH_IE);
+
+ /* ack any pending irq */
+ ethwrite32(dev, ethread32(dev, ETH_IS), ETH_IS);
+
+ /* program station address */
+ ethwrite32(dev, dev->dev_addr[0] | dev->dev_addr[1] << 8, ETH_SA3);
+ ethwrite32(dev, dev->dev_addr[2] | dev->dev_addr[3] << 8, ETH_SA2);
+ ethwrite32(dev, dev->dev_addr[4] | dev->dev_addr[5] << 8, ETH_SA1);
+
+ for (i = 0; i < HW_RX_RINGS; i++) {
+ ethwrite32(dev, priv->rxdeschandle + (i * NR_RXDESC_PER_RING),
+ ETH_RXABDP + (i * 4));
+ }
+
+ ethwrite32(dev, ETH_SAF_BROAD, ETH_SAF);
+
+ egcr1 = ETH_EGCR1_ERX | ETH_EGCR1_ERXDMA | ETH_EGCR1_ETX |
+ ETH_EGCR1_PM_MII | ETH_EGCR1_ITXA | ETH_EGCR1_MB1;
+ ethwrite32(dev, egcr1 | ETH_EGCR1_ERXINIT, ETH_EGCR1);
+
+ timeout = 6;
+ while (!(ethread32(dev, ETH_EGSR) & ETH_EGSR_RXINIT) && timeout--)
+ udelay(1);
+
+ if (!timeout)
+ return -EBUSY;
+
+ ethwrite32(dev, ETH_EGSR_RXINIT, ETH_EGSR);
+ ethwrite32(dev, egcr1, ETH_EGCR1);
+
+ ethwrite32(dev, ETH_MAC1_RXEN, ETH_MAC1);
+ ethwrite32(dev, ETH_MAC2_CRCEN | ETH_MAC2_PADEN, ETH_MAC2);
+ ethwrite32(dev, 0x12, ETH_B2BIPG);
+
+ /* clear and enable statistics */
+ ethupdate32(dev, 0xffffffff, ETH_EGCR2_CLRCNT, ETH_EGCR2);
+ ethupdate32(dev, ~ETH_EGCR2_CLRCNT, 0, ETH_EGCR2);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR2_AUTOZ | ETH_EGCR2_STEN, ETH_EGCR2);
+
+ ie = ethread32(dev, ETH_IE);
+ ethwrite32(dev, ie | ETH_RX_IRQS, ETH_IE);
+
+ ethwrite32(dev, 0xf, ETH_RXFREE);
+ return 0;
+}
+
+static void ns9xxx_eth_hwdisable(struct net_device *dev)
+{
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ ethwrite32(dev, 0, ETH_EGCR1);
+ ethwrite32(dev, 0, ETH_IE);
+ ethwrite32(dev, 0, ETH_MAC1);
+ ethwrite32(dev, 0, ETH_MAC2);
+}
+
+static int ns9xxx_eth_open(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct resource *res;
+ int ret = -ENOMEM;
+ int i;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ res = request_mem_region(priv->mapbase, 0x2800, DRIVER_NAME);
+ if (!res) {
+ dev_dbg(&dev->dev, "%s: err_request_mem\n", __func__);
+ goto err_request_mem;
+ }
+
+ phy_start(priv->phy);
+
+ priv->txfree = 1;
+ priv->txbusy = 1;
+
+ priv->rxdesc = dma_alloc_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ &priv->rxdeschandle, GFP_KERNEL);
+ if (!priv->rxdesc) {
+ dev_dbg(&dev->dev, "%s: err_alloc_rxdesc\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_rxdesc;
+ }
+
+ priv->rxskb = kmalloc(sizeof(*priv->rxskb) * TOTAL_NR_RXDESC,
+ GFP_KERNEL);
+ if (!priv->rxskb) {
+ dev_dbg(&dev->dev, "%s: err_alloc_rxskb\n", __func__);
+ goto err_alloc_rxskb;
+ }
+
+ for (i = 0; i < TOTAL_NR_RXDESC; ++i) {
+ ret = ns9xxx_eth_create_skbuff(dev, i);
+
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_setup_rxskb (i = %d)\n",
+ __func__, i);
+ goto err_setup_rxskb;
+ }
+ }
+
+ priv->txskb = kmalloc(sizeof(*priv->txskb) * TOTAL_NR_TXDESC,
+ GFP_KERNEL);
+ if (!priv->txskb) {
+ dev_dbg(&dev->dev, "%s: err_alloc_txskb\n", __func__);
+ goto err_alloc_txskb;
+ }
+
+ ret = ns9xxx_eth_hwinit(dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_hwinit -> %d\n", __func__, ret);
+ goto err_hwinit;
+ }
+
+ ret = request_irq(priv->irqtx, ns9xxx_eth_tx_int, 0, DRIVER_NAME, dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_request_irq_tx -> %d\n",
+ __func__, ret);
+ goto err_request_irq_tx;
+ }
+
+ ret = request_irq(priv->irqrx, ns9xxx_eth_rx_int, 0, DRIVER_NAME, dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_request_irq_rx -> %d\n",
+ __func__, ret);
+
+ free_irq(priv->irqtx, dev);
+err_request_irq_tx:
+
+ ns9xxx_eth_hwdisable(dev);
+err_hwinit:
+
+err_setup_rxskb:
+ for (i = 0; priv->rxskb[i]; ++i) {
+ dma_unmap_single(&dev->dev, priv->rxdesc[i].source,
+ priv->rxskb[i]->len, DMA_FROM_DEVICE);
+ kfree_skb(priv->rxskb[i]);
+ }
+
+ kfree(priv->txskb);
+err_alloc_txskb:
+
+ kfree(priv->rxskb);
+err_alloc_rxskb:
+
+ dma_free_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ priv->rxdesc, priv->rxdeschandle);
+err_alloc_rxdesc:
+
+ release_mem_region(priv->mapbase, 0x2800);
+err_request_mem:
+
+ return ret;
+ }
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int ns9xxx_eth_stop(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ free_irq(priv->irqrx, dev);
+ free_irq(priv->irqtx, dev);
+
+ cancel_delayed_work(&priv->recover_from_rx_stall);
+
+ ns9xxx_eth_hwdisable(dev);
+ kfree(priv->txskb);
+ kfree(priv->rxskb);
+ dma_free_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ priv->rxdesc, priv->rxdeschandle);
+
+ release_mem_region(priv->mapbase, 0x2800);
+
+ return 0;
+}
+
+static int ns9xxx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ int ret;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phy)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ret = phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void ns9xxx_eth_add_txdesc(struct net_device *dev,
+ union ns9xxx_dma_desc *txbuffer)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int i;
+
+ dev_vdbg(&dev->dev, "%s: txfree=%hu, txbusy=%hu\n",
+ __func__, priv->txfree, priv->txbusy);
+
+ for (i = 0; i < 4; ++i)
+ ethwrite32(dev, txbuffer->data[i],
+ ETH_TXBDR + 16 * priv->txfree + 4 * i);
+
+ priv->txfree = (priv->txfree + 1) % TOTAL_NR_TXDESC;
+}
+
+static int ns9xxx_eth_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ union ns9xxx_dma_desc txbuffer = {};
+ unsigned long flags;
+ int ret;
+
+ /* do I need to lock already here? */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ dev_vdbg(&dev->dev, "%s(skb=%p): skb->data=%p\n",
+ __func__, skb, skb->data);
+
+ if (likely(ns9xxx_eth_num_txfree(dev) > 0)) {
+ txbuffer.source = dma_map_single(&dev->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ txbuffer.len = skb->len;
+ txbuffer.flags = DMATXDESC_LAST | DMADESC_FULL;
+
+ if (unlikely(priv->txfree == TOTAL_NR_TXDESC - 1))
+ txbuffer.flags |= DMADESC_WRAP;
+
+ priv->txskb[priv->txfree] = skb;
+ ns9xxx_eth_add_txdesc(dev, &txbuffer);
+
+ if (ns9xxx_eth_num_txbusy(dev) == 1)
+ ns9xxx_eth_start_tx_dma(dev);
+
+ ret = NETDEV_TX_OK;
+ } else {
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void ns9xxx_eth_tx_timeout(struct net_device *dev)
+{
+ /* XXX */
+ dev_warn(&dev->dev, "%s unimplemented\n", __func__);
+}
+
+static struct net_device_stats *ns9xxx_eth_get_stats(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ priv->stats.rx_length_errors += ethread32(dev, ETH_STAT_RFLR);
+ priv->stats.rx_over_errors += ethread32(dev, ETH_STAT_ROVR);
+ priv->stats.rx_crc_errors += ethread32(dev, ETH_STAT_RFCS);
+ priv->stats.rx_frame_errors += ethread32(dev, ETH_STAT_RALN);
+ priv->stats.rx_errors += ethread32(dev, ETH_STAT_RCDE) +
+ ethread32(dev, ETH_STAT_RCSE) +
+ ethread32(dev, ETH_STAT_RUND) +
+ ethread32(dev, ETH_STAT_RFRG) +
+ ethread32(dev, ETH_STAT_RJBR);
+ priv->stats.multicast += ethread32(dev, ETH_STAT_RMCA);
+ priv->stats.tx_aborted_errors += ethread32(dev, ETH_STAT_TXCL);
+ priv->stats.collisions += ethread32(dev, ETH_STAT_TNCL);
+
+ return &priv->stats;
+}
+
+static void ns9xxx_set_multicast_list(struct net_device *dev)
+{
+ u32 saf = ETH_SAF_BROAD;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ /* TODO: use the hash tables to improve multicast reception */
+ if (dev->flags & IFF_PROMISC)
+ /* get everything */
+ saf |= ETH_SAF_PRO;
+
+ else if (dev->flags & IFF_ALLMULTI)
+ /* get all multicast traffic */
+ saf |= ETH_SAF_PRM;
+
+ else if (dev->mc_count > 0) {
+ struct dev_addr_list *m;
+ u32 ht[2] = {0, 0};
+
+ for (m = dev->mc_list; m; m = m->next) {
+ /*
+ * The HRM of ns9360 and ns9215 state that the upper 6
+ * bits are used to calculate the bit in the hash table,
+ * but the sample code (and the NET+OS driver) uses bits
+ * 28:23 ...
+ */
+ u32 crc = ether_crc(ETH_ALEN, m->dmi_addr) >> 23;
+ crc &= 0x3f;
+
+ ht[crc & 0x20 ? 1 : 0] |= 1 << (crc & 0x1f);
+ }
+
+ saf |= ETH_SAF_PRA;
+
+ ethwrite32(dev, ht[0], ETH_HT1);
+ ethwrite32(dev, ht[1], ETH_HT2);
+ }
+
+ ethwrite32(dev, saf, ETH_SAF);
+}
+
+static int ns9xxx_eth_mdiobus_init(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct plat_ns9xxx_eth *pdata = pdev->dev.platform_data;
+ int i;
+ char phyid[BUS_ID_SIZE];
+ int ret = -ENOMEM;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ priv->mdiobus = mdiobus_alloc();
+ if (priv->mdiobus == NULL)
+ goto err_out;
+
+ priv->mdiobus->name = DRIVER_NAME "-mii";
+ snprintf(priv->mdiobus->id, MII_BUS_ID_SIZE, "0");
+ priv->mdiobus->priv = dev;
+ priv->mdiobus->read = ns9xxx_eth_mdiobus_read;
+ priv->mdiobus->write = ns9xxx_eth_mdiobus_write;
+ priv->mdiobus->reset = ns9xxx_eth_mdiobus_reset;
+ priv->mdiobus->phy_mask = pdata->phy_mask;
+ priv->mdiobus->parent = &pdev->dev;
+ priv->mdiobus->irq = kmalloc(sizeof(*priv->mdiobus->irq) * PHY_MAX_ADDR,
+ GFP_KERNEL);
+
+ if (!priv->mdiobus->irq) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_irq\n", __func__);
+ goto err_alloc_irq;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ priv->mdiobus->irq[i] = PHY_POLL;
+
+ ret = mdiobus_register(priv->mdiobus);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_mdiobus_register -> %d\n",
+ __func__, ret);
+ goto err_mdiobus_register;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ if (priv->mdiobus->phy_map[i])
+ break;
+
+ if (i >= PHY_MAX_ADDR) {
+ dev_dbg(&pdev->dev, "%s: no phy found\n", __func__);
+ ret = -ENODEV;
+ goto err_find_phy;
+ }
+
+ dev_dbg(&pdev->dev, "%s: using phy at address %d\n", __func__, i);
+
+ snprintf(phyid, sizeof(phyid), PHY_ID_FMT,
+ priv->mdiobus->id, i);
+
+ priv->phy = phy_connect(dev, phyid, &ns9xxx_eth_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(priv->phy)) {
+ ret = PTR_ERR(priv->phy);
+ dev_dbg(&pdev->dev, "%s: err_phy_connect -> %d\n",
+ __func__, ret);
+err_find_phy:
+
+ mdiobus_unregister(priv->mdiobus);
+err_mdiobus_register:
+
+ kfree(priv->mdiobus->irq);
+err_alloc_irq:
+ mdiobus_free(priv->mdiobus);
+err_out:
+ return ret;
+ }
+
+ priv->phy->supported &= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_MII;
+
+ priv->phy->advertising = priv->phy->supported;
+
+ return 0;
+}
+
+static int ns9xxx_eth_mdiobus_disable(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ phy_disconnect(priv->phy);
+ mdiobus_unregister(priv->mdiobus);
+ kfree(priv->mdiobus->irq);
+ mdiobus_free(priv->mdiobus);
+
+ return 0;
+}
+
+static __devinit int ns9xxx_eth_pdrv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ns9xxx_eth_priv *priv;
+
+ struct resource *mem;
+ struct plat_ns9xxx_eth *pdata = pdev->dev.platform_data;
+ unsigned char __iomem *membase;
+ u32 sa;
+ int ret = -ENODEV;
+ int i;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (!pdata)
+ goto err_pdata;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ ret = -ENOMEM;
+
+ membase = ioremap(mem->start, 0x2800);
+ if (!membase) {
+ dev_dbg(&pdev->dev, "%s: err_ioremap\n", __func__);
+ goto err_ioremap;
+ }
+
+ dev = alloc_etherdev(sizeof(*priv));
+ if (!dev) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_etherdev\n", __func__);
+ goto err_alloc_etherdev;
+ }
+
+ dev->dev.coherent_dma_mask = (u32)-1;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->open = ns9xxx_eth_open;
+ dev->stop = ns9xxx_eth_stop;
+ dev->hard_start_xmit = ns9xxx_eth_hard_start_xmit;
+ dev->tx_timeout = ns9xxx_eth_tx_timeout;
+ dev->get_stats = ns9xxx_eth_get_stats;
+ dev->set_multicast_list = ns9xxx_set_multicast_list;
+ dev->do_ioctl = ns9xxx_eth_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = ns9xxx_eth_netpoll;
+#endif
+
+ /* TODO: implement VLAN */
+ dev->features = 0;
+
+ priv = netdev_priv(dev);
+
+ spin_lock_init(&priv->lock);
+ INIT_DELAYED_WORK(&priv->recover_from_rx_stall, ns9xxx_eth_recover_from_rx_stall);
+
+ priv->membase = membase;
+ priv->mapbase = mem->start;
+ priv->irqrx = pdata->irqrx;
+ priv->irqtx = pdata->irqtx;
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ priv->activityled = pdata->activityled;
+ /* init kernel timer for toggling
+ * the activity LED */
+ activityled_timer.data = (unsigned long)priv->activityled;
+ activityled_timer.function = activityled_timer_fn;
+ activityled_timer.expires = jiffies + ACTIVITYLED_TOGGLE_TIMEOUT;
+ init_timer(&activityled_timer);
+ add_timer(&activityled_timer);
+#endif
+
+ priv->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_dbg(&pdev->dev, "%s: err_clk_get -> %d\n", __func__, ret);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable -> %d\n",
+ __func__, ret);
+ goto err_clk_enable;
+ }
+
+ sa = ethread32(dev, ETH_SA1);
+ if (sa == 0) {
+ dev_warn(&pdev->dev, "Warning: Using default mac address "
+ "00:04:f3:ff:ff:fa\n");
+ memcpy(dev->dev_addr, "\x00\x04\xf3\xff\xff\xfa", ETH_ALEN);
+ } else {
+ DECLARE_MAC_BUF(mac);
+
+ /* assume the bootloader has provided a valid mac address */
+ dev->dev_addr[4] = sa;
+ dev->dev_addr[5] = sa >> 8;
+ sa = ethread32(dev, ETH_SA2);
+ dev->dev_addr[2] = sa;
+ dev->dev_addr[3] = sa >> 8;
+ sa = ethread32(dev, ETH_SA3);
+ dev->dev_addr[0] = sa;
+ dev->dev_addr[1] = sa >> 8;
+ dev_dbg(&pdev->dev, "mac address: %s\n",
+ print_mac(mac, dev->dev_addr));
+ }
+
+ ethwrite32(dev, ETH_EGCR1_PM_MII | ETH_EGCR1_MACHRST, ETH_EGCR1);
+ udelay(5);
+ ethwrite32(dev, ETH_EGCR1_PM_MII | ETH_EGCR1_ETX, ETH_EGCR1);
+
+ ethwrite32(dev, 0, ETH_MAC1);
+
+ dev_dbg(&pdev->dev, "%s: clear tx DMA descs\n", __func__);
+ for (i = 0; i < 4 * TOTAL_NR_TXDESC; ++i)
+ ethwrite32(dev, 0, ETH_TXBDR + 4 * i);
+
+ ret = ns9xxx_eth_mdiobus_init(pdev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_mdiobus_init -> %d\n",
+ __func__, ret);
+ goto err_mdiobus_init;
+ }
+
+ /* Make the device wakeup capable, but disabled by default */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_register_netdev -> %d\n",
+ __func__, ret);
+
+ ns9xxx_eth_mdiobus_disable(dev);
+err_mdiobus_init:
+
+ clk_disable(priv->clk);
+err_clk_enable:
+
+ clk_put(priv->clk);
+err_clk_get:
+
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+err_alloc_etherdev:
+
+ iounmap(membase);
+err_ioremap:
+err_get_mem:
+err_pdata:
+ return ret;
+ }
+
+ /* When using nfsroot, there are RPC requests which can be sent
+ * before the link is up (long autonegotiation...). Then the
+ * RPC is not completed until the autonegotiation timeouts (which
+ * adds long time to the boot process)
+ * Following line tries to workaround that situation by starting
+ * the phy here, so we have some worked completed in advance to
+ * the driver open call */
+ phy_start(priv->phy);
+
+ dev_info(&pdev->dev, "%s at MMIO %p\n", dev->name, membase);
+ dev_dbg(&pdev->dev, "&priv = %p\n", &priv);
+ dev_dbg(&pdev->dev, "&priv->txfree = %p\n", &priv->txfree);
+ dev_dbg(&pdev->dev, "&priv->txbusy = %p\n", &priv->txbusy);
+
+ return 0;
+}
+
+static __devexit int ns9xxx_eth_pdrv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ unregister_netdev(dev);
+
+ ns9xxx_eth_mdiobus_disable(dev);
+
+ clk_disable(priv->clk);
+
+ clk_put(priv->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ iounmap(priv->membase);
+
+ free_netdev(dev);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ del_timer(&activityled_timer);
+#endif
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int ns9xxx_eth_pdrv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (state.event != PM_EVENT_SUSPEND)
+ /* XXX: implement full state saving */
+ return -EBUSY;
+
+ netif_device_detach(dev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+
+ ret = enable_irq_wake(priv->irqrx);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_enable_irq_wake -> %d\n",
+ __func__, ret);
+ goto err_enable_irq_wake;
+ }
+
+ } else {
+ dev_dbg(&pdev->dev, "%s: !device_may_wakeup\n", __func__);
+ clk_disable(priv->clk);
+ }
+
+ return 0;
+
+err_enable_irq_wake:
+ netif_device_attach(dev);
+
+ return ret;
+}
+
+static int ns9xxx_eth_pdrv_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(priv->irqrx);
+ else {
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable -> %d",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ netif_device_attach(dev);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver ns9xxx_eth_pdriver = {
+ .probe = ns9xxx_eth_pdrv_probe,
+ .remove = __devexit_p(ns9xxx_eth_pdrv_remove),
+#if defined(CONFIG_PM)
+ .suspend = ns9xxx_eth_pdrv_suspend,
+ .resume = ns9xxx_eth_pdrv_resume,
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns9xxx_eth_init(void)
+{
+ int ret;
+
+ /* catch compiler bugs, endian issues etc pp */
+ BUG_ON(offsetof(union ns9xxx_dma_desc, data) != 0x0);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, source) != 0x0);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, len) != 0x4);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, dest) != 0x8);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, flags) != 0xe);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, status) != 0xc);
+
+ ret = platform_driver_register(&ns9xxx_eth_pdriver);
+ if (ret) {
+ pr_debug("%s: err_pdrv_register\n", __func__);
+
+ return ret;
+ }
+ pr_info("Digi NS9XXX Ethernet driver\n");
+
+ return 0;
+}
+
+static void __exit ns9xxx_eth_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_eth_pdriver);
+}
+
+module_init(ns9xxx_eth_init);
+module_exit(ns9xxx_eth_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig");
+MODULE_DESCRIPTION("Digi NS9XXX ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h
index cc7d85bdfb3e..2ea8f4aa1d91 100644
--- a/drivers/net/smc911x.h
+++ b/drivers/net/smc911x.h
@@ -52,6 +52,14 @@
#ifdef SMC_USE_PXA_DMA
#define SMC_USE_DMA
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
+#endif
+
+#ifdef CONFIG_ARCH_MXC
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
#endif
/* store this information for the driver.. */
diff --git a/drivers/net/smsc9118/Makefile b/drivers/net/smsc9118/Makefile
new file mode 100644
index 000000000000..d33fe1dc8e38
--- /dev/null
+++ b/drivers/net/smsc9118/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SMSC9118) += smsc911x.o
diff --git a/drivers/net/smsc9118/smsc911x.c b/drivers/net/smsc9118/smsc911x.c
new file mode 100644
index 000000000000..3a55f78cee8a
--- /dev/null
+++ b/drivers/net/smsc9118/smsc911x.c
@@ -0,0 +1,2711 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ *
+ * History:
+ * 05/05/2005 bahadir.balban@arm.com
+ * - Transition to linux coding style
+ * - Platform driver and module interface
+ *
+ * 17/07/2006 steve.glendinning@smsc.com
+ * - Added support for LAN921x family
+ * - Added workaround for multicast filters
+ *
+ * 31/07/2006 steve.glendinning@smsc.com
+ * - Removed tasklet, using NAPI poll instead
+ * - Multiple device support
+ * - Large tidy-up following feedback from netdev list
+ *
+ * 03/08/2006 steve.glendinning@smsc.com
+ * - Added ethtool support
+ * - Convert to use generic MII interface
+ *
+ * 04/08/2006 bahadir.balban@arm.com
+ * - Added ethtool eeprom r/w support
+ *
+ * 17/06/2007 steve.glendinning@smsc.com
+ * - Incorporate changes from Bill Gatliff and Russell King
+ *
+ * 04/07/2007 steve.glendinning@smsc.com
+ * - move irq configuration to platform_device
+ * - fix link poller after interface is stopped and restarted
+ *
+ * 13/07/2007 bahadir.balban@arm.com
+ * - set irq polarity before requesting irq
+ *
+ * 26/06/2007 hennerich@blackfin.uclinux.org
+ * - Fixed minor style issue to pass checkpatch.pl
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+
+/* For having the same platform-data as in the Vanilla kernel */
+#include <linux/smc911x.h>
+
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_DRV_VERSION "2007-07-13"
+
+MODULE_LICENSE("GPL");
+
+
+/* Base address of the connected controller: S3C2410_CS5 = 0x28000000 */
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] smsc911x: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "smsc911x: " fmt, ## args)
+
+#if 0
+#define SMSC911X_DEBUG
+#endif
+
+#ifdef SMSC911X_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "smsc911x: " fmt, ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+/* Enables the debug messages for the PM-operations (WOL, suspend, etc.) */
+#if 0
+#define SMSC911X_PM_DEBUG
+#endif
+
+#ifdef SMSC911X_PM_DEBUG
+# define printk_pmdbg(fmt, args...) printk(KERN_DEBUG "smsc911x: " fmt, ## args)
+#else
+# define printk_pmdbg(fmt, args...)
+#endif
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+ unsigned int generation; /* used to decide which workarounds apply */
+
+ /* device configuration */
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+ unsigned int irq_flags;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ * smsc911x_phy_read(), smsc911x_phy_write()
+ */
+ spinlock_t phy_lock;
+
+ struct mii_if_info mii;
+ unsigned int using_extphy;
+ u32 msg_enable;
+#ifdef USE_LED1_WORK_AROUND
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+#endif
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct timer_list link_poll_timer;
+ unsigned int stop_link_poll;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+ unsigned int last_rxstat;
+
+ /* Registers for the internal PM */
+ unsigned long mac_wucsr;
+ unsigned long pmt_ctrl;
+ unsigned long phy_intmsk;
+};
+
+
+static int smsc911x_set_mac(struct net_device *dev, void *addr);
+
+#if SMSC_CAN_USE_32BIT
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ return readl(pdata->ioaddr + reg);
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ writel(val, pdata->ioaddr + reg);
+}
+
+#else /* SMSC_CAN_USE_32BIT */
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ /* these two 16-bit reads must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ reg_val = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ /* these two 16-bit writes must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ local_irq_restore(flags);
+}
+
+#endif /* SMSC_CAN_USE_32BIT */
+
+#ifndef CONFIG_BLACKFIN
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ smsc911x_reg_write(*buf++, pdata, TX_DATA_FIFO);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+}
+#else
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (wordcount > 24)
+ dma_outsl((u_long)pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+ else
+ outsl((u_long)pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (wordcount > 24)
+ dma_insl((u_long)pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+ else
+ insl((u_long)pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+}
+#endif
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes phy_lock is held */
+static int smsc911x_mac_notbusy(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 1;
+ }
+ SMSC_WARNING("Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return 0;
+}
+
+/* Fetches a MAC register value. Assumes phy_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_
+ | MAC_CSR_CMD_R_NOT_W_), pdata, MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to happen */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, phy_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(val, pdata, MAC_CSR_DATA);
+
+ /* Write the actual data */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_), pdata,
+ MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return;
+
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Gets a phy register, phy_lock must be acquired before calling */
+static u16 smsc911x_phy_read(struct smsc911x_data *pdata, unsigned int index)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_phy_read???");
+ return 0;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11)
+ | ((index & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ return smsc911x_mac_read(pdata, MII_DATA);
+ }
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+ return 0xFFFF;
+}
+
+/* Sets a phy register, phy_lock must be acquired before calling */
+static void smsc911x_phy_write(struct smsc911x_data *pdata,
+ unsigned int index, u16 val)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_write_phy???");
+ return;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11) |
+ ((index & 0x1F) << 6) | MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_))
+ return;
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+}
+
+static int smsc911x_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ int reg;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ reg = smsc911x_phy_read(pdata, location);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return reg;
+}
+
+static void smsc911x_mdio_write(struct net_device *dev, int phy_id,
+ int location, int val)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, location, val);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
+ * If something goes wrong, returns -ENODEV to revert back to internal phy.
+ * Performed at initialisation only, so interrupts are enabled */
+static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int address;
+ unsigned int hwcfg;
+ unsigned int phyid1;
+ unsigned int phyid2;
+
+ hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* External phy is requested, supported, and detected */
+ if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+
+ /* Attempt to switch to external phy for auto-detecting
+ * its address. Assuming tx and rx are stopped because
+ * smsc911x_phy_initialise is called before
+ * smsc911x_rx_initialise and tx_initialise.
+ */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Auto-detect PHY */
+ spin_lock_irq(&pdata->phy_lock);
+ for (address = 0; address <= 31; address++) {
+ pdata->mii.phy_id = address;
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ if ((phyid1 != 0xFFFFU) || (phyid2 != 0xFFFFU)) {
+ SMSC_TRACE("Detected PHY at address = "
+ "0x%02X = %d", address, address);
+ break;
+ }
+ }
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFFU) && (phyid2 == 0xFFFFU)) {
+ SMSC_WARNING("External PHY is not accessable, "
+ "using internal PHY instead");
+ /* Revert back to internal phy settings. */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to internal phy */
+ hwcfg &= (~HW_CFG_EXT_PHY_EN_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_INT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg &= (~HW_CFG_SMI_SEL_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ /* Use internal phy */
+ return -ENODEV;
+ } else {
+ SMSC_TRACE("Successfully switched to external PHY");
+ pdata->using_extphy = 1;
+ }
+ } else {
+ SMSC_WARNING("No external PHY detected.");
+ SMSC_WARNING("Using internal PHY instead.");
+ /* Use internal phy */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* called by phy_initialise and loopback test */
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+ unsigned int i = 100000;
+ unsigned long flags;
+
+ SMSC_TRACE("Performing PHY BCR Reset");
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_RESET);
+ do {
+ udelay(10);
+ temp = smsc911x_phy_read(pdata, MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe
+ */
+ msleep(1);
+
+ return 1;
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ u32 bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (((unsigned int)pdata->loopback_tx_pkt)
+ & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(txcmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(txcmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32) pdata->loopback_tx_pkt) & 0xFFFFFFFC;
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (((u32) pdata->loopback_tx_pkt) & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to transmit during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARNING("Transmit encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARNING("Receive encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (u32)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += ((u32)pdata->loopback_rx_pkt) & 0x3;
+ rdsz >>= 2;
+
+ smsc911x_rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARNING("Unexpected packet size during "
+ "loop back test, size=%d, "
+ "will retry", pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE("Successfully verified "
+ "loopback packet");
+ return 1;
+ } else {
+ SMSC_WARNING("Data miss match during "
+ "loop back test, will retry.");
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct smsc911x_data *pdata)
+{
+ int result = 0;
+ unsigned int i;
+ unsigned int val;
+ unsigned long flags;
+
+ /* Initialise tx packet */
+ for (i = 0; i < 6; i++) {
+ /* Use broadcast destination address */
+ pdata->loopback_tx_pkt[i] = (char)0xFF;
+ }
+
+ for (i = 6; i < 12; i++) {
+ /* Use incrementing source address */
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+ for (i = 14; i < MIN_PACKET_SIZE; i++) {
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(val, pdata, HW_CFG);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+ smsc911x_reg_write((((unsigned int)pdata->loopback_rx_pkt)
+ & 0x03) << 8, pdata, RX_CFG);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, 0x4100);
+
+ /* Enable MAC tx/rx, FD */
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata)) {
+ result = 1;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_phy_write(pdata, MII_BMCR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(0, pdata, TX_CFG);
+ smsc911x_reg_write(0, pdata, RX_CFG);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+
+inline static void smsc911x_phy_dump_regs(struct smsc911x_data *pdata)
+{
+ printk("BCR = 0x%04x\n", smsc911x_phy_read(pdata, MII_BMCR));
+ printk("BSR = 0x%04x\n", smsc911x_phy_read(pdata, MII_BMSR));
+ printk("ID1 = 0x%04x\n", smsc911x_phy_read(pdata, MII_PHYSID1));
+ printk("ID2 = 0x%04x\n", smsc911x_phy_read(pdata, MII_PHYSID2));
+ printk("ADVER = 0x%04x\n", smsc911x_phy_read(pdata, MII_ADVERTISE));
+ printk("LPA = 0x%04x\n", smsc911x_phy_read(pdata, MII_LPA));
+ printk("EXP = 0x%04x\n", smsc911x_phy_read(pdata, MII_EXPANSION));
+}
+
+/* assumes phy_lock is held */
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+
+ if (pdata->mii.full_duplex) {
+ unsigned int phy_adv;
+ unsigned int phy_lpa;
+ phy_adv = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ phy_lpa = smsc911x_phy_read(pdata, MII_LPA);
+ if (phy_adv & phy_lpa & LPA_PAUSE_CAP) {
+ /* Both ends support symmetric pause, enable
+ * PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else if (((phy_adv & ADVERTISE_PAUSE_ALL) ==
+ ADVERTISE_PAUSE_ALL) &&
+ ((phy_lpa & LPA_PAUSE_ALL) == LPA_PAUSE_ASYM)) {
+ /* We support symmetric and asym pause, the
+ * other end only supports asym, Enable PAUSE
+ * receive, disable PAUSE transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else {
+ /* Disable PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+ } else {
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+}
+
+static void smsc911x_phy_update_duplex(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int mac_cr;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (pdata->mii.full_duplex) {
+ SMSC_TRACE("configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE("configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_phy_update_flowcontrol(pdata);
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+
+/* Update link mode if any thing has changed */
+static void smsc911x_phy_update_linkmode(struct net_device *dev, int init)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ if (mii_check_media(&pdata->mii, netif_msg_link(pdata), init))
+ smsc911x_phy_update_duplex(dev);
+ /* mii_check_media() exists if the media is forced... */
+ if (pdata->mii.force_media) {
+ int cur_link = mii_link_ok(&pdata->mii);
+ int prev_link = netif_carrier_ok(dev);
+
+ if (!prev_link && cur_link) {
+ printk(KERN_INFO "%s: link up\n", dev->name);
+ netif_carrier_on(dev);
+ } else if (prev_link && !cur_link) {
+ printk(KERN_INFO "%s: link down\n", dev->name);
+ netif_carrier_off(dev);
+ }
+ }
+
+#ifdef USE_LED1_WORK_AROUND
+ if (netif_carrier_ok(dev)) {
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore orginal GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ } else {
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
+ && (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * orginal GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ }
+#endif /* USE_LED1_WORK_AROUND */
+}
+
+/* Entry point for the link poller */
+static void smsc911x_phy_checklink(unsigned long ptr)
+{
+ struct net_device *dev = (struct net_device *)ptr;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_phy_update_linkmode(dev, 0);
+
+ if (!(pdata->stop_link_poll)) {
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+ } else {
+ pdata->stop_link_poll = 0;
+ }
+}
+
+static void smsc911x_phy_set_automdx(struct smsc911x_data *pdata)
+{
+ u16 ctrlstatus;
+
+ ctrlstatus = smsc911x_phy_read(pdata, 27);
+ ctrlstatus &= 0x1fff;
+ ctrlstatus |= (0x6 << 13);
+ smsc911x_phy_write(pdata, 27, ctrlstatus);
+}
+
+/* Initialises the PHY layer. Called at initialisation by open() so
+ * interrupts are enabled */
+static int smsc911x_phy_initialise(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int phyid1 = 0;
+ unsigned int phyid2 = 0;
+ unsigned int temp;
+
+ printk_debug("Calling phy_initialise()\n");
+
+ pdata->using_extphy = 0;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ /* External PHY supported, try to autodetect */
+ if (smsc911x_phy_initialise_external(pdata) < 0) {
+ SMSC_TRACE("External PHY is not detected, using "
+ "internal PHY instead");
+ pdata->mii.phy_id = 1;
+ }
+ break;
+ default:
+ SMSC_TRACE("External PHY is not supported, using internal PHY "
+ "instead");
+ pdata->mii.phy_id = 1;
+ break;
+ }
+
+ spin_lock_irq(&pdata->phy_lock);
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFF) && (phyid2 == 0xFFFF)) {
+ SMSC_WARNING("Internal PHY not detected!");
+ return 0;
+ }
+
+ /* Reset the phy */
+ if (!smsc911x_phy_reset(pdata)) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+#ifdef USE_PHY_WORK_AROUND
+ if (!smsc911x_phy_loopbacktest(pdata)) {
+ SMSC_WARNING("Failed Loop Back Test");
+ return 0;
+ } else {
+ SMSC_TRACE("Passed Loop Back Test");
+ }
+#endif /* USE_PHY_WORK_AROUND */
+
+
+ smsc911x_phy_set_automdx(pdata);
+
+ /* Advertise all speeds and pause capabilities */
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ temp |= (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ smsc911x_phy_write(pdata, MII_ADVERTISE, temp);
+ pdata->mii.advertising = temp;
+
+ if (!pdata->using_extphy) {
+ /* using internal phy, enable PHY interrupts */
+ smsc911x_phy_read(pdata, MII_INTSTS);
+ smsc911x_phy_write(pdata, MII_INTMSK, PHY_INTMSK_DEFAULT_);
+ }
+
+ /* begin to establish link */
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_phy_update_linkmode(dev, 1);
+
+ pdata->stop_link_poll = 0;
+ setup_timer(&pdata->link_poll_timer, smsc911x_phy_checklink,
+ (unsigned long)dev);
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+
+ printk_debug("PHY initialised succesfully\n");
+ return 1;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ unsigned int result = (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+ return result;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct smsc911x_data *pdata)
+{
+ struct net_device *netdev = pdata->netdev;
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARNING("Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & 0x00008000)) {
+ printk_debug("TX status error: 0x%08x (MAC 0x%08x)\n",
+ tx_stat, smsc911x_mac_read(pdata, MAC_CR));
+ netdev->stats.tx_errors++;
+ } else {
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & 0x00000100)) {
+ netdev->stats.collisions += 16;
+ netdev->stats.tx_aborted_errors += 1;
+ } else {
+ netdev->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & 0x00000800)) {
+ netdev->stats.tx_carrier_errors += 1;
+ }
+ if (unlikely(tx_stat & 0x00000200)) {
+ netdev->stats.collisions++;
+ netdev->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct smsc911x_data *pdata, unsigned int rxstat)
+{
+ struct net_device *netdev = pdata->netdev;
+ int crc_err = 0;
+
+ if (unlikely(rxstat & 0x00008000)) {
+ netdev->stats.rx_errors++;
+ if (unlikely(rxstat & 0x00000002)) {
+ netdev->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
+ /* Frame type indicates length,
+ * and length error is set */
+ netdev->stats.rx_length_errors++;
+ }
+ if (rxstat & RX_STS_MCAST_)
+ netdev->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(RX_DP_CTRL_RX_FFWD_, pdata, RX_DP_CTRL);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING("Timed out waiting for RX FFWD "
+ "to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata = container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->netdev;
+ int npackets = 0;
+
+ while (npackets < budget) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ /* break out of while loop if there are no more packets waiting */
+ if (!rxstat) {
+ printk_debug("Stopping the RX poll\n");
+ break;
+ }
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ printk_debug("Going to read %i words (pktlen %i)\n",
+ pktwords, pktlength);
+
+ smsc911x_rx_counterrors(pdata, rxstat);
+
+ if (likely((rxstat & RX_STS_ES_) == 0)) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
+ if (likely(skb)) {
+ skb->data = skb->head;
+ skb->tail = skb->head;
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ smsc911x_rx_readfifo(pdata,
+ (unsigned int *)skb->head,
+ pktwords);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += (pktlength - 4);
+ dev->last_rx = jiffies;
+ npackets++;
+ continue;
+ } else {
+ SMSC_WARNING("Unable to allocate sk_buff "
+ "for rx packet, in PIO path");
+ dev->stats.rx_dropped++;
+ }
+ }
+ /* At this point, the packet is to be read out
+ * of the fifo and discarded */
+ smsc911x_rx_fastforward(pdata, pktlength);
+ }
+
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);
+
+ if (npackets < budget) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ netif_rx_complete(dev, napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ }
+
+ /* Return total received packets */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ unsigned int crc;
+ unsigned int result;
+
+ crc = ether_crc(ETH_ALEN, addr);
+ result = (crc >> 26) & 0x3f;
+
+ return result;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the phy_lock held */
+ unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->phy_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARNING("Rx not stopped\n");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->phy_lock);
+}
+
+/* Sets the device MAC address to dev_addr, called with phy_lock held */
+static void
+smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg = 0;
+ struct sockaddr addr;
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ printk_err("Failed to complete reset");
+ return -ENODEV;
+ }
+
+ smsc911x_reg_write(0x00050000, pdata, HW_CFG);
+ smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((timeout--) &&
+ (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0)) {
+ SMSC_WARNING("Timed out waiting for EEPROM "
+ "busy bit to clear");
+ }
+#if USE_DEBUG >= 1
+ smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
+#else
+ smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
+#endif
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(0, pdata, INT_EN);
+ smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
+
+ /* Set interrupt deassertion to 100uS */
+ //intcfg = ((0x38 << 24) | INT_CFG_IRQ_EN_);
+ intcfg = ((0x00 << 24) | INT_CFG_IRQ_EN_);
+ // PPH modified intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->irq_polarity) {
+ SMSC_TRACE("irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE("irq polarity: active low");
+ }
+
+ if (pdata->irq_type) {
+ SMSC_TRACE("irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE("irq type: open drain");
+ }
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ printk_debug("Testing irq handler using IRQ %d\n", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ timeout = 1000;
+ while (timeout--) {
+ smp_rmb();
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+
+ printk_debug("IRQ handler passed test using IRQ %d\n", dev->irq);
+ netif_carrier_off(dev);
+
+ if (!smsc911x_phy_initialise(dev)) {
+ printk_err("Failed to initialize the PHY");
+ return -ENODEV;
+ }
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ temp &= HW_CFG_TX_FIF_SZ_;
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(temp, pdata, HW_CFG);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write((2 << 8), pdata, RX_CFG);
+
+ /* enable the polling before enabling the interrupts */
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_RDFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ spin_lock_irq(&pdata->phy_lock);
+
+ /*
+ * Reenable the full duplex mode, otherwise the TX engine will generate
+ * status errors (Luis Galdos)
+ */
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_ | MAC_CR_FDPX_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+
+ /* Set the MAC once again */
+ memcpy(addr.sa_data, dev->dev_addr, dev->addr_len);
+ if(smsc911x_set_mac(dev, &addr))
+ printk_err("Couldn't set the MAC address.\n");
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ printk_info("Stopping the interface\n");
+
+ napi_disable(&pdata->napi);
+
+ /* disable interrupts */
+ smsc911x_reg_write(0, pdata, INT_EN);
+
+ pdata->stop_link_poll = 1;
+ del_timer_sync(&pdata->link_poll_timer);
+
+ netif_stop_queue(dev);
+
+ /* At this point all Rx and Tx activity is stopped */
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(pdata);
+
+ /* Stop sending data after the last transmission */
+ smsc911x_reg_write(TX_CFG_STOP_TX_, pdata, TX_CFG);
+
+ SMSC_TRACE("Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ u32 bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARNING("Tx data fifo low, space available: %d",
+ freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32)skb->data) & 0xFFFFFFFC;
+ wrsz = (u32)skb->len + 3;
+ wrsz += ((u32)skb->data) & 0x3;
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ dev_kfree_skb(skb);
+ dev->trans_start = jiffies;
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(pdata);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(pdata);
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &dev->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->mc_count > 0) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ unsigned int count = 0;
+ struct dev_mc_list *mc_list = dev->mc_list;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while (mc_list) {
+ count++;
+ if ((mc_list->dmi_addrlen) == ETH_ALEN) {
+ unsigned int bitnum =
+ smsc911x_hash(mc_list->dmi_addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ } else {
+ SMSC_WARNING("dmi_addrlen != 6");
+ }
+ mc_list = mc_list->next;
+ }
+ if (count != (unsigned int)dev->mc_count)
+ SMSC_WARNING("mc_count != dev->mc_count");
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE("scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RXSTOP_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int intsts;
+ unsigned int inten;
+ unsigned int temp;
+ unsigned int intcfg;
+ int serviced = IRQ_NONE;
+
+ intcfg = smsc911x_reg_read(pdata, INT_CFG);
+ intsts = smsc911x_reg_read(pdata, INT_STS);
+ inten = smsc911x_reg_read(pdata, INT_EN);
+
+ printk_debug("New IRQ: intsts 0x%08x\n", intsts);
+
+ if ((intcfg & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) != (INT_CFG_IRQ_INT_ |
+ INT_CFG_IRQ_EN_))
+ return serviced;
+
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_SW_INT_, pdata, INT_STS);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE("RX Stop interrupt");
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ smsc911x_reg_write(INT_STS_TDFA_, pdata, INT_STS);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ smsc911x_reg_write(INT_STS_RXE_, pdata, INT_STS);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ if (likely(netif_rx_schedule_prep(dev, &pdata->napi))) {
+ /* Disable Rx interrupts and schedule NAPI poll */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ __netif_rx_schedule(dev, &pdata->napi);
+ }
+
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_PHY_INT_)) {
+ smsc911x_reg_write(INT_STS_PHY_INT_, pdata, INT_STS);
+ spin_lock(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_INTSTS);
+ spin_unlock(&pdata->phy_lock);
+ SMSC_TRACE("PHY interrupt, sts 0x%04X", (u16)temp);
+ smsc911x_phy_update_linkmode(dev, 0);
+ serviced = IRQ_HANDLED;
+ }
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int chg_in_duplex;
+ int ret;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+ ret = generic_mii_ioctl(&pdata->mii, if_mii(ifr), cmd, &chg_in_duplex);
+ if ((ret == 0) && (chg_in_duplex != 0))
+ smsc911x_phy_update_duplex(dev);
+
+ return ret;
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return mii_ethtool_gset(&pdata->mii, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_ethtool_sset(&pdata->mii, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strncpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strncpy(info->bus_info, dev->dev.bus_id, sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_nway_restart(&pdata->mii);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_CMD - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_CMD; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ for (i = MAC_CR; i <= WUCSR; i++)
+ data[j++] = smsc911x_mac_read(pdata, i);
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_phy_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(temp, pdata, GPIO_CFG);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE("op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(e2cmd, pdata, E2P_CMD);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ SMSC_TRACE("TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE("Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write((u32)data, pdata, E2P_DATA);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static int smsc911x_ethtool_set_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct smsc911x_data *pdata;
+
+ /* Check for unsupported options */
+ if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST
+ | WAKE_BCAST | WAKE_ARP))
+ return -EINVAL;
+
+ pdata = netdev_priv(dev);
+
+ /* When disable the WOL options need to disable the PHY-interrupts too */
+ if (!wol->wolopts) {
+ printk_pmdbg("[ WOL ] Disabling all sources\n");
+ pdata->pmt_ctrl &= ~(PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_);
+ pdata->phy_intmsk &= ~PHY_INTMSK_ENERGYON_;
+ pdata->mac_wucsr = 0;
+ goto exit_set_wol;
+ }
+
+ /*
+ * For the magic packet we MUST configure the MAC too, but we can't do it
+ * at this point, cause the controller stops working.
+ */
+ if (wol->wolopts & WAKE_MAGIC) {
+ printk_pmdbg("WOL: Enabling magic frame\n");
+ pdata->mac_wucsr |= WUCSR_MPEN_;
+ pdata->pmt_ctrl |= PMT_CTRL_WOL_EN_;
+ }
+
+ /* For the PHY-wakeup we must use the energy detection */
+ if (wol->wolopts & WAKE_PHY) {
+ printk_pmdbg("[ WOL ] Enabling PHY energy\n");
+ pdata->phy_intmsk |= PHY_INTMSK_ENERGYON_;
+ pdata->pmt_ctrl |= PMT_CTRL_ED_EN_;
+ }
+
+ exit_set_wol:
+ return 0;
+}
+
+/* Function for getting the infos about the WOL */
+static void smsc911x_ethtool_get_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ /* Only for magic and PHY power detection available up now */
+ wol->supported = WAKE_MAGIC | WAKE_PHY;
+}
+
+static struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+ .get_wol = smsc911x_ethtool_get_wol,
+ .set_wol = smsc911x_ethtool_set_wol,
+};
+
+
+static int smsc911x_set_mac(struct net_device *dev, void *addr)
+{
+ unsigned int reg;
+ int retval;
+ unsigned long flags;
+ struct smsc911x_data *pdata;
+ unsigned int low, high;
+ struct sockaddr *paddr = addr;
+
+ printk_debug("Set mac called\n");
+
+ pdata = netdev_priv(dev);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ /* First check that the MAC is not busy */
+ reg = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(reg & MAC_CSR_CMD_CSR_BUSY_)) {
+ printk_err("smsc911x_mac_read failed, MAC busy at entry");
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* Get the MAC address */
+ high = 0;
+ memcpy(&low, &(paddr->sa_data[0]), 4);
+ memcpy(&high, &(paddr->sa_data[4]), 2);
+ printk_debug("Going to set the MAC %04X%08X\n", high, low);
+
+ /* Now set the high address */
+ smsc911x_reg_write(high, pdata, MAC_CSR_DATA);
+ smsc911x_reg_write(ADDRH | MAC_CSR_CMD_CSR_BUSY_, pdata, MAC_CSR_CMD);
+ reg = smsc911x_reg_read(pdata, BYTE_TEST);
+ if (!smsc911x_mac_notbusy(pdata)) {
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* First set the low address */
+ smsc911x_reg_write(low, pdata, MAC_CSR_DATA);
+ smsc911x_reg_write(ADDRL | MAC_CSR_CMD_CSR_BUSY_, pdata, MAC_CSR_CMD);
+ reg = smsc911x_reg_read(pdata, BYTE_TEST);
+ if (!smsc911x_mac_notbusy(pdata)) {
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* And save the IP inside the driver structure */
+ memcpy(dev->dev_addr, paddr->sa_data, dev->addr_len);
+
+ printk_debug("MAC successful changed to %02X%08X\n",
+ smsc911x_mac_read(pdata, ADDRH),
+ smsc911x_mac_read(pdata, ADDRL));
+
+ retval = 0;
+
+ exit_unlock:
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return retval;
+}
+
+/* Initializing private device structures */
+static int smsc911x_init(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ SMSC_TRACE("Driver Parameters:");
+ SMSC_TRACE("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
+ SMSC_TRACE("IRQ: %d", dev->irq);
+ SMSC_TRACE("PHY will be autodetected.");
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARNING("pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ if (((pdata->idrev >> 16) & 0xFFFF) == (pdata->idrev & 0xFFFF)) {
+ SMSC_WARNING("idrev top 16 bits equal to bottom 16 bits, "
+ "idrev: 0x%08X", pdata->idrev);
+ SMSC_TRACE("This may mean the chip is set for 32 bit while "
+ "the bus is reading as 16 bit");
+ return -ENODEV;
+ }
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9118 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01170000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9117 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01160000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01150000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x118A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x117A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x116A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x115A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x92100000UL:
+ case 0x92110000UL:
+ case 0x92200000UL:
+ case 0x92210000UL:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ if (pdata->generation == 0)
+ SMSC_WARNING("This driver is not intended "
+ "for this chip revision");
+
+ ether_setup(dev);
+ dev->open = smsc911x_open;
+ dev->stop = smsc911x_stop;
+ dev->hard_start_xmit = smsc911x_hard_start_xmit;
+ dev->get_stats = smsc911x_get_stats;
+ dev->set_multicast_list = smsc911x_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+ dev->do_ioctl = smsc911x_do_ioctl;
+ dev->set_mac_address = smsc911x_set_mac;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, 64);
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = smsc911x_poll_controller;
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+ pdata->mii.phy_id_mask = 0x1f;
+ pdata->mii.reg_num_mask = 0x1f;
+ pdata->mii.force_media = 0;
+ pdata->mii.full_duplex = 0;
+ pdata->mii.dev = dev;
+ pdata->mii.mdio_read = smsc911x_mdio_read;
+ pdata->mii.mdio_write = smsc911x_mdio_write;
+
+ pdata->msg_enable = NETIF_MSG_LINK;
+
+ return 0;
+}
+
+static int smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ SMSC_TRACE("Stopping driver.");
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, res->end - res->start);
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static int smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+ unsigned int intcfg = 0;
+ int res_size;
+ int retval;
+
+ printk(KERN_INFO "%s: Driver version %s.\n", SMSC_CHIPNAME,
+ SMSC_DRV_VERSION);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_WARNING "%s: Could not allocate resource.\n",
+ SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = res->end - res->start;
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ printk(KERN_WARNING "%s: Could not allocate device.\n",
+ SMSC_CHIPNAME);
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+ pdata->netdev = dev;
+
+ dev->irq = platform_get_irq(pdev, 0);
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ /* copy config parameters across if present, otherwise pdata
+ * defaults to zeros */
+ if (pdev->dev.platform_data) {
+ struct smc911x_platdata *config = pdev->dev.platform_data;
+ pdata->irq_polarity = config->irq_polarity;
+ pdata->irq_flags = config->irq_flags;
+ }
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARNING("Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ retval = smsc911x_init(dev);
+ if (retval < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->irq_polarity)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ /*
+ * @XXX: The "irq_type" is not used at this moment, because we are using
+ * the same platform-data as the driver from the Vanilla-kernel.
+ * (Luis Galdos)
+ */
+ if (pdata->irq_type)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler,
+ pdata->irq_flags,
+ SMSC_CHIPNAME, dev);
+ if (retval) {
+ SMSC_WARNING("Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARNING("Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE("Network interface: \"%s\"", dev->name);
+ }
+
+ spin_lock_init(&pdata->phy_lock);
+
+ spin_lock_irq(&pdata->phy_lock);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_mac(dev, dev->dev_addr);
+ SMSC_TRACE("MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is set to random_ether_addr");
+ }
+ }
+
+ spin_unlock_irq(&pdata->phy_lock);
+
+ printk_info("%s: MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /* Enable the wakeup over this device (Luis Galdos) */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+ return 0;
+
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, res->end - res->start);
+out_0:
+ return retval;
+}
+
+/* Enter in the suspend mode */
+#if defined(CONFIG_PM)
+
+/*
+ * For the mode D1 we MUST left the interrupts enabled
+ */
+static int smsc911x_drv_state_wakeup(struct smsc911x_data *pdata, int mode)
+{
+ int retval;
+ unsigned long regval;
+
+ retval = 0;
+
+ if (mode != 1 && mode != 2)
+ return -EINVAL;
+
+ /* Clear already received WUs */
+ regval = smsc911x_mac_read(pdata, WUCSR);
+ regval &= ~(WUCSR_MPR_ | WUCSR_WUFR_);
+ regval |= pdata->mac_wucsr; /* Magic packet enable 'WUCSR_MPEN_' */
+ printk_pmdbg("[ SUSP ] WUCSR 0x%08lx\n", regval);
+ smsc911x_mac_write(pdata, WUCSR, regval);
+
+ /* For the D2 we must enable the PHY interrupt for the energy detection */
+ regval = smsc911x_reg_read(pdata, INT_EN);
+ regval |= (INT_EN_PME_INT_EN_ | INT_EN_PHY_INT_EN_);
+ printk_pmdbg("[ SUSP ] INT_EN 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, INT_EN);
+
+ if (mode /* @FIXME: Enabled only for D2 */) {
+ u16 phy_mode;
+
+ phy_mode = smsc911x_phy_read(pdata, MII_INTMSK);
+ phy_mode |= PHY_INTMSK_ENERGYON_;
+ smsc911x_phy_write(pdata, MII_INTMSK, phy_mode);
+ }
+
+ /* Clear the PM mode and clear the current wakeup status */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_WUPS_;
+ printk_pmdbg("[ SUSP ] PMT_CTRL 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Enable the PME at prior and the wake on LAN */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval |= pdata->pmt_ctrl; /* Enable the ENERGY detect or WOL interrupt */
+ regval |= PMT_CTRL_PME_EN_;
+
+ if (mode == 1)
+ regval |= PMT_CTRL_PM_MODE_D1_;
+ else
+ regval |= PMT_CTRL_PM_MODE_D2_;
+
+ printk_pmdbg("[ SUSP ] PMT_CTRL 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ return retval;
+}
+
+/* For the state D2 we must disable the host-interrupts */
+static int smsc911x_drv_state_d2(struct smsc911x_data *pdata)
+{
+ unsigned long regval;
+
+ /* Disable the interrupts of the controller */
+ regval = smsc911x_reg_read(pdata, INT_CFG);
+ regval &= ~INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(regval, pdata, INT_CFG);
+
+ /* Set the phy to the power down mode */
+ regval = smsc911x_phy_read(pdata, MII_BMCR);
+ regval |= BMCR_PDOWN;
+ smsc911x_phy_write(pdata, MII_BMCR, regval);
+
+ /*
+ * Enter into the power mode D2 (the controller doesn't
+ * support the mode D3)
+ */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_PM_MODE_D2_;
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ return 0;
+}
+
+static int smsc911x_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev;
+ struct smsc911x_data *pdata;
+ int retval;
+
+ ndev = platform_get_drvdata(pdev);
+ pdata = netdev_priv(ndev);
+
+ if (!ndev)
+ return -ENODEV;
+
+ /* @FIXME: Implement the other supported power modes of the smsc911x */
+ if (state.event != PM_EVENT_SUSPEND)
+ return -ENOTSUPP;
+
+ if (netif_running(ndev)) {
+
+ /* The below code is coming from the WinCE guys */
+ netif_device_detach(ndev);
+
+ /*
+ * If configured as wakeup-source enter the mode D1 for packet
+ * detection using the standard IRQ-line
+ */
+ if (device_may_wakeup(&pdev->dev)) {
+
+ /*
+ * Sanity check for verifying that a wakeup-source was
+ * configured from the user space. If the energy-detect
+ * wakeup was enabled, then use the D2 for entering into the
+ * power mode
+ */
+ if (!(pdata->pmt_ctrl & (PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_))) {
+ printk_err("[ SUSP ] No WOL source defined.\n");
+ retval = -EINVAL;
+ goto err_attach;
+ }
+
+ /*
+ * By the WOL (magic packet, etc.) we can ONLY use the D1, but
+ * for the energy detect over the PHY we can change into D2
+ */
+ if (pdata->pmt_ctrl & PMT_CTRL_WOL_EN_) {
+ printk_pmdbg("[ SUSP ] Preparing D1 with wakeup\n");
+ smsc911x_drv_state_wakeup(pdata, 1);
+ } else {
+ /* @TEST: Use first only D1 for the wakups */
+ printk_pmdbg("[ SUSP ] Preparing D2 with wakeup\n");
+ smsc911x_drv_state_wakeup(pdata, 2);
+ }
+
+ enable_irq_wake(ndev->irq);
+
+ } else {
+ /*
+ * Enter into the power mode D2 (the controller doesn't
+ * support the mode D3)
+ */
+ smsc911x_drv_state_d2(pdata);
+ }
+ }
+
+ return 0;
+
+err_attach:
+ netif_device_attach(ndev);
+ return retval;
+}
+
+static int smsc911x_drv_resume(struct platform_device *pdev)
+{
+ int retval;
+ struct net_device *ndev;
+ unsigned long pmt_ctrl;
+
+ pmt_ctrl = 0;
+ ndev = platform_get_drvdata(pdev);
+ retval = 0;
+ if (ndev) {
+ struct smsc911x_data *pdata = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ unsigned long timeout;
+ unsigned long regval, pmt_ctrl;
+
+ /* Assert the byte test register for waking up */
+ smsc911x_reg_write(0x0, pdata, BYTE_TEST);
+
+ timeout = 100000;
+ do {
+ timeout--;
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ udelay(1);
+ } while (timeout && !(regval & PMT_CTRL_READY_));
+
+ if (!timeout) {
+ printk_err("Wakeup timeout by the controller\n");
+ retval = -EBUSY;
+ goto exit_resume;
+ }
+
+ /*
+ * Check if we received a PM interrupt
+ * Please take note that we are supporting ONLY the Wake On LAN
+ * interrupts, and not the energy-on
+ * (Luis Galdos)
+ */
+ pmt_ctrl = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval = smsc911x_reg_read(pdata, INT_STS);
+ printk_pmdbg("[ WAKE ] PMT_CTRL 0x%08lx\n", pmt_ctrl);
+ printk_pmdbg("[ WAKE ] INT_STS 0x%08lx\n", regval);
+ if (regval & (INT_STS_PME_INT_ | INT_STS_PHY_INT_)) {
+
+ /* Disable the power management interrupts */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ pmt_ctrl = regval & (PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_);
+ regval &= ~(PMT_CTRL_WOL_EN_ | PMT_CTRL_PME_EN_ |
+ PMT_CTRL_ED_EN_);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Disable the PM interrupts */
+ regval = smsc911x_reg_read(pdata, INT_EN);
+ regval &= ~(INT_EN_PME_INT_EN_ | INT_EN_PHY_INT_EN_);
+ smsc911x_reg_write(regval, pdata, INT_EN);
+
+ /* Disable the wakeup-events on the MAC */
+ regval = smsc911x_mac_read(pdata, WUCSR);
+ regval &= ~(WUCSR_MPEN_);
+ smsc911x_mac_write(pdata, WUCSR, regval);
+ }
+
+ /* @XXX: Clear only the interrupts that were generated */
+ regval = (INT_STS_PME_INT_ | INT_STS_PHY_INT_);
+ smsc911x_reg_write(regval, pdata, INT_STS);
+
+ /* Set the controller into the state D0 */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_PM_MODE_D0_;
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Paranoic sanity checks */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ if (regval & PMT_CTRL_PM_MODE_)
+ printk_err("PM mode isn't disabled (0x%04lx)\n", regval);
+
+ if (!(regval & PMT_CTRL_READY_)) {
+ retval = -EBUSY;
+ printk_err("Device is still NOT ready.\n");
+ goto exit_resume;
+ }
+
+ regval = smsc911x_phy_read(pdata, MII_BMCR);
+ regval &= ~BMCR_PDOWN;
+ smsc911x_phy_write(pdata, MII_BMCR, regval);
+
+ /* Reenable the interrupts now */
+ regval = smsc911x_reg_read(pdata, INT_CFG);
+ regval |= INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(regval, pdata, INT_CFG);
+
+ /* Reset the wakeup control and status register */
+ smsc911x_mac_write(pdata, WUCSR, 0x00);
+
+ netif_device_attach(ndev);
+ }
+ }
+
+exit_resume:
+ return retval;
+}
+#else
+#define smsc911x_drv_suspend NULL
+#define smsc911x_drv_resume NULL
+#endif /* defined(CONFIG_PM) */
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = smsc911x_drv_remove,
+ .suspend = smsc911x_drv_suspend,
+ .resume = smsc911x_drv_resume,
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ printk(KERN_INFO "SMSC 911X: Starting the platform driver.\n");
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/smsc9118/smsc911x.h b/drivers/net/smsc9118/smsc911x.h
new file mode 100644
index 000000000000..a2ee96aeb989
--- /dev/null
+++ b/drivers/net/smsc9118/smsc911x.h
@@ -0,0 +1,393 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************/
+#ifndef __SMSC911X_H__
+#define __SMSC911X_H__
+
+#if defined(CONFIG_MACH_CC9M2443JS) || defined(CONFIG_MACH_CCW9M2443JS)
+# define SMSC_CAN_USE_32BIT 0
+#else
+# define SMSC_CAN_USE_32BIT 1
+#endif
+
+
+//#define SMSC_CAN_USE_32BIT 1
+#define TX_FIFO_LOW_THRESHOLD (u32)1600
+#define SMSC911X_EEPROM_SIZE (u32)7
+#define USE_DEBUG 0
+//#define USE_DEBUG 2
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be succesfully looped back */
+#define USE_PHY_WORK_AROUND
+
+/* 10/100 LED link-state inversion when media is disconnected */
+/* #define USE_LED1_WORK_AROUND */
+
+/* platform_device configuration data, should be assigned to
+ * the platform_device's dev.platform_data */
+struct smsc911x_platform_config {
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+};
+
+#if USE_DEBUG >= 1
+#define SMSC_WARNING(fmt, args...) \
+ printk(KERN_EMERG "SMSC_WARNING: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_WARNING(msg, args...)
+#endif /* USE_DEBUG >= 1 */
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(fmt,args...) \
+ printk(KERN_EMERG "SMSC_TRACE: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_TRACE(msg, args...)
+#endif /* USE_DEBUG >= 2 */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_MCAST_ 0x00000400
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define ENDIAN 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
new file mode 100644
index 000000000000..6af63187bd3a
--- /dev/null
+++ b/drivers/net/smsc911x.c
@@ -0,0 +1,2253 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ *
+ * History:
+ * 05/05/2005 bahadir.balban@arm.com
+ * - Transition to linux coding style
+ * - Platform driver and module interface
+ *
+ * 17/07/2006 steve.glendinning@smsc.com
+ * - Added support for LAN921x family
+ * - Added workaround for multicast filters
+ *
+ * 31/07/2006 steve.glendinning@smsc.com
+ * - Removed tasklet, using NAPI poll instead
+ * - Multiple device support
+ * - Large tidy-up following feedback from netdev list
+ *
+ * 03/08/2006 steve.glendinning@smsc.com
+ * - Added ethtool support
+ * - Convert to use generic MII interface
+ *
+ * 04/08/2006 bahadir.balban@arm.com
+ * - Added ethtool eeprom r/w support
+ *
+ * 17/06/2007 steve.glendinning@smsc.com
+ * - Incorporate changes from Bill Gatliff and Russell King
+ *
+ * 04/07/2007 steve.glendinning@smsc.com
+ * - move irq configuration to platform_device
+ * - fix link poller after interface is stopped and restarted
+ *
+ * 13/07/2007 bahadir.balban@arm.com
+ * - set irq polarity before requesting irq
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_DRV_VERSION "2007-07-13"
+
+MODULE_LICENSE("GPL");
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+ unsigned int generation; /* used to decide which workarounds apply */
+
+ /* device configuration */
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ * smsc911x_phy_read(), smsc911x_phy_write()
+ */
+ spinlock_t phy_lock;
+
+ struct net_device_stats stats;
+ struct mii_if_info mii;
+ unsigned int using_extphy;
+ u32 msg_enable;
+#ifdef USE_LED1_WORK_AROUND
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+#endif
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct timer_list link_poll_timer;
+ unsigned int stop_link_poll;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+};
+
+#if SMSC_CAN_USE_32BIT
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ return readl(pdata->ioaddr + reg);
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ writel(val, pdata->ioaddr + reg);
+}
+
+#elif SMSC_CAN_USE_SPI
+
+static u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ reg_val = spi_cpld_read(reg);
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ spi_cpld_write(reg, val);
+ local_irq_restore(flags);
+}
+
+#else /* SMSC_CAN_USE_32BIT */
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ /* these two 16-bit reads must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ reg_val = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ /* these two 16-bit writes must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ local_irq_restore(flags);
+}
+
+#endif /* SMSC_CAN_USE_32BIT */
+
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ smsc911x_reg_write(*buf++, pdata, TX_DATA_FIFO);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+}
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes phy_lock is held */
+static int smsc911x_mac_notbusy(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 1;
+ }
+ SMSC_WARNING("Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return 0;
+}
+
+/* Fetches a MAC register value. Assumes phy_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_
+ | MAC_CSR_CMD_R_NOT_W_), pdata, MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to happen */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, phy_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(val, pdata, MAC_CSR_DATA);
+
+ /* Write the actual data */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_), pdata,
+ MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return;
+
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Gets a phy register, phy_lock must be acquired before calling */
+static u16 smsc911x_phy_read(struct smsc911x_data *pdata, unsigned int index)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_phy_read???");
+ return 0;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11)
+ | ((index & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ return smsc911x_mac_read(pdata, MII_DATA);
+ }
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+ return 0xFFFF;
+}
+
+/* Sets a phy register, phy_lock must be acquired before calling */
+static void smsc911x_phy_write(struct smsc911x_data *pdata,
+ unsigned int index, u16 val)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_write_phy???");
+ return;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11) |
+ ((index & 0x1F) << 6) | MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_))
+ return;
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+}
+
+static int smsc911x_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ int reg;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ reg = smsc911x_phy_read(pdata, location);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return reg;
+}
+
+static void smsc911x_mdio_write(struct net_device *dev, int phy_id,
+ int location, int val)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, location, val);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
+ * If something goes wrong, returns -ENODEV to revert back to internal phy.
+ * Performed at initialisation only, so interrupts are enabled */
+static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int address;
+ unsigned int hwcfg;
+ unsigned int phyid1;
+ unsigned int phyid2;
+
+ hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* External phy is requested, supported, and detected */
+ if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+
+ /* Attempt to switch to external phy for auto-detecting
+ * its address. Assuming tx and rx are stopped because
+ * smsc911x_phy_initialise is called before
+ * smsc911x_rx_initialise and tx_initialise.
+ */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Auto-detect PHY */
+ spin_lock_irq(&pdata->phy_lock);
+ for (address = 0; address <= 31; address++) {
+ pdata->mii.phy_id = address;
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ if ((phyid1 != 0xFFFFU) || (phyid2 != 0xFFFFU)) {
+ SMSC_TRACE("Detected PHY at address = "
+ "0x%02X = %d", address, address);
+ break;
+ }
+ }
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFFU) && (phyid2 == 0xFFFFU)) {
+ SMSC_WARNING("External PHY is not accessable, "
+ "using internal PHY instead");
+ /* Revert back to internal phy settings. */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to internal phy */
+ hwcfg &= (~HW_CFG_EXT_PHY_EN_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_INT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg &= (~HW_CFG_SMI_SEL_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ /* Use internal phy */
+ return -ENODEV;
+ } else {
+ SMSC_TRACE("Successfully switched to external PHY");
+ pdata->using_extphy = 1;
+ }
+ } else {
+ SMSC_WARNING("No external PHY detected.");
+ SMSC_WARNING("Using internal PHY instead.");
+ /* Use internal phy */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* called by phy_initialise and loopback test */
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+ unsigned int i = 100000;
+ unsigned long flags;
+
+ SMSC_TRACE("Performing PHY BCR Reset");
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_RESET);
+ do {
+ udelay(10);
+ temp = smsc911x_phy_read(pdata, MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe
+ */
+ msleep(1);
+
+ return 1;
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ u32 bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (((unsigned int)pdata->loopback_tx_pkt)
+ & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(txcmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(txcmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32) pdata->loopback_tx_pkt) & 0xFFFFFFFC;
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (((u32) pdata->loopback_tx_pkt) & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to transmit during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARNING("Transmit encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARNING("Receive encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (u32)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += ((u32)pdata->loopback_rx_pkt) & 0x3;
+ rdsz >>= 2;
+
+ smsc911x_rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARNING("Unexpected packet size during "
+ "loop back test, size=%d, "
+ "will retry", pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE("Successfully verified "
+ "loopback packet");
+ return 1;
+ } else {
+ SMSC_WARNING("Data miss match during "
+ "loop back test, will retry.");
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct smsc911x_data *pdata)
+{
+ int result = 0;
+ unsigned int i;
+ unsigned int val;
+ unsigned long flags;
+
+ /* Initialise tx packet */
+ for (i = 0; i < 6; i++) {
+ /* Use broadcast destination address */
+ pdata->loopback_tx_pkt[i] = (char)0xFF;
+ }
+
+ for (i = 6; i < 12; i++) {
+ /* Use incrementing source address */
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+ for (i = 14; i < MIN_PACKET_SIZE; i++) {
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(val, pdata, HW_CFG);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+ smsc911x_reg_write((((unsigned int)pdata->loopback_rx_pkt)
+ & 0x03) << 8, pdata, RX_CFG);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, 0x4100);
+
+ /* Enable MAC tx/rx, FD */
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata)) {
+ result = 1;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_phy_write(pdata, MII_BMCR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(0, pdata, TX_CFG);
+ smsc911x_reg_write(0, pdata, RX_CFG);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+/* assumes phy_lock is held */
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+
+ if (pdata->mii.full_duplex) {
+ unsigned int phy_adv;
+ unsigned int phy_lpa;
+ phy_adv = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ phy_lpa = smsc911x_phy_read(pdata, MII_LPA);
+ if (phy_adv & phy_lpa & LPA_PAUSE_CAP) {
+ /* Both ends support symmetric pause, enable
+ * PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else if (((phy_adv & ADVERTISE_PAUSE_ALL) ==
+ ADVERTISE_PAUSE_ALL) &&
+ ((phy_lpa & LPA_PAUSE_ALL) == LPA_PAUSE_ASYM)) {
+ /* We support symmetric and asym pause, the
+ * other end only supports asym, Enable PAUSE
+ * receive, disable PAUSE transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else {
+ /* Disable PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+ } else {
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+}
+
+/* Update link mode if any thing has changed */
+static void smsc911x_phy_update_linkmode(struct net_device *dev, int init)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (mii_check_media(&pdata->mii, netif_msg_link(pdata), init)) {
+ /* duplex state has changed */
+ unsigned int mac_cr;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (pdata->mii.full_duplex) {
+ SMSC_TRACE("configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE("configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ smsc911x_phy_update_flowcontrol(pdata);
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ }
+#ifdef USE_LED1_WORK_AROUND
+ if (netif_carrier_ok(dev)) {
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore orginal GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ } else {
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
+ && (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * orginal GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ }
+#endif /* USE_LED1_WORK_AROUND */
+}
+
+/* Entry point for the link poller */
+static void smsc911x_phy_checklink(unsigned long ptr)
+{
+ struct net_device *dev = (struct net_device *)ptr;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_phy_update_linkmode(dev, 0);
+
+ if (!(pdata->stop_link_poll)) {
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+ } else {
+ pdata->stop_link_poll = 0;
+ }
+}
+
+/* Initialises the PHY layer. Called at initialisation by open() so
+ * interrupts are enabled */
+static int smsc911x_phy_initialise(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int phyid1 = 0;
+ unsigned int phyid2 = 0;
+ unsigned int temp;
+
+ pdata->using_extphy = 0;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ /* External PHY supported, try to autodetect */
+ if (smsc911x_phy_initialise_external(pdata) < 0) {
+ SMSC_TRACE("External PHY is not detected, using "
+ "internal PHY instead");
+ pdata->mii.phy_id = 1;
+ }
+ break;
+ default:
+ SMSC_TRACE("External PHY is not supported, using internal PHY "
+ "instead");
+ pdata->mii.phy_id = 1;
+ break;
+ }
+
+ spin_lock_irq(&pdata->phy_lock);
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFF) && (phyid2 == 0xFFFF)) {
+ SMSC_WARNING("Internal PHY not detected!");
+ return 0;
+ }
+
+ /* Reset the phy */
+ if (!smsc911x_phy_reset(pdata)) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+#ifdef USE_PHY_WORK_AROUND
+ if (!smsc911x_phy_loopbacktest(pdata)) {
+ SMSC_WARNING("Failed Loop Back Test");
+ return 0;
+ } else {
+ SMSC_TRACE("Passed Loop Back Test");
+ }
+#endif /* USE_PHY_WORK_AROUND */
+
+ /* Advertise all speeds and pause capabilities */
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ temp |= (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ smsc911x_phy_write(pdata, MII_ADVERTISE, temp);
+ pdata->mii.advertising = temp;
+
+ if (!pdata->using_extphy) {
+ /* using internal phy, enable PHY interrupts */
+ smsc911x_phy_read(pdata, MII_INTSTS);
+ smsc911x_phy_write(pdata, MII_INTMSK, PHY_INTMSK_DEFAULT_);
+ }
+
+ /* begin to establish link */
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_phy_update_linkmode(dev, 1);
+
+ setup_timer(&pdata->link_poll_timer, smsc911x_phy_checklink,
+ (unsigned long)dev);
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+
+ SMSC_TRACE("phy initialised succesfully");
+ return 1;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ unsigned int result = (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+ return result;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct smsc911x_data *pdata)
+{
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARNING("Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & 0x00008000)) {
+ pdata->stats.tx_errors++;
+ } else {
+ pdata->stats.tx_packets++;
+ pdata->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & 0x00000100)) {
+ pdata->stats.collisions += 16;
+ pdata->stats.tx_aborted_errors += 1;
+ } else {
+ pdata->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & 0x00000800)) {
+ pdata->stats.tx_carrier_errors += 1;
+ }
+ if (unlikely(tx_stat & 0x00000200)) {
+ pdata->stats.collisions++;
+ pdata->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct smsc911x_data *pdata, unsigned int rxstat)
+{
+ int crc_err = 0;
+
+ if (unlikely(rxstat & 0x00008000)) {
+ pdata->stats.rx_errors++;
+ if (unlikely(rxstat & 0x00000002)) {
+ pdata->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
+ /* Frame type indicates length,
+ * and length error is set */
+ pdata->stats.rx_length_errors++;
+ }
+ if (rxstat & RX_STS_MCAST_)
+ pdata->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(RX_DP_CTRL_RX_FFWD_, pdata, RX_DP_CTRL);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING("Timed out waiting for RX FFWD "
+ "to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata = container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->netdev;
+ int npackets = 0;
+
+ while (npackets < budget) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ /* break out of while loop if there are no more packets waiting */
+ if (!rxstat)
+ break;
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ smsc911x_rx_counterrors(pdata, rxstat);
+
+ if (likely((rxstat & RX_STS_ES_) == 0)) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
+ if (likely(skb)) {
+ skb->data = skb->head;
+ skb->tail = skb->head;
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ smsc911x_rx_readfifo(pdata,
+ (unsigned int *)skb->head,
+ pktwords);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ pdata->stats.rx_packets++;
+ pdata->stats.rx_bytes += (pktlength - 4);
+ dev->last_rx = jiffies;
+ npackets++;
+ continue;
+ } else {
+ SMSC_WARNING("Unable to allocate sk_buff "
+ "for rx packet, in PIO path");
+ pdata->stats.rx_dropped++;
+ }
+ }
+ /* At this point, the packet is to be read out
+ * of the fifo and discarded */
+ smsc911x_rx_fastforward(pdata, pktlength);
+ }
+
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);
+
+ if (npackets < budget) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ netif_rx_complete(dev, napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ }
+
+ /* There are still packets waiting */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ unsigned int crc;
+ unsigned int result;
+
+ crc = ether_crc(ETH_ALEN, addr);
+ result = (crc >> 26) & 0x3f;
+
+ return result;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the phy_lock held */
+ unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+ SMSC_TRACE("maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X", mac_cr,
+ pdata->hashhi, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->phy_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARNING("Rx not stopped\n");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->phy_lock);
+}
+
+/* Sets the device MAC address to dev_addr, called with phy_lock held */
+static void
+smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg;
+
+ spin_lock_init(&pdata->phy_lock);
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ SMSC_WARNING("Failed to complete reset");
+ return -ENODEV;
+ }
+
+ smsc911x_reg_write(0x00050000, pdata, HW_CFG);
+ smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((timeout--) &&
+ (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0)) {
+ SMSC_WARNING("Timed out waiting for EEPROM "
+ "busy bit to clear");
+ }
+#if USE_DEBUG >= 1
+ smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
+#else
+ smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
+#endif
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(0, pdata, INT_EN);
+ smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
+
+ /* Set interrupt deassertion to 100uS */
+ intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->irq_polarity) {
+ SMSC_TRACE("irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE("irq polarity: active low");
+ }
+
+ if (pdata->irq_type) {
+ SMSC_TRACE("irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE("irq type: open drain");
+ }
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ SMSC_TRACE("Testing irq handler using IRQ %d", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ timeout = 1000;
+ while (timeout--) {
+ smp_rmb();
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+ SMSC_TRACE("IRQ handler passed test using IRQ %d", dev->irq);
+
+ printk(KERN_INFO "%s: SMSC911x/921x identified at %#08lx, IRQ: %d\n",
+ dev->name, (unsigned long)pdata->ioaddr, dev->irq);
+
+ netif_carrier_off(dev);
+
+ if (!smsc911x_phy_initialise(dev)) {
+ SMSC_WARNING("Failed to initialize PHY");
+ return -ENODEV;
+ }
+
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ temp &= HW_CFG_TX_FIF_SZ_;
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(temp, pdata, HW_CFG);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write((2 << 8), pdata, RX_CFG);
+
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_PHY_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ napi_disable(&pdata->napi);
+
+ pdata->stop_link_poll = 1;
+ del_timer_sync(&pdata->link_poll_timer);
+
+ smsc911x_reg_write((smsc911x_reg_read(pdata, INT_CFG) &
+ (~INT_CFG_IRQ_EN_)), pdata, INT_CFG);
+ netif_stop_queue(dev);
+
+ /* At this point all Rx and Tx activity is stopped */
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(pdata);
+
+ SMSC_TRACE("Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ u32 bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARNING("Tx data fifo low, space available: %d",
+ freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32)skb->data) & 0xFFFFFFFC;
+ wrsz = (u32)skb->len + 3;
+ wrsz += ((u32)skb->data) & 0x3;
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ dev_kfree_skb(skb);
+ dev->trans_start = jiffies;
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(pdata);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(pdata);
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &pdata->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->mc_count > 0) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ unsigned int count = 0;
+ struct dev_mc_list *mc_list = dev->mc_list;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while (mc_list) {
+ count++;
+ if ((mc_list->dmi_addrlen) == ETH_ALEN) {
+ unsigned int bitnum =
+ smsc911x_hash(mc_list->dmi_addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ } else {
+ SMSC_WARNING("dmi_addrlen != 6");
+ }
+ mc_list = mc_list->next;
+ }
+ if (count != (unsigned int)dev->mc_count)
+ SMSC_WARNING("mc_count != dev->mc_count");
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE("scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RXSTOP_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int intsts;
+ unsigned int inten;
+ unsigned int temp;
+ int serviced = IRQ_NONE;
+
+ intsts = smsc911x_reg_read(pdata, INT_STS);
+ inten = smsc911x_reg_read(pdata, INT_EN);
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_SW_INT_, pdata, INT_STS);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE("RX Stop interrupt");
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ smsc911x_reg_write(INT_STS_TDFA_, pdata, INT_STS);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ smsc911x_reg_write(INT_STS_RXE_, pdata, INT_STS);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ /* Disable Rx interrupts and schedule NAPI poll */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ netif_rx_schedule(dev, &pdata->napi);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_PHY_INT_)) {
+ smsc911x_reg_write(INT_STS_PHY_INT_, pdata, INT_STS);
+ spin_lock(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_INTSTS);
+ spin_unlock(&pdata->phy_lock);
+ SMSC_TRACE("PHY interrupt, sts 0x%04X", (u16)temp);
+ smsc911x_phy_update_linkmode(dev, 0);
+ serviced = IRQ_HANDLED;
+ }
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+ unsigned long flags;
+
+ SMSC_TRACE("ioctl cmd 0x%x", cmd);
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = pdata->mii.phy_id;
+ return 0;
+ case SIOCGMIIREG:
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ data->val_out = smsc911x_phy_read(pdata, data->reg_num);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+ case SIOCSMIIREG:
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, data->reg_num, data->val_in);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+ }
+
+ SMSC_TRACE("unsupported ioctl cmd");
+ return -1;
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return mii_ethtool_gset(&pdata->mii, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_ethtool_sset(&pdata->mii, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strncpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strncpy(info->bus_info, dev->dev.bus_id, sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_nway_restart(&pdata->mii);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_CMD - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_CMD; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ for (i = MAC_CR; i <= WUCSR; i++)
+ data[j++] = smsc911x_mac_read(pdata, i);
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_phy_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(temp, pdata, GPIO_CFG);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE("op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(e2cmd, pdata, E2P_CMD);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ SMSC_TRACE("TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE("Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write((u32)data, pdata, E2P_DATA);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+};
+
+/* Initializing private device structures */
+static int smsc911x_init(struct net_device *dev)
+{
+ u32 mac_high16;
+ u32 mac_low32;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ SMSC_TRACE("Driver Parameters:");
+ SMSC_TRACE("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
+ SMSC_TRACE("IRQ: %d", dev->irq);
+ SMSC_TRACE("PHY will be autodetected.");
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARNING("pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ if (((pdata->idrev >> 16) & 0xFFFF) == (pdata->idrev & 0xFFFF)) {
+ SMSC_WARNING("idrev top 16 bits equal to bottom 16 bits, "
+ "idrev: 0x%08X", pdata->idrev);
+ SMSC_TRACE("This may mean the chip is set for 32 bit while "
+ "the bus is reading as 16 bit");
+ return -ENODEV;
+ }
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9118 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01170000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9117 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01160000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01150000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x118A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x117A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x116A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x115A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x92100000UL:
+ case 0x92110000UL:
+ case 0x92200000UL:
+ case 0x92210000UL:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ if (pdata->generation == 0)
+ SMSC_WARNING("This driver is not intended "
+ "for this chip revision");
+
+ ether_setup(dev);
+ dev->open = smsc911x_open;
+ dev->stop = smsc911x_stop;
+ dev->hard_start_xmit = smsc911x_hard_start_xmit;
+ dev->get_stats = smsc911x_get_stats;
+ dev->set_multicast_list = smsc911x_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+ dev->do_ioctl = smsc911x_do_ioctl;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, 64);
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is set to random_ether_addr");
+ }
+ }
+
+ printk(KERN_INFO
+ "%s: SMSC911x MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = smsc911x_poll_controller;
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+ pdata->mii.phy_id_mask = 0x1f;
+ pdata->mii.reg_num_mask = 0x1f;
+ pdata->mii.force_media = 0;
+ pdata->mii.full_duplex = 0;
+ pdata->mii.dev = dev;
+ pdata->mii.mdio_read = smsc911x_mdio_read;
+ pdata->mii.mdio_write = smsc911x_mdio_write;
+
+ pdata->msg_enable = NETIF_MSG_LINK;
+
+ return 0;
+}
+
+static int smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ SMSC_TRACE("Stopping driver.");
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, res->end - res->start);
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static int smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+ unsigned int intcfg = 0;
+ int res_size;
+ int retval;
+
+ printk(KERN_INFO "%s: Driver version %s.\n", SMSC_CHIPNAME,
+ SMSC_DRV_VERSION);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_WARNING "%s: Could not allocate resource.\n",
+ SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = res->end - res->start;
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ printk(KERN_WARNING "%s: Could not allocate device.\n",
+ SMSC_CHIPNAME);
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+ pdata->netdev = dev;
+
+ dev->irq = platform_get_irq(pdev, 0);
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ /* copy config parameters across if present, otherwise pdata
+ * defaults to zeros */
+ if (pdev->dev.platform_data) {
+ struct smsc911x_platform_config *config = pdev->dev.platform_data;
+ pdata->irq_polarity = config->irq_polarity;
+ pdata->irq_type = config->irq_type;
+ }
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARNING("Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ if ((retval = smsc911x_init(dev)) < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->irq_polarity)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ if (pdata->irq_type)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler, IRQF_DISABLED,
+ SMSC_CHIPNAME, dev);
+ if (retval) {
+ SMSC_WARNING("Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARNING("Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE("Network interface: \"%s\"", dev->name);
+ }
+
+ return 0;
+
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, res->end - res->start);
+out_0:
+ return retval;
+}
+
+static int smsc911x_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_PDOWN);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(PMT_CTRL_PM_MODE_D2_, pdata, PMT_CTRL);
+
+ return 0;
+}
+
+static int smsc911x_drv_resume(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ unsigned int temp;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ smsc911x_reg_write(0xFFFFFFFF, pdata, BYTE_TEST);
+ while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_))
+ ;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+}
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = smsc911x_drv_remove,
+ .suspend = smsc911x_drv_suspend,
+ .resume = smsc911x_drv_resume,
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
new file mode 100644
index 000000000000..d67c9971b136
--- /dev/null
+++ b/drivers/net/smsc911x.h
@@ -0,0 +1,395 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************/
+#ifndef __SMSC911X_H__
+#define __SMSC911X_H__
+
+#if defined(CONFIG_MACH_MX37_3DS) || defined(CONFIG_MACH_MX25_3DS)
+#define SMSC_CAN_USE_SPI 1
+#define SMSC_CAN_USE_32BIT 0
+#elif defined(CONFIG_MACH_MX31_3DS) || defined(CONFIG_MACH_MX35_3DS)
+#define SMSC_CAN_USE_32BIT 0
+#define SMSC_CAN_USE_SPI 0
+#else
+#define SMSC_CAN_USE_32BIT 1
+#define SMSC_CAN_USE_SPI 0
+#endif
+
+#define TX_FIFO_LOW_THRESHOLD (u32)1600
+#define SMSC911X_EEPROM_SIZE (u32)7
+#define USE_DEBUG 0
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be succesfully looped back */
+#define USE_PHY_WORK_AROUND
+
+/* 10/100 LED link-state inversion when media is disconnected */
+#define USE_LED1_WORK_AROUND
+
+/* platform_device configuration data, should be assigned to
+ * the platform_device's dev.platform_data */
+struct smsc911x_platform_config {
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+};
+
+#if USE_DEBUG >= 1
+#define SMSC_WARNING(fmt, args...) \
+ printk(KERN_EMERG "SMSC_WARNING: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_WARNING(msg, args...)
+#endif /* USE_DEBUG >= 1 */
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(fmt,args...) \
+ printk(KERN_EMERG "SMSC_TRACE: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_TRACE(msg,args...)
+#endif /* USE_DEBUG >= 2 */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_MCAST_ 0x00000400
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define ENDIAN 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 45bdf0b339bb..46bcaca38b46 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -717,5 +717,44 @@ source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
+source "drivers/net/wireless/digiPiper/Kconfig"
+
+config DIGI_WI_G
+ tristate "Digi Wireless Module 802.11ab/g"
+ depends on WLAN_80211 && MACH_CCW9C
+ select IEEE80211
+ select IEEE80211_SOFTMAC
+ ---help---
+ A 802.11ab/g wireless driver for Digi ConnectCore Wi-9C modules.
+
+config DIGI_WI_G_HW_ENCRYPTION
+ bool "Use HW encryption"
+ depends on DIGI_WI_G
+ default y
+ help
+ AES/CCMP decoding is performed by using hardware logic instead of
+ software emulation. This can also be adjusted at run time with the
+ kernel command line option dw_sw_aes=0
+
+config DIGI_WI_G_DEBUG
+ bool "Debugging support"
+ depends on DIGI_WI_G
+ default n
+ help
+ The digi_wi_g driver is compiled with debugging support.
+ Only the driver maintainer may need to enable it.
+
+choice
+ prompt "UBEC Transceiver Revision"
+ depends on DIGI_WI_G
+ default DIGI_WI_G_UBEC_JD
+
+config DIGI_WI_G_UBEC_JD
+ bool "UBEC Revision JD"
+
+config DIGI_WI_G_UBEC_HC
+ bool "UBEC Revision HC"
+
+endchoice
endmenu
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 59d2d805f60b..5fcf43bbf8c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_B43LEGACY) += b43legacy/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
+obj-$(CONFIG_DIGI_PIPER_WIFI) += digiPiper/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
@@ -67,3 +68,4 @@ obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K) += ath9k/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
+obj-$(CONFIG_DIGI_WI_G) += digi_wi_g.o
diff --git a/drivers/net/wireless/digiPiper/Kconfig b/drivers/net/wireless/digiPiper/Kconfig
new file mode 100644
index 000000000000..aa0659f0e499
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Kconfig
@@ -0,0 +1,27 @@
+config DIGI_PIPER_WIFI
+ bool "Digi Piper Wifi support"
+ depends on MAC80211 && WLAN_80211 && I2C && (MACH_CCW9P9215JS || MACH_CCW9M2443JS)
+ ---help---
+ This driver is for the Piper 802.11 MAC by Digi. This MAC is
+ supported on the Digi ConnectCore Wi-9P 9215 and Digi ConnectCore Wi-9M 2443
+ embedded modules.
+
+config PIPER_STATUS_LED
+ bool "Enable GPIO for Wifi status LED"
+ depends on DIGI_PIPER_WIFI && MACH_CCW9M2443JS
+ default y
+ help
+ ConnectCore Wi-9M 2443 does not provide any dedicated LED
+ in the module for showing the Wifi status.
+ This option lets you define one available CPU GPIO for this purpose.
+ Default value is set to USER LED 1 on JumpStart board.
+
+config PIPER_STATUS_LED_GPIO
+ int "GPIO for Wifi status LED (0-144)"
+ range 0 144
+ depends on PIPER_STATUS_LED
+ default "141"
+ help
+ Set CPU GPIO for Wifi status LED.
+
+ Default: User LED 1 on JumpStart board (LE5)
diff --git a/drivers/net/wireless/digiPiper/Makefile b/drivers/net/wireless/digiPiper/Makefile
new file mode 100644
index 000000000000..a3686b942088
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Makefile
@@ -0,0 +1,16 @@
+
+mpiper-y := piper.o
+mpiper-y += airoha.o
+mpiper-y += digiDebug.o
+mpiper-y += digiIsr.o
+mpiper-y += digiTx.o
+mpiper-y += digiRx.o
+mpiper-y += digiMac80211.o
+mpiper-y += piperDsp.o
+mpiper-y += piperMacAssist.o
+mpiper-y += phy.o
+mpiper-y += adc121c027.o
+mpiper-y += airohaCalibration.o
+mpiper-y += digiPs.o
+obj-$(CONFIG_DIGI_PIPER_WIFI) := mpiper.o
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.c b/drivers/net/wireless/digiPiper/adc121c027.c
new file mode 100644
index 000000000000..17564bcd7a66
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.c
@@ -0,0 +1,161 @@
+/*
+ adc121C027.c - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "airohaCalibration.h"
+#include "pipermain.h"
+
+/* Addresses to scan: none, device is not autodetected */
+/* static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; */
+
+#define ADC_I2C_ADDR (0x51)
+#define ADC_CYCLE_TIME (0x20)
+
+static const unsigned short normal_i2c[] = { ADC_I2C_ADDR, I2C_CLIENT_END };
+static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr = {
+ .normal_i2c = normal_i2c,
+ .probe = dummy_i2c_addrlist,
+ .ignore = dummy_i2c_addrlist,
+};
+
+enum adc121C027_cmd {
+ ADC_RESULT = 0,
+ ADC_ALERT_STATUS = 1,
+ ADC_CONFIGURATION = 2,
+ ADC_LOW_LIMIT = 3,
+ ADC_HIGH_LIMIT = 4,
+ ADC_HYSTERESIS = 5,
+ ADC_LOWEST_VALUE = 6,
+ ADC_HIGHEST_VALUE = 7,
+};
+
+static u16 adc121C027_read_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_HIGHEST_VALUE));
+}
+
+static void adc121C027_clear_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_HIGHEST_VALUE, 0);
+}
+
+static u16 adc121C027_read_last_sample(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_RESULT));
+}
+
+static int adc121C027_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ return (client->addr != ADC_I2C_ADDR) ? -EINVAL : 0;
+}
+
+static int adc121C027_remove(struct i2c_client *client)
+{
+ /* Real shut down will be done by adc121C027_shutdown() */
+ return 0;
+}
+
+static const struct i2c_device_id adc121C027_id[] = {
+ { "adc121C027", 0 },
+ {}
+};
+
+static struct i2c_driver adc121C027_driver = {
+ .driver = {
+ .name = "adc121C027",
+ },
+ .probe = adc121C027_probe,
+ .remove = __devexit_p(adc121C027_remove),
+ .id_table = adc121C027_id,
+ .address_data = &addr,
+};
+
+/* Turn on automatic A/D process by setting a non zero cycle time */
+static void adc121C027_hw_init(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_CONFIGURATION, ADC_CYCLE_TIME);
+}
+
+void adc121C027_shutdown(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ if (i2cclient) {
+ i2c_unregister_device(i2cclient);
+ cal->priv = NULL;
+ }
+ i2c_del_driver(&adc121C027_driver);
+}
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter)
+{
+ struct i2c_board_info board_info = {
+ .type = "adc121C027",
+ .addr = ADC_I2C_ADDR,
+ };
+ struct i2c_adapter *adapter;
+ struct i2c_client *adc_i2c_client;
+ int ret;
+
+ ret = i2c_add_driver(&adc121C027_driver);
+ if (ret) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error adding driver adc121C027_driver (%d)\n", ret);
+ return ret;
+ }
+
+ adapter = i2c_get_adapter(i2cadapter);
+ if (!adapter) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error getting i2c adapter\n");
+ return -EINVAL;
+ }
+
+ adc_i2c_client = i2c_new_device(adapter, &board_info);
+ if (!adc_i2c_client) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error creating new i2c client\n");
+ return -EINVAL;
+ }
+
+ cal->priv = (void *)adc_i2c_client;
+ adc121C027_hw_init(cal);
+
+ cal->cops = kmalloc(sizeof(struct calibration_ops), GFP_KERNEL);
+ if (!cal->cops) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": unable to allocate memory for cal->cops\n");
+ return -ENOMEM;
+ }
+ cal->cops->adc_read_peak = adc121C027_read_peak;
+ cal->cops->adc_clear_peak = adc121C027_clear_peak;
+ cal->cops->adc_read_last_val = adc121C027_read_last_sample;
+ cal->cops->adc_shutdown = adc121C027_shutdown;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adc121C027_init);
+
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.h b/drivers/net/wireless/digiPiper/adc121c027.h
new file mode 100644
index 000000000000..1453d8a66963
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.h
@@ -0,0 +1,17 @@
+/*
+ adc121C027.h - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+*/
+
+#ifndef ADC121C027_H
+#define ADC121C027_H
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter);
+
+#endif /* ADC121C027_H */
+
diff --git a/drivers/net/wireless/digiPiper/airoha.c b/drivers/net/wireless/digiPiper/airoha.c
new file mode 100644
index 000000000000..17ef8a812f98
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.c
@@ -0,0 +1,986 @@
+/*
+ * Ubec AH7230 radio support.
+ *
+ * Copyright © 2009 Digi International, Inc
+ *
+ * Author: Contact support@digi.com for information about this software.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <net/mac80211.h>
+#include <net/wireless.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airohaCalibration.h"
+#include "airoha.h"
+
+/*
+ * Number of us to change channels. I counted the number of udelays once
+ * and it was about 2030, plus the 1 us delays for each register write.
+ * So probably about 2200 in reality, I'm saying 2500 to be safe.
+ */
+#define CHANNEL_CHANGE_TIME (2500)
+
+/*
+ * Maximum possible receive signal strength in dbm. Most of the
+ * values will be negative.
+ */
+#define MAX_SIGNAL_IN_DBM (5)
+
+#define read_reg(reg) priv->ac->rd_reg(priv,reg)
+#define write_reg(reg,val,op) priv->ac->wr_reg(priv,reg,val,op)
+#define mac_set_tx_power(x) al7230_set_txpwr(hw,x)
+
+static unsigned int hw_revision = WCD_HW_REV_A;
+static unsigned int hw_platform = WCD_CCW9P_PLATFORM;
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection);
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t val);
+
+static const struct {
+ unsigned int integer;
+ unsigned int fraction;
+ unsigned int address4;
+ unsigned int tracking;
+} freqTableAiroha_7230[] = {
+ { 0, 0, 0, 0 }, // 0
+
+ // 2.4 GHz band (802.11b/g)
+ { 0x00379, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-1 (2412 MHz) 1
+ { 0x00379, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-2 (2417 MHz) 2
+ { 0x00379, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-3 (2422 MHz) 3
+ { 0x00379, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-4 (2427 MHz) 4
+ { 0x0037A, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-5 (2432 MHz) 5
+ { 0x0037A, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-6 (2437 MHz) 6
+ { 0x0037A, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-7 (2442 MHz) 7
+ { 0x0037A, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-8 (2447 MHz) 8
+ { 0x0037B, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-9 (2452 MHz) 9
+ { 0x0037B, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-10 (2457 MHz) 10
+ { 0x0037B, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-11 (2462 MHz) 11
+ { 0x0037B, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-12 (2467 MHz) 12
+ { 0x0037C, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-13 (2472 MHz) 13
+ { 0x0037C, 0x06666, 0x7FD78, TRACK_BG_BAND }, // B-14 (2484 MHz) 14
+
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 15
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 16
+
+ // Extended 4 GHz bands (802.11a) - Lower Band
+ { 0x0FF52, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-184 (4920 MHz) 17
+ { 0x0FF52, 0x0AAAA, 0x77F78, TRACK_4920_4980_A_BAND }, // L-188 (4940 MHz) 18
+ { 0x0FF53, 0x15555, 0x77F78, TRACK_4920_4980_A_BAND }, // L-192 (4960 MHz) 19
+ { 0x0FF53, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-196 (4980 MHz) 20
+
+ // Extended 5 GHz bands (802.11a)
+ { 0x0FF54, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-8 (5040 MHz) 21 tracking?
+ { 0x0FF54, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-12 (5060 MHz) 22 tracking?
+ { 0x0FF55, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-16 (5080 MHz) 23 tracking?
+ { 0x0FF56, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-34 (5170 MHz) 24
+ { 0x0FF56, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-36 (5180 MHz) 25
+ { 0x0FF57, 0x10000, 0x77F78, TRACK_5150_5350_A_BAND }, // A-38 (5190 MHz) 26
+ { 0x0FF57, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-40 (5200 MHz) 27
+ { 0x0FF57, 0x1AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-42 (5210 MHz) 28
+ { 0x0FF57, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-44 (5220 MHz) 29
+ { 0x0FF57, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-46 (5230 MHz) 30
+ { 0x0FF57, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-48 (5240 MHz) 31
+
+ { 0x0FF58, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-52 (5260 MHz) 32
+ { 0x0FF58, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-56 (5280 MHz) 33
+ { 0x0FF58, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-60 (5300 MHz) 34
+ { 0x0FF59, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-64 (5320 MHz) 35
+
+ { 0x0FF5C, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-100 (5500 MHz) 36
+ { 0x0FF5C, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-104 (5520 MHz) 37
+ { 0x0FF5C, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-108 (5540 MHz) 38
+ { 0x0FF5D, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-112 (5560 MHz) 39
+ { 0x0FF5D, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-116 (5580 MHz) 40
+ { 0x0FF5D, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-120 (5600 MHz) 41
+ { 0x0FF5E, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-124 (5620 MHz) 42
+ { 0x0FF5E, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-128 (5640 MHz) 43
+ { 0x0FF5E, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-132 (5660 MHz) 44
+ { 0x0FF5F, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-136 (5680 MHz) 45
+ { 0x0FF5F, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-140 (5700 MHz) 46
+
+ { 0x0FF60, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-149 (5745 MHz) 47
+ { 0x0FF60, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-153 (5765 MHz) 48
+ { 0x0FF60, 0x0D555, 0x77F78, TRACK_5725_5825_A_BAND }, // A-157 (5785 MHz) 49
+ { 0x0FF61, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-161 (5805 MHz) 50
+ { 0x0FF61, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-165 (5825 MHz) 51
+};
+
+#define CHAN4G(idx, _freq) \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = idx, \
+ .max_antenna_gain = 0, \
+ .max_power = 12
+
+static struct ieee80211_channel al7230_bg_channels[] = {
+ { CHAN4G(1, 2412) },
+ { CHAN4G(2, 2417) },
+ { CHAN4G(3, 2422) },
+ { CHAN4G(4, 2427) },
+ { CHAN4G(5, 2432) },
+ { CHAN4G(6, 2437) },
+ { CHAN4G(7, 2442) },
+ { CHAN4G(8, 2447) },
+ { CHAN4G(9, 2452) },
+ { CHAN4G(10, 2457) },
+ { CHAN4G(11, 2462) },
+ { CHAN4G(12, 2467) },
+ { CHAN4G(13, 2472) },
+ { CHAN4G(14, 2484) },
+};
+
+static const struct ieee80211_rate al7230_bg_rates[] = {
+ /* psk/cck rates */
+ {
+ .bitrate = 10,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 20,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 55,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 110,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+#define CHAN5G(idx, frequency) \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = frequency, \
+ .max_antenna_gain = 0, \
+ .max_power = 8, \
+ .hw_value = idx
+
+static struct ieee80211_channel al7230_a_channels[] = {
+ { CHAN5G(17, 4920) },
+ { CHAN5G(18, 4940) },
+ { CHAN5G(19, 4960) },
+ { CHAN5G(20, 4980) },
+
+ { CHAN5G(21, 5040) },
+ { CHAN5G(22, 5060) },
+ { CHAN5G(23, 5080) },
+ { CHAN5G(24, 5170) },
+ { CHAN5G(25, 5180) },
+ { CHAN5G(26, 5190) },
+ { CHAN5G(27, 5200) },
+ { CHAN5G(28, 5210) },
+ { CHAN5G(29, 5220) },
+ { CHAN5G(30, 5230) },
+ { CHAN5G(31, 5240) },
+
+ { CHAN5G(32, 5260) },
+ { CHAN5G(33, 5280) },
+ { CHAN5G(34, 5300) },
+ { CHAN5G(35, 5320) },
+
+ { CHAN5G(36, 5500) },
+ { CHAN5G(37, 5520) },
+ { CHAN5G(38, 5540) },
+ { CHAN5G(39, 5560) },
+ { CHAN5G(40, 5580) },
+ { CHAN5G(41, 5600) },
+ { CHAN5G(42, 5620) },
+ { CHAN5G(43, 5640) },
+ { CHAN5G(44, 5660) },
+ { CHAN5G(45, 5680) },
+ { CHAN5G(46, 5700) },
+
+ { CHAN5G(47, 5745) },
+ { CHAN5G(48, 5765) },
+ { CHAN5G(49, 5785) },
+ { CHAN5G(50, 5805) },
+ { CHAN5G(51, 5825) }
+};
+
+static const struct ieee80211_rate al7230_a_rates[] = {
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+static enum ieee80211_band getBand(int channelIndex)
+{
+ enum ieee80211_band result;
+
+ if (channelIndex >= BAND_A_OFFSET) {
+ result = IEEE80211_BAND_5GHZ;
+ } else {
+ result = IEEE80211_BAND_2GHZ;
+ }
+
+ return result;
+}
+
+static int getFrequency(int channelIndex)
+{
+ int result;
+
+ if (getBand(channelIndex) == IEEE80211_BAND_5GHZ) {
+ result = al7230_a_channels[channelIndex - BAND_A_OFFSET].center_freq;
+ } else {
+ result = al7230_bg_channels[channelIndex - 1].center_freq;
+ }
+
+ return result;
+}
+
+static int write_rf(struct ieee80211_hw *hw, unsigned char reg, unsigned int val)
+{
+ struct piper_priv *priv = hw->priv;
+ int err;
+
+ err = write_reg(BB_SPI_DATA, val << 4 | reg, op_write);
+ udelay(3); /* Mike Schaffner says to allow 2 us or more between all writes */
+ return err;
+}
+
+
+/*
+ * This function is called to set the value of Airoha register
+ * 0xc. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board
+ * design.
+ */
+static void set_hw_specific_parameters(struct ieee80211_hw *hw,
+ unsigned int band,
+ unsigned int hw_revision,
+ unsigned int hw_platform)
+{
+ switch (hw_platform) {
+ case WCD_CCW9P_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x2b);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0xa3);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x70);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int al7230_rf_set_chan_private(struct ieee80211_hw *hw, int channelIndex, bool enable_rx)
+{
+ struct piper_priv *priv = hw->priv;
+ static int rf_band;
+#ifdef WANT_DEBUG
+ const char *channelLookup[] = {
+ "invalid 0",
+ "B-1",
+ "B-2",
+ "B-3",
+ "B-4",
+ "B-5",
+ "B-6",
+ "B-7",
+ "B-8",
+ "B-9",
+ "B-10",
+ "B-11",
+ "B-12",
+ "B-13",
+ "B-14",
+ "invalid 15",
+ "invalid 16",
+ "L-184",
+ "L-188",
+ "L-192",
+ "L-196",
+ "A-8",
+ "A-12",
+ "A-16",
+ "A-34",
+ "A-36",
+ "A-38",
+ "A-40",
+ "A-42",
+ "A-44",
+ "A-46",
+ "A-48",
+ "A-52",
+ "A-56",
+ "A-60",
+ "A-64",
+ "A-100",
+ "A-104",
+ "A-108",
+ "A-112",
+ "A-116",
+ "A-120",
+ "A-124",
+ "A-128",
+ "A-132",
+ "A-136",
+ "A-140",
+ "A-149",
+ "A-153",
+ "A-157",
+ "A-161",
+ "A-165"
+ };
+printk(KERN_ERR "Setting channel %s\n", channelLookup[channelIndex]);
+#endif
+ if (channelIndex >= BAND_A_OFFSET)
+ rf_band = IEEE80211_BAND_5GHZ;
+ else
+ rf_band = IEEE80211_BAND_2GHZ;
+ /* Disable the rx processing path */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000880, op_or);
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+/* TODO, when using this transceiver, resolve this commented code */
+#ifdef BUILD_THIS_CODE_SECTION
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_B_EN, op_or);
+
+ if (macParams.band == WLN_BAND_B) {
+ /* turn off OFDM */
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* turn on OFDM */
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+ }
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ write_rf(hw, 0, freqTableAiroha_2236[channelIndex].integer);
+ write_rf(hw, 1, freqTableAiroha_2236[channelIndex].fraction);
+
+ /* critical delay for correct calibration */
+ udelay(150);
+
+ /*
+ * TXON, PAON and RXON should all be low before Calibration
+ * TXON and PAON will be low as long as no frames are written to the TX
+ * DATA fifo.
+ * RXON will be low as long as the receive path is not enabled (bit 0 of
+ * GEN CTL register is 0).
+ */
+
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87);
+ udelay(50);
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787);
+ udelay(50);
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+
+ /*Re-enable the rx processing path */
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+#endif
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ /* enable the frequency-band specific PA */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ //HW_GEN_CONTROL &= ~GEN_PA_ON;
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN | BB_GENERAL_STAT_B_EN,
+ op_or);
+
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ } else {
+ //HW_GEN_CONTROL |= GEN_PA_ON;
+
+ // turn off PSK/CCK
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_B_EN, op_and);
+
+ // turn on OFDM
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+
+ /* Set the 802.11a frequency sub-band specific tracking constant */
+ /* All 8 supported 802.11a channels are in this 802.11a frequency sub-band */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, freqTableAiroha_7230[channelIndex].tracking,
+ op_or);
+ }
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ /* Set the channel frequency */
+ write_rf(hw, 0, freqTableAiroha_7230[channelIndex].integer);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 1, freqTableAiroha_7230[channelIndex].fraction);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 4, freqTableAiroha_7230[channelIndex].address4);
+ udelay(150); /* Mike Schaffner says this is needed here */
+
+
+ // Select the frequency band: 5Ghz or 2.4Ghz
+ if (rf_band == IEEE80211_BAND_5GHZ) {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ /*
+ * This bit always as to be turned off when we are using
+ * the Airoha chip, even though it's named the 5G EN bit.
+ * It has to do with how they hooked up the Airoha.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ } else {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ /*
+ * No short preambles allowed for ODFM.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+
+ /*Re-enable the rx processing path */
+ if (enable_rx)
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+
+ /* re-enable transmitter */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This is a patch for a problem which should be corrected in
+ * hardware on new units. We are rewriting the MAC address
+ * because on units without the H/W patch the address can
+ * be corrupted when we change channels.
+ */
+ piper_set_macaddr(priv);
+digiWifiDumpRegisters(priv, MAIN_REGS);
+
+ return 0;
+}
+
+static int al7230_rf_set_chan(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, true);
+}
+
+static int al7230_rf_set_chan_no_rx(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, false);
+}
+
+
+
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t value)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ const unsigned char powerTable_2236[] = {
+ 4, 10, 10, 18, 22, 22, 28, 28,
+ 33, 33, 36, 38, 40, 43, 45, 47
+ };
+ write_rf(hw, 9, 0x05440 | powerTable_2236[value & 0xf]);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ const unsigned char powerTable_7230[] = {
+ 0x14, 0x14, 0x14, 0x18, 0x18, 0x1c, 0x1c, 0x20,
+ 0x20, 0x24, 0x24, 0x29, 0x29, 0x2c, 0x2c, 0x30
+ };
+ int correctedPowerIndex = digiWifiCalibrationPowerIndex(priv);
+
+ if (correctedPowerIndex != -1) {
+ write_rf(hw, 11, 0x08040 | correctedPowerIndex);
+ } else {
+ write_rf(hw, 11, 0x08040 | powerTable_7230[value & 0xf]);
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void al7230_set_power_index(struct ieee80211_hw *hw, unsigned int value)
+{
+ write_rf(hw, 11, 0x08040 | value);
+}
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ digi_dbg("**** transceiver == RF_AIROHA_2236\n");
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+
+ /* CH_integer: Frequency register 0 */
+ write_rf(hw, 0, 0x01f79 );
+
+ /* CH_fraction: Frequency register 1 */
+ write_rf(hw, 1, 0x03333 );
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x00B80 );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x00E7F );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x0905A );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x0F4DC );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x0805B );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0x0116C );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x05B68 );
+
+ /* TX gain control for LA2236 */
+ write_rf(hw, 9, 0x05460 ); // sit at the middle
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0x001BB );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ write_rf(hw, 11, 0x000f9 );
+
+ /* PA current = default value */
+ write_rf(hw, 12, 0x039D8 );
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0x08000 );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x00587 );
+
+ //mac_set_tx_power (macParams.tx_power);
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+ udelay(150);
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587 );
+ udelay(50);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ switch (band_selection) {
+ case IEEE80211_BAND_2GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x00379 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x13333 );
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x841FF );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x3FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x7FD78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x802BF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0xE0040 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070);
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_2GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x1ABA8 );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8 );
+ udelay(50);
+
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG, op_or);
+ break;
+
+ case IEEE80211_BAND_5GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11a */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x0FF56 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x0AAAA );
+
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x451FE );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x5FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x67f78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x853FF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi value */
+ write_rf(hw, 10, 0xE0600 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070 );
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_5GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x12BAC );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC );
+ udelay(50);
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A, op_or);
+ break;
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ }
+}
+
+static int al7230_rf_stop(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+
+static void getOfdmBrs(int channelIndex, u64 brsBitMask, unsigned int *ofdm, unsigned int *psk)
+{
+ /*
+ * brsBitMask is a bit mask into the al7230_bg_rates array. Bit 0 refers
+ * to the first entry in the array, bit 1 the second, and so on. The first
+ * 4 bits/array entries refer to the PSK bit rates we support, the next 8
+ * bits/array entries refer to the OFDM rates we support. So the PSK BRS
+ * mask is bits 0-3, the OFDM bit mask is bits 4-11.
+ */
+
+ if (getBand(channelIndex) == IEEE80211_BAND_2GHZ)
+ {
+ *psk = brsBitMask & 0xf;
+ *ofdm = (brsBitMask & 0xff0) >> 4;
+ }
+ else
+ {
+ *psk = 0;
+ *ofdm = (brsBitMask & 0xff);
+ }
+}
+
+static struct ieee80211_supported_band al7230_bands[] = {
+ {
+ .band = IEEE80211_BAND_2GHZ,
+ .n_channels = ARRAY_SIZE(al7230_bg_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_bg_rates),
+ .channels = (struct ieee80211_channel *) al7230_bg_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_bg_rates,
+ },
+ {
+ .band = IEEE80211_BAND_5GHZ,
+ .n_channels = ARRAY_SIZE(al7230_a_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_a_rates),
+ .channels = (struct ieee80211_channel *) al7230_a_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_a_rates,
+ },
+};
+
+static const struct ieee80211_rate *getRate(unsigned int rateIndex)
+{
+ return &al7230_bg_rates[rateIndex];
+}
+
+
+/*
+ * This routine can power up or power down the airoha transceiver.
+ * When the transceiver is powered back up, you must delay 1 ms and
+ * then call the set channel routine to make it operational again.
+ */
+static void power_on(struct ieee80211_hw *hw, bool want_power_on)
+{
+ if (want_power_on) {
+ write_rf(hw, 15, 0x1ABA8 ); /* this is actually for 2 Ghz */
+ } else {
+ write_rf(hw, 15, 0x1ABAE );
+ }
+}
+
+static void al7230_set_hw_info(struct ieee80211_hw *hw, int channel,
+ u16 hw_platform_code)
+{
+ hw_revision = hw_platform_code & WCD_HW_REV_MASK;
+ hw_platform = (hw_platform_code & WCD_PLATFORM_MASK);
+
+ set_hw_specific_parameters(hw, getBand(channel), hw_revision, hw_platform);
+}
+
+struct digi_rf_ops al7230_rf_ops = {
+ .name = "Airoha 7230",
+ .init = InitializeRF,
+ .stop = al7230_rf_stop,
+ .set_chan = al7230_rf_set_chan,
+ .set_chan_no_rx = al7230_rf_set_chan_no_rx,
+ .set_pwr = al7230_set_txpwr,
+ .set_pwr_index = al7230_set_power_index,
+ .set_hw_info = al7230_set_hw_info,
+ .channelChangeTime = CHANNEL_CHANGE_TIME,
+ .maxSignal = MAX_SIGNAL_IN_DBM,
+ .getOfdmBrs = getOfdmBrs,
+ .getBand = getBand,
+ .getFrequency = getFrequency,
+ .getRate = getRate,
+ .bands = al7230_bands,
+ .power_on = power_on,
+ .n_bands = ARRAY_SIZE(al7230_bands),
+};
+EXPORT_SYMBOL_GPL(al7230_rf_ops);
diff --git a/drivers/net/wireless/digiPiper/airoha.h b/drivers/net/wireless/digiPiper/airoha.h
new file mode 100644
index 000000000000..aa3b1d10fbe3
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.h
@@ -0,0 +1,30 @@
+#ifndef DIGI_RF_AH7230_H_
+#define DIGI_RF_AH7230_H_
+
+/* 0 is reserved for unknown transceiver */
+#define RF_AIROHA_7230 (1)
+#define RF_AIROHA_2236 (2)
+
+#define SPI_INIT_AIROHA (0x00000018) /* AIROHA-specific SPI length */
+#define SPI_INIT_AIROHA2236 (0x00000014) /* AIROHA 2236-specific SPI length */
+#define GEN_INIT_AIROHA_24GHZ (0x31720005) /* Initial state; 2.4GHZ_PA_ON= active low; bit 25 */
+#define GEN_INIT_AIROHA_50GHZ (0x33760008) /* Initial state; 5.0GHZ_PA_ON= active high; bit 25 */
+
+#define AIROHA_LOWEST_PSK_RATE_INDEX (0)
+#define AIROHA_LOWEST_OFDM_RATE_INDEX (4)
+#define AIROHA_55_MBPS_RATE_INDEX (2)
+
+/*
+ * Subtract this number from the channel index to index into
+ * the 802.11a channel array.
+ */
+#define BAND_A_OFFSET (17)
+
+struct digi_rf_freq {
+ uint16_t integer;
+ uint16_t fract;
+};
+
+extern struct digi_rf_ops al7230_rf_ops;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.c b/drivers/net/wireless/digiPiper/airohaCalibration.c
new file mode 100644
index 000000000000..9da912f320ba
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.c
@@ -0,0 +1,974 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free softbware; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/crc32.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "adc121c027.h"
+
+#define POWER_INDEX_STEP (1) /* TODO: come up with a rational value for this */
+
+#define SAMPLE_TIMEOUT (HZ * 10) /* TODO: What is a good sample timeout? Do we need one? */
+#define RECALIBRATION_PERIOD (HZ * 15) /* amount of time to wait between recalibrations */
+
+/*
+ * This defines the amount of time to wait for the power levels
+ * to settle down after making a large correction (user has
+ * changed power level).
+ */
+#define DEBOUNCE_DELAY (HZ * 2)
+
+#define MAX_TOLERATED_ERROR_HIGH (300) /* maximum over target we will allow */
+#define MAX_TOLERATED_ERROR_LOW (500) /* maximum under the target we will allow */
+#define CONVERT_TO_MDBM(x) (1000 * (x)) /* power levels are dBm externally, but dBm/1000 internally */
+
+#define NVRAM_WCAL_SIGNATURE "WCALDATA"
+
+#define MINIMUM_POWER_INDEX (10)
+
+/*
+ * Set piperp->calibrationTxRate to this value to make the
+ * transmit routine use the data rate specified by the
+ * mac80211 library.
+ */
+#define USE_MAC80211_DATA_RATE (NULL)
+
+/*
+ * Events we will wait for, also return values for waitForEvent().
+ */
+#define TIMED_OUT_EVENT (1 << 0)
+#define TRANSMIT_DONE_EVENT (1 << 1)
+#define SHUTDOWN_AUTOCALIBRATION_EVENT (1 << 2)
+#define RESTART_AUTOCALIBRATION_EVENT (1 << 3)
+
+/*
+ * Set this constant to (1) if you want to force calibration status to
+ * be printed.
+ */
+#define WANT_CALIBRATION_STATUS (0)
+
+
+static struct airohaCalibrationData calibration;
+
+static DECLARE_WAIT_QUEUE_HEAD(waitQueue);
+
+
+
+/*
+ * This routine is called to shut down the transmit ADC sampler.
+ */
+static void stopSampler(struct piper_priv *digi)
+{
+ digi->tx_calib_cb = NULL;
+}
+
+/*
+ * This routine is called to update the state of the calibration state machine
+ * and wake up its thread.
+ */
+static void kickCalibrationThread(struct piper_priv *digi, unsigned int event)
+{
+ unsigned long spinlockFlags;
+
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ calibration.events |= event;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+ wake_up_interruptible(&waitQueue);
+}
+
+
+/*
+ * This routine is called each time we complete a transmit while we are in
+ * the sampling state. We record the peak ADC reading. We kick the state
+ * machine if we now have all the samples we need.
+ */
+static void processSample(struct piper_priv *digi)
+{
+#define MINIMUM_ADC_VALUE (10) /* if ADC is below this value, it's probably bad */
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ /*
+ * Read the ADC value. It is a 12-bit value. We shift it 4 bits to
+ * create an 8-bit value.
+ */
+ calibration.sample[calibration.sampleCount].sample =
+ (calibration.cops->adc_read_peak(&calibration) >> 4);
+ if (calibration.sample[calibration.sampleCount].sample >
+ MINIMUM_ADC_VALUE) {
+ calibration.sampleCount++;
+ }
+ }
+}
+
+static void transmitHasCompleted(struct piper_priv *digi)
+{
+ stopSampler(digi);
+ kickCalibrationThread(digi, TRANSMIT_DONE_EVENT);
+}
+
+
+
+/*
+ * Determine the appropriate transmit rate to use during calibration.
+ */
+static struct ieee80211_rate *determineCalibrationTxRate(struct piper_priv
+ *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ struct ieee80211_rate *calibrationTxRate;
+
+ rates &= (MAC_PSK_BRS_MASK | MAC_OFDM_BRS_MASK);
+
+ if ((digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ)
+ && (rates & MAC_PSK_BRS_MASK)) {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_PSK_RATE_INDEX);
+ } else {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_OFDM_RATE_INDEX);
+ }
+
+ return calibrationTxRate;
+}
+
+
+
+
+/*
+ * Start collecting sample ADC peak measurements for calibration. Start
+ * the process by installing the callbacks which the transmit code will
+ * use to notify us when transmit frames go out.
+ */
+static void startSampleCollection(struct piper_priv *digi)
+{
+ calibration.cops->adc_clear_peak(&calibration);
+ digi->tx_calib_cb = transmitHasCompleted;
+}
+
+
+static unsigned int waitForEvent(unsigned int timeout, unsigned int eventToWaitFor)
+{
+#define ALL_EVENTS_TO_WAIT_FOR(x) (eventToWaitFor \
+ | SHUTDOWN_AUTOCALIBRATION_EVENT \
+ | RESTART_AUTOCALIBRATION_EVENT)
+
+ unsigned long spinlockFlags;
+ int ccode;
+ unsigned int event;
+ int result = TIMED_OUT_EVENT;
+
+ if (timeout != 0) {
+ ccode = wait_event_interruptible_timeout(waitQueue,
+ ((calibration.
+ events &
+ ALL_EVENTS_TO_WAIT_FOR
+ (eventToWaitFor))
+ != 0), timeout);
+ __set_current_state(TASK_RUNNING);
+
+ } else {
+ ccode = 0;
+ }
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ event = calibration.events;
+ calibration.events = 0;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+
+ if ((ccode < 0) || (event & SHUTDOWN_AUTOCALIBRATION_EVENT)) {
+ result = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ } else if (event & RESTART_AUTOCALIBRATION_EVENT) {
+ result = RESTART_AUTOCALIBRATION_EVENT;
+ } else if (event & eventToWaitFor) {
+ result = (event & eventToWaitFor);
+ } else {
+ result = TIMED_OUT_EVENT;
+ }
+
+ return result;
+}
+
+
+#ifdef WANT_CAL_DEBUG
+static void printPoint(wcd_point_t * p)
+{
+ printk("(%d, %d, %d)", p->out_power, p->adc_val, p->power_index);
+}
+#endif
+
+
+/*
+ * This routine finds the closest pair of points in a calibration curve.
+ *
+ * curve calibration curve
+ * value look for the pr of points closest to this value
+ * p1 storage for one point
+ * p2 storage for another point
+ * field tells us which field in the point struct to compare
+ */
+static void findClosestPoints(wcd_curve_t * curve, int value,
+ wcd_point_t ** p1, wcd_point_t ** p2, int field)
+{
+ if (value <= curve->points[0].out_power) {
+ *p1 = &curve->points[0];
+ *p2 = &curve->points[1];
+ } else if (value >=
+ curve->points[calibration.nvram->header.numcalpoints - 1].out_power) {
+ *p1 = &curve->points[calibration.nvram->header.numcalpoints - 2];
+ *p2 = &curve->points[calibration.nvram->header.numcalpoints - 1];
+ } else {
+ unsigned int idx;
+
+ for (idx = 1; idx < calibration.nvram->header.numcalpoints; idx++) {
+ if ((value < curve->points[idx].out_power)
+ || (idx == (calibration.nvram->header.numcalpoints - 1))) {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ break;
+ } else if (value == curve->points[idx].out_power) {
+ /*
+ * Note that the if statement befpre the for loop already tested for the
+ * value being equal to the first or last point in the curve, so we don't
+ * have to worry about that condition in the code below.
+ */
+ if ((value -
+ curve->points[idx - 1].out_power) >=
+ (curve->points[idx + 1].out_power - value)) {
+ /*
+ * If the two points are equal distant, then favor the larger pair
+ * because I think the values on the low end are screwy.
+ */
+ *p1 = &curve->points[idx];
+ *p2 = &curve->points[idx + 1];
+ } else {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Compute the slope of a curve between 2 points. The slope is the rise over the run,
+ * or (Y2 - Y1)/(X2 - X1). This function handles more than one type of slope.
+ */
+static int computeSlopeTimes1000(wcd_point_t * p1, wcd_point_t * p2, int slopeType)
+{
+ int slope = 0;
+ int divisor;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case ADC_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->adc_val - p1->adc_val) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case OUT_POWER_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->out_power - p1->out_power) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case POWER_INDEX_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ }
+
+ return slope;
+}
+
+
+/*
+ * If (x,y) is a point on a curve, then compute y given x, the slope of the curve,
+ * and a known point on the curve.
+ *
+ * If (Xd, Yd) is the desired point, p1 is the known point, and m the slope, then
+ *
+ * Yd - p1->y = m(Xd - p1->x)
+ * Yd = m(Xd - p1->x) + p1->y
+ * Yd = m(Xd) - m(p1->x) + p1->y
+ */
+static int computeY(wcd_point_t * p1, int slopeTimes1000, int x, int slopeType)
+{
+ int y = 0;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->power_index;
+ break;
+ case ADC_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->adc_val;
+ break;
+ case OUT_POWER_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->out_power;
+ break;
+ case POWER_INDEX_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->power_index;
+ break;
+ }
+
+ return y;
+}
+
+
+/*
+ * Return a pointer to the curve we should use for the currently selected
+ * channel.
+ */
+static wcd_curve_t *determineCurve(struct piper_priv *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ wcd_curve_t *curve = NULL;
+
+ if (digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ) {
+ if (rates & MAC_PSK_BRS_MASK) {
+ /*
+ * This is the normal case for 802.11b and 802.11bg. We select
+ * the PSK curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_B_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_B_CURVE_INDEX];
+ } else { /* if associated with AP that only supports G rates */
+
+ /*
+ * This is a very unusual, but theoretically possible case. We
+ * are associated with an AP that only supports OFDM modulation
+ * (G only, no B). We determine this by looking at the BRS
+ * setting. If no PSK rates are set for BRS, then we assume that
+ * this must be a G only AP. Obviously, we must select the OFDM curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_G_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_G_CURVE_INDEX];
+ }
+ } else {
+ /*
+ * An 802.11a channel is selected.
+ */
+ curve = &calibration.nvram->cal_curves_a[digi->channel - BAND_A_OFFSET];
+ digi_dbg("Using A curve [%d]\n", digi->channel - BAND_A_OFFSET);
+ }
+
+ return curve;
+}
+
+/*
+ * Determine the maximum mdBm allowed for the current channel. Return the
+ * lesser of the max and the mdBm value passed to us.
+ */
+static int getFilteredPower(struct piper_priv *digi, int mdBm)
+{
+ int max = 0;
+
+ if (digi->channel < BAND_A_OFFSET) {
+ max = 16000; /* all BG channels can go to 16 dBm */
+ } else if (digi->channel < (BAND_A_OFFSET + 4)) {
+ max = 3000; /* first 4 A channels max out at 3 dBm */
+ } else {
+ max = 8000; /* all other A channels can handle 8 dBm */
+ }
+
+ if (mdBm > max) {
+ mdBm = max;
+ }
+
+ return mdBm;
+}
+
+
+/*
+ * This routine performs open loop calibration for Airoha. It takes a value in mdbm
+ * and uses the factory calibration routines to determine the appropriate register
+ * value to write to airoha.
+ */
+static void setInitialPowerLevel(struct piper_priv *digi, int mdBm)
+{
+ wcd_point_t *p1, *p2;
+
+ mdBm = getFilteredPower(digi, mdBm);
+ digi_dbg("Setting initial powerlevel %d milli dBm.\n", mdBm);
+ calibration.curve = determineCurve(digi);
+ findClosestPoints(calibration.curve, mdBm, &p1, &p2, OUT_POWER);
+ calibration.slopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_OUT_POWER);
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ calibration.p1 = p1;
+ } else {
+ calibration.p1 = p2;
+ }
+ calibration.powerIndex =
+ computeY(calibration.p1, calibration.slopeTimes1000, mdBm,
+ POWER_INDEX_OVER_OUT_POWER);
+ calibration.correctedPowerIndex = calibration.powerIndex;
+
+ digi_dbg("Computed power index = %d.\n", calibration.powerIndex);
+ digi->rf->set_pwr_index(digi->hw, calibration.powerIndex);
+
+ /*
+ * Let's compute and save the expected ADC value while we have all the necessary
+ * information handy.
+ */
+#ifdef WANT_CAL_DEBUG
+ digi_dbg("Using points ");
+ printPoint(p1);
+ printPoint(p2);
+ printk("\n");
+ digi_dbg("Max ADC = %d.\n", calibration.curve->max_adc_value);
+#endif
+ calibration.adcSlopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+ calibration.expectedAdc =
+ computeY(calibration.p1, calibration.adcSlopeTimes1000, mdBm,
+ ADC_OVER_OUT_POWER);
+ if (calibration.expectedAdc > calibration.curve->max_adc_value) {
+ calibration.expectedAdc = calibration.curve->max_adc_value;
+ }
+ digi_dbg("adcSlopeTimes1000 = %d, expectedAdc = %d\n",
+ calibration.adcSlopeTimes1000, calibration.expectedAdc);
+ calibration.outPowerSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, OUT_POWER_OVER_ADC);
+ calibration.powerIndexSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_ADC);
+}
+
+
+
+/*
+ * This routine performs closed loop recalibration. It is called periodically
+ * to adjust the transmit power level. It will be called after the ADC levels
+ * for several transmit frames have been sampled. It does the following:
+ *
+ * 1. Average the samples together.
+ * 2. If the measured ADC level is too low, then increase the power
+ * level one step.
+ * 3. If the measured ADC level is too high, then decrease the power
+ * level one step.
+ */
+static void recalibrate(struct piper_priv *digi)
+{
+ unsigned int idx;
+ int actualAdc = 0;
+ int errorInAdc, errorInMdbm;
+ wcd_point_t p = {
+ .out_power = 0,
+ .adc_val = 0
+ };
+ int needCorrection = 0;
+
+#ifdef WANT_CAL_DEBUG_1
+ digi_dbg("Samples: ");
+#endif
+ for (idx = 0; idx < calibration.sampleCount; idx++) {
+#ifdef WANT_CAL_DEBUG_1
+ printk("%d, ", calibration.sample[idx].sample);
+#endif
+ actualAdc += calibration.sample[idx].sample;
+ }
+#ifdef WANT_CAL_DEBUG_1
+ printk("\n");
+#endif
+ actualAdc = actualAdc / calibration.sampleCount;
+
+#ifdef WANT_TO_FORCE_26
+
+ digi->rf->set_pwr_index(digi->hw, 26);
+#else
+
+ errorInAdc = actualAdc - calibration.expectedAdc;
+ errorInMdbm =
+ computeY(&p, calibration.outPowerSlopeTimes1000, errorInAdc,
+ OUT_POWER_OVER_ADC);
+ needCorrection = (errorInMdbm > MAX_TOLERATED_ERROR_HIGH);
+ if (errorInMdbm < 0) {
+ needCorrection = ((MAX_TOLERATED_ERROR_LOW + errorInMdbm) < 0);
+ }
+ if (needCorrection) {
+ int correction = computeY(calibration.p1,
+ calibration.powerIndexSlopeTimes1000,
+ actualAdc, POWER_INDEX_OVER_ADC);
+#if defined(WANT_CAL_DEBUG)
+ int oldIndex = calibration.correctedPowerIndex;
+#endif
+ correction = (3 * (calibration.powerIndex - correction)) / 4;
+ if (correction == 0) {
+ if (errorInAdc > 0) {
+ /*
+ * If power level is too high but the minimum correction would
+ * overshoot the target, do it anyway because we would rather
+ * be below the target rather than over the target.
+ */
+ correction = -1;
+ } else {
+ /*
+ * But if the power level is too low, but the minimum correction
+ * would overshoot, then leave it be. Better too low than too high.
+ */
+ }
+ }
+ calibration.correctedPowerIndex += correction;
+ if (calibration.correctedPowerIndex < MINIMUM_POWER_INDEX) {
+ calibration.correctedPowerIndex = MINIMUM_POWER_INDEX;
+ }
+ digi->rf->set_pwr_index(digi->hw, calibration.correctedPowerIndex);
+#ifdef WANT_CAL_DEBUG
+ digi_dbg
+ ("actualAdc = %d, expectedAdc = %d, error mdbm = %d\n",
+ actualAdc, calibration.expectedAdc, errorInMdbm);
+ digi_dbg
+ ("Power index corrected by %d: was %d, set to %d.\n",
+ correction, oldIndex, calibration.correctedPowerIndex);
+#endif
+#if WANT_CALIBRATION_STATUS
+ if (correction != 0)
+ printk(KERN_ERR
+ "error mdBm = %d, correcting airoha index by %d\n",
+ errorInMdbm, correction);
+#endif
+ }
+#endif
+}
+
+
+/*
+ * This routine is called by the 80211mac library to set a new power level. We
+ * update the value in context memory and then kick the autocalibration thread.
+ */
+static int setNewPowerLevel(struct ieee80211_hw *hw, uint8_t newPowerLevel)
+{
+ struct piper_priv *digi = hw->priv;
+
+ (void) newPowerLevel; /* has already been written into piper->tx_power */
+ /*
+ * Kick the calibration thread.
+ */
+ stopSampler(digi);
+ kickCalibrationThread(digi, RESTART_AUTOCALIBRATION_EVENT);
+
+ return 0;
+}
+
+
+
+
+/*
+ * Compute the maximum ADC value for a curve given the maximum
+ * allowed power in dBm.
+ */
+static u8 getMaxAdcValue(wcd_curve_t * curve, int dBm)
+{
+ wcd_point_t *p1, *p2;
+ int slopeTimes1000;
+ u8 result = 0;
+ int mdBm = 1000 * dBm;
+
+ findClosestPoints(curve, mdBm, &p1, &p2, OUT_POWER);
+ slopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ result = computeY(p1, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ } else {
+ result = computeY(p2, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ }
+
+ return result;
+}
+
+
+/*
+ * The version 1.0 calibration data provided maximum Airoha index
+ * values, but later versions provided maximum ADC values. This
+ * routine replaces the max Airoha indexes with max ADC values.
+ */
+static void determineMaxAdcValues(wcd_data_t * cdata)
+{
+ int i;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ /*
+ * All BG channels are limited to 16 dBm.
+ */
+ cdata->cal_curves_bg[i][0].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][0], 16);
+ cdata->cal_curves_bg[i][1].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][1], 16);
+ }
+ for (i = 0; i < 4; i++) {
+ /*
+ * First 4 802.11a channels are limited to 3 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 3);
+ }
+ for (i = 4; i < WCD_CHANNELS_A; i++) {
+ /*
+ * All other 802.11a channels are limited to 8 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 8);
+ }
+}
+
+
+
+/*
+ * This routine writes a default set of calibration values into the
+ * calibration data structure. Maximum power output is severely limited.
+ */
+static void setDefaultCalibrationValues(struct piper_priv *piperp)
+{
+#define MIN_MDBM (-2905)
+#define MAX_BG_MDBM (6000)
+#define DEFAULT_NUM_POINTS (DEFAULT_NUM_POINTS)
+#define BAND_A_1_START (0)
+#define BAND_A_2_START (4)
+#define BAND_A_3_START (7)
+#define BAND_A_4_START (15)
+#define BAND_A_5_START (19)
+#define BAND_A_6_START (30)
+
+ int i;
+
+ calibration.nvram->header.numcalpoints = 2;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][0].points[0].adc_val = 19;
+ calibration.nvram->cal_curves_bg[i][0].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][0].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][0].points[1].adc_val = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[1].power_index = 16;
+
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][1].points[0].adc_val = 12;
+ calibration.nvram->cal_curves_bg[i][1].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][1].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][1].points[1].adc_val = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[1].power_index = 24;
+ }
+
+ for (i = BAND_A_1_START; i < BAND_A_2_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 22;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 0;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 22;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 19;
+ }
+ for (i = BAND_A_2_START; i < BAND_A_3_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 29;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 29;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 20;
+ }
+ for (i = BAND_A_3_START; i < BAND_A_4_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 42;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 15;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 4000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 42;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 22;
+ }
+ for (i = BAND_A_4_START; i < BAND_A_5_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 54;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 21;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 54;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 18;
+ }
+ for (i = BAND_A_5_START; i < BAND_A_6_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 39;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 39;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 26;
+ }
+ for (i = BAND_A_6_START; i < WCD_CHANNELS_A; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 31;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 31;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 30;
+ }
+}
+
+
+/*
+ * The calibration data is passed to the kernel from U-Boot. The kernel
+ * start up routines will have copied the data into digi->pdata->wcd.
+ * We do a few sanity checks on the data and set up our own pointers to
+ * it.
+ */
+static int getCalibrationData(struct piper_priv *digi)
+{
+ int result = 0;
+
+ calibration.nvram = &digi->pdata->wcd;
+
+ if (strncmp(calibration.nvram->header.magic_string,
+ NVRAM_WCAL_SIGNATURE, strlen(NVRAM_WCAL_SIGNATURE)) == 0) {
+ unsigned int crc = ~crc32_le(~0,
+ (unsigned char const *) calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+
+ if (crc == calibration.nvram->header.wcd_crc) {
+ digi_dbg("CRC and signature for calibration data is okay\n");
+ result = 0;
+ if ((calibration.nvram->header.ver_major == '1')
+ && (calibration.nvram->header.ver_minor == '0')) {
+ digi_dbg("Converting version 1.0 calibration data\n");
+ determineMaxAdcValues(calibration.nvram);
+ /*
+ * Now that we have updated the format of the data, we need
+ * to recompute the check sum and set the new version.
+ */
+ calibration.nvram->header.ver_minor = '1';
+ calibration.nvram->header.wcd_crc =
+ ~crc32_le(~0, (unsigned char const *)
+ calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+ }
+ digi->rf->set_hw_info(digi->hw, digi->channel,
+ calibration.nvram->header.hw_platform);
+ } else {
+ digi_dbg("Calibration data has invalid CRC.\n");
+ setDefaultCalibrationValues(digi);
+ }
+ } else {
+ digi_dbg("Calibration data has invalid signature.\n");
+ setDefaultCalibrationValues(digi);
+ }
+
+ return result;
+}
+
+
+
+/*
+ * This routine:
+ *
+ * 1. Loads the ADC driver.
+ * 2. Loads the calibration data.
+ * 3. Implements the automatic calibration state machine.
+ *
+ */
+static int digiWifiCalibrationThreadEntry(void *data)
+{
+ struct piper_priv *digi = data;
+ int state;
+
+ __set_current_state(TASK_RUNNING);
+
+ while (1) {
+ /*
+ * We, the wireless driver, may be loaded before the I2C core has
+ * loaded. Therefore we may not be able to load our ADC driver,
+ * which is an I2C client driver, when we load. This loop tries
+ * and retries to load the ADC driver until it succeeds.
+ */
+
+ /* TODO, FIXME make following code dependent on platform information
+ * allowign to initialize different adc */
+ if (adc121C027_init(&calibration, digi->pdata->i2c_adapter_num) == 0) {
+ digi_dbg("ADC driver loaded...\n");
+ break;
+ }
+ digi_dbg("Will try to load ADC driver again...\n");
+ ssleep(10);
+ }
+
+ if (getCalibrationData(digi) == 0) {
+ digi->rf->set_pwr = setNewPowerLevel;
+
+ state = RESTART_STATE;
+
+ digi_dbg("Starting autocalibration state machine.\n");
+ do {
+ int event;
+ int timeout = 0;
+ int expectedEvent = TIMED_OUT_EVENT;
+ int nextState = RESTART_STATE;
+
+ switch (state) {
+ case RESTART_STATE:
+ setInitialPowerLevel(digi,
+ CONVERT_TO_MDBM(digi->tx_power));
+ calibration.initialized = true;
+ calibration.sampleCount = 0;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = DEBOUNCE_DELAY;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ case COLLECT_SAMPLES_STATE:
+ digi->calibrationTxRate =
+ determineCalibrationTxRate(digi);
+ startSampleCollection(digi);
+ nextState = GOT_SAMPLE_STATE;
+ timeout = SAMPLE_TIMEOUT;
+ expectedEvent = TRANSMIT_DONE_EVENT;
+ break;
+ case GOT_SAMPLE_STATE:
+ processSample(digi);
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = 0;
+ break;
+ }
+ /* fall through is intended operation */
+ case RECALIBRATE_STATE:
+ recalibrate(digi);
+ calibration.sampleCount = 0;
+ digi->calibrationTxRate = USE_MAC80211_DATA_RATE;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ default:
+ digi_dbg("Unknown state %d\n", state);
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ }
+
+ state = nextState;
+ event = waitForEvent(timeout, expectedEvent);
+
+ switch (event) {
+ case SHUTDOWN_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received SHUTDOWN_AUTOCALIBRATION_EVENT\n");
+ break;
+ case RESTART_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received RESTART_AUTOCALIBRATION_EVENT\n");
+ state = RESTART_STATE;
+ break;
+ case TRANSMIT_DONE_EVENT:
+ break;
+ case TIMED_OUT_EVENT:
+ if (state == GOT_SAMPLE_STATE) {
+ state = COLLECT_SAMPLES_STATE;
+ }
+ }
+ } while (!kthread_should_stop());
+ } else {
+ printk(KERN_ERR
+ "\nWarning: Wireless interface calibration data is corrupted.\n");
+ printk(KERN_ERR
+ " The wireless interface will operate at a low power level.\n");
+ while (!kthread_should_stop()) {
+ ssleep(1);
+ }
+ }
+ calibration.cops->adc_shutdown(&calibration);
+
+ return 0;
+}
+
+
+
+/*
+ * This routine is called at initialization to set up the Airoha calibration routines.
+ */
+void digiWifiInitCalibration(struct piper_priv *digi)
+{
+
+ calibration.events = 0;
+ calibration.sampleCount = 0;
+ calibration.initialized = false;
+
+ spin_lock_init(&calibration.lock);
+
+ calibration.threadCB =
+ kthread_run(digiWifiCalibrationThreadEntry, digi,
+ PIPER_DRIVER_NAME " - calibration");
+}
+
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp)
+{
+ if (calibration.initialized)
+ return calibration.correctedPowerIndex;
+ else
+ return -1;
+}
+
+
+void digiWifiDeInitCalibration(struct piper_priv *digi)
+{
+ calibration.events = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ wake_up_interruptible(&waitQueue);
+ kthread_stop(calibration.threadCB);
+ calibration.initialized = false;
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDeInitCalibration);
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.h b/drivers/net/wireless/digiPiper/airohaCalibration.h
new file mode 100644
index 000000000000..36543c87fc51
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.h
@@ -0,0 +1,108 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef AIROHA_CALIBRATION_H
+#define AIROHA_CALIBRATION_H
+
+#include "pipermain.h"
+
+
+#define MAX_SAMPLES (3)
+
+/*
+ * Field values used for computing ABS values.
+ */
+enum {
+ OUT_POWER,
+ ADC_VAL,
+ POWER_INDEX
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *fieldDescription[] = {
+ "OUT_POWER",
+ "ADC_VAL",
+ "POWER_INDEX"
+};
+#endif
+
+/*
+ * States for the auto calibration thread.
+ */
+enum {
+ RESTART_STATE,
+ COLLECT_SAMPLES_STATE,
+ GOT_SAMPLE_STATE,
+ RECALIBRATE_STATE
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *stateText[] = {
+ "RESTART_STATE",
+ "COLLECT_SAMPLES_STATE",
+ "GOT_SAMPLE_STATE",
+ "RECALIBRATE_STATE"
+};
+#endif
+
+
+/*
+ * Slope types accepted by computeSlope().
+ */
+enum {
+ POWER_INDEX_OVER_OUT_POWER,
+ ADC_OVER_OUT_POWER,
+ OUT_POWER_OVER_ADC,
+ POWER_INDEX_OVER_ADC
+};
+
+
+typedef struct {
+ unsigned rate; /* rate packet transmitted at */
+ unsigned int sample; /* measured sample */
+} sampleInfo_t;
+
+struct airohaCalibrationData {
+ struct task_struct *threadCB;
+ spinlock_t lock;
+ volatile unsigned int events;
+ unsigned int sampleCount;
+ sampleInfo_t sample[MAX_SAMPLES];
+ wcd_data_t *nvram;
+ wcd_curve_t *curve;
+ int slopeTimes1000;
+ int adcSlopeTimes1000;
+ int outPowerSlopeTimes1000;
+ int powerIndexSlopeTimes1000;
+ int expectedAdc;
+ int powerIndex, correctedPowerIndex;
+ wcd_point_t *p1;
+
+
+ void *priv;
+ struct calibration_ops *cops;
+ bool initialized;
+};
+
+struct calibration_ops {
+ u16(*adc_read_peak) (struct airohaCalibrationData *);
+ void (*adc_clear_peak) (struct airohaCalibrationData *);
+ u16(*adc_read_last_val) (struct airohaCalibrationData *);
+ void (*adc_shutdown) (struct airohaCalibrationData *);
+};
+
+
+void digiWifiInitCalibration(struct piper_priv *digi);
+void digiWifiDeInitCalibration(struct piper_priv *digi);
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp);
+
+#endif /* AIROHA_CALIBRATION_H */
diff --git a/drivers/net/wireless/digiPiper/digiDebug.c b/drivers/net/wireless/digiPiper/digiDebug.c
new file mode 100644
index 000000000000..627dd8c698ec
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiDebug.c
@@ -0,0 +1,205 @@
+/*
+ * digiDebug.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains some debugging routines.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+
+#define DUMP_WORDS_MAX (700)
+static unsigned int dumpWordsWord[DUMP_WORDS_MAX];
+static unsigned int dumpWordsCount = 0;
+
+void digiWifiDumpWordsAdd(unsigned int word)
+{
+ if (dumpWordsCount < DUMP_WORDS_MAX)
+ {
+ dumpWordsWord[dumpWordsCount++] = word;
+ }
+}
+void digiWifiDumpWordsDump(void)
+{
+ unsigned int *p = dumpWordsWord;
+ unsigned int wordsToGo = dumpWordsCount;
+
+ dumpWordsCount = 0;
+
+ while (wordsToGo >= 4)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X %8.8X\n", p[0], p[1], p[2], p[3]);
+ p += 4;
+ wordsToGo -= 4;
+ }
+ if (wordsToGo == 3)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X\n", p[0], p[1], p[2]);
+ }
+ else if (wordsToGo == 2)
+ {
+ digi_dbg("%8.8X %8.8X \n", p[0], p[1]);
+ }
+ else if (wordsToGo == 1)
+ {
+ digi_dbg("%8.8X \n", p[0]);
+ }
+ digi_dbg("--------------\n");
+}
+
+void digiWifiDumpWordsReset(void)
+{
+ dumpWordsCount = 0;
+}
+
+
+void digiWifiDumpBuffer(u8 *buffer, unsigned int length)
+{
+ unsigned int i, word;
+
+ digiWifiDumpWordsReset();
+
+ for (i = 0; i < length / sizeof(unsigned int); i++)
+ {
+ memcpy(&word, &buffer[i*sizeof(unsigned int)], sizeof(word));
+ digiWifiDumpWordsAdd(cpu_to_be32(word));
+ }
+
+ digiWifiDumpWordsDump();
+}
+
+
+void digiWifiDumpSkb(struct sk_buff *skb)
+{
+ unsigned int bytesLeft = skb->len;
+ unsigned char *p = skb->data;
+
+ digi_dbg("skb has %d bytes\n", skb->len);
+ while (bytesLeft >= 16)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X - %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ p += 16;
+ bytesLeft -= 16;
+ }
+ if (bytesLeft >= 8)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ bytesLeft -= 8;
+ }
+ if (bytesLeft >= 4)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X \n",
+ p[0], p[1], p[2], p[3]);
+ p += 4;
+ bytesLeft -= 4;
+ }
+ if (bytesLeft >= 2)
+ {
+ digi_dbg("%2.2X %2.2X \n",
+ p[0], p[1]);
+ p += 2;
+ bytesLeft -= 2;
+ }
+ if (bytesLeft >= 1)
+ {
+ digi_dbg("%2.2X \n",
+ p[0]);
+ p += 1;
+ bytesLeft -= 1;
+ }
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDumpSkb);
+
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs)
+{
+#ifdef WANT_DEBUG
+ unsigned int i;
+
+ if (regs & CTRL_STATUS_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "Gen Ctrl", digi->ac->rd_reg(digi, BB_GENERAL_CTL),
+ "Gen Status", digi->ac->rd_reg(digi, BB_GENERAL_STAT));
+ }
+ else if (regs & IRQ_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "IRQ Mask", digi->ac->rd_reg(digi, BB_IRQ_MASK),
+ "IRQ Status", digi->ac->rd_reg(digi, BB_IRQ_STAT));
+ }
+ else if (regs & MAIN_REGS)
+ {
+ const char *regNames[] = {"Version", "Gen Ctrl", "Gen Status", "RSSI/AES",
+ "Int Mask", "Int Status", "SPI Data", "SPI Ctrl",
+ "Data FIFO", "not used", "conf-1", "conf-2", "AES FIFO",
+ "not used", "AES Ctrl", "IO Ctrl"};
+ printk(KERN_ERR "Main Registers:\n");
+ for (i = BB_VERSION; i <= BB_OUTPUT_CONTROL; i = i+8)
+ {
+ if ((i != BB_DATA_FIFO) && (i != BB_AES_FIFO))
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[i>>2], digi->ac->rd_reg(digi, i), regNames[(i>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ }
+ if (regs & MAC_REGS)
+ {
+ const char *regNames[] = {"STA ID0", "STA ID1", "BSS ID0", "BSS ID1",
+ "OFDM/PSK", "Backoff", "DTIM/List", "B Int",
+ "Rev/M Stat", "C C/M CTL", "Measure", "Beac Fltr"};
+
+ printk(KERN_ERR "Secondary Registers:\n");
+ for (i = MAC_STA_ID0; i <= MAC_BEACON_FILT; i = i+8)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[((i - MAC_STA_ID0) >>2)], digi->ac->rd_reg(digi, i), regNames[((i - MAC_STA_ID0)>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ if (regs & FRAME_BUFFER_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "Real time frame buffer\n");
+
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xcc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xdc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+ if (regs & FIFO_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "FIFO contents\n");
+
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(digiWifiDumpRegisters);
diff --git a/drivers/net/wireless/digiPiper/digiIsr.c b/drivers/net/wireless/digiPiper/digiIsr.c
new file mode 100644
index 000000000000..e2128e254055
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiIsr.c
@@ -0,0 +1,159 @@
+/*
+ * digiIsr.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to processing interrupts
+ * from the MAC.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+
+#define IRQ_DEBUG (0)
+#if IRQ_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This routine handles interrupts from the MAC.
+ */
+irqreturn_t piper_irq_handler(int irq, void *dev_id)
+{
+ struct piper_priv *piperp = dev_id;
+ u32 status;
+
+ /* Acknowledge pending interrupts */
+ status = piperp->ac->rd_reg(piperp, BB_IRQ_STAT);
+ status &= piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, status, op_write);
+
+ if (status & BB_IRQ_MASK_RX_FIFO) {
+ /*
+ * This interrupt indicates we have a frame in the FIFO.
+ * Set up to receive the packet. Disable further interrupts
+ * until the receive is complete.
+ */
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ /*
+ * Call the receive routine directly inside the irq handler
+ * or in the tasklet, depending on configuration.
+ */
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ piper_rx_tasklet((unsigned long) piperp);
+#else
+ tasklet_hi_schedule(&piperp->rx_tasklet);
+#endif
+ }
+
+ if (status & BB_IRQ_MASK_TX_FIFO_EMPTY) {
+ /*
+ * Transmit complete interrupt. This IRQ is only unmasked if we are
+ * not expecting the packet to be ACKed. This will be the case for
+ * broadcasts. In this case, tell mac80211 the transmit occurred and
+ * restart the tx queue.
+ */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ piperp->tx_signal_strength = 0;
+ piperp->tx_result = TX_COMPLETE;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_FIFO_EMPTY and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TIMEOUT) {
+ /* AP did not ACK our TX packet */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ /* Update retry counter */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TIMEOUT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_TX_ABORT)) {
+ dprintk(DWARNING, "TX abort\n");
+
+ /* Could not transmit a packet because the media was busy */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_ABORT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TBTT) {
+ /*
+ * This interrupt occurs at the start of a beacon period. The only thing
+ * we need to do is to write a new beacon backoff value.
+ */
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /*
+ * TODO:
+ * Improve the way we keep track of whether or not we sent the last
+ * beacon. What we are doing now is to assume that we did until and
+ * unless we receive a beacon. What we should do is look for either
+ * a beacon or a TX end interrupt. However, since mac80211 doesn't
+ * tell us what the ATIM window is, we have to assume it is zero,
+ * which means we could be transmitting a frame at the same
+ * time we are sending the beacon, so there isn't really any easy
+ * way for us to do this. In fact, even if there was an ATIM
+ * window, we could have started a transmit just before we get this
+ * interrupt, so I'm not sure how we are really suppose to keep
+ * track of this.
+ */
+ /* assume we sent last beacon unless we receive one */
+ piperp->beacon.weSentLastOne = true;
+ }
+
+ if (status & BB_IRQ_MASK_ATIM) {
+ /*
+ * This interrupt should not occur since we are not using it. When in
+ * IBSS mode, the beacon period starts at the TBTT interrupt and ends
+ * at this interrupt. We are not suppose to send packets between the
+ * two interrupts. However, mac80211 does not seem to provide a way
+ * for us to find out how long the ATIM period is, so we have to assume
+ * that there isn't one.
+ *
+ * If we were supporting this interrupt we would have to synchronize
+ * with the transmit routine so that transmit is paused during this
+ * time.
+ */
+ dprintk(DWARNING, "BB_IRQ_MASK_ATIM irq (0x%08x)\n", status);
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_ATIM);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_RX_OVERRUN))
+ piperp->pstats.rx_overruns++;
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(piper_irq_handler);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiMac80211.c b/drivers/net/wireless/digiPiper/digiMac80211.c
new file mode 100644
index 000000000000..b9ca3ef10230
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiMac80211.c
@@ -0,0 +1,1000 @@
+/*
+ * digiMac80211.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that interface with the mac80211
+ * library.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include <crypto/aes.h>
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define MAC_DEBUG (1)
+
+#if MAC_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This constant determines how many times per second the led_timer_fn
+ * function will be called. (HZ >> 3) means 8 times a second.
+ */
+#define LED_TIMER_RATE (HZ >> 3)
+#define LED_MAX_COUNT (15)
+
+/*
+ * This function is called from a timer to blink an LED in order to
+ * indicate what are current state is.
+ */
+static void led_timer_fn(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+ static unsigned int count = 0;
+
+ if(!piperp->pdata->set_led)
+ return;
+
+ switch (piperp->led_state) {
+ case led_shutdown:
+ /* Turn LED off if we are shut down */
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+ break;
+ case led_adhoc:
+ /* Blink LED slowly in ad-hoc mode */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 8) ? 0 : 1);
+ break;
+ case led_not_associated:
+ /* Blink LED rapidly when not associated with an AP */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 1) ? 0 : 1);
+ break;
+ case led_associated:
+ /* LED steadily on when associated */
+ piperp->pdata->set_led(piperp, STATUS_LED, 1);
+ break;
+ default:
+ /* Oops. How did we get here? */
+ break;
+ }
+ count++;
+ if (count > LED_MAX_COUNT) {
+ count = 0;
+ }
+
+ piperp->led_timer.expires += LED_TIMER_RATE;
+ add_timer(&piperp->led_timer);
+}
+
+/*
+ * This function sets the current LED state.
+ */
+static int piper_set_status_led(struct ieee80211_hw *hw, enum led_states state)
+{
+ struct piper_priv *piperp = (struct piper_priv *)hw->priv;
+
+ piperp->led_state = state;
+
+ if(!piperp->pdata->set_led)
+ return -ENOSYS;
+
+ if (state == led_shutdown)
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+
+ return 0;
+}
+
+/*
+ * This routine is called to enable IBSS support whenever we receive
+ * configuration commands from mac80211 related to IBSS support. The
+ * routine examines the configuration settings to determine if IBSS
+ * support should be enabled and, if so, turns on automatic beacon
+ * generation.
+ *
+ * TODO: This code may need to be moved into piper.c since other
+ * H/W does not implement automatic generate of beacons.
+ */
+static void piper_enable_ibss(struct piper_priv *piperp, enum nl80211_iftype iftype)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ if (((iftype == NL80211_IFTYPE_ADHOC) || (iftype == NL80211_IFTYPE_MESH_POINT))
+ && (piperp->beacon.loaded) && (piperp->beacon.enabled)
+ && ((piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & MAC_BEACON_INTERVAL_MASK) != 0)) {
+ /*
+ * If we come here, then we are running in IBSS mode, beacons are enabled,
+ * and we have the information we need, so start sending beacons.
+ */
+ /* TODO: Handle non-zero ATIM period. mac80211 currently has no way to
+ * tell us what the ATIM period is, but someday they might fix that.*/
+
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /* enable beacon interrupts*/
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ piperp->ac->wr_reg(piperp,
+ MAC_CTL, MAC_CTL_BEACON_TX | MAC_CTL_IBSS, op_or);
+ dprintk(DVERBOSE, "IBSS turned ON\n");
+ piper_set_status_led(piperp->hw, led_adhoc);
+ } else {
+ /*
+ * If we come here, then either we are not suppose to transmit beacons,
+ * or we do not yet have all the information we need to transmit
+ * beacons. Make sure the automatic beacon function is disabled.
+ */
+ /* shut down beacons */
+ piperp->ac->wr_reg(piperp, MAC_CTL,
+ ~(MAC_CTL_BEACON_TX | MAC_CTL_IBSS), op_and);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ dprintk(DVERBOSE, "IBSS turned OFF\n");
+ }
+}
+
+/*
+ * Set the transmit power level. The real work is done in the
+ * transceiver code.
+ */
+static int piper_set_tx_power(struct ieee80211_hw *hw, int power)
+{
+ struct piper_priv *digi = hw->priv;
+ int err;
+ int oldTxPower = digi->tx_power;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (power == digi->tx_power)
+ return 0;
+
+ digi->tx_power = power;
+ err = digi->rf->set_pwr(hw, power);
+ if (err)
+ digi->tx_power = oldTxPower;
+
+ return err;
+}
+
+/*
+ * Utility routine that sets a sequence number for a data packet.
+ */
+static void assign_seq_number(struct sk_buff *skb, bool increment)
+{
+#define SEQUENCE_NUMBER_MASK (0xfff0)
+ static u16 seq_number = 0;
+ _80211HeaderType *header = (_80211HeaderType *)skb->data;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (skb->len >= sizeof(*header)) {
+ u16 seq_field;
+
+ /*
+ * TODO: memcpy's are here because I am concerned we may get
+ * an unaligned frame. Is this a real possibility? Or
+ * am I just wasting CPU time?
+ */
+ memcpy(&seq_field, &header->squ.sq16, sizeof(header->squ.sq16));
+ seq_field &= ~SEQUENCE_NUMBER_MASK;
+ seq_field |= (SEQUENCE_NUMBER_MASK & (seq_number << 4));
+ memcpy(&header->squ.sq16, &seq_field, sizeof(header->squ.sq16));
+ if (increment)
+ seq_number++;
+ }
+}
+
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+/* Get 16 bits at byte pointer */
+#define GET16(bp) ((bp)[0] | ((bp)[1] << 8))
+/* Get 32 bits at byte pointer */
+#define GET32(bp) ((bp)[0] | ((bp)[1] << 8) | ((bp)[2] << 16) | ((bp)[3] << 24))
+/* Store 16 bits at byte pointer */
+#define SET16(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; }
+/* Store 32 bits at byte pointer */
+#define SET32(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; \
+ (bp)[2] = (data) >> 16; \
+ (bp)[3] = (data) >> 24; }
+
+static inline void dw_inc_48(u48* n)
+{
+ (*n)++;
+ *n &= ((u64) 1 << 48) - 1;
+}
+
+/*
+ * This function prepares a blob of data we will have to send to the AES
+ * H/W encryption engine. The data consists of the AES initialization
+ * vector and 2 16 byte headers.
+ *
+ * Returns true if successful, or false if something goes wrong
+ */
+bool piper_prepare_aes_datablob(struct piper_priv *piperp, unsigned int keyIndex,
+ u8 *aesBlob, u8 *frame, u32 length, bool isTransmit)
+{
+ _80211HeaderType *header = (_80211HeaderType *)frame;
+ u8 *body = &frame[sizeof(*header)];
+ int dlen = length - (_80211_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (keyIndex >= PIPER_MAX_KEYS) {
+ dprintk(DWARNING, "encryption key index %d is out of range.\n",
+ keyIndex);
+ return false;
+ }
+
+ if (piperp->key[keyIndex].valid == false)
+ return false;
+
+ /* Set up CCM initial block for MIC IV */
+ memset(aesBlob, 0, AES_BLOB_LENGTH);
+ aesBlob[0] = 0x59;
+ aesBlob[1] = 0;
+ memcpy (&aesBlob[2], header->addr2, ETH_ALEN);
+ aesBlob[8] = body[7];
+ aesBlob[9] = body[6];
+ aesBlob[10] = body[5];
+ aesBlob[11] = body[4];
+ aesBlob[12] = body[1];
+ aesBlob[13] = body[0];
+ aesBlob[14] = dlen >> 8;
+ aesBlob[15] = dlen;
+
+ /* Set up MIC header blocks */
+#define AES_HEADER_0_OFFSET (16)
+#define AES_HEADER_1_OFFSET (32)
+ aesBlob[AES_HEADER_0_OFFSET+0] = 0;
+ aesBlob[AES_HEADER_0_OFFSET+1] = 22;
+ aesBlob[AES_HEADER_0_OFFSET+2] = frame[0] & 0xcf;
+ aesBlob[AES_HEADER_0_OFFSET+3] = frame[1] & 0xd7;
+ /*
+ * This memcpy writes data into the last 12 bytes of the first header
+ * and the first 6 bytes of the 2nd header. I did it as one memcpy
+ * call for efficiency.
+ */
+ memcpy(&aesBlob[AES_HEADER_0_OFFSET+4], header->addr1, 3*ETH_ALEN);
+ aesBlob[AES_HEADER_1_OFFSET+6] = header->squ.sq.frag;
+ aesBlob[AES_HEADER_1_OFFSET+7] = 0;
+ memset (&aesBlob[AES_HEADER_1_OFFSET+8], 0, 8); /* clear vector location in data */
+
+ return true;
+}
+
+/*
+ * mac80211 calls this routine to transmit a frame. We set up
+ * up the information the trasmit tasklet will need, and then
+ * schedule the tasklet.
+ */
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(skb);
+ unsigned long flags;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (piperp->is_radio_on == false) {
+ dprintk(DERROR, "called with radio off\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Our H/W can only transmit a single packet at a time. mac80211
+ * already maintains a queue of packets, so there is no reason
+ * for us to set up another one. We stop the mac80211 queue everytime
+ * we get a transmit request and restart it when the transmit
+ * operation completes.
+ */
+ ieee80211_stop_queues(hw);
+
+ if (txInfo->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ assign_seq_number(skb,
+ !!(txInfo->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT));
+ }
+
+ piperp->use_hw_aes = false;
+ if (txInfo->control.hw_key != NULL) {
+ /*
+ * We've been passed an encryption key, so mac80211 must want us
+ * to encrypt the packet with our fancy H/W encryptor. Let's get
+ * set up for that now.
+ */
+ piperp->tx_aes_key = txInfo->control.hw_key->hw_key_idx;
+ piperp->use_hw_aes =
+ piper_prepare_aes_datablob(piperp,
+ txInfo->control.hw_key->hw_key_idx,
+ (u8 *)piperp->tx_aes_blob, skb->data,
+ skb->len, true);
+ }
+ piper_ps_set_header_flag(piperp, ((_80211HeaderType *) skb->data)); /* set power management bit as appropriate*/
+
+ /*
+ * Add space at the start of the frame for the H/W level transmit header.
+ * We can't generate the header now. It must be generated everytime we
+ * transmit because the transmit rate changes when we do retries.
+ */
+ skb_push(skb, TX_HEADER_LENGTH);
+
+ piperp->pstats.tx_retry_index = 0;
+ piperp->pstats.tx_total_tetries = 0;
+ memset(piperp->pstats.tx_retry_count, 0, sizeof(piperp->pstats.tx_retry_count));
+ txInfo->flags &= ~(IEEE80211_TX_STAT_TX_FILTERED |
+ IEEE80211_TX_STAT_ACK |
+ IEEE80211_TX_STAT_AMPDU |
+ IEEE80211_TX_STAT_AMPDU_NO_BACK);
+ piperp->pstats.tx_queue.len++;
+ piperp->pstats.tx_queue.count++;
+
+ if (piper_tx_enqueue(piperp, skb, skb_return_cb) == -1) {
+ skb_pull(skb, TX_HEADER_LENGTH); /* undo the skb_push above */
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ if (!piperp->tx_tasklet_running) {
+ piperp->tx_tasklet_running = true;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+
+ piperp->pstats.tx_start_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_hw_tx_private);
+
+
+int piper_hw_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ return piper_hw_tx_private(hw, skb, ieee80211_tx_status_irqsafe);
+}
+
+
+/*
+ * mac80211 calls this routine to initialize the H/W.
+ */
+static int piper_hw_start(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+ piperp->if_type = __NL80211_IFTYPE_AFTER_LAST;
+
+ /* Initialize the MAC H/W */
+ if ((ret = piperp->init_hw(piperp, IEEE80211_BAND_2GHZ)) != 0) {
+ dprintk(DERROR, "unable to initialize piper HW (%d)\n", ret);
+ return ret;
+ }
+
+ piperp->is_radio_on = true;
+
+ /*
+ * Initialize the antenna with the defualt setting defined in the
+ * probe function. This can be changed, currently, through a sysfs
+ * entry in the device directory
+ */
+ if ((ret = piperp->set_antenna(piperp, piperp->antenna)) != 0) {
+ dprintk(DERROR, "piper_set_antenna_div() failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* set status led to link off */
+ piper_set_status_led(hw, led_shutdown);
+
+ /* Get the tasklets ready to roll */
+ piperp->tx_result = TX_NOT_DONE;
+ tasklet_enable(&piperp->rx_tasklet);
+ tasklet_enable(&piperp->tx_tasklet);
+
+ /*
+ * Enable receive interrupts, but leave the transmit interrupts
+ * and beacon interrupts off for now.
+ */
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ enable_irq(piperp->irq);
+
+ memset(piperp->bssid, 0, ETH_ALEN);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to shut us down.
+ */
+static void piper_hw_stop(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+
+ /* Initialize the MAC H/W */
+ if (piperp->deinit_hw)
+ piperp->deinit_hw(piperp);
+
+ /* set status led to link off */
+ if (piper_set_status_led(hw, led_shutdown))
+ return; /* hardware's probably gone, give up */
+
+ /* turn off phy */
+ piperp->rf->stop(hw);
+
+ /* Disable interrupts before turning off */
+ disable_irq(piperp->irq);
+
+ /* turn off MAX_GAIN, ADC clocks, and so on */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RESET, op_and);
+
+ /* turn off MAC control/mac filt/aes key */
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+
+ /* turn off interrupts */
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+}
+
+/*
+ * mac80211 calls this routine to really start the H/W.
+ * The device type is also set here.
+ */
+static int piper_hw_add_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "if type: %x\n", conf->type);
+
+ /* __NL80211_IFTYPE_AFTER_LAST means no mode selected */
+ if (piperp->if_type != __NL80211_IFTYPE_AFTER_LAST) {
+ dprintk(DERROR, "unsupported interface type %x, expected %x\n",
+ conf->type, __NL80211_IFTYPE_AFTER_LAST);
+ return -EOPNOTSUPP;
+ }
+
+ switch (conf->type) {
+ case NL80211_IFTYPE_ADHOC:
+ piper_set_status_led(piperp->hw, led_adhoc);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ piper_set_status_led(hw, led_not_associated);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_MESH_POINT:
+ piperp->if_type = conf->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this function to shut down us down.
+ */
+static void piper_hw_rm_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *digi = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ digi->if_type = __NL80211_IFTYPE_AFTER_LAST;
+}
+
+/*
+ * mac80211 calls this function to pass us configuration information.
+ */
+static int piper_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ u32 tempval;
+ int err;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ /*
+ * Enable power save mode if bit set in flags, and if we are in station
+ * mode. Power save is not supported in ad-hoc/mesh mode.
+ */
+ piper_ps_scan_event(piperp);
+ piper_ps_set(piperp, ( (conf->flags & IEEE80211_CONF_PS)
+ && (piperp->if_type == NL80211_IFTYPE_STATION)
+ && (piperp->areWeAssociated)));
+ }
+ /* Should we turn the radio off? */
+ if ((piperp->is_radio_on = (conf->radio_enabled != 0)) != 0) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_RX_EN, op_or);
+ } else {
+ dprintk(DNORMAL, "Turning radio off\n");
+ return piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_RX_EN, op_and);
+ }
+
+ /* Adjust the beacon interval and listen interval */
+ tempval = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_BEACON_INTERVAL_MASK;
+ tempval |= conf->beacon_int << MAC_BEACON_INTERVAL_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, tempval, op_write);
+
+ tempval = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_LISTEN_INTERVAL_MASK;
+ tempval |= conf->listen_interval;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, tempval, op_write);
+
+ /* Adjust the power level */
+ if ((err = piper_set_tx_power(hw, conf->power_level)) != 0) {
+ dprintk(DERROR, "unable to set tx power to %d\n",
+ conf->power_level);
+ return err;
+ }
+
+ /* Set channel */
+ if (conf->channel->hw_value != piperp->channel) {
+ piper_ps_scan_event(piperp);
+ if ((err = piperp->rf->set_chan(hw, conf->channel->hw_value)) !=0) {
+ dprintk(DERROR, "unable to set ch to %d\n",
+ conf->channel->hw_value);
+ return err;
+ }
+ piperp->channel = conf->channel->hw_value;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set BSS related configuration settings.
+ */
+static int piper_hw_config_intf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 bssid[2];
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (conf->changed & IEEE80211_IFCC_BSSID &&
+ !is_zero_ether_addr(conf->bssid) &&
+ !is_multicast_ether_addr(conf->bssid)) {
+
+ piper_ps_scan_event(piperp);
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ dprintk(DVERBOSE, "BSSID: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+ conf->bssid[0], conf->bssid[1], conf->bssid[2],
+ conf->bssid[3], conf->bssid[4], conf->bssid[5]);
+
+ bssid[0] = conf->bssid[3] | conf->bssid[2] << 8 |
+ conf->bssid[1] << 16 | conf->bssid[0] << 24;
+ bssid[1] = conf->bssid[5] << 16 | conf->bssid[4] << 24;
+
+ if ((bssid[0] == 0) && (bssid[1] == 0)) {
+ /*
+ * If we come here, then the MAC layer is telling us to set a 0
+ * SSID. In this case, we really want to set the SSID to the
+ * broadcast address so that we receive broadcasts.
+ */
+ bssid[0] = 0xffffffff;
+ bssid[1] = 0xffffffff;
+ }
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, bssid[0], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, bssid[1], op_write);
+ memcpy(piperp->bssid, conf->bssid, ETH_ALEN);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((conf->changed & IEEE80211_IFCC_BEACON) &&
+ (piperp->if_type == NL80211_IFTYPE_ADHOC)) {
+ struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ struct ieee80211_rate rate;
+
+ if (!beacon)
+ return -ENOMEM;
+
+ rate.bitrate = 10; /* beacons always sent at 1 Megabit*/
+ skb_push(beacon, TX_HEADER_LENGTH);
+ phy_set_plcp(beacon->data, beacon->len - TX_HEADER_LENGTH, &rate, 0);
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_BEACON_TX, op_and);
+ piperp->load_beacon(piperp, beacon->data, beacon->len);
+
+ /* TODO: digi->beacon.enabled should be set by IEEE80211_IFCC_BEACON_ENABLED
+ when we update to latest mac80211 */
+ piperp->beacon.enabled = true;
+ piper_enable_ibss(piperp, vif->type);
+ dev_kfree_skb(beacon); /* we are responsible for freeing this buffer*/
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 wants to change our frame filtering settings. We don't
+ * actually support this.
+ */
+static void piper_hw_config_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags, unsigned int *total_flags,
+ int mc_count, struct dev_addr_list *mclist)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ /* we don't support filtering so clear all flags; however, we also
+ * can't pass failed FCS/PLCP frames, so don't clear those. */
+ *total_flags &= (FIF_FCSFAIL | FIF_PLCPFAIL);
+}
+
+/*
+ * There are 1024 TU's (time units) to a second, and HZ jiffies to a
+ * second. This macro converts TUs to jiffies.
+ */
+#define TU_TO_JIFFIES(x) (((x*HZ) + 512) / 1024)
+
+
+/*
+ * mac80211 calls this routine when something about our BSS has changed.
+ * Usually, this routine only gets called when we associate, or disassociate.
+ */
+static void piper_hw_bss_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 reg;
+
+ dprintk(DVVERBOSE, " changed = 0x%08x\n", changed);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ piper_ps_scan_event(piperp);
+ /* Our association status has changed */
+ if (piperp->if_type == NL80211_IFTYPE_STATION) {
+ piper_set_status_led(hw, conf->assoc ? led_associated : led_not_associated);
+ }
+ piperp->areWeAssociated = conf->assoc;
+ piperp->ps.aid = conf->aid;
+
+ digi_dbg(" AP %s\n", conf->assoc ?
+ "associated" : "disassociated");
+ }
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ piperp->tx_cts = conf->use_cts_prot;
+ }
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+#define WANT_SHORT_PREAMBLE_SUPPORT (1)
+/* TODO: Determine if short preambles really hurt us, or if I'm just seeing things. */
+#if WANT_SHORT_PREAMBLE_SUPPORT
+ if (conf->use_short_preamble) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_SH_PRE, op_or);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+ piperp->use_short_preamble = conf->use_short_preamble;
+#else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_SH_PRE, op_or);
+#endif
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ /*
+ * The list of transmit rates has changed. Update the
+ * rates we will receive at to match those the AP will
+ * transmit at. This should improve our receive performance
+ * since we won't listen to junk at the wrong rate.
+ */
+ unsigned int ofdm = 0, psk = 0;
+
+ reg = piperp->ac->rd_reg(piperp, MAC_SSID_LEN) &
+ ~(MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK);
+
+ piperp->rf->getOfdmBrs(piperp->channel, conf->basic_rates, &ofdm, &psk);
+ reg |= ofdm << MAC_OFDM_BRS_SHIFT;
+ reg |= psk << MAC_PSK_BRS_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, reg, op_write);
+
+ dprintk(DVERBOSE, "BRS mask set to 0x%8.8X\n", reg);
+
+ if (ofdm == 0) {
+ /* Disable ofdm receiver if no ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* Enable ofdm receiver if any ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ BB_GENERAL_STAT_A_EN, op_or);
+ }
+ }
+
+ /* Save new DTIM period */
+ reg = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_DTIM_PERIOD_MASK;
+ reg |= conf->dtim_period << MAC_DTIM_PERIOD_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, reg, op_write);
+ reg = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_DTIM_CFP_MASK;
+ piperp->ps.beacon_int = conf->beacon_int;
+ reg |= conf->beacon_int << 16;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, reg, op_write);
+}
+
+/*
+ * Use the SSL library routines to expand the AES key.
+ */
+static int piper_expand_aes_key(struct ieee80211_key_conf *key,
+ u32 *expandedKey)
+{
+ struct crypto_aes_ctx aes;
+ int ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (key->keylen != AES_KEYSIZE_128)
+ return -EOPNOTSUPP;
+
+ ret = crypto_aes_expand_key(&aes, key->key, key->keylen);
+ if (ret)
+ return -EOPNOTSUPP;
+
+ memcpy(expandedKey, aes.key_enc, EXPANDED_KEY_LENGTH);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set a new encryption key, or to
+ * retire an old one. We support H/W AES encryption/decryption, so
+ * save the AES related keys.
+ */
+static int piper_hw_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if ((key->alg != ALG_CCMP) || (key->keyidx >= PIPER_MAX_KEYS)) {
+ /*
+ * If we come here, then mac80211 was trying to set a key for some
+ * algorithm other than AES, or trying to set a key index greater
+ * than 3. We only support AES, and only 4 keys.
+ */
+ ret = -EOPNOTSUPP;
+ goto set_key_error;
+ }
+ key->hw_key_idx = key->keyidx;
+
+ if (cmd == SET_KEY) {
+ ret = piper_expand_aes_key(key, piperp->key[key->keyidx].expandedKey);
+ if (ret)
+ goto set_key_error;
+
+ if (!piperp->key[key->keyidx].valid)
+ piperp->aes_key_count++;
+ piperp->key[key->keyidx].txPn = 0;
+ piperp->key[key->keyidx].rxPn = 0;
+ piperp->key[key->keyidx].valid = (ret == 0);
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ } else {
+ /* disable key */
+ if (piperp->key[key->keyidx].valid)
+ piperp->aes_key_count--;
+ piperp->key[key->keyidx].valid = false;
+ }
+
+ if (piperp->aes_key_count > 0)
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_AES_DISABLE, op_and);
+ else
+ piperp->ac->wr_reg(piperp, MAC_CTL, MAC_CTL_AES_DISABLE, op_or);
+
+set_key_error:
+ if (ret)
+ dprintk(DVERBOSE, "unable to set AES key\n");
+
+ return ret;
+}
+
+/*
+ * mac80211 calls this routine to determine if we transmitted the
+ * last beacon. Unfortunately, we can't tell for sure. We give
+ * mac80211 our best guess.
+ */
+static int piper_hw_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ return piperp->beacon.weSentLastOne ? 1 : 0;
+}
+
+static int piper_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.tx_queue, sizeof(piperp->pstats.tx_queue));
+
+ return 0;
+}
+
+static int piper_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.ll_stats, sizeof(piperp->pstats.ll_stats));
+
+ return 0;
+}
+
+const struct ieee80211_ops hw_ops = {
+ .tx = piper_hw_tx,
+ .start = piper_hw_start,
+ .stop = piper_hw_stop,
+ .add_interface = piper_hw_add_intf,
+ .remove_interface = piper_hw_rm_intf,
+ .config = piper_config,
+ .config_interface = piper_hw_config_intf,
+ .configure_filter = piper_hw_config_filter,
+ .bss_info_changed = piper_hw_bss_changed,
+ .tx_last_beacon = piper_hw_tx_last_beacon,
+ .set_key = piper_hw_set_key,
+ .get_tx_stats = piper_get_tx_stats,
+ .get_stats = piper_get_stats,
+};
+
+/*
+ * This routine is called by the probe routine to allocate the
+ * data structure we need to communicate with mac80211.
+ */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz)
+{
+ struct piper_priv *piperp;
+ struct ieee80211_hw *hw;
+
+ hw = ieee80211_alloc_hw(priv_sz, &hw_ops);
+ if (!hw)
+ return -ENOMEM;
+
+ hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS
+ | IEEE80211_HW_SIGNAL_DBM
+ | IEEE80211_HW_NOISE_DBM
+ | IEEE80211_HW_SPECTRUM_MGMT
+ | IEEE80211_HW_NO_STACK_DYNAMIC_PS
+#if !WANT_SHORT_PREAMBLE_SUPPORT
+ | IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE
+#endif
+ /* | IEEE80211_HW_SPECTRUM_MGMT TODO: Turn this on when we are ready*/;
+
+ hw->queues = 1;
+ hw->ampdu_queues = 0;
+ hw->extra_tx_headroom = 4 + sizeof(struct ofdm_hdr);
+ piperp = hw->priv;
+ *priv = piperp;
+ piperp->pstats.tx_queue.len = 0;
+ piperp->pstats.tx_queue.limit = 1;
+ piperp->pstats.tx_queue.count = 0;
+ piperp->areWeAssociated = false;
+ memset(&piperp->pstats.ll_stats, 0, sizeof(piperp->pstats.ll_stats));
+ piperp->hw = hw;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_alloc_hw);
+
+/*
+ * This routine is called by the remove function to free the memory
+ * allocated by piper_alloc_hw.
+ */
+void piper_free_hw(struct piper_priv *piperp)
+{
+ ieee80211_free_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_free_hw);
+
+/*
+ * This routine is called by the probe routine to register
+ * with mac80211.
+ */
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ u8 macaddr[8];
+ u32 temp;
+ int i, ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ priv->rf = rf;
+ for (i = 0; i < rf->n_bands; i++) {
+ enum ieee80211_band b = rf->bands[i].band;
+ hw->wiphy->bands[b] = &rf->bands[i];
+ }
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_ADHOC)
+ | BIT(NL80211_IFTYPE_STATION)
+/* TODO: Enable this | BIT(NL80211_IFTYPE_MESH_POINT) */
+ ;
+ hw->channel_change_time = rf->channelChangeTime;
+ hw->vif_data_size = 0;
+ hw->sta_data_size = 0;
+ hw->max_rates = IEEE80211_TX_MAX_RATES;
+ hw->max_rate_tries = 5; /* completely arbitrary, and apparently ignored by the rate algorithm */
+ hw->max_signal = rf->maxSignal;
+ hw->max_listen_interval = 10; /* I don't think APs will work with values larger than 4 actually */
+
+ SET_IEEE80211_DEV(hw, dev);
+
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID0));
+ memcpy(macaddr, &temp, sizeof(temp));
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID1));
+ memcpy(&macaddr[4], &temp, sizeof(temp));
+ SET_IEEE80211_PERM_ADDR(hw, macaddr);
+
+ if ((ret = ieee80211_register_hw(hw)) != 0) {
+ dprintk(DERROR, "unable to register ieee80211 hw\n");
+ goto error;
+ }
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": registered as %s\n",
+ wiphy_name(hw->wiphy));
+
+ init_timer(&priv->led_timer);
+ priv->led_state = led_shutdown;
+ priv->led_timer.function = led_timer_fn;
+ priv->led_timer.data = (unsigned long) priv;
+ priv->led_timer.expires = jiffies + LED_TIMER_RATE;
+ add_timer(&priv->led_timer);
+
+error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_register_hw);
+
+
+void piper_unregister_hw(struct piper_priv *piperp)
+{
+ dprintk(DVVERBOSE, "\n");
+ del_timer_sync(&piperp->led_timer);
+ piper_set_status_led(piperp->hw, led_shutdown);
+ ieee80211_unregister_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_unregister_hw);
+
+
+MODULE_DESCRIPTION("Digi Piper WLAN core");
+MODULE_AUTHOR("contact support@digi.com for information about this code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/digiPs.c b/drivers/net/wireless/digiPiper/digiPs.c
new file mode 100644
index 000000000000..c6ae6b0b656a
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.c
@@ -0,0 +1,1164 @@
+/*
+ * digiPs.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "digiPs.h"
+
+/*
+ * Macro converts milliseconds to HZ.
+ *
+ * TODO: Look for standard Linux version of this.
+ */
+#define MILLS_TO_JIFFIES(x) ((((x)*HZ) + 500) / 1000)
+
+
+
+
+#define MINIMUM_SLEEP_PERIOD (20)
+
+/*
+ * Amount of time before shutdown deadline to start the shutdown process. This
+ * should allow enough time to get one last frame out, which will be the
+ * null-data frame notifying the AP that we are shutting down.
+ */
+#define PS_TRANSMITTER_SHUTDOWN_MS (10)
+
+/*
+ * Amount of time we will wake up before the next beacon. We try to wake up before
+ * the next beacon so that we don't miss it.
+ */
+#define PS_WAKE_BEFORE_BEACON_MS (20)
+
+/*
+ * Minimum amount of time we we keep awake for.
+ */
+#define PS_MINUMUM_POWER_UP_PERIOD_MS (10)
+
+/*
+ * Minimum amount of time we will sleep. If we will end up sleeping
+ * for less than this, then don't go to sleep.
+ */
+#define PS_MINIMUM_SLEEP_TIME (10)
+
+/*
+ * Length of one tick of the transmit clean up timer in ms. This is
+ * the minimum amount of time we will sleep for.
+ */
+#define PS_TRANSMIT_TIMER_TICK_MS ((1000/HZ) ? (1000/HZ) : 1)
+
+/*
+ * Length of time we will wait past the expected arrival of a beacon before assuming
+ * that we missed it.
+ */
+#define PS_BEACON_TIMEOUT_MS (40)
+
+
+/*
+ * Minimum beacon interval we will support for duty cycling. There is so much overhead
+ * in duty cycling that it doesn't make sense to do it for short beacon intervals.
+ */
+#define PS_MINIMUM_BEACON_INT (100)
+
+/*
+ * Amount of time we will pause duty cycling for after receiving an event that suggests
+ * wpa_supplicant is attempting to reassociate with an AP.
+ */
+#define PS_SCAN_DELAY (5000)
+
+
+// Powersave register index
+#define INDX_GEN_CONTROL 0 // General control
+#define INDX_GEN_STATUS 1 // General status
+#define INDX_RSSI_AES 2 // RSSI and AES status
+#define INDX_INTR_MASK 3 // Interrupt mask
+#define INDX_SPI_CTRL 4 // RF SPI control
+#define INDX_CONF1 5 // Configuration 1
+#define INDX_CONF2 6 // Configuration 2
+#define INDX_AES_MODE 7 // ARS mode
+#define INDX_OUT_CTRL 8 // Output control
+#define INDX_MAC_CONTROL 9 // MAC control
+#define INDX_STAID_1 10 // first part of stations ID
+#define INDX_STAID_2 11 // 2nd part of station ID
+#define INDX_BSSID_1 12 // 1st part of BSS ID
+#define INDX_BSSID_2 13 // 2nd part of BSS ID
+#define INDX_BRS_SSID 14 // BRS mask and SSID length
+#define INDX_BACKOFF 15 // backoff
+#define INDX_DTIM_LISTEN 16 // DTIM perido and listen interval
+#define INDX_BEACON_INT 17 // beacon interval
+#define INDX_MAC_CTL 18 // MAC control register
+#define INDX_BEACON_MASK 19 // beacon mask and backoff
+#define INDX_TOTAL 20
+
+static u32 savedRegs[INDX_TOTAL]; // Used to save registers for sleep mode
+
+
+/*
+ * TODO: Delete this.
+ */
+struct ps_stats {
+ unsigned int modeStart;
+ unsigned int cycleStart;
+ unsigned int receivedBeacons;
+ unsigned int missedBeacons;
+ unsigned int jiffiesOn;
+ unsigned int jiffiesOff;
+} stats;
+
+
+
+static void ps_free_frame(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ if (skb) {
+ dev_kfree_skb(skb);
+ piperp->ps.frames_pending--;
+ }
+}
+
+
+
+
+
+#define ACK_SIZE (14) /* length of ACK in bytes */
+
+// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
+// not including SIFS and PLCP preamble/header
+#define CCK_DURATION(bytes, rate) ((16*(bytes)+(rate)-1)/(rate))
+
+#define USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ((!is_1_Mbit) & use_short_preamble)
+
+// Length (in usecs) of SIFS and PLCP preamble/header.
+#define PRE_LEN(is_1_Mbit, use_short_preamble) (USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ? 106 : 202)
+
+// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+// including SIFS and PLCP preamble/header
+#define OFDM_DURATION(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate)))
+
+static unsigned int getRateIndex(struct piper_priv *piperp)
+{
+ unsigned int rates = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ unsigned int result = AIROHA_LOWEST_OFDM_RATE_INDEX;
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_2GHZ) {
+ if ((rates & MAC_PSK_BRS_MASK) != 0) {
+ result = AIROHA_LOWEST_PSK_RATE_INDEX;
+ }
+ }
+
+ return result;
+}
+
+static int getAckDuration(struct piper_priv *piperp)
+{
+ bool is_1_Mbit = (getRateIndex(piperp) == AIROHA_LOWEST_PSK_RATE_INDEX);
+ int duration = 0;
+
+ if (is_1_Mbit) {
+ duration = CCK_DURATION(ACK_SIZE, 1);
+ } else {
+ duration = OFDM_DURATION(ACK_SIZE, 6);
+ }
+
+ duration += PRE_LEN(is_1_Mbit, piperp->use_short_preamble);
+
+ return duration;
+}
+
+
+/*
+ * This function is used to notify the AP about the current state of
+ * power save. One of the bits in the 802.11 header field is set to
+ * indicate the status of power save. This bit is actually set appropriately
+ * for all frames sent, we just send a null data frame just to make
+ * sure something is sent to the AP in a reasonable amount of time.
+ */
+/*
+ * Possible values for is_power_management_on argument
+ */
+#define POWERING_DOWN (true)
+#define POWERED_UP (false)
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool is_power_management_on)
+{
+ struct sk_buff *skb = NULL;
+ _80211HeaderType *header;
+ struct ieee80211_tx_info *tx_info;
+
+ skb =
+ __dev_alloc_skb(sizeof(_80211HeaderType) +
+ piperp->hw->extra_tx_headroom, GFP_ATOMIC);
+ if (skb == NULL)
+ goto piper_sendNullDataFrame_Exit;
+
+ tx_info = (struct ieee80211_tx_info *) skb->cb;
+
+ skb_reserve(skb, piperp->hw->extra_tx_headroom);
+ header = (_80211HeaderType *) skb_put(skb, sizeof(_80211HeaderType));
+ memset(header, 0, sizeof(*header));
+ header->fc.type = TYPE_NULL_DATA;
+ header->fc.pwrMgt = is_power_management_on;
+ header->duration = getAckDuration(piperp);
+ memcpy(header->addr1, piperp->bssid, sizeof(header->addr1));
+ memcpy(header->addr2, piperp->pdata->macaddr, sizeof(header->addr2));
+ memcpy(header->addr3, piperp->bssid, sizeof(header->addr3));
+
+ tx_info->flags = IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT;
+ tx_info->band = piperp->rf->getBand(piperp->channel);
+ tx_info->antenna_sel_tx = 1; /* actually this is ignored for now */
+ tx_info->control.rates[0].idx = 0;
+ tx_info->control.rates[0].count = 2; /* no retries. Don't tie us up waiting for an ACK */
+ tx_info->control.rates[0].flags = 0;
+ tx_info->control.rates[1].idx = -1;
+ tx_info->control.rts_cts_rate_idx = -1;
+ piperp->ps.frames_pending++;
+
+ if (piper_hw_tx_private(piperp->hw, skb, ps_free_frame) != 0) {
+ /* printk(KERN_ERR
+ "piper_hw_tx() failed unexpectedly when sending null data frame.\n"); */
+ ps_free_frame(piperp->hw, skb);
+ }
+
+piper_sendNullDataFrame_Exit:
+ return;
+}
+
+
+
+
+#define RESET_PIPER (1) /* must be set to 1 (0 case is only for debugging)*/
+#define PS_DONT_FORCE (false) /* set force to this value if we want to be safe */
+#define PS_FORCE_POWER_DOWN (true) /* set force to this value to shut down the H/W reguardless*/
+/*
+ * This routine shuts down Piper and the Airoha transceiver. First we check to
+ * make sure the driver and H/W are idle. Then we save the state of the H/W.
+ * Then we shut down the Airoha and place piper into reset.
+ */
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force)
+{
+ /*
+ * Interrupts are already disabled when we get here.
+ */
+
+ if (piperp->ps.poweredDown)
+ return 0;
+
+ savedRegs[INDX_INTR_MASK] = piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+ if (!force) {
+ if ( (piperp->ps.rxTaskletRunning)
+ || ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY))
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)
+ & BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0)
+ || (piperp->tx_tasklet_running)
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ || (piperp->ac->rd_reg(piperp, BB_IRQ_STAT) & savedRegs[INDX_INTR_MASK])) {
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ return -1;
+ }
+ }
+
+ disable_irq(piperp->irq);
+
+ savedRegs[INDX_GEN_CONTROL] = piperp->ac->rd_reg(piperp, BB_GENERAL_CTL);
+ savedRegs[INDX_GEN_STATUS] = piperp->ac->rd_reg(piperp, BB_GENERAL_STAT);
+ savedRegs[INDX_RSSI_AES] = piperp->ac->rd_reg(piperp, BB_RSSI) & ~BB_RSSI_EAS_BUSY;
+ savedRegs[INDX_SPI_CTRL] = piperp->ac->rd_reg(piperp, BB_SPI_CTRL);
+ savedRegs[INDX_CONF1] = piperp->ac->rd_reg(piperp, BB_TRACK_CONTROL);
+ savedRegs[INDX_CONF2] = piperp->ac->rd_reg(piperp, BB_CONF_2);
+ savedRegs[INDX_OUT_CTRL] = piperp->ac->rd_reg(piperp, BB_OUTPUT_CONTROL);
+ savedRegs[INDX_MAC_CONTROL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+
+ savedRegs[INDX_STAID_1] = piperp->ac->rd_reg(piperp, MAC_STA_ID0);
+ savedRegs[INDX_STAID_2] = piperp->ac->rd_reg(piperp, MAC_STA_ID1);
+ savedRegs[INDX_BSSID_1] = piperp->ac->rd_reg(piperp, MAC_BSS_ID0);
+ savedRegs[INDX_BSSID_2] = piperp->ac->rd_reg(piperp, MAC_BSS_ID1);
+ savedRegs[INDX_BRS_SSID] = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ savedRegs[INDX_BACKOFF] = piperp->ac->rd_reg(piperp, MAC_BACKOFF);
+ savedRegs[INDX_DTIM_LISTEN] = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD);
+ savedRegs[INDX_BEACON_INT] = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM);
+ savedRegs[INDX_MAC_CTL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+ savedRegs[INDX_BEACON_MASK] = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT);
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and); //disable receiving
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+#if RESET_PIPER
+ // Power down the airoha transceiver
+ piperp->rf->power_on(piperp->hw, false);
+ udelay(10);
+ // hold the transceiver in reset mode
+ if (piperp->pdata->reset)
+ piperp->pdata->reset(piperp, 1);
+#endif
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+ piperp->ps.poweredDown = true;
+
+ return 0;
+}
+
+
+#define PS_NO_SPIKE_SUPPRESSION (false) /* want_spike_suppression = don't want spike suppression*/
+#define PS_WANT_SPIKE_SUPPRESSION (true) /* want_spike_suppression = do spike suppression*/
+/*
+ * Turn the H/W back on after it was shutdown with piper_MacEnterSleepMode.
+ *
+ * 1. Power up the hardware.
+ * 2. Perform the spike suppression routine if desired. The spike suppression
+ * routine resyncs the clocks in Piper in order to prevent us from generating
+ * noise spikes at 1 and 2 MHz. Unfortunately it takes an indeterminate amount
+ * of time, so we normally don't do it and just make sure we do not send at
+ * at those data rates while duty cycling.
+ * 3. Set the channel. The Airoha was shut off so we have to set the channel
+ * again.
+ * 4. Restore the state of piper registers.
+ * 5. Zero out and reset the transmitter FIFO. Mike Schaffner claims this should
+ * not be necessary, but we seem to run into trouble when we don't do it.
+ * 6. Restore the interrupts.
+ */
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression)
+{
+ int i;
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+ static unsigned int run = 0;
+#endif
+
+#if RESET_PIPER
+
+ if (piperp->pdata->reset) {
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** While in reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** While in reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+#endif
+ piperp->pdata->reset(piperp, 0);
+ udelay(10);
+
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** After reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** After reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ run++;
+#endif
+ piperp->rf->power_on(piperp->hw, true);
+ mdelay(1);
+ piper_spike_suppression(piperp, want_spike_suppression);
+ }
+#endif
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+
+#if RESET_PIPER
+ piperp->rf->set_chan_no_rx(piperp->hw, piperp->channel);
+#endif
+
+ // store the registers back
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x30000000, op_write);
+ piperp->ac->wr_reg(piperp, BB_RSSI, savedRegs[INDX_RSSI_AES] & ~BB_RSSI_EAS_BUSY, op_write);
+
+// piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, savedRegs[INDX_SPI_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, savedRegs[INDX_CONF1], op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, savedRegs[INDX_CONF2], op_write);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, savedRegs[INDX_OUT_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CONTROL], op_write);
+
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, savedRegs[INDX_STAID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, savedRegs[INDX_STAID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, savedRegs[INDX_BSSID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, savedRegs[INDX_BSSID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, savedRegs[INDX_BRS_SSID], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF, savedRegs[INDX_BACKOFF], op_write);
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, savedRegs[INDX_DTIM_LISTEN], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, savedRegs[INDX_BEACON_INT], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CTL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT, savedRegs[INDX_BEACON_MASK], op_write);
+
+//****
+ // set bit-11 in the general control register to a 1 to start the processors
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+
+ // set the TX-hold bit
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+
+ // reset the TX-FIFO and RX-FIFO
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200E0, op_write);
+
+
+ // release the TX-hold and reset
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+
+//***
+ /*
+ * Reset the interrupt mask. We could have been receiving a frame when we
+ * powered down. This could cause us to store the wrong mask, so we want
+ * to make sure we enabe the RX interrupts. However, we should not have the
+ * TX interrupts enabled when we come out of power save mode.
+ */
+ udelay(50);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ (savedRegs[INDX_GEN_CONTROL] | 0x37000000 |
+ BB_GENERAL_CTL_RX_EN), op_write);
+
+ stats.jiffiesOff += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+
+ /* TODO, this is a temporary workaround and should be better analyzed in future.
+ * The problem is that the general power save code is not synchronized with the
+ * dynamic PW and this is causing that, the following line, unbalances the
+ * piper wireless irq */
+ if (piperp->ps.poweredDown != false)
+ enable_irq(piperp->irq);
+
+ piperp->ps.poweredDown = false;
+}
+
+
+
+
+/*
+ * So what crazy thing are we doing here? Well, Piper has a bug where it
+ * can send noise spikes at 1 Mbps and 2 Mbps if it is powered up without
+ * running a special spike suppression routine. The spike suppression code
+ * takes an average of 30 ms, and I have timed it taking as long as 300 ms.
+ * This is not something you want to use for duty cycling. The solution is
+ * to avoid sending at those two rates. After the transmit routine determines
+ * the rate mac80211 specified, it will call us and we will decide whether
+ * we like that rate. If it is one of our bad rates, then we will bump it
+ * up to a good rate.
+ */
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate)
+{
+#define BAD_RATE_1MBPS (10)
+#define BAD_RATE_2MBPS (20)
+ if ((piperp->ps.mode == PS_MODE_LOW_POWER) && (rate != NULL)) {
+ if ((rate->bitrate == BAD_RATE_1MBPS)
+ || (rate->bitrate == BAD_RATE_2MBPS)) {
+ rate =
+ (struct ieee80211_rate *) piperp->rf->
+ getRate(AIROHA_55_MBPS_RATE_INDEX);
+ }
+ }
+
+ return rate;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_check_rate);
+
+
+
+/*
+ * This routine restarts the transmitter after powering back
+ * up, or failing to power down.
+ *
+ * 1) Clear the power management bit so that frames will be
+ * sent indicating that we are poweredup.
+ * 2) Set the flag to allow transmits again.
+ * 3) If we stopped the mac80211 transmitter queue, then start
+ * it back up again.
+ * 4) Notify the AP that we are awake by sending a null-data frame
+ * with the power management bit clear.
+ */
+static void ps_resume_transmits(struct piper_priv *piperp)
+{
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ ieee80211_wake_queues(piperp->hw);
+}
+
+
+
+
+
+
+/*
+ * This routine sets an event timer. The ps_state_machine routine
+ * will be executed when the event timer expires.
+ */
+static void ps_set_timer_event(struct piper_priv *piperp,
+ enum piper_ps_event next_event,
+ unsigned int timeout_ms)
+{
+ unsigned int delay_in_jiffies = MILLS_TO_JIFFIES(timeout_ms);
+
+ if (delay_in_jiffies == 0)
+ delay_in_jiffies++;
+
+ del_timer_sync(&piperp->ps.timer);
+ piperp->ps.this_event = next_event;
+ piperp->ps.timer.expires = delay_in_jiffies + jiffies;
+ add_timer(&piperp->ps.timer);
+}
+
+
+/*
+ * This routine cancels an event timer set by ps_set_timer_event.
+ */
+static void ps_cancel_timer_event(struct piper_priv *piperp)
+{
+ del_timer_sync(&piperp->ps.timer);
+}
+
+
+#define WANT_STATE_MACHINE_DEBUG (0)
+#if WANT_STATE_MACHINE_DEBUG
+
+struct event_record {
+ enum piper_ps_event event;
+ enum piper_ps_state state;
+};
+
+#define MAX_EVENTS_RECORDED (15)
+static unsigned int debug_events_index = 0;
+static struct event_record debug_events[MAX_EVENTS_RECORDED];
+
+void debug_track_event(enum piper_ps_event event, enum piper_ps_state state)
+{
+ debug_events[debug_events_index].event = event;
+ debug_events[debug_events_index].state = state;
+ if (debug_events_index == MAX_EVENTS_RECORDED) {
+ unsigned int i;
+
+ printk(KERN_ERR);
+ for (i = 0; i < MAX_EVENTS_RECORDED; i++) {
+ printk("(%d,%d)", debug_events[i].event,
+ debug_events[i].state);
+ }
+ printk("\n");
+ debug_events_index = 0;
+ } else {
+ debug_events_index++;
+ }
+}
+
+
+#else
+ #define debug_track_event(event, state)
+#endif
+
+/*
+ * This is the entry point into the duty cycle state machine. It may be called
+ * by:
+ *
+ * 1) piper_ps_handle_beacon when a beacon frame is received.
+ * 2) the receiver task when we receive the ACK for the last pending frame
+ * when we are waiting to power down.
+ * 3) by ps_timer for many timer events.
+ */
+static void ps_state_machine(struct piper_priv *piperp, enum piper_ps_event event)
+{
+ unsigned long flags;
+
+ debug_track_event(event, piperp->ps.state);
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+
+ switch (event) {
+ case PS_EVENT_BEACON_RECEIVED:
+ /*
+ * We just received a beacon. This is really the driving event in this state
+ * machine. Everything else is synchronized from it.
+ *
+ * We know we are powered up because we just received the beacon. The normal
+ * control path is to set a timer which will expire when we need to start
+ * preparations for powering down again.
+ */
+ ps_cancel_timer_event(piperp); /* cancel missed beacon timer*/
+ stats.receivedBeacons++;
+ if ( (!piperp->areWeAssociated)
+ || (piperp->ps.beacon_int < PS_MINIMUM_BEACON_INT)
+ || (piperp->ps.scan_timer != 0)) {
+ /*
+ * Don't duty cyle while we are trying to associate.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if (piperp->ps.scan_timer) {
+ piperp->ps.scan_timer--;
+ }
+ break;
+ }
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_BEACON) {
+ int timeout;
+ /*
+ * Calculate amount of time to sleep.
+ */
+ piperp->ps.sleep_time = (piperp->ps.beacon_int * (100 - piperp->power_duty)) / 100;
+
+ /*
+ * Now figure out how long we have before it's time to go to sleep. We
+ * have to wake up at least PS_WAKE_BEFORE_BEACON_MS before we expect to
+ * receive the next beacon, and we need to start powering down at least
+ * PS_TRANSMITTER_SHUTDOWN_MS ahead of time.
+ */
+ timeout = piperp->ps.beacon_int - (piperp->ps.sleep_time + PS_TRANSMITTER_SHUTDOWN_MS + PS_WAKE_BEFORE_BEACON_MS);
+ if ((timeout > 0) && (piperp->ps.sleep_time > PS_MINIMUM_SLEEP_TIME)) {
+ /*
+ * If there is enough time left that it makes sense to duty
+ * cycle, then program the timer and advance to the next state.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT;
+ ps_set_timer_event(piperp, PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED, timeout);
+
+ break;
+ }
+ } else {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_resume_transmits(piperp);
+ }
+ digi_dbg("*** Beacon event in state %d.\n", piperp->ps.state);
+ }
+ /*
+ * We will come here if we were in the wrong state when we received the
+ * beacon, or if the duty cycle is too short.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED:
+ /*
+ * This event is hit when it's time to start powering down. Unfortunately, this is
+ * not a simple thing to do. The first things to do are:
+ *
+ * 1. Set the power save on flag. This will cause any transmit frame to be
+ * sent with the power management bit set.
+ * 2. Stop the mac80211 layer from sending us anymore frames.
+ * 3. Signal the AP that we are powering down by sending a null-data frame with the
+ * power management bit set.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ if (piperp->ps.scan_timer) {
+ /*
+ * Don't shut down if we are scanning.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ break;
+ }
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * This should never happen (famous last words)
+ */
+ digi_dbg("** stop tx event, state = %d.\n", piperp->ps.state);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ }
+ break;
+ case PS_EVENT_TRANSMITTER_DONE:
+ /*
+ * This event is triggered when the receive task finishes processing the ACK
+ * from the null-data frame sent by the PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED event.
+ * We try to power down now.
+ */
+ if (piperp->ps.scan_timer == 0) {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_cancel_timer_event(piperp); /* cancel transmitter done timeout timer*/
+ if (piper_MacEnterSleepMode(piperp, PS_DONT_FORCE) == 0) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_WAKEUP_ALARM;
+ /*
+ * Note that the value PS_EVENT_TRANSMITTER_DONE_TIMER_TICK is
+ * updated as necessary by the PS_EVENT_TRANSMITTER_DONE_TIMER_TICK
+ * event to take into account the amount of time it took for the
+ * transmitter to finish sending the last frame.
+ */
+ ps_set_timer_event(piperp, PS_EVENT_WAKEUP, piperp->ps.sleep_time);
+ break;
+ }
+ } else {
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "couldn't sleep, rxt=%d, AES busy = %d, txfifo=%d, txt=%d, rxfifo=%d\n",
+ (piperp->ps.rxTaskletRunning),((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) != 0),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0),
+ (piperp->tx_tasklet_running),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0));
+#endif
+ digi_dbg("** PS_EVENT_TRANSMITTER_DONE event, but state == %d.\n", piperp->ps.state);
+ }
+ }
+ /*
+ * If we fall through to here, then either we are in the wrong state, or we were
+ * not able to power down the H/W.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_TRANSMITTER_DONE_TIMER_TICK:
+ /*
+ * This event is triggered periodically while we are waiting for the
+ * transmitter to finish sending that last packet. We decrement
+ * piperp->ps.sleep_time (which is used by the PS_EVENT_TRANSMITTER_DONE
+ * event). If piperp->ps.sleep_time is still larger than our minimum
+ * required sleep time, then we just restart the timer.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ if (piperp->ps.sleep_time >= PS_MINIMUM_SLEEP_TIME) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * Transmitter did not shut down in time. Resume normal operations
+ * and stay awake until the next beacon.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.sleep_time + PS_WAKE_BEFORE_BEACON_MS
+ + PS_BEACON_TIMEOUT_MS);
+ }
+ } else if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ /*
+ * The piper_ps_rx_task_exiting routine sets this state just before it
+ * releases the lock on ps.state and calls this event. If we ever
+ * come here, then the timer tick occurred just between the time
+ * piper_ps_rx_task_exiting released the lock and ps_state_machine
+ * reset it. Since the tx done event is in progress, we should ignore
+ * this tick.
+ */
+ break;
+ } else {
+ digi_dbg("** done tick in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_WAKEUP:
+ /*
+ * This event is called when we have powered down and it is time
+ * to power back up again.
+ *
+ * 1) Power up the H/W.
+ * 2) Resume normal operations
+ * 3) Update our state.
+ * 4) Set a timeout for receiving the next beacom frame.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_WAKEUP_ALARM) {
+ piper_MacEnterActiveMode(piperp, PS_NO_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ PS_BEACON_TIMEOUT_MS + PS_WAKE_BEFORE_BEACON_MS);
+ } else {
+ digi_dbg("** wake event in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_MISSED_BEACON:
+ /*
+ * This event is called when we miss a beacon. For now just update
+ * our statistics.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if ((piperp->areWeAssociated) && (piperp->ps.scan_timer == 0)) {
+ stats.missedBeacons++;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, piperp->ps.beacon_int);
+ }
+ break;
+ default:
+ digi_dbg("**** ps_state_machine received unknown event %d.\n", event);
+ break;
+ }
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+
+}
+
+/*
+ * This routine is called by the receiver task when it exits. We use it to generate
+ * the PS_EVENT_TRANSMITTER_DONE event. The event signifies that the transmitter has
+ * sent our null-data frame and is idle. We determine this by checking frames_pending,
+ * while will be nonzero if a null-data frame is waiting to be sent, and the machine's
+ * state.
+ */
+void piper_ps_rx_task_exiting(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ if ( (piperp->ps.frames_pending == 0)
+ && (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)) {
+ /*
+ * We have a race condition between the transmitter done tick and
+ * this routine. So this routine changes the state to
+ * PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT before it releases the
+ * lock so that we don't get confused.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT;
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ ps_state_machine(piperp, PS_EVENT_TRANSMITTER_DONE);
+ } else {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ }
+}
+
+/*
+ * Called when the event timer expires. Call the state machine to handle
+ * the event.
+ */
+static void ps_timer(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+
+ ps_state_machine(piperp, piperp->ps.this_event);
+}
+
+
+
+/*
+ * This routine is called when we receive a beacon. We extract the beacon interval
+ * in case it has changed and then call the state machine.
+ */
+static void piper_ps_handle_beacon(struct piper_priv *piperp, struct sk_buff *skb)
+{
+#define BEACON_INT_LSB (8)
+#define BEACON_INT_MSB (9)
+ u32 beacon_int;
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+ bool fromOurAp = piperp->areWeAssociated
+ && (memcmp(piperp->bssid, header->addr3, sizeof (header->addr3)) == 0);
+
+ /*
+ * mac80211 does not inform us when the beacon interval changes, so we have
+ * to read this information from the beacon ourselves.
+ */
+
+ if (fromOurAp) {
+ beacon_int = skb->data[sizeof(_80211HeaderType) + BEACON_INT_LSB];
+ beacon_int |= (skb->data[sizeof(_80211HeaderType) + BEACON_INT_MSB] << 8);
+ piperp->ps.beacon_int = beacon_int;
+
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ ps_state_machine(piperp, PS_EVENT_BEACON_RECEIVED);
+ }
+ }
+}
+
+
+
+/*
+ * This routine is called when mac80211 starts doing things that might indicate it
+ * is attempting to scan or reassociate. Things like changing the channel or
+ * disassociating. When we receive an event like that, we stop duty cycling for
+ * a while since it may interfere with attempts to reassociate with an access point.
+ */
+void piper_ps_scan_event(struct piper_priv *piperp)
+{
+ (void) piperp;
+#if 0
+ /*
+ * It appears that pausing duty cycling during association events may actually
+ * worsen performance I suspect that either the AP or mac80211 is measuring
+ * our throughput and adjusting the load accordingly, and that momentary changes
+ * in performance caused by pausing duty cyling interfere with this.
+ *
+ * TODO: Consider removing this code. I left it in for now in case we decide
+ * to try it again, but if we're not going to use it, it just makes the
+ * driver more confusing and should be removed.
+ */
+ if (piperp->ps.beacon_int != 0) {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / piperp->ps.beacon_int;
+ } else {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / 100;
+ }
+#endif
+}
+
+
+
+/*
+ * This routine is called so we can process incoming frames. We do the
+ * handshaking to receive buffered frames in PS mode here.
+ */
+void piper_ps_process_receive_frame(struct piper_priv *piperp, struct sk_buff *skb)
+{
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+
+ if (header->fc.type == TYPE_BEACON) {
+ piper_ps_handle_beacon(piperp, skb);
+ } else if ( (header->fc.type == TYPE_ASSOC_RESP)
+ || (header->fc.type == TYPE_REASSOC_RESP)
+ || (header->fc.type == TYPE_PROBE_RESP)
+ || (header->fc.type == TYPE_DISASSOC)
+ || (header->fc.type == TYPE_DEAUTH)
+ || (header->fc.type == TYPE_ACTION)) {
+ piper_ps_scan_event(piperp);
+ }
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_process_receive_frame);
+
+
+
+/*
+ * This function turns power save mode on or off.
+ */
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn)
+{
+#define MAX_SHUTDOWN_TIMEOUT (100)
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ piper_ps_scan_event(piperp);
+ if (powerSaveOn) {
+ if (piperp->ps.beacon_int >= PS_MINIMUM_BEACON_INT) {
+ if (piperp->ps.mode != PS_MODE_LOW_POWER) {
+ piperp->ps.aid = 0;
+ piperp->ps.mode = PS_MODE_LOW_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ stats.receivedBeacons = 0;
+ stats.missedBeacons = 0;
+ stats.modeStart = jiffies;
+ stats.cycleStart = jiffies;
+ stats.jiffiesOff = 0;
+ stats.jiffiesOn = 0;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ /*
+ * Will start it the next time we receive a beacon.
+ */
+ }
+ } else {
+ printk(KERN_ERR
+ "\nUnable to set power save mode because the beacon \n"
+ "interval set on this access point less than 100ms.\n");
+ }
+ } else {
+ ps_cancel_timer_event(piperp);
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ piperp->ps.mode = PS_MODE_FULL_POWER; /* stop duty cycle timer */
+ if (piperp->ps.poweredDown) {
+ /*
+ * If we were powered down, then power up and do the spike suppression.
+ */
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ } else {
+ unsigned int timeout = 50;
+ int result;
+ /*
+ * If we branch here, then we were already powered up. You would think
+ * that we would be all set, but it's not that easy. Piper has a bug in
+ * it where we have to run a special spike suppression routine when we
+ * power it up. However, this routine takes an average of 30 ms to run,
+ * and I've see it take as long as 300 ms. This is not acceptable when
+ * we are duty cycling every 100 ms. To get around this, we do NOT do
+ * the spike suppression while duty cycling. Instead, we simply avoid
+ * transmitting at those rates which would cause spikes. Now, however,
+ * we are ending duty cycling and returning to normal operations so we
+ * have to do the spike suppression. Since we are powered up, the first
+ * thing to do is to power down.
+ */
+ if (piperp->ps.state != PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ /*
+ * If we come here, then we did not happen to be trying to power down
+ * just as we got the command from mac80211, so we have to start
+ * the procedure. This is the normal case.
+ *
+ * 1. Set the power save on flag. This will cause frames to be
+ * transmitted with the power management bit on. The reason
+ * for doing that is to tell the AP to stop sending us frames.
+ * 2. Stop the mac80211 layer from sending us more frames by stopping
+ * the transmit queues.
+ * 3. Send a null-data frame to the AP with the power management bit
+ * set. This should cause it to stop sending us frames.
+ */
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ }
+ /*
+ * Now wait for that last frame to go and and then shut down.
+ */
+ result = -1;
+ for (timeout = 0; (timeout < MAX_SHUTDOWN_TIMEOUT) && (result != 0); timeout++) {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(10);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ result = piper_MacEnterSleepMode(piperp, PS_DONT_FORCE);
+ }
+ if (result != 0) {
+ /*
+ * This is bad. For some reason we are not able to power down. We
+ * will try to force it now, but this may end up putting the driver
+ * or H/W into a bad state. However, we can't sit in the loop above
+ * forever either.
+ */
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "Forcing Piper to power down\n");
+ printk(KERN_ERR "BB_RSSI_EAS_BUSY = %d\n", piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY);
+ printk(KERN_ERR "BB_GENERAL_CTL_TX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_EMPTY);
+ printk(KERN_ERR "BB_GENERAL_STAT_RX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) & BB_GENERAL_STAT_RX_FIFO_EMPTY);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+#endif
+ piper_MacEnterSleepMode(piperp, PS_FORCE_POWER_DOWN);
+ }
+ /*
+ * Wait a moment and then power the H/W back up and execute the spike suppression
+ * routine.
+ */
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(30);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ }
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+#define WANT_STATS (0)
+#if WANT_STATS
+ if ((piperp->ps.beacon_int != 0)
+ && ((jiffies - stats.modeStart) != 0)) {
+ printk(KERN_ERR
+ "jiffiesOff = %u, jiffiesOn = %u, total time = %lu\n",
+ stats.jiffiesOff, stats.jiffiesOn,
+ (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Powered down %ld percent of the time.\n",
+ (stats.jiffiesOff * 100) / (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Received %u of %lu beacons while in powersave mode.\n",
+ stats.receivedBeacons,
+ (jiffies -
+ stats.modeStart) /
+ MILLS_TO_JIFFIES(piperp->ps.beacon_int));
+ printk(KERN_ERR "received %d beacons, missed %d\n",
+ stats.receivedBeacons, stats.missedBeacons);
+ printk(KERN_ERR "allowTransmits = %d, stopped_tx_queues = %d, q_count = %d\n",
+ piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues,
+ piperp->tx_queue_count);
+ if ((stats.receivedBeacons + stats.missedBeacons) != 0)
+ printk(KERN_ERR "%d%% beacons were missed\n",
+ (100 * stats.missedBeacons) / (stats.receivedBeacons + stats.missedBeacons));
+ }
+#endif
+ }
+ piperp->ps.aid = 0;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ ps_resume_transmits(piperp);
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ }
+
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_set);
+
+
+
+/*
+ * Called when driver is loaded. Initialize our context.
+ */
+void piper_ps_init(struct piper_priv *piperp)
+{
+ memset(&piperp->ps, 0, sizeof(piperp->ps));
+ piperp->ps.beacon_int = 100;
+ piperp->ps.aid = 0;
+ init_timer(&piperp->ps.timer);
+ piperp->ps.timer.function = ps_timer;
+ piperp->ps.timer.data = (unsigned long) piperp;
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ spin_lock_init(&piperp->ps.lock);
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.rxTaskletRunning;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piperp->ps.frames_pending = 0;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_init);
+
+
+/*
+ * Called when driver is unloaded. Make sure the PS
+ * timer is shut down.
+ */
+void piper_ps_deinit(struct piper_priv *piperp)
+{
+ piper_ps_set(piperp, true);
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ del_timer_sync(&piperp->ps.timer);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_deinit);
diff --git a/drivers/net/wireless/digiPiper/digiPs.h b/drivers/net/wireless/digiPiper/digiPs.h
new file mode 100644
index 000000000000..1feedd87e228
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.h
@@ -0,0 +1,50 @@
+/*
+ * Header file for digiPs.c. Declares functions used for power save mode.
+ */
+
+#ifndef digiPs_h
+
+#define digiPS_h
+
+#include <net/mac80211.h>
+#include "pipermain.h"
+
+enum piper_ps_event {
+ PS_EVENT_BEACON_RECEIVED,
+ PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED,
+ PS_EVENT_TRANSMITTER_DONE,
+ PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_EVENT_WAKEUP,
+ PS_EVENT_MISSED_BEACON
+};
+
+enum piper_ps_tx_completion_result {
+ PS_RETURN_SKB_TO_MAC80211,
+ PS_DONT_RETURN_SKB_TO_MAC80211
+};
+
+enum piper_ps_active_result {
+ PS_CONTINUE_TRANSMIT,
+ PS_STOP_TRANSMIT
+};
+
+
+/*
+ * Current version of mac80211 doesn't set power management bit in frame headers,
+ * so I guess we have to for now.
+ *
+ * TODO: See if we still have to do this in the next drop.
+ */
+#define piper_ps_set_header_flag(piperp, header) \
+ header->fc.pwrMgt = (piperp->ps.power_management)
+
+int piper_ps_active(struct piper_priv *piperp);
+void piper_ps_process_receive_frame(struct piper_priv *piperp,
+ struct sk_buff *skb);
+void piper_ps_init(struct piper_priv *piperp);
+void piper_ps_deinit(struct piper_priv *piperp);
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn);
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/digiRx.c b/drivers/net/wireless/digiPiper/digiRx.c
new file mode 100644
index 000000000000..01ff486311b1
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiRx.c
@@ -0,0 +1,412 @@
+/*
+ * digiRx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define WANT_RECEIVE_COUNT_SCROLL (0)
+#define AES_TIMEOUT (200)
+#define RX_DEBUG (1)
+
+#if RX_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+
+/*
+ * This routine is called to flush the receive and transmit FIFOs. It is used
+ * for error recovery when we detect corrupted data in the FIFO's. It should be
+ * called with the AES lock set.
+ */
+static void reset_fifo(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RXFIFORST, op_and);
+}
+
+/*
+ * This routine is called to receive a frame. The hardware header has
+ * already been read from the FIFO. We need to read out the frame. If
+ * the frame was encrypted with AES and we have the correct key, then
+ * we use the AES H/W encryption engine to decrypt the frame. We also
+ * set up a ieee80211_rx_status structure with the appropriate info.
+ *
+ * Arguments
+ * digi context information
+ * skb empty buffer to receive packet into
+ * length number of bytes in FIFO
+ * fr_ctrl_field buffer to copy frame control header into
+ * status status structure we must write status into
+ *
+ * Returns
+ * true frame was received
+ * false an encryption error was detected
+ */
+static bool receive_packet(struct piper_priv *piperp, struct sk_buff *skb, int length,
+ frameControlFieldType_t * fr_ctrl_field,
+ struct ieee80211_rx_status *status)
+{
+ _80211HeaderType *header;
+ bool result = true;
+ int originalLength = length;
+ int headerlen;
+#if WANT_RECEIVE_COUNT_SCROLL
+ static int packetCount = 0;
+#endif
+
+ headerlen = _80211_HEADER_LENGTH;
+ if (length < _80211_HEADER_LENGTH) {
+ /*
+ * If we branch here, then there is not enough data to make a
+ * complete header. This is possible if this is a control frame.
+ * Adjust our length so that we do not read too much data from
+ * the FIFO.
+ */
+ headerlen = length;
+ }
+
+ /*
+ * Read the frame header. This includes the frame control fields
+ * as well as the 802.11 header.
+ */
+ header = (_80211HeaderType *) skb_put(skb, headerlen);
+ length -= headerlen;
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (uint8_t *) header, headerlen);
+ memcpy(fr_ctrl_field, &header->fc, sizeof(fr_ctrl_field));
+
+ if (((u32)(skb->tail)) & 0x3) {
+ /* align data */
+ skb_reserve(skb, 4 - ((u32)(skb->tail) & 0x3));
+ }
+
+ if (header->fc.protected) {
+ /*
+ * If we branch here, then the frame is encrypted. We need
+ * to figure out if we should try to decrypt it.
+ */
+ unsigned char *rsnHeader;
+ unsigned int aesDataBlob[AES_BLOB_LENGTH / sizeof(unsigned int)];
+ unsigned int keyIndex;
+
+ rsnHeader = skb_put(skb, PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 1: Read the rest of the unencrypted data, which should
+ * consist of the extiv fields.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, rsnHeader, PIPER_EXTIV_SIZE);
+ length -= PIPER_EXTIV_SIZE;
+ keyIndex = rsnHeader[3] >> 6;
+
+ if (piper_prepare_aes_datablob(piperp, keyIndex, (u8 *) aesDataBlob,
+ (unsigned char *)header, originalLength - 12,
+ false)) {
+ /*
+ * If we come here, then we have the correct encryption key for
+ * the frame and will now try to decrypt it.
+ */
+ unsigned int timeout = AES_TIMEOUT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->aesLock, flags);
+
+ /*
+ * Step 2: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ dprintk(DWARNING, "1st AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * Recover by flushing the FIFO and returning in error.
+ */
+ reset_fifo(piperp);
+#if 0
+ /*
+ * TODO: Figure out why this code snippet doesn't work. I would think
+ * that if we reset the fifo, we should just return in error since we will
+ * have discarded the frame. However, when we do that the system hangs
+ * (after a while). This doesn't make sense.
+ */
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ result = false;
+ goto receive_packet_exit;
+#else
+ break;
+#endif
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 3: Set the AES mode, and then read from the AES control
+ * register to put the AES engine into receive mode.
+ */
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[keyIndex].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)aesDataBlob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, read the unencrypted frame from the
+ * AES FIFO. Adjust the length so that we don't try
+ * to read the MIC or the ICV which the AES engine will
+ * process for us.
+ */
+ length -= MIC_SIZE + ICV_SIZE;
+ piperp->ac->rd_fifo(piperp, BB_AES_FIFO, skb_put(skb, length), length);
+ /*
+ * mac80211 seems to expect there to be a MIC even if the packet
+ * has already been decrypted. It will drop off what it thinks
+ * are the extra MIC bytes, so add some extra bytes that it
+ * can drop off without losing any data.
+ */
+ skb_put(skb, MIC_SIZE); /* add fake MIC */
+
+ /*
+ * Step 7: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ dprintk(DWARNING, "2nd AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ }
+ udelay(1);
+ }
+ result = ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_MIC) != 0);
+ timeout = 500;
+ while ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_FIFO_EMPTY) == 0) {
+ timeout--;
+ piperp->ac->rd_reg(piperp, BB_AES_FIFO);
+ udelay(1);
+ }
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ digi_dbg("AES busy set at end of rx\n");
+ }
+#endif
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+
+ /* pad an extra 8 bytes for the MIC which the H/W strips */
+ skb_put(skb, 8);
+ if (result) {
+ status->flag |= RX_FLAG_DECRYPTED;
+ } else {
+ digi_dbg("Error decrypting packet\n");
+ }
+ } else {
+ /*
+ * If we branch here, then we are not able to decrypt the
+ * packet possibly because we don't have the key, or because
+ * the packet was encrypted using TKIP. Read the rest of the
+ * encrypted data. mac80211 will have to decrypt it in software.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length),
+ length);
+ }
+ } else {
+ /*
+ * Frame is not encrypted, so just read it.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length), length);
+ }
+
+#if WANT_RECEIVE_COUNT_SCROLL
+ if (((++packetCount) & 1023) == 0) {
+ printk(KERN_ERR "\n%d recd, tx_start_count = %d, tx_complete_count = %d.\n",
+ packetCount, piperp->pstats.tx_start_count,
+ piperp->pstats.tx_complete_count);
+ }
+#endif
+#if 0
+receive_packet_exit:
+#endif
+ return result;
+}
+
+/*
+ * This routine is called when we receive an ACK. This should be after
+ * we have transmitted a packet. We need to tell the upper layer we
+ * have a packet by calling ieee80211_tx_status_irqsafe with status
+ * information. The transmit routine also disables queuing whenever we
+ * transmit since we can only transmit one packet at a time, so we need
+ * to reenable to transmit queue too.
+ */
+static inline void handle_ack(struct piper_priv *piperp, int signal_strength)
+{
+ if (piper_tx_getqueue(piperp) && piperp->expectingAck) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ if ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0) {
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ piperp->tx_signal_strength = signal_strength;
+ piperp->tx_result = RECEIVED_ACK;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+}
+
+
+/*
+ * This is the entry point for the receive tasklet. It is executed
+ * to process receive packets. It allocates an SKB and receives
+ * the packet into it.
+ *
+ * We may be called from the receive ISR if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ * is set.
+ */
+void piper_rx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+
+ /*
+ * This while loop will keep executing as long as the H/W indicates there
+ * are more frames in the FIFO to be received.
+ */
+
+ piperp->ps.rxTaskletRunning = true;
+
+ while (((piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) &
+ BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ && (!piperp->ps.poweredDown)) {
+ struct sk_buff *skb = NULL;
+ struct ieee80211_rx_status status = { 0 };
+ struct rx_frame_hdr header;
+ unsigned int length = 0;
+ frameControlFieldType_t fr_ctrl_field;
+
+ /*
+ * Read and process the H/W header. This header is created by
+ * the hardware is is not part of the frame.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (u8 *)&header, sizeof(header));
+ phy_process_plcp(piperp, &header, &status, &length);
+ if ((length == 0) || (length > (RX_FIFO_SIZE - 48))) { /* 48 bytes for padding and related stuff */
+ unsigned long flags;
+
+ dprintk(DERROR, "bogus frame length (%d)\n", length);
+ dprintk(DERROR, "0x%08x 0x%08x\n", *(u32 *)&header, *(((u32 *)&header) + 1));
+ spin_lock_irqsave(&piperp->aesLock, flags);
+ reset_fifo(piperp);
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ continue;
+ }
+
+ if (length != 0) {
+
+ skb = __dev_alloc_skb(RX_FIFO_SIZE + 100, GFP_ATOMIC);
+ if (skb == NULL) {
+ /* Oops. Out of memory. Exit the tasklet */
+ dprintk(DERROR, "__dev_alloc_skb failed\n");
+ break;
+ }
+
+ if (receive_packet(piperp, skb, length, &fr_ctrl_field, &status)) {
+
+ if (length >= _80211_HEADER_LENGTH)
+ {
+ /*
+ * If using the Airoha transceiver, then we want to monitor
+ * the receive signal strength and continuously adjust the
+ * receive amplifier so that we get the best possible signal
+ * to noise ratio.
+ */
+ unsigned int rssi = phy_determine_rssi(&header);
+
+ piperp->adjust_max_agc(piperp, rssi, (_80211HeaderType *) skb->data);
+ }
+
+ if (fr_ctrl_field.type == TYPE_ACK)
+ handle_ack(piperp, status.signal);
+
+ if ((fr_ctrl_field.type == TYPE_ACK)
+ || (fr_ctrl_field.type == TYPE_RTS)
+ || (fr_ctrl_field.type == TYPE_CTS)) {
+ /*
+ * Don't pass up RTS, CTS, or ACK frames. They just
+ * confuse the stack
+ */
+ dev_kfree_skb(skb);
+ } else {
+ if (fr_ctrl_field.type == TYPE_BEACON) {
+ piperp->beacon.weSentLastOne = false;
+ }
+
+ piper_ps_process_receive_frame(piperp, skb);
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ ieee80211_rx_irqsafe(piperp->hw, skb, &status);
+#else
+ ieee80211_rx(piperp->hw, skb, &status);
+#endif
+ }
+ } else {
+ /* Frame failed MIC, so discard it */
+ dprintk(DWARNING, "dropping bad frame\n");
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+ piperp->ps.rxTaskletRunning = false;
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ piper_ps_rx_task_exiting(piperp);
+}
+EXPORT_SYMBOL_GPL(piper_rx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiTx.c b/drivers/net/wireless/digiPiper/digiTx.c
new file mode 100644
index 000000000000..4bea846a42a4
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiTx.c
@@ -0,0 +1,604 @@
+/*
+ * digiTx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define FRAME_CONTROL_FIELD_OFFSET (sizeof(struct tx_frame_hdr) + sizeof(struct psk_cck_hdr))
+#define AES_TIMEOUT (200)
+
+#define TX_DEBUG (1)
+
+#if TX_DEBUG
+//static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * Adds an entry into the tx queue.
+ */
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ unsigned long flags;
+ int result = -1;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head) != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_head].skb = skb;
+ piperp->tx_queue[piperp->tx_queue_head].skb_return_cb = skb_return_cb;
+ piperp->tx_queue_head = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head);
+ piperp->tx_queue_count++;
+ result = 0;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(piper_tx_enqueue);
+
+
+/*
+ * Returns the skb for the current element.
+ */
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp)
+{
+ if (piperp->tx_queue_head == piperp->tx_queue_tail) {
+ return NULL;
+ } else {
+ return piperp->tx_queue[piperp->tx_queue_tail].skb;
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_getqueue);
+
+/*
+ * Returns the skb buffer call back for the current element.
+ */
+static inline tx_skb_return_cb_t piper_tx_getqueue_return_fn(struct piper_priv *piperp)
+{
+ return piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb;
+}
+
+
+/*
+ * Called to advance the queue tail.
+ */
+static inline void piper_tx_queue_next(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (piperp->tx_queue_head != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_tail].skb = NULL;
+ piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb = NULL;
+ piperp->tx_queue_tail = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_tail);
+ piperp->tx_queue_count--;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+}
+
+/*
+ * Called when we unload to clear any remaining queue entries.
+ */
+void piper_empty_tx_queue(struct piper_priv *piperp)
+{
+ while (piper_tx_getqueue(piperp)) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+
+ ieee80211_tx_info_clear_status(info);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, piper_tx_getqueue(piperp));
+ piper_tx_queue_next(piperp);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_empty_tx_queue);
+
+
+bool piper_tx_queue_half_full(struct piper_priv *piperp)
+{
+ return (piperp->tx_queue_count >= (PIPER_TX_QUEUE_SIZE >> 1));
+}
+EXPORT_SYMBOL_GPL(piper_tx_queue_half_full);
+
+
+
+/*
+ * This routine writes a frame using H/W AES encryption.
+ *
+ * Arguments
+ * digi context
+ * buffer pointer to start of frame
+ * length number of bytes in frame
+ *
+ * Return Values
+ * 0 success
+ * !0 transmit failed
+ */
+static int piper_write_aes(struct piper_priv *piperp, unsigned char *buffer,
+ unsigned int length)
+{
+ int result;
+ int timeout = AES_TIMEOUT;
+ unsigned long spinLockFlags;
+
+ /*
+ * Step 1: Wait for AES to become ready.
+ */
+ spin_lock_irqsave(&piperp->aesLock, spinLockFlags);
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ digi_dbg("write AES, AES busy stuck on\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * We recover by simply continuing on. Step 3 writes to the AES control
+ * register. This will reset the AES engine and clear the error condition.
+ */
+ break;
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 2: Write the unencrypted part of the frame into the normal
+ * data FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, buffer,
+ _80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 3: Write to the AES control register. Writing to it puts
+ * AES H/W engine into transmit mode. We also make sure
+ * the AES mode is set correctly.
+ */
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[piperp->tx_aes_key].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)piperp->tx_aes_blob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, write the part of the frame that needs to
+ * be encrypted into the AES FIFO.
+ */
+ result =
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ &buffer[_80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE],
+ length - (_80211_HEADER_LENGTH + TX_HEADER_LENGTH +
+ PIPER_EXTIV_SIZE));
+
+ spin_unlock_irqrestore(&piperp->aesLock, spinLockFlags);
+
+ return result;
+}
+
+/*
+ * Determine what bit rate the next retry should be sent at.
+ *
+ * The mac80211 library passes us an array of tx bit rates. Each entry
+ * has a rate index and a limit (max number of retries at that rate).
+ * We use the rate index to build the H/W transmit header. The limit
+ * is decremented each time we retry. When it reaches zero, we try the
+ * next rate in the array.
+ */
+static struct ieee80211_rate *get_tx_rate(struct piper_priv *piperp, struct ieee80211_tx_info *info)
+{
+ struct ieee80211_rate *ret = NULL;
+
+ if (piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index] >=
+ info->control.rates[piperp->pstats.tx_retry_index].count) {
+ piperp->pstats.tx_retry_index++;
+ }
+
+ if (piperp->pstats.tx_retry_index >= IEEE80211_TX_MAX_RATES) {
+ return NULL; /* don't go beyond the end of the rates array */
+ }
+
+ if (piperp->pstats.tx_retry_index == 0) {
+ ret = ieee80211_get_tx_rate(piperp->hw, info);
+ } else {
+ ret = ieee80211_get_alt_retry_rate(piperp->hw, info, piperp->pstats.tx_retry_index - 1);
+ }
+
+ if (ret != NULL) {
+ if (piperp->calibrationTxRate) {
+ ret = piperp->calibrationTxRate;
+ }
+ }
+
+ ret = piper_ps_check_rate(piperp, ret);
+
+ return ret;
+}
+
+/*
+ * This function returns a value for the contention window in microseconds. We
+ * start with the contention window at CW_MIN and double it everytime we have to
+ * retry.
+ */
+static u16 piper_get_cw(struct piper_priv *piperp, bool isFirstTime)
+{
+ static u16 cw = DEFAULT_CW_MIN;
+
+ if (isFirstTime) {
+ cw = DEFAULT_CW_MIN;
+ } else {
+ cw <<= 1;
+ if (cw > DEFAULT_CW_MAX) {
+ cw = DEFAULT_CW_MAX;
+ }
+ }
+ return (cw + (10 * (piperp->rand() & (cw - 1)))) & 0xffff;
+}
+
+/*
+ * This function will prepend an RTS or CTS to self frame ahead of the current
+ * TX frame. This is done if the wantRts or wantCts flag is set. The mac80211
+ * library determines if either of these flags is set.
+ *
+ * The RTS or CTS message is written into the transmit FIFO ahead of the
+ * data frame. Note that RTS and CTS messages are always sent in the clear
+ * so we do not have to worry about encryption.
+ *
+ * Our caller, the master transmit routine, is responsible for setting the
+ * transmit hold bit before calling us and clearing it after the data frame
+ * has been written into the FIFO. This ensures that the RTS/CTS frame is
+ * not transmitted until after the data frame is ready to go.
+ *
+ * Also note that if we are unable to send the RTS/CTS frame, then the H/W
+ * is smart enough to also about the data frame. So we will not send
+ * the data frame without the RTS/CTS frame.
+ */
+static void handle_rts_cts(struct piper_priv *piperp,
+ struct ieee80211_tx_info *txInfo, unsigned int frameType)
+{
+ piperp->tx_rts = false;
+
+ if (frameType == TYPE_DATA) {
+ unsigned int header[2];
+ struct ieee80211_rate *rate = NULL;
+ bool wantCts = (!!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ | piperp->tx_cts);
+ bool wantRts = !!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_RTS_CTS);
+
+ if ((wantRts) || (wantCts)) {
+ /*
+ * If we are sending an RTS or a CTS, then get the rate information.
+ */
+ if (piperp->calibrationTxRate) {
+ rate = piperp->calibrationTxRate;
+ } else {
+ rate = ieee80211_get_rts_cts_rate(piperp->hw, txInfo);
+ }
+ if (rate == NULL) {
+ digi_dbg
+ ("ieee80211_get_rts_cts_rate(digi->hw, txInfo) returned NULL!\n");
+ }
+ }
+ if ((wantRts) && (rate)) {
+ /*
+ * We're sending an RTS, so load it into the FIFO.
+ */
+ struct ieee80211_rts rtsFrame;
+
+ ieee80211_rts_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH, txInfo,
+ &rtsFrame);
+ /*
+ * If we come here, then we need to send an RTS frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_rts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&rtsFrame,
+ sizeof(rtsFrame));
+ if (piperp->pstats.tx_total_tetries != 0) {
+ piperp->pstats.ll_stats.dot11RTSFailureCount++;
+ }
+ piperp->tx_rts = true;
+ } else if ((wantCts) && (rate)) {
+ /*
+ * We're sending a CTS, so load it into the FIFO.
+ */
+ struct ieee80211_cts ctsFrame;
+
+ ieee80211_ctstoself_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txInfo, &ctsFrame);
+ /*
+ * At the time this code was written, the mac80211 library had
+ * a bug in the ieee80211_ctstoself_get which caused it to copy
+ * the wrong MAC address into the cts frame. So we copy the
+ * right one (ours) in now.
+ */
+ memcpy(piperp->ctsFrame.ra, piperp->hw->wiphy->perm_addr, ETH_ALEN);
+
+ /*
+ * If we come here, then we need to send a CTS to self frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_cts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&ctsFrame,
+ sizeof(ctsFrame));
+ }
+ }
+}
+
+/*
+ * This routine is called to report the result of a transmit operation to
+ * mac80211. It is used for both successful transmissions and failures.
+ * It sends the result to the stack, removes the current tx frame from the
+ * queue, and then wakes
+ * up the transmit queue.
+ */
+void packet_tx_done(struct piper_priv *piperp, tx_result_t result,
+ int signal_strength)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ int i;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+#define WANT_TRANSMIT_RESULT (0)
+#if WANT_TRANSMIT_RESULT
+ const char *resultText[] =
+ {
+ "RECEIVED_ACK",
+ "TX_COMPLETE",
+ "OUT_OF_RETRIES",
+ "TX_NOT_DONE"
+ };
+#endif
+ del_timer_sync(&piperp->tx_timer);
+ piperp->expectingAck = false;
+
+#if WANT_TRANSMIT_RESULT
+ printk(KERN_ERR "Transmit result %s\n", resultText[result]);
+#endif
+ if (piperp->tx_calib_cb)
+ piperp->tx_calib_cb(piperp);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ skb_pull(piper_tx_getqueue(piperp), TX_HEADER_LENGTH);
+
+ ieee80211_tx_info_clear_status(info);
+
+ /* prepare statistics and pass them to the stack */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->status.rates[i].count = piperp->pstats.tx_retry_count[i];
+ if (info->status.rates[i].count == 0)
+ info->status.rates[i].idx = -1;
+ }
+
+ info->status.ack_signal = signal_strength;
+ info->flags |= (result == RECEIVED_ACK) ? IEEE80211_TX_STAT_ACK : 0;
+ piperp->pstats.tx_complete_count++;
+ piperp->pstats.tx_queue.len--;
+ if (piperp->tx_rts)
+ piperp->pstats.ll_stats.dot11RTSSuccessCount++;
+
+ skb = piper_tx_getqueue(piperp);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, skb);
+
+ piper_tx_queue_next(piperp);
+
+ if (piper_tx_getqueue(piperp) == NULL) {
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ if (piperp->ps.allowTransmits) {
+ ieee80211_wake_queues(piperp->hw);
+ } else {
+ /*
+ * Do not wake up the mac80211 Tx queue if we are trying to power
+ * down. Make sure we set the stopped_tx_queues flag so that we
+ * know to restart the queues.
+ */
+ piperp->ps.stopped_tx_queues = true;
+ }
+ } else {
+ if (result == OUT_OF_RETRIES) {
+ /*
+ * If we come here, then we are being called from the middle of the
+ * transmit routine and we have to reschedule the transmit task to
+ * start dequeueing the next frame.
+ */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+ } else {
+ digi_dbg("packet_tx_done called with empty queue\n");
+ }
+
+ piperp->tx_result = TX_NOT_DONE;
+}
+EXPORT_SYMBOL_GPL(packet_tx_done);
+
+/*
+ * This function is the entry point for the transmit tasklet. It
+ * is called to transmit frames. It will first be called to transmit
+ * the frame and then to retry if the original transmit fails. So
+ * it does both the first transmit and the subsequent retries.
+ *
+ * Arguments
+ * context context information
+ */
+void piper_tx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+ frameControlFieldType_t *fc;
+ int err;
+
+ piperp->expectingAck = false;
+ del_timer_sync(&piperp->tx_timer);
+ if ((piperp->tx_result == RECEIVED_ACK) || (piperp->tx_result == TX_COMPLETE)) {
+ /*
+ * We will come here if the receiver task received an ACK, or if we got
+ * a tx fifo empty interrupt. In these cases the receiver thread or ISR
+ * schedule the tx tasklet to handle the event rather than calling
+ * packet_tx_done directly.
+ */
+ packet_tx_done(piperp, piperp->tx_result, piperp->tx_signal_strength);
+ }
+
+
+ /*
+ * Clear flags here to cover ACK case. We do not clear the flags in the ACK
+ * routine since it is possible to receive an ACK after we have started the
+ * next packet. The appropriate interrupts will be reenabled if we decide
+ * to retransmit.
+ */
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ struct ieee80211_rate *txRate = get_tx_rate(piperp, txInfo);
+
+ if (txRate != NULL) {
+ fc = (frameControlFieldType_t *)
+ &piper_tx_getqueue(piperp)->data[FRAME_CONTROL_FIELD_OFFSET];
+
+ /* set the retry bit if this is not the first try */
+ if (piperp->pstats.tx_retry_count[0] != 0)
+ fc->retry = 1;
+
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF,
+ piper_get_cw(piperp,
+ (piperp->pstats.tx_retry_count[0] == 0)),
+ op_write);
+
+ /*
+ * Build the H/W transmit header. The transmit header is rebuilt on each
+ * retry because it has the TX rate information which may change for
+ * retries.
+ */
+ phy_set_plcp(piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txRate, piperp->use_hw_aes ? 8 : 0);
+
+ /*
+ * Pause the transmitter so that we don't start transmitting before we
+ * are ready.
+ */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_TX_HOLD, op_or);
+
+ handle_rts_cts(piperp, txInfo, fc->type);
+
+ if (piperp->use_hw_aes == true && txInfo->control.hw_key != NULL) {
+ err =
+ piper_write_aes(piperp, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ } else {
+ err =
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ }
+
+ /* Clear any pending TX interrupts */
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT, op_write);
+
+ /*
+ * Now start the transmitter.
+ */
+ piperp->expectingAck = ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_TX_HOLD,
+ op_and);
+
+ /*
+ * Set interrupt flags. Use the timeout interrupt if we expect
+ * an ACK. Use the FIFO empty interrupt if we do not expect an ACK.
+ */
+ if (txInfo->flags & IEEE80211_TX_CTL_NO_ACK) {
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TX_ABORT);
+ } else {
+ /*
+ * We set up a timer to fire in 1/4 second. We should not need it, but somehow
+ * we seem to miss a timeout interrupt occasionally. Perhaps we encounter a receive
+ * overrun which causes the H/W to discard the ACK packet without generating
+ * a timeout.
+ */
+ piperp->tx_timer.expires = jiffies + (HZ >> 2);
+ add_timer(&piperp->tx_timer);
+
+ /*
+ * Also set the IRQ mask to listen for timeouts and TX aborts. We will receive
+ * an ACK (which is handled by the RX routine) if the TX is successful.
+ */
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ }
+ if ((piperp->pstats.tx_total_tetries != 0) &&
+ ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0)) {
+ piperp->pstats.ll_stats.dot11ACKFailureCount++;
+ }
+ piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index]++;
+ piperp->pstats.tx_total_tetries++;
+ } else {
+ packet_tx_done(piperp, OUT_OF_RETRIES, 0);
+ }
+ } else {
+ long unsigned int flags;
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/mac.h b/drivers/net/wireless/digiPiper/mac.h
new file mode 100644
index 000000000000..892a15a8b67c
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/mac.h
@@ -0,0 +1,375 @@
+#ifndef DIGI_MAC_H_
+#define DIGI_MAC_H_
+
+enum baseband_control_regs {
+ BB_VERSION = 0x00,
+ BB_GENERAL_CTL = 0x04,
+ BB_GENERAL_STAT = 0x08,
+ BB_RSSI = 0x0c,
+ BB_IRQ_MASK = 0x10,
+ BB_IRQ_STAT = 0x14,
+ BB_SPI_DATA = 0x18,
+ BB_SPI_CTRL = 0x1c,
+ BB_DATA_FIFO = 0x20,
+ BB_TRACK_CONTROL = 0x28,
+ BB_CONF_2 = 0x2c,
+ BB_AES_FIFO = 0x30,
+ BB_AES_CTL = 0x38,
+ BB_OUTPUT_CONTROL = 0x3c
+};
+
+#define BB_VERSION_MASK(v) ((v) & 0xffff)
+
+#define BB_GENERAL_CTL_RX_EN (1<<0)
+#define BB_GENERAL_CTL_ANT_DIV (1<<1)
+#define BB_GENERAL_CTL_ANT_SEL (1<<2)
+#define BB_GENERAL_CTL_GEN_5GEN (1<<3) // 5 GHz band enable
+#define BB_GENERAL_CTL_SH_PRE (1<<4)
+#define BB_GENERAL_CTL_RXFIFORST (1<<5)
+#define BB_GENERAL_CTL_TXFIFORST (1<<6)
+#define BB_GENERAL_CTL_TX_HOLD (1<<7)
+#define BB_GENERAL_CTL_BEACON_EN (1<<8)
+#define BB_GENERAL_CTL_FW_LOAD_ENABLE (1 << 9)
+#define BB_GENERAL_CTL_DSP_LOAD_ENABLE (1 << 10)
+#define BB_GENERAL_CTL_MAC_ASSIST_ENABLE (1 << 11)
+#define BB_GENERAL_CTL_TX_FIFO_FULL (1<<15)
+#define BB_GENERAL_CTL_TX_FIFO_EMPTY (1<<14)
+/* TODO: verify max gain value for Piper and Wi9p*/
+#define BB_GENERAL_CTL_MAX_GAIN(g) (((g) & 0x7f)<<16)
+#define BB_GENERAL_CTL_PWR_UP (1<<24)
+#define BB_GENERAL_CTL_ADC_CLK_EN (1<<25)
+#define BB_GENERAL_CTL_BOOT_STAT (1<<28)
+#define BB_GENERAL_CTL_CLK_EN (1<<29)
+#define BB_GENERAL_CTL_SPI_RST (1<<30)
+
+#define BB_GENERAL_CTL_MAX_GAIN_MASK (0x007F0000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A (0x00790000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG (0x007c0000)
+
+#if defined(CONFIG_PIPER_WIFI)
+#if 0
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_SPI_RST
+#endif
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#endif
+#define BB_RSSI_LED (1<<8)
+#define BB_RSSI_EAS_FIFO_EMPTY (1 << 16)
+#define BB_RSSI_EAS_FIFO_FULL (1 << 17)
+#define BB_RSSI_EAS_BUSY (1 << 18)
+#define BB_RSSI_EAS_MIC (1 << 19)
+#define BB_RSSI_ANT_MASK (0xff<<24)
+#define BB_RSSI_ANT_NO_DIV_MAP (0x96000000)
+#define BB_RSSI_ANT_DIV_MAP (0x1E000000)
+
+#define BB_GENERAL_STAT_RESET (1<<30)
+/*
+ * STAT_B_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the PSK/CCK receiver baseband circuitry (802.11b receiver).
+ * STAT_A_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the OFDM receive baseband circuitry (802.11a receiver).
+ */
+
+#define BB_GENERAL_STAT_B_EN 0x10000000 // B EN (PSK/CCK)
+#define BB_GENERAL_STAT_A_EN 0x20000000 // A EN (OFDM)
+#define BB_GENERAL_STAT_RX_FIFO_EMPTY (1 << 4)
+#define BB_GENERAL_STAT_DC_DIS (1 << 24)
+#define BB_GENERAL_STAT_SRC_DIS (1 << 16)
+#define BB_GENERAL_STAT_SPRD_DIS (1 << 17)
+#define BB_GENERAL_STAT_DLL_DIS (1 << 18)
+#define TRACK_TX_B_GAIN_MASK 0xff000000 // Mask word for B_TX_GAIN
+#define TRACK_TX_B_GAIN_NORMAL 0xA0000000 // normal setting for B_TX_GAIN
+#define TRACK_BG_BAND 0x00430000 // Tracking constant for 802.11 b/g frequency band
+#define TRACK_CONSTANT_MASK 0x00ff0000 // mask for tracking constant
+#define TRACK_4920_4980_A_BAND 0x00210000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5150_5350_A_BAND 0x001F0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5470_5725_A_BAND 0x001D0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5725_5825_A_BAND 0x001C0000 // Tracking constant for 802.11 a sub-frequency band
+
+
+#define BB_IRQ_MASK_RX_FIFO (1<<0)
+#define BB_IRQ_MASK_TX_FIFO_EMPTY (1<<1)
+#define BB_IRQ_MASK_TIMEOUT (1<<2)
+#define BB_IRQ_MASK_TX_ABORT (1<<3)
+#define BB_IRQ_MASK_TBTT (1<<4)
+#define BB_IRQ_MASK_ATIM (1<<5)
+#define BB_IRQ_MASK_RX_OVERRUN (1<<6)
+
+#define BB_AES_CTL_KEY_LOAD (1<<2)
+#define BB_AES_CTL_AES_MODE (1<<4)
+
+enum mac_control_regs {
+ MAC_STA_ID0 = 0x40,
+ MAC_STA_ID1 = 0x44,
+ MAC_BSS_ID0 = 0x48,
+ MAC_BSS_ID1 = 0x4c,
+ MAC_SSID_LEN = 0x50, /* OFDM_BRS, PSK_BRS, TX_CTL, SSID_LEN */
+ MAC_BACKOFF = 0x54, /* actually 0x56; 2 low order bytes are empty */
+ MAC_DTIM_PERIOD = 0x58,
+ /*MAC_CFP_PERIOD = 0x59,*/
+ /*MAC_LISTEN_INTERVAL = 0x5a,*/
+ MAC_CFP_ATIM = 0x5c, /* beacon interval, CFP/ATIM duration */
+ MAC_STATUS = 0x60,
+ /*MAC_TXP_TIMING = 0x62,*/
+ /*MAC_STATUS = 0x63,*/
+ MAC_CTL = 0x64, /* MAC_AES_KEY_DIS (8 bits), MAC_CTL (8 bits) */
+ MAC_MEASURE = 0x68, /* actually 0x69 */
+ /*MAC_REMAIN_BO = 0x6a,*/
+ MAC_BEACON_FILT = 0x6c, /* actally 0x6d */
+ /*MAC_BEACON_BO = 0x6e,*/
+ MAC_STA2_ID0 = 0xb0,
+ MAC_STA2_ID1 = 0xb4,
+ MAC_STA3_ID0 = 0xb8,
+ MAC_STA3_ID1 = 0xbc,
+
+ MAC_EEPROM_CTL = 0xf0,
+ MAC_EEPROM_DATA = 0xf8,
+
+ MAC_SSID = 0x80,
+
+ BEACON_FIFO = 0x85, /* dummy value used to select data fifo for beacon load */
+};
+
+
+#define MAC_SSID_LEN_MASK (0x000000ff)
+#define MAC_REVISION_MASK(v) (((v) >> 16) & 0xffff)
+
+#define MAC_BEACON_INTERVAL_SHIFT (16)
+#define MAC_BEACON_INTERVAL_MASK (0xffff0000)
+
+#define MAC_ATIM_PERIOD_MASK (0x0000ffff)
+
+#define MAC_LISTEN_INTERVAL_MASK (0x0000ffff)
+
+#define MAC_DTIM_PERIOD_SHIFT (24)
+#define MAC_DTIM_PERIOD_MASK (0xff000000)
+
+#define MAC_DTIM_CFP_SHIFT (16)
+#define MAC_DTIM_CFP_MASK (0x00ff0000)
+
+#define MAC_OFDM_BRS_MASK (0xff000000)
+#define MAC_OFDM_BRS_SHIFT (24)
+#define MAC_PSK_BRS_MASK (0x000f0000)
+#define MAC_PSK_BRS_SHIFT (16)
+
+#define MAC_BEACON_BACKOFF_MASK (0x0000ffff)
+
+#define MAC_BRS_MASK (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK)
+
+#define MAC_CTL_TX_REQ (1)
+#define MAC_CTL_BEACON_TX (1<<2)
+#define MAC_CTL_IBSS (1<<4)
+#define MAC_CTL_AES_DISABLE (1<<5)
+#define MAC_CTL_MAC_FLTR (1<<6)
+#define MAC_CTL_KEY0_DISABLE (1<<8)
+#define MAC_CTL_KEY1_DISABLE (1<<9)
+#define MAC_CTL_KEY2_DISABLE (1<<10)
+#define MAC_CTL_KEY3_DISABLE (1<<11)
+
+#define MAC_EEPROM_CTL_WAIT_MS 21
+
+/*
+ * RX packets look something like:
+ * <custom bus protocol header(s)> - protocol-dependent
+ * <rx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header> - dealt with by the mac80211 stack, not the driver
+ * <data>
+ *
+ * TX packets are similar:
+ * <custom bus protocol header(s)>
+ * <tx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header>
+ * <data>
+ */
+
+
+
+// MAC type field values
+#define TYPE_ASSOC_REQ 0x00 // Association request
+#define TYPE_ASSOC_RESP 0x10 // Association response
+#define TYPE_REASSOC_REQ 0x20 // Reassociation request
+#define TYPE_REASSOC_RESP 0x30 // Reassociation response
+#define TYPE_PROBE_REQ 0x40 // Probe request
+#define TYPE_PROBE_RESP 0x50 // Probe response
+
+#define TYPE_BEACON 0x80 // Beacon
+#define TYPE_ATIM 0x90 // Annoucement traffice indication
+#define TYPE_DISASSOC 0xa0 // Disassociation
+#define TYPE_AUTH 0xb0 // Authentication
+#define TYPE_DEAUTH 0xc0 // Deauthentication
+#define TYPE_ACTION 0xd0 // Action
+
+#define TYPE_RTS 0xb4 // Request to send
+#define TYPE_CTS 0xc4 // Clear to send
+#define TYPE_ACK 0xd4 // Acknowledgement
+#define TYPE_PSPOLL 0xa4 // Power Save(PS)-Poll
+
+#define TYPE_DATA 0x08 // Data
+#define TYPE_NULL_DATA 0x48 // Null Data
+
+struct tx_frame_hdr {
+#if 1
+ unsigned int modulation_type:8;
+ unsigned int length:9;
+ unsigned int pad:15;
+#else
+ uint8_t modulation_type;
+ __le16 length:9;
+ unsigned int pad:15;
+#endif
+} __attribute__((packed));
+
+
+#define MOD_TYPE_PSKCCK 0x00
+#define MOD_TYPE_OFDM 0xee
+
+struct psk_cck_hdr {
+ uint8_t signal; /* x100Kbps */
+ uint8_t service;
+ __le16 length; /* usecs */
+} __attribute__((packed));
+
+/* PSK/CCK PLCP service field bits */
+#define PLCP_SERVICE_LOCKED 0x04 /* locked clocks */
+#define PLCP_SERVICE_MODSEL 0x08 /* modulation selection */
+#define PLCP_SERVICE_LENEXT 0x80 /* length extension */
+
+struct ofdm_hdr {
+ unsigned int rate:4;
+ unsigned int pad_a:1;
+ unsigned int length:12; /* in bytes */
+ unsigned int parity:1;
+ unsigned int pad_b:14;
+} __attribute__((packed));
+
+
+struct rx_frame_hdr {
+ uint8_t modulation_type;
+ unsigned int rssi_variable_gain_attenuator:5;
+ unsigned int rssi_low_noise_amp:2;
+ unsigned int antenna:1;
+ __be16 freq_offset;
+ union
+ {
+ struct psk_cck_hdr psk;
+ struct ofdm_hdr ofdm;
+ } mod;
+} __attribute__((packed));
+
+typedef struct
+{
+ unsigned type :8; // Type, subtype, version
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protectd :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+} frameControlFieldType_t;
+
+#define WLN_ADDR_SIZE (6)
+
+typedef unsigned char MacAddr[WLN_ADDR_SIZE];
+
+
+#define PACKED_H
+#define PACKED_F __attribute__ ((packed))
+
+typedef PACKED_H struct {
+ unsigned type :8; // Type, subtype, version
+#ifdef BIG_ENDIAN
+ unsigned order :1; // Strictly ordered
+ unsigned protected :1; // Encrypted
+ unsigned moreData :1; // More data buffered
+ unsigned pwrMgt :1; // Power management state
+ unsigned retry :1; // Retransmission
+ unsigned moreFrag :1; // More fragments
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned toDS :1; // To distribution service (AP)
+#else
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protected :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+#endif
+} PACKED_F FrameControl_t;
+
+
+// Sequence control field
+// Need to swap bytes on BIG_ENDIAN
+typedef PACKED_H struct {
+#ifdef BIG_ENDIAN
+ unsigned seq :12; // Sequence number
+ unsigned frag :4; // Fragment number
+#else
+ unsigned frag :4; // Fragment number
+ unsigned seq :12; // Sequence number
+#endif
+} PACKED_F SeqControl;
+
+// Union of sequence control types
+typedef PACKED_H union {
+ SeqControl sq; // Sequence control fields
+ unsigned short sq16; // Sequence control as 16-bit int (needs byte swap)
+} PACKED_F SeqControlU;
+
+
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short duration; // Duration/ID (needs byte swap)
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+ MacAddr addr3; // Address 3
+ SeqControlU squ; // Sequence control fields
+} PACKED_F _80211HeaderType;
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short aid; // association identifier
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+} PACKED_F _80211PSPollType;
+
+#define _80211_HEADER_LENGTH (sizeof(_80211HeaderType))
+#define TX_HEADER_LENGTH (sizeof(struct ofdm_hdr) + sizeof(struct tx_frame_hdr))
+/* FIFO sizes in bytes */
+#define TX_FIFO_SIZE 1792
+#define RX_FIFO_SIZE 2048
+
+#define RATE_MASK_BASIC 0x0153
+#define RATE_MASK_OFDM 0x0ff0
+#define RATE_MASK_PSK_CCK 0x000f
+
+#define BEACON_INT 100 /* in TU */
+
+
+#define DEFAULT_CW_MIN 32 // Min contention window size
+#define DEFAULT_CW_MAX 1024 // Max contention window size
+
+#define ASLOT_TIME 20
+#endif
diff --git a/drivers/net/wireless/digiPiper/phy.c b/drivers/net/wireless/digiPiper/phy.c
new file mode 100644
index 000000000000..5a6c53638e81
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.c
@@ -0,0 +1,269 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * Author: Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "phy.h"
+
+#define PHY_DEBUG (0)
+
+#if PHY_DEBUG
+static int dlevel = DVERBOSE;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+#define NUMBER_OF_WORD32(x) ((x + 3) >> 2)
+
+static int is_ofdm_rate(int rate)
+{
+ return (rate % 3) == 0;
+}
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len)
+{
+ int ofdm = is_ofdm_rate(rate->bitrate);
+ int plcp_len = length + FCS_LEN + aes_len;
+ struct tx_frame_hdr *hdr;
+
+ if (ofdm) {
+ /* OFDM header */
+ struct ofdm_hdr *ofdm;
+
+ ofdm = (struct ofdm_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ memset(ofdm, 0, sizeof(*ofdm));
+ ofdm->rate = rate->hw_value;
+ ofdm->length = cpu_to_le16(plcp_len);
+ } else {
+ /* PSK/CCK header */
+ struct psk_cck_hdr *pskcck;
+ int us_len;
+
+ pskcck = (struct psk_cck_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ pskcck->signal = rate->bitrate;
+ pskcck->service = PLCP_SERVICE_LOCKED;
+
+ /* convert length from bytes to usecs */
+ switch (rate->bitrate) {
+ case 10:
+ us_len = plcp_len * 8;
+ break;
+ case 20:
+ us_len = plcp_len * 4;
+ break;
+ case 55:
+ us_len = (16 * plcp_len + 10) / 11;
+ break;
+ case 110:
+ us_len = (8 * plcp_len + 10) / 11;
+
+ /* set length extension bit if needed */
+ dprintk(DALL, "us_len = %d, plcp_len = %d, (11 * us_len) = %d, \
+ (11 * us_len) / 8 = %d\n", us_len, plcp_len,
+ (11 * us_len), (11 * us_len) / 8);
+
+ if ((11 * us_len) / 8 > plcp_len) {
+ pskcck->service |= PLCP_SERVICE_LENEXT;
+ dprintk(DALL, "Set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ } else {
+ dprintk(DALL, "Did not set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ }
+ break;
+ default:
+ digi_dbg("rate = %p, rate->bitrate%d\n", rate, rate->bitrate);
+ WARN_ON(1);
+ us_len = 0;
+ }
+
+ pskcck->length = cpu_to_le16(us_len);
+
+ dprintk(DALL, "pskcck .length = %d, signal = %d, service = %d\n",
+ pskcck->length, pskcck->signal, pskcck->service);
+ dprintk(DALL, "rate->bitrate=%x (@%dM), pckcck->length=%d\n",
+ rate->bitrate, rate->bitrate/10, pskcck->length);
+ }
+
+ hdr = (struct tx_frame_hdr *) frame;
+ hdr->pad = 0;
+ hdr->length = NUMBER_OF_WORD32((length + aes_len + TX_HEADER_LENGTH));
+ hdr->modulation_type = ofdm ? MOD_TYPE_OFDM : MOD_TYPE_PSKCCK;
+
+ dprintk(DVVERBOSE, "frame hdr .length = %d, .modulation_type = %d\n",
+ hdr->length, hdr->modulation_type);
+
+ dprintk(DVERBOSE, "TX: %d byte %s packet @ %dmbit\n", length,
+ ofdm ? "OFDM" : "PSK/CCK", rate->bitrate/10);
+}
+EXPORT_SYMBOL_GPL(phy_set_plcp);
+
+
+static int get_signal(struct rx_frame_hdr *hdr, enum ieee80211_band rf_band, int transceiver)
+{
+ int gain;
+ int signal;
+
+ if (transceiver == RF_AIROHA_2236) {
+ const u8 lnaTable_al2236[] =
+ {
+ 0, 0, 20, 36
+ };
+
+ // Map high gain values to dbm
+ const signed char gainTable_al2236[] =
+ {
+ -85, -85, -88, -88, -92
+ };
+ // Convert received signal strength to dbm
+ gain = lnaTable_al2236[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ if (gain > 92)
+ signal = -96;
+ else if (gain > 87)
+ signal = gainTable_al2236[gain - 88];
+ else
+ signal = 4 - gain;
+ } else {
+ static const u8 lnaTable_al7230_24ghz[] =
+ {
+ 0, 0, 18, 42
+ };
+ static const u8 lnaTable_al7230_50ghz[] =
+ {
+ 0, 0, 17, 37
+ };
+ /* Convert received signal strength to dbm for RF_AIROHA_7230 */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ gain = lnaTable_al7230_24ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = 2 - gain;
+ } else {
+ gain = lnaTable_al7230_50ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = -5 - gain;
+ }
+ }
+
+ return signal;
+}
+
+
+
+
+/* FIXME, following limtis should depend on the platform */
+#define PIPER_MAX_SIGNAL_DBM (-30)
+#define PIPER_MIN_SIGNAL_DBM (-90)
+
+static int calculate_link_quality(int signal)
+{
+ int quality;
+
+ if (signal < PIPER_MIN_SIGNAL_DBM)
+ quality = 0;
+ else if (signal > PIPER_MAX_SIGNAL_DBM)
+ quality = 100;
+ else
+ quality = (signal - PIPER_MIN_SIGNAL_DBM) * 100 /
+ (PIPER_MAX_SIGNAL_DBM - PIPER_MIN_SIGNAL_DBM);
+
+ dprintk(DVERBOSE, "signal: %d, quality: %d/100\n", signal, quality);
+
+ return quality;
+}
+
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr)
+{
+ return (hdr->rssi_low_noise_amp << 5) | hdr->rssi_variable_gain_attenuator;
+}
+EXPORT_SYMBOL_GPL(phy_determine_rssi);
+
+
+
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length)
+{
+ unsigned rate, i;
+ struct digi_rf_ops *rf = piper->rf;
+
+ memset(status, 0, sizeof(*status));
+ status->band = piper->rf->getBand(piper->channel);
+ status->signal = get_signal(hdr, status->band, piper->pdata->rf_transceiver);
+ status->antenna = hdr->antenna;
+ status->freq = piper->rf->getFrequency(piper->channel);
+ status->qual = calculate_link_quality(status->signal);
+
+ if (hdr->modulation_type == MOD_TYPE_OFDM) {
+ /* OFDM */
+ struct ofdm_hdr *ofdm = &hdr->mod.ofdm;
+ const int ofdm_rate[] = {
+ 480, 240, 120, 60, 540, 360, 180, 90
+ };
+
+ rate = ofdm_rate[ofdm->rate & 0x7];
+ *length = le16_to_cpu(ofdm->length);
+ dprintk(DVVERBOSE, "%d byte OFDM packet @ %dmbit\n",
+ *length, rate/10);
+ } else {
+ /* PSK/CCK */
+ struct psk_cck_hdr *pskcck = &hdr->mod.psk;
+
+ rate = pskcck->signal;
+
+ *length = le16_to_cpu(pskcck->length);
+ switch (rate) {
+ case 10:
+ *length /= 8;
+ break;
+ case 20:
+ *length /= 4;
+ break;
+ case 55:
+ *length = (11 * (*length)) / 16;
+ break;
+ case 110:
+ *length = (11 * (*length)) / 8;
+ if (pskcck->service & PLCP_SERVICE_LENEXT)
+ (*length)--;
+ break;
+ default:
+ /* WARN_ON(1); This happens to often for us to generate that long error message */
+ *length = 0;
+ }
+
+ dprintk(DVVERBOSE, "%d byte PSK/CCK packet @ %dmbit\n",
+ *length, rate/10);
+ }
+
+ /* match rate with the list of bitrates that we supplied the stack */
+ for (i = 0; i < rf->bands[status->band].n_bitrates; i++) {
+ if (rf->bands[status->band].bitrates[i].bitrate == rate)
+ break;
+ }
+
+ if (i != rf->bands[status->band].n_bitrates)
+ status->rate_idx = i;
+ else {
+ *length = 0;
+ digi_dbg(PIPER_DRIVER_NAME
+ ": couldn't find bitrate index for %d?\n",
+ rate);
+ status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(phy_process_plcp);
diff --git a/drivers/net/wireless/digiPiper/phy.h b/drivers/net/wireless/digiPiper/phy.h
new file mode 100644
index 000000000000..96500ab92e3e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.h
@@ -0,0 +1,28 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef phy_h_
+#define phy_h_
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+
+#include "pipermain.h"
+#include "mac.h"
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len);
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length);
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piper.c b/drivers/net/wireless/digiPiper/piper.c
new file mode 100644
index 000000000000..a3c24720dd11
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piper.c
@@ -0,0 +1,1032 @@
+/*
+ * piper.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <asm/gpio.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "piperDsp.h"
+#include "piperMacAssist.h"
+#include "digiPs.h"
+
+#define WANT_AIROHA_CALIBRATION (1)
+#define WANT_DEBUG_COMMANDS (1)
+
+
+static void piper_clear_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, ~bits, op_and);
+}
+
+static void piper_set_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, bits, op_or);
+}
+
+/* Generate a random number */
+static int local_rand(void)
+{
+ static unsigned long next = 1;
+
+ /* RAND_MAX assumed to be 32767 */
+ next = next * 1103515245 + 12345;
+ return((unsigned)(next/65536) % 32768);
+}
+
+/*
+ * Load the MAC Assist firmware into the chip. This is done by setting a bit
+ * in the control register to enable MAC Assist firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_mac_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading MAC Assist firmware\n");
+
+ /* Zero out MAC assist SRAM (put into known state before enabling MAC assist) */
+ for (i = 0; i < 0x100; i += 4)
+ piperp->ac->wr_reg(piperp, i, 0, op_write);
+
+ /* Enable download the MAC Assist program RAM */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_FW_LOAD_ENABLE, op_or);
+
+ /* load MAC Assist data */
+ for (i = 0; i < piper_macassist_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_macassist_ucode[i],
+ op_write);
+
+ /* disable MAC Assist download */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_FW_LOAD_ENABLE, op_and);
+}
+
+/*
+ * Load the DSP firmware into the chip. This is done by setting a bit
+ * in the control register to enable DSP firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_dsp_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading DSP firmware\n");
+
+ /* Enable load of DSP firmware */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_or);
+
+ /* load DSP data */
+ for (i = 0; i < piper_dsp_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_dsp_ucode[i],
+ op_write);
+
+ /* Disable load of DSP firmware */
+ udelay(10);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_and);
+
+ /* Let her rip */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+}
+
+
+/*
+ * This routine corrects a bug in the Piper chip where internal clocks would
+ * be out of sync with each other and cause the chip to generate noise spikes.
+ * This problem should be fixed in the next chip (Chopper).
+ *
+ * I'm not sure exactly what this code is doing. It comes straight from the
+ * guy who designed the chip.
+ */
+int piper_spike_suppression(struct piper_priv *piperp, bool retry)
+{
+ int timeout1 = 300, timeout2 = 300;
+ int ret = 0;
+
+ /*
+ * Initial timing measurement to avoid spike
+ * The new "magic" value is 0x63 at address 0xA62. Bit-0 indicates the
+ * timing measurement is complete. Bit-1 indicates that a second timing
+ * measurment was performed. The upper nibble is the timing measurement
+ * value. This code should eliminate the possibility of spikes at the
+ * beginning of all PSK/CCK frames and eliminate the spikes at the end of
+ * all PSK (1M, 2M) frames.
+ */
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00006300) {
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ /* issue WiFi soft reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x40000000, op_write);
+
+ /* Set TX_ON Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xffffff3f, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000080, op_or);
+
+ /* Set PA_2G Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xfffff0ff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000a00, op_or);
+
+ /* Set RX_ON low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xcfffffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x20000000, op_or);
+
+ /* start the WiFi mac & dsp */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720820, op_write);
+ timeout1 = 500;
+
+ /* Wait for timing measurement to finish */
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00000100) {
+ udelay(2);
+ timeout1--;
+ if (!timeout1)
+ break;
+ }
+
+ timeout2--;
+ if (!timeout2) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!retry) {
+ ret = -EIO;
+ break;
+ }
+ }
+
+ /* Set TX_ON/RXHP_ON and RX to normal wifi, restore the reset value to HW_OUT_CTRL */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x1, op_write);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_spike_suppression);
+
+void piper_reset_mac(struct piper_priv *piperp)
+{
+ int i;
+
+ /* set the TX-hold bit */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ /* clear the TX-FIFO memory */
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ /* reset the TX-FIFO */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200C0, op_write);
+
+ /* release the TX-hold and reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+/* iowrite32(ioread32(piperp->vbase + MAC_STATUS) & ~0x40000000,
+ piperp->vbase + BB_GENERAL_STAT);*/
+ mdelay(1);
+}
+
+/*
+ * Load the MAC address into the chip. Use the value stored in the
+ * environment, if there is one, otherwise use the default value.
+ */
+void piper_set_macaddr(struct piper_priv *piperp)
+{
+ /* Default MAC Addr used if the nvram parameters are corrupted */
+ u8 mac[6] = {0x00, 0x04, 0xf3, 0x11, 0x43, 0x35};
+ u8 *pmac = piperp->pdata->macaddr;
+ int i;
+ static bool firstTime = true;
+
+ for (i = 0; i < 6; i++) {
+ if (*(pmac + i) != 0xff)
+ break;
+ if (i == 5) {
+ /* There is a problem with the parameters, use default */
+ if (firstTime) {
+ printk(KERN_INFO PIPER_DRIVER_NAME
+ ": invalid mac address, using default\n");
+ }
+ memcpy(piperp->pdata->macaddr, mac, sizeof(piperp->pdata->macaddr));
+ }
+ }
+
+ firstTime = false;
+ memcpy(piperp->hw->wiphy->perm_addr, piperp->pdata->macaddr,
+ sizeof(piperp->hw->wiphy->perm_addr));
+
+ /* configure ethernet address */
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, *(pmac + 3) | *(pmac + 2) << 8 |
+ *(pmac + 1) << 16 | *(pmac + 0) << 24, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, *(pmac + 5) << 16 | *(pmac + 4) << 24,
+ op_write);
+}
+EXPORT_SYMBOL_GPL(piper_set_macaddr);
+
+
+/* Configure the H/W with the antenna settings */
+static int piper_set_antenna(struct piper_priv *piperp, enum antenna_select sel)
+{
+ if (sel == ANTENNA_BOTH) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_DIV, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_DIV, op_and);
+ /* select the antenna if !diversity */
+ if (sel == ANTENNA_1)
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_SEL, op_or);
+ }
+
+ /* select which antenna to transmit on */
+ piperp->ac->wr_reg(piperp, BB_RSSI, ~BB_RSSI_ANT_MASK, op_and);
+ if (sel == ANTENNA_BOTH)
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_DIV_MAP, op_or);
+ else
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_NO_DIV_MAP, op_or);
+
+ return 0;
+}
+
+/*
+ * Compute a beacon backoff time as described in section 11.1.2.2 of 802.11 spec.
+ *
+ */
+static u16 get_next_beacon_backoff(void)
+{
+#define MAX_BEACON_BACKOFF (2 * ASLOT_TIME * DEFAULT_CW_MIN)
+
+ /*
+ * We shift the result of local_rand() by 4 bits because the notes
+ * for the algorithm say that we shouldn't rely on the last few
+ * bits being random. Other than that, we just take the random
+ * value and make sure it is less than MAX_BEACON_BACKOFF.
+ */
+ return (local_rand() >> 4) % MAX_BEACON_BACKOFF;
+}
+
+static int load_beacon(struct piper_priv *digi, unsigned char *buffer,
+ unsigned int length)
+{
+ return digi->ac->wr_fifo(digi, BEACON_FIFO, buffer, length);
+}
+
+static int piper_init_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_init(&piperp->rx_tasklet, piper_rx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->rx_tasklet);
+ piperp->expectingAck = false;
+
+ spin_lock_init(&piperp->tx_tasklet_lock);
+ spin_lock_init(&piperp->tx_queue_lock);
+ piperp->tx_tasklet_running = false;
+ memset(&piperp->tx_queue, 0, sizeof(piperp->tx_queue));
+ piperp->tx_queue_head = 0;
+ piperp->tx_queue_tail = 0;
+ piperp->tx_queue_count = 0;
+ tasklet_init(&piperp->tx_tasklet, piper_tx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->tx_tasklet);
+
+ return 0;
+}
+
+static void piper_free_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_kill(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ tasklet_kill(&piperp->tx_tasklet);
+ piper_empty_tx_queue(piperp);
+}
+
+/*
+ * This function sets the tracking control according to a channel's
+ * frequency.
+ */
+static int piper_set_tracking_constant(struct piper_priv *piperp, unsigned megahertz)
+{
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_CONSTANT_MASK, op_and);
+ if (megahertz < 4920)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ }
+ else if (megahertz <= 4980)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_4920_4980_A_BAND, op_or);
+ }
+ else if (megahertz <= 5350)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ }
+ else if (megahertz <= 5725)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5470_5725_A_BAND, op_or);
+ }
+ else
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5725_5825_A_BAND, op_or);
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called to set the value of the B_TX_GAIN field of the
+ * HW_CONF1 mac register. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board design.
+ */
+static unsigned int get_b_tx_gain(struct piper_priv *piperp)
+{
+ u16 platform = piperp->pdata->wcd.header.hw_platform & WCD_PLATFORM_MASK;
+ u16 hw_revision = piperp->pdata->wcd.header.hw_platform & WCD_HW_REV_MASK;
+ unsigned int tx_gain = 0;
+
+ switch (platform) {
+ case WCD_CCW9P_PLATFORM:
+ tx_gain = TRACK_TX_B_GAIN_NORMAL;
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ tx_gain = 0xc0000000;
+ break;
+ case WCD_HW_REV_A:
+ default:
+ tx_gain = 0x90000000;
+ break;
+ }
+ break;
+ }
+ return tx_gain;
+}
+
+
+static int piper_init_hw(struct piper_priv *piperp, enum ieee80211_band band)
+{
+ int ret = 0;
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_INIT, op_write);
+
+ /* Initialize baseband general control register for the specific transceiver */
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_7230) {
+ if (band == IEEE80211_BAND_2GHZ) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band B / BG\n");
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_50GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band A\n");
+ }
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA, op_write);
+ } else if (piperp->pdata->rf_transceiver == RF_AIROHA_2236) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA2236, op_write);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ *Clear the Intretupt Mask Register before enabling external intretupts.
+ * Also clear out any status bits in the Intretupt Status Register.
+ */
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+ /*
+ * If this firmware supports additional MAC addresses.
+ */
+ if (((piperp->ac->rd_reg(piperp, MAC_STATUS) >> 16) & 0xff) >= 8) {
+ /* Disable additional addresses to start with */
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_MAC_FLTR, op_and);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID1, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID1, 0, op_write);
+ }
+ /* TODO: Set this register programatically */
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, 0x0, op_write);
+
+ /*
+ * Note that antenna diversity will be set by hw_start, which is the
+ * caller of this function.
+ */
+
+ /* reset RX and TX FIFOs */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~(BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST), op_and);
+
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xC043002C, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_TX_B_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, get_b_tx_gain(piperp), op_or);
+
+ /* Initialize RF transceiver */
+ piperp->rf->init(piperp->hw, band);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x04000001, op_or);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, 0x0, op_write);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_DC_DIS
+ | BB_GENERAL_STAT_SPRD_DIS), op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_SRC_DIS
+ | BB_GENERAL_STAT_DLL_DIS), op_and);
+
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK),
+ op_write);
+
+ /*
+ * Set BSSID to the broadcast address so that we receive all packets. The stack
+ * will set a real BSSID when it's ready.
+ */
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, 0xffffffff, op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, 0xffffffff, op_write);
+
+ piperp->ps.poweredDown = false;
+
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiInitCalibration()\n");
+ digiWifiInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+static int piper_deinit_hw(struct piper_priv *piperp)
+{
+ int ret = 0;
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiDeInitCalibration()\n");
+ digiWifiDeInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+
+static void adjust_max_agc(struct piper_priv *piperp, unsigned int rssi, _80211HeaderType *header)
+{
+#define LOWEST_MAXAGC_AL2236 0x76
+#define HIGHEST_MAXAGC_AL2236 0x7B
+#define HIGHEST_MAXAGC_AL7230_24GHZ 0x7c
+#define LOWEST_MAXAGC_AL7230_24GHZ 0x76
+#define HIGHEST_MAXAGC_AL7230_50GHZ 0x79
+#define LOWEST_MAXAGC_AL7230_50GHZ 0x73
+#define RSSI_AVG_COUNT 8
+
+ unsigned char maxgain = 0;
+ static unsigned char lowest = 0, highest = 0;
+ static int k=0, j=0, i =0, tempRssi=0;
+ static unsigned int savedRSSI[RSSI_AVG_COUNT]; /****/
+
+ savedRSSI[k % RSSI_AVG_COUNT] = rssi;
+ if ( (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ || (piperp->pdata->rf_transceiver == RF_AIROHA_7230)) {
+
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ {
+ lowest = LOWEST_MAXAGC_AL2236;
+ highest = HIGHEST_MAXAGC_AL2236;
+ }
+ else
+ {
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_5GHZ) {
+ highest = HIGHEST_MAXAGC_AL7230_50GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_50GHZ;
+ }
+ else {
+ highest = HIGHEST_MAXAGC_AL7230_24GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_24GHZ;
+ }
+ }
+
+ if (piperp->areWeAssociated)
+ {
+
+ if ( (piperp->if_type == NL80211_IFTYPE_ADHOC)
+ || (piperp->if_type == NL80211_IFTYPE_MESH_POINT))
+ {
+ //Monitor the receive signal strength from Ad-hoc network
+ if (memcmp (piperp->bssid, header->addr3, sizeof(piperp->bssid)) == 0)
+ {
+ /* we don't do avareging on all the signals here because it may come from different
+ * unit in that Ad-hoc network. Instead, we do avareging on the signals with higher rssi
+ */
+
+ if ((rssi + 4) > lowest)
+ {
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT)
+ {
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ k = 0;
+ tempRssi = 0;
+ i =0;
+ }
+ }
+ else
+ {
+ i++;
+ if (i >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = lowest;
+ i = 0;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ //Monitor the receive signal strength from the frames we received from the associated AP
+ if (memcmp (piperp->bssid, header->addr2, sizeof(piperp->bssid)) == 0)
+ {
+ //averaging all the signals because they come from the same AP
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT*2)
+ {
+ if (((tempRssi/k) + 4) > lowest)
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ else
+ maxgain = lowest;
+
+ k = 0;
+ tempRssi = 0;
+ }
+ }
+ }
+ j = 0;
+ }
+ else
+ {
+ j++;
+ if (j >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = highest;
+ j = 0;
+ }
+ k = 0;
+ tempRssi = 0;
+ }
+
+ if( (maxgain != 0)
+ && (maxgain != ((piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_MAX_GAIN_MASK) >> 16)))
+ {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, (maxgain << 16) & BB_GENERAL_CTL_MAX_GAIN_MASK, op_or);
+ }
+ }
+}
+
+
+/* Make sure all keys are disabled when we start */
+static void piper_init_keys(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ for (i = 0; i < PIPER_MAX_KEYS; i++)
+ piperp->key[i].valid = false;
+
+ piperp->aes_key_count = 0;
+}
+
+static void tx_timer_timeout(unsigned long arg)
+{
+ struct piper_priv *piperp = (struct piper_priv *) arg;
+
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+}
+
+/* sysfs entries to get/set antenna mode */
+static ssize_t show_antenna_sel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", piperp->antenna == ANTENNA_BOTH ? "diversity" :
+ piperp->antenna == ANTENNA_1 ? "primary" : "secondary");
+}
+
+static ssize_t store_antenna_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ enum antenna_select ant;
+ size_t len = count;
+ int ret;
+
+ ant = piperp->antenna;
+
+ if (buf[count - 1] == '\n')
+ len--;
+
+ /* TODO check also string length */
+ if (!strncmp("diversity", buf, len))
+ ant = ANTENNA_BOTH;
+ else if (!strncmp("primary", buf, len))
+ ant = ANTENNA_1;
+ else if (!strncmp("secondary", buf, len))
+ ant = ANTENNA_2;
+
+ if (ant != piperp->antenna) {
+ if ((ret = piperp->set_antenna(piperp, ant)) != 0) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error setting antenna to %d (err: %d)\n", ant, ret);
+ } else
+ piperp->antenna = ant;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(antenna_sel, S_IWUSR | S_IRUGO, show_antenna_sel, store_antenna_sel);
+
+static ssize_t show_power_duty(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", piperp->power_duty);
+}
+
+static ssize_t store_power_duty(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#define MINIMUM_DUTY_CYCLE (33)
+#define LIMIT_LINEAL_DUTY_CYCLE (75)
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ int pw_duty;
+ ssize_t ret = -EINVAL;
+
+ ret = sscanf(buf, "%d\n", &pw_duty);
+ if (ret > 0) {
+ if (pw_duty < MINIMUM_DUTY_CYCLE) {
+ piperp->power_duty = MINIMUM_DUTY_CYCLE;
+ } else if (pw_duty > LIMIT_LINEAL_DUTY_CYCLE && pw_duty < 100) {
+ piperp->power_duty = LIMIT_LINEAL_DUTY_CYCLE;
+ } else if (pw_duty == 100 ||
+ (pw_duty >= MINIMUM_DUTY_CYCLE && pw_duty <= LIMIT_LINEAL_DUTY_CYCLE)) {
+ piperp->power_duty = pw_duty;
+ }
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(power_duty, S_IWUSR | S_IRUGO, show_power_duty, store_power_duty);
+
+#if WANT_DEBUG_COMMANDS
+
+static ssize_t show_debug_cmd(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", piperp->debug_cmd);
+}
+
+static ssize_t store_debug_cmd(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+
+ if (strlen(buf) < sizeof(piperp->debug_cmd))
+ {
+ if (strstr(buf, "dump") != NULL) {
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ } else if (strstr(buf, "ps_state") != NULL) {
+ printk(KERN_ERR "rxTaskletRunning = %d, allowTransmits = %d, stopped_tx_queues = %d\n",
+ piperp->ps.rxTaskletRunning, piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues);
+ ret = 1;
+ } else if (strstr(buf, "rssi_dump") != NULL) {
+ spinlock_t lock;
+ unsigned long flags;
+ unsigned int rssi;
+
+ spin_lock_init(&lock);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+ udelay(15);
+ rssi = piperp->ac->rd_reg(piperp, BB_RSSI);
+ printk(KERN_ERR "\n**rssi = 0x%8.8X\n", rssi);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ strcpy(piperp->debug_cmd, buf);
+ piperp->debug_cmd[strlen(buf)-1] = 0; /* truncate the \n */
+ }
+ ret = count;
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(debug_cmd, S_IWUSR | S_IRUGO, show_debug_cmd, store_debug_cmd);
+
+#endif
+
+#ifdef CONFIG_PM
+
+static int piper_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ piperp->power_save_was_on_when_suspended = (piperp->ps.mode == PS_MODE_LOW_POWER);
+ if (piperp->power_save_was_on_when_suspended)
+ piper_ps_set(piperp, false);
+ mdelay(10);
+ piper_sendNullDataFrame(piperp, true);
+ ssleep(1);
+
+ local_irq_save(flags);
+ /*
+ * Save power save state and then make sure power save is turned off.
+ */
+ piper_MacEnterSleepMode(piperp, true);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int piper_resume(struct platform_device *dev)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ if (piperp->pdata->early_resume)
+ piperp->pdata->early_resume(piperp);
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ local_irq_save(flags);
+ piper_MacEnterActiveMode(piperp, true);
+ if (piperp->tx_tasklet_running) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ ieee80211_wake_queues(piperp->hw);
+ }
+ local_irq_restore(flags);
+
+ /*
+ * Restore power save if it was on before
+ */
+ if (piperp->power_save_was_on_when_suspended) {
+ piper_ps_set(piperp, true);
+ } else {
+ piper_sendNullDataFrame(piperp, false);
+ }
+
+ return 0;
+}
+#else
+#define piper_suspend NULL
+#define piper_resume NULL
+#endif
+
+static int __init piper_probe(struct platform_device* pdev)
+{
+ struct piper_pdata *pdata = pdev->dev.platform_data;
+ struct piper_priv *piperp;
+ int ret = 0;
+
+ if (!pdata)
+ return -EINVAL;
+
+ ret = piper_alloc_hw(&piperp, sizeof(*piperp));
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc piper_priv\n");
+ return ret;
+ }
+
+ piperp->ac = kzalloc(sizeof(struct access_ops), GFP_KERNEL);
+ if (!piperp->ac){
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc memory for ac struct\n");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ piperp->drv_name = PIPER_DRIVER_NAME;
+ dev_set_drvdata(&pdev->dev, piperp);
+ piperp->pdata = pdata;
+ pdata->piperp = piperp;
+ spin_lock_init(&piperp->ac->reg_lock);
+ spin_lock_init(&piperp->aesLock);
+
+ piperp->vbase = ioremap(pdev->resource[0].start,
+ pdev[0].resource->end - pdev->resource[0].start);
+
+ if (!piperp->vbase) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": ioremap base %x, len %x error\n",
+ pdev->resource[0].start, pdev[0].resource->end - pdev->resource[0].start);
+ ret = -ENOMEM;
+ goto error_remap;
+ }
+
+ piperp->pstats.tx_start_count = 0;
+ piperp->pstats.tx_complete_count = 0;
+
+ /*
+ * Platform initialization. This will initialize the hardware, including the load
+ * of the mac and dsp firmware into the piper chip
+ */
+ if (pdata->init) {
+ if ((ret = pdata->init(piperp)) != 0) {
+ printk(KERN_ERR PIPER_DRIVER_NAME
+ ": platform init() returned error (%d)\n", ret);
+ goto error_init;
+ }
+ }
+
+ piper_ps_init(piperp);
+ init_timer(&piperp->tx_timer);
+ piperp->tx_timer.function = tx_timer_timeout;
+ piperp->tx_timer.data = (unsigned long) piperp;
+ piper_init_rx_tx(piperp);
+ piper_init_keys(piperp);
+
+ piperp->init_hw = piper_init_hw;
+ piperp->deinit_hw = piper_deinit_hw;
+ piperp->set_irq_mask_bit = piper_set_irq_mask;
+ piperp->clear_irq_mask_bit = piper_clear_irq_mask;
+ piperp->load_beacon = load_beacon;
+ piperp->rand = local_rand;
+ piperp->get_next_beacon_backoff = get_next_beacon_backoff;
+ piperp->set_antenna = piper_set_antenna;
+ piperp->set_tracking_constant = piper_set_tracking_constant;
+ piperp->antenna = ANTENNA_1;
+ piperp->adjust_max_agc = adjust_max_agc;
+
+ /*
+ * Set the default duty cycle value. Note that duty cycling
+ * is disabled reguardless of what this variable is set to until
+ * the user types "iwconfig wlan0 power on". I just love the
+ * "power on" syntax to turn *down* the power.
+ */
+ piperp->power_duty = 100;
+
+ /* TODO this should be read earlier and actions should be taken
+ * based on different revisions at driver initialization or runtime */
+ piperp->version = piperp->ac->rd_reg(piperp, BB_VERSION);
+
+ piperp->irq = pdev->resource[1].start;
+ piperp->tx_cts = false;
+ piperp->beacon.loaded = false;
+ piperp->beacon.enabled = false;
+ piperp->beacon.weSentLastOne = false;
+
+ ret = request_irq(piperp->irq, piper_irq_handler,
+ IRQF_TRIGGER_HIGH, PIPER_DRIVER_NAME, piperp);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": unable to request irq %d (%d)",
+ piperp->irq, ret);
+ goto retor_irq;
+ }
+
+ disable_irq(piperp->irq);
+
+ ret = piper_register_hw(piperp, &pdev->dev, &al7230_rf_ops);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to register priv\n");
+ goto error_reg_hw;
+ }
+
+ if (pdata->late_init)
+ pdata->late_init(piperp);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_antenna_sel);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ ret = device_create_file(&pdev->dev, &dev_attr_power_duty);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ strcpy(piperp->debug_cmd, "off");
+#if WANT_DEBUG_COMMANDS
+ ret = device_create_file(&pdev->dev, &dev_attr_debug_cmd);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+#endif
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": driver loaded (fw ver = 0x%08x)\n",
+ piperp->version);
+
+ return 0;
+
+error_sysfs:
+ piper_unregister_hw(piperp);
+error_reg_hw:
+ piper_ps_deinit(piperp);
+ piper_free_rx_tx(piperp);
+retor_irq:
+ free_irq(piperp->irq, piperp);
+error_init:
+ iounmap(piperp->vbase);
+ piperp->vbase = NULL;
+error_remap:
+ release_resource(pdev->resource);
+error_alloc:
+ piper_free_hw(piperp);
+ return ret;
+}
+
+static int piper_remove(struct platform_device *pdev)
+{
+ struct piper_priv *piperp = dev_get_drvdata(&pdev->dev);
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME " %s\n", __func__);
+
+ device_remove_file(&pdev->dev, &dev_attr_antenna_sel);
+ device_remove_file(&pdev->dev, &dev_attr_power_duty);
+#if WANT_DEBUG_COMMANDS
+ device_remove_file(&pdev->dev, &dev_attr_debug_cmd);
+#endif
+
+ piper_ps_deinit(piperp);
+ piper_unregister_hw(piperp);
+ disable_irq(piperp->irq);
+ piper_clear_irq_mask(piperp, 0xffffffff);
+ free_irq(piperp->irq, piperp);
+ piper_free_rx_tx(piperp);
+ release_resource(pdev->resource);
+ piper_free_hw(piperp);
+
+ return 0;
+}
+
+/* describes the driver */
+static struct platform_driver piper_driver = {
+ .probe = piper_probe,
+ .remove = piper_remove,
+ .suspend = piper_suspend,
+ .resume = piper_resume,
+ .driver = {
+ .name = PIPER_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init piper_init_module(void)
+{
+ return platform_driver_register(&piper_driver);
+}
+
+static void __exit piper_exit_module(void)
+{
+ platform_driver_unregister(&piper_driver);
+}
+
+module_init(piper_init_module);
+module_exit(piper_exit_module);
+
+MODULE_DESCRIPTION("Digi Piper WLAN Driver");
+MODULE_AUTHOR("Contact support@digi.com for questions on this code");
+MODULE_VERSION(DRV_VERS);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/piperDsp.c b/drivers/net/wireless/digiPiper/piperDsp.c
new file mode 100644
index 000000000000..0f3d0e1b9b5e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi DSP Code for Piper
+*/
+
+
+const unsigned long piper_wifi_dsp_ucode[1024] = {
+ 0x32000, 0x3200F, 0x320AB, 0x0403F,
+ 0x0C000, 0x38000, 0x38060, 0x10004,
+ 0x10008, 0x10009, 0x1000A, 0x1000B,
+ 0x10001, 0x10010, 0x08000, 0x08000,
+ 0x10001, 0x08000, 0x08000, 0x08000,
+ 0x08000, 0x08000, 0x08000, 0x08000,
+ 0x32030, 0x39010, 0x39010, 0x30802,
+ 0x2B030, 0x10001, 0x10800, 0x38000,
+ 0x10004, 0x10040, 0x11002, 0x320D1,
+ 0x041AD, 0x0C000, 0x38000, 0x38160,
+ 0x38400, 0x061AD, 0x38100, 0x39171,
+ 0x39571, 0x32072, 0x11002, 0x320E1,
+ 0x38100, 0x39171, 0x39571, 0x36002,
+ 0x29039, 0x38000, 0x11002, 0x38000,
+ 0x38060, 0x10001, 0x083D5, 0x10050,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10104, 0x10013, 0x321D1, 0x0422D,
+ 0x0E000, 0x38000, 0x38260, 0x38400,
+ 0x0622D, 0x0C000, 0x38200, 0x39271,
+ 0x39671, 0x32002, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x36002, 0x29034, 0x38000, 0x10003,
+ 0x10001, 0x38060, 0x083B1, 0x10050,
+ 0x10104, 0x10040, 0x10213, 0x320D1,
+ 0x0422D, 0x0E000, 0x38000, 0x38260,
+ 0x38400, 0x0622D, 0x0C000, 0x38200,
+ 0x39271, 0x39671, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10003, 0x10001, 0x38060, 0x10004,
+ 0x10000, 0x08392, 0x10010, 0x08380,
+ 0x30802, 0x29003, 0x37012, 0x082BF,
+ 0x38000, 0x29002, 0x37012, 0x08078,
+ 0x29002, 0x37012, 0x08105, 0x29002,
+ 0x37012, 0x081C2, 0x29002, 0x37172,
+ 0x0822F, 0x04236, 0x10801, 0x29002,
+ 0x370F2, 0x0838A, 0x29002, 0x370F2,
+ 0x083A7, 0x29002, 0x370F2, 0x083C4,
+ 0x38000, 0x08002, 0x38000, 0x38000,
+ 0x30822, 0x10104, 0x10040, 0x10213,
+ 0x320D1, 0x0422D, 0x0E000, 0x38000,
+ 0x38260, 0x38400, 0x0622D, 0x0C000,
+ 0x38200, 0x39271, 0x39671, 0x10003,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10013, 0x321E1, 0x38200, 0x39271,
+ 0x39671, 0x10003, 0x38000, 0x38060,
+ 0x36002, 0x2A005, 0x0422D, 0x38400,
+ 0x0402D, 0x38060, 0x38400, 0x10004,
+ 0x10000, 0x10001, 0x08349, 0x10010,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x3601F, 0x30033, 0x23103, 0x38000,
+ 0x08030, 0x38000, 0x08031, 0x38000,
+ 0x327F0, 0x300F1, 0x37091, 0x21034,
+ 0x30803, 0x300F2, 0x23009, 0x32081,
+ 0x10002, 0x04180, 0x0C100, 0x38000,
+ 0x38040, 0x04400, 0x320A1, 0x38060,
+ 0x04900, 0x0E630, 0x38000, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x04000, 0x0C000, 0x300F1, 0x22004,
+ 0x37041, 0x23003, 0x04401, 0x38000,
+ 0x38020, 0x3601F, 0x30033, 0x2310C,
+ 0x38000, 0x082EA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x38000, 0x10800, 0x38000,
+ 0x300F2, 0x32081, 0x35021, 0x20017,
+ 0x300F2, 0x37052, 0x28036, 0x0440A,
+ 0x0C000, 0x38000, 0x38060, 0x30011,
+ 0x20004, 0x0440A, 0x0E000, 0x2103F,
+ 0x38060, 0x05410, 0x0E000, 0x38000,
+ 0x38060, 0x08005, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x04000,
+ 0x10008, 0x05C02, 0x0C000, 0x38000,
+ 0x38020, 0x04C12, 0x0E000, 0x10002,
+ 0x38020, 0x0C209, 0x04914, 0x38000,
+ 0x0C409, 0x38040, 0x38000, 0x38000,
+ 0x38020, 0x32011, 0x36001, 0x0E000,
+ 0x05E02, 0x38000, 0x38120, 0x0E201,
+ 0x04914, 0x38000, 0x0E401, 0x38040,
+ 0x38000, 0x38020, 0x21034, 0x38000,
+ 0x10202, 0x04924, 0x38000, 0x38400,
+ 0x10202, 0x041A4, 0x0C100, 0x38060,
+ 0x04424, 0x38060, 0x04924, 0x0E630,
+ 0x320A1, 0x38000, 0x38060, 0x38000,
+ 0x2103E, 0x38060, 0x04403, 0x0C000,
+ 0x300F0, 0x38020, 0x0E000, 0x37030,
+ 0x39030, 0x39030, 0x0401B, 0x10401,
+ 0x38040, 0x10001, 0x05C1C, 0x38000,
+ 0x38040, 0x08282, 0x10010, 0x00000,
+ 0x10023, 0x10104, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0C000,
+ 0x38060, 0x100D3, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x10003, 0x10402,
+ 0x0422D, 0x0C000, 0x323F0, 0x38200,
+ 0x39270, 0x39770, 0x04401, 0x0C000,
+ 0x1000B, 0x38020, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x08254,
+ 0x10010, 0x38000, 0x38000, 0x38000,
+ 0x10462, 0x10014, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0C000,
+ 0x38060, 0x10512, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x05036, 0x0E008,
+ 0x10212, 0x38060, 0x38400, 0x04427,
+ 0x0C000, 0x38060, 0x0403F, 0x38000,
+ 0x38020, 0x0493F, 0x0E630, 0x32051,
+ 0x38000, 0x38060, 0x38000, 0x2103E,
+ 0x38060, 0x300F1, 0x37061, 0x0443E,
+ 0x0C000, 0x38020, 0x0643F, 0x0C000,
+ 0x38020, 0x38020, 0x38020, 0x0563F,
+ 0x0E000, 0x2000A, 0x38000, 0x21004,
+ 0x38020, 0x04C37, 0x08014, 0x38020,
+ 0x2000C, 0x38000, 0x21006, 0x38000,
+ 0x04637, 0x38020, 0x05C37, 0x0800B,
+ 0x38020, 0x38020, 0x21005, 0x04437,
+ 0x38020, 0x05637, 0x08004, 0x38020,
+ 0x05C37, 0x38020, 0x38000, 0x11102,
+ 0x041B6, 0x0C000, 0x38000, 0x38020,
+ 0x04E37, 0x0E000, 0x38020, 0x05C3F,
+ 0x0C000, 0x38020, 0x04438, 0x0E000,
+ 0x38020, 0x04000, 0x38000, 0x10008,
+ 0x04401, 0x0C000, 0x38020, 0x06439,
+ 0x0E000, 0x38020, 0x38020, 0x38020,
+ 0x38020, 0x38020, 0x38020, 0x04439,
+ 0x38000, 0x38020, 0x04409, 0x0C000,
+ 0x1000B, 0x06409, 0x38020, 0x38020,
+ 0x38020, 0x1000A, 0x10212, 0x04136,
+ 0x0C800, 0x38000, 0x38060, 0x11112,
+ 0x041B6, 0x0E000, 0x38000, 0x38060,
+ 0x25004, 0x38000, 0x10800, 0x38000,
+ 0x38000, 0x10400, 0x081CD, 0x38000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10422, 0x10023, 0x10114, 0x04BAD,
+ 0x0C080, 0x32190, 0x38300, 0x39370,
+ 0x39F70, 0x104D2, 0x100D3, 0x32190,
+ 0x38300, 0x39370, 0x39F70, 0x38000,
+ 0x10433, 0x0422D, 0x0C000, 0x32030,
+ 0x39270, 0x39A10, 0x10063, 0x10334,
+ 0x32030, 0x39270, 0x39A10, 0x10512,
+ 0x041AD, 0x32020, 0x39170, 0x39510,
+ 0x10782, 0x10114, 0x32020, 0x39170,
+ 0x39510, 0x104D3, 0x0422D, 0x0C070,
+ 0x10334, 0x38260, 0x0E070, 0x32060,
+ 0x39210, 0x39270, 0x107B2, 0x104D3,
+ 0x10020, 0x05BAD, 0x0E060, 0x32190,
+ 0x38300, 0x39370, 0x39B70, 0x13FC3,
+ 0x0422D, 0x0C070, 0x10114, 0x38260,
+ 0x0E070, 0x32060, 0x39210, 0x39270,
+ 0x104E2, 0x13FC3, 0x05BAD, 0x0E060,
+ 0x32190, 0x38300, 0x39370, 0x39B70,
+ 0x10004, 0x08326, 0x10000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10114, 0x10173, 0x10902, 0x0616D,
+ 0x0C800, 0x321B0, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x0416D,
+ 0x321B0, 0x10173, 0x10912, 0x38260,
+ 0x38260, 0x38260, 0x39270, 0x39770,
+ 0x10003, 0x10C42, 0x0616D, 0x0C800,
+ 0x321B0, 0x38260, 0x38260, 0x38260,
+ 0x39270, 0x39770, 0x0416D, 0x321B0,
+ 0x10003, 0x10C52, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x32028,
+ 0x0C070, 0x10973, 0x0423F, 0x38060,
+ 0x10B33, 0x0E070, 0x108E3, 0x38060,
+ 0x38060, 0x0E060, 0x10AA3, 0x38000,
+ 0x38060, 0x0C000, 0x0503F, 0x10222,
+ 0x38400, 0x080C6, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x108D3, 0x10104, 0x10140, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38080,
+ 0x382C0, 0x380A0, 0x216FF, 0x380A0,
+ 0x10823, 0x0C202, 0x32181, 0x38200,
+ 0x38080, 0x382C0, 0x380A0, 0x216FF,
+ 0x380A0, 0x080FA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10902, 0x108D3, 0x10114, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38280, 0x38040, 0x381A0, 0x216FE,
+ 0x38440, 0x10C42, 0x10823, 0x047F6,
+ 0x0D202, 0x32171, 0x38280, 0x38040,
+ 0x381A0, 0x216FE, 0x38440, 0x381A0,
+ 0x386E0, 0x321A1, 0x2103F, 0x10001,
+ 0x38400, 0x080D2, 0x10010, 0x00000,
+ 0x38000, 0x38000, 0x38000, 0x0423F,
+ 0x10973, 0x0C000, 0x38060, 0x10B33,
+ 0x0E000, 0x108E3, 0x38060, 0x38060,
+ 0x0E030, 0x10AA3, 0x38000, 0x38060,
+ 0x18083, 0x0C0C0, 0x0443F, 0x36018,
+ 0x38060, 0x21002, 0x0C000, 0x32018,
+ 0x0503F, 0x10222, 0x38400, 0x041A4,
+ 0x0C100, 0x38060, 0x04424, 0x38060,
+ 0x048A4, 0x0E630, 0x32061, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x10080, 0x04401, 0x0C000, 0x38020,
+ 0x06639, 0x0E000, 0x38020, 0x04409,
+ 0x0C000, 0x1000B, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x30811,
+ 0x22007, 0x37021, 0x20003, 0x38000,
+ 0x08383, 0x38000, 0x083A1, 0x38000,
+ 0x10902, 0x108D3, 0x10104, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38080, 0x38240, 0x381A0, 0x38460,
+ 0x214FD, 0x38740, 0x10C42, 0x10823,
+ 0x047F6, 0x0D202, 0x32171, 0x38080,
+ 0x38240, 0x381A0, 0x38460, 0x214FD,
+ 0x38740, 0x381A0, 0x38460, 0x384E0,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38400, 0x10001, 0x08068,
+ 0x10010, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x041A4, 0x0C100, 0x38000, 0x38060,
+ 0x04424, 0x32061, 0x38060, 0x04924,
+ 0x0E630, 0x38000, 0x38000, 0x38060,
+ 0x38000, 0x2103E, 0x38060, 0x10080,
+ 0x108D3, 0x10104, 0x10040, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38200,
+ 0x38200, 0x21240, 0x21640, 0x10823,
+ 0x32181, 0x38200, 0x38200, 0x38200,
+ 0x21240, 0x21640, 0x04401, 0x0C000,
+ 0x38020, 0x06639, 0x0E000, 0x38020,
+ 0x04409, 0x0C000, 0x1000B, 0x06409,
+ 0x38020, 0x38020, 0x38020, 0x1000A,
+ 0x0801F, 0x10010, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000
+};
+
+const int piper_dsp_data_len = (sizeof piper_wifi_dsp_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperDsp.h b/drivers/net/wireless/digiPiper/piperDsp.h
new file mode 100644
index 000000000000..836ffcb96341
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.h
@@ -0,0 +1,8 @@
+#ifndef PIPER_DSP_H_
+#define PIPER_DSP_H_
+
+
+extern const unsigned long piper_wifi_dsp_ucode[];
+extern const int piper_dsp_data_len;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.c b/drivers/net/wireless/digiPiper/piperMacAssist.c
new file mode 100644
index 000000000000..1a8ada81b84b
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi MAC assist Code for Piper
+*/
+
+const unsigned long piper_wifi_macassist_ucode[2048] = {
+ 0x00F00, 0x2CF22, 0x3033B, 0x00E20, 0x2CED0, 0x2CFD2, 0x2CFD8, 0x2CFD9,
+ 0x2CF31, 0x2CFDB, 0x00E20, 0x2CEDA, 0x00E64, 0x2CE1D, 0x00EFF, 0x2CEEA,
+ 0x2CEEB, 0x00E01, 0x2CE20, 0x00E10, 0x2CE21, 0x04522, 0x00603, 0x2C6CE,
+ 0x00001, 0x2C040, 0x00000, 0x2C041, 0x2C042, 0x2C043, 0x0016E, 0x2C144,
+ 0x00000, 0x0010A, 0x00200, 0x0A5FF, 0x35027, 0x0010B, 0x00280, 0x2C245,
+ 0x2C146, 0x2C047, 0x00180, 0x2C148, 0x2C049, 0x2C0CE, 0x00000, 0x00100,
+ 0x00223, 0x040C0, 0x302E2, 0x3031A, 0x00030, 0x0011C, 0x00801, 0x042C0,
+ 0x0A204, 0x35037, 0x042C0, 0x0A5FF, 0x35040, 0x00031, 0x00117, 0x00806,
+ 0x30338, 0x1C001, 0x35C40, 0x1C101, 0x35C43, 0x00000, 0x042C0, 0x043C0,
+ 0x044C0, 0x045C0, 0x046C0, 0x047C0, 0x00000, 0x1C801, 0x35C4D, 0x048C1,
+ 0x049C1, 0x04AC1, 0x04BC1, 0x04CC1, 0x04DC1, 0x0D320, 0x0D430, 0x0D540,
+ 0x0D650, 0x0D760, 0x19230, 0x19240, 0x19250, 0x19260, 0x19270, 0x0A880,
+ 0x35062, 0x18208, 0x0A980, 0x35065, 0x18208, 0x0AA80, 0x35068, 0x18208,
+ 0x0AB80, 0x3506B, 0x18208, 0x0AC80, 0x3506E, 0x18208, 0x0AD80, 0x35071,
+ 0x18208, 0x20206, 0x04122, 0x0A1FF, 0x3547B, 0x2C222, 0x0E260, 0x35015,
+ 0x0E260, 0x00151, 0x3407C, 0x00103, 0x19120, 0x2C122, 0x00E00, 0x2CECE,
+ 0x3C001, 0x04EC3, 0x0AE20, 0x04FC2, 0x0AF80, 0x0DEF0, 0x04F30, 0x2CE30,
+ 0x0FFE0, 0x35095, 0x0EE20, 0x35495, 0x3C000, 0x04EC1, 0x0AE80, 0x3548D,
+ 0x04E17, 0x2CED4, 0x04E16, 0x2CED6, 0x3C001, 0x04E27, 0x0AE10, 0x04F33,
+ 0x2CE33, 0x0EF10, 0x0BFE0, 0x350AC, 0x00E00, 0x00FE0, 0x2DEF0, 0x18F01,
+ 0x0EFE8, 0x350A4, 0x0EFE8, 0x3409E, 0x04F1D, 0x00E00, 0x1DEF0, 0x2CEEA,
+ 0x04F1C, 0x00E00, 0x1FEF0, 0x2CEEB, 0x04E27, 0x0AE80, 0x350C8, 0x3C000,
+ 0x00E01, 0x2CECE, 0x04EC1, 0x0AE80, 0x354B2, 0x04EE0, 0x2CE27, 0x04EE1,
+ 0x2CE26, 0x04EE2, 0x2CE25, 0x04EE3, 0x2CE24, 0x04EE4, 0x2CE23, 0x04EE5,
+ 0x2CE22, 0x04EE6, 0x04FE7, 0x2CE21, 0x2CF20, 0x00E00, 0x2CECE, 0x3C001,
+ 0x04E35, 0x04F36, 0x0DEF0, 0x3507E, 0x04E3A, 0x04FEB, 0x15EF0, 0x3547E,
+ 0x04FEA, 0x04E37, 0x15EF0, 0x3547E, 0x3C000, 0x04EC1, 0x0AE80, 0x354D5,
+ 0x04E36, 0x12EFF, 0x354E7, 0x04F35, 0x14F40, 0x35CE7, 0x00E00, 0x2CED8,
+ 0x20F06, 0x20F06, 0x2CFD9, 0x3C001, 0x2CE36, 0x2CE35, 0x3407E, 0x04F35,
+ 0x1CF01, 0x1EE00, 0x2CF35, 0x2CE36, 0x00E0F, 0x2CED8, 0x2CED9, 0x3C001,
+ 0x04E37, 0x04F3A, 0x18E01, 0x1AF00, 0x2CE37, 0x2CF3A, 0x3407E, 0x2CF32,
+ 0x04FC0, 0x20F0E, 0x3190B, 0x20F0E, 0x31BFC, 0x20F0E, 0x3195B, 0x20F0E,
+ 0x31A72, 0x20F0E, 0x31ACA, 0x20F0E, 0x319DC, 0x20F0E, 0x319E3, 0x20F0E,
+ 0x31A63, 0x04F32, 0x38001, 0x04031, 0x0C001, 0x2C031, 0x040C2, 0x0A008,
+ 0x35120, 0x04031, 0x0A004, 0x35517, 0x04031, 0x0C080, 0x2C031, 0x302E2,
+ 0x040C3, 0x0A010, 0x3511C, 0x00016, 0x18011, 0x00100, 0x00200, 0x3031A,
+ 0x302FF, 0x0E3FF, 0x35127, 0x04031, 0x0A0BF, 0x2C031, 0x3412A, 0x04031,
+ 0x0C040, 0x2C031, 0x04244, 0x040C3, 0x0A010, 0x35547, 0x302E8, 0x00400,
+ 0x04510, 0x0A5FF, 0x35534, 0x0C515, 0x13350, 0x3513B, 0x01430, 0x1232F,
+ 0x35544, 0x2030E, 0x34134, 0x1232F, 0x3553F, 0x2030E, 0x34134, 0x124FF,
+ 0x35544, 0x2030E, 0x35D34, 0x00401, 0x302F4, 0x2C33B, 0x2A000, 0x04511,
+ 0x1426E, 0x3514F, 0x14237, 0x35152, 0x14214, 0x35155, 0x34158, 0x0026E,
+ 0x12508, 0x35559, 0x00237, 0x12504, 0x35559, 0x00214, 0x12502, 0x35559,
+ 0x0020A, 0x2C23B, 0x2A000, 0x04127, 0x00001, 0x2C0CE, 0x040C2, 0x0A040,
+ 0x35163, 0x00005, 0x2C0CE, 0x0A110, 0x35192, 0x04048, 0x0E080, 0x3516C,
+ 0x04048, 0x0E050, 0x3516C, 0x34192, 0x00012, 0x04140, 0x0E1EE, 0x35175,
+ 0x041C1, 0x0A120, 0x35574, 0x18020, 0x18007, 0x302FF, 0x00100, 0x19030,
+ 0x1A100, 0x30338, 0x042E0, 0x043E1, 0x044E2, 0x045E3, 0x046E4, 0x047E5,
+ 0x048E6, 0x049E7, 0x19200, 0x1B310, 0x1A400, 0x1A500, 0x1A600, 0x1A700,
+ 0x1A800, 0x1A900, 0x2C260, 0x2C361, 0x2C462, 0x2C563, 0x2C664, 0x2C765,
+ 0x2C866, 0x2C967, 0x0002C, 0x00100, 0x042C2, 0x044C1, 0x0A480, 0x35595,
+ 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x2C3C2, 0x04140, 0x04348, 0x00000,
+ 0x2C0CE, 0x04031, 0x0A0FB, 0x2C031, 0x0A204, 0x355B6, 0x0A30F, 0x0E304,
+ 0x351B6, 0x04012, 0x0A001, 0x351B6, 0x0E1EE, 0x355AF, 0x0C004, 0x0C00A,
+ 0x2C012, 0x00064, 0x1C001, 0x355B2, 0x00040, 0x2C0C4, 0x040C2, 0x0A040,
+ 0x2B400, 0x0403E, 0x0A0FF, 0x2B400, 0x00001, 0x2C0CE, 0x04040, 0x0A0FF,
+ 0x351C5, 0x0E001, 0x351C5, 0x0E0EF, 0x355D4, 0x04042, 0x0A0FF, 0x351CB,
+ 0x0E001, 0x355D4, 0x341CE, 0x04041, 0x0A0FF, 0x351D4, 0x04043, 0x0A0FF,
+ 0x355D4, 0x00000, 0x2C0CE, 0x2A000, 0x00080, 0x2C0C4, 0x0403F, 0x18001,
+ 0x2C03F, 0x00000, 0x2C0CE, 0x2A000, 0x00008, 0x2C0C4, 0x00100, 0x2C13E,
+ 0x00012, 0x00200, 0x3431A, 0x00101, 0x2C1CE, 0x0402B, 0x12001, 0x35203,
+ 0x04128, 0x121FF, 0x351EE, 0x1C101, 0x2C128, 0x35603, 0x04129, 0x0422D,
+ 0x0432C, 0x0442F, 0x0452E, 0x12002, 0x351F7, 0x2C128, 0x341F9, 0x0A0FE,
+ 0x2C02B, 0x00000, 0x2C0CE, 0x0401D, 0x1D200, 0x0401C, 0x1F300, 0x2C237,
+ 0x2C33A, 0x2C435, 0x2C536, 0x00000, 0x2C0CE, 0x04027, 0x0A010, 0x35236,
+ 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A, 0x00100, 0x0401F, 0x1D100,
+ 0x2C1E8, 0x00100, 0x0401E, 0x1F100, 0x2C1E9, 0x040C2, 0x0A004, 0x3561D,
+ 0x3033B, 0x00004, 0x2C0D0, 0x00000, 0x2C0D2, 0x04027, 0x0A004, 0x3522C,
+ 0x30338, 0x30338, 0x040C2, 0x0C080, 0x2C0C2, 0x040C2, 0x0A040, 0x3522C,
+ 0x0402F, 0x2C0D4, 0x0402E, 0x2C0D6, 0x00110, 0x040C1, 0x0A010, 0x35234,
+ 0x04031, 0x0D010, 0x2C031, 0x34246, 0x2C1C4, 0x34246, 0x00010, 0x2C0C4,
+ 0x04034, 0x0C000, 0x35246, 0x040ED, 0x0C000, 0x35646, 0x3033B, 0x0421F,
+ 0x0431E, 0x2C2D8, 0x2C3D9, 0x04119, 0x1D010, 0x2C0ED, 0x0421D, 0x0431C,
+ 0x00000, 0x00100, 0x1D020, 0x1F130, 0x3033B, 0x2C0EA, 0x2C1EB, 0x040EC,
+ 0x0C000, 0x35655, 0x04118, 0x1D010, 0x2C0EC, 0x3033B, 0x040EE, 0x0C000,
+ 0x2B400, 0x041EF, 0x0C100, 0x2B400, 0x0421B, 0x0431A, 0x1D020, 0x1F130,
+ 0x2C0EE, 0x2C1EF, 0x2A000, 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A,
+ 0x040C1, 0x0A010, 0x3566E, 0x00020, 0x2C0C4, 0x2A000, 0x04031, 0x0C020,
+ 0x2C031, 0x2A000, 0x04012, 0x0A0F7, 0x2C012, 0x00500, 0x00601, 0x00100,
+ 0x00700, 0x04031, 0x0A0F7, 0x2C031, 0x2C53E, 0x040C3, 0x0A0C0, 0x35287,
+ 0x00603, 0x0A080, 0x35684, 0x00605, 0x2C6CE, 0x04440, 0x342A4, 0x00702,
+ 0x2C6CE, 0x04440, 0x0424C, 0x0A201, 0x356A4, 0x04248, 0x0E2B4, 0x35293,
+ 0x04249, 0x0A204, 0x35298, 0x2C5CE, 0x04231, 0x0C208, 0x2C231, 0x2C6CE,
+ 0x00203, 0x04048, 0x2C5CE, 0x0E0C4, 0x352B9, 0x00030, 0x0A4EE, 0x356A2,
+ 0x00080, 0x1805E, 0x00209, 0x342B9, 0x2C5CE, 0x00000, 0x00100, 0x04231,
+ 0x0A240, 0x356AD, 0x042C1, 0x0A201, 0x356B2, 0x0A4EE, 0x356B0, 0x000FE,
+ 0x1803C, 0x1A100, 0x00200, 0x0A4EE, 0x356B7, 0x18016, 0x1A100, 0x18012,
+ 0x1A100, 0x3031A, 0x12702, 0x352C2, 0x0A202, 0x356C2, 0x04131, 0x12108,
+ 0x356C2, 0x0C780, 0x04131, 0x0A130, 0x0D710, 0x2C7C4, 0x04731, 0x0A7CF,
+ 0x2C731, 0x2A000, 0x04531, 0x12504, 0x352D4, 0x3033B, 0x0A5FB, 0x2C531,
+ 0x00500, 0x2C5D8, 0x2C5D9, 0x342DE, 0x04031, 0x0A080, 0x2B400, 0x04131,
+ 0x0A1F7, 0x2C131, 0x00184, 0x2C1C4, 0x00101, 0x2C1CE, 0x04440, 0x302E2,
+ 0x00500, 0x342A4, 0x040C1, 0x0A080, 0x356E2, 0x00020, 0x2C0DB, 0x2A000,
+ 0x00310, 0x12202, 0x352EC, 0x00301, 0x12201, 0x356F0, 0x20306, 0x20306,
+ 0x12204, 0x352F3, 0x20306, 0x2A000, 0x00308, 0x12455, 0x356F8, 0x0030C,
+ 0x12433, 0x352FB, 0x0C301, 0x1240F, 0x352FE, 0x0C302, 0x2A000, 0x04240,
+ 0x0E2EE, 0x35708, 0x04244, 0x003FF, 0x0A208, 0x2B000, 0x00300, 0x2A000,
+ 0x003FF, 0x04244, 0x0E26E, 0x3570D, 0x00311, 0x04244, 0x0E237, 0x35711,
+ 0x00323, 0x04244, 0x0E214, 0x35715, 0x00360, 0x04244, 0x0E20A, 0x2B400,
+ 0x003C0, 0x2A000, 0x0C000, 0x35322, 0x04325, 0x01430, 0x20306, 0x19430,
+ 0x19040, 0x1A100, 0x1C01A, 0x1E100, 0x04AC1, 0x0AA80, 0x35724, 0x043DB,
+ 0x19030, 0x1A100, 0x12180, 0x3532F, 0x00001, 0x00100, 0x34331, 0x18001,
+ 0x1A100, 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000,
+ 0x04AC1, 0x0AA80, 0x35338, 0x04AC1, 0x0AA80, 0x3573B, 0x2A000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00001, 0x2C0CF, 0x2A000, 0x340F7,
+ 0x040C3, 0x0A002, 0x35008, 0x300EA, 0x04031, 0x0C002, 0x2C031, 0x343FC,
+ 0x04031, 0x12002, 0x3540E, 0x12001, 0x353FC, 0x300EA, 0x04031, 0x0A0F8,
+ 0x2C031, 0x30058, 0x04031, 0x0A0F7, 0x2C031, 0x04123, 0x01010, 0x0A00C,
+ 0x0E004, 0x35020, 0x04031, 0x0A080, 0x35042, 0x00084, 0x2C0C4, 0x34042,
+ 0x04023, 0x0C010, 0x2C023, 0x04027, 0x0A008, 0x35429, 0x01010, 0x0A003,
+ 0x3501A, 0x04031, 0x0A080, 0x35036, 0x04048, 0x0A0EC, 0x0E0C4, 0x3503A,
+ 0x00084, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x3403A, 0x04048, 0x0A0EC,
+ 0x0E0C4, 0x35042, 0x040C1, 0x0A002, 0x35040, 0x00040, 0x2C0C4, 0x3401A,
+ 0x00001, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x04023, 0x0A00C, 0x0E004,
+ 0x357FC, 0x0404C, 0x0A001, 0x3504E, 0x300B0, 0x343FC, 0x04023, 0x0A001,
+ 0x35453, 0x300BF, 0x343FC, 0x04048, 0x0E050, 0x357FC, 0x300DB, 0x343FC,
+ 0x04031, 0x0A040, 0x3505F, 0x04023, 0x0C020, 0x2C023, 0x3406B, 0x040C1,
+ 0x0A001, 0x3506B, 0x04023, 0x0C004, 0x2C023, 0x04048, 0x0A003, 0x35073,
+ 0x04023, 0x0C008, 0x2C023, 0x040C3, 0x0A010, 0x3506F, 0x00054, 0x1804E,
+ 0x00100, 0x00200, 0x34306, 0x040C2, 0x0A004, 0x2B400, 0x040C3, 0x0A010,
+ 0x3507A, 0x00016, 0x18012, 0x00100, 0x00200, 0x30306, 0x0004C, 0x05100,
+ 0x0A101, 0x2B000, 0x04008, 0x0A001, 0x35088, 0x04048, 0x0E080, 0x3509A,
+ 0x04049, 0x0A002, 0x3548C, 0x00008, 0x18050, 0x05100, 0x0A101, 0x35494,
+ 0x00208, 0x3032E, 0x0C400, 0x2B400, 0x04048, 0x0E040, 0x3549A, 0x04027,
+ 0x0A010, 0x2B000, 0x0402D, 0x0A0FF, 0x350AC, 0x04048, 0x0E080, 0x354AC,
+ 0x04063, 0x04139, 0x2C039, 0x0F010, 0x354AC, 0x04062, 0x04138, 0x0422D,
+ 0x0B020, 0x2C038, 0x0F010, 0x2B000, 0x04023, 0x0C002, 0x2C023, 0x2A000,
+ 0x04048, 0x0A0EC, 0x0E0E4, 0x354BC, 0x04036, 0x04135, 0x0D010, 0x2B400,
+ 0x3033C, 0x2C0D8, 0x2C0D9, 0x2A000, 0x04048, 0x0E080, 0x310DB, 0x0414B,
+ 0x0A180, 0x2B400, 0x0414A, 0x0424B, 0x1C10A, 0x1E200, 0x35CC9, 0x00100,
+ 0x00200, 0x30340, 0x2BC00, 0x04048, 0x0E0B4, 0x2B400, 0x2A000, 0x04031,
+ 0x0C004, 0x2C031, 0x00078, 0x00100, 0x042C3, 0x0A210, 0x350D9, 0x0006A,
+ 0x00101, 0x00209, 0x34306, 0x0466D, 0x1866F, 0x05160, 0x19610, 0x0416A,
+ 0x0A10C, 0x2C134, 0x350E9, 0x1860A, 0x05160, 0x18601, 0x05260, 0x30340,
+ 0x1C60B, 0x34213, 0x00000, 0x2C023, 0x04031, 0x0A040, 0x2B400, 0x040C3,
+ 0x0A010, 0x354FB, 0x04046, 0x0A001, 0x354FB, 0x04045, 0x14005, 0x35CFB,
+ 0x040C3, 0x0A002, 0x354F8, 0x04048, 0x0A003, 0x2B400, 0x0004C, 0x05100,
+ 0x0A101, 0x2B400, 0x0424C, 0x0434D, 0x0444E, 0x0454F, 0x04750, 0x04851,
+ 0x04100, 0x0F120, 0x04001, 0x0F030, 0x0D100, 0x04002, 0x0F040, 0x0D100,
+ 0x04003, 0x0F050, 0x0D100, 0x04004, 0x0F070, 0x0D100, 0x04005, 0x0F080,
+ 0x0D100, 0x35146, 0x04027, 0x0A040, 0x35145, 0x00001, 0x2C0CE, 0x04130,
+ 0x0F120, 0x04038, 0x0F200, 0x04031, 0x0F030, 0x0D100, 0x04039, 0x0F030,
+ 0x0D200, 0x04032, 0x0F040, 0x0D100, 0x0403A, 0x0F040, 0x0D200, 0x04033,
+ 0x0F050, 0x0D100, 0x0403B, 0x0F050, 0x0D200, 0x04034, 0x0F070, 0x0D100,
+ 0x0403C, 0x0F070, 0x0D200, 0x04035, 0x0433D, 0x00400, 0x2C4CE, 0x0F080,
+ 0x0D100, 0x35146, 0x0F380, 0x0D230, 0x35146, 0x2A000, 0x04023, 0x0C001,
+ 0x2C023, 0x040C1, 0x0A002, 0x2B400, 0x04048, 0x0A00C, 0x0E004, 0x35564,
+ 0x04048, 0x0A0E0, 0x0E0A0, 0x35164, 0x0E060, 0x2B400, 0x04131, 0x0A108,
+ 0x2B000, 0x3020A, 0x040C3, 0x0A002, 0x3555A, 0x040C1, 0x0A002, 0x35577,
+ 0x040C1, 0x0A001, 0x35177, 0x2A000, 0x3020A, 0x0413B, 0x040C3, 0x0A010,
+ 0x355C5, 0x0C1C0, 0x002EE, 0x00300, 0x00701, 0x00603, 0x040C3, 0x0A002,
+ 0x3556E, 0x040C1, 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x3557C, 0x042C2,
+ 0x0A2E0, 0x0C203, 0x2C2C2, 0x2A000, 0x2C6CE, 0x2C240, 0x2C341, 0x2C342,
+ 0x2C343, 0x2C144, 0x2C745, 0x2C346, 0x2C347, 0x2C3CE, 0x04248, 0x0434A,
+ 0x0444B, 0x01540, 0x0E580, 0x35193, 0x01520, 0x0E5B4, 0x35195, 0x04549,
+ 0x0A504, 0x35595, 0x0A400, 0x0A300, 0x341AB, 0x00528, 0x01010, 0x0A007,
+ 0x351A9, 0x0E004, 0x351A9, 0x0E001, 0x351A9, 0x18504, 0x0E004, 0x351A9,
+ 0x0E007, 0x351A9, 0x18508, 0x0E004, 0x351A9, 0x18504, 0x0E005, 0x351A9,
+ 0x1850C, 0x1D350, 0x1E400, 0x00000, 0x2C6CE, 0x0E2B4, 0x351B1, 0x005D4,
+ 0x341B2, 0x005C4, 0x2C548, 0x2C049, 0x2C34A, 0x2C44B, 0x00306, 0x00152,
+ 0x0024C, 0x2C0CE, 0x05810, 0x2C6CE, 0x2D820, 0x18101, 0x18201, 0x1C301,
+ 0x355B9, 0x2C0CE, 0x00001, 0x2C03E, 0x2A000, 0x045C1, 0x0A540, 0x351CC,
+ 0x01510, 0x0E50A, 0x355CC, 0x00114, 0x01510, 0x00784, 0x00800, 0x0E56E,
+ 0x351D9, 0x00704, 0x0E559, 0x351D8, 0x0E523, 0x351D7, 0x18838, 0x18823,
+ 0x1880A, 0x1880B, 0x00603, 0x00300, 0x040C3, 0x0A002, 0x355DC, 0x040C1,
+ 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x35177, 0x2C6CE, 0x2C340, 0x2C341,
+ 0x2C342, 0x2C343, 0x2C144, 0x2C745, 0x2C846, 0x2C347, 0x2C3CE, 0x04248,
+ 0x0434A, 0x0444B, 0x01540, 0x0E580, 0x351FC, 0x01520, 0x0E5B4, 0x351FE,
+ 0x04549, 0x0A504, 0x355FE, 0x0A400, 0x0A300, 0x341AB, 0x01580, 0x1850A,
+ 0x1D350, 0x1E400, 0x045C1, 0x0A540, 0x35606, 0x185A0, 0x18520, 0x1D350,
+ 0x1E400, 0x341AB, 0x00000, 0x00100, 0x00217, 0x04348, 0x0E3B4, 0x35211,
+ 0x00207, 0x30306, 0x2A000, 0x18603, 0x04027, 0x0A010, 0x0416A, 0x0A102,
+ 0x20106, 0x20106, 0x20106, 0x0F010, 0x2B400, 0x0C100, 0x35237, 0x0426D,
+ 0x04013, 0x0F020, 0x2B400, 0x0006E, 0x00100, 0x0A2FF, 0x2B000, 0x00500,
+ 0x05300, 0x00701, 0x2C7CE, 0x05410, 0x00700, 0x2C7CE, 0x0F340, 0x0D530,
+ 0x18001, 0x18101, 0x1C201, 0x35628, 0x0C500, 0x2B400, 0x34257, 0x00058,
+ 0x00108, 0x00206, 0x00500, 0x05300, 0x05410, 0x0F340, 0x0D530, 0x18001,
+ 0x18101, 0x1C201, 0x3563B, 0x0C500, 0x2B400, 0x04034, 0x0C000, 0x35257,
+ 0x18603, 0x05060, 0x00100, 0x1D100, 0x2C1ED, 0x18601, 0x05060, 0x2C019,
+ 0x18601, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x18602, 0x04048,
+ 0x0A0EC, 0x0E080, 0x35676, 0x04023, 0x0C002, 0x2C023, 0x040C2, 0x0A080,
+ 0x35265, 0x040C2, 0x0A05F, 0x2C0C2, 0x2C0C2, 0x18603, 0x04027, 0x0A010,
+ 0x3526F, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x34276, 0x05060,
+ 0x00100, 0x1D100, 0x2C1EC, 0x18601, 0x05060, 0x2C018, 0x040C3, 0x0A010,
+ 0x35685, 0x04044, 0x04145, 0x04246, 0x00304, 0x2020E, 0x20108, 0x20008,
+ 0x2010E, 0x20008, 0x1C301, 0x35680, 0x34294, 0x04046, 0x04147, 0x04244,
+ 0x003C0, 0x0E26E, 0x3568C, 0x00311, 0x04244, 0x0E237, 0x35690, 0x00323,
+ 0x04244, 0x0E214, 0x35694, 0x00360, 0x1D030, 0x1E100, 0x30339, 0x1801A,
+ 0x1A100, 0x04260, 0x19200, 0x04361, 0x1B310, 0x04462, 0x1A400, 0x04563,
+ 0x1A500, 0x04664, 0x1A600, 0x04765, 0x1A700, 0x04866, 0x1A800, 0x04967,
+ 0x1A900, 0x0406A, 0x0A002, 0x352BF, 0x040E0, 0x1D020, 0x040E1, 0x1F030,
+ 0x040E2, 0x1F040, 0x040E3, 0x1F050, 0x040E4, 0x1F060, 0x040E5, 0x1F070,
+ 0x040E6, 0x1F080, 0x040E7, 0x1F090, 0x2BC00, 0x00000, 0x342C0, 0x30339,
+ 0x2C2E0, 0x2C3E1, 0x2C4E2, 0x2C5E3, 0x2C6E4, 0x2C7E5, 0x2C8E6, 0x2C9E7,
+ 0x01230, 0x0401D, 0x0411C, 0x00D27, 0x00C01, 0x20006, 0x20100, 0x35AD5,
+ 0x18C01, 0x0EC10, 0x352F4, 0x0EC10, 0x342CD, 0x20108, 0x342D8, 0x2010E,
+ 0x20008, 0x1CC01, 0x352EC, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10, 0x35AD7,
+ 0x018A0, 0x019B0, 0x342D7, 0x1CD01, 0x352F8, 0x20306, 0x20400, 0x20500,
+ 0x20600, 0x20700, 0x20800, 0x20900, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10,
+ 0x35AE3, 0x018A0, 0x019B0, 0x342E3, 0x00100, 0x008FF, 0x009FF, 0x34301,
+ 0x30339, 0x0401D, 0x0411C, 0x04AE1, 0x0FA20, 0x0AAFC, 0x35301, 0x18801,
+ 0x1A900, 0x1D800, 0x1F910, 0x2C8EA, 0x2C9EB, 0x2A000, 0x0442C, 0x0C000,
+ 0x3530D, 0x04325, 0x01430, 0x20306, 0x19430, 0x19040, 0x1A100, 0x043C3,
+ 0x0A310, 0x35717, 0x04344, 0x0A302, 0x35317, 0x0031A, 0x34318, 0x0031E,
+ 0x1D030, 0x1E100, 0x04AC1, 0x0AA80, 0x3571A, 0x04ADB, 0x190A0, 0x1A100,
+ 0x12180, 0x35325, 0x00002, 0x00100, 0x34327, 0x18002, 0x1A100, 0x2C0D0,
+ 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000, 0x00506, 0x00400,
+ 0x05100, 0x05320, 0x0F130, 0x0D410, 0x18201, 0x18001, 0x1C501, 0x35730,
+ 0x2A000, 0x04AC1, 0x0AA80, 0x35339, 0x04AC1, 0x0AA80, 0x3573C, 0x2A000,
+ 0x30339, 0x043D8, 0x044D9, 0x18301, 0x1A400, 0x1D310, 0x1F420, 0x2BC00,
+ 0x2C1D8, 0x2C2D9, 0x2A000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x2C0CF, 0x34000, 0x00000
+};
+
+
+const int piper_macassist_data_len = (sizeof piper_wifi_macassist_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.h b/drivers/net/wireless/digiPiper/piperMacAssist.h
new file mode 100644
index 000000000000..27b2b77fb493
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.h
@@ -0,0 +1,9 @@
+
+#ifndef PIPER_MACASSIST_H_
+#define PIPER_MACASSIST_H_
+
+extern const unsigned long piper_wifi_macassist_ucode[];
+extern const int piper_macassist_data_len;
+
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/pipermain.h b/drivers/net/wireless/digiPiper/pipermain.h
new file mode 100644
index 000000000000..dba31309e081
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/pipermain.h
@@ -0,0 +1,381 @@
+#ifndef __PIPER_H_
+#define __PIPER_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/i2c.h>
+#include "mac.h"
+
+
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+#define digi_dbg(fmt, arg...) \
+ printk(KERN_ERR PIPER_DRIVER_NAME ": %s - " fmt, __func__, ##arg)
+#else
+#define digi_dbg(fmt, arg...) \
+ do { } while (0)
+#endif
+
+#define ERROR(x) printk(KERN_ALERT x)
+
+/* Debug levels */
+#define DSILENT 0xffff
+#define DERROR 0xfff0
+#define DWARNING 0xffe0
+#define DNORMAL 0xffd0
+#define DVERBOSE 0xffc0
+#define DVVERBOSE 0xffb0
+#define DALL 0xffa0
+
+
+#define PIPER_DRIVER_NAME "piper"
+#define DRV_VERS "0.1"
+
+/* Useful defines for AES */
+#define PIPER_EXTIV_SIZE 8 /* IV and extended IV size */
+#define MIC_SIZE 8 /* Message integrity check size */
+#define ICV_SIZE 4
+#define DATA_SIZE 28 /* Data frame header+FCS size */
+#define CCMP_SIZE (PIPER_EXTIV_SIZE + MIC_SIZE) /* Total CCMP size */
+#define EXPANDED_KEY_LENGTH (176) /* length of expanded AES key */
+#define PIPER_MAX_KEYS (4)
+#define AES_BLOB_LENGTH (48) /* length of AES IV and headers */
+
+/* Calibration constants */
+#define WCD_MAGIC "WCALDATA"
+#define WCD_MAX_CAL_POINTS (8)
+#define WCD_CHANNELS_BG (14)
+#define WCD_CHANNELS_A (35)
+#define WCD_B_CURVE_INDEX (0)
+#define WCD_G_CURVE_INDEX (1)
+
+/*
+ * Set this #define to receive frames in the ISR. This may improve
+ * performance under heavy load at the expense of interrupt latency.
+ */
+#define WANT_TO_RECEIVE_FRAMES_IN_ISR (0)
+
+
+typedef u64 u48;
+
+/*
+ * This enum lists the possible LED states.
+ */
+enum led_states {
+ led_shutdown,
+ led_adhoc,
+ led_not_associated,
+ led_associated
+};
+
+/* Available leds */
+enum wireless_led {
+ STATUS_LED,
+ ACTIVITY_LED,
+};
+
+#define WCD_HW_REV_MASK 0xf000
+#define WCD_HW_REV_PROTOTYPE 0x0000
+#define WCD_HW_REV_PILOT 0x1000
+#define WCD_HW_REV_A 0x2000
+#define WCD_PLATFORM_MASK 0x0ff0
+#define WCD_CCW9P_PLATFORM 0x0010
+#define WCD_CCW9M_PLATFORM 0x0020
+
+
+typedef struct nv_wcd_header {
+ char magic_string[8]; /* WCALDATA */
+ char ver_major; /* Major version in ascii */
+ char ver_minor; /* Minor version in ascii */
+ u16 hw_platform; /* Hardware Platform used for calibration */
+ u8 numcalpoints; /* Number of points per curve */
+ u8 padding[107]; /* Reserved for future use */
+ u32 wcd_len; /* Total length of the data section */
+ u32 wcd_crc; /* Data section crc32 */
+} nv_wcd_header_t;
+
+typedef struct wcd_point {
+ s16 out_power; /* Output Power */
+ u16 adc_val; /* Measured ADC val */
+ u8 power_index; /* Airoha Power Index */
+ u8 reserved[3]; /* For future use */
+} wcd_point_t;
+
+typedef struct wcd_curve {
+ u8 max_adc_value; /* maximum allowed ADC value for this curve */
+ u8 reserved[3]; /* Resered for future use */
+ /* Calibration curve points */
+ wcd_point_t points[WCD_MAX_CAL_POINTS];
+} wcd_curve_t;
+
+typedef struct wcd_data {
+ nv_wcd_header_t header;
+ wcd_curve_t cal_curves_bg[WCD_CHANNELS_BG][2];
+ wcd_curve_t cal_curves_a[WCD_CHANNELS_A];
+} wcd_data_t;
+
+typedef enum {
+ op_write,
+ op_or,
+ op_and
+} reg_op_t;
+
+enum antenna_select {
+ ANTENNA_BOTH = 0,
+ ANTENNA_1,
+ ANTENNA_2,
+};
+
+typedef struct {
+ bool loaded;
+ bool enabled;
+ bool weSentLastOne;
+} piperBeaconInfo_t;
+
+typedef enum {
+ RECEIVED_ACK,
+ TX_COMPLETE,
+ OUT_OF_RETRIES,
+ TX_NOT_DONE
+} tx_result_t;
+
+
+/* Structure that holds the information we need to support H/W AES encryption */
+struct piperKeyInfo {
+ bool valid; /* indicates if this record is valid */
+ u8 addr[ETH_ALEN]; /* MAC address associated with key */
+ u32 expandedKey[EXPANDED_KEY_LENGTH / sizeof(u32)];
+ u48 txPn; /* packet number for transmit */
+ u48 rxPn; /* expected receive packet number */
+};
+
+/* rf */
+struct digi_rf_ops {
+ const char *name;
+ void (*init) (struct ieee80211_hw *, int);
+ int (*stop) (struct ieee80211_hw *);
+ int (*set_chan) (struct ieee80211_hw *, int chan);
+ int (*set_chan_no_rx) (struct ieee80211_hw *, int chan);
+ int (*set_pwr) (struct ieee80211_hw *, uint8_t val);
+ void (*set_pwr_index) (struct ieee80211_hw *, unsigned int val);
+ void (*power_on) (struct ieee80211_hw *, bool want_power_on);
+ void (*getOfdmBrs) (int channel, u64 brsBitMask, unsigned int *ofdm,
+ unsigned int *psk);
+ enum ieee80211_band (*getBand) (int);
+ int (*getFrequency) (int);
+ void (*set_hw_info)(struct ieee80211_hw *, int channel, u16 hw_platform);
+ const struct ieee80211_rate *(*getRate) (unsigned int);
+ int channelChangeTime;
+ s8 maxSignal;
+ struct ieee80211_supported_band *bands;
+ u8 n_bands;
+};
+
+struct piper_stats {
+ u32 rx_overruns;
+ u32 tx_complete_count;
+ u32 tx_start_count;
+ u32 tx_total_tetries;
+ u32 tx_retry_count[IEEE80211_TX_MAX_RATES];
+ u32 tx_retry_index;
+ struct ieee80211_tx_queue_stats tx_queue;
+ struct ieee80211_low_level_stats ll_stats;
+ spinlock_t lock;
+};
+
+enum piper_ps_mode {
+ PS_MODE_LOW_POWER,
+ PS_MODE_FULL_POWER
+};
+
+enum piper_ps_state {
+ PS_STATE_WAIT_FOR_BEACON,
+ PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE,
+ PS_STATE_WAIT_FOR_WAKEUP_ALARM,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT
+};
+
+struct piper_priv;
+
+struct piper_ps {
+ u32 beacon_int;
+ u16 aid;
+ volatile unsigned int scan_timer;
+ unsigned int sleep_time;
+ struct timer_list timer;
+ enum piper_ps_mode mode;
+ enum piper_ps_state state;
+ unsigned int this_event;
+ spinlock_t lock;
+ volatile bool power_management;
+ volatile bool poweredDown;
+ volatile bool rxTaskletRunning;
+ volatile bool allowTransmits;
+ volatile bool stopped_tx_queues;
+ volatile unsigned int frames_pending;
+};
+
+typedef void (*tx_skb_return_cb_t)(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
+struct piper_queue {
+ struct sk_buff *skb;
+ tx_skb_return_cb_t skb_return_cb;
+};
+
+#define PIPER_TX_QUEUE_SIZE (16)
+#define NEXT_TX_QUEUE_INDEX(x) ((x+1) & 0xf)
+
+struct piper_priv {
+ const char *drv_name;
+ char debug_cmd[32];
+ u32 version;
+ struct piper_pdata *pdata;
+ struct ieee80211_hw *hw;
+ struct ieee80211_key_conf txKeyInfo;
+ struct ieee80211_cts ctsFrame;
+ struct ieee80211_rts rtsFrame;
+ struct ieee80211_rate *calibrationTxRate;
+ struct piper_stats pstats;
+ struct tasklet_struct rx_tasklet;
+ struct tasklet_struct tx_tasklet;
+ spinlock_t tx_tasklet_lock;
+ bool tx_tasklet_running;
+ spinlock_t tx_queue_lock;
+ struct piper_queue tx_queue[PIPER_TX_QUEUE_SIZE];
+ unsigned int tx_queue_head;
+ unsigned int tx_queue_tail;
+ unsigned int tx_queue_count;
+ bool expectingAck;
+ struct timer_list tx_timer;
+ struct timer_list led_timer;
+ enum led_states led_state;
+ struct piper_ps ps;
+ bool power_save_was_on_when_suspended;
+ struct access_ops *ac;
+ spinlock_t aesLock;
+ struct digi_rf_ops *rf;
+ void *__iomem vbase;
+ int irq;
+ tx_result_t tx_result;
+ int tx_signal_strength;
+
+ /* Function callbacks */
+ int (*init_hw) (struct piper_priv *, enum ieee80211_band);
+ int (*deinit_hw) (struct piper_priv *);
+ void (*set_irq_mask_bit) (struct piper_priv *, u32);
+ void (*clear_irq_mask_bit) (struct piper_priv *, u32);
+ u16(*get_next_beacon_backoff) (void);
+ int (*load_beacon) (struct piper_priv *, u8 *, u32);
+ int (*rand) (void);
+ void (*tx_calib_cb) (struct piper_priv *);
+ int (*set_antenna) (struct piper_priv *, enum antenna_select);
+ int (*set_tracking_constant) (struct piper_priv * piperp,
+ unsigned megahertz);
+ void (*adjust_max_agc) (struct piper_priv * piperp, unsigned int rssi,
+ _80211HeaderType * header);
+
+ /* General settings */
+ enum nl80211_iftype if_type;
+ bool areWeAssociated;
+ bool is_radio_on;
+ bool use_short_preamble;
+ int channel;
+ int tx_power;
+ u8 bssid[ETH_ALEN];
+ bool tx_cts;
+ bool tx_rts;
+ enum antenna_select antenna;
+ int power_duty;
+
+ /* AES stuff */
+ bool use_hw_aes;
+ u32 aes_key_count;
+ struct piperKeyInfo key[PIPER_MAX_KEYS];
+ u32 tx_aes_key;
+ u32 tx_aes_blob[AES_BLOB_LENGTH / sizeof(u32)];
+
+ /* IBSS */
+ piperBeaconInfo_t beacon;
+};
+
+struct access_ops {
+ spinlock_t reg_lock;
+ int (*wr_reg) (struct piper_priv *, u8 reg, u32 val, reg_op_t op);
+ u32(*rd_reg) (struct piper_priv *, u8 reg);
+ int (*wr_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+ int (*rd_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+};
+
+struct piper_pdata {
+ u8 macaddr[6];
+ int rst_gpio;
+ int irq_gpio;
+ int status_led_gpio;
+ int rf_transceiver;
+ wcd_data_t wcd;
+ int i2c_adapter_num;
+ struct piper_priv *piperp;
+
+ /* Platform callbacks */
+ void (*reset) (struct piper_priv *, int);
+ int (*init) (struct piper_priv *);
+ int (*late_init) (struct piper_priv *);
+ void (*set_led) (struct piper_priv *, enum wireless_led, int);
+ void (*early_resume) (struct piper_priv *);
+};
+
+/* main */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz);
+void piper_free_hw(struct piper_priv *priv);
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf);
+void piper_unregister_hw(struct piper_priv *priv);
+irqreturn_t piper_irq_handler(int irq, void *dev_id);
+void packet_tx_done(struct piper_priv *piperp,
+ tx_result_t result, int singalstrength);
+void piper_rx_tasklet(unsigned long context);
+void piper_tx_tasklet(unsigned long context);
+bool piper_prepare_aes_datablob(struct piper_priv *digi,
+ unsigned int keyIndex, u8 * aesBlob,
+ u8 * frame, u32 length, bool isTransmit);
+void piper_load_mac_firmware(struct piper_priv *piperp);
+void piper_load_dsp_firmware(struct piper_priv *piperp);
+int piper_spike_suppression(struct piper_priv *piperp, bool retry);
+void piper_reset_mac(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t fn);
+void piper_empty_tx_queue(struct piper_priv *piperp);
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb);
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp);
+bool piper_tx_queue_half_full(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression);
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force);
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool isPowerSaveOn);
+void piper_ps_rx_task_exiting(struct piper_priv *piperp);
+void piper_ps_scan_event(struct piper_priv *piperp);
+
+/*
+ * Defines for debugging function dumpRegisters
+ */
+#define MAIN_REGS (1)
+#define MAC_REGS (2)
+#define RF_REGS (4)
+#define FRAME_BUFFER_REGS (8)
+#define CTRL_STATUS_REGS (0x10)
+#define FIFO_REGS (0x20)
+#define IRQ_REGS (0x40)
+#define ALL_REGS (0xf)
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs);
+void digiWifiDumpSkb(struct sk_buff *skb);
+
+extern void digiWifiDumpWordsAdd(unsigned int word);
+extern void digiWifiDumpWordsDump(void);
+extern void digiWifiDumpWordsReset(void);
+
+#endif /* __PIPER_H_ */
diff --git a/drivers/net/wireless/digi_wi_g.c b/drivers/net/wireless/digi_wi_g.c
new file mode 100644
index 000000000000..af9dd8eb4694
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g.c
@@ -0,0 +1,5021 @@
+/*
+ * wifi/digi_wi_g.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version2 as published by
+ * the Free Software Foundation.
+*/
+/*
+ * !Revision: $Revision: 1.147 $
+ * !Author: Bernd Westermann/Markus Pietrek/Miriam Ruiz
+ * !Descr:
+ * !References: [1] 802.11G_Programming_Guide_1.0
+ * [2] /net/m/ISO_STANDARDS/802.11g-2003.pdf
+ * [3] Wireless LANs: 802.11 WLAN Technologie und praktische
+ * Umsetzung im Detail
+*/
+
+#include <linux/module.h>
+#include <linux/platform_device.h> /* platform_get */
+#include <linux/init.h> /* __init */
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <linux/crc32.h>
+#include <linux/vmalloc.h> /* vmalloc */
+#include <linux/random.h> /* get_random_bytes */
+#include <linux/workqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/irq.h> /* NO_IRQ */
+#include <mach/regs-mem.h>
+#include <mach/regs-sys-common.h>
+
+/* for supported_rates */
+#include <linux/../../net/ieee80211/softmac/ieee80211softmac_priv.h>
+#undef assert
+
+#include "digi_wi_g.h"
+
+#define REMOVE_ME
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+# define DBG_INIT 0x0001
+# define DBG_MINOR 0x0002
+# define DBG_RX 0x0004
+# define DBG_TX 0x0008
+# define DBG_INT 0x0010
+# define DBG_INTERFACE 0x0020
+# define DBG_LINK 0x0040
+# define DBG_SECURITY 0x0080
+# define DBG_UPDATE_RATE 0x0100
+# define DBG_TIMEOUT 0x0200
+# define DBG_TIME 0x0800
+# define DBG_MAJOR 0x1000
+# define DBG_HW_IO 0x2000
+# define DBG_ERROR 0x4000
+# define DBG_ERROR_CRIT 0x8000
+
+static unsigned int dw_dbg_level = DBG_ERROR_CRIT;
+
+# define DBG(flag, format, ...) \
+ do { \
+ if ((dw_dbg_level & (flag)) == (flag)) \
+ printk(KERN_INFO format "\n", ##__VA_ARGS__); \
+ } while (0)
+# define DBG_FN(flag) \
+ do { \
+ if ((dw_dbg_level & (flag)) == (flag)) \
+ printk(KERN_INFO DRIVER_NAME "@%s:line %d\n", __func__, __LINE__); \
+ } while (0)
+# define DBG_EXEC(expr) \
+ do { \
+ expr; \
+ } while (0)
+
+# define ASSERT(expr) \
+ do { \
+ if (!(expr)) { \
+ ERROR("Assertion failed! line %d %s", \
+ __LINE__,#expr); \
+ } \
+ } while (0)
+# define REQUIRE_LOCKED_ANY(lock) \
+ do { \
+ if (unlikely(!spin_is_locked(&lock))) { \
+ ERROR(#lock " should be locked\n"); \
+ dump_stack(); \
+ } \
+ } while (0)
+
+# define REQUIRE_UNLOCKED_ANY(lock) \
+ do { \
+ if (unlikely(spin_is_locked(&lock))) { \
+ ERROR(#lock " should be unlocked\n"); \
+ dump_stack(); \
+ } \
+ } while (0)
+
+# define REQUIRE_LOCKED(priv) REQUIRE_LOCKED_ANY(priv->lock)
+# define REQUIRE_UNLOCKED(priv) REQUIRE_UNLOCKED_ANY(priv->lock)
+
+/* for timing collection */
+enum {
+ INT = 0,
+ INT_DONE,
+ INT_TASKLET,
+ INT_TASKLET_DONE,
+ RX_FIFO,
+ RX_AES_FIFO,
+ TX_FIFO,
+ TX_AES_FIFO,
+ RX_TASKLET,
+ RX_TASKLET_DONE,
+ RX_FRAME_TO_STACK,
+ RX_FRAME_TO_STACK_DONE,
+ RX_PROCESS_FRAME,
+ RX_PROCESS_FRAME_RX,
+ RX_PROCESS_FRAME_DONE,
+ RX_DECRYPT,
+ RX_DECRYPT_DONE,
+ RX_OVERRUN,
+ RX_WORK,
+ RX_WORK_DONE,
+ RX_PAUSE,
+ START_XMIT,
+ START_XMIT_DONE,
+ TX_TASKLET,
+ TX_TASKLET_DONE,
+ TX_SEND,
+ INTERMEDIATE,
+ INTERMEDIATE2,
+ SET_CHANNEL,
+};
+
+# define NAME(x) [x] = #x
+
+static const char* ns_hperf_type_names[] = {
+ NAME(INT),
+ NAME(INT_DONE),
+ NAME(INT_TASKLET),
+ NAME(INT_TASKLET_DONE),
+ NAME(RX_FIFO),
+ NAME(RX_AES_FIFO),
+ NAME(TX_FIFO),
+ NAME(TX_AES_FIFO),
+ NAME(RX_TASKLET),
+ NAME(RX_TASKLET_DONE),
+ NAME(RX_FRAME_TO_STACK),
+ NAME(RX_FRAME_TO_STACK_DONE),
+ NAME(RX_PROCESS_FRAME),
+ NAME(RX_PROCESS_FRAME_RX),
+ NAME(RX_PROCESS_FRAME_DONE),
+ NAME(RX_DECRYPT),
+ NAME(RX_DECRYPT_DONE),
+ NAME(RX_OVERRUN),
+ NAME(RX_WORK),
+ NAME(RX_WORK_DONE),
+ NAME(RX_PAUSE),
+ NAME(START_XMIT),
+ NAME(START_XMIT_DONE),
+ NAME(TX_TASKLET),
+ NAME(TX_TASKLET_DONE),
+ NAME(TX_SEND),
+ NAME(INTERMEDIATE),
+ NAME(INTERMEDIATE2),
+ NAME(SET_CHANNEL),
+};
+
+#define NS_HPERF_SIZE 2048
+
+/* ns_hperf_type_names needs to be define first before including */
+//# include <asm/arch-ns9xxx/ns9xxx_hperf.h>
+
+/* only in debug case we are interested in revision */
+# define REVISION " $Revision: 1.147 $"
+#else /* CONFIG_DIGI_WI_G_DEBUG */
+# define DBG(flag, format, ...) do {} while (0)
+# define DBG_FN(flag) do {} while (0)
+# define DBG_EXEC(flag) do {} while (0)
+# define ASSERT(expr) do {} while (0)
+# define REQUIRE_LOCKED(lock) do {} while (0)
+# define REQUIRE_LOCKED_ANY(lock) do {} while (0)
+# define REQUIRE_UNLOCKED(lock) do {} while (0)
+# define REQUIRE_UNLOCKED_ANY(lock) do {} while (0)
+# define REVISION ""
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#define CLEAR(x) memset(&(x), 0, sizeof((x)))
+#define dw_iosetbits32(offs, mask) dw_iowrite32(dw_ioread32(offs)|(mask),(offs))
+#define dw_iocleanbits32(offs, mask) dw_iowrite32(dw_ioread32(offs)&~(mask),(offs))
+
+#define BASIC_RATE_MASK(x) ((x) & ((unsigned char) ~IEEE80211_BASIC_RATE_MASK))
+#define BASIC_RATE(x) ((x) | IEEE80211_BASIC_RATE_MASK)
+
+#define ERROR(format, ...) printk(KERN_ERR "*** ERROR " DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define ERRORL(format, ...) printkl(KERN_ERR "*** ERROR " DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define WARNL(format, ...) printkl(KERN_INFO DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define to_dev(pdev) platform_get_drvdata(pdev)
+
+#define rate_is_enough(priv) ((priv)->rate.tx_data_any >= 4)
+/* at least 3/4 are acknowledged */
+#define rate_is_success(priv) \
+ (4 * (priv)->rate.tx_data_ack > 3 * (priv)->rate.tx_data_any)
+/* less than 1/4 are not acknowledged */
+#define rate_is_failure(priv) \
+ (4 * (priv)->rate.tx_data_ack < (priv)->rate.tx_data_any)
+
+#define MAC_GROUP 0x01 /* broadcast or multicast address */
+#define IS_GROUP_ADDR(addr) (addr[ 0 ] & MAC_GROUP)
+
+#define IS_FRAME(fc, ftype, stype) \
+ (((fc) & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == (ftype | stype))
+#define IS_DATA(fc) IS_FRAME(fc, IEEE80211_FTYPE_DATA, IEEE80211_STYPE_DATA)
+#define IS_MGMT(fc) IS_FRAME(fc, IEEE80211_FTYPE_MGMT, 0 /* any */)
+
+
+#define ACK_SIZE 14 /* ACK frame size */
+
+#define USE_SHORTPRE(fi, rate) \
+ (((rate) != IEEE80211_CCK_RATE_1MB) && fi->use_short_preamble)
+/* from Net+OS: mac_rate.c */
+/* not including SIFS and PLCP preamble/header */
+#define LENGTH(bytes, rate) ((16 * (bytes) + (rate) - 1) / (rate))
+/* Length (in usecs) of SIFS and PLCP preamble/header. */
+#define PRE_LEN( fi, rate) (USE_SHORTPRE(fi, rate) ? 106 : 202)
+/* Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+ * including SIFS and PLCP preamble/header */
+#define OFDM_DUR(bytes, rate) (36 + 4 * ((4 * (bytes) + (rate) + 10) / (rate)))
+
+typedef struct {
+ u8 bps; /* bit rate in 500kbps units */
+ u8 ofdm_code; /* ofdm rate code, 0 if not ofdm */
+ u16 ack_len; /* duration of ack or cts in usecs */
+} rate_info_t;
+
+typedef int dw_rate_index_t;
+
+/* separate maintained static because of polling performance */
+static void* __iomem vbase = NULL;
+
+static void dw_cw_set(const dw_priv_t* priv, u16 fc);
+static void dw_rx_fifo_error(dw_priv_t* priv);
+static int dw_rx_frame_get_length(const dw_frame_rx_t* frame);
+static int dw_rx_frame_is_duplicate(dw_priv_t* priv,
+ const struct ieee80211_hdr_3addr* hdr);
+static void dw_rx_frame_give_to_stack(dw_priv_t* priv, dw_frame_rx_t* frame);
+static void dw_rx_frame_fetch(dw_priv_t* priv);
+static void dw_rx_tasklet_handler(unsigned long data);
+static void dw_tx_set_plcp(dw_frame_tx_info_t* fi, int fragment, int rate);
+static int dw_tx_frame_prepare(dw_priv_t* priv,
+ dw_frame_tx_info_t* fi, struct ieee80211_txb* txb);
+static void dw_tx_fragment_send(dw_priv_t* priv, dw_frame_tx_t* frame);
+static void dw_tx_tasklet_handler(unsigned long data);
+static irqreturn_t dw_int(int irq, void *dev_id);
+static void dw_tx_reset(dw_priv_t* priv);
+static void dw_tx_wait_for_idle_and_pause(dw_priv_t* priv);
+static void dw_tx_continue_queue(dw_priv_t* priv);
+static int dw_ieee80211_hard_start_xmit(struct ieee80211_txb* txb,
+ struct net_device* dev, int priority);
+static void dw_update_status_led(dw_priv_t* priv);
+static void dw_rate_reset(dw_priv_t* priv);
+static void dw_rate_update(dw_priv_t* priv);
+static void dw_management_timer(u_long a);
+
+/* initialization stuff */
+static int __init dw_hw_init_card(struct net_device* dev);
+static int __init dw_start_dev(struct platform_device* pdev);
+static int __init dw_probe( struct platform_device* pdev);
+static void dw_stop_dev(struct platform_device* pdev);
+static int dw_remove(struct platform_device* pdev);
+static void dw_release_device(struct device* dev);
+
+static void dw_set_channel(dw_priv_t*priv, u8 channel);
+
+/* softmac/ieee interface stuff */
+static void dw_softmac_txrates_change(struct net_device* dev, u32 changes);
+static int dw_wx_set_encode(struct net_device* dev,
+ struct iw_request_info* info,
+ union iwreq_data* data, char* extra);
+static int dw_wx_set_encodeext(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra);
+static void dw_softmac_notify_authenticated(
+ struct net_device* dev, int event_type, void* context);
+static void dw_softmac_set_chan(struct net_device* dev, u8 channel);
+static void dw_softmac_set_bssid_filter(
+ struct net_device *dev, const u8* bssid);
+static void dw_ieee80211_set_security(struct net_device* dev,
+ struct ieee80211_security* sec);
+static int dw_geo_init(dw_priv_t* priv);
+static int dw_open(struct net_device* dev);
+static int dw_close(struct net_device* dev);
+static void dw_set_multicast_list(struct net_device* dev);
+
+/* ********** Local Variables ********** */
+
+static const char* dw_version =
+ "WiFi: " DRIVER_NAME " driver " COMPILE_TIME REVISION;
+
+/* define the resources the driver will use */
+static struct resource dw_mem = {
+ .name = DRIVER_NAME,
+ .start = MAC_BASE_PHYS,
+ .end = MAC_BASE_PHYS + MAC_BASE_SIZE,
+ .flags = IORESOURCE_MEM,
+};
+
+/* describes the device */
+static struct platform_device dw_device = {
+ .id = -1,
+ .name = DRIVER_NAME,/* must be equal to platform-driver.driver.name*/
+ .resource = &dw_mem,
+ .dev = {
+ .release = dw_release_device,
+ },
+};
+
+/* describes the driver */
+static struct platform_driver dw_driver = {
+ .probe = dw_probe,
+ .remove = dw_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* RF transceiver frequency divider for each channel */
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+static const struct {
+ u16 integer;
+ u16 fraction;
+} freq_table[] = {
+ { 0, 0 },
+ { 0x6847, 0x0999 }, /* 1 (2412 MHz) */
+ { 0x6847, 0x099b }, /* 2 (2417 MHz) */
+ { 0x6867, 0x0998 }, /* 3 */
+ { 0x6867, 0x099a }, /* 4 */
+ { 0x6867, 0x0999 }, /* 5 */
+ { 0x6867, 0x099b }, /* 6 */
+ { 0x6857, 0x0998 }, /* 7 */
+ { 0x6857, 0x099a }, /* 8 */
+ { 0x6857, 0x0999 }, /* 9 */
+ { 0x6857, 0x099b }, /* 10 */
+ { 0x6877, 0x0998 }, /* 11 */
+ { 0x6877, 0x099a }, /* 12 */
+ { 0x6877, 0x0999 }, /* 13 (2472 MHz) */
+ { 0x684f, 0x0ccc }, /* 14 (2484 MHz) */
+};
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+static const struct {
+ u16 integer;
+ u16 fraction;
+} freq_table[] = {
+ { 0, 0 },
+ { 0x04c7, 0x0999 }, /* 1 (2412 MHz) */
+ { 0x04c7, 0x099b }, /* 2 (2417 MHz) */
+ { 0x04e7, 0x0998 }, /* 3 */
+ { 0x04e7, 0x099a }, /* 4 */
+ { 0x04e7, 0x0999 }, /* 5 */
+ { 0x04e7, 0x099b }, /* 6 */
+ { 0x04d7, 0x0998 }, /* 7 */
+ { 0x04d7, 0x099a }, /* 8 */
+ { 0x04d7, 0x0999 }, /* 9 */
+ { 0x04d7, 0x099b }, /* 10 */
+ { 0x04f7, 0x0998 }, /* 11 */
+ { 0x04f7, 0x099a }, /* 12 */
+ { 0x04f7, 0x0999 }, /* 13 (2472 MHz) */
+ { 0x04cf, 0x0ccc }, /* 14 (2484 MHz) */
+};
+#endif
+/* see [2] 10.4.42. */
+static const u8 dw_rates[ RATES_SUPPORTED ] = {
+ BASIC_RATE(IEEE80211_CCK_RATE_1MB),
+ BASIC_RATE(IEEE80211_CCK_RATE_2MB),
+ BASIC_RATE(IEEE80211_CCK_RATE_5MB),
+ /* we need to give 11MB also to AP, as otherwise we are not
+ * authenticated or softmac ignores it because rates don't match */
+ BASIC_RATE(IEEE80211_CCK_RATE_11MB),
+ IEEE80211_OFDM_RATE_6MB,
+ IEEE80211_OFDM_RATE_9MB,
+ IEEE80211_OFDM_RATE_12MB,
+ IEEE80211_OFDM_RATE_18MB,
+ IEEE80211_OFDM_RATE_24MB,
+ IEEE80211_OFDM_RATE_36MB,
+ IEEE80211_OFDM_RATE_48MB,
+ IEEE80211_OFDM_RATE_54MB,
+};
+
+/* basic rate will be calculated */
+#define MK_CCK(rate, ofdm) \
+ { .bps = rate, .ofdm_code = ofdm, .ack_len = LENGTH(ACK_SIZE, rate) }
+#define MK_OFDM(rate, ofdm) \
+ { .bps = rate, .ofdm_code = ofdm, .ack_len = OFDM_DUR(ACK_SIZE, rate) }
+/* they need to be ordered in their bitrate, because softmac returns us
+ * ap_ri.rate this way, and rates_info uses the indexes. */
+static const rate_info_t rates_info[ RATES_SUPPORTED ] = {
+ MK_CCK( IEEE80211_CCK_RATE_1MB, 0 ),
+ MK_CCK( IEEE80211_CCK_RATE_2MB, 0 ),
+ MK_CCK( IEEE80211_CCK_RATE_5MB, 0 ),
+ MK_OFDM(IEEE80211_OFDM_RATE_6MB, 0xb),
+ MK_OFDM(IEEE80211_OFDM_RATE_9MB, 0xf),
+ MK_CCK( IEEE80211_CCK_RATE_11MB, 0 ),
+ MK_OFDM(IEEE80211_OFDM_RATE_12MB, 0xa),
+ MK_OFDM(IEEE80211_OFDM_RATE_18MB, 0xe),
+ MK_OFDM(IEEE80211_OFDM_RATE_24MB, 0x9),
+ MK_OFDM(IEEE80211_OFDM_RATE_36MB, 0xd),
+ MK_OFDM(IEEE80211_OFDM_RATE_48MB, 0x8),
+ MK_OFDM(IEEE80211_OFDM_RATE_54MB, 0xc),
+};
+#undef MK_OFDM
+#undef MK_CCK
+
+#ifdef CONFIG_DIGI_WI_G_HW_ENCRYPTION
+# define DW_SW_AES_DEFAULT 0
+#else
+# define DW_SW_AES_DEFAULT 1
+#endif /* CONFIG_DIGI_WI_G_HW_ENCRYPTION */
+
+static unsigned int dw_sw_aes = DW_SW_AES_DEFAULT;
+static unsigned int dw_cfg_vco = 0; /* use built-in */
+
+/* Tables for the conversion of the signal strenght to dBm */
+/* Map LNA value to gain value */
+static const unsigned char lnatable[] = {0, 0, 23, 39};
+/* Map high gain values to dBm */
+static const char gaintable[] = {-82, -84, -85, -86, -87, -89, -90, -92, -94, -98};
+
+/* ********** Local Functions ********** */
+
+/* ********** inline stuff ********** */
+
+/**
+ * dw_to_48 - converts to numbers to an 48bit number
+ */
+static inline u48 dw_to_48(u32 n1, u16 n0)
+{
+ return (((u64) n1) << 16) | n0;
+}
+
+/**
+ * dw_48_inc - increments a 48bit number, wrapping aroung on 1<<48
+ */
+static inline void dw_inc_48(u48* n)
+{
+ (*n)++;
+ *n &= ((u64) 1 << 48) - 1;
+}
+
+/**
+ * dw_ioread32 - reads from memory mapped FPGA
+ */
+static inline u32 dw_ioread32(u32 offs)
+{
+ u32 val = ioread32(vbase + offs);
+
+ DBG(DBG_HW_IO, "R %04x = %x", offs, val);
+
+ return val;
+}
+
+/**
+ * dw_write32 - writes to memory mapped FPGA
+ */
+static inline void dw_iowrite32(u32 val, u32 offs)
+{
+ DBG(DBG_HW_IO, "W %04x = %x", offs, val);
+ iowrite32(val, vbase + offs);
+}
+
+/**
+ * dw_hw_get_status - returns the FPGA's status
+ */
+static inline u32 dw_hw_get_status(const dw_priv_t* priv)
+{
+ REQUIRE_LOCKED(priv);
+
+ return dw_ioread32(HW_GEN_STATUS);
+}
+
+/**
+ * dw_hw_memcpy_to - copies memory to FPGA @ offs
+ */
+static inline void dw_hw_memcpy_to(u32 offs, const void* _src, int len)
+{
+ u32* src = (u32 *) _src;
+
+ for (; len > 0; len -= 4, offs += 4)
+ dw_iowrite32(cpu_to_be32(*src++), offs);
+}
+
+/**
+ * dw_hw_memcpy_from - copies memory from FPGA @ offs
+ */
+static inline void dw_hw_memcpy_from(void* _dst, u32 offs, int len)
+{
+ u32* dst = (u32 *) _dst;
+
+ for (; len > 0; len -= 4, offs += 4)
+ *dst++ = be32_to_cpu(dw_ioread32(offs));
+}
+
+static inline void dw_hw_write_rf(u8 addr, u32 data)
+{
+ dw_iowrite32((((u32) addr) << 20) | data, HW_SPI_DATA);
+
+ udelay(10);
+}
+
+/**
+ * dw_hw_read_fifo - read's the FIFO's and swaps data
+ *
+ * no check for underrun is performed
+ */
+static inline void dw_hw_read_fifo(void * _dst, int _len)
+{
+ u32* dst = (u32 *) _dst;
+ int len = _len;
+
+ if (dw_ioread32(HW_GEN_STATUS) & STAT_RXFE)
+ printk("Reading from an EMPTY RX FIFO\n");
+
+ for (; len >= 32; len -= 32) {
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ }
+
+ for (; len > 0; len -= 4)
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+}
+
+/**
+ * dw_hw_write_fifo - writes swapped data to FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_write_fifo(const void* _src, int _len)
+{
+ const u32* src = (const u32 *) _src;
+ int len = _len;
+
+ for (; len >= 32; len -= 32) {
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ }
+
+ for (; len > 0; len -= 4)
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+}
+
+/**
+ * dw_hw_aes_read_fifo - read's the AES FIFO's and swaps data
+ *
+ */
+static inline void dw_hw_aes_read_fifo(void* _dst, int len)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+ u32* dst = (u32 *) _dst;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_EMPTY)) {
+ *dst++ = be32_to_cpu(dw_ioread32(HW_AES_FIFO));
+ len -= 4;
+ } else {
+ /* !TODO. No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on read AES FIFO @ %i", len);
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_read_fifo_noswap - read's the AES FIFO's
+ *
+ */
+static inline void dw_hw_aes_read_fifo_noswap(void* _dst, int len)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+ u32* dst = (u32 *) _dst;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_EMPTY)) {
+ *dst++ = dw_ioread32(HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on read AES FIFO @ %i", len);
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_write_fifo - writes swapped data to AES FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_aes_write_fifo(const void* _src, int len)
+{
+ const u32* src = (const u32 *) _src;
+ int timeout = AES_BUSY_TIMEOUT;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_FULL)) {
+ dw_iowrite32(cpu_to_be32(*src++), HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on write AES FIFO");
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_write_fifo_noswap - writes to AES FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_aes_write_fifo_noswap(const void* _src, int len)
+{
+ const u32* src = (const u32 *) _src;
+ int timeout = AES_BUSY_TIMEOUT;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_FULL)) {
+ dw_iowrite32(*src++, HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on write AES FIFO");
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_wait - waits until AES is finished
+ *
+ * @return: 0 on timeout, otherwise > 1
+ */
+static inline int dw_hw_aes_wait(void)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+
+ /* !TODO: redesign it to run with interrupts enabled, then use jiffies */
+ DBG_FN(DBG_TIMEOUT);
+
+ while (timeout && (dw_ioread32(HW_RSSI_AES) & AES_BUSY)) {
+ timeout--;
+ ndelay(1);
+ }
+
+ if (!timeout)
+ ERROR("Timedout on AES");
+
+ return timeout;
+}
+
+/**
+ * dw_channel_to_freq_a - calculates frequency out of channel (for 802.11a)
+ */
+static inline int dw_channel_to_freq_a(u8 channel)
+{
+ return (5000 + (5 * channel));
+}
+
+/**
+ * dw_channel_to_freq_bg - calculates frequency out of channel (for 802.11b/g)
+ */
+static inline int dw_channel_to_freq_bg(u8 channel)
+{
+ int freq;
+
+ if (14 == channel)
+ freq = 2484;
+ else
+ freq = 2407 + (5 * channel);
+
+ return freq;
+}
+
+static inline void dw_set_led_on(int led, u8 on)
+{
+ gpio_set_value(led, !on);
+}
+
+/**
+ * dw_list_move - moves a list from src and appends it to dst
+ */
+static inline void dw_list_move(struct list_head* dst, struct list_head* src)
+{
+ struct list_head* cursor;
+ struct list_head* next;
+
+ list_for_each_safe(cursor, next, src)
+ list_move_tail(cursor, dst);
+}
+
+static inline void dw_hw_set_vco(int channel)
+{
+ u32 vco = dw_cfg_vco;
+
+ if (!vco)
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ vco = 0x46662;
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ vco = 0x3020;
+#else
+ BUG();
+#endif
+ dw_hw_write_rf(3, vco);
+}
+
+/**
+ * dw_rate_info - returns the index to rates_basic/rates_info
+ *
+ * bitrate is in 500kbps
+ */
+static inline dw_rate_index_t dw_rate_info_index(int bitrate)
+{
+ dw_rate_index_t i = 0;
+
+ while (i < ARRAY_SIZE(rates_info)) {
+ if (rates_info[ i ].bps == bitrate)
+ return i;
+ i++;
+ }
+
+ ERROR("Unsupported rate %i\n", bitrate);
+
+ return 0;
+}
+
+/**
+ * dw_rx_pause - pauses the receiver
+ *
+ * This will lead probably to FIFO overruns. In this case, the FPGA will not
+ * send the acknowledgment, so the sender will try again.
+ * And this gives the stack time to clear the queue.
+ */
+static inline void dw_rx_pause(dw_priv_t* priv, char pause)
+{
+ DBG_FN(DBG_RX | DBG_MINOR);
+ REQUIRE_LOCKED(priv);
+
+ priv->rx.pause = pause;
+
+ if (pause)
+ dw_iocleanbits32(HW_INTR_MASK, INTR_RXFIFO);
+ else
+ dw_iosetbits32(HW_INTR_MASK, INTR_RXFIFO);
+}
+
+/* ********** now the not inline stuff ********** */
+
+/***********************************************************************
+ * @Function: dw_ccmp_get_data_tx
+ * @Return:
+ * @Descr: Get AES encryption data for a frame
+ ***********************************************************************/
+static void dw_ccmp_get_data_tx(dw_priv_t *priv, struct ieee80211_hdr_3addr * hdr,
+ const dw_fragment_tx_t* frag, ccmp_data_t* data,
+ u8* extiv, int dlen)
+{
+ ccmp_key_t *key = frag->crypt.key;
+ u8 *bp;
+
+ DBG_FN(DBG_TX);
+
+ /* Increment packet number */
+ dw_inc_48(&key->tx_pn);
+
+ CLEAR(*data);
+
+ memset(extiv, 0, EXTIV_SIZE);
+
+ SET16(extiv, key->tx_pn & 0xffff);
+ extiv[3] = priv->ieee->tx_keyidx << 6 | EXT_IV;
+
+ SET32(&extiv[4], key->tx_pn >> 16);
+
+ /* Set up CCM initial block for MIC IV */
+ data->init[0] = 0x59;
+ data->init[1] = 0;
+ memcpy (&data->init[2], hdr->addr2, ETH_ALEN);
+ data->init[8] = extiv[7];
+ data->init[9] = extiv[6];
+ data->init[10] = extiv[5];
+ data->init[11] = extiv[4];
+ data->init[12] = extiv[1];
+ data->init[13] = extiv[0];
+ data->init[14] = dlen >> 8;
+ data->init[15] = dlen;
+
+ /* Set up MIC header blocks */
+ bp = (u8 *) &hdr->frame_ctl;
+
+ data->header[0] = 0;
+ data->header[1] = 22;
+ data->header[2] = bp[0] & 0xcf;
+ data->header[3] = bp[1] & 0xd7;
+ memcpy(&data->header[4], hdr->addr1, 3*ETH_ALEN);
+ data->header[22] = WLAN_GET_SEQ_FRAG(le16_to_cpu(hdr->seq_ctl));
+ data->header[23] = 0;
+ memset (&data->header[24], 0, 8);
+}
+
+/***********************************************************************
+ * @Function: dw_ccmp_get_data_rx
+ * @Return:
+ * @Descr: Get AES encryption data for a frame
+ ***********************************************************************/
+static int dw_ccmp_get_data_rx(dw_priv_t *priv,
+ const struct ieee80211_hdr_3addr * hdr,
+ int dlen, ccmp_data_t *data)
+{
+ ccmp_key_t *key;
+ u8 *bp;
+
+ DBG_FN(DBG_RX);
+
+ /* Not encrypted */
+ if (dlen < 0 || !(hdr->payload[3] & EXT_IV)) {
+ return 0;
+ }
+
+ /* Key not set */
+ key = &priv->aeskeys[hdr->payload[3] >> 6];
+ if (!key->valid) {
+ return 0;
+ }
+
+ CLEAR(*data);
+
+ /* Set up CCM initial block for MIC IV */
+ data->init[0] = 0x59;
+ data->init[1] = 0;
+ memcpy (data->init+2, hdr->addr2, ETH_ALEN);
+
+ /* extiv */
+ data->init[8] = hdr->payload[7];
+ data->init[9] = hdr->payload[6];
+ data->init[10] = hdr->payload[5];
+ data->init[11] = hdr->payload[4];
+ data->init[12] = hdr->payload[1];
+ data->init[13] = hdr->payload[0];
+ data->init[14] = dlen >> 8;
+ data->init[15] = dlen;
+
+ /* Set up MIC header blocks */
+ bp = (u8 *) &hdr->frame_ctl;
+
+ data->header[0] = 0;
+ data->header[1] = 22;
+ data->header[2] = bp[0] & 0xcf;
+ data->header[3] = bp[1] & 0xd7;
+ memcpy (data->header+4, hdr->addr1, 3*ETH_ALEN);
+ data->header[22] = WLAN_GET_SEQ_FRAG(le16_to_cpu(hdr->seq_ctl));
+ data->header[23] = 0;
+ memset (data->header+24, 0, 8);
+
+ return 1;
+}
+
+/***********************************************************************
+ * Function: dump_hex_buffer
+ * Return: nothing
+ * Descr: prints a buffer hexadecimal and with character if printable
+ ***********************************************************************/
+static void dump_hex_buffer(const void* buffer, const int len)
+{
+ const unsigned char* hexbuffer = (const unsigned char*)buffer;
+ int i;
+ const int colcount = 16;
+ const int colnum = 4;
+ const int colcut = colcount / colnum;
+
+ for (i = 0; i < len; i += colcount) {
+ /* print one row*/
+ int j, rowlen;
+
+ if (i+colcount <= len)
+ rowlen = colcount;
+ else
+ rowlen = len - i;
+
+ printk("%08X ", (int) hexbuffer);
+ printk(" ");
+
+ /* print hexadecimal representation */
+ for (j = 0; j < rowlen; j++) {
+ printk("%02X ", *(hexbuffer+j));
+ if ((j + 1) % colcut == 0)
+ /* additional separator*/
+ printk(" ");
+ }
+
+ for (j = rowlen; j < colcount; j++)
+ printk(" ");
+
+ if (rowlen != colcount)
+ for (j = 0; j <= (colcount - rowlen - 1) / colcut; j++)
+ printk(" ");
+
+ printk(" ");
+
+ /* print character representation row */
+ for (j=0; j < rowlen; j++) {
+ unsigned char c = *(hexbuffer+j);
+ if (c < 32 || c > 127)
+ c = '.';
+
+ printk("%c", c);
+ }
+ printk("\n");
+ hexbuffer += colcount;
+ }
+}
+
+/**
+ * dw_plcp_get_bitrate_cck - returns the bitrate of HW's internal rate code
+ *
+ * retval is rate_code/5. But division is 1us slower than switch (5us to 4us)
+ */
+static int dw_plcp_get_bitrate_cck(int rate_code)
+{
+ volatile int ret;
+
+ DBG_FN(DBG_RX | DBG_MINOR);
+
+ switch(rate_code) {
+ case 0x0A: ret = IEEE80211_CCK_RATE_1MB; break;
+ case 0x14: ret = IEEE80211_CCK_RATE_2MB; break;
+ case 0x37: ret = IEEE80211_CCK_RATE_5MB; break;
+ case 0x6E: ret = IEEE80211_CCK_RATE_11MB; break;
+ default:
+// ERROR("Unknown rate_code %i cck, using 1MB\n", rate_code);
+ ret = IEEE80211_CCK_RATE_1MB; break;
+ }
+
+ return ret;
+}
+
+/**
+ * dw_plcp_get_ratecode_cck - returns the HW's internal rate code from bitrate
+ *
+ * retval is 5*bitrate. But switch is 1us slower than multiplication (2us to 3us)
+ */
+static int dw_plcp_get_ratecode_cck(int bitrate)
+{
+ DBG_FN(DBG_TX | DBG_MINOR);
+
+ /* no check for unsupported bitrates, but upper layer should have handled it */
+ return 5 * bitrate;
+}
+
+/**
+ * dw_plcp_get_bitrate_ofdm - returns the bitrate of HW's internal rate code.
+ *
+ * see [1], 2.5.2
+ */
+static int dw_plcp_get_bitrate_ofdm(int rate_code)
+{
+ int ret;
+
+ DBG_FN(DBG_RX | DBG_MINOR);
+
+ switch(rate_code) {
+ case 0xB: ret = IEEE80211_OFDM_RATE_6MB; break;
+ case 0xF: ret = IEEE80211_OFDM_RATE_9MB; break;
+ case 0xA: ret = IEEE80211_OFDM_RATE_12MB; break;
+ case 0xE: ret = IEEE80211_OFDM_RATE_18MB; break;
+ case 0x9: ret = IEEE80211_OFDM_RATE_24MB; break;
+ case 0xD: ret = IEEE80211_OFDM_RATE_36MB; break;
+ case 0x8: ret = IEEE80211_OFDM_RATE_48MB; break;
+ case 0xC: ret = IEEE80211_OFDM_RATE_54MB; break;
+ default:
+ ERROR("Unknown rate_code %i ofdm, using 6MB\n", rate_code);
+ ret = IEEE80211_OFDM_RATE_6MB; break;
+ }
+
+ return ret;
+}
+
+/**
+ * dw_plcp_get_ratecode_ofdm - returns the HW's internal rate code from bitrate
+ *
+ * see [1], 2.5.2
+ */
+static int dw_plcp_get_ratecode_ofdm(int bitrate)
+{
+ DBG_FN(DBG_TX | DBG_MINOR);
+
+ return rates_info[ dw_rate_info_index(bitrate) ].ofdm_code;
+}
+
+/**
+ * dw_cw_set - updates the contention window depending on the frame type
+ */
+static void dw_cw_set(const dw_priv_t* priv, u16 fc)
+{
+ int cw;
+
+ DBG_FN(DBG_INIT);
+ REQUIRE_LOCKED(priv);
+
+ if (fc & IEEE80211_STYPE_BEACON)
+ cw = 2 * CW_MIN + 1;
+ else if (fc & IEEE80211_STYPE_PROBE_RESP)
+ cw = CW_MIN;
+ else
+ cw = priv->cw;
+
+ /* Set backoff timer */
+ if (cw) {
+ u16 rand;
+ get_random_bytes(&rand, 2);
+
+ /* Pick a random time up to cw, convert to usecs */
+ cw = 10 * (rand & cw);
+ if (fc & IEEE80211_STYPE_BEACON)
+ dw_iowrite32(cw, HW_BEACON_BO);
+ else
+ dw_iowrite32(cw, HW_BACKOFF);
+ }
+}
+
+/**
+ * RF transceiver transmitter gain for each power level.
+ * This is the 0-15 power level, bit reversed.
+ */
+static unsigned char powerTable[] = {
+ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
+};
+
+/**
+ * This is used to set the transmit power. Values can range
+ * from 0-15, where 8 is the default and I am told provides about
+ * 12dBm (16mW) output power.
+ */
+static int dw_set_tx_power(struct dw_priv *priv, unsigned char value)
+{
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ if (value > DW_TX_POWER_MAX_DBM)
+ value = DW_TX_POWER_MAX_DBM;
+
+ dw_hw_write_rf(5, 0x19e40 | powerTable[value]);
+ priv->tx_power = value;
+ return 1;
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ if (value < 0)
+ value = 0;
+ // Map max value (15) to 14 to avoid hardware problems
+ else if (value > 14)
+ value = 14;
+#ifdef _UNDEFINED_
+ dw_hw_write_rf(5, 0x09e40 | powerTable[value]);
+ priv->tx_power = value;
+ return 1;
+#else
+ dw_hw_write_rf(5, 0x09ee0);
+ priv->tx_power = 0;
+ return -EIO;
+#endif
+
+#endif // defined(CONFIG_DIGI_WI_G_UBEC_JD)
+}
+
+/**
+ * dw_rx_fifo_error - reports a fifo error and resets it
+ */
+static void dw_rx_fifo_error(dw_priv_t* priv)
+{
+ DBG_FN(DBG_RX);
+ REQUIRE_LOCKED(priv);
+ REQUIRE_LOCKED_ANY(priv->ieee->lock);
+
+ /* the reason of the error should have been reported already */
+
+ /* give a pulse */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXFIFORST);
+ wmb();
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXFIFORST);
+
+ priv->wstats.discard.misc++;
+}
+
+/**
+ * dw_rx_frame_get_length - determines the frame length from hardware values
+ *
+ * @return frame length in bytes or -1 on failure
+ */
+static int dw_rx_frame_get_length(const dw_frame_rx_t* frame)
+{
+ const dw_hw_hdr_rx_t* hdr = &frame->hdr;
+ int len = 0;
+
+ DBG_FN(DBG_RX);
+
+ if (MOD_OFDM == hdr->mod.mod_type) {
+ const dw_hw_ofdm_t* ofdm = &hdr->plcp.ofdm;
+ /* switch and no table because check for bad data from FPGA */
+ switch(ofdm->rate) {
+ case 0xB:
+ case 0xF:
+ case 0xA:
+ case 0xE:
+ case 0x9:
+ case 0xD:
+ case 0x8:
+ case 0xC:
+ break;
+ default:
+ ERROR("Wrong rate %i", ofdm->rate);
+ goto error;
+ }
+ len = ofdm->length;
+ } else {
+ const dw_hw_pskcck_t* pskcck = &hdr->plcp.pskcck;
+ int service;
+
+ /* service check */
+ service = pskcck->service;
+ service &= ~SERVICE_LOCKED;
+ service &= ~SERVICE_MODSEL;
+ service &= ~SERVICE_LENEXT;
+
+ /* Use a switch to avoid doing a divide operation.*/
+ len = pskcck->length;
+ switch(pskcck->signal) {
+ case 10:
+ len /= 8;
+ break;
+ case 20:
+ len /= 4;
+ break;
+ case 55:
+ len = (11 * len) / 16;
+ break;
+ case 110:
+ len = (11 * len) / 8;
+ if (pskcck->service & SERVICE_LENEXT)
+ len--;
+ break;
+ default:
+ ERRORL("Signal not defined %i %i", pskcck->signal,
+ pskcck->length);
+
+ /* !TODO: Remove me when solved */
+ dump_hex_buffer(hdr, sizeof(*hdr));
+ goto error;
+ }
+ }
+
+ /* check length for integrity? */
+ if (unlikely(len > HW_RX_FIFO_SIZE)) {
+ ERRORL("Wrong size %i", len);
+ goto error;
+ }
+
+ return len;
+
+error:
+ return -1;
+}
+
+/**
+ * dw_rx_frame_is_duplicate - checks whether the frame has been already
+ * received. This is not done by hardware.
+ *
+ * @return 1 if duplicate otherwise 1
+ */
+static int dw_rx_frame_is_duplicate(dw_priv_t* priv,
+ const struct ieee80211_hdr_3addr* hdr)
+{
+ struct list_head* it;
+ dw_duplicate_t* sender = NULL;
+ int is_duplicate = 0;
+
+ REQUIRE_LOCKED(priv);
+
+ /* do we had anything from that sender already? */
+ list_for_each(it, &priv->rx.dups.known.list) {
+ /* addr2 is sender */
+ dw_duplicate_t* entry = list_entry(it, dw_duplicate_t, list);
+ if (!memcmp(entry->src, hdr->addr2, ARRAY_SIZE(entry->src))) {
+ /* found sender */
+ sender = entry;
+ break;
+ }
+ } /* list_for_each */
+
+ if (NULL == sender) {
+ /* create an entry for the new sender */
+ struct list_head* new;
+
+ if (unlikely(list_empty(&priv->rx.dups.free.list)))
+ /* the last one in known list was the first
+ * added and so may possible be the oldest.
+ * Using jiffies is probably not necessary */
+ new = priv->rx.dups.known.list.prev;
+ else
+ new = priv->rx.dups.free.list.next;
+
+ /* move it to head of known entries */
+ list_del(new);
+ list_add(new, &priv->rx.dups.known.list);
+
+ sender = list_entry(new, dw_duplicate_t, list);
+
+ memcpy(sender->src, hdr->addr2, ARRAY_SIZE(sender->src));
+ } else {
+ /* did we receive the frame already? */
+ u16 fc;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ if ((fc & IEEE80211_FCTL_RETRY) &&
+ (sender->seq_ctl == hdr->seq_ctl))
+ /* we did see already the sequence number */
+ is_duplicate = 1;
+ } /* if (NULL == sender) */
+
+ if (!is_duplicate)
+ /* update sequence control field */
+ sender->seq_ctl = hdr->seq_ctl;
+
+ return is_duplicate;
+}
+
+/**
+ * dw_rx_frame_decrypt - decrypts a frame when necessary.
+ *
+ * skb->data contains undecrypted data on return
+ */
+#ifndef REMOVE_ME
+static void dw_rx_frame_decrypt(dw_priv_t* priv, struct sk_buff* skb)
+{
+ DBG_FN(DBG_RX | DBG_MINOR);
+}
+#endif
+
+/**
+ * dw_rx_frame_give_to_stack - give the kernel/user the data
+ */
+static void dw_rx_frame_give_to_stack(dw_priv_t* priv, dw_frame_rx_t* frame)
+{
+ static struct ieee80211_rx_stats stats;
+ int gain; /* For received signal strength to dBm conversion */
+
+ DBG_FN(DBG_RX | DBG_MAJOR);
+
+ /* processing needs time, but we want to be able to stil copy frames
+ * from Rx FIFO. Therefore we are unlocked. */
+ REQUIRE_UNLOCKED(priv);
+
+#ifndef REMOVE_ME
+ dw_rx_frame_decrypt(priv, frame->skb);
+#endif
+
+ /* update stats */
+ CLEAR(stats);
+ stats.mask =
+ IEEE80211_STATMASK_RSSI |
+ IEEE80211_STATMASK_SIGNAL |
+ IEEE80211_STATMASK_RATE;
+ stats.mac_time = jiffies;
+ stats.rate = ((MOD_OFDM == frame->hdr.mod.mod_type) ?
+ dw_plcp_get_bitrate_ofdm(frame->hdr.plcp.ofdm.rate) :
+ dw_plcp_get_bitrate_cck(frame->hdr.plcp.pskcck.signal));
+ stats.freq = IEEE80211_24GHZ_BAND;
+ stats.len = frame->skb->len;
+
+
+ /* Convert received signal strength to dBm */
+ gain = lnatable[frame->hdr.mod.rssi_lna] + 2 * frame->hdr.mod.rssi_vga;
+ if (gain > 96)
+ stats.signal = -98;
+ else if (gain > 86)
+ stats.signal = gaintable[gain-87];
+ else
+ stats.signal = 5 - gain;
+
+ /* RSSI is used only internally to determine best network and not reported back */
+
+ /* RSSI (Received Signal Strength Indication) is a measurement of the
+ * power present in a received radio signal. In an IEEE 802.11 system
+ * RSSI is the received signal strength in a wireless environment, in
+ * arbitrary units. RSSI measurements will vary from 0 to 255 depending
+ * on the vendor. It consists of a one byte integer value. A value of 1
+ * will indicate the minimum signal strength detectable by the wireless
+ * card, while 0 indicates no signal. The value has a maximum of
+ * RSSI_Max. - http://en.wikipedia.org/wiki/RSSI
+ *
+ * See also: http://www.ces.clemson.edu/linux/dbm-rssi.shtml
+ */
+ stats.rssi = MAC_RSSI_MAX
+ - 15 * (frame->hdr.mod.rssi_lna - 1)
+ - 2 * (frame->hdr.mod.rssi_vga);
+
+ /* we don't know it here, but ieee80211 stack will look into it with
+ our patch in ieee80211_rx.c before
+ + if (255 == stats->received_channel)
+ + stats->received_channel = network->channel;
+ memcpy(&network->stats, stats, sizeof(network->stats));
+ */
+ stats.received_channel = 255;
+
+ /* put it on stack */
+ ieee80211_rx_any(priv->ieee, frame->skb, &stats);
+
+ /* allocate next buffer */
+ frame->skb = dev_alloc_skb(DW_MTU);
+ if (unlikely(NULL == frame->skb)) {
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ panic(DRIVER_NAME ": Out of memory\n");
+ }
+}
+
+/**
+ * dw_rx_frame_fetch - Copies a frame from FIFO
+ */
+static void dw_rx_frame_fetch(dw_priv_t* priv)
+{
+ struct list_head* element;
+ dw_frame_rx_t* frame;
+ int len;
+ int ignore_frame = 0;
+ u16 fc;
+ char spy_buf[2312];
+ unsigned int spy_len = 0;
+
+ DBG_FN(DBG_RX | DBG_INT);
+ REQUIRE_LOCKED(priv);
+
+ if (unlikely(list_empty(&priv->rx.queue.free.list))) {
+ ERROR("Frame received, but queue empty. Receiver should be paused");
+ len = 0xffff; /* for DBG_MARK */
+
+ /* nowhere to store. Reset FIFO */
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_over_errors++;
+ dw_rx_fifo_error(priv);
+ spin_unlock(&priv->ieee->lock);
+
+ return;
+ }
+
+ /* use a free skb and fill it with the frame */
+ element = priv->rx.queue.free.list.next;
+ frame = list_entry(element, dw_frame_rx_t, list);
+
+ memset(spy_buf, 0, sizeof(spy_buf)); spy_len = 0;
+
+ /* copy frame header, swapped, we need it's header for length */
+ dw_hw_read_fifo(&frame->hdr, sizeof(frame->hdr));
+ memcpy(spy_buf + spy_len, &frame->hdr, sizeof(frame->hdr)); spy_len += sizeof(frame->hdr);
+
+ len = dw_rx_frame_get_length(frame);
+
+ if ((len >= sizeof(struct ieee80211_hdr)) && (len <= DW_MTU)) {
+ /* read data and if necessary decrypt it */
+ size_t remaining_len = len;
+ /* */
+ size_t delta_decrypt_header = sizeof(struct ieee80211_hdr_3addr) + EXTIV_SIZE - sizeof(struct ieee80211_hdr);
+
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr*) frame->skb->data;
+ struct ieee80211_hdr_3addr* hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ char* data = frame->skb->data;
+
+
+ /* reads data and keeps track of remaining_len */
+#define READ_FIFO(to_read) do { \
+ dw_hw_read_fifo(data, to_read); \
+ memcpy(spy_buf + spy_len, data, to_read); spy_len += to_read; \
+ data += to_read; \
+ remaining_len -= to_read; \
+ ASSERT(remaining_len >= 0); } while (0)
+
+ /* read header */
+ READ_FIFO(sizeof(struct ieee80211_hdr));
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if (!dw_sw_aes &&
+ (fc & IEEE80211_FCTL_PROTECTED) &&
+ (remaining_len > delta_decrypt_header)) {
+ /* there is a encrypted frame present */
+ ccmp_data_t cdata;
+ ccmp_key_t* key;
+ size_t data_len;
+ int index;
+ u48 pn;
+
+ /* get frame headers and init vector from rx FIFO */
+ READ_FIFO(delta_decrypt_header);
+
+ /* get key index from message */
+ index = (hdr3->payload[ 3 ] >> 6) & (WEP_KEYS - 1);
+ key = &priv->aeskeys[ index ];
+
+ /* get packet number from IV and check for replay.
+ packet number must be greated or equal than
+ expected one. Takes care of wrap around. */
+ pn = dw_to_48(GET32(&hdr3->payload[ 4 ]),
+ GET16(&hdr3->payload[ 0 ]));
+
+ data_len = remaining_len - IEEE80211_FCS_LEN - MIC_SIZE;
+
+ if (key->valid && /* we know the key */
+ (pn - key->rx_pn >= 0) &&
+ dw_ccmp_get_data_rx(priv, hdr3, data_len, &cdata)) {
+ /* payload doesn't include MIC or CCMP */
+ len -= CCMP_SIZE;
+
+ /* !TODO. Convert AES_wait into a non-busy
+ polling function */
+ /* retreive and decrypt encoded payload data */
+ dw_hw_aes_wait();
+
+ /* configure mode and key */
+ dw_iowrite32(HW_AES_MODE_1 | (index & 0xf),
+ HW_AES_MODE);
+
+ /* this read puts AES into decrypt mode */
+ dw_ioread32(HW_AES_MODE);
+
+ /* write key and init vector to AES engine */
+ dw_hw_aes_write_fifo(&cdata, sizeof(cdata));
+
+ /* get decrypted payload data.
+ * We will overvwrite the CCMP header
+ * previously read. But the stack doesn't want
+ * to see it anyway because it expects unencrypted data */
+ dw_hw_aes_read_fifo(hdr3->payload, data_len);
+
+ /* wait for MIC calculation to finish.
+ !TODO: convert to non-busy */
+ dw_hw_aes_wait();
+
+ if (dw_ioread32(HW_RSSI_AES) & AES_MIC) {
+ /* frame was ok */
+ dw_inc_48(&key->rx_pn);
+ /* the stack should not convert it */
+ hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_PROTECTED);
+ } else {
+ ignore_frame = 1;
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->ieee_stats.rx_discards_undecryptable++;
+ spin_unlock(&priv->ieee->lock);
+ ERROR("Wrong MIC");
+ }
+ } else {
+ /* TKIP decrypted? Le'ts handle it by SW */
+ READ_FIFO(remaining_len);
+ }
+#undef READ_FIFO
+ } else {
+ /* retrieve remaining unencrypted data.
+ We read FCS, but stack will ignore it. */
+ dw_hw_read_fifo(data, remaining_len);
+ memcpy(spy_buf + spy_len, data, remaining_len); spy_len += remaining_len;
+ }
+ } else {
+ if (len > DW_MTU)
+ {
+ ERROR("Oversized frame with %i bytes, ignoring it\n", len);
+
+ }
+
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_length_errors++;
+ dw_rx_fifo_error(priv);
+ spin_unlock(&priv->ieee->lock);
+
+ ignore_frame = 1;
+ }
+
+ if (!ignore_frame) {
+ /* process frame */
+
+ int skblen = len - IEEE80211_FCS_LEN;/* need no FCS further */
+ int ignore = 0;
+ int control = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL);
+
+ switch(fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_MGMT: /* no break */
+ case IEEE80211_FTYPE_DATA:
+ break;
+
+ case IEEE80211_FTYPE_CTL: /* no break */
+ default:
+ ignore = 1;
+ break;
+ }
+
+ if (!ignore &&
+ (len >= sizeof(struct ieee80211_hdr_3addr)) &&
+ dw_rx_frame_is_duplicate(priv,
+ (const struct ieee80211_hdr_3addr*) frame->skb->data))
+ ignore = 1;
+
+ if (!ignore) {
+ /* the layer ignores the above frames, so we don't
+ * need to alloc/free skb's for them. CTL are
+ * ignored anyway and not freed by layer (bug)?
+ * -> out of memory
+ *
+ * We have read the whole frame because a CTL frame is
+ * typically 10 Bytes large, the header 4 Bytes.
+ * Not much gain for reading only header, but a loose
+ * for all other frames and still some overhead of
+ * calculation */
+ skb_put(frame->skb, skblen);
+
+ if (IS_DATA(fc))
+ priv->rate.have_activity = 1;
+
+ list_move_tail(element, &priv->rx.queue.filled.list);
+
+ if (unlikely(list_empty(&priv->rx.queue.free.list)))
+ /* no room to store any longer. So, FIFO may
+ * overrun, but in this case, no 802.11
+ * acknowledgements are transmitted and the
+ * frame is repeatedly sent => less errors */
+
+ dw_rx_pause(priv, 1);
+ } else {
+ if (priv->tx.last_was_data && control &&
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ACK))
+ {
+ priv->tx.data_pending_ack = NULL;
+ priv->rate.tx_data_ack++;
+ }
+
+ } /* fc */
+ } else {
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_dropped++;
+ spin_unlock(&priv->ieee->lock);
+ }
+}
+
+/**
+ * dw_rx_tasklet_handler - Read's all frames in fifo
+ */
+static void dw_rx_tasklet_handler(unsigned long data)
+{
+ dw_priv_t* priv = (dw_priv_t*) data;
+ struct list_head frames;
+ struct list_head* cursor;
+ unsigned long flags;
+
+ DBG_FN(DBG_RX | DBG_INT);
+
+ /* move the filled list to our context, so the tasklet is able to
+ * continue mostly unlocked. */
+ INIT_LIST_HEAD(&frames);
+
+ dw_lock(priv, flags);
+ dw_list_move(&frames, &priv->rx.queue.filled.list);
+ dw_unlock(priv, flags);
+
+ list_for_each(cursor, &frames) {
+ dw_frame_rx_t* frame = list_entry(cursor, dw_frame_rx_t, list);
+ dw_rx_frame_give_to_stack(priv, frame);
+ }
+
+ /* now move the processed frame list that is now empty again back to
+ free */
+ dw_lock(priv, flags);
+ dw_list_move(&priv->rx.queue.free.list, &frames);
+ dw_rx_pause(priv, 0);
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_tx_reset - reset's the Tx FIFO when a timeout is detected
+ */
+static void dw_tx_reset(dw_priv_t* priv)
+{
+ DBG_FN(DBG_TX | DBG_ERROR);
+ REQUIRE_LOCKED(priv);
+
+ ERRORL("TX Reset and retrying transmission");
+
+ /* reset FIFO */
+
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_TXFIFORST);
+ wmb();
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_TXFIFORST);
+
+ priv->tx.data_pending_ack = NULL;
+
+ /* retransmit it */
+
+ if (likely(!list_empty(&priv->tx.queued))) {
+ dw_frame_tx_t* frame = list_entry(priv->tx.queued.prev, dw_frame_tx_t, list);
+ dw_tx_fragment_send(priv, frame);
+ } else {
+ ERROR("Tx Queue is empty, but we have a tx timeout????");
+ }
+}
+
+/**
+ * dw_tx_wait_for_idle_and_pause - on return, no frame is processed any more by
+ * Tx
+ *
+ * After the return it is safe to configure the transmitter
+ */
+static void dw_tx_wait_for_idle_and_pause(dw_priv_t* priv)
+{
+ unsigned long flags;
+ int sleep;
+
+ DBG_FN(DBG_TX);
+ REQUIRE_UNLOCKED(priv);
+
+ /* no more new transmits until dw_tx_continue_queue */
+ dw_lock(priv, flags);
+ priv->tx.pause = 1;
+ sleep = priv->tx.pending;
+ netif_stop_queue(priv->dev);
+ dw_unlock(priv, flags);
+
+ if (sleep)
+ /* wait for queue to be emptied.
+ * we mustn't be locked, because we can sleep. But that's not a
+ * problem. If the interrupt handler is faster, it will
+ * already have freed semaphore, so we run through */
+ down(&priv->tx.pause_sem);
+}
+
+static void dw_tx_continue_queue(dw_priv_t* priv)
+{
+ unsigned long flags;
+
+ DBG_FN(DBG_TX);
+ REQUIRE_UNLOCKED(priv);
+
+ dw_lock(priv, flags);
+
+ priv->tx.pause = 0;
+ netif_wake_queue(priv->dev);
+ if (!list_empty(&priv->tx.queued)) {
+ /* transmitter is paused, so nothing is pending. Send next
+ queued entry */
+ dw_frame_tx_t* frame = list_entry(priv->tx.queued.prev, dw_frame_tx_t, list);
+ dw_tx_fragment_send(priv, frame);
+ }
+
+ dw_unlock(priv, flags);
+}
+
+
+/**
+ * dw_tx_set_plcp - set's PLCP Header of the frame.
+ */
+static void dw_tx_set_plcp(dw_frame_tx_info_t* fi, int fragment, int rate)
+{
+ dw_fragment_tx_t* frag = &fi->frags[ fragment ];
+ dw_hw_hdr_tx_t* hdr = &frag->hdr;
+ size_t length = frag->phys_len;
+
+ DBG_FN(DBG_TX);
+
+ CLEAR(*hdr);
+ /* FIFO length in words of complete frame with header,
+ rounded up. FCS is added automatically */
+ hdr->mod.length = (sizeof(*hdr) + length + 3) / 4;
+
+ /* FCS length is required for signal */
+ length += IEEE80211_FCS_LEN;
+ if (!ieee80211_is_cck_rate(rate)) {
+ hdr->mod.mod_type = MOD_OFDM;
+ hdr->plcp.ofdm.rate = dw_plcp_get_ratecode_ofdm(rate);
+ hdr->plcp.ofdm.length = length;
+ hdr->plcp.ofdm.raw32 = cpu_to_le32(hdr->plcp.ofdm.raw32);
+ } else {
+ int signal = dw_plcp_get_ratecode_cck(rate);
+ hdr->mod.mod_type = MOD_PSKCCK;
+ hdr->plcp.pskcck.signal = signal;
+ hdr->plcp.pskcck.service = SERVICE_LOCKED;
+
+ /* convert length from bytes to microseconds */
+ switch(signal) {
+ case 10: length *= 8; break;
+ case 20: length *= 4; break;
+ case 55: length = (16 * length + 10) / 11; break;
+ case 110:
+ length = (8 * length + 10) / 11;
+ /* set length extension bit if needed */
+ if ((11 * length) / 8 > ( frag->phys_len + IEEE80211_FCS_LEN))
+ hdr->plcp.pskcck.service |= SERVICE_LENEXT;
+ break;
+ default:
+ ERRORL("Unsupported signal/rate %i/%i", signal,rate);
+ break;
+ }
+ hdr->plcp.pskcck.length = cpu_to_le16(length);
+ hdr->plcp.pskcck.raw32 = cpu_to_le32(hdr->plcp.pskcck.raw32);
+ }
+ hdr->mod.raw32 = cpu_to_le32(hdr->mod.raw32);
+}
+
+/**
+ * dw_tx_ack_duration -
+ *
+ * @return the duration for the acknowledgement of our data package in us
+ */
+static int dw_tx_ack_duration(
+ dw_priv_t* priv,
+ const dw_frame_tx_info_t* fi,
+ dw_rate_index_t index)
+{
+ const rate_info_t* rate_info = &rates_info[ priv->tx.basics[ index ] ];
+ /* ack/crts is sent at equal or lower basic rate */
+ int dur = rate_info->ack_len;
+
+ REQUIRE_LOCKED(priv);
+
+ /* add psk/cck preamble time */
+ if (!rate_info->ofdm_code)
+ dur += PRE_LEN(fi, rate_info->bps);
+
+ return dur;
+}
+
+/**
+ * dw_tx_duration -
+ *
+ * PLCP must be set.
+ * @return the duration for the frame in us
+ */
+static int dw_tx_duration(
+ dw_priv_t* priv,
+ const dw_frame_tx_info_t* fi,
+ int fragment,
+ int rate)
+{
+ int index = dw_rate_info_index(rate);
+ int dur;
+
+ /* frame duration */
+ if (rates_info[ index ].ofdm_code)
+ dur = OFDM_DUR(fi->frags[ fragment ].phys_len, rate);
+ else
+ dur = PRE_LEN(fi, rates_info[ index ].bps) + le16_to_cpu(fi->frags[ fragment ].hdr.plcp.pskcck.length);
+
+ return dur + dw_tx_ack_duration(priv, fi, index);
+}
+
+
+/**
+ * dw_tx_frame_prepare - prepare everything that is needed to send linux frame
+ *
+ * Determine PLCP, rate, sequence/fragment number
+ *
+ * @return 0 on failure. Frame needs to be dropped
+ */
+static int dw_tx_frame_prepare(
+ dw_priv_t* priv,
+ dw_frame_tx_info_t* fi,
+ struct ieee80211_txb* txb)
+{
+ struct ieee80211_hdr_3addr* hdr3 = NULL;
+ const struct ieee80211_hdr_1addr* hdr1 = NULL;
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr *)txb->fragments[ 0 ]->data;
+ int fc = le16_to_cpu(hdr->frame_ctl);
+ int i;
+ int rate;
+ int rate_last_fragment;
+ int rate_index;
+ int duration;
+
+ REQUIRE_LOCKED(priv);
+
+ if (txb->fragments[ 0 ]->len >= sizeof(struct ieee80211_hdr_3addr))
+ /* we need it for sequence number */
+ hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ if (txb->fragments[ 0 ]->len >= sizeof(struct ieee80211_hdr_1addr))
+ /* we need it for group addressing */
+ hdr1 = (struct ieee80211_hdr_1addr*) hdr;
+
+ CLEAR(*fi);
+ fi->txb = txb;
+ fi->is_data = IS_DATA(fc);
+
+ /* the stack sets encrypted whenenver the frame needs to be encrypted,
+ * but set's FCTL_PROTECTED only when it has encrypted it itself. This
+ * leaves CCMP for us.
+ * encrypted can also be set to 1 if we don't provide hardware. */
+ fi->use_hw_encryption = fi->txb->encrypted && !(fc & IEEE80211_FCTL_PROTECTED);
+ if (fi->use_hw_encryption) {
+ fc |= IEEE80211_FCTL_PROTECTED;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ }
+
+ /* determine bit rate for fragment */
+ spin_lock(&priv->softmac->lock);
+ rate = rate_last_fragment = priv->softmac->txrates.default_rate;
+
+ if (IS_MGMT(fc))
+ rate_last_fragment = dw_rates[ 0 ];
+ else if ((NULL != hdr3) && is_multicast_ether_addr(hdr3->addr1))
+ rate = rate_last_fragment = priv->softmac->txrates.mcast_rate;
+ else if (fc & IEEE80211_FCTL_PROTECTED)
+ /* send all but the last at broadcast_rate */
+ rate_last_fragment = priv->softmac->txrates.mcast_rate;
+ spin_unlock(&priv->softmac->lock);
+
+ /* generate sequence number, encryption and plcp */
+ for (i = 0; i < fi->txb->nr_frags; i++) {
+ dw_fragment_tx_t* frag = &fi->frags[ i ];
+ struct sk_buff* skb = txb->fragments[ i ];
+ int last_frag = (i == fi->txb->nr_frags - 1);
+ int rate_frag = BASIC_RATE_MASK(last_frag ? rate_last_fragment : rate);
+
+ if (txb->fragments[ i ]->len >= sizeof(struct ieee80211_hdr_3addr)) {
+ int fragment = (fi->txb->rts_included ? (i - 1) : i);
+ /* it holds a sequence/fragment number */
+ struct ieee80211_hdr_3addr* frag_hdr3 = (struct ieee80211_hdr_3addr*) txb->fragments[ i ]->data;
+ /* see ieee80211.h: WLAN_GET_SEQ_FRAG(seq) */
+ frag_hdr3->seq_ctl = cpu_to_le16((atomic_read(&priv->tx.seq_nr) << 4) |
+ (fragment & 0xf));
+ }
+
+ fi->use_short_preamble =
+ (rate_frag != IEEE80211_CCK_RATE_1MB) && priv->softmac->bssinfo.short_preamble;
+
+ frag->phys_len = skb->len;
+
+ /* provide encryption. Requires sequence number for MIC */
+ if (fi->use_hw_encryption) {
+ frag->crypt.key_index = priv->ieee->tx_keyidx;
+ frag->crypt.key = &priv->aeskeys[ frag->crypt.key_index ];
+
+ /* Key not set */
+ if (!frag->crypt.key->valid)
+ goto error;
+
+ /* EXTIV and MIC are automatically appended */
+ frag->phys_len += CCMP_SIZE;
+ }
+
+ /* requires fragment length with encryption */
+ dw_tx_set_plcp(fi, i, rate_frag);
+ } /* plcp */
+
+ /* add duration, for initial fragments */
+ rate_index = dw_rate_info_index(BASIC_RATE_MASK(rate));
+ for (i = 0; i < fi->txb->nr_frags - 1; i++) {
+ struct ieee80211_hdr* frag_hdr = (struct ieee80211_hdr *)txb->fragments[ i ]->data;
+
+ /* ack of this frame + data of next frame */
+ duration = dw_tx_ack_duration(priv, fi, rate_index) +
+ dw_tx_duration(priv, fi, i + 1, rate);
+
+ frag_hdr->duration_id = cpu_to_le16(duration);
+ }
+
+ /* set duration for final or last fragment */
+ if ((NULL != hdr1) && IS_GROUP_ADDR(hdr1->addr1))
+ duration = 0;
+ else
+ duration = dw_tx_ack_duration(priv, fi,
+ dw_rate_info_index(BASIC_RATE_MASK(rate_last_fragment)));
+
+ hdr->duration_id = cpu_to_le16(duration);
+
+ if (NULL != hdr3)
+ /* no need for cutting it at 0xffff, will be done on
+ assignment to seq_ctl */
+ atomic_inc(&priv->tx.seq_nr);
+
+ return 1;
+
+error:
+ return 0;
+}
+
+/**
+ * dw_tx_fragment_send - copies a fragment to FIFO and sets all registers.
+ */
+static void dw_tx_fragment_send(
+ dw_priv_t* priv,
+ dw_frame_tx_t* frame)
+{
+ const dw_fragment_tx_t* frag = &frame->s.frags[ priv->tx.fragment ];
+ const struct sk_buff* skb = frame->s.txb->fragments[ priv->tx.fragment ];
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr_3addr* hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ int hw_gen_ctrl = dw_ioread32(HW_GEN_CONTROL);
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ DBG_FN(DBG_TX);
+ REQUIRE_LOCKED(priv);
+
+ //if (priv->tx.retries) {
+ if (priv->tx.times_sent) {
+ /* Add RETRY flag, sequence number is already set */
+ fc |= IEEE80211_FCTL_RETRY;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ if (priv->cw < CW_MAX)
+ /* code starts always with 2^n-1. Therefore, we still
+ * always have 2^n-1 and never overrun CW_MAX. */
+ priv->cw = priv->cw * 2 + 1;
+
+ priv->wstats.discard.retries++;
+ } else {
+ priv->cw = CW_MIN;
+ }
+
+ dw_cw_set(priv, fc);
+
+ if (frame->s.use_short_preamble)
+ hw_gen_ctrl |= GEN_SHPRE;
+ else
+ /* may be set from a previous frame */
+ hw_gen_ctrl &= ~GEN_SHPRE;
+
+ priv->tx.last_was_data = frame->s.is_data;
+ if (frame->s.is_data) {
+ priv->rate.have_activity = 1;
+ priv->rate.tx_data_any++;
+ }
+
+ /* sent frame, either bye AES or unencrypted engine */
+ if (!frame->s.use_hw_encryption) {
+ /* Prevent transmitting until all data have been written into the tx fifo */
+ dw_iowrite32(hw_gen_ctrl | GEN_TXHOLD, HW_GEN_CONTROL);
+ dw_hw_write_fifo(&frag->hdr, sizeof(frag->hdr));
+ dw_hw_write_fifo(skb->data, skb->len);
+ dw_iowrite32(hw_gen_ctrl, HW_GEN_CONTROL);
+ } else {
+ ccmp_data_t cdata;
+ u8 extiv[ EXTIV_SIZE ];
+
+ dw_ccmp_get_data_tx(priv, hdr3, frag, &cdata, extiv, skb->len - sizeof(*hdr3));
+
+ /* frame ctl is part of data used for MIC. */
+ dw_hw_aes_wait();
+
+ /* prevent transmitting until encrypted data is ready */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_TXHOLD);
+
+ /* write MAC, Frame Header and IV FIFO */
+ dw_hw_write_fifo(&frag->hdr, sizeof(frag->hdr) );
+ dw_hw_write_fifo(hdr3, sizeof(*hdr3) );
+ dw_hw_write_fifo(&extiv, sizeof(extiv));
+
+ /* configure mode and key */
+ dw_iowrite32(HW_AES_MODE_1 | (frag->crypt.key_index & 0xf),
+ HW_AES_MODE);
+
+ /* write init block to AES FIFO */
+ dw_hw_aes_write_fifo(&cdata, sizeof(cdata));
+
+ /* start transmit */
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_TXHOLD);
+
+ /* write plaintext data to AES FIFO */
+ dw_hw_aes_write_fifo(hdr3->payload, skb->len - sizeof(*hdr3));
+ }
+ priv->dev->trans_start = jiffies;
+ priv->tx.pending = 1;
+ priv->tx.times_sent++;
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC) {
+ if (((*(char *)(skb->data)) & 0x0C) == 0x08) { /* If it is a data frame */
+ priv->tx.data_pending_ack = (void *)frag;
+ priv->tx.jiffies_pending_ack = jiffies;
+ }
+ }
+}
+
+/**
+ * dw_tx_tasklet_handler - One fragment has been completed.
+ *
+ * is only called when entries are in tx.queued list
+ */
+static void dw_tx_tasklet_handler(unsigned long data)
+{
+ dw_priv_t* priv = (dw_priv_t*) data;
+ dw_frame_tx_t* frame = NULL;
+ unsigned int retry_frame = 0;
+ unsigned long flags;
+
+ DBG_FN(DBG_TX | DBG_INT);
+
+ dw_lock(priv, flags);
+
+ /* check if there are further fragments left */
+ if (list_empty(&priv->tx.queued) && !priv->tx.timeout)
+ /* Spurious Interrupt. May happen if we slow everything down
+ * with debug messages */
+ goto out;
+
+ spin_lock(&priv->ieee->lock); /* interrupts already disabled */
+
+ frame = list_entry(priv->tx.queued.next, dw_frame_tx_t, list);
+ if (priv->tx.timeout) {
+ int max_retries =
+ ((frame->s.txb->fragments[ priv->tx.fragment ]->len >= priv->ieee->rts) ?
+ priv->long_retry_limit :
+ priv->short_retry_limit);
+
+ priv->tx.retries++;
+ priv->tx.timeout = 0;
+
+ if (priv->tx.retries <= max_retries) {
+ retry_frame = 1;
+ } else {
+ priv->tx.data_pending_ack = NULL;
+ priv->ieee->ieee_stats.tx_retry_limit_exceeded++;
+ }
+ } /* if (priv->tx.timeout) */
+
+ if (!retry_frame && priv->tx.data_pending_ack == NULL) {
+ /* send next fragment */
+ priv->tx.fragment++;
+ if (priv->tx.fragment == frame->s.txb->nr_frags) {
+ /* frame is complete. Free resources */
+ ieee80211_txb_free(frame->s.txb);
+ frame->s.txb = NULL;
+
+ /* queue entry can be reused */
+ list_move_tail(priv->tx.queued.next, &priv->tx.free);
+ if (!list_empty(&priv->tx.queued))
+ /* take next frame's first fragment */
+ frame = list_entry(priv->tx.queued.next, dw_frame_tx_t, list);
+ else
+ frame = NULL;
+
+ priv->tx.fragment = 0;
+ priv->tx.retries = 0;
+ priv->tx.times_sent = 0;
+
+ if (!priv->tx.pause &&
+ netif_queue_stopped(priv->dev))
+ /* we have space again */
+ netif_wake_queue(priv->dev);
+
+ }
+ }
+
+ if (priv->tx.data_pending_ack && time_after(jiffies, priv->tx.jiffies_pending_ack + ACK_TIMEOUT)) {
+ priv->tx.data_pending_ack = NULL;
+ }
+
+ spin_unlock(&priv->ieee->lock);
+
+ if (NULL != frame)
+ {
+ dw_tx_fragment_send(priv, frame);
+ } else {
+ /* no more frames */
+ priv->tx.pending = 0;
+ }
+
+ if (priv->tx.pause && list_empty(&priv->tx.queued))
+ /* nothing is being processed any longer.
+ Awake listener */
+ up(&priv->tx.pause_sem);
+
+out:
+ dw_unlock(priv, flags);
+}
+
+/* Supported rates info elements */
+static const u8 ratesA[] = { DW_ELEM_SUPRATES, 8, DW_RATE_BASIC+12, 18, DW_RATE_BASIC+24, 36, DW_RATE_BASIC+48, 72, 96, 108 };
+static const u8 ratesB[] = { DW_ELEM_SUPRATES, 4, DW_RATE_BASIC+2, DW_RATE_BASIC+4, 11, 22 };
+static const u8 ratesG[] = { DW_ELEM_SUPRATES, 8, DW_RATE_BASIC+2, DW_RATE_BASIC+4, 11, 22, 12, 18, 24, 36 };
+static const u8 ratesGx[] = { DW_ELEM_EXTSUPRATES, 4, 48, 72, 96, 108 };
+
+static void dw_beacon_set_plcp(dw_hw_hdr_tx_t * hdr, int rate, size_t phys_len)
+{
+ size_t length = phys_len;
+ memset(hdr, 0, sizeof(*hdr));
+
+ /**
+ * Length in words, including Frame Header and PLCP Header, and excluding FCS, rounded up
+ * FCS is added automatically
+ */
+ hdr->mod.length = (phys_len - FCS_SIZE + 3) / 4;
+
+ /* FCS length is required for signal */
+ length += IEEE80211_FCS_LEN;
+ if (!ieee80211_is_cck_rate(rate)) {
+ hdr->mod.mod_type = MOD_OFDM;
+ hdr->plcp.ofdm.rate = dw_plcp_get_ratecode_ofdm(rate);
+ hdr->plcp.ofdm.length = length;
+ hdr->plcp.ofdm.raw32 = cpu_to_le32(hdr->plcp.ofdm.raw32);
+ } else {
+ int signal = dw_plcp_get_ratecode_cck(rate);
+ hdr->mod.mod_type = MOD_PSKCCK;
+ hdr->plcp.pskcck.signal = signal;
+ hdr->plcp.pskcck.service = SERVICE_LOCKED;
+
+ /* convert length from bytes to microseconds */
+ switch(signal) {
+ case 10: length *= 8; break;
+ case 20: length *= 4; break;
+ case 55: length = (16 * length + 10) / 11; break;
+ case 110:
+ length = (8 * length + 10) / 11;
+ /* set length extension bit if needed */
+ if ((11 * length) / 8 > ( phys_len + IEEE80211_FCS_LEN))
+ hdr->plcp.pskcck.service |= SERVICE_LENEXT;
+ break;
+ default:
+ ERRORL("Unsupported signal/rate %i/%i", signal, rate);
+ break;
+ }
+ hdr->plcp.pskcck.length = cpu_to_le16(length);
+ hdr->plcp.pskcck.raw32 = cpu_to_le32(hdr->plcp.pskcck.raw32);
+ }
+ hdr->mod.raw32 = cpu_to_le32(hdr->mod.raw32);
+}
+
+/**
+ * Store supported rates elements into a buffer
+ * @param bp Pointer into buffer
+ * @param elem Element to store: DW_ELEM_SUPRATES, DW_ELEM_EXTSUPRATES, or 0 for both
+ * @param channel Channel number
+ * @return Updated buffer pointer
+ */
+static u8 *dw_beacon_set_rates(u8 *bp, int elem, int channel)
+{
+ const u8 *sr;
+
+ if (DW_CHAN_5G (channel))
+ sr = ratesA;
+ //else if (OPT_BONLY)
+ // sr = ratesB;
+ else
+ sr = ratesG;
+
+ /* Store up to 8 supported rates */
+ if (elem != DW_ELEM_EXTSUPRATES) {
+ memcpy (bp, sr, sr[1]+2);
+ bp += bp[1] + 2;
+ }
+
+ /* Store remaining extended supported rates */
+ if (elem != DW_ELEM_SUPRATES && sr == ratesG) {
+ memcpy (bp, ratesGx, ratesGx[1]+2);
+ bp += bp[1] + 2;
+ }
+
+ return bp;
+}
+
+/**
+ * Create beacon and probe response frames to send in an IBSS
+ * @param interval Beacon interval in TU
+ * @return 1 if success, 0 if error
+ */
+static void dw_beacon_make_beacon(dw_priv_t * priv, int interval)
+{
+ dw_beacon_frame * bcnFrame = &priv->beacon_frame;
+ u16 atimWindow = 0; /* ATIM window size, 0 if none */
+
+ u8 *bp = bcnFrame->body;
+ u16 bss_caps = 0, caps;
+
+ u8 bss_addr[ ETH_ALEN ];
+
+ priv->beacon_body_length = 0;
+
+ bss_caps = DW_CAP_IBSS;
+ //if (!(macParams.encrypt & WLN_ENCR_OPEN))
+ // bss_caps |= CAP_PRIVACY;
+ //if (OPT_SHORTPRE)
+ // bss_caps |= CAP_SHORTPRE;
+
+ memcpy(bss_addr, priv->adhoc.bssid, ETH_ALEN);
+
+ /* Init beacon MAC header */
+ memset (bcnFrame, 0, sizeof (dw_beacon_frame));
+ bcnFrame->fc = IEEE80211_STYPE_BEACON;
+ memset (bcnFrame->addr1, 0xff, ETH_ALEN);
+ DW_SET_ADDR (bcnFrame->addr2, priv->dev->dev_addr); /* station MAC address */
+ DW_SET_ADDR (bcnFrame->addr3, bss_addr); /* BSS to associate with */
+
+ /** Set fixed params
+ * Timestamp is set by hardware */
+ SET16 (&bp[8], interval);
+
+ /* Set capabilities */
+ caps = bss_caps & (DW_CAP_ESS|DW_CAP_IBSS|DW_CAP_PRIVACY);
+ /* Use short preamble if allowed in BSS and params and rate > 1 mbps. */
+ /* caps |= DW_CAP_SHORTPRE; */
+ SET16 (&bp[10], caps);
+ bp += 12;
+
+ /* Set SSID */
+ bp[0] = DW_ELEM_SSID;
+ bp[1] = priv->softmac->associnfo.req_essid.len;
+ memcpy (&bp[2], priv->softmac->associnfo.req_essid.data, priv->softmac->associnfo.req_essid.len);
+ bp += bp[1] + 2;
+
+ /* Set supported rates */
+ bp = dw_beacon_set_rates(bp, DW_ELEM_SUPRATES, priv->adhoc.channel);
+
+ /* Set channel number */
+ if (!DW_CHAN_5G (priv->adhoc.channel)) {
+ bp[0] = DW_ELEM_DSPARAM;
+ bp[1] = 1;
+ bp[2] = priv->adhoc.channel;
+ bp += bp[1] + 2;
+ }
+
+ /* Set IBSS ATIM window */
+ bp[0] = DW_ELEM_IBSSPARAM;
+ bp[1] = 2;
+ SET16 (&bp[2], atimWindow);
+ bp += bp[1] + 2;
+
+ /* Set ERP info. */
+ //if (!DW_CHAN_5G (priv->adhoc.channel) && !(OPT_BONLY)))
+ {
+ bp[0] = DW_ELEM_ERPINFO;
+ bp[1] = 1;
+ bp[2] = 0;
+ bp += bp[1] + 2;
+ }
+
+ /* Set extended supported rates */
+ bp = dw_beacon_set_rates(bp, DW_ELEM_EXTSUPRATES, priv->adhoc.channel);
+ priv->beacon_body_length = ((u8 *)bp - (u8 *)bcnFrame->body);
+ dw_beacon_set_plcp(&bcnFrame->hwHdr, IEEE80211_CCK_RATE_1MB , priv->beacon_body_length + sizeof(dw_beacon_frame) - BEACON_BODY_SIZE + FCS_SIZE);
+ bcnFrame->hwHdr.plcp.pskcck.raw32 = cpu_to_be32(0x0a046802);
+ priv->beacon_ready = 1;
+}
+
+static void dw_beacon_start_IBSS(struct dw_priv *priv)
+{
+ int atim = 0;
+
+ dw_beacon_make_beacon(priv, DW_BEACON_INT);
+ /* If starting IBSS, set beacon and ATIM intervals */
+ dw_iowrite32(atim | (DW_BEACON_INT << 16), HW_CFP_ATIM);
+ dw_cw_set(priv, IEEE80211_STYPE_BEACON);
+ /* Write beacon frame to beacon buffer */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_BEACEN);
+ dw_hw_write_fifo((void *)&priv->beacon_frame, priv->beacon_body_length + sizeof(dw_beacon_frame) - BEACON_BODY_SIZE);
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_BEACEN);
+ /* Set interrupt mask to enable TBTT and ATIM interrupts */
+ dw_iosetbits32(HW_INTR_MASK, INTR_TBTT|INTR_ATIM);
+ /* Enable IBSS mode */
+ dw_iosetbits32(HW_MAC_CONTROL, CTRL_IBSS|CTRL_BEACONTX);
+}
+
+/**
+ * dw_beacon_associate - does whatever is necessary for association
+ */
+static void dw_beacon_associate(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response *resp,
+ struct ieee80211softmac_network *net)
+{
+ u16 cap = 0;
+ u8 erp_value = net->erp_value;
+
+ if (resp != NULL)
+ cap = le16_to_cpu(resp->capability);
+ mac->associnfo.associating = 0;
+ mac->bssinfo.supported_rates = net->supported_rates;
+ ieee80211softmac_recalc_txrates(mac);
+
+ mac->associnfo.associated = 1;
+
+ if (resp != NULL)
+ mac->associnfo.short_preamble_available =
+ (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0;
+ ieee80211softmac_process_erp(mac, erp_value);
+
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+ memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
+ netif_carrier_on(mac->dev);
+
+ if (resp != NULL)
+ mac->association_id = le16_to_cpup(&resp->aid);
+}
+
+static void dw_beacon_start(struct work_struct *work)
+{
+ dw_priv_t *priv = container_of((struct delayed_work *)work, struct dw_priv, beacon_work);
+ unsigned long flags;
+ int channel;
+ struct ieee80211softmac_network *net;
+ struct ieee80211softmac_device *mac = priv->softmac;
+
+ if (IW_MODE_ADHOC != priv->ieee->iw_mode
+ || mac->associnfo.associating
+ || mac->scanning
+ || priv->beacon_ready
+ ) return;
+
+ if (!mac->associnfo.req_essid.len
+ || *mac->associnfo.req_essid.data == '\0'
+ ) return;
+
+ channel = priv->adhoc.channel;
+ if (!channel) channel = priv->channel;
+ if (!channel) channel = DW_IBSS_DEFAULT_CHANNEL;
+
+ memset(&priv->adhoc, 0, sizeof(priv->adhoc));
+ get_random_bytes(priv->adhoc.bssid, ETH_ALEN); // Select a random BSSID
+ priv->adhoc.bssid[0] &= ~DW_MAC_GROUP; // clear group bit
+ priv->adhoc.bssid[0] |= DW_MAC_LOCAL; // set local bit
+ priv->adhoc.channel = channel;
+ priv->adhoc.mode=IW_MODE_ADHOC;
+ priv->adhoc.essid.len = mac->associnfo.req_essid.len;
+ memcpy(priv->adhoc.essid.data, mac->associnfo.req_essid.data, IW_ESSID_MAX_SIZE + 1);
+
+ net = ieee80211softmac_get_network_by_essid_locked(mac, &mac->associnfo.associate_essid);
+ if (!net) {
+ net = &priv->adhoc;
+
+ mac->set_channel(mac->dev, net->channel);
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ dw_beacon_associate(mac, NULL, net);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ mac->associnfo.bssvalid = 1;
+ mac->associnfo.channel = net->channel;
+ memcpy(mac->associnfo.bssid, net->bssid, ETH_ALEN);
+ mac->associnfo.associate_essid.len = net->essid.len;
+ memcpy(mac->associnfo.associate_essid.data, net->essid.data, IW_ESSID_MAX_SIZE + 1);
+ } else {
+ memcpy(priv->adhoc.bssid, net->bssid, ETH_ALEN);
+ priv->adhoc.essid.len = net->essid.len;
+ memcpy(priv->adhoc.essid.data, net->essid.data, IW_ESSID_MAX_SIZE + 1);
+ }
+
+ dw_beacon_start_IBSS(priv);
+}
+
+/**
+ * dw_int - interrupt handler for WiFi FPGA
+ */
+static irqreturn_t dw_int(int irq, void *dev_id)
+{
+ dw_priv_t* priv = dev_id;
+ u32 status;
+
+ DBG_FN(DBG_INT);
+
+ /* acknowledge interrupt */
+ spin_lock(&priv->lock);
+ status = dw_ioread32(HW_INTR_STATUS);
+ dw_iowrite32(status, HW_INTR_STATUS);
+
+ if (status & INTR_RXFIFO) {
+ int frame_received = 0;
+ /* process all frames */
+ while (!priv->rx.pause &&
+ (!(dw_hw_get_status(priv) & STAT_RXFE))) {
+ frame_received = 1;
+ dw_rx_frame_fetch(priv);
+ }
+
+ if (frame_received)
+ /* the FIFO is prone to run full, do it with high
+ priority */
+ tasklet_hi_schedule(&priv->rx.tasklet);
+
+ DBG_EXEC(status &= ~INTR_RXFIFO);
+ } /* if (status & INTR_RXFIFO) */
+
+ if (status & INTR_TIMEOUT) {
+ priv->tx.timeout = 1;
+ DBG_EXEC(status &= ~INTR_TIMEOUT);
+ }
+
+ if (unlikely(status & INTR_ABORT)) {
+ priv->tx.timeout = 1;
+ /* retransmit it */
+ DBG_EXEC(status &= ~INTR_ABORT);
+ }
+
+ if (status & INTR_TXEND) {
+ tasklet_schedule(&priv->tx.tasklet);
+ DBG_EXEC(status &= ~INTR_TXEND);
+ }
+
+ if (status & (INTR_TBTT | INTR_ATIM)) {
+ /* is emitted by hardware even if masked out :-(. */
+
+ /** Beacon frame is already in beacon buffer.
+ * Only set backoff timer here. */
+ dw_cw_set(priv, IEEE80211_STYPE_BEACON);
+ DBG_EXEC(status &= ~(INTR_TBTT | INTR_ATIM));
+ }
+
+ if (unlikely(status & INTR_RXOVERRUN)) {
+ DBG(DBG_RX, "Receiver overrun");
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_over_errors++;
+ spin_unlock(&priv->ieee->lock);
+ DBG_EXEC(status &= ~INTR_RXOVERRUN);
+ }
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+ /* when not developing/debugging, we don't run extra tests. */
+ if (unlikely(status))
+ /* check that we had everything handled*/
+ ERRORL("Unhandled interrupt 0x%08x, mask is 0x%08x", status,
+ dw_ioread32(HW_INTR_MASK));
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int dw_ieee80211_handle_beacon (
+ struct net_device * dev,
+ struct ieee80211_beacon * beacon,
+ struct ieee80211_network * network)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ if ( !priv->softmac->associnfo.associated &&
+ !priv->softmac->associnfo.associating &&
+ IW_MODE_INFRA == priv->ieee->iw_mode &&
+ priv->jiffies_last_beacon + RESCAN_TIMEOUT < jiffies &&
+ (strncmp(priv->softmac->associnfo.req_essid.data,
+ beacon->info_element->data, beacon->info_element->len) == 0) &&
+ priv->reconnection_attempts &&
+ priv->reconnection_attempts < MAX_RECONNECTION_ATTEMPTS ) {
+ dw_lock(priv, flags);
+ dw_set_channel(priv, network->channel);
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts++;
+ dw_unlock(priv, flags);
+ }
+
+ if ( priv->softmac->associnfo.associated &&
+ IW_MODE_INFRA == priv->ieee->iw_mode &&
+ memcmp(network->bssid, beacon->header.addr3, ETH_ALEN) == 0 ) {
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts = 0;
+ }
+ return ieee80211softmac_handle_beacon(dev, beacon, network);
+}
+
+/* Allocate a management frame */
+static u8 * ieee80211softmac_alloc_mgt(u32 size)
+{
+ u8 * data;
+
+ /* Add the header and FCS to the size */
+ size = size + IEEE80211_3ADDR_LEN;
+ if (size > IEEE80211_DATA_LEN)
+ return NULL;
+ /* Allocate the frame */
+ data = kzalloc(size, GFP_ATOMIC);
+ return data;
+}
+
+static void
+ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
+{
+ /* Fill in the frame control flags */
+ header->frame_ctl = cpu_to_le16(type);
+ /* Control packets always have WEP turned off */
+ if (type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
+ header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
+
+ /* Fill in the duration */
+ header->duration_id = 0;
+ /* FIXME: How do I find this?
+ * calculate. But most drivers just fill in 0 (except if it's a station id of course) */
+
+ /* Fill in the Destination Address */
+ if (dest == NULL)
+ memset(header->addr1, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr1, dest, ETH_ALEN);
+ /* Fill in the Source Address */
+ memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
+}
+
+static void
+ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
+{
+ /* This is common with 2addr, so use that instead */
+ ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);
+
+ /* Fill in the BSS ID */
+ if (bssid == NULL)
+ memset(header->addr3, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr3, bssid, ETH_ALEN);
+
+ /* Fill in the sequence # */
+ /* FIXME: I need to add this to the softmac struct
+ * shouldn't the sequence number be in ieee80211? */
+}
+
+static int dw_ieee80211_handle_probe_request (
+ struct net_device * dev,
+ struct ieee80211_probe_request * req,
+ struct ieee80211_rx_stats * stats)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+
+ if (priv->softmac->associnfo.associate_essid.len == req->info_element->len &&
+ memcmp(priv->softmac->associnfo.associate_essid.data, req->info_element->data, priv->softmac->associnfo.associate_essid.len) == 0) {
+ struct ieee80211_probe_response *pkt = NULL;
+ struct ieee80211softmac_device *mac = priv->softmac;
+ struct ieee80211softmac_network *net = &priv->adhoc;
+ u8 *data;
+ u32 pkt_size = 0;
+ int encrypt_mpdu = 0;
+
+ pkt = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(priv->beacon_body_length);
+
+ if (unlikely(pkt == NULL)) {
+ printk("Error, packet is nonexistant or 0 length\n");
+ return -ENOMEM;
+ }
+
+ ieee80211softmac_hdr_3addr(mac, &(pkt->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, priv->adhoc.bssid);
+ data = (u8 *)pkt->info_element;
+ memcpy(pkt->info_element, priv->beacon_frame.body, priv->beacon_body_length);
+ data += priv->beacon_body_length;
+ pkt_size = (data - (u8 *)pkt);
+ ieee80211_tx_frame(priv->ieee, (struct ieee80211_hdr *)pkt, IEEE80211_3ADDR_LEN, pkt_size, encrypt_mpdu);
+ kfree(pkt);
+ }
+
+ return 0;
+}
+
+/**
+ * dw_ieee80211_hard_start_xmit - prepares and sends a frame or put it in tx
+ * queue for transmission when current frames are completed.
+ */
+static int dw_ieee80211_hard_start_xmit(
+ struct ieee80211_txb* txb,
+ struct net_device* dev,
+ int priority)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ dw_frame_tx_t* frame = NULL;
+ int err = -ENOMEM;
+ unsigned long flags;
+ unsigned long ieeeflags;
+ int i;
+ dw_frame_tx_info_t info;
+
+ DBG_FN(DBG_TX);
+
+ /* sanity checks of txb */
+ BUG_ON(!txb->nr_frags); /* nothing to do */
+ if (txb->nr_frags >= TX_MAX_FRAGS) {
+ /* we are lazy and use a fixed-size array */
+ ERROR("Too many fragments, dropping it: %i", txb->nr_frags);
+ goto error;
+ }
+
+ /* Check if the device queue is big enough for every fragment. If not,
+ * drop the whole packet. */
+ for (i = 0; i < txb->nr_frags; i++) {
+ if (unlikely(txb->fragments[ i ]->len >
+ (HW_TX_FIFO_SIZE + sizeof(dw_hw_hdr_tx_t)))) {
+ ERROR("PIO Tx Device queue too small, dropping it");
+
+ goto error;
+ }
+ }
+
+ if (netif_queue_stopped(dev)) {
+ /* apperently, softmac doesn't honor netif_queue_stopped,
+ at least for control messages. Block it ourself. */
+ err = -EBUSY;
+ goto error;
+ }
+
+ /* take the next free queue entry and provide it's data */
+ dw_lock(priv, flags);
+
+ /* prepare everything. */
+ if (!dw_tx_frame_prepare(priv, &info, txb))
+ goto error_unlock;
+
+ frame = list_entry(priv->tx.free.next, dw_frame_tx_t, list);
+ frame->s = info;
+
+ /* put it to tx queue*/
+ if (list_empty(&priv->tx.queued) && !priv->tx.pending) {
+ /* no transmission is running yet, so the queue won't be
+ processed by int handler. Write it directly to FIFO
+ start with first fragment. */
+ priv->tx.retries = 0;
+ priv->tx.times_sent = 0;
+ dw_tx_fragment_send(priv, frame);
+ }
+ /* int handler is locked, so we can manage queue list after sending */
+ list_move_tail(priv->tx.free.next, &priv->tx.queued);
+
+ if (list_empty(&priv->tx.free)) {
+ /* Driver starts always with free entries.
+ But now they are all in use. Wait until we have some free
+ entries */
+ netif_stop_queue(dev);
+ }
+
+ dw_unlock(priv, flags);
+
+ return 0;
+
+error_unlock:
+ dw_unlock(priv, flags);
+
+error:
+ /* txb is free'd on !0 return */
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ priv->ieee->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return err;
+}
+
+/**
+ * dw_update_status_led - updates the LED depending on association info
+ */
+static void dw_update_status_led(dw_priv_t* priv)
+{
+ static int reduce_rate = 0;
+ reduce_rate++; /* slow it down to 500ms, don't divide */
+
+ if (reduce_rate == 9) {
+ static int count = 0;
+ reduce_rate = 0;
+
+ count++;
+ spin_lock(&priv->ieee->lock);
+ spin_lock(&priv->softmac->lock);
+ if (priv->softmac->associnfo.associated) {
+ if (IW_MODE_ADHOC == priv->ieee->iw_mode)
+ /* slow blink, 7/8 on */
+ dw_set_led_on(PIN_LED, count & 7);
+ else
+ dw_set_led_on(PIN_LED, 1); /* solid on */
+ } else
+ dw_set_led_on(PIN_LED, count & 1); /* faster blink */
+ spin_unlock(&priv->softmac->lock);
+ spin_unlock(&priv->ieee->lock);
+ }
+}
+
+/**
+ * dw_rate_reset - resets rate statistics
+ */
+static void dw_rate_reset(dw_priv_t* priv)
+{
+ int i;
+
+ DBG_FN(DBG_INTERFACE | DBG_UPDATE_RATE);
+ REQUIRE_LOCKED(priv);
+
+ CLEAR(priv->rate);
+
+ priv->rate.success_threshold = THRESH_MIN;
+
+ /* find index in ap_ri that matches default_rate */
+ for (i = 0; i < priv->ap_ri.count; i++) {
+ if (priv->softmac->txrates.default_rate == BASIC_RATE_MASK(priv->ap_ri.rates[ i ])) {
+ priv->rate.index = i;
+ break;
+ }
+ }
+}
+
+/**
+ * dw_rate_update - selects a better rate depending on frame error count
+ *
+ * taken from wifi_mac:mac_rate.c:UpdateRate
+ */
+static void dw_rate_update(dw_priv_t* priv)
+{
+ int changed = 0;
+
+ DBG_FN(DBG_UPDATE_RATE);
+ REQUIRE_LOCKED(priv);
+
+ if (atomic_read(&priv->fix_rate) || !rate_is_enough(priv))
+ /* user requested fix rate or not enough data*/
+ return;
+
+ spin_lock(&priv->softmac->lock);
+ if (!priv->softmac->associnfo.associated)
+ goto out;
+
+ if (rate_is_success(priv)) {
+ /* try to increase rate */
+ priv->rate.success++;
+ if ((priv->rate.success >= priv->rate.success_threshold) &&
+ (priv->rate.index < (priv->ap_ri.count - 1))) {
+ /* we are successfull long enough */
+ priv->rate.recovery = 1;
+ priv->rate.success = 0;
+ priv->rate.index++;
+ changed = 1;
+ } else
+ priv->rate.recovery = 0;
+ } else if (rate_is_failure(priv)) {
+ /* decrease rate */
+ if (priv->rate.index > 0) {
+ if (priv->rate.recovery) {
+ /* errors resulted from a rate increase.
+ double successfull intervals needed to
+ incrase next time */
+ priv->rate.success_threshold *= 2;
+ if (priv->rate.success_threshold > THRESH_MAX)
+ priv->rate.success_threshold = THRESH_MAX;
+ } else
+ priv->rate.success_threshold = THRESH_MIN;
+
+ priv->rate.index--;
+ changed = 1;
+ }
+
+ priv->rate.success = 0;
+ priv->rate.recovery = 0;
+ }
+
+ if (changed) {
+ priv->softmac->txrates.default_rate = BASIC_RATE_MASK(priv->ap_ri.rates[ priv->rate.index ]);
+ DBG(DBG_UPDATE_RATE, "Rate changed to %i",
+ priv->softmac->txrates.default_rate);
+ }
+
+ /* reset counter so we work on the next time slice */
+ priv->rate.tx_data_any = priv->rate.tx_data_ack = 0;
+
+out:
+ spin_unlock(&priv->softmac->lock);
+}
+
+/**
+ * dw_management_timer - performs all periodic background non tx/rx work
+ */
+static void dw_management_timer(unsigned long a)
+{
+ dw_priv_t* priv = (dw_priv_t*) a;
+ unsigned long flags;
+
+ DBG_FN(DBG_UPDATE_RATE);
+
+ dw_lock(priv, flags);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ if (!priv->softmac->scanning && !priv->beacon_ready) {
+ schedule_delayed_work(&priv->beacon_work, 0);
+ }
+ break;
+ case IW_MODE_INFRA:
+ if (priv->softmac->associnfo.associated && priv->jiffies_last_beacon + BEACON_TIMEOUT < jiffies)
+ {
+ priv->softmac->associnfo.associated = 0;
+ ieee80211softmac_start_scan(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->reconnection_attempts = 1;
+ } else if (
+ !priv->softmac->associnfo.associated
+ && !priv->softmac->associnfo.associating
+ && priv->reconnection_attempts
+ && priv->softmac->associnfo.req_essid.len
+ && priv->softmac->associnfo.req_essid.data
+ && *priv->softmac->associnfo.req_essid.data
+ && priv->jiffies_last_beacon + RESCAN_TIMEOUT < jiffies)
+ {
+ if (priv->reconnection_attempts < MAX_RECONNECTION_ATTEMPTS) {
+ priv->softmac->associnfo.associated = 0;
+ ieee80211softmac_start_scan(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->reconnection_attempts++;
+ } else {
+ ieee80211softmac_disassoc(priv->softmac);
+ memset(priv->softmac->associnfo.bssid, 0, ETH_ALEN);
+ priv->reconnection_attempts = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ dw_update_status_led(priv);
+
+ if (priv->rate.have_activity) {
+ priv->rate.have_activity = 0;
+ /* blink when data arrives */
+ dw_set_led_on(PIN_ACTIVITY_LED,
+ priv->activity.counter & 1);
+
+ priv->activity.counter++;
+ } else
+ /* turn it off */
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ priv->rate.counter++;
+ if (!(priv->rate.counter % MANAGEMENT_TICKS_FOR_UPDATE))
+ /* we don't need to update rate each LED timer event */
+ dw_rate_update(priv);
+
+ if (priv->tx.pending && time_after(jiffies, priv->dev->trans_start + TX_TIMEOUT)) {
+ /* we can't use tx_timeout. It only works when
+ * netif_carrier_ok, but that is set only when associated.
+ * But we could loose association and run into a tx
+ * hanger. Therefore we;d never get associated, and never
+ * reset.So do it homemade. */
+ dw_tx_reset(priv);
+ }
+
+ mod_timer(&priv->management_timer, jiffies + MANAGEMENT_JIFFIES);
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_hw_init_card - initializes card and memory settings
+ */
+static int __init dw_hw_init_card(struct net_device* dev)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ u32 firmware_id;
+ u32 ctrl;
+ u32 mac_status;
+
+ DBG_FN(DBG_INIT);
+
+ dw_lock(priv, flags);
+
+ /* Configure PIO pins */
+ gpio_direction_output(PIN_LED, 0);
+ gpio_direction_output(PIN_ACTIVITY_LED, 0);
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+ gpio_configure_ns9360(PIN_INTR, 0, 1, 0);
+
+ /* CS2 is used for wireless baseband registers.
+ Map to MAC_BASE address, 32 bit width, 8 wait states.*/
+ iowrite32(0x82, MEM_DMCONF(2));
+ iowrite32(0, MEM_SMWED(2));
+ iowrite32(2, MEM_SMOED(2));
+ iowrite32(8, MEM_SMRD(2));
+ iowrite32(0, MEM_SMPMRD(2));
+ iowrite32(4, MEM_SMWD(2));
+ iowrite32(2, MEM_SWT(2));
+
+ iowrite32(MAC_BASE_PHYS, SYS_SMCSSMB(2));
+ iowrite32(MAC_MASK, SYS_SMCSSMM(2));
+
+ /* Init baseband processor and MAC assist */
+
+ ctrl = GEN_INIT | GEN_ANTDIV;
+
+ /* we can't do a softreset here */
+
+ /* go back to normal state */
+
+ dw_iowrite32(ctrl, HW_GEN_CONTROL);
+ dw_iowrite32(0, HW_MAC_CONTROL);
+ dw_iowrite32(0, HW_INTR_MASK);
+ /* Mike's recommendation for antenna diversity and antenna map value.
+ Transmit only on primary antenna, receive on diversity */
+ dw_iowrite32(0x88000000, HW_RSSI_AES);
+
+ /* get MAC from fpga */
+ dw_hw_memcpy_from(dev->dev_addr, HW_STAID0, ETH_ALEN);
+
+ /* Initialize RF tranceiver */
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ dw_hw_write_rf(7, 0x27ffe); /* TEST register */
+ dw_hw_write_rf(6, 0xf81ac); /* Filter Register */
+ dw_set_tx_power(priv, DW_TX_POWER_DEFAULT); /* Transmitter Gain */
+ dw_hw_write_rf(4, 0x0002b); /* Receiver Gain */
+
+ dw_hw_set_vco(0); /* no channel selected yet */
+
+ dw_hw_write_rf(0, 0x25f9c); /* Mode Control - Calibrate Filter */
+ udelay(10);
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ dw_hw_set_vco(0); /* no channel selected yet */
+ dw_hw_write_rf(4, 0x0007b); /* Receiver Gain */
+ dw_set_tx_power(priv, DW_TX_POWER_DEFAULT); /* Transmitter Gain */
+ dw_hw_write_rf(6, 0xf81ac); /* Filter Register */
+ dw_hw_write_rf(7, 0x3fffe); /* TEST register */
+
+ dw_hw_write_rf(0, 0x27fdc); /* Mode Control - Calibrate Filter */
+ udelay(10);
+ dw_hw_write_rf(0, 0x27fd4); /* Mode Control - RX/TX mode */
+#endif
+
+ /* Firmware version */
+ firmware_id = dw_ioread32(HW_VERSION);
+ mac_status = dw_ioread32(HW_MAC_STATUS);
+ printk(KERN_DEBUG DRIVER_NAME ": FPGA HW version: %i.%02i FW Version: %i.%02i\n",
+ (firmware_id >> 8) & 0xff, firmware_id & 0xff,
+ (mac_status >> 24) & 0xff, (mac_status >> 16) & 0xff);
+
+ dw_unlock(priv, flags);
+
+ return 1;
+}
+
+/**
+ * dw_reset_rx_dups - resets the duplicate list and forgets the senders
+ */
+static void dw_reset_rx_dups(dw_priv_t* priv)
+{
+ unsigned long flags;
+ int i;
+
+ REQUIRE_UNLOCKED(priv);
+
+ dw_lock(priv, flags);
+
+ CLEAR(priv->rx.dups);
+
+ /* for detecting duplicates */
+ INIT_LIST_HEAD(&priv->rx.dups.known.list);
+ INIT_LIST_HEAD(&priv->rx.dups.free.list);
+ for (i = 0; i < ARRAY_SIZE(priv->rx.dups.entries); i++) {
+ struct list_head* entry = &priv->rx.dups.entries[ i ].list;
+ INIT_LIST_HEAD(entry);
+ list_add_tail(entry, &priv->rx.dups.free.list);
+ } /* for (i = 0) */
+
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_setup_rx_queue - creates the list structure
+ *
+ * @return: -ENOMEM on no failures otherwise 0
+ */
+static int __init dw_setup_rx_queue(dw_priv_t* priv)
+{
+ dw_frame_rx_t* tmp = NULL;
+ size_t aligned_size;
+ size_t buffer_size;
+ int i;
+
+ INIT_LIST_HEAD(&priv->rx.queue.free.list);
+ INIT_LIST_HEAD(&priv->rx.queue.filled.list);
+
+ aligned_size = ((char*) &tmp[ 1 ]) - ((char*) &tmp[ 0 ]);
+ buffer_size = aligned_size * RX_QUEUE_SIZE;
+ priv->rx.queue.buffer =
+ (dw_frame_rx_t*) vmalloc(buffer_size);
+ if (NULL == priv->rx.queue.buffer)
+ goto error;
+
+ memset(priv->rx.queue.buffer, 0, buffer_size);
+
+ /* create frame skb and list for moving unswapped frames */
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ dw_frame_rx_t* frame = &priv->rx.queue.buffer[ i ];
+ struct list_head* entry = &frame->list;
+
+ frame->skb = dev_alloc_skb(DW_MTU);
+ if (NULL == frame->skb) {
+ for (; i > 0; i--) {
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+
+ goto error_skb;
+ }
+
+ INIT_LIST_HEAD(entry);
+ list_add(entry, &priv->rx.queue.free.list);
+ }
+
+ return 0;
+
+error_skb:
+ vfree(priv->rx.queue.buffer);
+ priv->rx.queue.buffer = NULL;
+error:
+ ERROR("No Memory for Rx Queue");
+ return -ENOMEM;
+}
+
+/**
+ * dw_start_dev - starts resource
+ */
+static int dw_start_dev(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ struct list_head* cursor;
+ int err = -ENODEV;
+ int i;
+
+ DBG_FN(DBG_INIT);
+
+ CLEAR(*priv);
+
+ spin_lock_init(&priv->lock);
+ priv->dev = dev;
+ priv->short_retry_limit = DW_DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DW_DEFAULT_LONG_RETRY_LIMIT;
+ snprintf(priv->nick, IW_ESSID_MAX_SIZE, "Digi Wireless b/g");
+
+ atomic_set(&priv->fix_rate, 0);
+
+ /* the stack may later reduce the number of supported rates if the
+ * access point can't provide them */
+ ASSERT(ARRAY_SIZE(dw_rates) <= ARRAY_SIZE(priv->ap_ri.rates));
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ priv->ap_ri.rates[ i ] = dw_rates[ i ];
+ priv->ap_ri.count = i;
+
+ /* rx */
+ err = dw_setup_rx_queue(priv);
+ if (err < 0)
+ goto error_rx_queue;
+
+ tasklet_init(&priv->rx.tasklet, dw_rx_tasklet_handler,
+ (unsigned long) priv);
+ tasklet_disable(&priv->rx.tasklet); /* will be enabled in open */
+
+ /* tx */
+ atomic_set(&priv->tx.seq_nr, 0);
+ /* first down should sleep */
+ init_MUTEX_LOCKED(&priv->tx.pause_sem);
+ INIT_LIST_HEAD(&priv->tx.queued);
+ INIT_LIST_HEAD(&priv->tx.free);
+ for (i = 0; i < ARRAY_SIZE(priv->tx.frames); i++) {
+ struct list_head* entry = &priv->tx.frames[ i ].list;
+ INIT_LIST_HEAD(entry);
+ list_add_tail(entry, &priv->tx.free);
+ }
+
+ tasklet_init(&priv->tx.tasklet, dw_tx_tasklet_handler,
+ (unsigned long) priv);
+ tasklet_disable(&priv->tx.tasklet);
+
+ init_timer(&priv->management_timer);
+ priv->management_timer.function = dw_management_timer;
+ priv->management_timer.data = (unsigned long) priv;
+
+ if (!dw_hw_init_card(dev))
+ goto error_init;
+
+ /* card is setup, now we can register the irq */
+ err = request_irq(dev->irq, dw_int, 0, dev->name,
+ priv);
+ if (err) {
+ ERROR("register interrupt %d failed, err %d", dev->irq, err);
+ goto error_irq;
+ }
+ disable_irq(dev->irq);
+
+ priv->softmac = ieee80211_priv(dev);
+ priv->softmac->set_channel = dw_softmac_set_chan;
+ priv->softmac->set_bssid_filter = dw_softmac_set_bssid_filter;
+ priv->softmac->txrates_change = dw_softmac_txrates_change;
+ priv->ieee = netdev_priv(dev);
+ priv->ieee->modulation = IEEE80211_OFDM_MODULATION | IEEE80211_CCK_MODULATION;
+ priv->ieee->hard_start_xmit = dw_ieee80211_hard_start_xmit;
+ priv->ieee->set_security = dw_ieee80211_set_security;
+ priv->ieee->handle_beacon = dw_ieee80211_handle_beacon;
+ priv->ieee->handle_probe_request = dw_ieee80211_handle_probe_request;
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ priv->ieee->freq_band = IEEE80211_24GHZ_BAND;
+ priv->ieee->rts = DW_MAX_RTS_THRESHOLD;
+ priv->ieee->perfect_rssi = MAC_RSSI_MAX;
+ priv->ieee->worst_rssi = 0;
+ priv->ieee->config |= CFG_IEEE80211_RTS;
+
+ if (dw_geo_init(priv) < 0)
+ goto error_init2;
+
+ if (register_netdev(dev))
+ goto error_init2;
+
+ ieee80211softmac_notify(dev, IEEE80211SOFTMAC_EVENT_AUTHENTICATED,
+ dw_softmac_notify_authenticated, NULL);
+
+ return 0;
+
+error_init2:
+ free_irq(dev->irq, priv);
+error_irq:
+error_init:
+ list_for_each(cursor, &priv->rx.queue.free.list) {
+ dw_frame_rx_t* frame = list_entry(cursor, dw_frame_rx_t, list);
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+ vfree(priv->rx.queue.buffer);
+ priv->rx.queue.buffer = NULL;
+
+error_rx_queue:
+ return err;
+}
+
+/**
+ * dw_stop_dev - stops device
+ */
+static void dw_stop_dev(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ struct list_head* it;
+
+ DBG_FN(DBG_INIT);
+
+ dw_lock(priv, flags);
+ dw_iowrite32(0, HW_MAC_CONTROL);
+ dw_unlock(priv, flags);
+
+ list_for_each(it, &priv->rx.queue.free.list) {
+ dw_frame_rx_t* frame = list_entry(it, dw_frame_rx_t, list);
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+ vfree(priv->rx.queue.buffer);
+
+ unregister_netdev(dev);
+
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ gpio_free(PIN_LED);
+ gpio_free(PIN_ACTIVITY_LED);
+ gpio_free(PIN_INTR);
+}
+
+/**
+ * dw_remove - stops device and removes all resources
+ */
+static int dw_remove(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+
+ DBG_FN(DBG_INIT);
+
+ dw_stop_dev(pdev);
+
+ /* unmap resources */
+ if (0 != dev->base_addr) {
+ iounmap(vbase);
+ vbase = NULL;
+ }
+ free_irq(dev->irq, ieee80211softmac_priv(dev));
+
+ free_ieee80211softmac(dev);
+ dev = NULL;
+
+ platform_set_drvdata(pdev, NULL);
+
+ release_resource(pdev->resource);
+
+ return 0;
+}
+
+/**
+ * dw_release_device - dummy function
+ */
+static void dw_release_device(struct device* dev)
+{
+ DBG_FN(DBG_INIT);
+
+ /* nothing to do. But we need a !NULL function */
+}
+
+/**
+ * dw_softmac_txrates_change - adjusts rate to allowed values.
+ *
+ * Select a better on.
+ */
+static void dw_softmac_txrates_change(struct net_device* dev, u32 changes)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ int was_locked = spin_is_locked(&priv->lock);
+ unsigned long flags = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (!was_locked)
+ /* we are called either directly from open/user interface,
+ or from our tasklet context when processing a frame.*/
+ dw_lock(priv, flags);
+
+ /* new rate */
+ CLEAR(priv->rate);
+
+ dw_rate_reset(priv);
+
+ if (!was_locked)
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_wx_set_encode - changes the encoding
+ *
+ * Used by wireless tools <= 28 for WEP-1. Not AES
+ */
+static int dw_wx_set_encode(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->host_encrypt = 1;
+ priv->ieee->host_encrypt_msdu = 1;
+ priv->ieee->host_decrypt = 1;
+ priv->ieee->host_mc_decrypt = 1;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return ieee80211_wx_set_encode(priv->ieee, info, data, extra);
+}
+
+/**
+ * dw_wx_set_encodeext - changes the encoding
+ *
+ * Only used by wpa_supplicant or wireless_tool >= 29
+ */
+static int dw_wx_set_encodeext(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ struct iw_encode_ext* ext = (struct iw_encode_ext*) extra;
+ unsigned long ieeeflags;
+ int host = (dw_sw_aes || (IW_ENCODE_ALG_CCMP != ext->alg));
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->host_encrypt = host;
+ priv->ieee->host_encrypt_msdu = host;
+ priv->ieee->host_decrypt = host;
+ priv->ieee->host_mc_decrypt = host;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ /* wx_set_encodeext will initialize itself depending on host_encrypt.
+ * After wx_set_encodeext, it will be of no further use. */
+ return ieee80211_wx_set_encodeext(priv->ieee, info, data, extra);
+}
+
+/**
+ * dw_softmac_notify_authenticated - called when authenticated
+ *
+ * Resets statistics
+ */
+static void dw_softmac_notify_authenticated(struct net_device* dev,
+ int event_type, void* context)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE);
+
+ dw_lock(priv, flags);
+ dw_rate_reset(priv);
+ dw_unlock(priv, flags);
+}
+
+/***********************************************************************
+ * @Function: dw_ieee80211_get_network_by_bssid
+ * @Return:
+ * @Descr: Get a network from the list by BSSID with locking
+ ***********************************************************************/
+struct ieee80211_network *
+dw_ieee80211_get_network_by_bssid(struct dw_priv *priv, u8 *bssid)
+{
+ unsigned long ieeeflags;
+ struct ieee80211_network *ieee_net = NULL, *tmp_net;
+ struct list_head *list_ptr;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ list_for_each(list_ptr, &priv->ieee->network_list) {
+ tmp_net = list_entry(list_ptr, struct ieee80211_network, list);
+ if (!memcmp(tmp_net->bssid, bssid, ETH_ALEN)) {
+ if (ieee_net==NULL) {
+ ieee_net = tmp_net;
+ } else {
+ if (tmp_net->last_scanned > ieee_net->last_scanned)
+ ieee_net = tmp_net;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ return ieee_net;
+}
+
+/* CCMP encryption data */
+static const u32 te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+
+static const u32 rcon[] = {
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+ 0x01000000, 0x02000000,
+ 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000,
+ 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000,
+};
+
+/***********************************************************************
+ * @Function: dw_aes_set_encrypt_key
+ * @Return:
+ * @Descr: Expands the cipher key into the encryption key schedule
+ ***********************************************************************/
+static int dw_aes_set_encrypt_key(const unsigned char * user_key,
+ const int bits, struct aes_key_st * key)
+{
+ u32 *rk;
+ int i = 0;
+ u32 temp;
+
+ DBG_FN(DBG_SECURITY);
+
+ if (!user_key || !key)
+ return -1;
+ if (bits != 128 && bits != 192 && bits != 256)
+ return -2;
+
+ rk = (u32*)key->rd_key;
+
+ rk[0] = GETU32(user_key );
+ rk[1] = GETU32(user_key + 4);
+ rk[2] = GETU32(user_key + 8);
+ rk[3] = GETU32(user_key + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(user_key + 16);
+ rk[5] = GETU32(user_key + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(user_key + 24);
+ rk[7] = GETU32(user_key + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (te4[(temp >> 24) ] & 0xff000000) ^
+ (te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_ccmp_set_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static u8 dw_ccmp_set_key(struct dw_priv *priv, int id, u8 *data, int len, u8 *seq)
+{
+ ccmp_key_t *key;
+
+ DBG_FN(DBG_SECURITY);
+
+ key = &priv->aeskeys[id];
+
+ if (len == CCMP_KEY_SIZE)
+ {
+ CLEAR(*key);
+ key->valid = 1;
+ /* Set sequence counter if given */
+ if (seq)
+ key->rx_pn = dw_to_48(GET32(seq+2), GET16(seq));
+
+ /* Store only AES key schedule for this key */
+ dw_aes_set_encrypt_key(data, AES_BITS, &key->rk);
+ }
+ else if (len == 0) {
+ key->valid = 0;
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_key(int index, unsigned long *key)
+{
+ DBG_FN(DBG_SECURITY);
+
+ if (!dw_hw_aes_wait())
+ goto error;
+
+ /* Set key load mode */
+ dw_iowrite32(0x14 | index, HW_AES_MODE);
+
+ /* Write key to hw */
+ dw_hw_aes_write_fifo_noswap(key, 4*44);
+
+ return 0;
+
+error:
+ return -1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_tx_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_tx_key(struct dw_priv *priv, int index)
+{
+ ccmp_key_t *key;
+
+ DBG_FN(DBG_SECURITY);
+
+ if (!(priv->ieee->sec.flags & (1 << index)))
+ return 0;
+
+ /* expand key */
+ if (0 == dw_ccmp_set_key(priv, index, priv->ieee->sec.keys[index], priv->ieee->sec.key_sizes[index], 0)) {
+ printk(KERN_ERR "Error while expanding tx Key!!!\n");
+ return -1;
+ }
+ key = &priv->aeskeys[index];
+
+ return dw_send_key(index, key->rk.rd_key);
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_keys
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_keys(struct dw_priv *priv)
+{
+ ccmp_key_t *key;
+ int i, len, err = 0;
+
+ DBG_FN(DBG_SECURITY);
+ REQUIRE_LOCKED(priv);
+
+ /* Note: AES keys cannot be set for multiple times.
+ * Only set it at the first time. */
+ for (i = 0; i < 4; i++) {
+ key = &priv->aeskeys[i];
+ len = priv->ieee->sec.key_sizes[i];
+ if (!(priv->ieee->sec.flags & (1 << i))) {
+ continue;
+ }
+
+ /* expand key */
+ if (0 == dw_ccmp_set_key(priv, i, priv->ieee->sec.keys[i], len, 0)) {
+ printk(KERN_ERR "Error while expanding Key!!!\n");
+ continue;
+ }
+
+ if (dw_send_key(i, key->rk.rd_key)<0)
+ err = -1;
+ }
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_set_hwcrypto_keys
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static void dw_set_hwcrypto_keys(struct dw_priv *priv)
+{
+ int err = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ if (dw_send_tx_key(priv, priv->ieee->sec.active_key) < 0)
+ err = -1;
+
+ if (!dw_sw_aes)
+ if (dw_send_keys(priv) < 0)
+ err = -1;
+ break;
+ default:
+ break;
+ }
+ if (err < 0)
+ printk(KERN_ERR "Error while sending keys to hardware!!!\n");
+}
+
+
+/***********************************************************************
+ * @Function: dw_ieee80211_set_security
+ * @Return:
+ * @Descr: set_security() callback in struct ieee80211_device
+ ***********************************************************************/
+static void dw_ieee80211_set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int i, flag;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ for (i = 0; i < 4; i++) {
+ flag = 1 << i;
+ if (sec->flags & (flag)) {
+ priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
+ priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->ieee->sec.flags &= ~(flag);
+ else {
+ memcpy(priv->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ priv->ieee->sec.flags |= (flag);
+ }
+ } else if (sec->level != SEC_LEVEL_1)
+ priv->ieee->sec.flags &= ~(flag);
+ }
+
+ if (sec->flags & SEC_ACTIVE_KEY) {
+ if (sec->active_key <= 3) {
+ priv->ieee->sec.active_key = sec->active_key;
+ priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->ieee->sec.auth_mode != sec->auth_mode)) {
+ priv->ieee->sec.auth_mode = sec->auth_mode;
+ priv->ieee->sec.flags |= SEC_AUTH_MODE;
+ }
+
+ if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
+ priv->ieee->sec.flags |= SEC_ENABLED;
+ priv->ieee->sec.enabled = sec->enabled;
+ }
+
+ if (sec->flags & SEC_ENCRYPT)
+ priv->ieee->sec.encrypt = sec->encrypt;
+
+ if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
+ priv->ieee->sec.level = sec->level;
+ priv->ieee->sec.flags |= SEC_LEVEL;
+ }
+
+ if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
+ dw_set_hwcrypto_keys(priv);
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+}
+
+
+/***********************************************************************
+ * @Function: dw_softmac_set_bssid_filter
+ * @Return:
+ * @Descr: Set BSS mode and IDs (irq)
+ ***********************************************************************/
+static void dw_softmac_set_bssid_filter(struct net_device *dev, const u8 *bssid)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct ieee80211softmac_network *net;
+ int i;
+ int was_locked = spin_is_locked(&priv->lock);
+ unsigned long flags = 0;
+ int last_basic = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (!was_locked)
+ dw_lock(priv, flags);
+
+ priv->beacon_ready = 0;
+
+ /* Set BSSID in hardware */
+ dw_hw_memcpy_to(HW_BSSID0, bssid, ETH_ALEN);
+
+ if (priv->softmac->associnfo.associated) {
+ /* Set SSID in hardware */
+ dw_iowrite32(priv->softmac->associnfo.associate_essid.len |
+ 0x000e0000, HW_SSID_LEN);
+ /*TODO set rates*/
+
+ /* get ratesinfo from assocnetwork */
+ net = ieee80211softmac_get_network_by_essid_locked(priv->softmac, &priv->softmac->associnfo.associate_essid);
+
+ if (net) {
+ /* provide only the subset of the rates we support */
+ priv->ap_ri.count = 0;
+
+ for (i=0; i < net->supported_rates.count; i++) {
+ int j = 0;
+ for (j = 0; j < ARRAY_SIZE(dw_rates); j++) {
+ if ((net->supported_rates.rates[ i ] & 0x7F) == (dw_rates[ j ] & 0x7F)) {
+ priv->ap_ri.rates[ priv->ap_ri.count++ ] = net->supported_rates.rates[ i ];
+
+ break;
+ }
+ }
+ }
+ }
+
+
+ dw_hw_memcpy_to(HW_SSID, priv->softmac->associnfo.associate_essid.data, priv->softmac->associnfo.associate_essid.len);
+ /* Disable IBSS mode */
+ //dw_iocleanbits32(HW_MAC_CONTROL, CTRL_IBSS | CTRL_BEACONTX);
+ } else {
+ /* reset it */
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ priv->ap_ri.rates[ i ] = dw_rates[ i ];
+ priv->ap_ri.count = i;
+ }
+
+ /* determine equal or lower basic rate for the rate being used.
+ The acknowledge of AP is send with
+ api_ri.rates[ basics[ data_rate_index ] ]*/
+ for (i = 0; i < ARRAY_SIZE(priv->tx.basics); i++) {
+ if ((priv->ap_ri.rates[ i ] & IEEE80211_BASIC_RATE_MASK) == IEEE80211_BASIC_RATE_MASK)
+ last_basic = i;
+ priv->tx.basics[ i ] = last_basic;
+ }
+
+ priv->softmac->txrates.default_rate = BASIC_RATE_MASK(priv->ap_ri.rates[ priv->rate.index ]);
+ DBG(DBG_UPDATE_RATE, "Rate set to %i", priv->softmac->txrates.default_rate);
+ priv->rate.tx_data_any = priv->rate.tx_data_ack = 0; // reset counter so we work on the next time slice
+
+ if (!was_locked)
+ dw_unlock(priv, flags);
+
+}
+
+/***********************************************************************
+ * @Function: dw_set_channel
+ * @Return:
+ * @Descr: Select a channel
+ ***********************************************************************/
+static void dw_set_channel(dw_priv_t* priv, u8 channel)
+{
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+ REQUIRE_LOCKED(priv);
+
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ priv->beacon_ready = 0;
+
+ /* Set frequency divider for channel */
+ dw_hw_write_rf (1, freq_table[channel].integer);
+ dw_hw_write_rf (2, freq_table[channel].fraction);
+
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ /* Filter calibration */
+ dw_hw_write_rf (0, 0x25f9c);
+ udelay (10);
+
+ /* VCO calibration */
+ dw_hw_write_rf (0, 0x25f9a);
+ udelay (80);
+
+ /* Mode Control - RX/TX mode */
+ dw_hw_write_rf (0, 0x25f94);
+
+ /* Allow the trannsceiver to settle in the new mode of operation */
+ udelay (10);
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ /* VCO Calibration */
+ dw_hw_write_rf (0, 0x27fda);
+ udelay(40);
+
+ /* Set frequency divider for channel again */
+ dw_hw_write_rf (1, freq_table[channel].integer);
+ dw_hw_write_rf (2, freq_table[channel].fraction);
+
+ /* Mode Control - RX/TX mode */
+ dw_hw_write_rf (0, 0x27fd4);
+
+ dw_hw_set_vco(channel);
+#endif
+ priv->channel = channel;
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXEN);
+}
+
+/***********************************************************************
+ * @Function: dw_softmac_set_chan
+ * @Return:
+ * @Descr: Select a channel)
+ ***********************************************************************/
+static void dw_softmac_set_chan(struct net_device* dev, u8 channel)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ //struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ //if (IW_MODE_ADHOC == priv->ieee->iw_mode && !mac->scanning)
+ // printk("Selected channel = %d\n", channel);
+
+ /* we really don't want to change the channel while there is something
+ * in the queue for a different channel */
+ dw_tx_wait_for_idle_and_pause(priv);
+
+ dw_lock(priv, flags);
+ dw_set_channel( priv, channel);
+ dw_unlock(priv, flags);
+
+ dw_tx_continue_queue(priv);
+}
+
+
+/***********************************************************************
+ * @Function: dw_geo_init
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_geo_init(dw_priv_t *priv)
+{
+ struct ieee80211_geo *geo;
+ struct ieee80211_channel *chan;
+ int i;
+ u8 channel;
+ const char *iso_country;
+
+ DBG_FN(DBG_INIT);
+
+ geo = kzalloc(sizeof(*geo), GFP_KERNEL);
+ if (!geo)
+ return -ENOMEM;
+
+ iso_country = "XX";
+
+ for (i = 0, channel = IEEE80211_52GHZ_MIN_CHANNEL;
+ channel <= IEEE80211_52GHZ_MAX_CHANNEL; channel++) {
+ chan = &geo->a[i++];
+ chan->freq = dw_channel_to_freq_a(channel);
+ chan->channel = channel;
+ }
+ geo->a_channels = i;
+ for (i = 0, channel = IEEE80211_24GHZ_MIN_CHANNEL;
+ channel <= IEEE80211_24GHZ_MAX_CHANNEL; channel++) {
+ chan = &geo->bg[i++];
+ chan->freq = dw_channel_to_freq_bg(channel);
+ chan->channel = channel;
+ }
+ geo->bg_channels = i;
+ memcpy(geo->name, iso_country, 2);
+ if (0 /*TODO: Outdoor use only */)
+ geo->name[2] = 'O';
+ else if (0 /*TODO: Indoor use only */)
+ geo->name[2] = 'I';
+ else
+ geo->name[2] = ' ';
+ geo->name[3] = '\0';
+
+ ieee80211_set_geo(priv->ieee, geo);
+ kfree(geo);
+
+ return 1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_get_name
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_get_name(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ strcpy(data->name, "IEEE 802.11b/g");
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_get_encode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ err = ieee80211_wx_get_encode(priv->ieee, info, data, extra);
+
+ return err;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_get_encodeext
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ err = ieee80211_wx_get_encodeext(priv->ieee, info, data, extra);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_channelfreq
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_channelfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ u8 channel;
+ int err = -EINVAL;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_tx_wait_for_idle_and_pause(priv);
+
+ dw_lock(priv, flags);
+
+ if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
+ channel = data->freq.m;
+ } else {
+ channel = ieee80211_freq_to_channel(priv->ieee, data->freq.m);
+ }
+ if (ieee80211_is_valid_channel(priv->ieee, channel)) {
+ dw_set_channel(priv, channel);
+ err = 0;
+ }
+
+ dw_unlock(priv, flags);
+
+ dw_tx_continue_queue(priv);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_channelfreq
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_channelfreq(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ data->freq.e = 1;
+ data->freq.m = ieee80211_channel_to_freq(priv->ieee, priv->channel) * 100000;
+ data->freq.flags = IW_FREQ_FIXED;
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_set_mode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_mode(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->iw_mode = data->mode;
+
+ if (IW_MODE_ADHOC == priv->ieee->iw_mode) {
+ if (priv->channel)
+ priv->adhoc.channel = priv->channel;
+ } else {
+ dw_iocleanbits32(HW_MAC_CONTROL, CTRL_IBSS|CTRL_BEACONTX); /* Disable IBSS mode */
+ }
+
+ priv->beacon_ready = 0;
+ priv->beacon_body_length=0;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_mode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_mode(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->mode = priv->ieee->iw_mode;
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_rangeparams
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_rangeparams(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ const struct ieee80211_geo *geo;
+ int i, j, level;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /*TODO: What about 802.11b? Have to meassure!!! */
+ /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
+ range->throughput = 27 * 1000 * 1000;
+ range->min_nwid = 0x0000;
+ range->max_nwid = 0x0020;
+
+ range->max_qual.qual = 100;
+ range->max_qual.level = MAC_RSSI_MAX;
+ range->max_qual.noise = 0;
+ range->max_qual.updated =
+ IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED;
+
+ range->avg_qual.qual = 0;
+ range->avg_qual.level = 0;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
+
+ range->min_rts = DW_MIN_RTS_THRESHOLD;
+ range->max_rts = DW_MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = WEP_KEYS;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 19;
+
+ range->enc_capa = IW_ENC_CAPA_WPA |
+ IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP |
+ IW_ENC_CAPA_CIPHER_CCMP;
+
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ range->bitrate[ i ] = RATE_IN_BS(BASIC_RATE_MASK(dw_rates[ i ]));
+ range->num_bitrates = i;
+
+ geo = ieee80211_get_geo(priv->ieee);
+ range->num_channels = geo->bg_channels;
+ j = 0;
+ for (i = 0; i < geo->bg_channels; i++) {
+ if (j == IW_MAX_FREQUENCIES)
+ break;
+ range->freq[j].i = j + 1;
+ range->freq[j].m = geo->bg[i].freq * 100000;
+ range->freq[j].e = 1;
+ j++;
+ }
+ range->num_frequency = j;
+
+ /* retry limit capabilities */
+ range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = IW_RETRY_LIFETIME;
+
+ /* I don't know the range.*/
+ range->min_retry = 1;
+ range->max_retry = 65535;
+ range->min_r_time = 1024;
+ range->max_r_time = 65535 * 1024;
+
+ /* txpower is supported in dBm's */
+// if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ range->txpower_capa = IW_TXPOW_DBM;
+ range->num_txpower = IW_MAX_TXPOWER;
+ for (i = 0, level = (DW_TX_POWER_MAX_DBM * 16);
+ i < IW_MAX_TXPOWER;
+ i++, level -=
+ ((DW_TX_POWER_MAX_DBM -
+ DW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
+ range->txpower[i] = level / 16;
+// } else {
+// range->txpower_capa = 0;
+// range->num_txpower = 0;
+// }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_get_wireless_stats
+ * @Return:
+ * @Descr: Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS
+ ***********************************************************************/
+static struct iw_statistics *dw_get_wireless_stats(struct net_device *dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ wstats = &priv->wstats;
+ if (!mac->associnfo.associated) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->discard.nwid = 0;
+ wstats->discard.code = 0;
+ wstats->discard.fragment = 0;
+ wstats->discard.misc = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ } else {
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ /* fill in the real statistics when iface associated */
+ wstats->qual.qual =
+ (100 *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
+ (priv->ieee->perfect_rssi - priv->ieee->networks->stats.rssi) *
+ (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
+ 62 * (priv->ieee->perfect_rssi -
+ priv->ieee->networks->stats.rssi))) /
+ ((priv->ieee->perfect_rssi -
+ priv->ieee->worst_rssi) * (priv->ieee->perfect_rssi -
+ priv->ieee->worst_rssi));
+ /*
+ * !TODO. Seems they are not calculated. Take
+ * one from the last frame */
+ wstats->qual.level = priv->ieee->networks->stats.signal;
+ wstats->qual.noise = priv->ieee->networks->stats.noise;
+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED;
+ wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
+ wstats->discard.retries = priv->ieee->ieee_stats.tx_retry_limit_exceeded;
+ wstats->discard.nwid = priv->ieee->ieee_stats.tx_discards_wrong_sa;
+ wstats->discard.fragment = priv->ieee->ieee_stats.rx_fragments;
+ wstats->discard.misc = priv->wstats.discard.misc;
+ wstats->miss.beacon = 0; /* FIXME */
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ }
+ return wstats;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_nick
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ size_t len;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
+ memcpy(priv->nick, extra, len);
+ priv->nick[len] = '\0';
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_nick
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_nick(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ size_t len;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ len = strlen(priv->nick) + 1;
+ memcpy(extra, priv->nick, len);
+ data->data.length = (__u16)len;
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_rts
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_rts(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = -EINVAL;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ if (data->rts.disabled) {
+ priv->ieee->rts = DW_MAX_RTS_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->rts.value >= DW_MIN_RTS_THRESHOLD &&
+ data->rts.value <= DW_MAX_RTS_THRESHOLD) {
+ priv->ieee->rts = data->rts.value;
+ err = 0;
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_rts
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_rts(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->rts.value = priv->ieee->rts;
+ data->rts.fixed = 0;
+ data->rts.disabled = (priv->ieee->rts == DW_MAX_RTS_THRESHOLD);
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_rate
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ s32 in_rate = data->bitrate.value;
+ int err = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+
+ if (in_rate == -1)
+ /* auto */
+ atomic_set(&priv->fix_rate, 0);
+ else
+ atomic_set(&priv->fix_rate, 1);
+
+ err = ieee80211softmac_wx_set_rate(dev, info, data, extra);
+
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_frag
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = -EINVAL;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ if (data->frag.disabled) {
+ priv->ieee->fts = MAX_FRAG_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->frag.value >= MIN_FRAG_THRESHOLD &&
+ data->frag.value <= MAX_FRAG_THRESHOLD) {
+ priv->ieee->fts = data->frag.value & ~0x1;
+ err = 0;
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_frag
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->frag.value = priv->ieee->fts;
+ data->frag.fixed = 0;
+ data->frag.disabled = (priv->ieee->fts == MAX_FRAG_THRESHOLD);
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_set_xmitpower
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_xmitpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = 0, value;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL;
+ if (data->txpower.fixed == 0) return -EINVAL;
+ if (data->txpower.value < DW_TX_POWER_MIN_DBM || data->txpower.value > DW_TX_POWER_MAX_DBM) return -EINVAL;
+
+ value = data->txpower.value;
+ dw_lock(priv, flags);
+ err = dw_set_tx_power(priv, value);
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_xmitpower
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_xmitpower(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ data->txpower.fixed = 1;
+ data->txpower.value = priv->tx_power;
+ data->txpower.flags = IW_TXPOW_DBM;
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_retry
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_retry(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
+ return -EINVAL;
+
+ dw_lock(priv, flags);
+ if (wrqu->retry.flags & IW_RETRY_MIN)
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ else if (wrqu->retry.flags & IW_RETRY_MAX)
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ else {
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ }
+
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_retry
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_retry(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ wrqu->retry.disabled = 0;
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ dw_unlock(priv, flags);
+ return -EINVAL;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else if (wrqu->retry.flags & IW_RETRY_MIN) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.value = priv->short_retry_limit;
+ } else {
+ wrqu->retry.flags = IW_RETRY_LIMIT;
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+#ifdef WX
+# undef WX
+#endif
+#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
+static const iw_handler dw_wx_handlers[] = {
+ /* Wireless Identification */
+ WX(SIOCGIWNAME) = dw_get_name,
+ /* Basic operations */
+ WX(SIOCSIWFREQ) = dw_wx_set_channelfreq,
+ WX(SIOCGIWFREQ) = dw_wx_get_channelfreq,
+ WX(SIOCSIWMODE) = dw_wx_set_mode,
+ WX(SIOCGIWMODE) = dw_wx_get_mode,
+ /* Informative stuff */
+ WX(SIOCGIWRANGE) = dw_wx_get_rangeparams,
+ /* Spy support (statistics per MAC address - used for Mobile IP support) */
+ WX(SIOCSIWSPY) = iw_handler_set_spy,
+ WX(SIOCGIWSPY) = iw_handler_get_spy,
+ WX(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
+ WX(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
+ /* Access Point manipulation */
+ WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
+ WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
+ WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
+ WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
+ /* 802.11 specific support */
+ WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
+ WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
+ WX(SIOCSIWNICKN) = dw_wx_set_nick,
+ WX(SIOCGIWNICKN) = dw_wx_get_nick,
+ /* Other parameters */
+ WX(SIOCSIWRATE) = dw_wx_set_rate,
+ WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
+ WX(SIOCSIWRTS) = dw_wx_set_rts,
+ WX(SIOCGIWRTS) = dw_wx_get_rts,
+ WX(SIOCSIWFRAG) = dw_wx_set_frag,
+ WX(SIOCGIWFRAG) = dw_wx_get_frag,
+ WX(SIOCSIWTXPOW) = dw_wx_set_xmitpower,
+ WX(SIOCGIWTXPOW) = dw_wx_get_xmitpower,
+ WX(SIOCSIWRETRY) = dw_wx_set_retry,
+ WX(SIOCGIWRETRY) = dw_wx_get_retry,
+ /* Encoding */
+ WX(SIOCSIWENCODE) = dw_wx_set_encode,
+ WX(SIOCGIWENCODE) = dw_wx_get_encode,
+ WX(SIOCSIWENCODEEXT) = dw_wx_set_encodeext,
+ WX(SIOCGIWENCODEEXT) = dw_wx_get_encodeext,
+ /* Power saving */
+/*TODO WX(SIOCSIWPOWER) = dw_wx_set_power, */
+/*TODO WX(SIOCGIWPOWER) = dw_wx_get_power, */
+ WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
+ WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
+ WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme,
+ WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
+ WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
+};
+#undef WX
+
+#ifdef NEED_PRIVATE_HANDLER
+# include "dw_priv_handler.c"
+#else
+static const iw_handler dw_priv_wx_handlers[] = {};
+static const struct iw_priv_args dw_priv_wx_args[] = {};
+#endif
+
+const struct iw_handler_def dw_wx_handlers_def = {
+ .standard = dw_wx_handlers,
+ .num_standard = ARRAY_SIZE(dw_wx_handlers),
+ .num_private = ARRAY_SIZE(dw_priv_wx_handlers),
+ .num_private_args = ARRAY_SIZE(dw_priv_wx_args),
+ .private = dw_priv_wx_handlers,
+ .private_args = dw_priv_wx_args,
+ .get_wireless_stats = dw_get_wireless_stats,
+};
+
+/***********************************************************************
+ * @Function: dw_close
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_close(struct net_device* dev)
+{
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ struct list_head* it;
+
+ DBG_FN(DBG_INIT);
+
+ ieee80211softmac_stop(dev);
+
+ /* don't disable anything before softmac_stop. Maybe SoftMAC will run a
+ * disassoc cycle somewhat later. */
+ del_timer_sync(&priv->management_timer);
+
+ cancel_delayed_work(&priv->beacon_work);
+
+ disable_irq(dev->irq);
+
+ tasklet_disable(&priv->rx.tasklet);
+ /* from this point, rx queue.filled is empty, and it is not paused */
+ tasklet_disable(&priv->tx.tasklet);
+
+ dw_lock(priv, flags);
+
+ priv->tx.data_pending_ack = NULL;
+
+ /* delete unsend frames */
+ list_for_each(it, &priv->tx.queued) {
+ dw_frame_tx_t* frame = list_entry(it, dw_frame_tx_t, list);
+ ieee80211_txb_free(frame->s.txb);
+ frame->s.txb = NULL;
+ }
+ dw_list_move(&priv->tx.free, &priv->tx.queued);
+
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ dw_iowrite32(0, HW_INTR_MASK);
+ dw_iowrite32(dw_ioread32(HW_INTR_STATUS), HW_INTR_STATUS);
+
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_open
+ * @Return:
+ * @Descr: init/reinit
+ ***********************************************************************/
+static int dw_open(struct net_device* dev)
+{
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INIT);
+
+ dw_reset_rx_dups(priv);
+
+ priv->tx.data_pending_ack = NULL;
+
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts = 0;
+
+ tasklet_enable(&priv->rx.tasklet);
+ tasklet_enable(&priv->tx.tasklet);
+
+ ieee80211softmac_start(dev);
+
+ ieee80211softmac_set_rates(dev, ARRAY_SIZE(dw_rates), (u8*) dw_rates); /* cast away const */
+
+ /* softmac sets user_rate to 24MB, but we start much slower to work
+ always */
+ priv->softmac->txrates.default_rate = priv->softmac->txrates.user_rate = IEEE80211_CCK_RATE_1MB;
+
+ dw_lock(priv, flags);
+
+ priv->beacon_ready = 0;
+ INIT_DELAYED_WORK(&priv->beacon_work, dw_beacon_start);
+ priv->beacon_body_length=0;
+ priv->adhoc.channel = DW_IBSS_DEFAULT_CHANNEL;
+
+ /* acknowledge pending interrupts */
+ dw_iowrite32(dw_ioread32(HW_INTR_STATUS), HW_INTR_STATUS);
+ dw_iowrite32(INTR_ALL, HW_INTR_MASK);
+ dw_iowrite32(HW_AES_MODE_1, HW_AES_MODE );
+ mod_timer(&priv->management_timer, jiffies + MANAGEMENT_JIFFIES);
+
+ enable_irq(dev->irq);
+
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXEN);
+
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_set_multicast_list
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static void dw_set_multicast_list(struct net_device* dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ if (dev->flags & IFF_PROMISC) {
+ dw_iosetbits32(HW_MAC_CONTROL, CTRL_PROMISC);
+ } else {
+ dw_iocleanbits32(HW_MAC_CONTROL, CTRL_PROMISC);
+ }
+ dw_unlock(priv, flags);
+}
+
+/***********************************************************************
+ * @Function: dw_net_get_stats
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static struct net_device_stats * dw_net_get_stats(struct net_device *dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+
+ DBG_FN(DBG_INTERFACE);
+
+ return &priv->ieee->stats;
+}
+
+/**
+ * dw_probe - probe for resources and for chip being present
+ *
+ * @return <0 on failure
+ */
+static int __init dw_probe(struct platform_device* pdev)
+{
+ struct net_device* dev = NULL;
+ int err = -ENODEV;
+
+ DBG_FN(DBG_INIT);
+
+ /* Create the network device object. */
+ dev = alloc_ieee80211softmac(sizeof(dw_priv_t));
+ if (NULL == dev) {
+ ERROR(": Couldn't alloc_ieee80211softmac ");
+ err = -ENOMEM;
+ goto error_alloc;
+ }
+ platform_set_drvdata(pdev, dev);
+ dev->irq = INTR_ID;
+
+ if (dev_alloc_name(dev, "wlan%d") < 0) {
+ ERROR("Couldn't get name");
+ goto error_name;
+ }
+
+ /* allocate resources and map memory */
+ if (gpio_request(PIN_LED, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_LED);
+ goto error_pin_led;
+ }
+ if (gpio_request(PIN_ACTIVITY_LED, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_ACTIVITY_LED);
+ goto error_pin_activity_led;
+ }
+ if (gpio_request(PIN_INTR, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_INTR);
+ goto error_pin_intr;
+ }
+
+ err = request_resource(&iomem_resource, pdev->resource);
+ if (err) {
+ ERROR("Memory already in used: 0x%08x...0x%08x",
+ pdev->resource->start, pdev->resource->end);
+ goto error_res;
+ }
+
+ vbase = ioremap(pdev->resource->start,
+ pdev->resource->end - pdev->resource->start);
+ if (NULL == vbase) {
+ ERROR("ioremap failed");
+ err = -ENOMEM;
+ goto error_remap;
+ }
+ dev->base_addr = (int) vbase;
+
+ /* test if fpga is programmed */
+ if (dw_ioread32(HW_VERSION) == 0xffffffff) {
+ ERROR("FPGA not present");
+ goto error_fpga;
+ }
+
+ /* all resources available, initializw remaining stuff */
+ dev->open = dw_open;
+ dev->stop = dw_close;
+ dev->set_multicast_list = dw_set_multicast_list;
+ dev->wireless_handlers = &dw_wx_handlers_def;
+ dev->get_stats = dw_net_get_stats;
+
+ /* initialize hardware/private stuff */
+ err = dw_start_dev(pdev);
+ if (err)
+ goto error;
+
+ return 0;
+
+error_fpga:
+ iounmap(vbase);
+ vbase = NULL;
+error_remap:
+ release_resource(pdev->resource);
+error_res:
+ gpio_free(PIN_INTR);
+error_pin_intr:
+ gpio_free(PIN_ACTIVITY_LED);
+error_pin_activity_led:
+ gpio_free(PIN_LED);
+error_pin_led:
+error_name:
+ /* the IRQ hasn't been setup, so unwind a part and don't go to
+ * dw_remove */
+ free_ieee80211softmac(dev);
+error_alloc:
+ return err;
+
+error:
+ /* dev memory is setup, so we can use the remove function */
+ dw_remove(pdev);
+
+ return err;
+}
+
+/***********************************************************************
+ * @Function: dw_init_module
+ * @Return: 0 if successfull; -ENODEV if interrupt or device is not available
+ * @Descr: initialize module
+ ***********************************************************************/
+static int __init dw_init_module(void)
+{
+ int err;
+
+ DBG_FN(DBG_INIT);
+
+ printk(KERN_INFO "%s\n", dw_version);
+
+ if (dw_sw_aes)
+ printk(KERN_NOTICE "AES encoding/decoding is done in software\n");
+
+ err = platform_device_register(&dw_device);
+ if (err) {
+ ERROR("Device Register Failed");
+ goto error;
+ }
+
+ err = platform_driver_register(&dw_driver);
+ if (err) {
+ ERROR("Driver Register Failed");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_cleanup_module
+ * @Return: nothing
+ * @Descr: deinitializes module. Actually will never be called due to
+ * MOD_INC_USE_COUNT > 1. Will be fixed if hardware watchdog can be disabled.
+ ***********************************************************************/
+static void __exit dw_cleanup_module(void)
+{
+ DBG_FN(DBG_INIT);
+
+ platform_driver_unregister(&dw_driver);
+ platform_device_unregister(&dw_device);
+}
+
+#ifndef MODULE
+/**
+ * dw_sw_cmdline - parses kernel commandline
+ *
+ * @return always 1
+ */
+static int __init dw_sw_cmdline(char* arg)
+{
+ if ((NULL != arg) && *arg)
+ dw_sw_aes = simple_strtoul(arg, NULL, 10);
+
+ return 1;
+}
+#endif /* MODULE */
+
+module_init(dw_init_module);
+module_exit(dw_cleanup_module);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bernd Westermann");
+MODULE_AUTHOR("Markus Pietrek");
+MODULE_AUTHOR("Miriam Ruiz");
+MODULE_DESCRIPTION("WiFi driver for ConnectCore Wi-9C");
+
+module_param(dw_sw_aes, int, 0x00);
+MODULE_PARM_DESC(dw_sw_aes, "If set, AES encoding/decoding is done in software.");
+
+module_param(dw_cfg_vco, int, 0x00);
+MODULE_PARM_DESC(dw_cfg_vco, "Config Value of VCO (0 use built-in)");
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+module_param(dw_dbg_level, int, 0x00);
+MODULE_PARM_DESC(dw_dbg_level, "debug output level");
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#ifndef MODULE
+__setup("dw_sw_aes=", dw_sw_cmdline);
+#endif /* MODULE */
diff --git a/drivers/net/wireless/digi_wi_g.h b/drivers/net/wireless/digi_wi_g.h
new file mode 100644
index 000000000000..95bc6f06b1a5
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g.h
@@ -0,0 +1,660 @@
+/*******************************************************************************
+ * digi_wi_g.h
+ *
+ * Support for DIGI Wireless Module.
+ *
+ * $Id: digi_wi_g.h,v 1.64 2008-02-13 11:02:34 mruiz Exp $
+ * @Author: Bernd Westermann
+ * @References: [1] NET+OS code mac_hw_wi9c.c
+ *
+ * Copyright (C) 2007 by FS Forth-Systeme GmbH
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *******************************************************************************
+ * History:
+ * 14/03/06 Initial Version
+ *
+ *******************************************************************************/
+
+#ifndef FS_DIGI_WI_G_DRIVER_H
+#define FS_DIGI_WI_G_DRIVER_H
+
+#include <net/ieee80211.h>
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+
+#include <net/iw_handler.h> /* New driver API */
+#include <mach/regs-bbu.h>
+#include <mach/hardware.h> /* BBUS_CLK_FREQ */
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#ifdef dprintkl
+#undef dprintkl
+#endif
+#ifdef dprintk
+#undef dprintk
+#endif
+
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 60
+#define DRIVER_NAME "digi_wi_g"
+
+/* for LEDs, update rate etc. */
+/* LED time is 50ms */
+#define MANAGEMENT_TIME_MS 50
+#define MANAGEMENT_JIFFIES ( ( MANAGEMENT_TIME_MS * HZ ) / 1000 )
+/* update rate only every 500ms */
+#define MANAGEMENT_TICKS_FOR_UPDATE ( 500 / MANAGEMENT_TIME_MS )
+#define TX_TIMEOUT (4*HZ/2)
+#define ACK_TIMEOUT (HZ/2)
+#define BEACON_TIMEOUT (HZ)
+#define RESCAN_TIMEOUT (15*HZ)
+
+#define MAX_RECONNECTION_ATTEMPTS 5
+
+/* Min/max successful intervals to increase rate */
+#define THRESH_MIN 1
+#define THRESH_MAX 10
+
+#define TX_QUEUE_SIZE 8 /* not tuned yet */
+#define TX_MAX_FRAGS 16
+#define RX_QUEUE_SIZE 44 /* one ICMP frame */
+
+#define RX_DUP_SENDER_SIZE 16 /* somewhat lower than MAX_NETWORK_COUNT
+ * because in worst case we need to scan
+ * RX_DUP_SENDER_SIZE until we found the
+ * active one */
+
+#define DW_PIO_MAXTXPACKETS 4
+
+#define DW_DEFAULT_SHORT_RETRY_LIMIT 7
+#define DW_DEFAULT_LONG_RETRY_LIMIT 4
+#define AES_BUSY_TIMEOUT 1000000 /* in ns */
+
+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+# define assert(expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ printk(KERN_ERR PFX "ASSERTION FAILED (%s) \nat: %s:%d:%s()\n", \
+ #expr, __FILE__, __LINE__, __FUNCTION__); \
+ } \
+ } while (0)
+# define dprintkl printkl
+# define dprintk(f, x...) do { printk(f ,##x); } while (0)
+# define COMPILE_TIME " from ("__TIME__ ") "
+#else
+# define assert(expr) do { /* nothing */ } while (0)
+# define dprintkl(f, x...) do { /* nothing */ } while (0)
+# define dprintk(f, x...) do { /* nothing */ } while (0)
+# define COMPILE_TIME ""
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#define DIGI_WIRELESS_G_REG_PHYS NS9XXX_CSxSTAT_PHYS(2)
+
+/* Hardware register defines */
+#define MAC_BASE_PHYS DIGI_WIRELESS_G_REG_PHYS
+#define MAC_BASE_SIZE 0xe0000/*100U*/ /* Register segment size */
+#define MAC_MASK 0xffffc001 /* Size mask and enable bit */
+
+/* Baseband control registers */
+#define HW_VERSION (0x00) /* Version */
+#define HW_GEN_CONTROL (0x04) /* General control */
+#define HW_GEN_STATUS (0x08) /* General status */
+#define HW_RSSI_AES (0x0c) /* RSSI and AES status */
+#define HW_INTR_MASK (0x10) /* Interrupt mask */
+#define HW_INTR_STATUS (0x14) /* Interrupt status */
+#define HW_SPI_DATA (0x18) /* RF SPI data register */
+#define HW_SPI_CONTROL (0x1c) /* RF SPI control register */
+#define HW_DATA_FIFO (0x20) /* Data FIFO */
+#define HW_AES_FIFO (0x30) /* AES FIFO */
+#define HW_AES_MODE (0x38) /* AES mode */
+
+/* MAC control registers */
+#define HW_STAID0 (0x40) /* Station ID (6 bytes) */
+#define HW_STAID1 (0x44)
+#define HW_BSSID0 (0x48) /* BSS ID (6 bytes) */
+#define HW_BSSID1 (0x4c)
+#define HW_SSID_LEN (0x50) /* SSID length (8 bits) */
+#define HW_BACKOFF (0x54) /* Backoff period (16 bits) */
+#define HW_LISTEN (0x58) /* Listen interval (16 bits), CFP (8 bits), DTIM (8 bits) */
+#define HW_CFP_ATIM (0x5c) /* CFP max duration/ATIM period (16 bits), beacon interval (16 bits) */
+#define HW_MAC_STATUS (0x60) /* MAC status (8 bits) */
+#define HW_MAC_CONTROL (0x64) /* MAC control (8 bits) */
+#define HW_REMAIN_BO (0x68) /* Remaining backoff (16 bits) */
+#define HW_BEACON_BO (0x6c) /* Beacon backoff (16 bits), beacon mask (8 bits) */
+#define HW_SSID (0x80) /* Service set ID (32 bytes) */
+
+/* FIFO sizes in bytes */
+#define HW_TX_FIFO_SIZE 1792
+#define HW_RX_FIFO_SIZE 2048
+
+
+/* General control register bits */
+#define GEN_RXEN 0x00000001 /* Receive enable */
+#define GEN_ANTDIV 0x00000002 /* Antenna diversity */
+#define GEN_ANTSEL 0x00000004 /* Antenna select */
+#define GEN_5GEN 0x00000008 /* 5 GHz band enable */
+#define GEN_SHPRE 0x00000010 /* Transmit short preamble */
+#define GEN_RXFIFORST 0x00000020 /* Receive FIFO reset */
+#define GEN_TXFIFORST 0x00000040 /* Transmit FIFO reset */
+#define GEN_TXHOLD 0x00000080 /* Transmit FIFO hold */
+#define GEN_BEACEN 0x00000100 /* Beacon enable */
+#define GEN_BST 0x00001000 /* Boot status */
+#define GEN_CLKEN 0x00002000 /* Clock enable */
+#define GEN_TXFIFOEMPTY 0x00004000 /* Transmit FIFO empty */
+#define GEN_TXFIFOFULL 0x00008000 /* Transmit FIFO full */
+
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+# define GEN_INIT 0x377a0000 /* Initial state */
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+# define GEN_INIT 0x37700000 /* Initial state */
+#else
+# error "You need to choose an UBEC Transceiver Revision"
+#endif
+
+/* General status register bits */
+#define STAT_RXFE 0x00000010 /* Receive FIFO empty */
+#define SFT_RESET 0x04000000 /* WiFi Baseband soft reset */
+/* AES status register bits */
+#define AES_EMPTY 0x00010000 /* AES receive FIFO empty */
+#define AES_FULL 0x00020000 /* AES transmit FIFO full */
+#define AES_BUSY 0x00040000 /* AES engine busy */
+#define AES_MIC 0x00080000 /* AES MIC correct */
+
+/* Interrupt mask and status register bits */
+#define INTR_RXFIFO 0x00000001 /* Receive FIFO not empty */
+#define INTR_TXEND 0x00000002 /* Transmit complete */
+#define INTR_TIMEOUT 0x00000004 /* CTS/ACK receive timeout */
+#define INTR_ABORT 0x00000008 /* CTS transmit abort */
+#define INTR_TBTT 0x00000010 /* Beacon transmission time */
+#define INTR_ATIM 0x00000020 /* ATIM interval end */
+#define INTR_RXOVERRUN 0x00000040 /* Receive FIFO overrun */
+#define INTR_ALL ( INTR_RXFIFO | \
+ INTR_TIMEOUT | \
+ INTR_RXOVERRUN | \
+ INTR_TXEND )
+
+/* MAC control register bits */
+#define CTRL_TXREQ 0x00000001 /* Transmit request */
+#define CTRL_AUTOTXDIS 0x00000002 /* Auto-transmit disable */
+#define CTRL_BEACONTX 0x00000004 /* Beacon transmit enable */
+#define CTRL_PROMISC 0x00000008 /* Promiscuous mode */
+#define CTRL_IBSS 0x00000010 /* IBBS mode */
+
+#define HW_AES_MODE_0 0x00000000
+#define HW_AES_MODE_1 0x00000010
+
+/* BOOTMUX bit */
+#define BOOTMUX_LOW 0x00000001 /* Bootmux,bit 0, must go low after load */
+
+/* PIO pins */
+#define PIN_INIT 58 /* FPGA program init */
+#define PIN_INTR 65 /* Interrupt (same as done) */
+#define PIN_LED 67 /* Link status LED */
+#define PIN_ACTIVITY_LED 66 /* Link activity LED */
+#define PIN_BOOTMUX 66 /* Enables serial ports */
+
+#define INTR_ID IRQ_NS9XXX_ETHPHY /* Interrupt ID for PIN_INTR */
+
+/* Get/set/clear a PIO pin */
+#define PIO_SET(pin) ns9xxx_gpio_setpin(pin, 1)
+#define PIO_CLR(pin) ns9xxx_gpio_setpin(pin, 0)
+
+#define CW_MIN 31 /* Min contention window size */
+#define CW_MAX 1023 /* Max contention window size */
+
+/* Maximum rxsignal strength we expect to see (this is in dB) */
+#define MAC_RSSI_MAX 0x4f
+
+/* the CCK/OFDM base is 2 * Mb/s */
+#define RATE_IN_BS(x) ((x) * 500000)
+
+/* see email from Mike Schaffner from 16.03.07 (RE: Verification of
+ * initialization to mpietrek and hbujanda */
+#define VCO_DEFAULT_CHANNEL_12 0x7160
+#define VCO_DEFAULT 0x7020
+
+#define RATES_SUPPORTED 12
+
+#define AES_MAXNR 14
+
+#define CCMP_KEY_SIZE 16 /* CCMP key size */
+#define AES_BLOCK_SIZE 16
+#define EXTIV_SIZE 8 /* IV and extended IV size */
+#define MIC_SIZE 8 /* Message integrity check size */
+#define CCMP_SIZE (EXTIV_SIZE+MIC_SIZE) /* Total CCMP size */
+#define FCS_SIZE 4 // FCS (CRC-32) size
+#define DATA_SIZE 28 /* Data frame header+FCS size */
+/* Key ID byte in data frame body */
+#define EXT_IV 0x20 /* Extended IV is present */
+
+#define DW_TX_POWER_MIN_DBM 0
+#define DW_TX_POWER_MAX_DBM 15
+#define DW_TX_POWER_DEFAULT 10
+
+typedef u64 u48;
+
+struct aes_key_st {
+ unsigned long rd_key[4 *(AES_MAXNR + 1)];
+};
+
+typedef union {
+ struct {
+ u8 signal; /* (rate in 100 kbps) */
+ u8 service; /* Service: OR of SERVICE_xxx */
+ u16 length; /* Length in usecs (needs byte swap) */
+ };
+ u8 raw[4];
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_pskcck_t;
+
+typedef union {
+ struct {
+ unsigned rate :4; /* Data rate */
+ unsigned reserved :1;
+ unsigned length :12; /* Length in bytes */
+ unsigned parity :1; /* Even parity bit */
+ unsigned _pad :14; /* Service field */
+ };
+ u8 raw[4];
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_ofdm_t;
+
+/* modulation header (rx ) */
+typedef union {
+ struct {
+ unsigned mod_type : 8;
+ unsigned rssi_vga : 5;
+ unsigned rssi_lna : 2;
+ unsigned ant : 1;
+ unsigned freq_off : 16; /* Frequency offset (needs byte swap) */
+ };
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_mod_rx_t;
+
+/* modulation header ( tx ) */
+typedef union {
+ struct {
+ unsigned int mod_type :8;
+ unsigned int length :9;
+ unsigned int pad :15;
+ };
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_mod_tx_t;
+
+typedef union {
+ dw_hw_pskcck_t pskcck;
+ dw_hw_ofdm_t ofdm;
+} __attribute__((__packed__)) dw_hw_plcp_t;
+
+typedef struct {
+ dw_hw_mod_tx_t mod;
+ dw_hw_plcp_t plcp;
+} __attribute__((__packed__)) dw_hw_hdr_tx_t;
+
+typedef struct {
+ dw_hw_mod_rx_t mod;
+ dw_hw_plcp_t plcp;
+} __attribute__((__packed__)) dw_hw_hdr_rx_t;
+
+/* CCMP key data */
+
+typedef struct {
+ u8 init[AES_BLOCK_SIZE];
+ u8 header[2*AES_BLOCK_SIZE];
+} __attribute__ ((__packed__)) ccmp_data_t;
+
+typedef struct {
+ u8 valid; /* TRUE if key is valid */
+ u48 tx_pn; /* transmit packet number */
+ u48 rx_pn; /* next receive packet number */
+ struct aes_key_st rk; /* AES key schedule */
+} ccmp_key_t;
+
+/* stores the necessary data to detect duplicate frames */
+typedef struct {
+ struct list_head list;
+ u8 src[ ETH_ALEN ]; /* addr2/sender */
+ u16 seq_ctl; /* seq_ctl of last received frame */
+} dw_duplicate_t;
+
+typedef struct {
+ struct list_head list;
+ struct sk_buff* skb; /* message data */
+ dw_hw_hdr_rx_t hdr;
+} dw_frame_rx_t;
+
+typedef struct {
+ dw_frame_rx_t free;
+ dw_frame_rx_t filled;
+ dw_frame_rx_t* buffer;
+} dw_frame_rx_queue_t;
+
+typedef struct {
+ dw_hw_hdr_tx_t hdr;
+
+ /* physical length of data, including CCMP. Is not included skb->len if
+ * hardware encryption is performed. */
+ size_t phys_len;
+
+ struct {
+ ccmp_key_t* key;
+ int key_index;
+ } crypt;
+} dw_fragment_tx_t;
+
+typedef struct {
+ struct ieee80211_txb* txb;
+ dw_fragment_tx_t frags[ TX_MAX_FRAGS ];
+
+ u8 is_data : 1;
+ u8 use_hw_encryption : 1;
+ u8 use_short_preamble : 1;
+} dw_frame_tx_info_t;
+
+typedef struct {
+ struct list_head list;
+ dw_frame_tx_info_t s;
+} dw_frame_tx_t;
+
+// IBSS
+
+#define DW_BEACON_INT 100 // IBSS Beacon interval (in TU)
+
+//
+// 802.11 MIB constants
+//
+#define DW_SHORT_RETRY_LIMIT 7 // Small frame transmit retry limit
+#define DW_LONG_RETRY_LIMIT 4 // Large frame transmit retry limit
+
+#define DW_TU 1024L/1000 // Time unit (in msecs)
+#define DW_MAX_TX_LIFETIME (512*TU) // Transmit lifetime limit (in msecs)
+#define DW_MAX_RX_LIFETIME (512*TU) // Receive lifetime limit (in msecs)
+
+// Max number of fragments
+#define DW_MAX_FRAGS 16
+
+// Frame header modulation type field
+#define DW_MOD_PSKCCK 0x00 // PSK/CCK modulation
+#define DW_MOD_OFDM 0xee // OFDM modulation
+
+// PSK/CCK PLCP service field bits
+#define DW_SERVICE_LOCKED 0x04 // Locked clocks
+#define DW_SERVICE_MODSEL 0x08 // Modulation selection
+#define DW_SERVICE_LENEXT 0x80 // Length extension
+
+// MAC type field values
+#define DW_TYPE_ASSOC_REQ 0x00 // Association request
+#define DW_TYPE_ASSOC_RESP 0x10 // Association response
+#define DW_TYPE_REASSOC_REQ 0x20 // Reassociation request
+#define DW_TYPE_REASSOC_RESP 0x30 // Reassociation response
+#define DW_TYPE_PROBE_REQ 0x40 // Probe request
+#define DW_TYPE_PROBE_RESP 0x50 // Probe response
+
+#define DW_TYPE_BEACON 0x80 // Beacon
+#define DW_TYPE_ATIM 0x90 // Annoucement traffice indication
+#define DW_TYPE_DISASSOC 0xa0 // Disassociation
+#define DW_TYPE_AUTH 0xb0 // Authentication
+#define DW_TYPE_DEAUTH 0xc0 // Deauthentication
+
+#define DW_TYPE_RTS 0xb4 // Request to send
+#define DW_TYPE_CTS 0xc4 // Clear to send
+#define DW_TYPE_ACK 0xd4 // Acknowledgement
+#define DW_TYPE_PSPOLL 0xa4 // Power Save(PS)-Poll
+
+#define DW_TYPE_DATA 0x08 // Data
+
+// TRUE if buf is data or management frame
+#define DW_IS_DATA(buf) (((buf)->macHdr.fc.type & 0xcf) == TYPE_DATA)
+#define DW_IS_MGMT(buf) (((buf)->macHdr.fc.type & 0x0f) == 0)
+
+// MAC address macros
+#define DW_MAC_GROUP 0x01 // Broadcast or multicast address
+#define DW_MAC_LOCAL 0x02 // Locally administered address
+
+#define DW_EQUAL_ADDR(a1, a2) (memcmp (a1, a2, ETH_ALEN) == 0)
+#define DW_SET_ADDR(a1, a2) (memcpy (a1, a2, ETH_ALEN))
+
+// Authentication algorithm number field values
+#define DW_AUTH_OPEN 0x00 // Open system
+#define DW_AUTH_SHAREDKEY 0x01 // Shared key
+#define DW_AUTH_LEAP 0x80 // LEAP
+
+// Capability information field bits
+#define DW_CAP_ESS 0x0001 // Extended service set (infrastructure)
+#define DW_CAP_IBSS 0x0002 // Independent BSS (ad hoc)
+#define DW_CAP_POLLABLE 0x0004 // Contention free pollable
+#define DW_CAP_POLLREQ 0x0008 // Contention free poll request
+#define DW_CAP_PRIVACY 0x0010 // Privacy (WEP) required
+#define DW_CAP_SHORTPRE 0x0020 // Short preambles allowed
+#define DW_CAP_PBCC 0x0040 // PBCC modulation allowed
+#define DW_CAP_AGILITY 0x0080 // Channel agility in use
+#define DW_CAP_SHORTSLOT 0x0400 // Short slot time in use
+#define DW_CAP_DSSSOFDM 0x2000 // DSSS-OFDM in use
+
+// Status code field values
+#define DW_STAT_SUCCESS 0
+
+// Reason code field values
+#define DW_REAS_NOLONGERVALID 2
+#define DW_REAS_DEAUTH_LEAVING 3
+#define DW_REAS_INACTIVITY 4
+#define DW_REAS_INCORRECT_FRAME_UNAUTH 6
+#define DW_REAS_INCORRECT_FRAME_UNASSO 7
+
+// Information element IDs
+#define DW_ELEM_SSID 0 // Service set ID
+#define DW_ELEM_SUPRATES 1 // Supported rates
+#define DW_ELEM_DSPARAM 3 // DS parameter set
+#define DW_ELEM_IBSSPARAM 6 // IBSS parameter set
+#define DW_ELEM_COUNTRY 7 // Country information
+#define DW_ELEM_CHALLENGE 16 // Challenge text
+#define DW_ELEM_ERPINFO 42 // Extended rate PHY info
+#define DW_ELEM_RSN 48 // Robust security network (WPA2)
+#define DW_ELEM_EXTSUPRATES 50 // Extended supported rates
+#define DW_ELEM_VENDOR 221 // Vendor extension (WPA)
+
+// 802.11d related defines
+// minimum length field value in country information elelment
+#define DW_COUNTRY_INFO_MIN_LEN 6
+
+// Supported rates bits
+#define DW_RATE_BASIC 0x80 // Bit set if basic rate
+
+// TRUE if channel number in 5 GHz band
+#define DW_CHAN_5G(chan) ((chan) > 14)
+
+// ERP info bits
+#define DW_ERP_NONERP 0x01 // Non-ERP present
+#define DW_ERP_USEPROTECT 0x02 // Use protection
+#define DW_ERP_BARKER 0x04 // Barker (long) preamble mode
+
+// Key ID byte in data frame body
+#define DW_EXT_IV 0x20 // Extended IV is present
+
+// Correct CRC-32 check value
+#define DW_GOOD_CRC32 0x2144df1c
+
+#pragma pack()
+
+#define BEACON_BODY_SIZE 64
+
+// MAC buffer, including complete MAC frame
+typedef struct {
+ dw_hw_hdr_tx_t hwHdr; // Frame and PLCP headers
+ u16 fc; // Frame control
+ u16 duration; // Duration/ID (needs byte swap)
+ u8 addr1[ ETH_ALEN ]; // Address 1
+ u8 addr2[ ETH_ALEN ]; // Address 2
+ u8 addr3[ ETH_ALEN ]; // Address 3
+ u16 seq_ctl; // Sequence control fields
+ u8 body[BEACON_BODY_SIZE];
+} __attribute__ ((packed)) dw_beacon_frame;
+
+// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
+// not including SIFS and PLCP preamble/header
+#define DW_LENGTH_uS(bytes, rate) ((16*(bytes)+(rate)-1)/(rate))
+
+// Length (in usecs) of SIFS and PLCP preamble/header.
+#define DW_PRE_LEN_uS(rate) (USE_SHORTPRE(rate) ? 106 : 202)
+
+// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+// including SIFS and PLCP preamble/header
+#define DW_OFDM_DUR(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate)))
+
+// Information on each supported rate
+typedef struct {
+ u8 bps; // Bit rate in 500kbps units
+ u8 ofdmCode; // OFDM rate code, 0 if not OFDM
+ u16 ackLen; // Duration of ACK or CTS in usecs
+} RateInfo;
+
+#define DW_IBSS_DEFAULT_CHANNEL 6
+
+// End of IBSS
+
+typedef struct dw_priv {
+ struct net_device* dev;
+ struct ieee80211_device* ieee;
+ struct ieee80211softmac_device* softmac;
+
+ struct iw_statistics wstats;
+
+ spinlock_t lock;
+
+ struct timer_list management_timer;
+
+ /* Additional information, specific to the 80211 cores. */
+ /* Driver status flags. */
+ u32 recovery:1; /* TRUE if interval follows rate increase */
+ /* Interrupt Service Routine tasklet (bottom-half) */
+ u8 channel;
+ /* Informational stuff. */
+ char nick[IW_ESSID_MAX_SIZE + 1];
+ int short_retry_limit;
+ int long_retry_limit;
+ ccmp_key_t aeskeys[WEP_KEYS];
+ int cw; /* Contention window size */
+ int success; /* Successful intervals */
+ int success_threshold; /* Successful intervals needed to increase rate */
+ atomic_t fix_rate;
+
+ struct ieee80211softmac_ratesinfo ap_ri;
+
+ struct {
+ int counter;
+ } activity;
+
+ struct {
+ int index;
+ int counter;
+ int recovery;
+ int tx_data_any;
+ int tx_data_ack;
+ int have_activity;
+ int success;
+ int success_threshold;
+ } rate;
+
+ struct {
+ struct tasklet_struct tasklet;
+ dw_frame_rx_queue_t queue;
+ char pause;
+
+ /* maintains the list of received frames */
+ struct {
+ dw_duplicate_t known;
+ dw_duplicate_t free;
+ dw_duplicate_t entries[ RX_DUP_SENDER_SIZE ];
+ } dups;
+ } rx;
+
+ struct {
+ struct tasklet_struct tasklet;
+ dw_frame_tx_t frames[ TX_QUEUE_SIZE ];
+ struct list_head queued;
+ struct list_head free;
+ atomic_t seq_nr;
+ int timeout;
+ int times_sent;
+ int retries;
+ int fragment;
+ char pending;
+ char pause;
+ struct semaphore pause_sem;
+ char last_was_data;
+ int basics[ RATES_SUPPORTED ];
+ void * data_pending_ack;
+ unsigned long jiffies_pending_ack;
+ } tx;
+
+ unsigned char tx_power;
+
+ int beacon_ready;
+ struct delayed_work beacon_work;
+ int beacon_body_length;
+ dw_beacon_frame beacon_frame;
+ struct ieee80211softmac_network adhoc;
+
+ unsigned long jiffies_last_beacon;
+ int reconnection_attempts;
+} dw_priv_t;
+
+/* dw_(un)lock() protect struct dw_private.
+ */
+#define dw_lock(priv, flags) spin_lock_irqsave(&(priv)->lock, flags)
+#define dw_unlock(priv, flags) spin_unlock_irqrestore(&(priv)->lock, flags)
+
+/* Frame header modulation type field */
+#define MOD_PSKCCK 0x00 /* PSK/CCK modulation */
+#define MOD_OFDM 0xee /* OFDM modulation */
+
+/* PLCP service field bits */
+#define SERVICE_LOCKED 0x04 /* Locked clocks */
+#define SERVICE_MODSEL 0x08 /* Modulation selection */
+#define SERVICE_LENEXT 0x80 /* Length extension */
+
+/* Threshold values. */
+#define DW_MIN_RTS_THRESHOLD 1U
+#define DW_MAX_RTS_THRESHOLD 2304U
+/* 1536 is default for most routers, but our FIFO is larger, so we could accept
+ * more data, e.g. for improvements when doing AES etc. This has been reported
+ * being done with Apple iTunes */
+#define DW_MTU 2048
+
+#define AES_BITS 128 /* 128 bit keys, 10 rounds */
+
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+/* Get 16 bits at byte pointer */
+#define GET16(bp) ((bp)[0] | ((bp)[1] << 8))
+/* Get 32 bits at byte pointer */
+#define GET32(bp) ((bp)[0] | ((bp)[1] << 8) | ((bp)[2] << 16) | ((bp)[3] << 24))
+/* Store 16 bits at byte pointer */
+#define SET16(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; }
+/* Store 32 bits at byte pointer */
+#define SET32(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; \
+ (bp)[2] = (data) >> 16; \
+ (bp)[3] = (data) >> 24; }
+
+#endif /* FS_DIGI_WI_G_DRIVER_H */
diff --git a/drivers/net/wireless/digi_wi_g_priv_handler.c b/drivers/net/wireless/digi_wi_g_priv_handler.c
new file mode 100644
index 000000000000..c7ab82a2e335
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g_priv_handler.c
@@ -0,0 +1,345 @@
+/*
+ * net/wireless/digi_wi_g_priv_handler.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version2 as published by
+ * the Free Software Foundation.
+*/
+/*
+ * !Revision: $Revision: 1.1 $
+ * !Author: Markus Pietrek
+ * !Descr: Contains private user handler stuff.
+*/
+
+#warning fix the getter functions, they compare 0 instead of '0'
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_swencryption
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_swencryption(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ on = *extra;
+
+ digi_wi_g_lock(priv, flags);
+ if (on)
+ priv->hw_aes = 0;
+ else
+ priv->hw_aes = 1;
+ digi_wi_g_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_swencryption
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_swencryption(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ if (priv->hw_aes)
+ on = 0;
+ else
+ on = 1;
+ digi_wi_g_unlock(priv, flags);
+
+ if (on)
+ strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_ant_div
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_ant_div(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ on = *extra;
+
+ digi_wi_g_lock(priv, flags);
+ if (on) {
+ priv->ant_div = 1;
+ HW_GEN_CONTROL |= GEN_ANTDIV;
+ } else {
+ priv->ant_div = 0;
+ HW_GEN_CONTROL &= ~GEN_ANTDIV;
+ }
+ digi_wi_g_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_ant_div
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_ant_div(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ if (priv->ant_div)
+ on = 1;
+ else
+ on = 0;
+ digi_wi_g_unlock(priv, flags);
+
+ if (on)
+ strncpy(extra, "1 (Antenna Diversity is enabled) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (Antenna Diversity is disabled) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_antenna
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_antenna(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char ant;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ ant = *extra;
+
+ if ((ant == 0) || (ant == 1)) {
+ digi_wi_g_lock(priv, flags);
+ if (ant == 0) {
+ priv->antenna = 0;
+ HW_GEN_CONTROL &= ~GEN_ANTSEL;
+ } else {
+ priv->antenna = 1;
+ HW_GEN_CONTROL |= GEN_ANTSEL;
+ }
+ digi_wi_g_unlock(priv, flags);
+ } else {
+ printk(PFX KERN_ERR "Value should be 0 or 1.\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_antenna
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_antenna(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int ant;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ ant = priv->antenna;
+ digi_wi_g_unlock(priv, flags);
+
+ if (ant == 0)
+ strncpy(extra, "0 (Antenna 1 selected) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "1 (Antenna 2 selected) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_rx_off
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_rx_off(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char rx_off;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ rx_off = *extra;
+
+ if ((rx_off == 0) || (rx_off == 1)) {
+ digi_wi_g_lock(priv, flags);
+ if (rx_off == 0) {
+ priv->rx_off = 0;
+ } else {
+ priv->rx_off = 1;
+ }
+ digi_wi_g_unlock(priv, flags);
+ } else {
+ printk(PFX KERN_ERR "Value should be 0 or 1.\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_rx_off
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_rx_off(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int rx_off;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ rx_off = priv->rx_off;
+ digi_wi_g_unlock(priv, flags);
+
+ if (rx_off == 0)
+ strncpy(extra, "0 (rx is on) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "1 (rx is off) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+static const iw_handler digi_wi_g_priv_wx_handlers[] = {
+ /* Enable/Disable Software Encryption mode */
+ digi_wi_g_wx_set_swencryption,
+ /* Get Software Encryption mode */
+ digi_wi_g_wx_get_swencryption,
+ /* Enable/Disable Antenna Diversity mode */
+ digi_wi_g_wx_set_ant_div,
+ /* Get Antenna Diversity mode */
+ digi_wi_g_wx_get_ant_div,
+ /* Select Antenna */
+ digi_wi_g_wx_set_antenna,
+ /* Get Antenna */
+ digi_wi_g_wx_get_antenna,
+ /* Set rx off */
+ digi_wi_g_wx_set_rx_off,
+ /* Get rx off */
+ digi_wi_g_wx_get_rx_off,
+};
+
+#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 0)
+#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 1)
+#define PRIV_WX_SET_ANTDIV (SIOCIWFIRSTPRIV + 2)
+#define PRIV_WX_GET_ANTDIV (SIOCIWFIRSTPRIV + 3)
+#define PRIV_WX_SET_ANTSEL (SIOCIWFIRSTPRIV + 4)
+#define PRIV_WX_GET_ANTSEL (SIOCIWFIRSTPRIV + 5)
+#define PRIV_WX_SET_RX_OFF (SIOCIWFIRSTPRIV + 6)
+#define PRIV_WX_GET_RX_OFF (SIOCIWFIRSTPRIV + 7)
+
+static const struct iw_priv_args digi_wi_g_priv_wx_args[] = {
+ {
+ .cmd = PRIV_WX_SET_SWENCRYPTION,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_swencrypt",
+ },
+ {
+ .cmd = PRIV_WX_GET_SWENCRYPTION,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_swencrypt",
+ },
+ {
+ .cmd = PRIV_WX_SET_ANTDIV,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_ant_div",
+ },
+ {
+ .cmd = PRIV_WX_GET_ANTDIV,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_ant_div",
+ },
+ {
+ .cmd = PRIV_WX_SET_ANTSEL,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_ant_sel",
+ },
+ {
+ .cmd = PRIV_WX_GET_ANTSEL,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_ant_sel",
+ },
+ {
+ .cmd = PRIV_WX_SET_RX_OFF,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_rx_off",
+ },
+ {
+ .cmd = PRIV_WX_GET_RX_OFF,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_rx_off",
+ },
+};
+