kernel/2.6/drivers/net/can/dev.c
kernel/2.6/drivers/net/can/sysfs.h
kernel/2.6/drivers/net/can/mcp251x.c
+kernel/2.6/drivers/net/can/at91_can.c
kernel/2.6/drivers/net/can/Makefile
kernel/2.6/drivers/net/can/mscan/mscan.c
kernel/2.6/drivers/net/can/mscan/mpc52xx_can.c
6.2 local loopback of sent frames
6.3 CAN controller hardware filters
6.4 The virtual CAN driver (vcan)
- 6.5 currently supported CAN hardware
- 6.6 todo
+ 6.5 The CAN network device driver interface
+ 6.5.1 Netlink interface to set/get devices properties
+ 6.5.2 Setting the CAN bit-timing
+ 6.5.3 Starting and stopping the CAN network device
+ 6.6 supported CAN hardware
- 7 Credits
+ 7 Socket CAN resources
+
+ 8 Credits
============================================================================
the user application using the common CAN filter mechanisms. Inside
this filter definition the (interested) type of errors may be
selected. The reception of error frames is disabled by default.
+ The format of the CAN error frame is briefly decribed in the Linux
+ header file "include/linux/can/error.h".
4. How to use Socket CAN
------------------------
removal of vcan network devices can be managed with the ip(8) tool:
- Create a virtual CAN network interface:
- ip link add type vcan
+ $ ip link add type vcan
- Create a virtual CAN network interface with a specific name 'vcan42':
- ip link add dev vcan42 type vcan
+ $ ip link add dev vcan42 type vcan
- Remove a (virtual CAN) network interface 'vcan42':
- ip link del vcan42
-
- The tool 'vcan' from the SocketCAN SVN repository on BerliOS is obsolete.
-
- Virtual CAN network device creation in older Kernels:
- In Linux Kernel versions < 2.6.24 the vcan driver creates 4 vcan
- netdevices at module load time by default. This value can be changed
- with the module parameter 'numdev'. E.g. 'modprobe vcan numdev=8'
-
- 6.5 currently supported CAN hardware
+ $ ip link del vcan42
+
+ 6.5 The CAN network device driver interface
+
+ The CAN network device driver interface provides a generic interface
+ to setup, configure and monitor CAN network devices. The user can then
+ configure the CAN device, like setting the bit-timing parameters, via
+ the netlink interface using the program "ip" from the "IPROUTE2"
+ utility suite. The following chapter describes briefly how to use it.
+ Furthermore, the interface uses a common data structure and exports a
+ set of common functions, which all real CAN network device drivers
+ should use. Please have a look to the SJA1000 or MSCAN driver to
+ understand how to use them. The name of the module is can-dev.ko.
+
+ 6.5.1 Netlink interface to set/get devices properties
+
+ The CAN device must be configured via netlink interface. The supported
+ netlink message types are defined and briefly described in
+ "include/linux/can/netlink.h". CAN link support for the program "ip"
+ of the IPROUTE2 utility suite is avaiable and it can be used as shown
+ below:
+
+ - Setting CAN device properties:
+
+ $ ip link set can0 type can help
+ Usage: ip link set DEVICE type can
+ [ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |
+ [ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1
+ phase-seg2 PHASE-SEG2 [ sjw SJW ] ]
+
+ [ loopback { on | off } ]
+ [ listen-only { on | off } ]
+ [ triple-sampling { on | off } ]
+
+ [ restart-ms TIME-MS ]
+ [ restart ]
+
+ Where: BITRATE := { 1..1000000 }
+ SAMPLE-POINT := { 0.000..0.999 }
+ TQ := { NUMBER }
+ PROP-SEG := { 1..8 }
+ PHASE-SEG1 := { 1..8 }
+ PHASE-SEG2 := { 1..8 }
+ SJW := { 1..4 }
+ RESTART-MS := { 0 | NUMBER }
+
+ - Display CAN device details and statistics:
+
+ $ ip -details -statistics link show can0
+ 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP qlen 10
+ link/can
+ can <TRIPLE-SAMPLING> state ERROR-ACTIVE restart-ms 100
+ bitrate 125000 sample_point 0.875
+ tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
+ sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
+ clock 8000000
+ re-started bus-errors arbit-lost error-warn error-pass bus-off
+ 41 17457 0 41 42 41
+ RX: bytes packets errors dropped overrun mcast
+ 140859 17608 17457 0 0 0
+ TX: bytes packets errors dropped carrier collsns
+ 861 112 0 41 0 0
+
+ More info to the above output:
+
+ "<TRIPLE-SAMPLING>"
+ Shows the list of selected CAN controller modes: LOOPBACK,
+ LISTEN-ONLY, or TRIPLE-SAMPLING.
+
+ "state ERROR-ACTIVE"
+ The current state of the CAN controller: "ERROR-ACTIVE",
+ "ERROR-WARNING", "ERROR-PASSIVE", "BUS-OFF" or "STOPPED"
+
+ "restart-ms 100"
+ Automatic restart delay time. If set to a non-zero value, a
+ restart of the CAN controller will be triggered automatically
+ in case of a bus-off condition after the specified delay time
+ in milliseconds. By default it's off.
+
+ "bitrate 125000 sample_point 0.875"
+ Shows the real bit-rate in bits/sec and the sample-point in the
+ range 0.000..0.999. If the calculation of bit-timing parameters
+ is enabled in the kernel (CONFIG_CAN_CALC_BITTIMING=y), the
+ bit-timing can be defined by setting the "bitrate" argument.
+ Optionally the "sample-point" can be specified. By default it's
+ 0.000 assuming CIA-recommended sample-points.
+
+ "tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1"
+ Shows the time quanta in ns, propagation segment, phase buffer
+ segment 1 and 2 and the synchronisation jump width in units of
+ tq. They allow to define the CAN bit-timing in a hardware
+ independent format as proposed by the Bosch CAN 2.0 spec (see
+ chapter 8 of http://www.semiconductors.bosch.de/pdf/can2spec.pdf).
+
+ "sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
+ clock 8000000"
+ Shows the bit-timing constants of the CAN controller, here the
+ "sja1000". The minimum and maximum values of the time segment 1
+ and 2, the synchronisation jump width in units of tq, the
+ bitrate pre-scaler and the CAN system clock frequency in Hz.
+ These constants could be used for user-defined (non-standard)
+ bit-timing calculation algorithms in user-space.
+
+ "re-started bus-errors arbit-lost error-warn error-pass bus-off"
+ Shows the number of restarts, bus and arbitration lost errors,
+ and the state changes to the error-warning, error-passive and
+ bus-off state. RX overrun errors are listed in the "overrun"
+ field of the standard network statistics.
+
+ 6.5.2 Setting the CAN bit-timing
+
+ The CAN bit-timing parameters can always be defined in a hardware
+ independent format as proposed in the Bosch CAN 2.0 specification
+ specifying the arguments "tq", "prop_seg", "phase_seg1", "phase_seg2"
+ and "sjw":
+
+ $ ip link set canX type can tq 125 prop-seg 6 \
+ phase-seg1 7 phase-seg2 2 sjw 1
+
+ If the kernel option CONFIG_CAN_CALC_BITTIMING is enabled, CIA
+ recommended CAN bit-timing parameters will be calculated if the bit-
+ rate is specified with the argument "bitrate":
+
+ $ ip link set canX type can bitrate 125000
+
+ Note that this works fine for the most common CAN controllers with
+ standard bit-rates but may *fail* for exotic bit-rates or CAN system
+ clock frequencies. Disabling CONFIG_CAN_CALC_BITTIMING saves some
+ space and allows user-space tools to solely determine and set the
+ bit-timing parameters. The CAN controller specific bit-timing
+ constants can be used for that purpose. They are listed by the
+ following command:
+
+ $ ip -details link show can0
+ ...
+ sja1000: clock 8000000 tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
+
+ 6.5.3 Starting and stopping the CAN network device
+
+ A CAN network device is started or stopped as usual with the command
+ "ifconfig canX up/down" or "ip link set canX up/down". Be aware that
+ you *must* define proper bit-timing parameters for real CAN devices
+ before you can start it to avoid error-prone default settings:
+
+ $ ip link set canX up type can bitrate 125000
+
+ A device may enter the "bus-off" state if too much errors occurred on
+ the CAN bus. Then no more messages are received or sent. An automatic
+ bus-off recovery can be enabled by setting the "restart-ms" to a
+ non-zero value, e.g.:
+
+ $ ip link set canX type can restart-ms 100
+
+ Alternatively, the application may realize the "bus-off" condition
+ by monitoring CAN error frames and do a restart when appropriate with
+ the command:
+
+ $ ip link set canX type can restart
+
+ Note that a restart will also create a CAN error frame (see also
+ chapter 3.4).
- On the project website http://developer.berlios.de/projects/socketcan
- there are different drivers available:
+ 6.6 Supported CAN hardware
- vcan: Virtual CAN interface driver (if no real hardware is available)
- sja1000: Philips SJA1000 CAN controller (recommended)
- i82527: Intel i82527 CAN controller
- mscan: Motorola/Freescale CAN controller (e.g. inside SOC MPC5200)
- ccan: CCAN controller core (e.g. inside SOC h7202)
- slcan: For a bunch of CAN adaptors that are attached via a
- serial line ASCII protocol (for serial / USB adaptors)
+ Please check the "Kconfig" file in "drivers/net/can" to get an actual
+ list of the support CAN hardware. On the Socket CAN project website
+ (see chapter 7) there might be further drivers available, also for
+ older kernel versions.
- Additionally the different CAN adaptors (ISA/PCI/PCMCIA/USB/Parport)
- from PEAK Systemtechnik support the CAN netdevice driver model
- since Linux driver v6.0: http://www.peak-system.com/linux/index.htm
+7. Socket CAN resources
+-----------------------
- Please check the Mailing Lists on the berlios OSS project website.
+ You can find further resources for Socket CAN like user space tools,
+ support for old kernel versions, more drivers, mailing lists, etc.
+ at the BerliOS OSS project website for Socket CAN:
- 6.6 todo
+ http://developer.berlios.de/projects/socketcan
- The configuration interface for CAN network drivers is still an open
- issue that has not been finalized in the socketcan project. Also the
- idea of having a library module (candev.ko) that holds functions
- that are needed by all CAN netdevices is not ready to ship.
- Your contribution is welcome.
+ If you have questions, bug fixes, etc., don't hesitate to post them to
+ the Socketcan-Users mailing list. But please search the archives first.
-7. Credits
+8. Credits
----------
- Oliver Hartkopp (PF_CAN core, filters, drivers, bcm)
+ Oliver Hartkopp (PF_CAN core, filters, drivers, bcm, SJA1000 driver)
Urs Thuermann (PF_CAN core, kernel integration, socket interfaces, raw, vcan)
Jan Kizka (RT-SocketCAN core, Socket-API reconciliation)
- Wolfgang Grandegger (RT-SocketCAN core & drivers, Raw Socket-API reviews)
+ Wolfgang Grandegger (RT-SocketCAN core & drivers, Raw Socket-API reviews,
+ CAN device driver interface, MSCAN driver)
Robert Schwebel (design reviews, PTXdist integration)
Marc Kleine-Budde (design reviews, Kernel 2.6 cleanups, drivers)
Benedikt Spranger (reviews)
Thomas Gleixner (LKML reviews, coding style, posting hints)
- Andrey Volkov (kernel subtree structure, ioctls, mscan driver)
+ Andrey Volkov (kernel subtree structure, ioctls, MSCAN driver)
Matthias Brukner (first SJA1000 CAN netdevice implementation Q2/2003)
Klaus Hitschler (PEAK driver integration)
Uwe Koppe (CAN netdevices with PF_PACKET approach)
Michael Schulze (driver layer loopback requirement, RT CAN drivers review)
+ Pavel Pisa (Bit-timing calculation)
+ Sascha Hauer (SJA1000 platform driver)
+ Sebastian Haas (SJA1000 EMS PCI driver)
+ Markus Plessing (SJA1000 EMS PCI driver)
+ Per Dalen (SJA1000 Kvaser PCI driver)
+ Sam Ravnborg (reviews, coding style, kbuild help)
TOPDIR := $(PWD)
export CONFIG_CAN_VCAN=m
+export CONFIG_CAN_SLCAN=m
export CONFIG_CAN_DEV=m
+export CONFIG_CAN_CALC_BITTIMING=y
+#export CONFIG_CAN_DEV_SYSFS=y
#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_SJA1000_OF_PLATFORM=m
+export CONFIG_CAN_ESD_PCI=m
+export CONFIG_CAN_IXXAT_PCI=m
+export CONFIG_CAN_PEAK_PCI=m
+export CONFIG_CAN_KVASER_PCI=m
export CONFIG_CAN_EMS_PCI=m
export CONFIG_CAN_EMS_PCMCIA=m
+export CONFIG_CAN_EMS_104M=m
+export CONFIG_CAN_ESD_PCI=m
+export CONFIG_CAN_ESD_331=m
export CONFIG_CAN_PIPCAN=m
export CONFIG_CAN_SOFTING=m
export CONFIG_CAN_SOFTING_CS=m
--- /dev/null
+This is a list of knwon driver issues:
+
+drivers/net/can/at91_can.c:
+ FIXME: bus-off handling
+ FIXME: review non-standard error counting and statistics
+ FIXME: to be tested
+
+drivers/net/can/mcp251x.c:
+
+ FIXME: echo support missing (requilred for IFF_ECHO)
+ FIXME: increment can_stats in case of state changes
+ FIXME: state changes by interrupt
+ FIXME: do_set_mode not implemented (required for restart)
+ FIXME: remove tx timeout callback
+ FIXME: to be tested
+
+drivers/net/can/softing/softing_main.c:
+
+ FIXME: coding style issues!
+ FIXME: check softing_flush_echo_skb and bus_off handling.
+ FIXME: to be tested
+
+drivers/net/can/mscan/mscan.c:
+ FIXME: don't use netdev->base_addr
+
+drivers/net/can/mscan/mpc52xx_can.c:
+
+ FIXME: add support for the MPC512x as well
+ FIXME: rename mpc52xx to mpc5xxx
endif
config CAN_DEV
- tristate "Prompt for platform CAN drivers with sysfs support"
+ tristate "Prompt for platform CAN drivers with netlink support"
depends on CAN && SYSFS
default Y
---help---
support. This is the standard library for CAN drivers.
If unsure, say Y.
+config CAN_DEV_SYSFS
+ bool "Support for sysfs interface (deprecated)"
+ depends on CAN_DEV && SYSFS
+ default N
+ ---help---
+ Adds support for the legacy sysfs interface to configure CAN
+ devices. If possible, please use the new netlink interface
+ instead.
+ If unsure, say N.
+
config CAN_CALC_BITTIMING
bool "CAN bit-timing calculation"
depends on CAN_DEV
files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw".
If unsure, say Y.
+config CAN_CC770
+ depends on HAS_IOMEM && CAN_DEV
+ tristate "Bosch CC770 and Intel AN82527"
+ ---help---
+ Driver for the Bosch CC770 and the compatible Intel AN82527
+ CAN controllers.
+
+config CAN_CC770_ISA
+ depends on CAN_CC770 && ISA
+ tristate "ISA Bus based legacy CC770 driver"
+ ---help---
+ This driver adds legacy support for CC770 and AN82527 chips
+ connected to the ISA bus using I/O port, memory mapped or
+ indirect access.
+
+config CAN_CC770_OF_PLATFORM
+ depends on CAN_CC770 && PPC_OF
+ tristate "Generic OF Platform Bus based CC770 driver"
+ ---help---
+ This driver adds support for the CC770 and other AN82527
+ compatible chips connected to the OpenFirmware "platform bus"
+ found on embedded systems with OpenFirmware bindings, e.g. if
+ you have a PowerPC based system you may want to enable this
+ option.
+
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.
+ Driver for the SJA1000 CAN controllers from Philips or NXP
- This driver will use the new device interface.
+config CAN_SJA1000_ISA
+ depends on CAN_SJA1000 && ISA
+ tristate "ISA Bus based legacy SJA1000 driver"
+ ---help---
+ This driver adds legacy support for SJA1000 chips connected to
+ the ISA bus using I/O port, memory mapped or indirect access.
config CAN_SJA1000_PLATFORM
depends on CAN_SJA1000
config CAN_SJA1000_OF_PLATFORM
depends on CAN_SJA1000 && PPC_OF
- tristate "generic OF Platform Bus based SJA1000 driver"
+ 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.
+ you may want to enable this option.
config CAN_EMS_PCI
- tristate "EMS CPC-PCI and CPC-PCIe Card"
+ tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P 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).
+ This driver is for the one, two or four channel CPC-PCI,
+ CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
+ (http://www.ems-wuensche.de).
config CAN_EMS_PCMCIA
tristate "EMS CPC-CARD Card"
This driver is for the one or two channel CPC-CARD cards from
EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+config CAN_EMS_104M
+ tristate "EMS CPC-104M Card"
+ depends on ISA && CAN_SJA1000
+ ---help---
+ This driver is for the one, two or four channel CPC-104M cards
+ from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
config CAN_ESD_PCI
tristate "ESD PCI Cards"
depends on PCI && CAN_SJA1000
---help---
- This driver supports the esd PCI CAN cards CAN-PCI/200,
- CAN-PCI/266, CAN-PMC/266 (PMC), CAN-CPCI/200 (CompactPCI),
- CAN-PCIe2000 (PCI Express) and CAN-PCI104/200 (PCI104)
+ This driver supports the esd PCI CAN cards CAN-PCI/200,
+ CAN-PCI/266, CAN-PMC/266 (PMC), CAN-CPCI/200 (CompactPCI),
+ CAN-PCIe2000 (PCI Express) and CAN-PCI104/200 (PCI104)
from the esd electronic system design gmbh (http://www.esd.eu).
config CAN_IXXAT_PCI
config CAN_ESD_PCI331
tristate "ESD CAN 331 Cards"
- depends on PCI
+ depends on PCI && CAN_DEV
---help---
This driver supports the PCI/331, CPCI/331 and PMC/331 CAN cards
from the esd system design gmbh (http://www.esd.eu).
depends on CAN_DEV
---help---
generic softing CAN cards
+ Sofing CAN cards come with 1 or 2 physical busses.
+ The API of the card does not allow fine control per bus, but
+ controls the 2 busses on the card together.
+ As such, some actions (start/stop/busoff recovery) on 1 bus
+ must bring down the other bus too temporarily.
+ You have been warned.
+ This driver is written on safe on 64bit, but not on big endian.
config CAN_SOFTING_CS
tristate "Softing CAN pcmcia cards"
depends on CAN_SOFTING && PCMCIA
+ ---help---
+ Support for PCMCIA cards from Softing Gmbh & some cards
+ from Vector Gmbh.
+ You need firmware for these, which you can get at
+ http://developer.berlios.de/projects/socketcan/
+ This version of the driver is written against
+ firmware version 4.6
config CAN_MSCAN
depends on CAN_DEV && (PPC || M68K || M68KNOMMU)
export CONFIG_CAN_DEV=m
#export CONFIG_CAN_SJA1000_OLD=m
#export CONFIG_CAN_I82527_OLD=m
+export CONFIG_CAN_CC770=m
export CONFIG_CAN_SJA1000=m
export CONFIG_CAN_SJA1000_PLATFORM=m
export CONFIG_CAN_EMS_PCI=m
obj-$(CONFIG_CAN_SLCAN) += slcan.o
obj-$(CONFIG_CAN_DEV) += can-dev.o
-can-dev-y := dev.o sysfs.o
+can-dev-y := dev.o
+can-dev-$(CONFIG_CAN_DEV_SYSFS) += sysfs.o
+obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_SJA1000) += sja1000/
obj-$(CONFIG_CAN_SOFTING) += softing/
obj-$(CONFIG_CAN_MSCAN) += mscan/
obj-$(CONFIG_CAN_CCAN_OLD) += old/ccan/
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+ifeq ($(CONFIG_CAN_DEV_SYSFS),y)
+ EXTRA_CFLAGS += -DCONFIG_CAN_DEV_SYSFS
+endif
+ifneq ($(CONFIG_CAN_CALC_BITTIMING),n)
+ EXTRA_CFLAGS += -DCONFIG_CAN_CALC_BITTIMING
+endif
endif
#include <linux/init.h>
#include <linux/clk.h>
-#include <linux/can.h>
-#include <linux/can/error.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/error.h>
+#include <socketcan/can/dev.h>
#include <mach/board.h>
unsigned int tx_echo;
unsigned int rx_bank;
+ void __iomem *reg_base; /* ioremap'ed address to registers */
+};
+
+
+static struct can_bittiming_const at91_bittiming_const = {
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 2,
+ .brp_max = 128,
+ .brp_inc = 1,
};
static inline u32 at91_read(struct net_device *dev, enum at91_reg reg)
{
- return readl((void __iomem *)dev->base_addr + reg);
+ struct at91_priv *priv = netdev_priv(dev);
+ return readl(priv->reg_base + reg);
}
static inline void
at91_write(struct net_device *dev, enum at91_reg reg, u32 value)
{
- writel(value, (void __iomem *)dev->base_addr + reg);
+ struct at91_priv *priv = netdev_priv(dev);
+ writel(value, priv->reg_base + reg);
}
else
reg_mid = (cf->can_id & CAN_SFF_MASK) << 18;
- reg_mcr = (cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0 |
+ reg_mcr = ((cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0 ) |
(cf->can_dlc << 16) |
AT91_MCR_MTCR;
}
-static void at91_tx_timeout(struct net_device *dev)
-{
- dev->stats.tx_errors++;
- dev_dbg(ND2D(dev), "TX timeout!\n");
-}
-
-
/*
* theory of operation:
*
at91_write(dev, AT91_IDR, 1 << mb);
/*
- * only echo if mailbox signals us an transfer
- * complete (MSR_MRDY). Otherwise it's an tansfer
+ * only echo if mailbox signals us a transfer
+ * complete (MSR_MRDY). Otherwise it's a tansfer
* abort. "can_bus_off()" takes care about the skbs
* parked in the echo queue.
*/
/*
* restart queue if we don't have a wrap around but restart if
- * we get an TX int for the last can frame directly before a
+ * we get a TX int for the last can frame directly before a
* wrap around.
*/
if ((priv->tx_next & AT91_NEXT_MASK) != 0 ||
if (unlikely(reg_sr & AT91_IRQ_BOFF))
new_state = CAN_STATE_BUS_OFF;
else if (unlikely(reg_sr & AT91_IRQ_ERRP))
- new_state = CAN_STATE_BUS_PASSIVE;
+ new_state = CAN_STATE_ERROR_PASSIVE;
else if (unlikely(reg_sr & AT91_IRQ_WARN))
- new_state = CAN_STATE_BUS_WARNING;
+ new_state = CAN_STATE_ERROR_WARNING;
else if (likely(reg_sr & AT91_IRQ_ERRA))
- new_state = CAN_STATE_ACTIVE;
+ new_state = CAN_STATE_ERROR_ACTIVE;
else {
BUG(); /* FIXME */
return;
switch (priv->can.state) {
- case CAN_STATE_ACTIVE:
+ case CAN_STATE_ERROR_ACTIVE:
/*
* from: ACTIVE
* to : BUS_WARNING, BUS_PASSIVE, BUS_OFF
* => : there was a warning int
*/
- if (new_state >= CAN_STATE_BUS_WARNING &&
+ if (new_state >= CAN_STATE_ERROR_WARNING &&
new_state <= CAN_STATE_BUS_OFF)
priv->can.can_stats.error_warning++;
- case CAN_STATE_BUS_WARNING: /* fallthrough */
+ case CAN_STATE_ERROR_WARNING: /* fallthrough */
/*
* from: ACTIVE, BUS_WARNING
* to : BUS_PASSIVE, BUS_OFF
* => : error passive int
*/
- if (new_state >= CAN_STATE_BUS_PASSIVE &&
+ if (new_state >= CAN_STATE_ERROR_PASSIVE &&
new_state <= CAN_STATE_BUS_OFF)
priv->can.can_stats.error_passive++;
break;
* success it leaves bus off. so we have to reenable
* the carrier.
*/
- if (new_state <= CAN_STATE_BUS_PASSIVE)
+ if (new_state <= CAN_STATE_ERROR_PASSIVE)
netif_carrier_on(dev);
break;
default:
/* process state changes depending on the new state */
switch (new_state) {
- case CAN_STATE_ACTIVE:
+ case CAN_STATE_ERROR_ACTIVE:
/*
* actually we want to enable AT91_IRQ_WARN here, but
* it screws up the system under certain
* circumstances. so just enable AT91_IRQ_ERRP, thus
* the "fallthrough"
*/
- case CAN_STATE_BUS_WARNING: /* fallthrough */
+ case CAN_STATE_ERROR_WARNING: /* fallthrough */
reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_BOFF;
reg_ier = AT91_IRQ_ERRP;
break;
- case CAN_STATE_BUS_PASSIVE:
+ case CAN_STATE_ERROR_PASSIVE:
reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_ERRP;
reg_ier = AT91_IRQ_BOFF;
break;
cf->can_dlc = CAN_ERR_DLC;
switch (new_state) {
- case CAN_STATE_BUS_WARNING:
- case CAN_STATE_BUS_PASSIVE:
+ case CAN_STATE_ERROR_WARNING:
+ case CAN_STATE_ERROR_PASSIVE:
cf->can_id |= CAN_ERR_CRTL;
- if (new_state == CAN_STATE_BUS_WARNING)
+ if (new_state == CAN_STATE_ERROR_WARNING)
cf->data[1] = (tec > rec) ?
CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING;
priv->tx_next = priv->tx_echo = priv->rx_bank = 0;
}
-
-static struct net_device_stats *at91_get_stats(struct net_device *dev)
-{
- struct at91_priv *priv = netdev_priv(dev);
- u32 reg_ecr = at91_read(dev, AT91_ECR);
-
- dev->stats.rx_errors = reg_ecr & 0xff;
- dev->stats.tx_errors = reg_ecr >> 16;
-
- /*
- * here comes another one:
- *
- * the transmit error counter (TEC) has only a width of 8
- * bits, so when the devices goes into BUS OFF (which is
- * defined by a TEC > 255), the TEC in the chip shows "0". Not
- * only that, it keeps accumulating errors, so they can vary
- * between 0 and 255. We set TEC to 256 (hard) in BUS_OFF.
- *
- */
- if (unlikely(priv->can.state == CAN_STATE_BUS_OFF))
- dev->stats.tx_errors = 256;
-
- return &dev->stats;
-}
-
-
static int at91_set_bittiming(struct net_device *dev)
{
struct at91_priv *priv = netdev_priv(dev);
((bt->phase_seg2 - 1) << 0);
dev_dbg(ND2D(dev), "writing AT91_BR: 0x%08x, can_sys_clock: %d\n",
- reg_br, priv->can.bittiming.clock);
+ reg_br, priv->can.clock.freq);
at91_write(dev, AT91_BR, reg_br);
return 0;
reg_mr = at91_read(dev, AT91_MR);
at91_write(dev, AT91_MR, reg_mr | AT91_MR_AT91EN);
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* Enable interrupts */
reg_ier =
clk_enable(priv->clk);
- /* determine and set bittime */
- err = can_set_bittiming(dev);
+ /* check or determine and set bittime */
+ err = open_candev(dev);
if (err)
goto out;
if (request_irq(dev->irq, at91_irq, IRQF_SHARED,
dev->name, dev)) {
err = -EAGAIN;
- goto out;
+ goto out_close;
}
/* start chip and queuing */
return 0;
+ out_close:
+ close_candev(dev);
out:
clk_disable(priv->clk);
free_irq(dev->irq, dev);
clk_disable(priv->clk);
- can_close_cleanup(dev);
+ close_candev(dev);
return 0;
}
-static int at91_get_state(struct net_device *dev, u32 *state)
-{
- struct at91_priv *priv = netdev_priv(dev);
- *state = priv->can.state;
- return 0;
-}
-
-
static int at91_set_mode(struct net_device *dev, u32 _mode)
{
enum can_mode mode = _mode;
}
-static struct can_bittiming_const at91_bittiming_const = {
- .tseg1_min = 4,
- .tseg1_max = 16,
- .tseg2_min = 2,
- .tseg2_max = 8,
- .sjw_max = 4,
- .brp_min = 2,
- .brp_max = 128,
- .brp_inc = 1,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops at91_netdev_ops = {
+ .ndo_open = at91_open,
+ .ndo_stop = at91_close,
+ .ndo_start_xmit = at91_start_xmit,
};
-
+#endif
static int __init at91_can_probe(struct platform_device *pdev)
{
goto exit_iounmap;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &at91_netdev_ops;
+#else
dev->open = at91_open;
dev->stop = at91_close;
dev->hard_start_xmit = at91_start_xmit;
- dev->tx_timeout = at91_tx_timeout;
- dev->get_stats = at91_get_stats;
+#endif
dev->irq = irq;
- dev->base_addr = (unsigned long)addr;
dev->flags |= IFF_ECHO;
priv = netdev_priv(dev);
- priv->can.bittiming.clock = clk_get_rate(clk);
+ priv->can.clock.freq = clk_get_rate(clk);
priv->can.bittiming_const = &at91_bittiming_const;
priv->can.do_set_bittiming = at91_set_bittiming;
- priv->can.do_get_state = at91_get_state;
priv->can.do_set_mode = at91_set_mode;
priv->clk = clk;
+ priv->reg_base = addr;
priv->pdata = pdev->dev.platform_data;
}
- dev_info(&pdev->dev, "device registered (base_addr=%#lx, irq=%d)\n",
- dev->base_addr, dev->irq);
+ dev_info(&pdev->dev, "device registered (reg_base=%#p, irq=%d)\n",
+ priv->reg_base, dev->irq);
return 0;
free_netdev(dev);
- iounmap((void __iomem *)dev->base_addr);
+ iounmap(priv->reg_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1);
--- /dev/null
+#
+# $Id: Makefile 443 2007-07-25 11:41:27Z hartkopp $
+#
+
+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_CC770) += cc770.o
+obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o
+obj-$(CONFIG_CAN_CC770_OF_PLATFORM) += cc770_of_platform.o
+
+ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+endif
--- /dev/null
+/*
+ * $Id: $
+ *
+ * cc770.c - Bosch CC770 and Intel AN82527 network device driver
+ *
+ * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from the old Socket-CAN i82527 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/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
+#include <socketcan/can/dev.h>
+
+#include "cc770.h"
+
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
+#define DRV_NAME "cc770"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver");
+
+/*
+ * The CC770 is a CAN controller from Bosch, which is 100% compatible
+ * with the AN82527 from Intel, but with "bugs" being fixed and some
+ * additional functionality, mainly:
+ *
+ * 1. RX and TX error counters are readable.
+ * 2. Support of silent (listen-only) mode.
+ * 3. Message object 15 can receive all types of frames, also RTR and EFF.
+ *
+ * Details are available from Bosch's "CC770_Product_Info_2007-01.pdf",
+ * which explains in detail the compatibility between the CC770 and the
+ * 82527. This driver use the additional functionality 3. on real CC770
+ * devices. Unfortunately, the CC770 does still not store the message
+ * identifier of received remote transmission request frames and
+ * therefore it's set to 0.
+ *
+ * The message objects 1..14 can be used for TX and RX while the message
+ * objects 15 is optimized for RX. It has a shadow register for reliable
+ * data receiption under heavy bus load. Therefore it makes sense to use
+ * this message object for the needed use case. The frame type (EFF/SFF)
+ * for the message object 15 can be defined via kernel module parameter
+ * "msgobj15_eff". If not equal 0, it will receive 29-bit EFF frames,
+ * otherwise 11 bit SFF messages.
+ */
+static int msgobj15_eff;
+module_param(msgobj15_eff, int, S_IRUGO);
+MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 "
+ "(default: 11-bit standard frames)");
+
+static int i82527_compat;
+module_param(i82527_compat, int, S_IRUGO);
+MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode "
+ "without using additional functions");
+
+/*
+ * This driver uses the last 5 message objects 11..15. The definitions
+ * and structure below allows to configure and assign them to the real
+ * message object.
+ */
+static unsigned char cc770_obj_flags[CC770_OBJ_MAX] = {
+ [CC770_OBJ_RX0] = CC770_OBJ_FLAG_RX,
+ [CC770_OBJ_RX1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_EFF,
+ [CC770_OBJ_RX_RTR0] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR,
+ [CC770_OBJ_RX_RTR1] = CC770_OBJ_FLAG_RX | CC770_OBJ_FLAG_RTR |
+ CC770_OBJ_FLAG_EFF,
+ [CC770_OBJ_TX] = 0,
+};
+
+static struct can_bittiming_const cc770_bittiming_const = {
+ .name = DRV_NAME,
+ .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 inline int intid2obj(unsigned int intid)
+{
+ if (intid == 2)
+ return 0;
+ else
+ return MSGOBJ_LAST + 2 - intid;
+}
+
+static void enable_all_objs(const struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ u8 msgcfg;
+ unsigned char obj_flags;
+ unsigned int o, mo;
+
+ for (o = 0; o < CC770_OBJ_MAX; o++) {
+ obj_flags = priv->obj_flags[o];
+ mo = obj2msgobj(o);
+
+ if (obj_flags & CC770_OBJ_FLAG_RX) {
+ /*
+ * We don't need extra objects for RTR and EFF if
+ * the additional CC770 functions are enabled.
+ */
+ if (priv->control_normal_mode & CTRL_EAF) {
+ if (o > 0)
+ continue;
+ dev_dbg(ND2D(dev), "Message object %d for "
+ "RX data, RTR, SFF and EFF\n", mo);
+ } else {
+ dev_dbg(ND2D(dev),
+ "Message object %d for RX %s %s\n", mo,
+ obj_flags & CC770_OBJ_FLAG_RTR ?
+ "RTR" : "data",
+ obj_flags & CC770_OBJ_FLAG_EFF ?
+ "EFF" : "SFF");
+ }
+
+ if (obj_flags & CC770_OBJ_FLAG_EFF)
+ msgcfg = MSGCFG_XTD;
+ else
+ msgcfg = 0;
+ if (obj_flags & CC770_OBJ_FLAG_RTR)
+ msgcfg |= MSGCFG_DIR;
+
+ cc770_write_reg(priv, msgobj[mo].config, msgcfg);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_SET | TXIE_RES |
+ RXIE_SET | INTPND_RES);
+
+ if (obj_flags & CC770_OBJ_FLAG_RTR)
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | CPUUPD_SET |
+ TXRQST_RES | RMTPND_RES);
+ else
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | MSGLST_RES |
+ TXRQST_RES | RMTPND_RES);
+ } else {
+ dev_dbg(ND2D(dev), "Message object %d for "
+ "TX data, RTR, SFF and EFF\n", mo);
+
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_RES |
+ CPUUPD_RES | NEWDAT_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES |
+ RXIE_RES | INTPND_RES);
+ }
+ }
+}
+
+static void disable_all_objs(const struct cc770_priv *priv)
+{
+ int o, mo;
+
+ for (o = 0; o < CC770_OBJ_MAX; o++) {
+ mo = obj2msgobj(o);
+
+ if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX) {
+ if (o > 0 &&
+ priv->control_normal_mode & CTRL_EAF)
+ continue;
+
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | MSGLST_RES |
+ TXRQST_RES | RMTPND_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES |
+ RXIE_RES | INTPND_RES);
+ } else {
+ /* Clear message object for send */
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_RES |
+ CPUUPD_RES | NEWDAT_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES |
+ RXIE_RES | INTPND_RES);
+ }
+ }
+}
+
+static void set_reset_mode(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ /* Enable configuration and puts chip in bus-off, disable interrupts */
+ cc770_write_reg(priv, control, CTRL_CCE | CTRL_INI);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ /* Clear interrupts */
+ cc770_read_reg(priv, interrupt);
+
+ /* Clear status register */
+ cc770_write_reg(priv, status, 0);
+
+ /* Disable all used message objects */
+ disable_all_objs(priv);
+}
+
+static void set_normal_mode(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ /* Clear interrupts */
+ cc770_read_reg(priv, interrupt);
+
+ /* Clear status register and pre-set last error code */
+ cc770_write_reg(priv, status, STAT_LEC_MASK);
+
+ /* Enable all used message objects*/
+ enable_all_objs(dev);
+
+ /*
+ * Clear bus-off, interrupts only for errors,
+ * not for status change
+ */
+ cc770_write_reg(priv, control, priv->control_normal_mode);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+static void chipset_init(struct cc770_priv *priv)
+{
+ int mo, id, data;
+
+ /* Enable configuration and put chip in bus-off, disable interrupts */
+ cc770_write_reg(priv, control, (CTRL_CCE | CTRL_INI));
+
+ /* Set CLKOUT divider and slew rates */
+ cc770_write_reg(priv, clkout, priv->clkout);
+
+ /* Configure CPU interface / CLKOUT enable */
+ cc770_write_reg(priv, cpu_interface, priv->cpu_interface | CPUIF_CEN);
+
+ /* Set bus configuration */
+ cc770_write_reg(priv, bus_config, priv->bus_config);
+
+ /* Clear interrupts */
+ cc770_read_reg(priv, interrupt);
+
+ /* Clear status register */
+ cc770_write_reg(priv, status, 0);
+
+ /* Clear and invalidate message objects */
+ for (mo = MSGOBJ_FIRST; mo <= MSGOBJ_LAST; mo++) {
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ INTPND_UNC | RXIE_RES |
+ TXIE_RES | MSGVAL_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ INTPND_RES | RXIE_RES |
+ TXIE_RES | MSGVAL_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | MSGLST_RES |
+ TXRQST_RES | RMTPND_RES);
+ for (data = 0; data < 8; data++)
+ cc770_write_reg(priv, msgobj[mo].data[data], 0);
+ for (id = 0; id < 4; id++)
+ cc770_write_reg(priv, msgobj[mo].id[id], 0);
+ cc770_write_reg(priv, msgobj[mo].config, 0);
+ }
+
+ /* Set all global ID masks to "don't care" */
+ cc770_write_reg(priv, global_mask_std[0], 0);
+ cc770_write_reg(priv, global_mask_std[1], 0);
+ cc770_write_reg(priv, global_mask_ext[0], 0);
+ cc770_write_reg(priv, global_mask_ext[1], 0);
+ cc770_write_reg(priv, global_mask_ext[2], 0);
+ cc770_write_reg(priv, global_mask_ext[3], 0);
+
+}
+
+static int cc770_probe_chip(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ /* Enable configuration, put chip in bus-off, disable ints */
+ cc770_write_reg(priv, control, CTRL_CCE | CTRL_EAF | CTRL_INI);
+ /* Configure cpu interface / CLKOUT disable */
+ cc770_write_reg(priv, cpu_interface, priv->cpu_interface);
+
+ /*
+ * Check if hardware reset is still inactive or maybe there
+ * is no chip in this address space
+ */
+ if (cc770_read_reg(priv, cpu_interface) & CPUIF_RST) {
+ dev_info(ND2D(dev), "probing @0x%p failed (reset)\n",
+ priv->reg_base);
+ return 0;
+ }
+
+ /* Write and read back test pattern */
+ cc770_write_reg(priv, msgobj[1].data[1], 0x25);
+ cc770_write_reg(priv, msgobj[2].data[3], 0x52);
+ cc770_write_reg(priv, msgobj[10].data[6], 0xc3);
+ if ((cc770_read_reg(priv, msgobj[1].data[1]) != 0x25) ||
+ (cc770_read_reg(priv, msgobj[2].data[3]) != 0x52) ||
+ (cc770_read_reg(priv, msgobj[10].data[6]) != 0xc3)) {
+ dev_info(ND2D(dev), "probing @0x%p failed (pattern)\n",
+ priv->reg_base);
+ return 0;
+ }
+
+ /* Check if this chip is a CC770 supporting additional functions */
+ if (cc770_read_reg(priv, control) & CTRL_EAF)
+ priv->control_normal_mode |= CTRL_EAF;
+
+ return 1;
+}
+
+static void cc770_start(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int cc770_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ cc770_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int cc770_set_bittiming(struct net_device *dev)
+{
+ struct cc770_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);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ btr1 |= 0x80;
+
+ dev_info(ND2D(dev),
+ "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ cc770_write_reg(priv, bit_timing_0, btr0);
+ cc770_write_reg(priv, bit_timing_1, btr1);
+
+ return 0;
+}
+
+static int cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ unsigned int mo = obj2msgobj(CC770_OBJ_TX);
+ u8 dlc, rtr;
+ u32 id;
+ int i;
+
+ if ((cc770_read_reg(priv,
+ msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
+ dev_err(ND2D(dev), "TX register is still occupied!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ netif_stop_queue(dev);
+
+ dlc = cf->can_dlc;
+ id = cf->can_id;
+ if (cf->can_id & CAN_RTR_FLAG)
+ rtr = 0;
+ else
+ rtr = MSGCFG_DIR;
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES);
+ if (id & CAN_EFF_FLAG) {
+ id &= CAN_EFF_MASK;
+ cc770_write_reg(priv, msgobj[mo].config,
+ (dlc << 4) + rtr + MSGCFG_XTD);
+ cc770_write_reg(priv, msgobj[mo].id[3],
+ (id << 3) & 0xFFU);
+ cc770_write_reg(priv, msgobj[mo].id[2],
+ (id >> 5) & 0xFFU);
+ cc770_write_reg(priv, msgobj[mo].id[1],
+ (id >> 13) & 0xFFU);
+ cc770_write_reg(priv, msgobj[mo].id[0],
+ (id >> 21) & 0xFFU);
+ } else {
+ id &= CAN_SFF_MASK;
+ cc770_write_reg(priv, msgobj[mo].config,
+ (dlc << 4) + rtr);
+ cc770_write_reg(priv, msgobj[mo].id[0],
+ (id >> 3) & 0xFFU);
+ cc770_write_reg(priv, msgobj[mo].id[1],
+ (id << 5) & 0xFFU);
+ }
+
+ dlc &= 0x0f; /* restore length only */
+ for (i = 0; i < dlc; i++)
+ cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]);
+
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
+
+ stats->tx_bytes += dlc;
+ dev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, dev, 0);
+
+ /*
+ * 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
+ */
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+
+ return NETDEV_TX_OK;
+}
+
+static void cc770_rx(struct net_device *dev, unsigned int mo, u8 ctrl1)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u8 config, dlc;
+ u32 id;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ config = cc770_read_reg(priv, msgobj[mo].config);
+
+ if (ctrl1 & RMTPND_SET) {
+ /*
+ * Unfortunately, the chip does not store the real message
+ * identifier of the received remote transmission request
+ * frame. Therefore we set it to 0.
+ */
+ cf->can_id = CAN_RTR_FLAG;
+ if (config & MSGCFG_XTD)
+ cf->can_id |= CAN_EFF_FLAG;
+ cf->can_dlc = 0;
+ } else {
+ if (config & MSGCFG_XTD) {
+ id = cc770_read_reg(priv, msgobj[mo].id[3]);
+ id |= cc770_read_reg(priv, msgobj[mo].id[2]) << 8;
+ id |= cc770_read_reg(priv, msgobj[mo].id[1]) << 16;
+ id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 24;
+ id >>= 3;
+ id |= CAN_EFF_FLAG;
+ } else {
+ id = cc770_read_reg(priv, msgobj[mo].id[1]);
+ id |= cc770_read_reg(priv, msgobj[mo].id[0]) << 8;
+ id >>= 5;
+ }
+
+ dlc = (config & 0xf0) >> 4; /* strip length code */
+ if (dlc > 8)
+ dlc = 8; /* limit count to 8 bytes */
+
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++)
+ cf->data[i] = cc770_read_reg(priv, msgobj[mo].data[i]);
+ }
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static int cc770_err(struct net_device *dev, u8 status)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u8 lec;
+
+ dev_dbg(ND2D(dev), "status interrupt (%#x)\n", status);
+
+ 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 (status & STAT_BOFF) {
+ /* Disable interrupts */
+ cc770_write_reg(priv, control, CTRL_INI);
+ cf->can_id |= CAN_ERR_BUSOFF;
+ priv->can.state = CAN_STATE_BUS_OFF;
+ can_bus_off(dev);
+ } else if (status & STAT_WARN) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING;
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ priv->can.can_stats.error_warning++;
+ }
+
+ lec = status & STAT_LEC_MASK;
+ if (lec < 7 && lec > 0) {
+ if (lec == STAT_LEC_ACK) {
+ cf->can_id |= CAN_ERR_ACK;
+ } else {
+ cf->can_id |= CAN_ERR_PROT;
+ switch (lec) {
+ case STAT_LEC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ case STAT_LEC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case STAT_LEC_BIT1:
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ break;
+ case STAT_LEC_BIT0:
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ break;
+ case STAT_LEC_CRC:
+ cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ break;
+ }
+ }
+ }
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+static int cc770_status_interrupt(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ u8 status;
+
+ status = cc770_read_reg(priv, status);
+ /* Reset the status register including RXOK and TXOK */
+ cc770_write_reg(priv, status, STAT_LEC_MASK);
+
+ if (status & (STAT_WARN | STAT_BOFF) ||
+ (status & STAT_LEC_MASK) != STAT_LEC_MASK) {
+ cc770_err(dev, status);
+ return status & STAT_BOFF;
+ }
+
+ return 0;
+}
+
+static void cc770_rx_interrupt(struct net_device *dev, unsigned int o)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned int mo = obj2msgobj(o);
+ u8 ctrl1;
+
+ while (1) {
+ ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
+
+ if (!(ctrl1 & NEWDAT_SET)) {
+ /* Check for RTR if additional functions are enabled */
+ if (priv->control_normal_mode & CTRL_EAF) {
+ if (!(cc770_read_reg(priv, msgobj[mo].ctrl0) &
+ INTPND_SET))
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (ctrl1 & MSGLST_SET) {
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ }
+ if (mo < MSGOBJ_LAST)
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | MSGLST_RES |
+ TXRQST_UNC | RMTPND_UNC);
+ cc770_rx(dev, mo, ctrl1);
+
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_SET | TXIE_RES |
+ RXIE_SET | INTPND_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | MSGLST_RES |
+ TXRQST_RES | RMTPND_RES);
+ }
+}
+
+static void cc770_rtr_interrupt(struct net_device *dev, unsigned int o)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ unsigned int mo = obj2msgobj(o);
+ u8 ctrl0, ctrl1;
+
+ while (1) {
+ ctrl0 = cc770_read_reg(priv, msgobj[mo].ctrl0);
+ if (!(ctrl0 & INTPND_SET))
+ break;
+
+ ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
+ cc770_rx(dev, mo, ctrl1);
+
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_SET | TXIE_RES |
+ RXIE_SET | INTPND_RES);
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
+ NEWDAT_RES | CPUUPD_SET |
+ TXRQST_RES | RMTPND_RES);
+ }
+}
+
+static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned int mo = obj2msgobj(o);
+
+ /* Nothing more to send, switch off interrupts */
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
+ /*
+ * We had some cases of repeated IRQ so make sure the
+ * INT is acknowledged
+ */
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
+ MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
+
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+irqreturn_t cc770_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+irqreturn_t cc770_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct cc770_priv *priv = netdev_priv(dev);
+ u8 intid;
+ int o, n = 0;
+
+ /* Shared interrupts and IRQ off? */
+ if (priv->can.state == CAN_STATE_STOPPED)
+ return IRQ_NONE;
+
+ if (priv->pre_irq)
+ priv->pre_irq(priv);
+
+ while (n < CC770_MAX_IRQ) {
+ /* Read the highest pending interrupt request */
+ intid = cc770_read_reg(priv, interrupt);
+ if (!intid)
+ break;
+ n++;
+
+ if (intid == 1) {
+ /* Exit in case of bus-off */
+ if (cc770_status_interrupt(dev))
+ break;
+ } else {
+ o = intid2obj(intid);
+
+ if (o >= CC770_OBJ_MAX) {
+ dev_err(ND2D(dev),
+ "Unexpected interrupt id %d\n", intid);
+ continue;
+ }
+
+ if (priv->obj_flags[o] & CC770_OBJ_FLAG_RTR)
+ cc770_rtr_interrupt(dev, o);
+ else if (priv->obj_flags[o] & CC770_OBJ_FLAG_RX)
+ cc770_rx_interrupt(dev, o);
+ else
+ cc770_tx_interrupt(dev, o);
+ }
+ }
+
+ if (priv->post_irq)
+ priv->post_irq(priv);
+
+ if (n >= CC770_MAX_IRQ)
+ dev_dbg(ND2D(dev), "%d messages handled in ISR", n);
+
+ return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int cc770_open(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* common open */
+ err = open_candev(dev);
+ if (err)
+ return err;
+
+ err = request_irq(dev->irq, &cc770_interrupt, priv->irq_flags,
+ dev->name, (void *)dev);
+ if (err) {
+ close_candev(dev);
+ return -EAGAIN;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* clear statistics */
+ memset(&priv->can.net_stats, 0, sizeof(priv->can.net_stats));
+#endif
+
+ /* init and start chip */
+ cc770_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int cc770_close(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ set_reset_mode(dev);
+
+ free_irq(dev->irq, (void *)dev);
+ close_candev(dev);
+
+ priv->open_time = 0;
+
+ return 0;
+}
+
+struct net_device *alloc_cc770dev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct cc770_priv *priv;
+
+ dev = alloc_candev(sizeof(struct cc770_priv) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ priv->dev = dev;
+ priv->can.bittiming_const = &cc770_bittiming_const;
+ priv->can.do_set_bittiming = cc770_set_bittiming;
+ priv->can.do_set_mode = cc770_set_mode;
+
+ memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags));
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct cc770_priv);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_cc770dev);
+
+void free_cc770dev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL_GPL(free_cc770dev);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops cc770_netdev_ops = {
+ .ndo_open = cc770_open,
+ .ndo_stop = cc770_close,
+ .ndo_start_xmit = cc770_start_xmit,
+};
+#endif
+
+int register_cc770dev(struct net_device *dev)
+{
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ if (!cc770_probe_chip(dev))
+ return -ENODEV;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &cc770_netdev_ops;
+#else
+ dev->open = cc770_open;
+ dev->stop = cc770_close;
+ dev->hard_start_xmit = cc770_start_xmit;
+#endif
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ /* Should we use additional functions? */
+ if (!i82527_compat && priv->control_normal_mode & CTRL_EAF) {
+ priv->control_normal_mode = CTRL_IE | CTRL_EAF | CTRL_EIE;
+ dev_dbg(ND2D(dev), "i82527 mode with additional functions\n");
+ } else {
+ priv->control_normal_mode = CTRL_IE | CTRL_EIE;
+ dev_dbg(ND2D(dev), "strict i82527 compatibility mode\n");
+ }
+
+ chipset_init(priv);
+ set_reset_mode(dev);
+
+ return register_candev(dev);
+}
+EXPORT_SYMBOL_GPL(register_cc770dev);
+
+void unregister_cc770dev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_cc770dev);
+
+static __init int cc770_init(void)
+{
+ if (msgobj15_eff) {
+ cc770_obj_flags[CC770_OBJ_RX0] |= CC770_OBJ_FLAG_EFF;
+ cc770_obj_flags[CC770_OBJ_RX1] &= ~CC770_OBJ_FLAG_EFF;
+ }
+
+ printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME);
+
+ return 0;
+}
+
+module_init(cc770_init);
+
+static __exit void cc770_exit(void)
+{
+ printk(KERN_INFO "%s: driver removed\n", DRV_NAME);
+}
+module_exit(cc770_exit);
--- /dev/null
+/*
+ * $Id: $
+ *
+ * cc770.h - Bosch CC770 and Intel AN82527 network device driver
+ *
+ * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from the old Socket-CAN i82527 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>
+ */
+
+#ifndef CC770_DEV_H
+#define CC770_DEV_H
+
+#include <socketcan/can/dev.h>
+
+struct cc770_msgobj {
+ u8 ctrl0;
+ u8 ctrl1;
+ u8 id[4];
+ u8 config;
+ u8 data[8];
+ u8 dontuse; /* padding */
+} __attribute__ ((packed));
+
+struct cc770_regs {
+ union {
+ struct cc770_msgobj msgobj[16]; /* Message object 1..15 */
+ struct {
+ u8 control; /* Control Register */
+ u8 status; /* Status Register */
+ u8 cpu_interface; /* CPU Interface Register */
+ u8 dontuse1;
+ u8 high_speed_read[2]; /* High Speed Read */
+ u8 global_mask_std[2]; /* Standard Global Mask */
+ u8 global_mask_ext[4]; /* Extended Global Mask */
+ u8 msg15_mask[4]; /* Message 15 Mask */
+ u8 dontuse2[15];
+ u8 clkout; /* Clock Out Register */
+ u8 dontuse3[15];
+ u8 bus_config; /* Bus Configuration Register */
+ u8 dontuse4[15];
+ u8 bit_timing_0; /* Bit Timing Register byte 0 */
+ u8 dontuse5[15];
+ u8 bit_timing_1; /* Bit Timing Register byte 1 */
+ u8 dontuse6[15];
+ u8 interrupt; /* Interrupt Register */
+ u8 dontuse7[15];
+ u8 rx_error_counter; /* Receive Error Counter */
+ u8 dontuse8[15];
+ u8 tx_error_counter; /* Transmit Error Counter */
+ u8 dontuse9[31];
+ u8 p1_conf;
+ u8 dontuse10[15];
+ u8 p2_conf;
+ u8 dontuse11[15];
+ u8 p1_in;
+ u8 dontuse12[15];
+ u8 p2_in;
+ u8 dontuse13[15];
+ u8 p1_out;
+ u8 dontuse14[15];
+ u8 p2_out;
+ u8 dontuse15[15];
+ u8 serial_reset_addr;
+ };
+ };
+} __attribute__ ((packed));
+
+/* Control Register (0x00) */
+#define CTRL_INI 0x01 /* Initialization */
+#define CTRL_IE 0x02 /* Interrupt Enable */
+#define CTRL_SIE 0x04 /* Status Interrupt Enable */
+#define CTRL_EIE 0x08 /* Error Interrupt Enable */
+#define CTRL_EAF 0x20 /* Enable additional functions */
+#define CTRL_CCE 0x40 /* Change Configuration Enable */
+
+/* Status Register (0x01) */
+#define STAT_LEC_STUFF 0x01 /* Stuff error */
+#define STAT_LEC_FORM 0x02 /* Form error */
+#define STAT_LEC_ACK 0x03 /* Acknowledgement error */
+#define STAT_LEC_BIT1 0x04 /* Bit1 error */
+#define STAT_LEC_BIT0 0x05 /* Bit0 error */
+#define STAT_LEC_CRC 0x06 /* CRC error */
+#define STAT_LEC_MASK 0x07 /* Last Error Code mask */
+#define STAT_TXOK 0x08 /* Transmit Message Successfully */
+#define STAT_RXOK 0x10 /* Receive Message Successfully */
+#define STAT_WAKE 0x20 /* Wake Up Status */
+#define STAT_WARN 0x40 /* Warning Status */
+#define STAT_BOFF 0x80 /* Bus Off Status */
+
+/* CPU Interface Register (0x02) */
+#define CPUIF_CEN 0x01 /* Clock Out Enable */
+#define CPUIF_MUX 0x04 /* Multiplex */
+#define CPUIF_SLP 0x08 /* Sleep */
+#define CPUIF_PWD 0x10 /* Power Down Mode */
+#define CPUIF_DMC 0x20 /* Divide Memory Clock */
+#define CPUIF_DSC 0x40 /* Divide System Clock */
+#define CPUIF_RST 0x80 /* Hardware Reset Status */
+
+/* Clock Out Register (0x1f) */
+#define CLKOUT_CD_MASK 0x0f /* Clock Divider mask */
+#define CLKOUT_SL_MASK 0x30 /* Slew Rate mask */
+#define CLKOUT_SL_SHIFT 4
+
+/* Bus Configuration Register (0x2f) */
+#define BUSCFG_DR0 0x01 /* Disconnect RX0 Input / Select RX input */
+#define BUSCFG_DR1 0x02 /* Disconnect RX1 Input / Silent mode */
+#define BUSCFG_DT1 0x08 /* Disconnect TX1 Output */
+#define BUSCFG_POL 0x20 /* Polarity dominant or recessive */
+#define BUSCFG_CBY 0x40 /* Input Comparator Bypass */
+
+/* Message Control Register 0 (Base Address + 0x0) */
+#define INTPND_RES 0x01 /* No Interrupt pending */
+#define INTPND_SET 0x02 /* Interrupt pending */
+#define INTPND_UNC 0x03
+#define RXIE_RES 0x04 /* Receive Interrupt Disable */
+#define RXIE_SET 0x08 /* Receive Interrupt Enable */
+#define RXIE_UNC 0x0c
+#define TXIE_RES 0x10 /* Transmit Interrupt Disable */
+#define TXIE_SET 0x20 /* Transmit Interrupt Enable */
+#define TXIE_UNC 0x30
+#define MSGVAL_RES 0x40 /* Message Invalid */
+#define MSGVAL_SET 0x80 /* Message Valid */
+#define MSGVAL_UNC 0xc0
+
+/* Message Control Register 1 (Base Address + 0x01) */
+#define NEWDAT_RES 0x01 /* No New Data */
+#define NEWDAT_SET 0x02 /* New Data */
+#define NEWDAT_UNC 0x03
+#define MSGLST_RES 0x04 /* No Message Lost */
+#define MSGLST_SET 0x08 /* Message Lost */
+#define MSGLST_UNC 0x0c
+#define CPUUPD_RES 0x04 /* No CPU Updating */
+#define CPUUPD_SET 0x08 /* CPU Updating */
+#define CPUUPD_UNC 0x0c
+#define TXRQST_RES 0x10 /* No Transmission Request */
+#define TXRQST_SET 0x20 /* Transmission Request */
+#define TXRQST_UNC 0x30
+#define RMTPND_RES 0x40 /* No Remote Request Pending */
+#define RMTPND_SET 0x80 /* Remote Request Pending */
+#define RMTPND_UNC 0xc0
+
+/* Message Configuration Register (Base Address + 0x06) */
+#define MSGCFG_XTD 0x04 /* Extended Identifier */
+#define MSGCFG_DIR 0x08 /* Direction is Transmit */
+
+#define MSGOBJ_FIRST 1
+#define MSGOBJ_LAST 15
+
+#define CC770_IO_SIZE 0x100
+#define CC770_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
+
+#define cc770_read_reg(priv, member) \
+ priv->read_reg(priv, offsetof(struct cc770_regs, member))
+
+#define cc770_write_reg(priv, member, value) \
+ priv->write_reg(priv, offsetof(struct cc770_regs, member), value)
+
+/*
+ * Message objects and flags used by this driver
+ */
+#define CC770_OBJ_FLAG_RX 0x01
+#define CC770_OBJ_FLAG_RTR 0x02
+#define CC770_OBJ_FLAG_EFF 0x04
+
+enum {
+ CC770_OBJ_RX0 = 0, /* for receiving normal messages */
+ CC770_OBJ_RX1, /* for receiving normal messages */
+ CC770_OBJ_RX_RTR0, /* for receiving remote transmission requests */
+ CC770_OBJ_RX_RTR1, /* for receiving remote transmission requests */
+ CC770_OBJ_TX, /* for sending messages */
+ CC770_OBJ_MAX
+};
+
+#define obj2msgobj(o) (MSGOBJ_LAST - (o)) /* message object 11..15 */
+
+/*
+ * CC770 private data structure
+ */
+struct cc770_priv {
+ struct can_priv can; /* must be the first member */
+ int open_time;
+ struct sk_buff *echo_skb;
+
+ /* the lower-layer is responsible for appropriate locking */
+ u8 (*read_reg)(const struct cc770_priv *priv, int reg);
+ void (*write_reg)(const struct cc770_priv *priv, int reg, u8 val);
+ void (*pre_irq)(const struct cc770_priv *priv);
+ void (*post_irq)(const struct cc770_priv *priv);
+
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+
+ void __iomem *reg_base; /* ioremap'ed address to registers */
+ unsigned long irq_flags; /* for request_irq() */
+
+ unsigned char obj_flags[CC770_OBJ_MAX];
+ u8 control_normal_mode; /* Control register for normal mode */
+ u8 cpu_interface; /* CPU interface register */
+ u8 clkout; /* Clock out register */
+ u8 bus_config; /* Bus conffiguration register */
+};
+
+struct net_device *alloc_cc770dev(int sizeof_priv);
+void free_cc770dev(struct net_device *dev);
+int register_cc770dev(struct net_device *dev);
+void unregister_cc770dev(struct net_device *dev);
+
+#endif /* CC770_DEV_H */
--- /dev/null
+/*
+ * Copyright (C) 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/version.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+
+#include "cc770.h"
+
+#define DRV_NAME "cc770_isa"
+
+#define MAXDEV 8
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#error This driver does not support Kernel versions < 2.6.16
+#endif
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
+MODULE_LICENSE("GPL v2");
+
+#define CLK_DEFAULT 16000000 /* 16 MHz */
+#define BCR_DEFAULT 0x00
+#define COR_DEFAULT 0x00
+
+static unsigned long port[MAXDEV];
+static unsigned long mem[MAXDEV];
+static int __devinitdata irq[MAXDEV];
+static int __devinitdata clk[MAXDEV];
+static char __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+static char __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+static char __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+static char __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+
+module_param_array(port, ulong, NULL, S_IRUGO);
+MODULE_PARM_DESC(port, "I/O port number");
+
+module_param_array(mem, ulong, NULL, S_IRUGO);
+MODULE_PARM_DESC(mem, "I/O memory address");
+
+module_param_array(indirect, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
+
+module_param_array(irq, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(irq, "IRQ number");
+
+module_param_array(clk, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(clk, "External oscillator clock frequency "
+ "(default=16000000 [16 MHz])");
+
+module_param_array(cir, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(cdr, "CPU interface register (default=0x40 [CPU_DSC])");
+
+module_param_array(bcr, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(ocr, "Bus configuration register (default=0x00)");
+
+module_param_array(cor, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
+
+#define CC770_IOSIZE 0x20
+#define CC770_IOSIZE_INDIRECT 0x02
+
+static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
+{
+ return readb(priv->reg_base + reg);
+}
+
+static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
+ int reg, u8 val)
+{
+ writeb(val, priv->reg_base + reg);
+}
+
+static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
+{
+ return inb((unsigned long)priv->reg_base + reg);
+}
+
+static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
+ int reg, u8 val)
+{
+ outb(val, (unsigned long)priv->reg_base + reg);
+}
+
+static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
+ int reg)
+{
+ unsigned long base = (unsigned long)priv->reg_base;
+
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
+ int reg, u8 val)
+{
+ unsigned long base = (unsigned long)priv->reg_base;
+
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static int __devinit cc770_isa_match(struct device *pdev, unsigned int idx)
+{
+ if (port[idx] || mem[idx]) {
+ if (irq[idx])
+ return 1;
+ } else if (idx)
+ return 0;
+
+ dev_err(pdev, "insufficient parameters supplied\n");
+ return 0;
+}
+
+static int __devinit cc770_isa_probe(struct device *pdev, unsigned int idx)
+{
+ struct net_device *dev;
+ struct cc770_priv *priv;
+ void __iomem *base = NULL;
+ int iosize = CC770_IOSIZE;
+ int err;
+ u32 clktmp;
+
+ if (mem[idx]) {
+ if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+ base = ioremap_nocache(mem[idx], iosize);
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ } else {
+ if (indirect[idx] > 0 ||
+ (indirect[idx] == -1 && indirect[0] > 0))
+ iosize = CC770_IOSIZE_INDIRECT;
+ if (!request_region(port[idx], iosize, DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+ }
+
+ dev = alloc_cc770dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_unmap;
+ }
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[idx];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+ if (mem[idx]) {
+ priv->reg_base = base;
+ dev->base_addr = mem[idx];
+ priv->read_reg = cc770_isa_mem_read_reg;
+ priv->write_reg = cc770_isa_mem_write_reg;
+ } else {
+ priv->reg_base = (void __iomem *)port[idx];
+ dev->base_addr = port[idx];
+
+ if (iosize == CC770_IOSIZE_INDIRECT) {
+ priv->read_reg = cc770_isa_port_read_reg_indirect;
+ priv->write_reg = cc770_isa_port_write_reg_indirect;
+ } else {
+ priv->read_reg = cc770_isa_port_read_reg;
+ priv->write_reg = cc770_isa_port_write_reg;
+ }
+ }
+
+ if (clk[idx])
+ clktmp = clk[idx];
+ else if (clk[0])
+ clktmp = clk[0];
+ else
+ clktmp = CLK_DEFAULT;
+ priv->can.clock.freq = clktmp;
+
+ if (cir[idx] != -1) {
+ priv->cpu_interface = cir[idx] & 0xff;
+ } else if (cir[0] != -1) {
+ priv->cpu_interface = cir[0] & 0xff;
+ } else {
+ /* The system clock may not exceed 10 MHz */
+ if (clktmp > 10000000) {
+ priv->cpu_interface |= CPUIF_DSC;
+ clktmp /= 2;
+ }
+ /* The memory clock may not exceed 8 MHz */
+ if (clktmp > 8000000)
+ priv->cpu_interface |= CPUIF_DMC;
+ }
+
+ if (priv->cpu_interface & CPUIF_DSC)
+ priv->can.clock.freq /= 2;
+
+ if (bcr[idx] != -1)
+ priv->bus_config = bcr[idx] & 0xff;
+ else if (bcr[0] != -1)
+ priv->bus_config = bcr[0] & 0xff;
+ else
+ priv->bus_config = BCR_DEFAULT;
+
+ if (cor[idx] != -1)
+ priv->clkout = cor[idx] & 0xff;
+ else if (cor[0] != -1)
+ priv->clkout = cor[0] & 0xff;
+ else
+ priv->clkout = COR_DEFAULT;
+
+ dev_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, pdev);
+
+ err = register_cc770dev(dev);
+ if (err) {
+ dev_err(pdev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_unmap;
+ }
+
+ dev_info(pdev, "%s device registered (reg_base=0x%p, irq=%d)\n",
+ DRV_NAME, priv->reg_base, dev->irq);
+ return 0;
+
+ exit_unmap:
+ if (mem[idx])
+ iounmap(base);
+ exit_release:
+ if (mem[idx])
+ release_mem_region(mem[idx], iosize);
+ else
+ release_region(port[idx], iosize);
+ exit:
+ return err;
+}
+
+static int __devexit cc770_isa_remove(struct device *pdev, unsigned int idx)
+{
+ struct net_device *dev = dev_get_drvdata(pdev);
+ struct cc770_priv *priv = netdev_priv(dev);
+
+ unregister_cc770dev(dev);
+ dev_set_drvdata(pdev, NULL);
+
+ if (mem[idx]) {
+ iounmap(priv->reg_base);
+ release_mem_region(mem[idx], CC770_IOSIZE);
+ } else {
+ if (priv->read_reg == cc770_isa_port_read_reg_indirect)
+ release_region(port[idx], CC770_IOSIZE_INDIRECT);
+ else
+ release_region(port[idx], CC770_IOSIZE);
+ }
+ free_cc770dev(dev);
+
+ return 0;
+}
+
+static struct isa_driver cc770_isa_driver = {
+ .match = cc770_isa_match,
+ .probe = cc770_isa_probe,
+ .remove = __devexit_p(cc770_isa_remove),
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init cc770_isa_init(void)
+{
+ int err = isa_register_driver(&cc770_isa_driver, MAXDEV);
+
+ if (!err)
+ printk(KERN_INFO
+ "Legacy %s driver for max. %d devices registered\n",
+ DRV_NAME, MAXDEV);
+ return err;
+}
+
+static void __exit cc770_isa_exit(void)
+{
+ isa_unregister_driver(&cc770_isa_driver);
+}
+
+module_init(cc770_isa_init);
+module_exit(cc770_isa_exit);
--- /dev/null
+/*
+ * Driver for CC770 CAN controllers on the OpenFirmware platform bus
+ *
+ * Copyright (C) 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.
+ */
+
+/* This is a generic driver for CC770 chips on the OpenFirmware platform
+ * bus found on embedded PowerPC systems. You need a CC770 CAN node
+ * definition in your flattened device tree source (DTS) file similar to:
+ *
+ * can@3,100 {
+ * compatible = "bosch,cc770";
+ * reg = <3 0x100 0x80>;
+ * interrupts = <2 0>;
+ * interrupt-parent = <&mpic>;
+ * bosch,external-clock-frequency = <16000000>;
+ * };
+ *
+ * See "Documentation/powerpc/dts-bindings/can/cc770.txt" for further
+ * information.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+
+#include <linux/of_platform.h>
+#include <asm/prom.h>
+
+#include "cc770.h"
+
+#define DRV_NAME "cc770_of_platform"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the OF platform bus");
+MODULE_LICENSE("GPL v2");
+
+#define CC770_OFP_CAN_CLOCK 16000000
+
+static u8 cc770_ofp_read_reg(const struct cc770_priv *priv, int reg)
+{
+ return in_8(priv->reg_base + reg);
+}
+
+static void cc770_ofp_write_reg(const struct cc770_priv *priv, int reg, u8 val)
+{
+ out_8(priv->reg_base + reg, val);
+}
+
+static int __devexit cc770_ofp_remove(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct cc770_priv *priv = netdev_priv(dev);
+ struct resource res;
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ unregister_cc770dev(dev);
+ iounmap(priv->reg_base);
+ /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
+ free_cc770dev(dev);
+
+ of_address_to_resource(ofdev->node, 0, &res);
+ release_mem_region(res.start, resource_size(&res));
+
+ return 0;
+}
+
+static int __devinit cc770_ofp_probe(struct of_device *ofdev,
+ const struct of_device_id *id)
+{
+ struct device_node *np = ofdev->node;
+ struct net_device *dev;
+ struct cc770_priv *priv;
+ struct resource res;
+ const u32 *prop;
+ u32 clkext;
+ int err, irq, res_size, prop_size;
+ void __iomem *base;
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err) {
+ dev_err(&ofdev->dev, "invalid address\n");
+ return err;
+ }
+
+ res_size = resource_size(&res);
+
+ if (!request_mem_region(res.start, res_size, DRV_NAME)) {
+ dev_err(&ofdev->dev, "couldn't request %#llx..%#llx\n",
+ (unsigned long long)res.start,
+ (unsigned long long)res.end);
+ return -EBUSY;
+ }
+
+ base = ioremap_nocache(res.start, res_size);
+ if (!base) {
+ dev_err(&ofdev->dev, "couldn't ioremap %#llx..%#llx\n",
+ (unsigned long long)res.start,
+ (unsigned long long)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_cc770dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_dispose_irq;
+ }
+
+ priv = netdev_priv(dev);
+
+ priv->read_reg = cc770_ofp_read_reg;
+ priv->write_reg = cc770_ofp_write_reg;
+
+ prop = of_get_property(np, "bosch,external-clock-frequency",
+ &prop_size);
+ if (prop && (prop_size == sizeof(u32)))
+ clkext = *prop;
+ else
+ clkext = CC770_OFP_CAN_CLOCK; /* default */
+ priv->can.clock.freq = clkext;
+
+ /* The system clock may not exceed 10 MHz */
+ if (priv->can.clock.freq > 10000000) {
+ priv->cpu_interface |= CPUIF_DSC;
+ priv->can.clock.freq /= 2;
+ }
+
+ /* The memory clock may not exceed 8 MHz */
+ if (priv->can.clock.freq > 8000000)
+ priv->cpu_interface |= CPUIF_DMC;
+
+ if (of_get_property(np, "bosch,divide-memory-clock", NULL))
+ priv->cpu_interface |= CPUIF_DMC;
+ if (of_get_property(np, "bosch,iso-low-speed-mux", NULL))
+ priv->cpu_interface |= CPUIF_MUX;
+
+ if (of_get_property(np, "bosch,comperator-bypass", NULL))
+ priv->bus_config |= BUSCFG_CBY;
+ if (of_get_property(np, "bosch,disconnect-rx0-input", NULL))
+ priv->bus_config |= BUSCFG_DR0;
+ if (of_get_property(np, "bosch,disconnect-rx1-input", NULL))
+ priv->bus_config |= BUSCFG_DR1;
+ if (of_get_property(np, "bosch,disconnect-tx1-output", NULL))
+ priv->bus_config |= BUSCFG_DT1;
+ if (of_get_property(np, "bosch,polarity-dominant", NULL))
+ priv->bus_config |= BUSCFG_POL;
+
+ prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size);
+ if (prop && (prop_size == sizeof(u32)) && *prop > 0) {
+ u32 cdv = clkext / *prop;
+ int slew;
+
+ if (cdv > 0 && cdv < 16) {
+ priv->cpu_interface |= CPUIF_CEN;
+ priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
+
+ prop = of_get_property(np, "bosch,slew-rate",
+ &prop_size);
+ if (prop && (prop_size == sizeof(u32))) {
+ slew = *prop;
+ } else {
+ /* Determine default slew rate */
+ slew = (CLKOUT_SL_MASK >> CLKOUT_SL_SHIFT) -
+ ((cdv * clkext - 1) / 8000000);
+ if (slew < 0)
+ slew = 0;
+ }
+ priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
+ CLKOUT_SL_MASK;
+ } else {
+ dev_dbg(ND2D(dev), "invalid clock-out-frequency\n");
+ }
+
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+ priv->reg_base = base;
+
+ dev->irq = irq;
+
+ dev_info(&ofdev->dev,
+ "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
+ "bus_config=0x%02x clkout=0x%02x\n",
+ priv->reg_base, dev->irq, priv->can.clock.freq,
+ priv->cpu_interface, priv->bus_config, priv->clkout);
+
+ dev_set_drvdata(&ofdev->dev, dev);
+ SET_NETDEV_DEV(dev, &ofdev->dev);
+
+ err = register_cc770dev(dev);
+ if (err) {
+ dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free_cc770;
+ }
+
+ return 0;
+
+exit_free_cc770:
+ free_cc770dev(dev);
+exit_dispose_irq:
+ /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
+exit_unmap_mem:
+ iounmap(base);
+exit_release_mem:
+ release_mem_region(res.start, res_size);
+
+ return err;
+}
+
+static struct of_device_id __devinitdata cc770_ofp_table[] = {
+ {.compatible = "bosch,cc770"}, /* CC770 from Bosch */
+ {.compatible = "intc,82527"}, /* AN82527 from Intel CP */
+ {},
+};
+
+static struct of_platform_driver cc770_ofp_driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .probe = cc770_ofp_probe,
+ .remove = __devexit_p(cc770_ofp_remove),
+ .match_table = cc770_ofp_table,
+};
+
+static int __init cc770_ofp_init(void)
+{
+ return of_register_platform_driver(&cc770_ofp_driver);
+}
+module_init(cc770_ofp_init);
+
+static void __exit cc770_ofp_exit(void)
+{
+ return of_unregister_platform_driver(&cc770_ofp_driver);
+};
+module_exit(cc770_ofp_exit);
*
* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
- * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ * 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
*/
#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+
+#ifndef CONFIG_CAN_DEV_SYSFS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+#error "CAN netlink interface not support by this kernel version"
+#endif
+#include <socketcan/can/netlink.h>
#include <net/rtnetlink.h>
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#error "CAN sysfs interface not support by this kernel version"
#endif
-
#include "sysfs.h"
+#endif
#define MOD_DESC "CAN device driver interface"
* Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
* Copyright 2005 Stanislav Marek
* email: pisa@cmp.felk.cvut.cz
+ *
+ * Calculates proper bit-timing parameters for a specified bit-rate
+ * and sample-point, which can then be used to set the bit-timing
+ * registers of the CAN controller. You can find more information
+ * in the header file socketcan/can/netlink.h.
*/
static int can_update_spt(const struct can_bittiming_const *btc,
int sampl_pt, int tseg, int *tseg1, int *tseg2)
return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
}
-static int can_calc_bittiming(struct net_device *dev)
+static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
{
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;
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;
+ brp = priv->clock.freq / (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);
+ rate = priv->clock.freq / (brp * tsegall);
error = bt->bitrate - rate;
/* tseg brp biterror */
if (error < 0)
/* 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",
+ dev_err(ND2D(dev),
+ "bitrate error %ld.%ld%% too high\n",
error / 10, error % 10);
return -EDOM;
} else {
spt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2);
v64 = (u64)best_brp * 1000000000UL;
- do_div(v64, bt->clock);
+ do_div(v64, priv->clock.freq);
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;
-
+#ifndef CONFIG_CAN_DEV_SYSFS
+ /* real bit-rate */
+ bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1));
+ /* real sample point */
+ bt->sample_point = spt;
+#endif
return 0;
}
#else /* !CONFIG_CAN_CALC_BITTIMING */
-static int can_calc_bittiming(struct net_device *dev)
+static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt)
{
dev_err(ND2D(dev), "bit-timing calculation not available\n");
return -EINVAL;
}
#endif /* CONFIG_CAN_CALC_BITTIMING */
+
+#ifdef CONFIG_CAN_DEV_SYSFS
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);
}
+#endif
-static int can_fixup_bittiming(struct net_device *dev)
+/*
+ * Checks the validity of the specified bit-timing parameters prop_seg,
+ * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
+ * prescaler value brp. You can find more information in the header
+ * file socketcan/can/netlink.h.
+ */
+static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt)
{
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)
+ bt->sjw = 1;
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;
+ return -ERANGE;
- brp64 = (u64)bt->clock * (u64)bt->tq;
+ brp64 = (u64)priv->clock.freq * (u64)bt->tq;
if (btc->brp_inc > 1)
do_div(brp64, btc->brp_inc);
brp64 += 500000000UL - 1;
return -EINVAL;
alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
- bitrate = bt->clock / (bt->brp * alltseg);
- bt->bitrate = bitrate;
+ bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
+ bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
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)
+int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt)
{
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)
+ if (!bt->tq)
/* Determine bit-timing parameters */
- err = can_calc_bittiming(dev);
+ err = can_calc_bittiming(dev, bt);
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);
+ err = can_fixup_bittiming(dev, bt);
if (err)
return err;
}
return 0;
}
-EXPORT_SYMBOL_GPL(can_set_bittiming);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-struct net_device_stats *can_get_stats(struct net_device *dev)
-{
- struct can_priv *priv = netdev_priv(dev);
-
- return &priv->net_stats;
-}
-EXPORT_SYMBOL_GPL(can_get_stats);
-#endif
-
-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;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
- dev->get_stats = can_get_stats;
-#endif
-}
-
-/*
- * 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
* 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);
* 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.
+ * of the device driver. The driver must protect access to
+ * priv->echo_skb, if necessary.
*/
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 */
+ /* check flag whether this packet has to be looped back */
if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
kfree_skb(skb);
return;
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__);
+ dev_err(ND2D(dev), "%s: BUG! echo_skb is occupied!\n",
+ __func__);
kfree_skb(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.
+ * is handled in the device driver. The driver must protect
+ * access to priv->echo_skb, if necessary.
*/
void can_get_echo_skb(struct net_device *dev, int idx)
{
/*
* CAN device restart for bus-off recovery
*/
-int can_restart_now(struct net_device *dev)
+void can_restart(unsigned long data)
{
+ struct net_device *dev = (struct net_device *)data;
struct can_priv *priv = netdev_priv(dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
struct net_device_stats *stats = can_get_stats(dev);
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 */
- }
+ BUG_ON(netif_carrier_ok(dev));
+ /*
+ * No synchronization needed because the device is bus-off and
+ * no messages can come in or go out.
+ */
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;
+ if (skb == NULL) {
+ err = -ENOMEM;
+ goto restart;
+ }
skb->dev = dev;
skb->protocol = htons(ETH_P_CAN);
cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
- return 0;
+restart:
+ dev_dbg(ND2D(dev), "restarted\n");
+ priv->can_stats.restarts++;
+
+ /* Now restart the device */
+ err = priv->do_set_mode(dev, CAN_MODE_START);
+
+ netif_carrier_on(dev);
+ if (err)
+ dev_err(ND2D(dev), "Error %d during restart", err);
}
-static void can_restart_after(unsigned long data)
+int can_restart_now(struct net_device *dev)
{
- 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);
+ /*
+ * A manual restart is only permitted if automatic restart is
+ * disabled and the device is in the bus-off state
+ */
+ if (priv->restart_ms)
+ return -EINVAL;
+ if (priv->state != CAN_STATE_BUS_OFF)
+ return -EBUSY;
+
+ /* Runs as soon as possible in the timer context */
+ mod_timer(&priv->restart_timer, jiffies);
+
+ return 0;
}
/*
dev_dbg(ND2D(dev), "bus-off\n");
netif_carrier_off(dev);
+ priv->can_stats.bus_off++;
- 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);
- }
+ if (priv->restart_ms)
+ mod_timer(&priv->restart_timer,
+ jiffies + (priv->restart_ms * HZ) / 1000);
}
EXPORT_SYMBOL_GPL(can_bus_off);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+struct net_device_stats *can_get_stats(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ return &priv->net_stats;
+}
+EXPORT_SYMBOL_GPL(can_get_stats);
+#endif
+
+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;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = can_get_stats;
+#endif
+}
+
+/*
+ * 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;
+
+ init_timer(&priv->restart_timer);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_candev);
+
+/*
+ * Free space of the CAN network device
+ */
+void free_candev(struct net_device *dev)
+{
+ free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_candev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+static inline void setup_timer(struct timer_list * timer,
+ void (*function)(unsigned long),
+ unsigned long data)
+{
+ timer->function = function;
+ timer->data = data;
+ init_timer(timer);
+}
+#endif
+
/*
- * Cleanup function before the device gets closed.
+ * Common open function when the device gets opened.
*
- * This functions should be called in the close function of the device
+ * This function should be called in the open function of the device
* driver.
*/
-void can_close_cleanup(struct net_device *dev)
+int open_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
+#ifdef CONFIG_CAN_DEV_SYSFS
+ int err;
+#endif
+
+ if (!priv->bittiming.tq && !priv->bittiming.bitrate) {
+ dev_err(ND2D(dev), "bit-timing not yet defined\n");
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_CAN_DEV_SYSFS
+ err = can_get_bittiming(dev, &priv->bittiming);
+ if (err)
+ return err;
- if (priv->timer.expires) {
- del_timer(&priv->timer);
- priv->timer.expires = 0;
+ if (priv->do_set_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_bittiming(dev);
+ if (err)
+ return err;
}
+#endif
+ setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(open_candev);
+
+/*
+ * Common close function for cleanup before the device gets closed.
+ *
+ * This function should be called in the close function of the device
+ * driver.
+ */
+void close_candev(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (del_timer_sync(&priv->restart_timer))
+ dev_put(dev);
can_flush_echo_skb(dev);
}
-EXPORT_SYMBOL_GPL(can_close_cleanup);
+EXPORT_SYMBOL_GPL(close_candev);
+
+#ifndef CONFIG_CAN_DEV_SYSFS
+/*
+ * CAN netlink interface
+ */
+static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
+ [IFLA_CAN_STATE] = { .type = NLA_U32 },
+ [IFLA_CAN_CTRLMODE] = { .len = sizeof(struct can_ctrlmode) },
+ [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 },
+ [IFLA_CAN_RESTART] = { .type = NLA_U32 },
+ [IFLA_CAN_BITTIMING] = { .len = sizeof(struct can_bittiming) },
+ [IFLA_CAN_BITTIMING_CONST]
+ = { .len = sizeof(struct can_bittiming_const) },
+ [IFLA_CAN_CLOCK] = { .len = sizeof(struct can_clock) },
+};
+
+static int can_changelink(struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* We need synchronization with dev->stop() */
+ ASSERT_RTNL();
+
+ if (data[IFLA_CAN_CTRLMODE]) {
+ struct can_ctrlmode *cm;
+
+ /* Do not allow changing controller mode while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+ priv->ctrlmode &= ~cm->mask;
+ priv->ctrlmode |= cm->flags;
+ }
+
+ if (data[IFLA_CAN_BITTIMING]) {
+ struct can_bittiming bt;
+
+ /* Do not allow changing bittiming while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
+ if ((!bt.bitrate && !bt.tq) || (bt.bitrate && bt.tq))
+ return -EINVAL;
+ err = can_get_bittiming(dev, &bt);
+ if (err)
+ return err;
+ memcpy(&priv->bittiming, &bt, sizeof(bt));
+
+ if (priv->do_set_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_bittiming(dev);
+ if (err)
+ return err;
+ }
+ }
+
+ if (data[IFLA_CAN_RESTART_MS]) {
+ /* Do not allow changing restart delay while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
+ }
+
+ if (data[IFLA_CAN_RESTART]) {
+ /* Do not allow a restart while not running */
+ if (!(dev->flags & IFF_UP))
+ return -EINVAL;
+ err = can_restart_now(dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_ctrlmode cm = {.flags = priv->ctrlmode};
+ enum can_state state = priv->state;
+
+ if (priv->do_get_state)
+ priv->do_get_state(dev, &state);
+ NLA_PUT_U32(skb, IFLA_CAN_STATE, state);
+ NLA_PUT(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm);
+ NLA_PUT_U32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms);
+ NLA_PUT(skb, IFLA_CAN_BITTIMING,
+ sizeof(priv->bittiming), &priv->bittiming);
+ NLA_PUT(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock);
+ if (priv->bittiming_const)
+ NLA_PUT(skb, IFLA_CAN_BITTIMING_CONST,
+ sizeof(*priv->bittiming_const), priv->bittiming_const);
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ NLA_PUT(skb, IFLA_INFO_XSTATS,
+ sizeof(priv->can_stats), &priv->can_stats);
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int can_newlink(struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ return -EOPNOTSUPP;
+}
+
+static struct rtnl_link_ops can_link_ops __read_mostly = {
+ .kind = "can",
+ .maxtype = IFLA_CAN_MAX,
+ .policy = can_policy,
+ .setup = can_setup,
+ .newlink = can_newlink,
+ .changelink = can_changelink,
+ .fill_info = can_fill_info,
+ .fill_xstats = can_fill_xstats,
+};
+
+#endif /* !CONFIG_CAN_DEV_SYSFS */
+
+/*
+ * Register the CAN network device
+ */
+int register_candev(struct net_device *dev)
+{
+#ifdef CONFIG_CAN_DEV_SYSFS
+ int err;
+
+ err = register_netdev(dev);
+ if (!err)
+ can_create_sysfs(dev);
+
+ return err;
+#else
+ dev->rtnl_link_ops = &can_link_ops;
+ return register_netdev(dev);
+#endif
+}
+EXPORT_SYMBOL_GPL(register_candev);
+
+/*
+ * Unregister the CAN network device
+ */
+void unregister_candev(struct net_device *dev)
+{
+#ifdef CONFIG_CAN_DEV_SYSFS
+ can_remove_sysfs(dev);
+#endif
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_candev);
static __init int can_dev_init(void)
{
- printk(KERN_INFO MOD_DESC "\n");
+#ifndef CONFIG_CAN_DEV_SYSFS
+ int err;
+
+ err = rtnl_link_register(&can_link_ops);
+ if (!err)
+ printk(KERN_INFO MOD_DESC "\n");
+
+ return err;
+#else
+ printk(KERN_INFO MOD_DESC " using the deprecated SYSFS interface\n");
return 0;
+#endif
}
module_init(can_dev_init);
static __exit void can_dev_exit(void)
{
+#ifndef CONFIG_CAN_DEV_SYSFS
+ rtnl_link_unregister(&can_link_ops);
+#endif
}
module_exit(can_dev_exit);
+
+#ifndef CONFIG_CAN_DEV_SYSFS
+MODULE_ALIAS_RTNL_LINK("can");
+#endif
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/types.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/byteorder/generic.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
-#include <linux/can.h>
-#include <linux/can/error.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/error.h>
+#include <socketcan/can/dev.h>
#define DRV_NAME "esd_pci331"
MODULE_DEVICE_TABLE(pci, esd331_pci_tbl);
MODULE_SUPPORTED_DEVICE("esd CAN-PCI/331, CAN-CPCI/331, CAN-PMC/331");
+#ifndef PCI_DEVICE_ID_PLX_9030
+# define PCI_DEVICE_ID_PLX_9030 0x9030
+#endif
#ifndef PCI_DEVICE_ID_PLX_9050
-# define PCI_DEVICE_ID_PLX_9050 0x9050
+# define PCI_DEVICE_ID_PLX_9050 0x9050
+#endif
+#ifndef PCI_VENDOR_ID_ESDGMBH
+#define PCI_VENDOR_ID_ESDGMBH 0x12fe
#endif
#define ESD_PCI_SUB_SYS_ID_PCI331 0x0001
if (unlikely(skb == NULL))
return -ENOMEM;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ stats = can_get_stats(dev);
+#else
stats = &dev->stats;
+#endif
skb->dev = dev;
skb->protocol = htons(ETH_P_CAN);
static void esd331_irq_rx(struct net_device *dev, struct esd331_can_msg *msg,
int eff)
{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
struct net_device_stats *stats = &dev->stats;
+#endif
struct can_frame *cfrm;
struct sk_buff *skb;
int i;
switch (msg->data[1]) {
case ESD331_ERR_OK:
if (priv->can.state != CAN_STATE_STOPPED)
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
break;
case ESD331_ERR_WARN:
- if ((priv->can.state != CAN_STATE_BUS_WARNING)
+ if ((priv->can.state != CAN_STATE_ERROR_WARNING)
&& (priv->can.state != CAN_STATE_STOPPED)) {
priv->can.can_stats.error_warning++;
- priv->can.state = CAN_STATE_BUS_WARNING;
+ priv->can.state = CAN_STATE_ERROR_WARNING;
/* might be RX warning, too... */
esd331_create_err_frame(dev, CAN_ERR_CRTL,
static void esd331_handle_messages(struct esd331_pci *board)
{
+ struct net_device *dev;
struct esd331_priv *priv;
struct net_device_stats *stats;
struct esd331_can_msg msg;
|| (board->dev[msg.net] == NULL)))
continue;
- priv = netdev_priv(board->dev[msg.net]);
+ dev = board->dev[msg.net];
+ priv = netdev_priv(dev);
if (priv->can.state == CAN_STATE_STOPPED)
continue;
- stats = &board->dev[msg.net]->stats;
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ stats = can_get_stats(dev);
+#else
+ stats = &dev->stats;
+#endif
switch (msg.cmmd) {
case ESD331_I20_BCAN:
case ESD331_I20_EX_BCAN:
- esd331_irq_rx(board->dev[msg.net], &msg,
+ esd331_irq_rx(dev, &msg,
(msg.cmmd == ESD331_I20_EX_BCAN));
break;
case ESD331_I20_TXDONE:
case ESD331_I20_EX_TXDONE:
stats->tx_packets++;
- can_get_echo_skb(board->dev[msg.net], 0);
- netif_wake_queue(board->dev[msg.net]);
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
break;
case ESD331_I20_TXTOUT:
case ESD331_I20_EX_TXTOUT:
stats->tx_errors++;
stats->tx_dropped++;
- can_free_echo_skb(board->dev[msg.net], 0);
- netif_wake_queue(board->dev[msg.net]);
+ can_free_echo_skb(dev, 0);
+ netif_wake_queue(dev);
break;
case ESD331_I20_ERROR:
- esd331_handle_errmsg(board->dev[msg.net], &msg);
+ esd331_handle_errmsg(dev, &msg);
break;
default:
return IRQ_HANDLED;
}
-EXPORT_SYMBOL_GPL(esd331_interrupt);
/* also enables interrupt when no other net on card is openened yet */
static int esd331_open(struct net_device *dev)
struct esd331_priv *priv = netdev_priv(dev);
int err;
- err = can_set_bittiming(dev);
+ err = open_candev(dev);
if (err)
return err;
if (esd331_all_nets_stopped(priv->board))
esd331_enable_irq(priv->board->conf_addr);
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
netif_start_queue(dev);
return 0;
priv->can.state = CAN_STATE_STOPPED;
netif_stop_queue(dev);
- can_close_cleanup(dev);
+ close_candev(dev);
if (esd331_all_nets_stopped(priv->board))
esd331_disable_irq(priv->board->conf_addr);
static int esd331_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct esd331_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
struct net_device_stats *stats = &dev->stats;
+#endif
struct can_frame *cf = (struct can_frame *)skb->data;
int err;
switch (mode) {
case CAN_MODE_START:
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_queue_stopped(dev))
netif_wake_queue(dev);
#endif
dev->irq = pdev->irq;
+ /* Set and enable PCI interrupts */
dev->flags |= IFF_ECHO;
priv->can.do_set_bittiming = esd331_set_bittiming;
/*
- *
* CAN bus driver for Microchip 251x CAN Controller with SPI Interface
*
* MCP2510 support and bug fixes by Christian Pellegrin
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
#include <linux/spi/spi.h>
-#include <linux/can/dev.h>
-#include <linux/can/core.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/core.h>
#include <linux/if_arp.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/uaccess.h>
#include <linux/io.h>
-#include <linux/can/platform/mcp251x.h>
+#include <socketcan/can/platform/mcp251x.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#error This driver does not support Kernel versions < 2.6.22
+#endif
/* SPI interface instruction set */
-#define INSTRUCTION_WRITE 0x02
-#define INSTRUCTION_READ 0x03
+#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
+#define INSTRUCTION_RESET 0xC0
/* MPC251x registers */
#define CANSTAT 0x0e
#define CNF2 0x29
# define CNF2_BTLMODE 0x80
#define CNF3 0x28
-# define CNF3_SOF 0x08
-# define CNF3_WAKFIL 0x04
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
# define CNF3_PHSEG2_MASK 0x07
#define CANINTE 0x2b
# define CANINTE_MERRE 0x80
# 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
+# 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). */
dma_addr_t spi_rx_dma;
struct sk_buff *tx_skb;
+ int tx_len;
struct workqueue_struct *wq;
struct work_struct tx_work;
struct work_struct irq_work;
int restart_tx;
};
-static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+static void mcp251x_clean(struct mcp251x_priv *priv)
+{
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ if (priv->tx_len)
+ can_free_echo_skb(priv->net, 0);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+}
+
+/*
+ Note about handling of error return of mcp251x_spi_trans: accessing
+ registers via SPI is not really different conceptually than using
+ normal I/O assembler instructions, although it's much more
+ complicated from a practical POV. So it's not advisable to always
+ check the return value of this function. Imagine that every
+ read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
+ error();", it would be a great mess (well there are some situation
+ when exception handling C++ like could be useful after all). So we
+ just check that transfers are OK at the beginning of our
+ conversation with the chip and to avoid doing really nasty things
+ (like injecting bogus packets in the network stack).
+ */
+static int mcp251x_spi_trans(struct spi_device *spi, int len)
{
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,
+ .len = len,
.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) {
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];
+ dev_err(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ return ret;
+}
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u8 val = 0;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ mcp251x_spi_trans(spi, 3);
+ 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[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);
+ mcp251x_spi_trans(spi, 3);
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[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);
+ mcp251x_spi_trans(spi, 4);
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,
+static void 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;
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);
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);
+ mcp251x_spi_trans(spi, 6 + CAN_FRAME_MAX_DATA_LEN);
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 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",
+ dev_err(&spi->dev, "%s: out of memory for Rx'd frame\n",
__func__);
priv->net->stats.rx_dropped++;
return;
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;
+ int ret;
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);
-
+ ret = mcp251x_spi_trans(spi, 14);
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;
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");
+ if (priv->tx_skb || priv->tx_len) {
+ dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
+ netif_stop_queue(net);
return NETDEV_TX_BUSY;
}
if (skb->len != sizeof(struct can_frame)) {
- dev_dbg(&spi->dev, "dropping packet - bad length\n");
+ dev_err(&spi->dev, "dropping packet - bad length\n");
dev_kfree_skb(skb);
net->stats.tx_dropped++;
return 0;
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) {
+ case CAN_MODE_START:
+ /* we have to delay work since SPI I/O may sleep */
+ priv->restart_tx = 1;
+ queue_work(priv->wq, &priv->irq_work);
+ break;
default:
return -EOPNOTSUPP;
}
/* Wait for the device to enter normal mode */
timeout = jiffies + HZ;
while (mcp251x_read_reg(spi, CANSTAT) & 0xE0) {
- udelay(10);
+ schedule();
if (time_after(jiffies, timeout)) {
dev_err(&spi->dev, "MCP251x didn't"
" enter in normal mode\n");
- break;
+ return;
}
}
}
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
}
static int mcp251x_do_set_bittiming(struct net_device *net)
return 0;
}
-static void mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+static int 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)
+ ret = open_candev(net);
+ if (ret) {
dev_err(&spi->dev, "unable to set initial baudrate!\n");
- else
- mcp251x_do_set_bittiming(net);
+ return ret;
+ }
/* Enable RX0->RX1 buffer roll over and disable filters */
mcp251x_write_bits(spi, RXBCTRL(0),
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)));
+ return 0;
}
static void mcp251x_hw_reset(struct spi_device *spi)
mutex_unlock(&priv->spi_lock);
if (ret < 0)
- dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ dev_err(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
/* wait for reset to finish */
mdelay(10);
}
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__);
+ int ret;
if (pdata->transceiver_enable)
pdata->transceiver_enable(1);
priv->force_quit = 0;
priv->tx_skb = NULL;
+ priv->tx_len = 0;
enable_irq(spi->irq);
mcp251x_hw_wakeup(spi);
mcp251x_hw_reset(spi);
- mcp251x_setup(net, priv, spi);
+ ret = mcp251x_setup(net, priv, spi);
+ if (ret < 0) {
+ disable_irq(spi->irq);
+ return ret;
+ }
mcp251x_set_normal_mode(spi);
netif_wake_queue(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);
flush_workqueue(priv->wq);
mcp251x_write_reg(spi, TXBCTRL(0), 0);
- if (priv->tx_skb) {
+ if (priv->tx_skb || priv->tx_len) {
net->stats.tx_errors++;
- dev_kfree_skb(priv->tx_skb);
- priv->tx_skb = NULL;
+ mcp251x_clean(priv);
}
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;
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(net);
return 0;
}
struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
tx_work);
struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
struct can_frame *frame;
- dev_dbg(&spi->dev, "%s\n", __func__);
-
+ WARN_ON(!priv->tx_skb);
+ WARN_ON(priv->tx_len);
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);
+ priv->tx_len = 1 + frame->can_dlc;
+ can_put_echo_skb(priv->tx_skb, net, 0);
+ priv->tx_skb = NULL;
}
}
struct net_device *net = priv->net;
u8 intf;
u8 txbnctrl;
+ enum can_state new_state;
if (priv->after_suspend) {
- /* Wait whilst the device wakes up */
+ /* Wait whilst the device wakes up WARN_ON */
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) {
+ if (priv->tx_skb || priv->tx_len) {
net->stats.tx_errors++;
- dev_kfree_skb(priv->tx_skb);
- priv->tx_skb = NULL;
+ mcp251x_clean(priv);
netif_wake_queue(net);
}
mcp251x_set_normal_mode(spi);
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");
+ dev_dbg(&spi->dev,
+ "timeout in txing a packet, restarting\n");
mcp251x_write_reg(spi, TXBCTRL(0), 0);
- if (priv->tx_skb) {
+ if (priv->tx_skb || priv->tx_len) {
net->stats.tx_errors++;
- dev_kfree_skb(priv->tx_skb);
- priv->tx_skb = NULL;
+ mcp251x_clean(priv);
}
+ priv->can.can_stats.restarts++;
netif_wake_queue(net);
}
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 */
+ /* if there are pending Tx buffers, restart queue */
txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
if (!(txbnctrl & TXBCTRL_TXREQ)) {
- if (priv->tx_skb) {
+ WARN_ON(priv->tx_skb);
+ WARN_ON(!priv->tx_len);
+ if (priv->tx_skb || priv->tx_len) {
net->stats.tx_errors++;
- dev_kfree_skb(priv->tx_skb);
- priv->tx_skb = NULL;
+ mcp251x_clean(priv);
}
netif_wake_queue(net);
}
struct can_frame *frame = NULL;
u8 eflag = mcp251x_read_reg(spi, EFLG);
- dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflag);
-
- /* Create error frame */
+ /* create error frame */
skb = dev_alloc_skb(sizeof(struct can_frame));
if (skb) {
frame = (struct can_frame *)
}
}
+ /* update net stats */
if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
if (eflag & EFLG_RX0OVR)
net->stats.rx_over_errors++;
CAN_ERR_CRTL_RX_OVERFLOW;
}
}
+
+ /* update can state */
+ if (eflag & EFLG_TXBO) {
+ new_state = CAN_STATE_BUS_OFF;
+ can_bus_off(net);
+ } else if (eflag & EFLG_TXEP)
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ else if (eflag & EFLG_RXEP)
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ else if (eflag & EFLG_TXWAR)
+ new_state = CAN_STATE_ERROR_WARNING;
+ else if (eflag & EFLG_RXWAR)
+ new_state = CAN_STATE_ERROR_WARNING;
+ else
+ new_state = CAN_STATE_ERROR_ACTIVE;
+
mcp251x_write_reg(spi, EFLG, 0x00);
if (skb)
netif_rx(skb);
+ } else
+ new_state = CAN_STATE_ERROR_ACTIVE;
+
+ /* update can state statistics */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (new_state >= CAN_STATE_ERROR_WARNING &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ case CAN_STATE_ERROR_WARNING: /* fallthrough */
+ if (new_state >= CAN_STATE_ERROR_PASSIVE &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ case CAN_STATE_BUS_OFF:
+ if (new_state <= CAN_STATE_ERROR_PASSIVE)
+ netif_carrier_on(net);
+ break;
+ default:
+ break;
}
+ priv->can.state = new_state;
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;
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += priv->tx_len - 1;
+ WARN_ON(priv->tx_skb);
+ WARN_ON(!priv->tx_len);
+ if (priv->tx_len) {
+ can_get_echo_skb(net, 0);
+ priv->tx_len = 0;
}
+ mcp251x_clean(priv);
netif_wake_queue(net);
}
}
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);
-}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops mcp251x_netdev_ops = {
+ .ndo_open = mcp251x_open,
+ .ndo_stop = mcp251x_stop,
+ .ndo_start_xmit = mcp251x_hard_start_xmit,
+};
+#endif
-static struct net_device *alloc_mcp251x_netdev(int sizeof_priv)
+static struct net_device *alloc_mcp251x_netdev(int sizeof_priv,
+ struct mcp251x_platform_data *pdata)
{
struct net_device *net;
struct mcp251x_priv *priv;
priv = netdev_priv(net);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ net->netdev_ops = &mcp251x_netdev_ops;
+#else
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;
+#endif
+ net->flags |= IFF_ECHO;
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->can.clock.freq = pdata->oscillator_frequency / 2;
+ priv->can.do_set_bittiming = mcp251x_do_set_bittiming;
priv->net = net;
}
/* Allocate can/net device */
- net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv));
+ net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv), pdata);
if (!net) {
ret = -ENOMEM;
goto error_alloc;
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;
mscan-mpc52xx-objs := mscan.o mpc52xx_can.o
-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
endif
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
#include <linux/of_platform.h>
#include <sysdev/fsl_soc.h>
#include "mscan.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
unsigned int freq;
u32 val;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
freq = mpc52xx_find_ipb_freq(np);
+#else
+ freq = mpc5xxx_get_bus_frequency(np);
+#endif
if (!freq)
return 0;
pvr = mfspr(SPRN_PVR);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
return mpc52xx_find_ipb_freq(np);
+#else
+ if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
+ return mpc5xxx_get_bus_frequency(np);
+#endif
return mpc52xx_can_xtal_freq(np);
}
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) {
+ priv->clock.freq = mpc52xx_can_clock_freq(np, clock_src);
+ if (!priv->clock.freq) {
dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n");
err = -ENODEV;
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);
+ dev->base_addr, dev->irq, priv->clock.freq);
return 0;
#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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
#include "mscan.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define MSCAN_NORMAL_MODE 0
#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)
+#define BTR1_SET_SAM(sam) ((sam) ? 1 << BTR1_SAM_SHIFT : 0)
static struct can_bittiming_const mscan_bittiming_const = {
+ .name = "mscan",
.tseg1_min = 4,
.tseg1_max = 16,
.tseg2_min = 2,
#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_ERROR_ACTIVE,
+ CAN_STATE_ERROR_WARNING,
+ CAN_STATE_ERROR_PASSIVE,
CAN_STATE_BUS_OFF
};
if (mode != MSCAN_NORMAL_MODE) {
if (priv->tx_active) {
- /* Abort transfers before going to sleep */
- out_8(®s->cantier, 0);
+ /* Abort transfers before going to sleep */#
out_8(®s->cantarq, priv->tx_active);
- out_8(®s->cantier, priv->tx_active);
+ /* Suppress TX done interrupts */
+ out_8(®s->cantier, 0);
}
canctl1 = in_8(®s->canctl1);
if (i >= MSCAN_SET_MODE_RETRIES)
ret = -ENODEV;
else
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
}
}
return ret;
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;
}
frame->can_id |= CAN_ERR_CRTL;
frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
stats->rx_over_errors++;
+ stats->rx_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:
+ case CAN_STATE_ERROR_WARNING:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_warning++;
if ((priv->shadow_statflg &
MSCAN_RSTAT_MSK) <
(canrflg & MSCAN_RSTAT_MSK))
frame->data[1] |=
- CAN_ERR_CRTL_RX_WARNING;
+ CAN_ERR_CRTL_RX_WARNING;
if ((priv->shadow_statflg &
MSCAN_TSTAT_MSK) <
frame->data[1] |=
CAN_ERR_CRTL_TX_WARNING;
break;
- case CAN_STATE_BUS_PASSIVE:
+ case CAN_STATE_ERROR_PASSIVE:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_passive++;
frame->data[1] |=
- CAN_ERR_CRTL_RX_PASSIVE;
+ CAN_ERR_CRTL_RX_PASSIVE;
break;
case CAN_STATE_BUS_OFF:
frame->can_id |= CAN_ERR_BUSOFF;
- frame->can_id &= ~CAN_ERR_CRTL;
+ can_bus_off(dev);
break;
default:
break;
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);
+ /* common open */
+ ret = open_candev(dev);
if (ret)
return ret;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
napi_enable(&priv->napi);
#endif
- ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
+ ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
if (ret < 0) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
napi_disable(&priv->napi);
struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
struct mscan_priv *priv = netdev_priv(dev);
+ netif_stop_queue(dev);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
napi_disable(&priv->napi);
#endif
out_8(®s->cantier, 0);
out_8(®s->canrier, 0);
- free_irq(dev->irq, dev);
mscan_set_mode(dev, MSCAN_INIT_MODE);
- can_close_cleanup(dev);
- netif_stop_queue(dev);
+ close_candev(dev);
+ free_irq(dev->irq, dev);
priv->open_time = 0;
return 0;
#endif
struct net_device *alloc_mscandev(void);
-/* clock_src:
+/*
+ * clock_src:
* 1 = The MSCAN clock source is the onchip Bus Clock.
* 0 = The MSCAN clock source is the chip Oscillator Clock.
*/
-include $(TOPDIR)/Makefile.common
-obj-$(CONFIG_CAN_CCAN) += ccan.o
-obj-$(CONFIG_CAN_H7202) += h7202_can.o
+obj-$(CONFIG_CAN_CCAN_OLD) += ccan.o
+obj-$(CONFIG_CAN_H7202_OLD) += h7202_can.o
endif
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <asm/io.h>
#endif
-#include <linux/can/dev.h>
-#include <linux/can/error.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
#include "ccan.h"
static u32 ccan_read_reg32(struct net_device *dev, enum c_regs reg)
#ifndef __CCAN_H__
#define __CCAN_H__
-#include <linux/can.h>
+#include <socketcan/can.h>
#include <linux/platform_device.h>
#undef CCAN_DEBUG
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
#include <linux/if_ether.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include <socketcan/can.h>
+#include <socketcan/can/ioctl.h> /* for struct can_device_stats */
#include "hal.h"
#include "i82527.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
return 0;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops can_netdev_ops = {
+ .ndo_open = can_open,
+ .ndo_stop = can_close,
+ .ndo_start_xmit = can_start_xmit,
+ .ndo_tx_timeout = can_tx_timeout,
+};
+#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;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
- dev->hard_header_cache = NULL;
- dev->header_cache_update = NULL;
- dev->hard_header_parse = NULL;
-#else
- dev->header_ops = NULL;
-#endif
-
dev->type = ARPHRD_CAN;
dev->hard_header_len = 0;
dev->mtu = sizeof(struct can_frame);
dev->flags = IFF_NOARP;
dev->features = NETIF_F_NO_CSUM;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &can_netdev_ops;
+#else
dev->open = can_open;
dev->stop = can_close;
dev->hard_start_xmit = can_start_xmit;
+ dev->tx_timeout = can_tx_timeout;
+#endif
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
dev->get_stats = can_get_stats;
#endif
- dev->tx_timeout = can_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
}
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/ioctl.h>
+#include <socketcan/can.h>
+#include <socketcan/can/ioctl.h>
#include "i82527.h"
#include "hal.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
extern struct net_device *can_dev[];
&& (priv->state != STATE_RESET_MODE)) {
len += snprintf(page + len, PAGE_SIZE - len,
"%s ", dev->name);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops->ndo_stop(dev);
+ dev->netdev_ops->ndo_open(dev);
+#else
dev->stop(dev);
dev->open(dev);
+#endif
/* count number of restarts */
priv->can_stats.restarts++;
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
#include <asm/io.h>
#include <asm/mpc52xx.h>
#include "mscan.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
#include <linux/list.h>
#include <asm/io.h>
-#include <linux/can/dev.h>
-#include <linux/can/error.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
#include "mscan.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define MSCAN_NORMAL_MODE 0
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/ioctl.h>
+#include <socketcan/can.h>
+#include <socketcan/can/ioctl.h>
#include "sja1000.h"
#include "hal.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
extern struct net_device *can_dev[];
&& (priv->state != STATE_RESET_MODE)) {
len += snprintf(page + len, PAGE_SIZE - len,
"%s ", dev->name);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops->ndo_stop(dev);
+ dev->netdev_ops->ndo_open(dev);
+#else
dev->stop(dev);
dev->open(dev);
+#endif
/* count number of restarts */
priv->can_stats.restarts++;
#include <linux/if_ether.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include <socketcan/can.h>
+#include <socketcan/can/ioctl.h> /* for struct can_device_stats */
#include "sja1000.h"
#include "hal.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
}
#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops can_netdev_ops = {
+ .ndo_open = can_open,
+ .ndo_stop = can_close,
+ .ndo_start_xmit = can_start_xmit,
+ .ndo_tx_timeout = can_tx_timeout,
+};
+#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;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
- dev->hard_header_cache = NULL;
- dev->header_cache_update = NULL;
- dev->hard_header_parse = NULL;
-#else
- dev->header_ops = NULL;
-#endif
-
dev->type = ARPHRD_CAN;
dev->hard_header_len = 0;
dev->mtu = sizeof(struct can_frame);
dev->features = NETIF_F_NO_CSUM;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &can_netdev_ops;
+#else
dev->open = can_open;
dev->stop = can_close;
dev->hard_start_xmit = can_start_xmit;
+ dev->tx_timeout = can_tx_timeout;
+#endif
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
dev->get_stats = can_get_stats;
#endif
- dev->tx_timeout = can_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
}
else
-include $(TOPDIR)/Makefile.common
-#EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/hal
obj-$(CONFIG_CAN_SJA1000) += sja1000.o
+obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.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_EMS_104M) += ems_104m.o
obj-$(CONFIG_CAN_ESD_PCI) += esd_pci.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
+ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
endif
--- /dev/null
+/*
+ * Copyright (C) 2009 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/version.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_104m"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#error This driver does not support Kernel versions < 2.6.16
+#endif
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-104M cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-104M CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_104M_MAX_DEV 4
+
+static unsigned long __devinitdata mem[EMS_104M_MAX_DEV];
+static int __devinitdata irq[EMS_104M_MAX_DEV];
+
+module_param_array(mem, ulong, NULL, S_IRUGO);
+MODULE_PARM_DESC(mem, "I/O memory address");
+
+module_param_array(irq, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(irq, "IRQ number");
+
+#define EMS_104M_MAX_CHAN 4
+
+struct ems_104m_card {
+ int channels;
+
+ struct net_device *net_dev[EMS_104M_MAX_CHAN];
+
+ void __iomem *base;
+ int irq;
+};
+
+#define EMS_104M_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_104M_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_104M_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_104M_MEM_SIZE 0x2000 /* Size of the remapped io-memory */
+#define EMS_104M_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */
+#define EMS_104M_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */
+
+#define EMS_104M_CARD_REG_IRQ_CTRL 7
+#define EMS_104M_CARD_REG_IRQ_STATUS 8
+#define EMS_104M_CARD_REG_VERSION 9
+
+#define EMS_104M_CARD_REG_CONTROL 4
+#define EMS_104M_CARD_REG_STATUS 6
+
+#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 u8 ems_104m_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + port);
+}
+
+static void ems_104m_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
+{
+ writeb(val, priv->reg_base + port);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t ems_104m_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+#else
+static irqreturn_t ems_104m_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct ems_104m_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_104M_MAX_CHAN; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ if (sja1000_interrupt(irq, dev, regs) == IRQ_HANDLED)
+ again = 1;
+#else
+ if (sja1000_interrupt(irq, dev) == IRQ_HANDLED)
+ again = 1;
+#endif
+ }
+
+ /* 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_104m_check_chan(struct sja1000_priv *priv)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_104m_write_reg(priv, REG_MOD, 1);
+
+ ems_104m_write_reg(priv, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_104m_read_reg(priv, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Probe ISA device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_104m_probe(struct device *pdev, unsigned int idx)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_104m_card *card;
+
+ int err, i;
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_104m_card), GFP_KERNEL);
+ if (card == NULL) {
+ dev_err(pdev, "couldn't allocate memory\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(pdev, card);
+
+ card->channels = 0;
+ card->irq = irq[idx];
+
+ card->base = ioremap_nocache(mem[idx], EMS_104M_MEM_SIZE);
+ if (card->base == NULL) {
+ dev_err(pdev, "couldn't map memory\n");
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ /* Check for unique EMS CAN signature */
+ if (readw(card->base) != 0xAA55) {
+ dev_err(pdev, "No EMS CPC Card hardware found.\n");
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ writeb(EMS_CMD_RESET, card->base);
+
+ /* Wait for reset to finish */
+ i = 0;
+ while (readb(card->base + EMS_104M_CARD_REG_STATUS) == 0x01) {
+ /* Check for timeout (50ms.) */
+ if (i >= 50) {
+ dev_err(pdev, "couldn't reset card.\n");
+
+ err = -EBUSY;
+ goto failure_cleanup;
+ }
+
+ msleep(1);
+ }
+
+ /* Make sure CAN controllers are mapped into card's memory space */
+ writeb(EMS_CMD_MAP, card->base);
+ writeb(EMS_CMD_MAP, card->base); /* Second call to workaround bug */
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_104M_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);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+ dev->irq = irq[idx];
+ priv->reg_base = card->base + EMS_104M_CAN_BASE_OFFSET
+ + (i * EMS_104M_CAN_CTRL_SIZE);
+
+ /* Check if channel is present */
+ if (ems_104m_check_chan(priv)) {
+ priv->read_reg = ems_104m_read_reg;
+ priv->write_reg = ems_104m_write_reg;
+ priv->can.clock.freq = EMS_104M_CAN_CLOCK;
+ priv->ocr = EMS_104M_OCR;
+ priv->cdr = EMS_104M_CDR;
+ priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(pdev, "registering device failed"
+ " (err=%d)\n", err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ /* Enable interrupts of this channel */
+ writeb(0x3 << (i * 2),
+ card->base + EMS_104M_CARD_REG_IRQ_CTRL);
+
+ card->channels++;
+
+ dev_info(pdev, "registered %s on channel at 0x%p,"
+ " irq %d\n", dev->name, priv->reg_base, dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ err = request_irq(card->irq, &ems_104m_interrupt, SA_SHIRQ,
+ DRV_NAME, (void *)card);
+#else
+ err = request_irq(card->irq, &ems_104m_interrupt, IRQF_SHARED,
+ DRV_NAME, (void *)card);
+#endif
+
+ if (err) {
+ dev_err(pdev, "registering device failed (err=%d)\n", err);
+ goto failure_cleanup;
+ }
+
+ return 0;
+
+failure_cleanup:
+ dev_err(pdev, "error: %d. Cleaning Up.\n", err);
+
+ if (card->base)
+ iounmap(card->base);
+
+ kfree(card);
+
+ return err;
+}
+
+/*
+ * Release claimed resources
+ */
+static int __devexit ems_104m_remove(struct device *pdev, unsigned int idx)
+{
+ struct ems_104m_card *card = dev_get_drvdata(pdev);
+ struct net_device *dev;
+ int i = 0;
+
+ if (!card)
+ return 0;
+
+ free_irq(card->irq, card);
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ dev_info(pdev, "removing %s on channel #%d\n", dev->name, i);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ writeb(EMS_CMD_UMAP, card->base);
+
+ if (card->base != NULL)
+ iounmap(card->base);
+
+ kfree(card);
+
+ dev_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static int __devinit ems_104m_match(struct device *pdev, unsigned int idx)
+{
+ if (!mem[idx])
+ return 0;
+
+ if (!irq[idx]) {
+ dev_err(pdev, "insufficient parameters supplied\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct isa_driver ems_104m_driver = {
+ .match = ems_104m_match,
+ .probe = ems_104m_probe,
+ .remove = __devexit_p(ems_104m_remove),
+
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init ems_104m_init(void)
+{
+ int err = isa_register_driver(&ems_104m_driver, EMS_104M_MAX_DEV);
+
+ if (!err)
+ printk(KERN_INFO
+ "Legacy %s driver for max. %d devices registered\n",
+ DRV_NAME, EMS_104M_MAX_DEV);
+
+ return err;
+}
+
+static void __exit ems_104m_exit(void)
+{
+ isa_unregister_driver(&ems_104m_driver);
+}
+
+module_init(ems_104m_init);
+module_exit(ems_104m_exit);
+
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
#define DRV_NAME "ems_pci"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#error This driver does not support Kernel versions < 2.6.23
+#endif
+
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
+#define EMS_PCI_V1_MAX_CHAN 2
+#define EMS_PCI_V2_MAX_CHAN 4
+#define EMS_PCI_MAX_CHAN EMS_PCI_V2_MAX_CHAN
struct ems_pci_card {
+ int version;
int channels;
struct pci_dev *pci_dev;
#define PITA2_MISC 0x1c /* Miscellaneous Register */
#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
+/*
+ * Register definitions for the PLX 9030
+ */
+#define PLX_ICSR 0x4c /* Interrupt Control/Status register */
+#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
+#define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */
+#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
+#define PLX_ICSR_ENA_CLR (PLX_ICSR_LINTI1_ENA | PLX_ICSR_PCIINT_ENA | \
+ PLX_ICSR_LINTI1_CLR)
+
/*
* 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.
+ * This means normal output mode, push-pull and the correct polarity.
*/
#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
* 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_V1_BASE_BAR 1
+#define EMS_PCI_V1_MEM_SIZE 4096
+#define EMS_PCI_V2_BASE_BAR 2
+#define EMS_PCI_V2_MEM_SIZE 128
#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,},
+ /* CPC-PCI v1 */
+ {PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,},
+ /* CPC-PCI v2 */
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4000},
+ /* CPC-104P v2 */
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_PLX, 0x4002},
{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)
+static u8 ems_pci_v1_readb(struct ems_pci_card *card, unsigned int port)
{
- return readb((void __iomem *)card->base_addr
- + (port * EMS_PCI_PORT_BYTES));
+ return readb(card->base_addr + (port * 4));
}
-static u8 ems_pci_read_reg(struct net_device *dev, int port)
+static u8 ems_pci_v1_read_reg(const struct sja1000_priv *priv, int port)
{
- return readb((void __iomem *)dev->base_addr
- + (port * EMS_PCI_PORT_BYTES));
+ return readb(priv->reg_base + (port * 4));
}
-static void ems_pci_write_reg(struct net_device *dev, int port, u8 val)
+static void ems_pci_v1_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
{
- writeb(val, (void __iomem *)dev->base_addr
- + (port * EMS_PCI_PORT_BYTES));
+ writeb(val, priv->reg_base + (port * 4));
}
-static void ems_pci_post_irq(struct net_device *dev)
+static void ems_pci_v1_post_irq(const struct sja1000_priv *priv)
{
- 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);
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
+ card->conf_addr + PITA2_ICR);
+}
+
+static u8 ems_pci_v2_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + port);
+}
+
+static void ems_pci_v2_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
+{
+ writeb(val, priv->reg_base + port);
+}
+
+static void ems_pci_v2_post_irq(const struct sja1000_priv *priv)
+{
+ struct ems_pci_card *card = (struct ems_pci_card *)priv->priv;
+
+ writel(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
}
/*
* 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)
+static inline int ems_pci_check_chan(const struct sja1000_priv *priv)
{
unsigned char res;
/* Make sure SJA1000 is in reset mode */
- ems_pci_write_reg(dev, REG_MOD, 1);
+ priv->write_reg(priv, REG_MOD, 1);
- ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN);
+ priv->write_reg(priv, REG_CDR, CDR_PELICAN);
/* read reset-values */
- res = ems_pci_read_reg(dev, REG_CDR);
+ res = priv->read_reg(priv, REG_CDR);
if (res == CDR_PELICAN)
return 1;
struct sja1000_priv *priv;
struct net_device *dev;
struct ems_pci_card *card;
+ int max_chan, mem_size, base_bar;
int err, i;
/* Enabling PCI device */
card->channels = 0;
- /* Remap PITA configuration space, and controller memory area */
- card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE);
+ if (pdev->vendor == PCI_VENDOR_ID_PLX) {
+ card->version = 2; /* CPC-PCI v2 */
+ max_chan = EMS_PCI_V2_MAX_CHAN;
+ base_bar = EMS_PCI_V2_BASE_BAR;
+ mem_size = EMS_PCI_V2_MEM_SIZE;
+ } else {
+ card->version = 1; /* CPC-PCI v1 */
+ max_chan = EMS_PCI_V1_MAX_CHAN;
+ base_bar = EMS_PCI_V1_BASE_BAR;
+ mem_size = EMS_PCI_V1_MEM_SIZE;
+ }
+
+ /* Remap configuration space and controller memory area */
+ card->conf_addr = pci_iomap(pdev, 0, mem_size);
if (card->conf_addr == NULL) {
err = -ENOMEM;
-
goto failure_cleanup;
}
- card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE);
+ card->base_addr = pci_iomap(pdev, base_bar, 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;
- }
+ if (card->version == 1) {
+ /* 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_v1_readb(card, 0) != 0x55 ||
+ ems_pci_v1_readb(card, 1) != 0xAA ||
+ ems_pci_v1_readb(card, 2) != 0x01 ||
+ ems_pci_v1_readb(card, 3) != 0xCB ||
+ ems_pci_v1_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++) {
+ for (i = 0; i < max_chan; i++) {
dev = alloc_sja1000dev(0);
if (dev == NULL) {
err = -ENOMEM;
card->net_dev[i] = dev;
priv = netdev_priv(dev);
priv->priv = card;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
dev->irq = pdev->irq;
- dev->base_addr = (unsigned long)(card->base_addr
- + EMS_PCI_CAN_BASE_OFFSET
- + (i * EMS_PCI_CAN_CTRL_SIZE));
+ priv->reg_base = card->base_addr + EMS_PCI_CAN_BASE_OFFSET
+ + (i * EMS_PCI_CAN_CTRL_SIZE);
+ if (card->version == 1) {
+ priv->read_reg = ems_pci_v1_read_reg;
+ priv->write_reg = ems_pci_v1_write_reg;
+ priv->post_irq = ems_pci_v1_post_irq;
+ } else {
+ priv->read_reg = ems_pci_v2_read_reg;
+ priv->write_reg = ems_pci_v2_write_reg;
+ priv->post_irq = ems_pci_v2_post_irq;
+ }
/* 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;
+ if (ems_pci_check_chan(priv)) {
+ priv->can.clock.freq = 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);
+ if (card->version == 1)
+ /* reset int flag of pita */
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0,
+ card->conf_addr + PITA2_ICR);
+ else
+ /* enable IRQ in PLX 9030 */
+ writel(PLX_ICSR_ENA_CLR,
+ card->conf_addr + PLX_ICSR);
/* Register SJA1000 device */
err = register_sja1000dev(dev);
card->channels++;
- dev_info(&pdev->dev, "Channel #%d at %#lX, irq %d\n",
- i + 1, dev->base_addr,
- dev->irq);
+ dev_info(&pdev->dev, "Channel #%d at 0x%p, irq %d\n",
+ i + 1, priv->reg_base, dev->irq);
} else {
free_sja1000dev(dev);
}
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#include <asm/io.h>
#include <pcmcia/cs_types.h>
#define DRV_NAME "ems_pcmcia"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#error This driver does not support Kernel versions < 2.6.16
+#endif
+
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");
static void ems_pcmcia_config(struct pcmcia_device *dev);
-static u8 ems_pcmcia_read_reg(struct net_device *dev, int port)
+static u8 ems_pcmcia_read_reg(const struct sja1000_priv *priv, int port)
{
- return readb((void __iomem *)dev->base_addr + port);
+ return readb(priv->reg_base + port);
}
-static void ems_pcmcia_write_reg(struct net_device *dev, int port, u8 val)
+static void ems_pcmcia_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
{
- writeb(val, (void __iomem *)dev->base_addr + port);
+ writeb(val, priv->reg_base + port);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
irqreturn_t retval = IRQ_NONE;
int i, again;
+ /* Card not present */
+ if (readw(card->base_addr) != 0xAA55)
+ return IRQ_HANDLED;
+
do {
again = 0;
* 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)
+static inline int ems_pcmcia_check_chan(struct sja1000_priv *priv)
{
unsigned char res;
/* Make sure SJA1000 is in reset mode */
- ems_pcmcia_write_reg(dev, REG_MOD, 1);
+ ems_pcmcia_write_reg(priv, REG_MOD, 1);
- ems_pcmcia_write_reg(dev, REG_CDR, CDR_PELICAN);
+ ems_pcmcia_write_reg(priv, REG_CDR, CDR_PELICAN);
/* read reset-values */
- res = ems_pcmcia_read_reg(dev, REG_CDR);
+ res = ems_pcmcia_read_reg(priv, REG_CDR);
if (res == CDR_PELICAN)
return 1;
priv->priv = card;
SET_NETDEV_DEV(dev, &pdev->dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
dev->irq = pdev->irq.AssignedIRQ;
- dev->base_addr = (unsigned long)(card->base_addr
+ priv->reg_base = (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)) {
+ if (ems_pcmcia_check_chan(priv)) {
priv->read_reg = ems_pcmcia_read_reg;
priv->write_reg = ems_pcmcia_write_reg;
- priv->can.bittiming.clock = EMS_PCMCIA_CAN_CLOCK;
+ priv->can.clock.freq = EMS_PCMCIA_CAN_CLOCK;
priv->ocr = EMS_PCMCIA_OCR;
priv->cdr = EMS_PCMCIA_CDR;
priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
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);
+ "#%d at 0x%p, irq %d\n", DRV_NAME, dev->name,
+ i, priv->reg_base, dev->irq);
} else {
free_sja1000dev(dev);
}
/*
* Configure PCMCIA socket
*/
-static void ems_pcmcia_config(struct pcmcia_device *dev)
+static void __devinit ems_pcmcia_config(struct pcmcia_device *dev)
{
win_req_t req;
memreq_t mem;
*/
#include <linux/kernel.h>
+#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
#define DRV_NAME "esd_pci"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#error This driver does not support Kernel versions < 2.6.21
+#endif
+
MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd.eu");
MODULE_DESCRIPTION("Socket-CAN driver for esd PCI/PMC/CPCI/PCIe/PCI104 " \
"CAN cards");
MODULE_DEVICE_TABLE(pci, esd_pci_tbl);
-static u8 esd_pci_read_reg(struct net_device *ndev, int port)
+static u8 esd_pci_read_reg(const struct sja1000_priv *priv, int port)
{
- return readb((void __iomem *)(ndev->base_addr + port));
+ return readb(priv->reg_base + port);
}
-static void esd_pci_write_reg(struct net_device *ndev, int port, u8 val)
+static void esd_pci_write_reg(const struct sja1000_priv *priv, int port, u8 val)
{
- writeb(val, (void __iomem *)(ndev->base_addr + port));
+ writeb(val, priv->reg_base + port);
}
static void esd_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev)
priv = netdev_priv(ndev);
- ndev->base_addr = (unsigned long)base_addr;
+ priv->reg_base = base_addr;
priv->read_reg = esd_pci_read_reg;
priv->write_reg = esd_pci_write_reg;
- priv->can.bittiming.clock = ESD_PCI_CAN_CLOCK;
+ priv->can.clock.freq = ESD_PCI_CAN_CLOCK;
priv->ocr = ESD_PCI_OCR;
priv->cdr = ESD_PCI_CDR;
/* Set and enable PCI interrupts */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
ndev->irq = pdev->irq;
- dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
- ndev->base_addr, ndev->irq);
+ dev_dbg(&pdev->dev, "reg_base=0x%p irq=%d\n",
+ priv->reg_base, ndev->irq);
SET_NETDEV_DEV(ndev, &pdev->dev);
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
MODULE_DEVICE_TABLE(pci, ixxat_pci_tbl);
-static u8 ixxat_pci_read_reg(struct net_device *ndev, int port)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline void *kzalloc(size_t size, unsigned int __nocast flags)
{
- u8 val;
- val = readb((void __iomem *)(ndev->base_addr + port));
- return val;
+ void *ret = kmalloc(size, flags);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
+
+static u8 ixxat_pci_read_reg(const struct sja1000_priv *priv, int port)
+{
+ return readb(priv->reg_base + port);
}
-static void ixxat_pci_write_reg(struct net_device *ndev, int port, u8 val)
+static void ixxat_pci_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
{
- writeb(val, (void __iomem *)(ndev->base_addr + port));
+ writeb(val, priv->reg_base + port);
}
static void ixxat_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev)
priv = netdev_priv(ndev);
- ndev->base_addr = (unsigned long)base_addr;
+ priv->reg_base = 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->can.clock.freq = IXXAT_PCI_CAN_CLOCK;
priv->ocr = IXXAT_PCI_OCR;
priv->cdr = IXXAT_PCI_CDR;
- /* Set and enable PCI interrupts */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
ndev->irq = pdev->irq;
- dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
- ndev->base_addr, ndev->irq);
+ dev_dbg(&pdev->dev, "reg_base=0x%p irq=%d\n",
+ priv->reg_base, ndev->irq);
SET_NETDEV_DEV(ndev, &pdev->dev);
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
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 */
+#define MAX_NO_OF_CHANNELS 4 /* max no of channels on a single card */
struct kvaser_pci {
int channel;
MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl);
-static u8 kvaser_pci_read_reg(struct net_device *dev, int port)
+static u8 kvaser_pci_read_reg(const struct sja1000_priv *priv, int port)
{
- return ioread8((void __iomem *)(dev->base_addr + port));
+ return ioread8(priv->reg_base + port);
}
-static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val)
+static void kvaser_pci_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
{
- iowrite8(val, (void __iomem *)(dev->base_addr + port));
+ iowrite8(val, priv->reg_base + 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;
+ u32 intcsr;
/* Disable interrupts from card */
- tmp = ioread32(board->conf_addr + S5920_INTCSR);
- tmp &= ~INTCSR_ADDON_INTENABLE_M;
- iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+ intcsr = ioread32(board->conf_addr + S5920_INTCSR);
+ intcsr &= ~INTCSR_ADDON_INTENABLE_M;
+ iowrite32(intcsr, 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;
+ u32 tmp_en_io;
/* Enable interrupts from card */
- tmp = ioread32(board->conf_addr + S5920_INTCSR);
- tmp |= INTCSR_ADDON_INTENABLE_M;
- iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+ tmp_en_io = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp_en_io |= INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp_en_io, board->conf_addr + S5920_INTCSR);
}
static int number_of_sja1000_chip(void __iomem *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;
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);
+ /* Disable PCI interrupts */
+ kvaser_pci_disable_irq(dev);
+
for (i = 0; i < board->no_channels - 1; i++) {
if (board->slave_dev[i]) {
dev_info(&board->pci_dev->dev, "Removing device %s\n",
}
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, priv->reg_base);
pci_iounmap(board->pci_dev, board->conf_addr);
pci_iounmap(board->pci_dev, board->res_addr);
struct net_device **master_dev,
void __iomem *conf_addr,
void __iomem *res_addr,
- unsigned long base_addr)
+ void __iomem *base_addr)
{
struct net_device *dev;
struct sja1000_priv *priv;
board->pci_dev = pdev;
board->channel = channel;
- /*S5920*/
+ /* S5920 */
board->conf_addr = conf_addr;
- /*XILINX board wide address*/
+ /* XILINX board wide address */
board->res_addr = res_addr;
if (channel == 0) {
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 {
board->xilinx_ver = master_board->xilinx_ver;
}
- dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES;
+ priv->reg_base = 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->can.clock.freq = KVASER_PCI_CAN_CLOCK;
priv->ocr = KVASER_PCI_OCR;
priv->cdr = KVASER_PCI_CDR;
- /* Register and setup interrupt handling */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
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);
+ dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n",
+ priv->reg_base, board->conf_addr, dev->irq);
SET_NETDEV_DEV(dev, &pdev->dev);
if (err)
goto failure_release_pci;
- /*S5920*/
+ /* S5920 */
conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE);
if (conf_addr == NULL) {
err = -ENODEV;
- goto failure_iounmap;
+ goto failure_release_regions;
}
- /*XILINX board wide address*/
+ /* XILINX board wide address */
res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE);
if (res_addr == NULL) {
err = -ENOMEM;
for (i = 0; i < no_channels; i++) {
err = kvaser_pci_add_chan(pdev, i, &master_dev,
conf_addr, res_addr,
- (unsigned long)base_addr);
+ base_addr);
if (err)
goto failure_cleanup;
}
kvaser_pci_del_chan(master_dev);
failure_iounmap:
- if (conf_addr == NULL)
+ if (conf_addr != NULL)
pci_iounmap(pdev, conf_addr);
- if (res_addr == NULL)
+ if (res_addr != NULL)
pci_iounmap(pdev, res_addr);
- if (base_addr == NULL)
+ if (base_addr != NULL)
pci_iounmap(pdev, base_addr);
+failure_release_regions:
pci_release_regions(pdev);
failure_release_pci:
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#include <linux/io.h>
#else
#define DRV_NAME "peak_pci"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#error This driver does not support Kernel versions < 2.6.23
+#endif
+
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_DEVICE_TABLE(pci, peak_pci_tbl);
-static u8 peak_pci_read_reg(struct net_device *dev, int port)
+static u8 peak_pci_read_reg(const struct sja1000_priv *priv, int port)
{
- u8 val;
- val = readb((const volatile void __iomem *)
- (dev->base_addr + (port << 2)));
- return val;
+ return readb(priv->reg_base + (port << 2));
}
-static void peak_pci_write_reg(struct net_device *dev, int port, u8 val)
+static void peak_pci_write_reg(const struct sja1000_priv *priv,
+ int port, u8 val)
{
- writeb(val, (volatile void __iomem *)
- (dev->base_addr + (port << 2)));
+ writeb(val, priv->reg_base + (port << 2));
}
-static void peak_pci_post_irq(struct net_device *dev)
+static void peak_pci_post_irq(const struct sja1000_priv *priv)
{
- struct sja1000_priv *priv = netdev_priv(dev);
struct peak_pci *board = priv->priv;
u16 icr_low;
icr_high &= ~0x0002;
writew(icr_high, board->conf_addr + PITA_ICR + 2);
case 3:
- iounmap((void *)dev->base_addr);
+ iounmap(priv->reg_base);
case 2:
if (board->channel != PEAK_PCI_SLAVE)
iounmap((void *)board->conf_addr);
if (channel == PEAK_PCI_SLAVE)
addr += PCI_PORT_SIZE;
- dev->base_addr = (unsigned long)ioremap(addr, PCI_PORT_SIZE);
- if (dev->base_addr == 0) {
+ priv->reg_base = ioremap(addr, PCI_PORT_SIZE);
+ if (priv->reg_base == 0) {
err = -ENOMEM;
goto failure;
}
priv->write_reg = peak_pci_write_reg;
priv->post_irq = peak_pci_post_irq;
- priv->can.bittiming.clock = PEAK_PCI_CAN_CLOCK;
+ priv->can.clock.freq = PEAK_PCI_CAN_CLOCK;
priv->ocr = PEAK_PCI_OCR;
else
priv->cdr = PEAK_PCI_CDR_SINGLE;
- /* Register and setup interrupt handling */
+ /* Setup interrupt handling */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
dev->irq = pdev->irq;
icr_high = readw(board->conf_addr + PITA_ICR + 2);
if (channel == PEAK_PCI_SLAVE)
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);
+ printk(KERN_INFO "%s: %s at reg_base=0x%p conf_addr=%p irq=%d\n",
+ DRV_NAME, dev->name, priv->reg_base, board->conf_addr, dev->irq);
return 0;
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#include <linux/io.h>
#include "sja1000.h"
#define DRV_NAME "pipcan"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+#error This driver does not support Kernel versions < 2.6.20
+#endif
+
MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
MODULE_DESCRIPTION("Socket-CAN driver for MPL PIPCAN module");
MODULE_SUPPORTED_DEVICE("MPL PIPCAN module");
#define PIPCAN_RES (0x804)
#define PIPCAN_RST (0x805)
-static u8 pc_read_reg(struct net_device *dev, int reg)
+static u8 pc_read_reg(const struct sja1000_priv *priv, int reg)
{
- return inb(dev->base_addr + reg);
+ return inb((unsigned long)priv->reg_base + reg);
}
-static void pc_write_reg(struct net_device *dev, int reg, u8 val)
+static void pc_write_reg(const struct sja1000_priv *priv, int reg, u8 val)
{
- outb(val, dev->base_addr + reg);
+ outb(val, (unsigned long)priv->reg_base + reg);
}
static int __init pc_probe(struct platform_device *pdev)
priv->read_reg = pc_read_reg;
priv->write_reg = pc_write_reg;
- priv->can.bittiming.clock = PIPCAN_CAN_CLOCK;
+ priv->can.clock.freq = PIPCAN_CAN_CLOCK;
priv->ocr = PIPCAN_OCR;
priv->cdr = PIPCAN_CDR;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
dev->irq = irq;
dev->base_addr = res->start;
+ priv->reg_base = (void __iomem *)res->start;
dev_set_drvdata(&pdev->dev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
#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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
+#include <socketcan/can/dev.h>
#include "sja1000.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id: sja1000.c 531 2007-10-19 07:38:29Z hartkopp $");
#define DRV_NAME "sja1000"
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver");
+MODULE_DESCRIPTION(DRV_NAME "CAN netdevice driver");
static struct can_bittiming_const sja1000_bittiming_const = {
+ .name = DRV_NAME,
.tseg1_min = 1,
.tseg1_max = 16,
.tseg2_min = 1,
{
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);
+ if (priv->reg_base && (priv->read_reg(priv, 0) == 0xFF)) {
+ dev_info(ND2D(dev), "probing @0x%p failed\n",
+ priv->reg_base);
return 0;
}
- return 1;
+ return -1;
}
-static int set_reset_mode(struct net_device *dev)
+static void set_reset_mode(struct net_device *dev)
{
struct sja1000_priv *priv = netdev_priv(dev);
- unsigned char status = priv->read_reg(dev, REG_MOD);
+ unsigned char status = priv->read_reg(priv, REG_MOD);
int i;
/* disable interrupts */
- priv->write_reg(dev, REG_IER, IRQ_OFF);
+ priv->write_reg(priv, 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;
+ return;
}
- priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */
- status = priv->read_reg(dev, REG_MOD);
+ priv->write_reg(priv, REG_MOD, MOD_RM); /* reset chip */
udelay(10);
+ status = priv->read_reg(priv, REG_MOD);
}
dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n");
- return 1;
-
}
-static int set_normal_mode(struct net_device *dev)
+static void set_normal_mode(struct net_device *dev)
{
struct sja1000_priv *priv = netdev_priv(dev);
- unsigned char status = priv->read_reg(dev, REG_MOD);
+ unsigned char status = priv->read_reg(priv, REG_MOD);
int i;
for (i = 0; i < 100; i++) {
/* check reset bit */
if ((status & MOD_RM) == 0) {
- priv->can.state = CAN_STATE_ACTIVE;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
/* enable all interrupts */
- priv->write_reg(dev, REG_IER, IRQ_ALL);
-
- return 0;
+ priv->write_reg(priv, REG_IER, IRQ_ALL);
+ return;
}
/* set chip to normal mode */
- priv->write_reg(dev, REG_MOD, 0x00);
- status = priv->read_reg(dev, REG_MOD);
+ priv->write_reg(priv, REG_MOD, 0x00);
udelay(10);
+ status = priv->read_reg(priv, REG_MOD);
}
dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n");
- return 1;
-
}
static void sja1000_start(struct net_device *dev)
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);
+ priv->write_reg(priv, REG_TXERR, 0x0);
+ priv->write_reg(priv, REG_RXERR, 0x0);
+ priv->read_reg(priv, REG_ECC);
/* leave reset mode */
set_normal_mode(dev);
{
struct sja1000_priv *priv = netdev_priv(dev);
+ if (!priv->open_time)
+ return -EINVAL;
+
switch (mode) {
case CAN_MODE_START:
- if (!priv->open_time)
- return -EINVAL;
-
sja1000_start(dev);
if (netif_queue_stopped(dev))
netif_wake_queue(dev);
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);
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);
+ (((bt->phase_seg2 - 1) & 0x7) << 4);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ btr1 |= 0x80;
- dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+ 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);
+ priv->write_reg(priv, REG_BTR0, btr0);
+ priv->write_reg(priv, REG_BTR1, btr1);
return 0;
}
struct sja1000_priv *priv = netdev_priv(dev);
/* set clock divider and output control register */
- priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN);
+ priv->write_reg(priv, 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(priv, REG_ACCC0, 0x00);
+ priv->write_reg(priv, REG_ACCC1, 0x00);
+ priv->write_reg(priv, REG_ACCC2, 0x00);
+ priv->write_reg(priv, 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(priv, REG_ACCM0, 0xFF);
+ priv->write_reg(priv, REG_ACCM1, 0xFF);
+ priv->write_reg(priv, REG_ACCM2, 0xFF);
+ priv->write_reg(priv, REG_ACCM3, 0xFF);
- priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+ priv->write_reg(priv, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
}
/*
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);
+ priv->write_reg(priv, REG_FI, fi);
+ priv->write_reg(priv, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ priv->write_reg(priv, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ priv->write_reg(priv, REG_ID3, (id & 0x00001fe0) >> 5);
+ priv->write_reg(priv, 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);
+ priv->write_reg(priv, REG_FI, fi);
+ priv->write_reg(priv, REG_ID1, (id & 0x000007f8) >> 3);
+ priv->write_reg(priv, REG_ID2, (id & 0x00000007) << 5);
}
for (i = 0; i < dlc; i++)
- priv->write_reg(dev, dreg++, cf->data[i]);
+ priv->write_reg(priv, 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);
+ priv->write_reg(priv, REG_CMR, CMD_TR);
return 0;
}
skb->dev = dev;
skb->protocol = htons(ETH_P_CAN);
- fi = priv->read_reg(dev, REG_FI);
+ fi = priv->read_reg(priv, 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 = (priv->read_reg(priv, REG_ID1) << (5 + 16))
+ | (priv->read_reg(priv, REG_ID2) << (5 + 8))
+ | (priv->read_reg(priv, REG_ID3) << 5)
+ | (priv->read_reg(priv, 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);
+ id = (priv->read_reg(priv, REG_ID1) << 3)
+ | (priv->read_reg(priv, REG_ID2) >> 5);
}
if (fi & FI_RTR)
cf->can_id = id;
cf->can_dlc = dlc;
for (i = 0; i < dlc; i++)
- cf->data[i] = priv->read_reg(dev, dreg++);
+ cf->data[i] = priv->read_reg(priv, dreg++);
while (i < 8)
cf->data[i++] = 0;
/* release receive buffer */
- priv->write_reg(dev, REG_CMR, CMD_RRB);
+ priv->write_reg(priv, REG_CMR, CMD_RRB);
netif_rx(skb);
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 */
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ priv->write_reg(priv, 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) {
cf->can_id |= CAN_ERR_BUSOFF;
can_bus_off(dev);
} else if (status & SR_ES) {
- state = CAN_STATE_BUS_WARNING;
+ state = CAN_STATE_ERROR_WARNING;
} else
- state = CAN_STATE_ACTIVE;
+ state = CAN_STATE_ERROR_ACTIVE;
}
if (isrc & IRQ_BEI) {
/* bus error interrupt */
priv->can.can_stats.bus_error++;
- ecc = priv->read_reg(dev, REG_ECC);
+ stats->rx_errors++;
+
+ ecc = priv->read_reg(priv, REG_ECC);
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
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;
+ state = CAN_STATE_ERROR_PASSIVE;
else
- state = CAN_STATE_ACTIVE;
+ state = CAN_STATE_ERROR_ACTIVE;
}
if (isrc & IRQ_ALI) {
/* arbitration lost interrupt */
dev_dbg(ND2D(dev), "arbitration lost interrupt\n");
- alc = priv->read_reg(dev, REG_ALC);
+ alc = priv->read_reg(priv, REG_ALC);
priv->can.can_stats.arbitration_lost++;
+ stats->rx_errors++;
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);
+ if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
+ state == CAN_STATE_ERROR_PASSIVE)) {
+ uint8_t rxerr = priv->read_reg(priv, REG_RXERR);
+ uint8_t txerr = priv->read_reg(priv, REG_TXERR);
cf->can_id |= CAN_ERR_CRTL;
- if (state == CAN_STATE_BUS_WARNING)
+ if (state == CAN_STATE_ERROR_WARNING) {
+ priv->can.can_stats.error_warning++;
cf->data[1] = (txerr > rxerr) ?
CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING;
- else
+ } else {
+ priv->can.can_stats.error_passive++;
cf->data[1] = (txerr > rxerr) ?
CAN_ERR_CRTL_TX_PASSIVE :
CAN_ERR_CRTL_RX_PASSIVE;
+ }
}
priv->can.state = state;
int n = 0;
/* Shared interrupts and IRQ off? */
- if (priv->read_reg(dev, REG_IER) == IRQ_OFF)
+ if (priv->read_reg(priv, REG_IER) == IRQ_OFF)
return IRQ_NONE;
if (priv->pre_irq)
- priv->pre_irq(dev);
+ priv->pre_irq(priv);
- while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
+ while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
n++;
- status = priv->read_reg(dev, REG_SR);
+ status = priv->read_reg(priv, REG_SR);
+
+ if (isrc & IRQ_WUI)
+ dev_warn(ND2D(dev), "wakeup interrupt\n");
- if (isrc & IRQ_WUI) {
- /* wake-up interrupt */
- priv->can.can_stats.wakeup++;
- }
if (isrc & IRQ_TI) {
/* transmission complete interrupt */
stats->tx_packets++;
/* receive interrupt */
while (status & SR_RBS) {
sja1000_rx(dev);
- status = priv->read_reg(dev, REG_SR);
+ status = priv->read_reg(priv, REG_SR);
}
}
if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
}
if (priv->post_irq)
- priv->post_irq(dev);
+ priv->post_irq(priv);
if (n >= SJA1000_MAX_IRQ)
dev_dbg(ND2D(dev), "%d messages handled in ISR", n);
/* set chip into reset mode */
set_reset_mode(dev);
- /* determine and set bittime */
- err = can_set_bittiming(dev);
+ /* common open */
+ err = open_candev(dev);
if (err)
return err;
/* register interrupt handler, if not done by the device driver */
if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
- err = request_irq(dev->irq, &sja1000_interrupt, SA_SHIRQ,
- dev->name, (void *)dev);
-#else
- err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED,
+ err = request_irq(dev->irq, &sja1000_interrupt, priv->irq_flags,
dev->name, (void *)dev);
-#endif
- if (err)
+ if (err) {
+ close_candev(dev);
return -EAGAIN;
+ }
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
{
struct sja1000_priv *priv = netdev_priv(dev);
- set_reset_mode(dev);
netif_stop_queue(dev);
- priv->open_time = 0;
- can_close_cleanup(dev);
+ set_reset_mode(dev);
if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER))
free_irq(dev->irq, (void *)dev);
+ close_candev(dev);
+
+ priv->open_time = 0;
+
return 0;
}
return NULL;
priv = netdev_priv(dev);
+
priv->dev = dev;
+ priv->can.bittiming_const = &sja1000_bittiming_const;
+ priv->can.do_set_bittiming = sja1000_set_bittiming;
+ priv->can.do_set_mode = sja1000_set_mode;
if (sizeof_priv)
priv->priv = (void *)priv + sizeof(struct sja1000_priv);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
static const struct net_device_ops sja1000_netdev_ops = {
- .ndo_open = sja1000_open,
- .ndo_stop = sja1000_close,
- .ndo_start_xmit = sja1000_start_xmit,
+ .ndo_open = sja1000_open,
+ .ndo_stop = sja1000_close,
+ .ndo_start_xmit = sja1000_start_xmit,
};
#endif
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 */
-
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
dev->netdev_ops = &sja1000_netdev_ops;
#else
dev->hard_start_xmit = sja1000_start_xmit;
#endif
- 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;
- }
+ dev->flags |= IFF_ECHO; /* we support local echo */
set_reset_mode(dev);
chipset_init(dev);
- return 0;
+
+ return register_candev(dev);
}
EXPORT_SYMBOL_GPL(register_sja1000dev);
/*
- * $Id: sja1000.h 505 2007-09-30 13:32:41Z hartkopp $
- *
* sja1000.h - Philips SJA1000 network device driver
*
* Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
#ifndef SJA1000_DEV_H
#define SJA1000_DEV_H
-#include <linux/version.h>
-#include <linux/can/dev.h>
-#include <linux/can/platform/sja1000.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/platform/sja1000.h>
#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
* SJA1000 private data structure
*/
struct sja1000_priv {
- struct can_priv can; /* must be the first member! */
- long open_time;
+ struct can_priv can; /* must be the first member */
+ int 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);
+ /* the lower-layer is responsible for appropriate locking */
+ u8 (*read_reg) (const struct sja1000_priv *priv, int reg);
+ void (*write_reg) (const struct sja1000_priv *priv, int reg, u8 val);
+ void (*pre_irq) (const struct sja1000_priv *priv);
+ void (*post_irq) (const struct sja1000_priv *priv);
void *priv; /* for board-specific data */
struct net_device *dev;
- u8 ocr;
- u8 cdr;
- u32 flags;
+ void __iomem *reg_base; /* ioremap'ed address to registers */
+ unsigned long irq_flags; /* for request_irq() */
+
+ u16 flags; /* custom mode flags */
+ u8 ocr; /* output control register */
+ u8 cdr; /* clock divider register */
};
struct net_device *alloc_sja1000dev(int sizeof_priv);
--- /dev/null
+/*
+ * Copyright (C) 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/version.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/platform/sja1000.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000_isa"
+
+#define MAXDEV 8
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#error This driver does not support Kernel versions < 2.6.16
+#endif
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus");
+MODULE_LICENSE("GPL v2");
+
+#define CLK_DEFAULT 16000000 /* 16 MHz */
+#define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF)
+#define OCR_DEFAULT OCR_TX0_PUSHPULL
+
+static unsigned long port[MAXDEV];
+static unsigned long mem[MAXDEV];
+static int __devinitdata irq[MAXDEV];
+static int __devinitdata clk[MAXDEV];
+static char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+static char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+static char __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
+
+module_param_array(port, ulong, NULL, S_IRUGO);
+MODULE_PARM_DESC(port, "I/O port number");
+
+module_param_array(mem, ulong, NULL, S_IRUGO);
+MODULE_PARM_DESC(mem, "I/O memory address");
+
+module_param_array(indirect, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
+
+module_param_array(irq, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(irq, "IRQ number");
+
+module_param_array(clk, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(clk, "External oscillator clock frequency "
+ "(default=16000000 [16 MHz])");
+
+module_param_array(cdr, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(cdr, "Clock divider register "
+ "(default=0x48 [CDR_CBP | CDR_CLK_OFF])");
+
+module_param_array(ocr, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(ocr, "Output control register "
+ "(default=0x18 [OCR_TX0_PUSHPULL])");
+
+#define SJA1000_IOSIZE 0x20
+#define SJA1000_IOSIZE_INDIRECT 0x02
+
+static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg)
+{
+ return readb(priv->reg_base + reg);
+}
+
+static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv,
+ int reg, u8 val)
+{
+ writeb(val, priv->reg_base + reg);
+}
+
+static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg)
+{
+ return inb((unsigned long)priv->reg_base + reg);
+}
+
+static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv,
+ int reg, u8 val)
+{
+ outb(val, (unsigned long)priv->reg_base + reg);
+}
+
+static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv,
+ int reg)
+{
+ unsigned long base = (unsigned long)priv->reg_base;
+
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv,
+ int reg, u8 val)
+{
+ unsigned long base = (unsigned long)priv->reg_base;
+
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static int __devinit sja1000_isa_match(struct device *pdev, unsigned int idx)
+{
+ if (port[idx] || mem[idx]) {
+ if (irq[idx])
+ return 1;
+ } else if (idx)
+ return 0;
+
+ dev_err(pdev, "insufficient parameters supplied\n");
+ return 0;
+}
+
+static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ void __iomem *base = NULL;
+ int iosize = SJA1000_IOSIZE;
+ int err;
+
+ if (mem[idx]) {
+ if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+ base = ioremap_nocache(mem[idx], iosize);
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ } else {
+ if (indirect[idx] > 0 ||
+ (indirect[idx] == -1 && indirect[0] > 0))
+ iosize = SJA1000_IOSIZE_INDIRECT;
+ if (!request_region(port[idx], iosize, DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+ }
+
+ dev = alloc_sja1000dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_unmap;
+ }
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[idx];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+ if (mem[idx]) {
+ priv->reg_base = base;
+ dev->base_addr = mem[idx];
+ priv->read_reg = sja1000_isa_mem_read_reg;
+ priv->write_reg = sja1000_isa_mem_write_reg;
+ } else {
+ priv->reg_base = (void __iomem *)port[idx];
+ dev->base_addr = port[idx];
+
+ if (iosize == SJA1000_IOSIZE_INDIRECT) {
+ priv->read_reg = sja1000_isa_port_read_reg_indirect;
+ priv->write_reg = sja1000_isa_port_write_reg_indirect;
+ } else {
+ priv->read_reg = sja1000_isa_port_read_reg;
+ priv->write_reg = sja1000_isa_port_write_reg;
+ }
+ }
+
+ if (clk[idx])
+ priv->can.clock.freq = clk[idx] / 2;
+ else if (clk[0])
+ priv->can.clock.freq = clk[0] / 2;
+ else
+ priv->can.clock.freq = CLK_DEFAULT / 2;
+
+ if (ocr[idx] != -1)
+ priv->ocr = ocr[idx] & 0xff;
+ else if (ocr[0] != -1)
+ priv->ocr = ocr[0] & 0xff;
+ else
+ priv->ocr = OCR_DEFAULT;
+
+ if (cdr[idx] != -1)
+ priv->cdr = cdr[idx] & 0xff;
+ else if (cdr[0] != -1)
+ priv->cdr = cdr[0] & 0xff;
+ else
+ priv->cdr = CDR_DEFAULT;
+
+ dev_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, pdev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(pdev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_unmap;
+ }
+
+ dev_info(pdev, "%s device registered (reg_base=0x%p, irq=%d)\n",
+ DRV_NAME, priv->reg_base, dev->irq);
+ return 0;
+
+ exit_unmap:
+ if (mem[idx])
+ iounmap(base);
+ exit_release:
+ if (mem[idx])
+ release_mem_region(mem[idx], iosize);
+ else
+ release_region(port[idx], iosize);
+ exit:
+ return err;
+}
+
+static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx)
+{
+ struct net_device *dev = dev_get_drvdata(pdev);
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ unregister_sja1000dev(dev);
+ dev_set_drvdata(pdev, NULL);
+
+ if (mem[idx]) {
+ iounmap(priv->reg_base);
+ release_mem_region(mem[idx], SJA1000_IOSIZE);
+ } else {
+ if (priv->read_reg == sja1000_isa_port_read_reg_indirect)
+ release_region(port[idx], SJA1000_IOSIZE_INDIRECT);
+ else
+ release_region(port[idx], SJA1000_IOSIZE);
+ }
+ free_sja1000dev(dev);
+
+ return 0;
+}
+
+static struct isa_driver sja1000_isa_driver = {
+ .match = sja1000_isa_match,
+ .probe = sja1000_isa_probe,
+ .remove = __devexit_p(sja1000_isa_remove),
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init sja1000_isa_init(void)
+{
+ int err = isa_register_driver(&sja1000_isa_driver, MAXDEV);
+
+ if (!err)
+ printk(KERN_INFO
+ "Legacy %s driver for max. %d devices registered\n",
+ DRV_NAME, MAXDEV);
+ return err;
+}
+
+static void __exit sja1000_isa_exit(void)
+{
+ isa_unregister_driver(&sja1000_isa_driver);
+}
+
+module_init(sja1000_isa_init);
+module_exit(sja1000_isa_exit);
* definition in your flattened device tree source (DTS) file similar to:
*
* can@3,100 {
- * compatible = "philips,sja1000";
+ * compatible = "nxp,sja1000";
* reg = <3 0x100 0x80>;
- * clock-frequency = <8000000>;
- * cdr-reg = <0x48>;
- * ocr-reg = <0x0a>;
* interrupts = <2 0>;
* interrupt-parent = <&mpic>;
+ * nxp,external-clock-frequency = <16000000>;
* };
+ *
+ * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further
+ * information.
*/
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#include <linux/of_platform.h>
#include <asm/prom.h>
#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN
#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF)
-static u8 sja1000_ofp_read_reg(struct net_device *dev, int reg)
+static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg)
{
- return in_8((void __iomem *)(dev->base_addr + reg));
+ return in_8(priv->reg_base + reg);
}
-static void sja1000_ofp_write_reg(struct net_device *dev, int reg, u8 val)
+static void sja1000_ofp_write_reg(const struct sja1000_priv *priv,
+ int reg, u8 val)
{
- out_8((void __iomem *)(dev->base_addr + reg), val);
+ out_8(priv->reg_base + reg, val);
}
static int __devexit sja1000_ofp_remove(struct of_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct sja1000_priv *priv = netdev_priv(dev);
struct device_node *np = ofdev->node;
struct resource res;
unregister_sja1000dev(dev);
free_sja1000dev(dev);
- iounmap((void __iomem *)dev->base_addr);
+ iounmap(priv->reg_base);
irq_dispose_mapping(dev->irq);
of_address_to_resource(np, 0, &res);
- release_mem_region(res.start, res.end - res.start + 1);
+ release_mem_region(res.start, resource_size(&res));
return 0;
}
return err;
}
- res_size = res.end - res.start + 1;
+ res_size = resource_size(&res);
if (!request_mem_region(res.start, res_size, DRV_NAME)) {
- dev_err(&ofdev->dev, "couldn't request %#x..%#x\n",
- res.start, res.end);
+ dev_err(&ofdev->dev, "couldn't request %#llx..%#llx\n",
+ (unsigned long long)res.start,
+ (unsigned long long)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);
+ dev_err(&ofdev->dev, "couldn't ioremap %#llx..%#llx\n",
+ (unsigned long long)res.start,
+ (unsigned long long)res.end);
err = -ENOMEM;
goto exit_release_mem;
}
priv->read_reg = sja1000_ofp_read_reg;
priv->write_reg = sja1000_ofp_write_reg;
+ /* backward compatibility */
prop = of_get_property(np, "clock-frequency", &prop_size);
+ if (!prop)
+ prop = of_get_property(np, "nxp,external-clock-frequency",
+ &prop_size);
+
if (prop && (prop_size == sizeof(u32)))
- priv->can.bittiming.clock = *prop;
+ priv->can.clock.freq = *prop / 2;
else
- priv->can.bittiming.clock = SJA1000_OFP_CAN_CLOCK;
+ priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; /* default */
+ /* backward compatibility */
prop = of_get_property(np, "ocr-reg", &prop_size);
- if (prop && (prop_size == sizeof(u32)))
+ if (prop && (prop_size == sizeof(u32))) {
priv->ocr = (u8)*prop;
- else
- priv->ocr = SJA1000_OFP_OCR;
+ } else {
+
+ prop = of_get_property(np, "nxp,tx-output-mode", &prop_size);
+ if (prop && (prop_size == sizeof(u32)))
+ priv->ocr |= *prop & OCR_MODE_MASK;
+ else
+ priv->ocr |= OCR_MODE_NORMAL; /* default */
+
+ prop = of_get_property(np, "nxp,tx-output-config", &prop_size);
+ if (prop && (prop_size == sizeof(u32)))
+ priv->ocr |= (*prop << OCR_TX_SHIFT) & OCR_TX_MASK;
+ else
+ priv->ocr |= OCR_TX0_PULLDOWN; /* default */
+ }
+ /* backward compatibility */
prop = of_get_property(np, "cdr-reg", &prop_size);
- if (prop && (prop_size == sizeof(u32)))
+ if (prop && (prop_size == sizeof(u32))) {
priv->cdr = (u8)*prop;
- else
- priv->cdr = SJA1000_OFP_CDR;
+ } else {
+ prop = of_get_property(np, "nxp,clock-out-frequency",
+ &prop_size);
+ if (prop && (prop_size == sizeof(u32)) && *prop) {
+ u32 divider = priv->can.clock.freq * 2 / *prop;
+
+ if (divider > 1)
+ priv->cdr |= divider / 2 - 1;
+ else
+ priv->cdr |= CDR_CLKOUT_MASK;
+ } else {
+ priv->cdr |= CDR_CLK_OFF; /* default */
+ }
+
+ prop = of_get_property(np, "nxp,no-comparator-bypass", NULL);
+ if (!prop)
+ priv->cdr |= CDR_CBP; /* default */
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ priv->irq_flags = SA_SHIRQ;
+#else
+ priv->irq_flags = IRQF_SHARED;
+#endif
+ priv->reg_base = base;
- dev->base_addr = (unsigned long)base;
dev->irq = irq;
dev_info(&ofdev->dev,
- "base=0x%lx irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n",
- dev->base_addr, dev->irq, priv->can.bittiming.clock,
+ "reg_base=0x%p irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n",
+ priv->reg_base, dev->irq, priv->can.clock.freq,
priv->ocr, priv->cdr);
dev_set_drvdata(&ofdev->dev, dev);
*/
#include <linux/kernel.h>
+#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/platform/sja1000.h>
#include <linux/io.h>
#include "sja1000.h"
#define DRV_NAME "sja1000_platform"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+#error This driver does not support Kernel versions < 2.6.27
+#endif
+
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)
+static u8 sp_read_reg(const struct sja1000_priv *priv, int reg)
{
- return ioread8((void __iomem *)(dev->base_addr + reg));
+ return ioread8(priv->reg_base + reg);
}
-static void sp_write_reg(struct net_device *dev, int reg, u8 val)
+static void sp_write_reg(const struct sja1000_priv *priv, int reg, u8 val)
{
- iowrite8(val, (void __iomem *)(dev->base_addr + reg));
+ iowrite8(val, priv->reg_base + reg);
}
static int sp_probe(struct platform_device *pdev)
{
- int err, irq;
+ int err;
void __iomem *addr;
struct net_device *dev;
struct sja1000_priv *priv;
goto exit;
}
- if (!request_mem_region(res_mem->start,
- res_mem->end - res_mem->start + 1,
+ if (!request_mem_region(res_mem->start, resource_size(res_mem),
DRV_NAME)) {
err = -EBUSY;
goto exit;
}
- addr = ioremap_nocache(res_mem->start,
- res_mem->end - res_mem->start + 1);
+ addr = ioremap_nocache(res_mem->start, resource_size(res_mem));
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;
}
priv = netdev_priv(dev);
+ dev->irq = res_irq->start;
+ priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+ priv->reg_base = addr;
priv->read_reg = sp_read_reg;
priv->write_reg = sp_write_reg;
- priv->can.bittiming.clock = pdata->clock;
+ priv->can.clock.freq = 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);
goto exit_free;
}
- dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n",
- DRV_NAME, dev->base_addr, dev->irq);
+ dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n",
+ DRV_NAME, priv->reg_base, dev->irq);
return 0;
exit_free:
exit_iounmap:
iounmap(addr);
exit_release:
- release_mem_region(res_mem->start, res_mem->end - res_mem->start + 1);
+ release_mem_region(res_mem->start, resource_size(res_mem));
exit:
return err;
}
static int sp_remove(struct platform_device *pdev)
{
struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct sja1000_priv *priv = netdev_priv(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);
+ if (priv->reg_base)
+ iounmap(priv->reg_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
free_sja1000dev(dev);
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
static __initdata const char banner[] =
return 0;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops slc_netdev_ops = {
+ .ndo_open = slc_open,
+ .ndo_stop = slc_close,
+ .ndo_start_xmit = slc_xmit,
+#ifdef SLC_CHECK_TRANSMIT
+ .ndo_tx_timeout = slc_tx_timeout,
+#endif
+};
+#endif
+
/* Netdevice register callback */
static void slc_setup(struct net_device *dev)
{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ dev->netdev_ops = &slc_netdev_ops;
+#else
dev->open = slc_open;
- dev->destructor = free_netdev;
dev->stop = slc_close;
+ dev->hard_start_xmit = slc_xmit;
+#endif
+ dev->destructor = free_netdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
dev->get_stats = slc_get_stats;
#endif
- dev->hard_start_xmit = slc_xmit;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->mtu = sizeof(struct can_frame);
dev->type = ARPHRD_CAN;
#ifdef SLC_CHECK_TRANSMIT
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
dev->tx_timeout = slc_tx_timeout;
+#endif
dev->watchdog_timeo = 20*HZ;
#endif
-include $(TOPDIR)/Makefile.common
-softing-y := softing_main.o softing_fw.o
+softing-y := softing_main.o softing_fw.o softing_sysfs.o
obj-$(CONFIG_CAN_SOFTING) += softing.o
obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o
#include <linux/interrupt.h>
#include <linux/netdevice.h>
-#include <linux/can.h>
-#include <linux/can/dev.h>
+#include <linux/ktime.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
struct softing;
struct sofing_desc;
int nbus;
struct softing_priv *bus[2];
spinlock_t spin; /* protect this structure & DPRAM access */
+ ktime_t boot_time;
+ u32 sample_at_boot_time;
struct {
/* indication of firmware status */
int up;
+ int failed; /* firmware has failed */
/* protection of the 'up' variable */
struct mutex lock;
} fw;
unsigned short manf;
unsigned short prod;
u32 serial, fw, hw, lic;
- u16 chip [2];
+ u16 chip[2];
u32 freq;
const char *name;
} id;
extern int softing_default_output(struct softing *card
, struct softing_priv *priv);
-extern u32 softing_time2usec(struct softing *card, u32 raw);
+extern ktime_t softing_raw2ktime(struct softing *card, u32 raw);
extern int softing_fct_cmd(struct softing *card
, int cmd, int vector, const char *msg);
*/
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);
+/* start/stop 1 bus on cardr*/
+extern int softing_cycle(
+ struct softing *card, struct softing_priv *priv, int up);
-/* reinitaliase the card, apply -1 for bus[01] for 'no change' */
-extern int softing_reinit(struct softing *card, int bus0, int bus1);
+/* netif_rx() */
+extern int softing_rx(struct net_device *netdev, const struct can_frame *msg,
+ ktime_t ktime);
+
+/* create/remove the per-card associated sysfs entries */
+extern int softing_card_sysfs_create(struct softing *card);
+extern void softing_card_sysfs_remove(struct softing *card);
+/* create/remove the per-bus associated sysfs entries */
+extern int softing_bus_sysfs_create(struct softing_priv *bus);
+extern void softing_bus_sysfs_remove(struct softing_priv *bus);
/* SOFTING DPRAM mappings */
struct softing_rx {
/* 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); \
- }
-
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
reg.Value = v ? 0x60 : 0;
ret = pcmcia_access_configuration_register(pcmcia, ®);
if (ret)
- mod_alert("failed %u", ret);
+ dev_alert(&pcmcia->dev, "failed %u\n", ret);
return ret;
}
/* 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__);
+ dev_alert(&pdev->dev, "cf->Vcc mismatch\n");
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__);
+ dev_alert(&pdev->dev, "cf->Vcc mismatch\n");
goto do_next;
}
}
csdev->win.AccessSpeed = 0;
ret = pcmcia_request_window(&pdev, &csdev->win, &pdev->win);
if (ret) {
- mod_alert("pcmcia_request_window() mismatch\n");
+ dev_alert(&pdev->dev,
+ "pcmcia_request_window() mismatch\n");
goto do_next;
}
/* softing specific: choose slower access for old cards */
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");
+ dev_alert(&pdev->dev,
+ "pcmcia_map_mem_page() mismatch\n");
goto do_next_win;
}
} else {
- mod_info("no memory window in tuple %u", cf->index);
+ dev_info(&pdev->dev, "no memory window in tuple %u\n",
+ cf->index);
goto do_next;
}
return 0;
{
struct softing *card = (struct softing *)pcmcia->priv;
struct softing_cs *cs = softing2cs(card);
- mod_trace("%s,device'%s'", card->id.name, pcmcia->devname);
+ dev_dbg(&pcmcia->dev, "%s, device '%s'\n"
+ , card->id.name, pcmcia->devname);
rm_softing(card);
/* release pcmcia stuff */
pcmcia_disable_device(pcmcia);
static int __devinit driver_probe(struct pcmcia_device *pcmcia)
{
struct softing_cs *cs;
- struct softing *card;
+ struct softing *card;
+ char *str;
+ char line[1024]; /* possible memory corruption */
- mod_trace("on %s", pcmcia->devname);
+ dev_dbg(&pcmcia->dev, "on %s\n", pcmcia->devname);
/* Create new softing device */
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
card->dpram.size = cs->win.Size;
if (card->dpram.size != 0x1000) {
- mod_alert("dpram size 0x%lx mismatch\n", card->dpram.size);
+ dev_alert(&pcmcia->dev, "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);
+ str = line;
+ str += sprintf(str, "config index %u", pcmcia->conf.ConfigIndex);
if (pcmcia->conf.Vpp)
- printk(", Vpp %d.%d", pcmcia->conf.Vpp/10, pcmcia->conf.Vpp%10);
+ str += sprintf(str, ", Vpp %d.%d",
+ pcmcia->conf.Vpp/10, pcmcia->conf.Vpp%10);
if (pcmcia->conf.Attributes & CONF_ENABLE_IRQ) {
- printk(", irq %d", pcmcia->irq.AssignedIRQ);
+ str += sprintf(str, ", 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"
+ str += sprintf(str, ", 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);
+ if (!p)
+ continue;
+ str += sprintf(str, " %s", p);
}
}
- printk("\n");
+ dev_info(&pcmcia->dev, "%s\n", line);
if (mk_softing(card))
goto softing_failed;
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);
}
/*don't read return-value now */
ret = card->dpram.fct->returned;
if (ret)
- mod_alert("%s returned %u", msg, ret);
+ dev_alert(card->dev,
+ "%s returned %u\n", msg, ret);
return 0;
}
if ((jiffies - stamp) >= 1 * HZ)
} while (!signal_pending(current));
if (ret == RES_NONE) {
- mod_alert("%s, no response from card on %u/0x%02x"
- , msg, cmd, vector);
+ dev_alert(card->dev,
+ "%s, no response from card on %u/0x%02x\n",
+ msg, cmd, vector);
return 1;
} else {
- mod_alert("%s, bad response from card on %u/0x%02x, 0x%04x"
- , msg, cmd, vector, ret);
+ dev_alert(card->dev,
+ "%s, bad response from card on %u/0x%02x, 0x%04x\n",
+ msg, cmd, vector, ret);
/*make sure to return something not 0 */
return ret ? ret : 1;
}
switch (ret) {
case RES_NONE:
- mod_alert("%s: no response from card", msg);
+ dev_alert(card->dev, "%s: no response from card\n", msg);
break;
case RES_NOK:
- mod_alert("%s: response from card nok", msg);
+ dev_alert(card->dev, "%s: response from card nok\n", msg);
break;
case RES_UNKNOWN:
- mod_alert("%s: command 0x%04x unknown", msg, command);
+ dev_alert(card->dev, "%s: command 0x%04x unknown\n",
+ msg, command);
break;
default:
- mod_alert("%s: bad response from card (%u)]", msg, ret);
+ dev_alert(card->dev, "%s: bad response from card (%u)]\n",
+ msg, ret);
break;
}
return ret ? ret : 1;
u32 start_addr;
struct fw_hdr rec;
int ok = 0;
- unsigned char buf[256];
+ unsigned char buf[1024];
ret = request_firmware(&fw, file, card->dev);
if (ret) {
- mod_alert("request_firmware(%s) got %i", file, ret);
+ dev_alert(card->dev, "request_firmware(%s) got %i\n",
+ 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));
+ dev_dbg(card->dev, "%s, firmware(%s) got %u bytes"
+ ", offset %c0x%04x\n",
+ card->id.name, file, (unsigned int)fw->size,
+ (offset >= 0) ? '+' : '-', abs(offset));
/* parse the firmware */
mem = fw->data;
end = &mem[fw->size];
if (fw_parse(&mem, &rec))
goto fw_end;
if (rec.type != 0xffff) {
- mod_alert("firware starts with type 0x%04x", rec.type);
+ dev_alert(card->dev, "firware starts with type 0x%04x\n",
+ 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);
+ dev_info(card->dev, "firware string '%.*s'\n",
+ rec.len, rec.base);
goto fw_end;
}
ok |= 1;
ok |= 4;
goto fw_end;
} else if (rec.type != 0) {
- mod_alert("unknown record type 0x%04x", rec.type);
+ dev_alert(card->dev, "unknown record type 0x%04x\n",
+ 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);
+ dev_alert(card->dev,
+ "firmware out of range (0x%08x / 0x%08x)\n",
+ (rec.addr + rec.len + offset), size);
goto fw_end;
}
memcpy_toio(&virt[rec.addr + offset],
/* 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);
+ dev_info(card->dev,
+ "record is big (%u bytes), not verifying\n",
+ rec.len);
continue;
}
/* verify record data */
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]);
+ dev_alert(card->dev, "0x%08x:0x%03x at 0x%p failed\n",
+ rec.addr, rec.len, &virt[rec.addr + offset]);
goto fw_end;
}
fw_end:
/*got eof & start */
return 0;
}
- mod_alert("failed");
+ dev_info(card->dev, "firmware %s failed\n", file);
return EINVAL;
}
u8 do_cs;
} __attribute__((packed)) *pcpy =
(struct cpy *)&card->dpram.command[1];
+ struct cmd {
+ u32 start;
+ u8 autorestart;
+ } __attribute__((packed)) *pcmdstart =
+ (struct cmd *)&card->dpram.command[1];
ret = request_firmware(&fw, file, card->dev);
if (ret) {
- mod_alert("request_firmware(%s) got %i", file, ret);
+ dev_alert(card->dev, "request_firmware(%s) got %i\n",
+ file, ret);
return ret;
}
- mod_trace("%s, firmware(%s) got %lu bytes", card->id.name, file,
- (unsigned long)fw->size);
+ dev_dbg(card->dev, "%s, firmware(%s) got %lu bytes\n",
+ card->id.name, file, (unsigned long)fw->size);
/* parse the firmware */
mem = fw->data;
end = &mem[fw->size];
if (fw_parse(&mem, &rec))
goto fw_end;
if (rec.type != 0xffff) {
- mod_alert("firware starts with type 0x%04x", rec.type);
+ dev_alert(card->dev, "firware starts with type 0x%04x\n",
+ 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);
+ dev_alert(card->dev, "firware string '%.*s' fault\n",
+ rec.len, rec.base);
goto fw_end;
}
ok |= 1;
ok |= 4;
goto fw_end;
} else if (rec.type != 0) {
- mod_alert("unknown record type 0x%04x", rec.type);
+ dev_alert(card->dev, "unknown record type 0x%04x\n",
+ rec.type);
break;
}
/* regualar data */
/*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);
+ dev_alert(card->dev, "SRAM seems to be damaged"
+ ", wanted 0x%04x, got 0x%04x\n", 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");
+ if (ok != 7)
+ goto fw_failed;
+ /*got start, start_addr, & eof */
+ pcmdstart->start = start_addr;
+ pcmdstart->autorestart = 1;
+ if (softing_bootloader_command(card, 3, "start app."))
+ goto fw_failed;
+ dev_info(card->dev, "firmware %s up\n", file);
+ return 0;
+fw_failed:
+ dev_info(card->dev, "firmware %s failed\n", file);
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;
return -EIO;
}
-int softing_reinit(struct softing *card, int bus0, int bus1)
+int softing_cycle(struct softing *card, struct softing_priv *bus, int up)
{
int ret;
- int restarted_bus = -1;
- mod_trace("%s", card->id.name);
+ struct softing_priv *pbus;
+ int mask_start;
+ int j;
+ struct can_frame msg;
+
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);
+ ret = mutex_lock_interruptible(&card->fw.lock);
+ if (ret)
return ret;
+ if (card->fw.failed)
+ goto failed_already;
+
+ mask_start = 0;
+ /* bring netdevs down */
+ for (j = 0; j < card->nbus; ++j) {
+ pbus = card->bus[j];
+ if (!pbus)
+ continue;
+
+ if (bus != pbus)
+ netif_stop_queue(pbus->netdev);
+
+ if ((bus != pbus) && netif_running(pbus->netdev))
+ mask_start |= (1 << j);
+ if (netif_running(pbus->netdev)) {
+ pbus->tx.pending = 0;
+ pbus->tx.echo_put = 0;
+ pbus->tx.echo_get = 0;
+ /* this bus' may just have called open_candev()
+ * which is rather stupid to call close_candev()
+ * already
+ * but we may come here from busoff recovery too
+ * in which case the echo_skb _needs_ flushing too.
+ * just be sure to call open_candev() again
+ */
+ close_candev(pbus->netdev);
+ }
+ pbus->can.state = CAN_STATE_STOPPED;
}
- 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->tx.pending = 0;
+ if (bus && up)
+ /* prepare to start this bus as well */
+ mask_start |= (1 << bus->index);
+
+ softing_card_irq(card, 0);
+ ret = softing_reset_chip(card);
+ if (ret)
+ goto failed;
+ if (!mask_start)
+ /* no busses to be brought up */
+ goto card_done;
+
+ /* from here, we must jump to failed: */
+
+ if (mask_start & 1) {
+ pbus = card->bus[0];
+ /*init chip 1 */
+ card->dpram.fct->param[1] = pbus->can.bittiming.brp;
+ card->dpram.fct->param[2] = pbus->can.bittiming.sjw;
card->dpram.fct->param[3] =
- card->bus[0]->can.bittiming.phase_seg1 +
- card->bus[0]->can.bittiming.prop_seg;
+ pbus->can.bittiming.phase_seg1 +
+ pbus->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;
+ pbus->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (pbus->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES) ? 1 : 0;
if (softing_fct_cmd(card, 1, 0, "initialize_chip[0]"))
goto failed;
/*set mode */
if (softing_fct_cmd(card, 7, 0, "set_filter[0]"))
goto failed;
/*set output control */
- card->dpram.fct->param[1] = card->bus[0]->output;
+ card->dpram.fct->param[1] = pbus->output;
if (softing_fct_cmd(card, 5, 0, "set_output[0]"))
goto failed;
}
- if (bus1) {
+ if (mask_start & 2) {
+ pbus = card->bus[1];
/*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[1] = pbus->can.bittiming.brp;
+ card->dpram.fct->param[2] = pbus->can.bittiming.sjw;
card->dpram.fct->param[3] =
- card->bus[1]->can.bittiming.phase_seg1 +
- card->bus[1]->can.bittiming.prop_seg;
+ pbus->can.bittiming.phase_seg1 +
+ pbus->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;
+ pbus->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (pbus->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES) ? 1 : 0;
if (softing_fct_cmd(card, 2, 0, "initialize_chip[1]"))
goto failed;
/*set mode2 */
if (softing_fct_cmd(card, 8, 0, "set_filter[1]"))
goto failed;
/*set output control2 */
- card->dpram.fct->param[1] = card->bus[1]->output;
+ card->dpram.fct->param[1] = pbus->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;
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);
+ dev_info(card->dev, "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();
}
+
+ card->boot_time = ktime_get_real();
+
/*run once */
/*the bottom halve will start flushing the tx-queue too */
- tasklet_schedule(&card->irq.bh);
-
+ /*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);
+ /*
+ * do socketcan notifications/status changes
+ * from here, no errors should occur, or the failed: part
+ * must be reviewed
+ */
+ memset(&msg, 0, sizeof(msg));
+ msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
+ msg.can_dlc = CAN_ERR_DLC;
+ for (j = 0; j < card->nbus; ++j) {
+ pbus = card->bus[j];
+ if (!pbus)
+ continue;
+ if (!(mask_start & (1 << j)))
+ continue;
+ pbus->can.state = CAN_STATE_ERROR_ACTIVE;
+ open_candev(pbus->netdev);
+ if (bus != pbus) {
+ /* notify other busses on the restart */
+ softing_rx(pbus->netdev, &msg, ktime_set(0, 0));
+ ++pbus->can.can_stats.restarts;
+ }
+ netif_wake_queue(pbus->netdev);
}
+card_done:
+ mutex_unlock(&card->fw.lock);
return 0;
failed:
+ dev_alert(card->dev, "firmware failed, going idle\n");
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);
+ card->fw.failed = 1;
+ mutex_unlock(&card->fw.lock);
+ /* bring all other interfaces down */
+ for (j = 0; j < card->nbus; ++j) {
+ pbus = card->bus[j];
+ if (!pbus)
+ continue;
+ dev_close(pbus->netdev);
+ }
+ return -EIO;
+
+failed_already:
+ mutex_unlock(&card->fw.lock);
return -EIO;
}
}
}
-u32 softing_time2usec(struct softing *card, u32 raw)
+ktime_t softing_raw2ktime(struct softing *card, u32 raw)
{
+ uint64_t ovf;
+ uint64_t rawl;
+ uint64_t expected;
+ ktime_t now;
+ ktime_t target;
+ ovf = 0x100000000ULL;
+ rawl = raw;
/*TODO : don't loose higher order bits in computation */
switch (card->desc->freq) {
case 20:
- return raw * 4 / 5;
+ ovf = ovf * 4 / 5;
+ rawl = rawl * 4 / 5;
+ break;
case 24:
- return raw * 2 / 3;
+ ovf = ovf * 2 / 3;
+ rawl = rawl * 2 / 3;
+ break;
case 25:
- return raw * 16 / 25;
+ ovf = ovf * 16 / 25;
+ rawl = rawl * 16 / 25;
+ break;
case 0:
case 16:
+ break;
default:
- return raw;
+ /* return empty time */
+ return ktime_set(0, 0);
}
+ now = ktime_get_real();
+ expected = (ktime_us_delta(now, card->boot_time)) % ovf;
+ /*
+ * strange seuence for equation, but mind the 'unsigned-ness'
+ * the idea was to:
+ * if (expected < (rawl - (ovf / 2)))
+ * meaning: on wrap-around (occurs at 'ovf'), expected (actual time)
+ * may wrap around, altough rawl (receive time)
+ * is just before wrap-around. In that case, offset 'expected'
+ * note that expected can also be slightly earlier, as the card's
+ * timer starts a little (but unknown to me) after I mark 'boot_time'
+ */
+ if (rawl < (expected + (ovf / 2)))
+ /* now (expected) is always later than card stamp */
+ expected += ovf;
+ target = ktime_sub_us(now, (expected - rawl));
+ return target;
}
-
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/version.h>
#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
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#error This driver does not support Kernel versions < 2.6.23
+#endif
+
+/*
+ * test is a specific CAN netdev
+ * is online (ie. up 'n running, not sleeping, not busoff
*/
-void softing_flush_echo_skb(struct softing_priv *priv)
+static inline int canif_is_active(struct net_device *netdev)
{
- can_close_cleanup(priv->netdev);
- priv->tx.pending = 0;
- priv->tx.echo_put = 0;
- priv->tx.echo_get = 0;
+ struct can_priv *can = netdev_priv(netdev);
+ if (!netif_running(netdev))
+ return 0;
+ return (can->state <= CAN_STATE_ERROR_PASSIVE);
}
-/*softing_unlocked_tx_run:*/
-/*trigger the tx queue-ing*/
-/*no locks are grabbed, so be sure to have the spin spinlock*/
+/* trigger the tx queue-ing */
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_priv *priv = netdev_priv(dev);
struct softing *card = priv->card;
int ret;
int bhlock;
unsigned int fifo_wr;
struct can_frame msg;
- ret = -ENOTTY;
if (in_interrupt()) {
bhlock = 0;
spin_lock(&card->spin);
bhlock = 1;
spin_lock_bh(&card->spin);
}
- if (!card->fw.up) {
- ret = -EIO;
+ ret = NETDEV_TX_BUSY;
+ if (!card->fw.up)
goto xmit_done;
- }
- if (netif_carrier_ok(priv->netdev) <= 0) {
- ret = -EBADF;
+ if (card->tx.pending >= TXMAX)
goto xmit_done;
- }
- if (card->tx.pending >= TXMAX) {
- ret = -EBUSY;
- goto xmit_done;
- }
- if (priv->tx.pending >= CAN_ECHO_SKB_MAX) {
- ret = -EBUSY;
+ if (priv->tx.pending >= CAN_ECHO_SKB_MAX)
goto xmit_done;
- }
fifo_wr = card->dpram.tx->wr;
- if (fifo_wr == card->dpram.tx->rd) {
+ 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;
sizeof(card->dpram.tx->fifo[0]))
fifo_wr = 0;
card->dpram.tx->wr = fifo_wr;
- ret = 0;
+ card->tx.last_bus = priv->index;
++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;
+ /* can_put_echo_skb() saves the skb, safe to return TX_OK */
+ ret = NETDEV_TX_OK;
xmit_done:
if (bhlock)
spin_unlock_bh(&card->spin);
netif_stop_queue(bus->netdev);
}
}
+ if (ret != NETDEV_TX_OK)
+ netif_stop_queue(dev);
+
+ return ret;
+}
+
+int softing_rx(struct net_device *netdev, const struct can_frame *msg,
+ ktime_t ktime)
+{
+ struct sk_buff *skb;
+ int ret;
+ struct net_device_stats *stats;
- /* free skb, if not handled by the driver */
- if (skb)
- dev_kfree_skb(skb);
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(*msg)), msg, sizeof(*msg));
+ skb->tstamp = ktime;
+ ret = netif_rx(skb);
+ if (ret == NET_RX_DROP) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ stats = can_get_stats(netdev);
+#else
+ stats = &netdev->stats;
+#endif
+ ++stats->rx_dropped;
+ }
return ret;
}
{
int j;
struct softing_priv *bus;
- struct sk_buff *skb;
+ ktime_t ktime;
struct can_frame msg;
unsigned int fifo_rd;
unsigned int cnt = 0;
struct net_device_stats *stats;
+ u8 *ptr;
+ u32 tmp;
+ u8 cmd;
memset(&msg, 0, sizeof(msg));
if (card->dpram.rx->lost_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 */
+ /*
+ * service to all busses, we don't know which it was applicable
+ * but only service busses that are online
+ */
for (j = 0; j < card->nbus; ++j) {
bus = card->bus[j];
if (!bus)
continue;
- if (!netif_carrier_ok(bus->netdev))
+ if (!canif_is_active(bus->netdev))
+ /* a dead bus has no overflows */
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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ stats = can_get_stats(bus->netdev);
+#else
+ stats = &bus->netdev->stats;
+#endif
+ ++stats->rx_over_errors;
+ softing_rx(bus->netdev, &msg, ktime_set(0, 0));
}
+ /* prepare for other use */
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]))
+ if (++fifo_rd >= ARRAY_SIZE(card->dpram.rx->fifo))
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];
+
+ if (card->dpram.rx->wr == fifo_rd)
+ return cnt;
+
+ ptr = &card->dpram.rx->fifo[fifo_rd][0];
+
+ cmd = *ptr++;
+ if (cmd == 0xff) {
+ /*not quite usefull, probably the card has got out */
+ dev_alert(card->dev, "got cmd 0x%02x,"
+ " I suspect the card is lost\n", cmd);
+ }
+ /*mod_trace("0x%02x", cmd);*/
+ bus = card->bus[0];
+ if (cmd & CMD_BUS2)
+ bus = card->bus[1];
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
- stats = can_get_stats(bus->netdev);
+ stats = can_get_stats(bus->netdev);
#else
- stats = &bus->netdev->stats;
+ stats = &bus->netdev->stats;
#endif
- 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;
+ if (cmd & CMD_ERR) {
+ u8 can_state;
+ u8 state;
+ state = *ptr++;
- /*trigger socketcan */
- if (state == 2) {
+ 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_ERROR_PASSIVE;
+ msg.can_id |= CAN_ERR_BUSERROR;
+ msg.data[1] = CAN_ERR_CRTL_TX_PASSIVE;
+ state = 1;
+ } else {
+ can_state = CAN_STATE_ERROR_ACTIVE;
+ state = 0;
+ msg.can_id |= CAN_ERR_BUSERROR;
+ }
+ /*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;
+ ktime = softing_raw2ktime(card, tmp);
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+
+ ++bus->can.can_stats.bus_error;
+ ++stats->rx_errors;
+ /*update internal status */
+ if (can_state != bus->can.state) {
+ bus->can.state = can_state;
+ if (can_state == CAN_STATE_ERROR_PASSIVE)
+ ++bus->can.can_stats.error_passive;
+ if (can_state == CAN_STATE_BUS_OFF) {
/* 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);
+ /*trigger socketcan */
+ softing_rx(bus->netdev, &msg, ktime);
+ }
+
+ } 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;
- /*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);
- }
- }
+ } 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;
+ ktime = softing_raw2ktime(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) {
+ struct sk_buff *skb;
+ skb = bus->can.echo_skb[bus->tx.echo_get];
+ if (skb)
+ skb->tstamp = ktime;
+ 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;
+ stats->tx_bytes += msg.can_dlc;
+ } else {
+ ++stats->rx_packets;
+ stats->rx_bytes += msg.can_dlc;
+ bus->netdev->last_rx = jiffies;
+ softing_rx(bus->netdev, &msg, ktime);
}
- ++cnt;
}
+ ++cnt;
return cnt;
}
spin_lock(&card->spin);
while (softing_dev_svc_once(card) > 0)
++card->irq.svc_count;
+ spin_unlock(&card->spin);
/*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;
+ bus = card->bus[(j + offset + 1) % card->nbus];
+ if (!bus)
+ continue;
+ if (!canif_is_active(bus->netdev))
+ /* it makes no sense to wake dead busses */
+ continue;
+ if (bus->tx.pending >= CAN_ECHO_SKB_MAX)
+ continue;
+ netif_wake_queue(bus->netdev);
}
- */
- mod_alert("I think the card is vanished");
}
static
ir = card->dpram.virt[0xe02];
card->dpram.virt[0xe02] = 0;
if (card->dpram.rx->rd == 0xffff) {
- card_seems_down(card);
+ dev_alert(card->dev, "I think the card is gone\n");
return IRQ_NONE;
}
if (ir == 1) {
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);
+ dev_alert(card->dev, "I think the card is gone\n");
return IRQ_NONE;
}
tasklet_schedule(&card->irq.bh);
{
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);
+ /* check or determine and set bittime */
+ ret = open_candev(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;
+ goto failed;
+ ret = softing_cycle(card, priv, 1);
+ if (ret)
+ goto failed;
netif_start_queue(ndev);
return 0;
+failed:
+ return ret;
}
static int netdev_stop(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);
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;
+ /* softing cycle does close_candev() */
+ ret = softing_cycle(card, priv, 0);
+ return ret;
}
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);
+ int ret;
+
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;
+ /* softing cycle does close_candev() */
+ ret = softing_cycle(card, priv, 1);
+ return ret;
case CAN_MODE_STOP:
case CAN_MODE_SLEEP:
return -EOPNOTSUPP;
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
- );
+ dev_alert(card->dev, "%s, request_irq(%u) failed\n",
+ card->id.name, card->irq.nr);
return ret;
}
card->irq.requested = 1;
static void shutdown_card(struct softing *card)
{
int fw_up = 0;
- mod_trace("%s", card->id.name);
+ dev_dbg(card->dev, "%s()\n", __func__);
if (mutex_lock_interruptible(&card->fw.lock))
/* return -ERESTARTSYS*/;
fw_up = card->fw.up;
static int boot_card(struct softing *card)
{
- mod_trace("%s", card->id.name);
+ 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)];
+ dev_dbg(card->dev, "%s()\n", __func__);
+
if (mutex_lock_interruptible(&card->fw.lock))
return -ERESTARTSYS;
if (card->fw.up) {
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 {
+ if (!card->dpram.virt)
+ goto open_failed;
+ 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)))
+ continue;
+ /* memory is not equal */
+ dev_alert(card->dev, "write to dpram failed at 0x%04lx\n",
+ (unsigned long)(lp - card->dpram.virt));
goto open_failed;
}
+ wmb();
/*load boot firmware */
if (softing_load_fw(card->desc->boot.fw, card, card->dpram.virt,
card->dpram.size,
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)",
+ dev_info(card->dev, "card booted, type %s, "
+ "serial %u, fw %u, hw %u, lic %u, chip (%u,%u)\n",
card->id.name, card->id.serial, card->id.fw, card->id.hw,
card->id.lic, card->id.chip[0], card->id.chip[1]);
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,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+static const struct net_device_ops softing_netdev_ops = {
+ .ndo_open = netdev_open,
+ .ndo_stop = netdev_stop,
+ .ndo_start_xmit = netdev_start_xmit,
};
-
-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);
-}
+#endif
static struct softing_priv *mk_netdev(struct softing *card, u16 chip_id)
{
ndev = alloc_candev(sizeof(*priv));
if (!ndev) {
- mod_alert("alloc_candev failed");
+ dev_alert(card->dev, "alloc_candev failed\n");
return 0;
}
priv = netdev_priv(ndev);
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->can.clock.freq = 8000000;
+ priv->chip = chip_id;
priv->output = softing_default_output(card, priv);
SET_NETDEV_DEV(ndev, card->dev);
ndev->flags |= IFF_ECHO;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
+ ndev->netdev_ops = &softing_netdev_ops;
+#else
ndev->open = netdev_open;
ndev->stop = netdev_stop;
ndev->hard_start_xmit = netdev_start_xmit;
- priv->can.do_get_state = candev_get_state;
+#endif
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);
+ dev_alert(priv->card->dev, "%s, register failed\n",
+ priv->card->id.name);
goto reg_failed;
}
- ret = mk_netdev_sysfs(priv);
+ ret = softing_bus_sysfs_create(priv);
if (ret) {
- mod_alert("%s, sysfs failed", priv->card->id.name);
+ dev_alert(priv->card->dev, "%s, sysfs failed\n",
+ priv->card->id.name);
goto sysfs_failed;
}
return 0;
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;
shutdown_card(card);
for (j = 0; j < card->nbus; ++j) {
- unreg_netdev(card->bus[j]);
- rm_netdev(card->bus[j]);
+ if (!card->bus[j])
+ continue;
+ softing_bus_sysfs_remove(card->bus[j]);
+ unregister_candev(card->bus[j]->netdev);
+ free_candev(card->bus[j]->netdev);
+ card->bus[j] = 0;
}
- rm_card_sysfs(card);
+ softing_card_sysfs_remove(card);
iounmap(card->dpram.virt);
}
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);
+ dev_alert(card->dev, "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");
+ dev_alert(card->dev, "dpram ioremap failed\n");
goto ioremap_failed;
}
if (card->fn.reset)
card->fn.reset(card, 1);
if (boot_card(card)) {
- mod_alert("%s, failed to boot", card->id.name);
+ dev_alert(card->dev, "failed to boot\n");
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);
+ if (softing_card_sysfs_create(card)) {
+ dev_alert(card->dev, "sysfs failed\n");
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);
+ dev_alert(card->dev, "have %u busses\n", 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);
+ dev_alert(card->dev, "failed to make can[%i]", 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);
+ dev_alert(card->dev,
+ "failed to register can[%i]\n", j);
goto reg_failed;
}
}
- mod_trace("card initialised");
+ dev_info(card->dev, "card initialised\n");
return 0;
reg_failed:
- for (j = 0; j < card->nbus; ++j)
- unreg_netdev(card->bus[j]);
+ for (j = 0; j < card->nbus; ++j) {
+ if (!card->bus[j])
+ continue;
+ softing_bus_sysfs_remove(card->bus[j]);
+ unregister_candev(card->bus[j]->netdev);
+ }
netdev_failed:
for (j = 0; j < card->nbus; ++j) {
- if (card->bus[j])
- rm_netdev(card->bus[j]);
+ if (!card->bus[j])
+ continue;
+ free_candev(card->bus[j]->netdev);
+ card->bus[j] = 0;
}
- rm_card_sysfs(card);
+ softing_card_sysfs_remove(card);
sysfs_failed:
shutdown_card(card);
boot_failed:
static int __init mod_start(void)
{
- mod_trace("");
+ printk(KERN_INFO "[%s] start\n", THIS_MODULE->name);
return 0;
}
static void __exit mod_stop(void)
{
- mod_trace("");
+ printk(KERN_INFO "[%s] stop\n", THIS_MODULE->name);
}
module_init(mod_start);
--- /dev/null
+/*
+* drivers/net/can/softing/softing_sysfs.c
+*
+* Copyright (C) 2009
+*
+* - 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/version.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "softing.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#error This driver does not support Kernel versions < 2.6.23
+#endif
+
+/*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),
+};
+
+int softing_card_sysfs_create(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;
+}
+void softing_card_sysfs_remove(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;
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ val &= 0xFF;
+
+ ret = mutex_lock_interruptible(&card->fw.lock);
+ if (ret)
+ return -ERESTARTSYS;
+ if (netif_running(ndev)) {
+ mutex_unlock(&card->fw.lock);
+ return -EBUSY;
+ }
+ priv->output = val;
+ 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,
+};
+
+int softing_bus_sysfs_create(struct softing_priv *priv)
+{
+ if (!priv->netdev->dev.kobj.sd) {
+ dev_alert(priv->card->dev, "sysfs_create_group would fail\n");
+ return ENODEV;
+ }
+ return sysfs_create_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+void softing_bus_sysfs_remove(struct softing_priv *priv)
+{
+ sysfs_remove_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+
#include <linux/capability.h>
#include <linux/kernel.h>
+#include <linux/version.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 <socketcan/can.h>
+#include <socketcan/can/dev.h>
#include "sysfs.h"
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;
+ priv->ctrlmode = ctrlmode;
- return err;
+ return 0;
}
static ssize_t store_can_ctrlmode(struct device *dev,
if (priv->restart_ms < 0)
return -EOPNOTSUPP;
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
priv->restart_ms = ms;
return 0;
}
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)
+static ssize_t fmt_can_clock(struct net_device *dev, char *buf)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->clock.freq);
+}
-CAN_BT_ENTRY_RO(clock);
+static ssize_t show_can_clock(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return can_dev_show(d, attr, buf, fmt_can_clock);
+}
+static DEVICE_ATTR(hw_clock, S_IRUGO, show_can_clock, NULL);
#define CAN_BT_ENTRY(name) \
static ssize_t show_##name(struct device *d, \
read_lock(&dev_base_lock);
if (dev_isalive(dev))
- ret = sprintf(buf, "%ld\n",
- *(unsigned long *)(((u8 *)stats) + offset));
+ ret = sprintf(buf, "%d\n",
+ *(u32 *)(((u8 *)stats) + offset));
read_unlock(&dev_base_lock);
return ret;
CAN_STAT_ENTRY(error_warning);
CAN_STAT_ENTRY(error_passive);
+CAN_STAT_ENTRY(bus_off);
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_off.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
};
void can_create_sysfs(struct net_device *dev);
void can_remove_sysfs(struct net_device *dev);
+int can_sample_point(struct can_bittiming *bt);
#endif /* CAN_SYSFS_H */
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
#include <net/rtnetlink.h>
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
static __initdata const char banner[] =
#define CAN_DEV_H
#include <linux/version.h>
+#include <linux/can/netlink.h>
#include <linux/can/error.h>
-/*
- * CAN bitrate and bit-timing
- */
-struct can_bittiming {
- u32 bitrate;
- u32 sample_point;
- u32 tq;
- u32 prop_seg;
- u32 phase_seg1;
- u32 phase_seg2;
- u32 sjw;
- u32 clock;
- u32 brp;
-};
-
-struct can_bittiming_const {
- u32 tseg1_min;
- u32 tseg1_max;
- u32 tseg2_min;
- u32 tseg2_max;
- u32 sjw_max;
- u32 brp_min;
- u32 brp_max;
- u32 brp_inc;
-};
-
/*
* CAN mode
*/
CAN_MODE_SLEEP
};
-/*
- * CAN controller mode
- */
-#define CAN_CTRLMODE_LOOPBACK 0x1
-#define CAN_CTRLMODE_LISTENONLY 0x2
-#define CAN_CTRLMODE_3_SAMPLES 0x4 /* Triple sampling mode */
-
-/*
- * CAN operational and error states
- */
-enum can_state {
- CAN_STATE_ACTIVE = 0,
- CAN_STATE_BUS_WARNING,
- CAN_STATE_BUS_PASSIVE,
- CAN_STATE_BUS_OFF,
- CAN_STATE_STOPPED,
- CAN_STATE_SLEEPING
-};
-
-/*
- * CAN device statistics
- */
-struct can_device_stats {
- unsigned long error_warning;
- unsigned long data_overrun;
- unsigned long wakeup;
- unsigned long bus_error;
- unsigned long error_passive;
- unsigned long arbitration_lost;
- unsigned long restarts;
- unsigned long bus_error_at_init;
-};
-
/*
* CAN common private data
*/
struct can_bittiming bittiming;
struct can_bittiming_const *bittiming_const;
-
- spinlock_t irq_lock;
+ struct can_clock clock;
enum can_state state;
u32 ctrlmode;
int restart_ms;
- struct timer_list timer;
+ struct timer_list restart_timer;
struct sk_buff *echo_skb[CAN_ECHO_SKB_MAX];
int (*do_set_bittiming)(struct net_device *dev);
- int (*do_get_state)(struct net_device *dev, enum can_state *state);
int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
- int (*do_set_ctrlmode)(struct net_device *dev, u32 ctrlmode);
- int (*do_get_ctrlmode)(struct net_device *dev, u32 *ctrlmode);
+ int (*do_get_state)(const struct net_device *dev,
+ enum can_state *state);
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
struct net_device *alloc_candev(int sizeof_priv);
void free_candev(struct net_device *dev);
+
+int open_candev(struct net_device *dev);
+void close_candev(struct net_device *dev);
+
int register_candev(struct net_device *dev);
void unregister_candev(struct net_device *dev);
-int can_set_bittiming(struct net_device *dev);
-
int can_restart_now(struct net_device *dev);
-
void can_bus_off(struct net_device *dev);
-void can_close_cleanup(struct net_device *dev);
-
void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx);
void can_get_echo_skb(struct net_device *dev, int idx);
void can_free_echo_skb(struct net_device *dev, int idx);
-int can_sample_point(struct can_bittiming *bt);
-
#endif /* CAN_DEV_H */
--- /dev/null
+/*
+ * linux/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * $Id: dev.h 939 2009-02-14 14:30:19Z wolf $
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_NETLINK_H
+#define CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For futher information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+ __u32 bitrate; /* Bit-rate in bits/second */
+ __u32 sample_point; /* Sample point in one-tenth of a percent */
+ __u32 tq; /* Time quanta (TQ) in nanoseconds */
+ __u32 prop_seg; /* Propagation segment in TQs */
+ __u32 phase_seg1; /* Phase buffer segment 1 in TQs */
+ __u32 phase_seg2; /* Phase buffer segment 2 in TQs */
+ __u32 sjw; /* Synchronisation jump width in TQs */
+ __u32 brp; /* Bit-rate prescaler */
+};
+
+/*
+ * CAN harware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segement 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+ __u32 freq; /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
+ CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
+ CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
+ CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
+ CAN_STATE_STOPPED, /* Device is stopped */
+ CAN_STATE_SLEEPING, /* Device is sleeping */
+ CAN_STATE_MAX
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+ __u32 mask;
+ __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK 0x1 /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY 0x2 /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES 0x4 /* Triple sampling mode */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ __u32 bus_error; /* Bus errors */
+ __u32 error_warning; /* Changes to error warning state */
+ __u32 error_passive; /* Changes to error passive state */
+ __u32 bus_off; /* Changes to bus off state */
+ __u32 arbitration_lost; /* Arbitration lost errors */
+ __u32 restarts; /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+ IFLA_CAN_UNSPEC,
+ IFLA_CAN_BITTIMING,
+ IFLA_CAN_BITTIMING_CONST,
+ IFLA_CAN_CLOCK,
+ IFLA_CAN_STATE,
+ IFLA_CAN_CTRLMODE,
+ IFLA_CAN_RESTART_MS,
+ IFLA_CAN_RESTART,
+ __IFLA_CAN_MAX
+};
+
+#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1)
+
+#endif /* CAN_NETLINK_H */
#define OCR_MODE_TEST 0x01
#define OCR_MODE_NORMAL 0x02
#define OCR_MODE_CLOCK 0x03
+#define OCR_MODE_MASK 0x07
#define OCR_TX0_INVERT 0x04
#define OCR_TX0_PULLDOWN 0x08
#define OCR_TX0_PULLUP 0x10
#define OCR_TX1_PULLDOWN 0x40
#define OCR_TX1_PULLUP 0x80
#define OCR_TX1_PUSHPULL 0xc0
+#define OCR_TX_MASK 0xfc
+#define OCR_TX_SHIFT 2
struct sja1000_platform_data {
u32 clock; /* CAN bus oscillator frequency in Hz */
--- /dev/null
+/*
+ * socketcan/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * $Id$
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/socketcan/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
--- /dev/null
+/*
+ * socketcan/can/bcm.h
+ *
+ * Definitions for CAN Broadcast Manager (BCM)
+ *
+ * $Id$
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_BCM_H
+#define CAN_BCM_H
+
+/**
+ * struct bcm_msg_head - head of messages to/from the broadcast manager
+ * @opcode: opcode, see enum below.
+ * @flags: special flags, see below.
+ * @count: number of frames to send before changing interval.
+ * @ival1: interval for the first @count frames.
+ * @ival2: interval for the following frames.
+ * @can_id: CAN ID of frames to be sent or received.
+ * @nframes: number of frames appended to the message head.
+ * @frames: array of CAN frames.
+ */
+struct bcm_msg_head {
+ __u32 opcode;
+ __u32 flags;
+ __u32 count;
+ struct timeval ival1, ival2;
+ canid_t can_id;
+ __u32 nframes;
+ struct can_frame frames[0];
+};
+
+enum {
+ TX_SETUP = 1, /* create (cyclic) transmission task */
+ TX_DELETE, /* remove (cyclic) transmission task */
+ TX_READ, /* read properties of (cyclic) transmission task */
+ TX_SEND, /* send one CAN frame */
+ RX_SETUP, /* create RX content filter subscription */
+ RX_DELETE, /* remove RX content filter subscription */
+ RX_READ, /* read properties of RX content filter subscription */
+ TX_STATUS, /* reply to TX_READ request */
+ TX_EXPIRED, /* notification on performed transmissions (count=0) */
+ RX_STATUS, /* reply to RX_READ request */
+ RX_TIMEOUT, /* cyclic message is absent */
+ RX_CHANGED /* updated CAN frame (detected content change) */
+};
+
+#define SETTIMER 0x0001
+#define STARTTIMER 0x0002
+#define TX_COUNTEVT 0x0004
+#define TX_ANNOUNCE 0x0008
+#define TX_CP_CAN_ID 0x0010
+#define RX_FILTER_ID 0x0020
+#define RX_CHECK_DLC 0x0040
+#define RX_NO_AUTOTIMER 0x0080
+#define RX_ANNOUNCE_RESUME 0x0100
+#define TX_RESET_MULTI_IDX 0x0200
+#define RX_RTR_FRAME 0x0400
+
+#endif /* CAN_BCM_H */
--- /dev/null
+/*
+ * socketcan/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * $Id$
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <socketcan/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20090105"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ struct proto *prot;
+#else
+ struct module *owner;
+ int (*init)(struct sock *sk);
+ size_t obj_size;
+#endif
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern int can_rx_register(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data, char *ident);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#endif /* CAN_CORE_H */
--- /dev/null
+/*
+ * socketcan/can/dev.h
+ *
+ * Definitions for the CAN network device driver interface
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Andrey Volkov <avolkov@varma-el.com>
+ * Varma Electronics Oy
+ *
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ */
+
+#ifndef CAN_DEV_H
+#define CAN_DEV_H
+
+#include <linux/version.h>
+#include <socketcan/can/netlink.h>
+#include <socketcan/can/error.h>
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN common private data
+ */
+#define CAN_ECHO_SKB_MAX 4
+
+struct can_priv {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats net_stats;
+#endif
+ struct can_device_stats can_stats;
+
+ struct can_bittiming bittiming;
+ struct can_bittiming_const *bittiming_const;
+ struct can_clock clock;
+
+ enum can_state state;
+ u32 ctrlmode;
+
+ int restart_ms;
+ struct timer_list restart_timer;
+
+ struct sk_buff *echo_skb[CAN_ECHO_SKB_MAX];
+
+ int (*do_set_bittiming)(struct net_device *dev);
+ int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
+ int (*do_get_state)(const struct net_device *dev,
+ enum can_state *state);
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define ND2D(_ndev) (_ndev->class_dev.dev)
+#else
+#define ND2D(_ndev) (_ndev->dev.parent)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+struct net_device_stats *can_get_stats(struct net_device *dev);
+#endif
+
+struct net_device *alloc_candev(int sizeof_priv);
+void free_candev(struct net_device *dev);
+
+int open_candev(struct net_device *dev);
+void close_candev(struct net_device *dev);
+
+int register_candev(struct net_device *dev);
+void unregister_candev(struct net_device *dev);
+
+int can_restart_now(struct net_device *dev);
+void can_bus_off(struct net_device *dev);
+
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx);
+void can_get_echo_skb(struct net_device *dev, int idx);
+void can_free_echo_skb(struct net_device *dev, int idx);
+
+#endif /* CAN_DEV_H */
--- /dev/null
+/*
+ * socketcan/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * $Id$
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--- /dev/null
+
+/*
+ * socketcan/can/ioctl.h
+ *
+ * Definitions for CAN controller setup (work in progress)
+ *
+ * $Id$
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_IOCTL_H
+#define CAN_IOCTL_H
+
+#include <linux/sockios.h>
+
+/*
+ * CAN bitrate
+ */
+#define CAN_BITRATE_UNCONFIGURED ((__u32) 0xFFFFFFFFU)
+#define CAN_BITRATE_UNKNOWN 0
+#define CAN_BITRATE_DEFAULT 500000
+
+/*
+ * CAN custom bit time
+ */
+enum can_bittimes {
+ CAN_BITTIME_STD,
+ CAN_BITTIME_BTR
+};
+
+/* TSEG1 of controllers usually is a sum of synch_seg (always 1),
+ * prop_seg and phase_seg1, TSEG2 = phase_seg2 */
+
+struct can_bittime_std {
+ __u32 brp; /* baud rate prescaler */
+ __u8 prop_seg; /* from 1 to 8 */
+ __u8 phase_seg1; /* from 1 to 8 */
+ __u8 phase_seg2; /* from 1 to 8 */
+ __u8 sjw:7; /* from 1 to 4 */
+ __u8 sam:1; /* 1 - enable triple sampling */
+};
+
+struct can_bittime_btr {
+ __u8 btr0;
+ __u8 btr1;
+};
+
+struct can_bittime {
+ enum can_bittimes type;
+ union {
+ struct can_bittime_std std;
+ struct can_bittime_btr btr;
+ };
+};
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN controller mode
+ */
+#define CAN_CTRLMODE_LOOPBACK 0x1
+#define CAN_CTRLMODE_LISTENONLY 0x2
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ACTIVE = 0,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF,
+ CAN_STATE_STOPPED,
+ CAN_STATE_SLEEPING
+};
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ int error_warning;
+ int data_overrun;
+ int wakeup;
+ int bus_error;
+ int error_passive;
+ int arbitration_lost;
+ int restarts;
+ int bus_error_at_init;
+};
+
+#endif /* CAN_IOCTL_H */
--- /dev/null
+/*
+ * socketcan/can/isotp.h
+ *
+ * Definitions for isotp CAN sockets
+ *
+ * $Id$
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2008 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ISOTP_H
+#define CAN_ISOTP_H
+
+#include <socketcan/can.h>
+
+#define SOL_CAN_ISOTP (SOL_CAN_BASE + CAN_ISOTP)
+
+/* for socket options affecting the socket (not the global system) */
+
+#define CAN_ISOTP_OPTS 1
+#define CAN_ISOTP_RECV_FC 2
+
+struct can_isotp_options {
+
+ __u32 flags; /* set flags for isotp behaviour. */
+ /* __u32 value : flags see below */
+
+ __u32 frame_txtime; /* frame transmission time (N_As/N_Ar) */
+ /* __u32 value : time in nano secs */
+
+ __u8 ext_address; /* set address for extended addressing */
+ /* __u8 value : extended address */
+
+ __u8 txpad_content; /* set content of padding byte (tx) */
+ /* __u8 value : content on tx path */
+
+ __u8 rxpad_content; /* set content of padding byte (rx) */
+ /* __u8 value : content on rx path */
+};
+
+struct can_isotp_fc_options {
+
+ __u8 bs; /* blocksize provided in FC frame */
+ /* __u8 value : blocksize. 0 = off */
+
+ __u8 stmin; /* separation time provided in FC frame */
+ /* __u8 value : */
+ /* 0x00 - 0x7F : 0 - 127 ms */
+ /* 0x80 - 0xF0 : reserved */
+ /* 0xF1 - 0xF9 : 100 us - 900 us */
+ /* 0xFA - 0xFF : reserved */
+
+ __u8 wftmax; /* max. number of wait frame transmiss. */
+ /* __u8 value : 0 = omit FC N_PDU WT */
+};
+
+
+/* flags for isotp behaviour */
+
+#define CAN_ISOTP_LISTEN_MODE 0x01 /* listen only (do not send FC) */
+#define CAN_ISOTP_EXTEND_ADDR 0x02 /* enable extended addressing */
+#define CAN_ISOTP_TX_PADDING 0x04 /* enable CAN frame padding tx path */
+#define CAN_ISOTP_RX_PADDING 0x08 /* enable CAN frame padding rx path */
+#define CAN_ISOTP_CHK_PAD_LEN 0x10 /* check received CAN frame padding */
+#define CAN_ISOTP_CHK_PAD_DATA 0x20 /* check received CAN frame padding */
+#define CAN_ISOTP_HALF_DUPLEX 0x40 /* half duplex error state handling */
+
+
+/* default values */
+
+#define CAN_ISOTP_DEFAULT_FLAGS 0
+#define CAN_ISOTP_DEFAULT_EXT_ADDRESS 0x00
+#define CAN_ISOTP_DEFAULT_RXPAD_CONTENT 0x00
+#define CAN_ISOTP_DEFAULT_TXPAD_CONTENT 0x00
+#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 0
+#define CAN_ISOTP_DEFAULT_RECV_BS 0
+#define CAN_ISOTP_DEFAULT_RECV_STMIN 0x00
+#define CAN_ISOTP_DEFAULT_RECV_WFTMAX 0
+
+/*
+ * Remark on CAN_ISOTP_DEFAULT_RECV_* values:
+ *
+ * We can strongly assume, that the Linux Kernel implementation of
+ * CAN_ISOTP is capable to run with BS=0, STmin=0 and WFTmax=0.
+ * But as we like to be able to behave as a commonly available ECU,
+ * these default settings can be changed via sockopts.
+ * For that reason the STmin value is intentionally _not_ checked for
+ * consistency and copied directly into the flow control (FC) frame.
+ *
+ */
+
+#endif
--- /dev/null
+/*
+ * socketcan/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * $Id: dev.h 939 2009-02-14 14:30:19Z wolf $
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_NETLINK_H
+#define CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For futher information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+ __u32 bitrate; /* Bit-rate in bits/second */
+ __u32 sample_point; /* Sample point in one-tenth of a percent */
+ __u32 tq; /* Time quanta (TQ) in nanoseconds */
+ __u32 prop_seg; /* Propagation segment in TQs */
+ __u32 phase_seg1; /* Phase buffer segment 1 in TQs */
+ __u32 phase_seg2; /* Phase buffer segment 2 in TQs */
+ __u32 sjw; /* Synchronisation jump width in TQs */
+ __u32 brp; /* Bit-rate prescaler */
+};
+
+/*
+ * CAN harware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segement 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+ __u32 freq; /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
+ CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
+ CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
+ CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
+ CAN_STATE_STOPPED, /* Device is stopped */
+ CAN_STATE_SLEEPING, /* Device is sleeping */
+ CAN_STATE_MAX
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+ __u32 mask;
+ __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK 0x1 /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY 0x2 /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES 0x4 /* Triple sampling mode */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ __u32 bus_error; /* Bus errors */
+ __u32 error_warning; /* Changes to error warning state */
+ __u32 error_passive; /* Changes to error passive state */
+ __u32 bus_off; /* Changes to bus off state */
+ __u32 arbitration_lost; /* Arbitration lost errors */
+ __u32 restarts; /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+ IFLA_CAN_UNSPEC,
+ IFLA_CAN_BITTIMING,
+ IFLA_CAN_BITTIMING_CONST,
+ IFLA_CAN_CLOCK,
+ IFLA_CAN_STATE,
+ IFLA_CAN_CTRLMODE,
+ IFLA_CAN_RESTART_MS,
+ IFLA_CAN_RESTART,
+ __IFLA_CAN_MAX
+};
+
+#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1)
+
+#endif /* CAN_NETLINK_H */
--- /dev/null
+#ifndef __CAN_PLATFORM_MCP251X_H__
+#define __CAN_PLATFORM_MCP251X_H__
+
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ */
+
+/**
+ * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data
+ * @oscillator_frequency: - oscillator frequency in Hz
+ * @model: - actual type of chip
+ * @board_specific_setup: - called before probing the chip (power,reset)
+ * @transceiver_enable: - called to power on/off the transceiver
+ * @power_enable: - called to power on/off the mcp *and* the
+ * transceiver
+ *
+ * Please note that you should define power_enable or transceiver_enable or
+ * none of them. Defining both of them is no use.
+ *
+ */
+
+struct mcp251x_platform_data {
+ unsigned long oscillator_frequency;
+ int model;
+#define CAN_MCP251X_MCP2510 0
+#define CAN_MCP251X_MCP2515 1
+ int (*board_specific_setup)(struct spi_device *spi);
+ int (*transceiver_enable)(int enable);
+ int (*power_enable) (int enable);
+};
+
+#endif /* __CAN_PLATFORM_MCP251X_H__ */
--- /dev/null
+#ifndef _CAN_PLATFORM_SJA1000_H_
+#define _CAN_PLATFORM_SJA1000_H_
+
+/* clock divider register */
+#define CDR_CLKOUT_MASK 0x07
+#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */
+#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */
+#define CDR_CBP 0x40 /* CAN input comparator bypass */
+#define CDR_PELICAN 0x80 /* PeliCAN mode */
+
+/* output control register */
+#define OCR_MODE_BIPHASE 0x00
+#define OCR_MODE_TEST 0x01
+#define OCR_MODE_NORMAL 0x02
+#define OCR_MODE_CLOCK 0x03
+#define OCR_MODE_MASK 0x07
+#define OCR_TX0_INVERT 0x04
+#define OCR_TX0_PULLDOWN 0x08
+#define OCR_TX0_PULLUP 0x10
+#define OCR_TX0_PUSHPULL 0x18
+#define OCR_TX1_INVERT 0x20
+#define OCR_TX1_PULLDOWN 0x40
+#define OCR_TX1_PULLUP 0x80
+#define OCR_TX1_PUSHPULL 0xc0
+#define OCR_TX_MASK 0xfc
+#define OCR_TX_SHIFT 2
+
+struct sja1000_platform_data {
+ u32 clock; /* CAN bus oscillator frequency in Hz */
+
+ u8 ocr; /* output control register */
+ u8 cdr; /* clock divider register */
+};
+
+#endif /* !_CAN_PLATFORM_SJA1000_H_ */
--- /dev/null
+/*
+ * socketcan/can/raw.h
+ *
+ * Definitions for raw CAN sockets
+ *
+ * $Id$
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_RAW_H
+#define CAN_RAW_H
+
+#include <socketcan/can.h>
+
+#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)
+
+/* for socket options affecting the socket (not the global system) */
+
+enum {
+ CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */
+ CAN_RAW_ERR_FILTER, /* set filter for error frames */
+ CAN_RAW_LOOPBACK, /* local loopback (default:on) */
+ CAN_RAW_RECV_OWN_MSGS /* receive my own msgs (default:off) */
+};
+
+#endif
--- /dev/null
+/*
+ * socketcan/can/version.h
+ *
+ * Version information for the CAN network layer implementation
+
+ * Author: Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_VERSION_H
+#define CAN_VERSION_H
+
+#define RCSID(s) asm(".section .rodata.str1.1,\"aMS\",@progbits,1\n\t" \
+ ".string \"" s "\"\n\t.previous\n")
+
+RCSID("$Id$");
+
+#endif /* CAN_VERSION_H */
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#endif
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
static __initdata const char banner[] = KERN_INFO
return -EAFNOSUPPORT;
#endif
-#ifdef CONFIG_KMOD
- /* try to load protocol module, when CONFIG_KMOD is defined */
+#ifdef CONFIG_MODULES
+ /* try to load protocol module kernel is modular */
if (!proto_tab[protocol]) {
err = request_module("can-proto-%d", protocol);
*/
static struct packet_type can_packet __read_mostly = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ .type = cpu_to_be16(ETH_P_CAN),
+#else
.type = __constant_htons(ETH_P_CAN),
+#endif
.dev = NULL,
.func = can_rcv,
};
}
spin_unlock(&can_rcvlists_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ rcu_barrier(); /* Wait for completion of call_rcu()'s */
+#endif
+
kmem_cache_destroy(rcv_cache);
}
#include <linux/netdevice.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
-#include <linux/can.h>
+#include <socketcan/can.h>
/* af_can rx dispatcher structures */
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/bcm.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/bcm.h>
#include <net/sock.h>
#include "compat.h"
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
/* use of last_frames[index].can_dlc */
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/bcm.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/bcm.h>
#include <net/sock.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
/* use of last_frames[index].can_dlc */
}
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+static inline void setup_timer(struct timer_list * timer,
+ void (*function)(unsigned long),
+ unsigned long data)
+{
+ timer->function = function;
+ timer->data = data;
+ init_timer(timer);
+}
+#endif
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
#define round_jiffies(j) (j)
#endif
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/isotp.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/isotp.h>
#include <net/sock.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define CAN_ISOTP_VERSION CAN_VERSION
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
-#include <linux/can/core.h>
+#include <socketcan/can/core.h>
#include "af_can.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
/*
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/raw.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/raw.h>
#include <net/sock.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define CAN_RAW_VERSION CAN_VERSION
#
# Call the program as
#
-# strip-src [-v <version>] [-i] [-d] [<file>]
+# strip-src [-v <version>] [-i] [-m] [-d] [<file>]
#
# The -v option will generate a source file for specified kernel version
# where <version> is the kernel version you want the code to be generated
#
# The -i option will strip all Subversion Id lines from the the source.
#
+# The -m option will replace macros like ND2D with real variable name.
+#
# The -d (debug) option will give you some output on what the program does.
#
# If <file> is given, read that source file, otherwise stdin.
$re_elif = "^\\#elif LINUX_VERSION_CODE (==|!=|<|<=|>|>=) " .
"KERNEL_VERSION\\((\\d+),(\\d+),(\\d+)\\)";
-Getopts("div:");
+Getopts("dimv:");
$debug = $opt_d;
if (defined($opt_v)) {
if (defined($opt_v)) {
strip_kversion($opt_v);
+ replace_socketcan($opt_v);
+
+ if (defined($opt_m)) {
+ replace_macros($opt_v);
+ }
}
if (defined($opt_i)) {
print if (defined($_));
}
+sub replace_socketcan {
+ my($i);
+
+ for $i (0..$#lines) {
+ $lines[$i] =~ s+socketcan/can+linux/can+g;
+ }
+}
+
+sub replace_macros {
+ my($version) = @_;
+ my($i);
+
+ $old = compare($version, "<", "2", "6", "21");
+
+ for $i (0..$#lines) {
+
+ if ($lines[$i] =~ /#define ND2D/) {
+ delete_line($i, "<del>");
+ } else {
+ if ($old) {
+ $lines[$i] =~ s/ND2D\(\b(\w+)\b\)/$1\->class_dev.dev/g;
+ } else {
+ $lines[$i] =~ s/ND2D\(\b(\w+)\b\)/$1\->dev.parent/g;
+ }
+ }
+ }
+}
+
sub strip_id {
my($i);
-
+
for $i (0..$#lines) {
if ($lines[$i] =~ /include.*linux(\/can)?\/version\.h/) {