* [PATCH 0/8] can: CAN network device driver interface and drivers @ 2009-02-19 19:01 Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 1/8] can: Documentation for the CAN device driver interface Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel the PF_CAN protocol family for the Controller Area Network is available in the kernel since version 2.6.25 but drivers for real CAN devices are still missing. This patch series adds a generic CAN network device driver interface and, as a start, a few drivers. It is the result of the ongoing discussion and development of the Socket-CAN project hosted at the BerliOS web-server (http://developer.berlios.de/projects/socketcan). The patch series consists of the following patches: [PATCH 1/8] can: Documentation for the CAN device driver interface [PATCH 2/8] can: Update MAINTAINERS and CREDITS file [PATCH 3/8] can: CAN Network device driver and SYSFS interface [PATCH 4/8] can: Driver for the SJA1000 CAN controller [PATCH 5/8] can: SJA1000 generic platform bus driver [PATCH 6/8] can: SJA1000 driver for EMS PCI cards [PATCH 7/8] can: SJA1000 driver for Kvaser PCI cards [PATCH 8/8] can: Driver for the Freescale MSCAN controller Please consider these patches for inclusion. Thanks, Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/8] can: Documentation for the CAN device driver interface 2009-02-19 19:01 [PATCH 0/8] can: CAN network device driver interface and drivers Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 2/8] can: Update MAINTAINERS and CREDITS file Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger, Oliver Hartkopp This patch documents the CAN netowrk device drivers interface, removes obsolete documentation and adds some useful links to CAN resources. Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> --- Documentation/networking/can.txt | 214 ++++++++++++++++++++++++++++++------- 1 files changed, 173 insertions(+), 41 deletions(-) diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt index 2035bc4..d2cc3de 100644 --- a/Documentation/networking/can.txt +++ b/Documentation/networking/can.txt @@ -36,10 +36,15 @@ This file contains 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 SYSFS files to set and 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 ============================================================================ @@ -234,6 +239,8 @@ solution for a couple of reasons: 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 ------------------------ @@ -613,53 +620,178 @@ solution for a couple of reasons: - 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 - - On the project website http://developer.berlios.de/projects/socketcan - there are different drivers available: - - 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) - - 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 - - Please check the Mailing Lists on the berlios OSS project website. - - 6.6 todo - - 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. - -7. Credits + 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 CAN devices, like setting the bit-timing parameters, through + SYSFS files. They are described in the following chapter. 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 SYSFS files to set and get devices properties + + When the CAN device is registered, a set of SYSFS files is created in + "/sys/class/net/canX/". These files allow to set and get device + properties. Here is a list of the files and a brief explanation for + what they are good for: + + can_state: + Returns the current state of the CAN controller: "active", + "stopped" or "sleeping" or the bus error states "bus-warn", + "bus-pass" or "bus-off". Please do not read it frequently. + State changes are also reported via CAN error frames and the + application should monitor and handle them instead (see also + chapter 3.4). + + can_ctrlmode: + Allows to set various CAN controller modes like loop-back (0x1), + listen-only (0x2) or triple-sampling (0x4) by writing the + corresponding bit mask to this file. Please use the prefix "0x" + for hexadecimal values. + + can_echo: + Allows to read and toggle the local loop-back functionality of + the device. See chapter 6.2 for further information. + + can_restart: + Writing "1" to this file allows to manually trigger a restart of + the CAN controller in case of a bus-off condition. This file is + not readable. + + can_restart_ms: + Allows to set and get the automatic restart delay time. If set to + a value greater than 0, 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. + + can_bittiming: + Sub-directory with files to set and get CAN bit-timing + parameters. + + can_bittiming/bitrate + can_bittiming/sample_point: + If calculation of CAN bit-timing parameters is enabled in the + kernel (CONFIG_CAN_CALC_BITTIMING=y), the bit-timing can be + defined by writing the bit-rate in bits/sec to "bitrate". + Otherwise the file is read-only and returns the actual bit-rate. + Accordingly the sample-point in one-tenth of a percent can be + set or read. By default it's set to 0 using CIA-recommended + sample-points. + + can_bittiming/tq + can_bittiming/prop_seg + can_bittiming/phase_seg1 + can_bittiming/phase_seg2 + can_bittiming/sjw: + These files allow to define the CAN bit-timing in a hardware + independent format as proposed by the Bosch CAN 2.0 + specification. + + can_bittiming/hw_clock + can_bittiming/hw_brp_inc + can_bittiming/hw_brp_max + can_bittiming/hw_brp_min + can_bittiming/hw_sjw_max + can_bittiming/hw_tseg1_max + can_bittiming/hw_tseg1_min + can_bittiming/hw_tseg2_max + can_bittiming/hw_tseg2_min: + These read-only files list the bit-timing constants for this + device. They are provided by the CAN controller driver and are + used to calculate and check CAN bit-timing parameters. + Furthermore, these values can be used for user-defined + (non-standard) bit-timing calculation algorithms in userspace. + + can_statistics + can_statistics/arbitration_lost + can_statistics/bus_error + can_statistics/data_overrun + can_statistics/error_passive + can_statistics/error_warning + can_statistics/restarts + can_statistics/wakeup: + These read-only files list CAN specific statistics values. + + 6.5.2 Setting the CAN bit-timing + + The CAN bit-timing parameters can always be specified in a hardware + independent format as proposed in the Bosch CAN 2.0 specification + using the SYSFS files "tq", "prop_seg", "phase_seg1", "phase_seg2" + and "sjw". The SYSFS files are described in the previous chapter. + + If the kernel option CONFIG_CAN_CALC_BITTIMING is enabled, CIA + recommended CAN bit-timing parameters will be calculated for the bit- + rate written to the SYSFS file "bitrate" when the device gets started. + Note that this works fine for the most common CAN controllers with + standard bit-rates but may *fail* for exotic bit-rates or CAN source + clock frequencies. Disabling 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 available in the + SYSFS directory "can_bittiming" as well with the name prefix "hw_". + + 6.5.3 Starting and stopping the CAN network device + + A CAN network device is started and stopped as usual with the command + "ifconfig canX up" and "ifconfig canX 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: + + # echo 125000 > /sys/class/net/can0/can_bittiming/bitrate + # ifconfig can0 up + + A device may enter "bus-off" state if too much errors occurred on the + CAN bus. Then no more messages can be received or sent. The SYSFS file + "/sys/class/net/can0/can_state" then reports "bus-off". An automatic + bus-off recovery can be enabled by writing the desired restart delay + in milliseconds to "/sys/class/net/canX/can_restart_ms". + Alternatively, the application may realize the "bus-off" condition + by monitoring CAN error frames and do a restart when appropriate by + writing to the SYSFS file /sys/class/net/canX/can_restart. Note that + a restart will also create a CAN error frame (see also chapter 3.4). + + 6.6 Supported CAN hardware + + 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. + +7. Socket CAN resources +----------------------- + + 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: + + http://developer.berlios.de/projects/socketcan + + 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. + +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) -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/8] can: Update MAINTAINERS and CREDITS file 2009-02-19 19:01 ` [PATCH 1/8] can: Documentation for the CAN device driver interface Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> --- CREDITS | 4 ++++ MAINTAINERS | 7 +++++++ 2 files changed, 11 insertions(+), 0 deletions(-) diff --git a/CREDITS b/CREDITS index 2b39168..ecbf6a2 100644 --- a/CREDITS +++ b/CREDITS @@ -1248,6 +1248,10 @@ S: 8124 Constitution Apt. 7 S: Sterling Heights, Michigan 48313 S: USA +N: Wolfgang Grandegger +E: wg@grandegger.com +D: Controller Area Network (device drivers) + N: William Greathouse E: wgreathouse@smva.com E: wgreathouse@myfavoritei.com diff --git a/MAINTAINERS b/MAINTAINERS index db65b4e..7bbf231 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1063,6 +1063,13 @@ L: socketcan-core@lists.berlios.de (subscribers-only) W: http://developer.berlios.de/projects/socketcan/ S: Maintained +CAN NETWORK DRIVERS +P: Wolfgang Grandegger +M: wg@grandegger.com +L: socketcan-core@lists.berlios.de (subscribers-only) +W: http://developer.berlios.de/projects/socketcan/ +S: Maintained + CELL BROADBAND ENGINE ARCHITECTURE P: Arnd Bergmann M: arnd@arndb.de -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-19 19:01 ` [PATCH 2/8] can: Update MAINTAINERS and CREDITS file Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger ` (2 more replies) 0 siblings, 3 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger, Oliver Hartkopp The CAN network device driver interface provides a generic interface to setup, configure and monitor CAN network devices. It exports a set of common data structures and 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. Furthermore adds a SYSFS interface to set and get CAN device properties. When the CAN device is registered, a set of SYSFS files is created in "/sys/class/net/canX/". These files allow to set and get device properties like bit-timing parameters, state, controller mode and CAN statistics. For further information please check "Documentation/networking/can.txt" Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> --- drivers/net/can/Kconfig | 23 ++ drivers/net/can/Makefile | 5 + drivers/net/can/dev.c | 528 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/can/sysfs.c | 509 ++++++++++++++++++++++++++++++++++++++++++++ drivers/net/can/sysfs.h | 24 ++ include/linux/can/dev.h | 136 ++++++++++++ 6 files changed, 1225 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/dev.c create mode 100644 drivers/net/can/sysfs.c create mode 100644 drivers/net/can/sysfs.h create mode 100644 include/linux/can/dev.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 57def0d..d609895 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -12,6 +12,29 @@ config CAN_VCAN This driver can also be built as a module. If so, the module will be called vcan. +config CAN_DEV + tristate "Platform CAN drivers with SYSFS support" + depends on CAN && SYSFS + default Y + ---help--- + Enables the common framework for platform CAN drivers with SYSFS + support. This is the standard library for CAN drivers. + If unsure, say Y. + +config CAN_CALC_BITTIMING + bool "CAN bit-timing calculation" + depends on CAN_DEV + default Y + ---help--- + If enabled, CAN bit-timing parameters will be calculated for the + bit-rate specified via SYSFS file "bitrate" when the device gets + started. This works fine for the most common CAN controllers + with standard bit-rates but may fail for exotic bit-rates or CAN + source clock frequencies. Disabling saves some space, but then the + bit-timing parameters must be specified directly using the SYSFS + files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". + If unsure, say Y. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index c4bead7..557114b 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -3,3 +3,8 @@ # obj-$(CONFIG_CAN_VCAN) += vcan.o + +obj-$(CONFIG_CAN_DEV) += can-dev.o +can-dev-y := dev.o sysfs.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c new file mode 100644 index 0000000..faa75d1 --- /dev/null +++ b/drivers/net/can/dev.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix + * Copyright (C) 2006 Andrey Volkov, Varma Electronics + * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <net/rtnetlink.h> + +#include "sysfs.h" + +#define MOD_DESC "CAN device driver interface" + +MODULE_DESCRIPTION(MOD_DESC); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); + +#ifdef CONFIG_CAN_CALC_BITTIMING +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ + +/* + * Bit-timing calculation derived from: + * + * Code based on LinCAN sources and H8S2638 project + * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz + * Copyright 2005 Stanislav Marek + * email: pisa@cmp.felk.cvut.cz + */ +static int can_update_spt(const struct can_bittiming_const *btc, + int sampl_pt, int tseg, int *tseg1, int *tseg2) +{ + *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; + if (*tseg2 < btc->tseg2_min) + *tseg2 = btc->tseg2_min; + if (*tseg2 > btc->tseg2_max) + *tseg2 = btc->tseg2_max; + *tseg1 = tseg - *tseg2; + if (*tseg1 > btc->tseg1_max) { + *tseg1 = btc->tseg1_max; + *tseg2 = tseg - *tseg1; + } + return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); +} + +static int can_calc_bittiming(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->bittiming; + const struct can_bittiming_const *btc = priv->bittiming_const; + long rate, best_rate = 0; + long best_error = 1000000000, error = 0; + int best_tseg = 0, best_brp = 0, brp = 0; + int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0; + int spt_error = 1000, spt = 0, sampl_pt; + u64 v64; + + if (!priv->bittiming_const) + return -ENOTSUPP; + + /* Use CIA recommended sample points */ + if (bt->sample_point) { + sampl_pt = bt->sample_point; + } else { + if (bt->bitrate > 800000) + sampl_pt = 750; + else if (bt->bitrate > 500000) + sampl_pt = 800; + else + sampl_pt = 875; + } + + /* tseg even = round down, odd = round up */ + for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; + tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) { + tsegall = 1 + tseg / 2; + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = bt->clock / (tsegall * bt->bitrate) + tseg % 2; + /* chose brp step which is possible in system */ + brp = (brp / btc->brp_inc) * btc->brp_inc; + if ((brp < btc->brp_min) || (brp > btc->brp_max)) + continue; + rate = bt->clock / (brp * tsegall); + error = bt->bitrate - rate; + /* tseg brp biterror */ + if (error < 0) + error = -error; + if (error > best_error) + continue; + best_error = error; + if (error == 0) { + spt = can_update_spt(btc, sampl_pt, tseg / 2, + &tseg1, &tseg2); + error = sampl_pt - spt; + if (error < 0) + error = -error; + if (error > spt_error) + continue; + spt_error = error; + } + best_tseg = tseg / 2; + best_brp = brp; + best_rate = rate; + if (error == 0) + break; + } + + if (best_error) { + /* Error in one-tenth of a percent */ + error = (best_error * 1000) / bt->bitrate; + if (error > CAN_CALC_MAX_ERROR) { + dev_err(ND2D(dev), "bitrate error %ld.%ld%% too high\n", + error / 10, error % 10); + return -EDOM; + } else { + dev_warn(ND2D(dev), "bitrate error %ld.%ld%%\n", + error / 10, error % 10); + } + } + + spt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2); + + v64 = (u64)best_brp * 1000000000UL; + do_div(v64, bt->clock); + bt->tq = (u32)v64; + bt->prop_seg = tseg1 / 2; + bt->phase_seg1 = tseg1 - bt->prop_seg; + bt->phase_seg2 = tseg2; + bt->sjw = 1; + bt->brp = best_brp; + + return 0; +} +#else /* !CONFIG_CAN_CALC_BITTIMING */ +static int can_calc_bittiming(struct net_device *dev) +{ + dev_err(ND2D(dev), "bit-timing calculation not available\n"); + return -EINVAL; +} +#endif /* CONFIG_CAN_CALC_BITTIMING */ + +int can_sample_point(struct can_bittiming *bt) +{ + return ((bt->prop_seg + bt->phase_seg1 + 1) * 1000) / + (bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1); +} + +static int can_fixup_bittiming(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->bittiming; + const struct can_bittiming_const *btc = priv->bittiming_const; + int tseg1, alltseg; + u32 bitrate; + u64 brp64; + + if (!priv->bittiming_const) + return -ENOTSUPP; + + tseg1 = bt->prop_seg + bt->phase_seg1; + if (bt->sjw > btc->sjw_max || + tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max || + bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max) + return -EINVAL; + + brp64 = (u64)bt->clock * (u64)bt->tq; + if (btc->brp_inc > 1) + do_div(brp64, btc->brp_inc); + brp64 += 500000000UL - 1; + do_div(brp64, 1000000000UL); /* the practicable BRP */ + if (btc->brp_inc > 1) + brp64 *= btc->brp_inc; + bt->brp = (u32)brp64; + + if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) + return -EINVAL; + + alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1; + bitrate = bt->clock / (bt->brp * alltseg); + bt->bitrate = bitrate; + + return 0; +} + +/* + * Set CAN bit-timing for the device + * + * This functions should be called in the open function of the device + * driver to determine, check and set appropriate bit-timing parameters. + */ +int can_set_bittiming(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + /* Check if bit-timing parameters have been pre-defined */ + if (!priv->bittiming.tq && !priv->bittiming.bitrate) { + dev_err(ND2D(dev), "bit-timing not yet defined\n"); + return -EINVAL; + } + + /* Check if the CAN device has bit-timing parameters */ + if (priv->bittiming_const) { + + /* Check if bit-timing parameters have already been set */ + if (priv->bittiming.tq && priv->bittiming.bitrate) + return 0; + + /* Non-expert mode? Check if the bitrate has been pre-defined */ + if (!priv->bittiming.tq) + /* Determine bit-timing parameters */ + err = can_calc_bittiming(dev); + else + /* Check bit-timing params and calculate proper brp */ + err = can_fixup_bittiming(dev); + if (err) + return err; + } + + if (priv->do_set_bittiming) { + /* Finally, set the bit-timing registers */ + err = priv->do_set_bittiming(dev); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(can_set_bittiming); + +static void can_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = sizeof(struct can_frame); + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; + + /* New-style flags. */ + dev->flags = IFF_NOARP; + dev->features = NETIF_F_NO_CSUM; +} + +/* + * Allocate and setup space for the CAN network device + */ +struct net_device *alloc_candev(int sizeof_priv) +{ + struct net_device *dev; + struct can_priv *priv; + + dev = alloc_netdev(sizeof_priv, "can%d", can_setup); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + priv->state = CAN_STATE_STOPPED; + spin_lock_init(&priv->irq_lock); + + init_timer(&priv->timer); + priv->timer.expires = 0; + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_candev); + +/* + * Allocate space of the CAN network device + */ +void free_candev(struct net_device *dev) +{ + free_netdev(dev); +} +EXPORT_SYMBOL_GPL(free_candev); + +/* + * Register the CAN network device + */ +int register_candev(struct net_device *dev) +{ + int err; + + err = register_netdev(dev); + if (err) + return err; + + can_create_sysfs(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(register_candev); + +/* + * Unregister the CAN network device + */ +void unregister_candev(struct net_device *dev) +{ + can_remove_sysfs(dev); + unregister_netdev(dev); +} +EXPORT_SYMBOL_GPL(unregister_candev); + +/* + * Local echo of CAN messages + * + * CAN network devices *should* support a local echo functionality + * (see Documentation/networking/can.txt). To test the handling of CAN + * interfaces that do not support the local echo both driver types are + * implemented. In the case that the driver does not support the echo + * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core + * to perform the echo as a fallback solution. + */ + +static void can_flush_echo_skb(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + int i; + + for (i = 0; i < CAN_ECHO_SKB_MAX; i++) { + if (priv->echo_skb[i]) { + kfree_skb(priv->echo_skb[i]); + priv->echo_skb[i] = NULL; + stats->tx_dropped++; + stats->tx_aborted_errors++; + } + } +} + +/* + * Put the skb on the stack to be looped backed locally lateron + * + * The function is typically called in the start_xmit function + * of the device driver. + */ +void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + /* set flag whether this packet has to be looped back */ + if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) { + kfree_skb(skb); + return; + } + + if (!priv->echo_skb[idx]) { + struct sock *srcsk = skb->sk; + + if (atomic_read(&skb->users) != 1) { + struct sk_buff *old_skb = skb; + + skb = skb_clone(old_skb, GFP_ATOMIC); + kfree_skb(old_skb); + if (!skb) + return; + } else + skb_orphan(skb); + + skb->sk = srcsk; + + /* make settings for echo to reduce code in irq context */ + skb->protocol = htons(ETH_P_CAN); + skb->pkt_type = PACKET_BROADCAST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->dev = dev; + + /* save this skb for tx interrupt echo handling */ + priv->echo_skb[idx] = skb; + } else { + /* locking problem with netif_stop_queue() ?? */ + printk(KERN_ERR "%s: %s: BUG! echo_skb is occupied!\n", + dev->name, __func__); + kfree_skb(skb); + } +} +EXPORT_SYMBOL_GPL(can_put_echo_skb); + +/* + * Get the skb from the stack and loop it back locally + * + * The function is typically called when the TX done interrupt + * is handled in the device driver. + */ +void can_get_echo_skb(struct net_device *dev, int idx) +{ + struct can_priv *priv = netdev_priv(dev); + + if ((dev->flags & IFF_ECHO) && priv->echo_skb[idx]) { + netif_rx(priv->echo_skb[idx]); + priv->echo_skb[idx] = NULL; + } +} +EXPORT_SYMBOL_GPL(can_get_echo_skb); + +/* + * CAN device restart for bus-off recovery + */ +int can_restart_now(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *cf; + int err; + + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); + + /* Cancel restart in progress */ + if (priv->timer.expires) { + del_timer(&priv->timer); + priv->timer.expires = 0; /* mark inactive timer */ + } + + can_flush_echo_skb(dev); + + err = priv->do_set_mode(dev, CAN_MODE_START); + if (err) + return err; + + netif_carrier_on(dev); + + dev_dbg(ND2D(dev), "restarted\n"); + priv->can_stats.restarts++; + + /* send restart message upstream */ + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (skb == NULL) + return -ENOMEM; + skb->dev = dev; + skb->protocol = htons(ETH_P_CAN); + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; + cf->can_dlc = CAN_ERR_DLC; + + netif_rx(skb); + + dev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +static void can_restart_after(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct can_priv *priv = netdev_priv(dev); + + priv->timer.expires = 0; /* mark inactive timer */ + can_restart_now(dev); +} + +/* + * CAN bus-off + * + * This functions should be called when the device goes bus-off to + * tell the netif layer that no more packets can be sent or received. + * If enabled, a timer is started to trigger bus-off recovery. + */ +void can_bus_off(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + dev_dbg(ND2D(dev), "bus-off\n"); + + netif_carrier_off(dev); + + if (priv->restart_ms > 0 && !priv->timer.expires) { + + priv->timer.function = can_restart_after; + priv->timer.data = (unsigned long)dev; + priv->timer.expires = + jiffies + (priv->restart_ms * HZ) / 1000; + add_timer(&priv->timer); + } +} +EXPORT_SYMBOL_GPL(can_bus_off); + +/* + * Cleanup function before the device gets closed. + * + * This functions should be called in the close function of the device + * driver. + */ +void can_close_cleanup(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + if (priv->timer.expires) { + del_timer(&priv->timer); + priv->timer.expires = 0; + } + + can_flush_echo_skb(dev); +} +EXPORT_SYMBOL_GPL(can_close_cleanup); + +static __init int can_dev_init(void) +{ + printk(KERN_INFO MOD_DESC "\n"); + + return 0; +} +module_init(can_dev_init); + +static __exit void can_dev_exit(void) +{ +} +module_exit(can_dev_exit); diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c new file mode 100644 index 0000000..746f641 --- /dev/null +++ b/drivers/net/can/sysfs.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2007-2008 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <net/sock.h> +#include <linux/rtnetlink.h> + +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "sysfs.h" + +#ifdef CONFIG_SYSFS + +/* + * SYSFS access functions and attributes. Use same locking as + * net/core/net-sysfs.c does. + */ +static inline int dev_isalive(const struct net_device *dev) +{ + return dev->reg_state <= NETREG_REGISTERED; +} + +/* use same locking rules as GIF* ioctl's */ +static ssize_t can_dev_show(struct device *d, + struct device_attribute *attr, char *buf, + ssize_t (*fmt)(struct net_device *, char *)) +{ + struct net_device *dev = to_net_dev(d); + ssize_t ret = -EINVAL; + + read_lock(&dev_base_lock); + if (dev_isalive(dev)) + ret = (*fmt)(dev, buf); + read_unlock(&dev_base_lock); + + return ret; +} + +/* generate a show function for simple field */ +#define CAN_DEV_SHOW(field, fmt_string) \ +static ssize_t fmt_can_##field(struct net_device *dev, char *buf) \ +{ \ + struct can_priv *priv = netdev_priv(dev); \ + return sprintf(buf, fmt_string, priv->field); \ +} \ +static ssize_t show_can_##field(struct device *d, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return can_dev_show(d, attr, buf, fmt_can_##field); \ +} + +/* use same locking and permission rules as SIF* ioctl's */ +static ssize_t can_dev_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t len, + int (*set)(struct net_device *, unsigned long)) +{ + struct net_device *dev = to_net_dev(d); + unsigned long new; + int ret = -EINVAL; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + ret = strict_strtoul(buf, 0, &new); + if (ret) + goto out; + + rtnl_lock(); + if (dev_isalive(dev)) { + ret = (*set)(dev, new); + if (!ret) + ret = len; + } + rtnl_unlock(); +out: + return ret; +} + +#define CAN_CREATE_FILE(_dev, _name) \ + if (device_create_file(&_dev->dev, &dev_attr_##_name)) \ + dev_err(ND2D(_dev), \ + "Couldn't create device file for ##_name\n") + +#define CAN_REMOVE_FILE(_dev, _name) \ + device_remove_file(&_dev->dev, &dev_attr_##_name) \ + +CAN_DEV_SHOW(ctrlmode, "0x%x\n"); + +static int change_can_ctrlmode(struct net_device *dev, unsigned long ctrlmode) +{ + struct can_priv *priv = netdev_priv(dev); + int err = 0; + + if (priv->state != CAN_STATE_STOPPED) + return -EBUSY; + + if (priv->do_set_ctrlmode) + err = priv->do_set_ctrlmode(dev, ctrlmode); + + if (!err) + priv->ctrlmode = ctrlmode; + + return err; +} + +static ssize_t store_can_ctrlmode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return can_dev_store(dev, attr, buf, len, change_can_ctrlmode); +} + +static DEVICE_ATTR(can_ctrlmode, S_IRUGO | S_IWUSR, + show_can_ctrlmode, store_can_ctrlmode); + +static const char *can_state_names[] = { + "active", "bus-warn", "bus-pass" , "bus-off", + "stopped", "sleeping", "unkown" +}; + +static ssize_t printf_can_state(struct net_device *dev, char *buf) +{ + struct can_priv *priv = netdev_priv(dev); + enum can_state state; + int err = 0; + + if (priv->do_get_state) { + err = priv->do_get_state(dev, &state); + if (err) + goto out; + priv->state = state; + } else + state = priv->state; + + if (state >= ARRAY_SIZE(can_state_names)) + state = ARRAY_SIZE(can_state_names) - 1; + err = sprintf(buf, "%s\n", can_state_names[state]); +out: + return err; +} + +static ssize_t show_can_state(struct device *d, + struct device_attribute *attr, char *buf) +{ + return can_dev_show(d, attr, buf, printf_can_state); +} + +static DEVICE_ATTR(can_state, S_IRUGO, show_can_state, NULL); + +CAN_DEV_SHOW(restart_ms, "%d\n"); + +static int change_can_restart_ms(struct net_device *dev, unsigned long ms) +{ + struct can_priv *priv = netdev_priv(dev); + + if (priv->restart_ms < 0) + return -EOPNOTSUPP; + priv->restart_ms = ms; + return 0; +} + +static ssize_t store_can_restart_ms(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return can_dev_store(dev, attr, buf, len, change_can_restart_ms); +} + +static DEVICE_ATTR(can_restart_ms, S_IRUGO | S_IWUSR, + show_can_restart_ms, store_can_restart_ms); + +static ssize_t printf_can_echo(struct net_device *dev, char *buf) +{ + return sprintf(buf, "%d\n", dev->flags & IFF_ECHO ? 1 : 0); +} + +static ssize_t show_can_echo(struct device *d, + struct device_attribute *attr, char *buf) +{ + return can_dev_show(d, attr, buf, printf_can_echo); +} + +static int change_can_echo(struct net_device *dev, unsigned long on) +{ + if (on) + dev->flags |= IFF_ECHO; + else + dev->flags &= ~IFF_ECHO; + return 0; +} + +static ssize_t store_can_echo(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return can_dev_store(dev, attr, buf, len, change_can_echo); +} + +static DEVICE_ATTR(can_echo, S_IRUGO | S_IWUSR, show_can_echo, store_can_echo); + +static int change_can_restart(struct net_device *dev, unsigned long on) +{ + return can_restart_now(dev); +} + +static ssize_t store_can_restart(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return can_dev_store(dev, attr, buf, len, change_can_restart); +} + +static DEVICE_ATTR(can_restart, S_IWUSR, NULL, store_can_restart); + +/* Show a given attribute if the CAN bittiming group */ +static ssize_t can_btc_show(const struct device *d, + struct device_attribute *attr, char *buf, + unsigned long offset) +{ + struct net_device *dev = to_net_dev(d); + struct can_priv *priv = netdev_priv(dev); + struct can_bittiming_const *btc = priv->bittiming_const; + ssize_t ret = -EINVAL; + + WARN_ON(offset >= sizeof(struct can_bittiming_const) || + offset % sizeof(u32) != 0); + + read_lock(&dev_base_lock); + if (dev_isalive(dev) && btc) + ret = sprintf(buf, "%d\n", + *(u32 *)(((u8 *)btc) + offset)); + + read_unlock(&dev_base_lock); + return ret; +} + +/* Generate a read-only bittiming const attribute */ +#define CAN_BT_CONST_ENTRY(name) \ +static ssize_t show_##name(struct device *d, \ + struct device_attribute *attr, char *buf) \ +{ \ + return can_btc_show(d, attr, buf, \ + offsetof(struct can_bittiming_const, name));\ +} \ +static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL) + +CAN_BT_CONST_ENTRY(tseg1_min); +CAN_BT_CONST_ENTRY(tseg1_max); +CAN_BT_CONST_ENTRY(tseg2_min); +CAN_BT_CONST_ENTRY(tseg2_max); +CAN_BT_CONST_ENTRY(sjw_max); +CAN_BT_CONST_ENTRY(brp_min); +CAN_BT_CONST_ENTRY(brp_max); +CAN_BT_CONST_ENTRY(brp_inc); + +static ssize_t can_bt_show(const struct device *d, + struct device_attribute *attr, char *buf, + unsigned long offset) +{ + struct net_device *dev = to_net_dev(d); + struct can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->bittiming; + ssize_t ret = -EINVAL; + u32 *ptr, val; + + WARN_ON(offset >= sizeof(struct can_bittiming) || + offset % sizeof(u32) != 0); + + read_lock(&dev_base_lock); + if (dev_isalive(dev)) { + ptr = (u32 *)(((u8 *)bt) + offset); + if (ptr == &bt->sample_point && + priv->state != CAN_STATE_STOPPED) + val = can_sample_point(bt); + else + val = *ptr; + ret = sprintf(buf, "%d\n", val); + } + read_unlock(&dev_base_lock); + return ret; +} + +static ssize_t can_bt_store(const struct device *d, + struct device_attribute *attr, + const char *buf, size_t count, + unsigned long offset) +{ + struct net_device *dev = to_net_dev(d); + struct can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->bittiming; + unsigned long new; + ssize_t ret = -EINVAL; + u32 *ptr; + + if (priv->state != CAN_STATE_STOPPED) + return -EBUSY; + + WARN_ON(offset >= sizeof(struct can_bittiming) || + offset % sizeof(u32) != 0); + + ret = strict_strtoul(buf, 0, &new); + if (ret) + goto out; + + ptr = (u32 *)(((u8 *)bt) + offset); + rtnl_lock(); + if (dev_isalive(dev)) { + *ptr = (u32)new; + + if ((ptr == &bt->bitrate) || (ptr == &bt->sample_point)) { + bt->tq = 0; + bt->brp = 0; + bt->sjw = 0; + bt->prop_seg = 0; + bt->phase_seg1 = 0; + bt->phase_seg2 = 0; + } else { + bt->bitrate = 0; + bt->sample_point = 0; + } + ret = count; + } + rtnl_unlock(); +out: + return ret; +} + +#define CAN_BT_ENTRY_RO(name) \ +static ssize_t show_##name(struct device *d, \ + struct device_attribute *attr, char *buf) \ +{ \ + return can_bt_show(d, attr, buf, \ + offsetof(struct can_bittiming, name)); \ +} \ +static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL) + +CAN_BT_ENTRY_RO(clock); + +#define CAN_BT_ENTRY(name) \ +static ssize_t show_##name(struct device *d, \ + struct device_attribute *attr, char *buf) \ +{ \ + return can_bt_show(d, attr, buf, \ + offsetof(struct can_bittiming, name)); \ +} \ +static ssize_t store_##name(struct device *d, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return can_bt_store(d, attr, buf, count, \ + offsetof(struct can_bittiming, name)); \ +} \ +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name) + +CAN_BT_ENTRY(bitrate); +CAN_BT_ENTRY(sample_point); +CAN_BT_ENTRY(tq); +CAN_BT_ENTRY(prop_seg); +CAN_BT_ENTRY(phase_seg1); +CAN_BT_ENTRY(phase_seg2); +CAN_BT_ENTRY(sjw); + +static struct attribute *can_bittiming_attrs[] = { + &dev_attr_hw_tseg1_min.attr, + &dev_attr_hw_tseg1_max.attr, + &dev_attr_hw_tseg2_max.attr, + &dev_attr_hw_tseg2_min.attr, + &dev_attr_hw_sjw_max.attr, + &dev_attr_hw_brp_min.attr, + &dev_attr_hw_brp_max.attr, + &dev_attr_hw_brp_inc.attr, + &dev_attr_hw_clock.attr, + &dev_attr_bitrate.attr, + &dev_attr_sample_point.attr, + &dev_attr_tq.attr, + &dev_attr_prop_seg.attr, + &dev_attr_phase_seg1.attr, + &dev_attr_phase_seg2.attr, + &dev_attr_sjw.attr, + NULL +}; + +static struct attribute_group can_bittiming_group = { + .name = "can_bittiming", + .attrs = can_bittiming_attrs, +}; + +/* Show a given attribute in the CAN statistics group */ +static ssize_t can_stat_show(const struct device *d, + struct device_attribute *attr, char *buf, + unsigned long offset) +{ + struct net_device *dev = to_net_dev(d); + struct can_priv *priv = netdev_priv(dev); + struct can_device_stats *stats = &priv->can_stats; + ssize_t ret = -EINVAL; + + WARN_ON(offset >= sizeof(struct can_device_stats) || + offset % sizeof(unsigned long) != 0); + + read_lock(&dev_base_lock); + if (dev_isalive(dev)) + ret = sprintf(buf, "%ld\n", + *(unsigned long *)(((u8 *)stats) + offset)); + + read_unlock(&dev_base_lock); + return ret; +} + +/* Generate a read-only CAN statistics attribute */ +#define CAN_STAT_ENTRY(name) \ +static ssize_t show_##name(struct device *d, \ + struct device_attribute *attr, char *buf) \ +{ \ + return can_stat_show(d, attr, buf, \ + offsetof(struct can_device_stats, name)); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +CAN_STAT_ENTRY(error_warning); +CAN_STAT_ENTRY(error_passive); +CAN_STAT_ENTRY(bus_error); +CAN_STAT_ENTRY(arbitration_lost); +CAN_STAT_ENTRY(data_overrun); +CAN_STAT_ENTRY(wakeup); +CAN_STAT_ENTRY(restarts); + +static struct attribute *can_statistics_attrs[] = { + &dev_attr_error_warning.attr, + &dev_attr_error_passive.attr, + &dev_attr_bus_error.attr, + &dev_attr_arbitration_lost.attr, + &dev_attr_data_overrun.attr, + &dev_attr_wakeup.attr, + &dev_attr_restarts.attr, + NULL +}; + +static struct attribute_group can_statistics_group = { + .name = "can_statistics", + .attrs = can_statistics_attrs, +}; + +void can_create_sysfs(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + int err; + + CAN_CREATE_FILE(dev, can_ctrlmode); + CAN_CREATE_FILE(dev, can_echo); + CAN_CREATE_FILE(dev, can_restart); + CAN_CREATE_FILE(dev, can_state); + CAN_CREATE_FILE(dev, can_restart_ms); + + err = sysfs_create_group(&(dev->dev.kobj), + &can_statistics_group); + if (err) { + printk(KERN_EMERG + "couldn't create sysfs group for CAN statistics\n"); + } + + if (priv->bittiming_const) { + err = sysfs_create_group(&(dev->dev.kobj), + &can_bittiming_group); + if (err) { + printk(KERN_EMERG "couldn't create sysfs " + "group for CAN bittiming\n"); + } + } +} + +void can_remove_sysfs(struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + + CAN_REMOVE_FILE(dev, can_ctrlmode); + CAN_REMOVE_FILE(dev, can_echo); + CAN_REMOVE_FILE(dev, can_state); + CAN_REMOVE_FILE(dev, can_restart); + CAN_REMOVE_FILE(dev, can_restart_ms); + + sysfs_remove_group(&(dev->dev.kobj), &can_statistics_group); + if (priv->bittiming_const) + sysfs_remove_group(&(dev->dev.kobj), &can_bittiming_group); +} + +#endif /* CONFIG_SYSFS */ + + + diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h new file mode 100644 index 0000000..e21f2fa --- /dev/null +++ b/drivers/net/can/sysfs.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CAN_SYSFS_H +#define CAN_SYSFS_H + +void can_create_sysfs(struct net_device *dev); +void can_remove_sysfs(struct net_device *dev); + +#endif /* CAN_SYSFS_H */ diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h new file mode 100644 index 0000000..078ac03 --- /dev/null +++ b/include/linux/can/dev.h @@ -0,0 +1,136 @@ +/* + * linux/can/dev.h + * + * Definitions for the CAN network device driver interface + * + * 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/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 + */ +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 +#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 + */ +#define CAN_ECHO_SKB_MAX 4 + +struct can_priv { + struct can_device_stats can_stats; + + struct can_bittiming bittiming; + struct can_bittiming_const *bittiming_const; + + spinlock_t irq_lock; + + enum can_state state; + u32 ctrlmode; + + int restart_ms; + struct timer_list 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); +}; + +#define ND2D(_ndev) (_ndev->dev.parent) + + +struct net_device *alloc_candev(int sizeof_priv); +void free_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); + +int can_sample_point(struct can_bittiming *bt); + +#endif /* CAN_DEV_H */ -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 4/8] can: Driver for the SJA1000 CAN controller 2009-02-19 19:01 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 5/8] can: SJA1000 generic platform bus driver Wolfgang Grandegger 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet 2009-02-19 23:49 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Jonathan Corbet 2009-02-20 9:44 ` Patrick McHardy 2 siblings, 2 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger, Oliver Hartkopp This patch adds the generic Socket-CAN driver for the Philips SJA1000 full CAN controller. Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> --- drivers/net/can/Kconfig | 11 + drivers/net/can/Makefile | 2 + drivers/net/can/sja1000/Makefile | 7 + drivers/net/can/sja1000/sja1000.c | 681 +++++++++++++++++++++++++++++++++++++ drivers/net/can/sja1000/sja1000.h | 177 ++++++++++ 5 files changed, 878 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/Makefile create mode 100644 drivers/net/can/sja1000/sja1000.c create mode 100644 drivers/net/can/sja1000/sja1000.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d609895..78a412b 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -35,6 +35,17 @@ config CAN_CALC_BITTIMING files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". If unsure, say Y. +config CAN_SJA1000 + depends on CAN_DEV + tristate "Philips SJA1000" + ---help--- + The SJA1000 is one of the top CAN controllers out there. As it + has a multiplexed interface it fits directly to 8051 + microcontrollers or into the PC I/O port space. The SJA1000 + is a full CAN controller, with shadow registers for RX and TX. + It can send and receive any kinds of CAN frames (SFF/EFF/RTR) + with a single (simple) filter setup. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 557114b..728d83f 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -7,4 +7,6 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o sysfs.o +obj-$(CONFIG_CAN_SJA1000) += sja1000/ + ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile new file mode 100644 index 0000000..893ab2c --- /dev/null +++ b/drivers/net/can/sja1000/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the SJA1000 CAN controller drivers. +# + +obj-$(CONFIG_CAN_SJA1000) += sja1000.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c new file mode 100644 index 0000000..6fe516d --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.c @@ -0,0 +1,681 @@ +/* + * sja1000.c - Philips SJA1000 network device driver + * + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to <socketcan-users@lists.berlios.de> + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/delay.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/dev.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000" + +MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver"); + +static struct can_bittiming_const sja1000_bittiming_const = { + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int sja1000_probe_chip(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + if (dev->base_addr && (priv->read_reg(dev, 0) == 0xFF)) { + printk(KERN_INFO "%s: probing @0x%lX failed\n", + DRV_NAME, dev->base_addr); + return 0; + } + return 1; +} + +static int set_reset_mode(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(dev, REG_MOD); + int i; + + /* disable interrupts */ + priv->write_reg(dev, REG_IER, IRQ_OFF); + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if (status & MOD_RM) { + priv->can.state = CAN_STATE_STOPPED; + return 0; + } + + priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */ + status = priv->read_reg(dev, REG_MOD); + udelay(10); + } + + dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n"); + return 1; + +} + +static int set_normal_mode(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + unsigned char status = priv->read_reg(dev, REG_MOD); + int i; + + for (i = 0; i < 100; i++) { + /* check reset bit */ + if ((status & MOD_RM) == 0) { + priv->can.state = CAN_STATE_ACTIVE; + /* enable all interrupts */ + priv->write_reg(dev, REG_IER, IRQ_ALL); + + return 0; + } + + /* set chip to normal mode */ + priv->write_reg(dev, REG_MOD, 0x00); + status = priv->read_reg(dev, REG_MOD); + udelay(10); + } + + dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n"); + return 1; + +} + +static void sja1000_start(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + /* leave reset mode */ + if (priv->can.state != CAN_STATE_STOPPED) + set_reset_mode(dev); + + /* Clear error counters and error code capture */ + priv->write_reg(dev, REG_TXERR, 0x0); + priv->write_reg(dev, REG_RXERR, 0x0); + priv->read_reg(dev, REG_ECC); + + /* leave reset mode */ + set_normal_mode(dev); +} + +static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + switch (mode) { + case CAN_MODE_START: + if (!priv->open_time) + return -EINVAL; + + sja1000_start(dev); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int sja1000_get_state(struct net_device *dev, enum can_state *state) +{ + struct sja1000_priv *priv = netdev_priv(dev); + u8 status; + + /* FIXME: inspecting the status register to get the current state + * is not really necessary, because state changes are handled by + * in the ISR and the variable priv->can.state gets updated. The + * CAN devicde interface needs fixing! + */ + + spin_lock_irq(&priv->can.irq_lock); + + if (priv->can.state == CAN_STATE_STOPPED) { + *state = CAN_STATE_STOPPED; + } else { + status = priv->read_reg(dev, REG_SR); + if (status & SR_BS) + *state = CAN_STATE_BUS_OFF; + else if (status & SR_ES) { + if (priv->read_reg(dev, REG_TXERR) > 127 || + priv->read_reg(dev, REG_RXERR) > 127) + *state = CAN_STATE_BUS_PASSIVE; + else + *state = CAN_STATE_BUS_WARNING; + } else + *state = CAN_STATE_ACTIVE; + } + /* Check state */ + if (*state != priv->can.state) + dev_err(ND2D(dev), + "Oops, state mismatch: hard %d != soft %d\n", + *state, priv->can.state); + spin_unlock_irq(&priv->can.irq_lock); + return 0; +} + +static int sja1000_set_bittiming(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4) | + ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 7); + + dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + priv->write_reg(dev, REG_BTR0, btr0); + priv->write_reg(dev, REG_BTR1, btr1); + + return 0; +} + +/* + * initialize SJA1000 chip: + * - reset chip + * - set output mode + * - set baudrate + * - enable interrupts + * - start operating mode + */ +static void chipset_init(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + /* set clock divider and output control register */ + priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN); + + /* set acceptance filter (accept all) */ + priv->write_reg(dev, REG_ACCC0, 0x00); + priv->write_reg(dev, REG_ACCC1, 0x00); + priv->write_reg(dev, REG_ACCC2, 0x00); + priv->write_reg(dev, REG_ACCC3, 0x00); + + priv->write_reg(dev, REG_ACCM0, 0xFF); + priv->write_reg(dev, REG_ACCM1, 0xFF); + priv->write_reg(dev, REG_ACCM2, 0xFF); + priv->write_reg(dev, REG_ACCM3, 0xFF); + + priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL); +} + +/* + * transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can-id ] [flags] [len] [can data (up to 8 bytes] + */ +static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + uint8_t fi; + uint8_t dlc; + canid_t id; + uint8_t dreg; + int i; + + netif_stop_queue(dev); + + fi = dlc = cf->can_dlc; + id = cf->can_id; + + if (id & CAN_RTR_FLAG) + fi |= FI_RTR; + + if (id & CAN_EFF_FLAG) { + fi |= FI_FF; + dreg = EFF_BUF; + priv->write_reg(dev, REG_FI, fi); + priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); + priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8)); + priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5); + priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3); + } else { + dreg = SFF_BUF; + priv->write_reg(dev, REG_FI, fi); + priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3); + priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5); + } + + for (i = 0; i < dlc; i++) + priv->write_reg(dev, dreg++, cf->data[i]); + + stats->tx_bytes += dlc; + dev->trans_start = jiffies; + + can_put_echo_skb(skb, dev, 0); + + priv->write_reg(dev, REG_CMR, CMD_TR); + + return 0; +} + +static void sja1000_rx(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + uint8_t fi; + uint8_t dreg; + canid_t id; + uint8_t dlc; + int i; + + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (skb == NULL) + return; + skb->dev = dev; + skb->protocol = htons(ETH_P_CAN); + + fi = priv->read_reg(dev, REG_FI); + dlc = fi & 0x0F; + + if (fi & FI_FF) { + /* extended frame format (EFF) */ + dreg = EFF_BUF; + id = (priv->read_reg(dev, REG_ID1) << (5 + 16)) + | (priv->read_reg(dev, REG_ID2) << (5 + 8)) + | (priv->read_reg(dev, REG_ID3) << 5) + | (priv->read_reg(dev, REG_ID4) >> 3); + id |= CAN_EFF_FLAG; + } else { + /* standard frame format (SFF) */ + dreg = SFF_BUF; + id = (priv->read_reg(dev, REG_ID1) << 3) + | (priv->read_reg(dev, REG_ID2) >> 5); + } + + if (fi & FI_RTR) + id |= CAN_RTR_FLAG; + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + cf->can_id = id; + cf->can_dlc = dlc; + for (i = 0; i < dlc; i++) + cf->data[i] = priv->read_reg(dev, dreg++); + + while (i < 8) + cf->data[i++] = 0; + + /* release receive buffer */ + priv->write_reg(dev, REG_CMR, CMD_RRB); + + netif_rx(skb); + + dev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += dlc; +} + +static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state state = priv->can.state; + uint8_t ecc, alc; + + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (skb == NULL) + return -ENOMEM; + skb->dev = dev; + skb->protocol = htons(ETH_P_CAN); + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + cf->can_id = CAN_ERR_FLAG; + cf->can_dlc = CAN_ERR_DLC; + + if (isrc & IRQ_DOI) { + /* data overrun interrupt */ + dev_dbg(ND2D(dev), "data overrun interrupt\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + priv->can.can_stats.data_overrun++; + priv->write_reg(dev, REG_CMR, CMD_CDO); /* clear bit */ + } + + if (isrc & IRQ_EI) { + /* error warning interrupt */ + priv->can.can_stats.error_warning++; + dev_dbg(ND2D(dev), "error warning interrupt\n"); + + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } else if (status & SR_ES) { + state = CAN_STATE_BUS_WARNING; + } else + state = CAN_STATE_ACTIVE; + } + if (isrc & IRQ_BEI) { + /* bus error interrupt */ + priv->can.can_stats.bus_error++; + ecc = priv->read_reg(dev, REG_ECC); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & ECC_MASK) { + case ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & ECC_SEG; + break; + } + /* Error occured during transmission? */ + if ((ecc & ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + if (isrc & IRQ_EPI) { + /* error passive interrupt */ + dev_dbg(ND2D(dev), "error passive interrupt\n"); + priv->can.can_stats.error_passive++; + if (status & SR_ES) + state = CAN_STATE_BUS_PASSIVE; + else + state = CAN_STATE_ACTIVE; + } + if (isrc & IRQ_ALI) { + /* arbitration lost interrupt */ + dev_dbg(ND2D(dev), "arbitration lost interrupt\n"); + alc = priv->read_reg(dev, REG_ALC); + priv->can.can_stats.arbitration_lost++; + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = alc & 0x1f; + } + + if (state != priv->can.state && (state == CAN_STATE_BUS_WARNING || + state == CAN_STATE_BUS_PASSIVE)) { + uint8_t rxerr = priv->read_reg(dev, REG_RXERR); + uint8_t txerr = priv->read_reg(dev, REG_TXERR); + cf->can_id |= CAN_ERR_CRTL; + if (state == CAN_STATE_BUS_WARNING) + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + else + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + + priv->can.state = state; + + netif_rx(skb); + + dev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +irqreturn_t sja1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sja1000_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + uint8_t isrc, status; + int n = 0; + + /* Shared interrupts and IRQ off? */ + if (priv->read_reg(dev, REG_IER) == IRQ_OFF) + return IRQ_NONE; + + if (priv->pre_irq) + priv->pre_irq(dev); + + while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) { + n++; + status = priv->read_reg(dev, REG_SR); + + if (isrc & IRQ_WUI) { + /* wake-up interrupt */ + priv->can.can_stats.wakeup++; + } + if (isrc & IRQ_TI) { + /* transmission complete interrupt */ + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + } + if (isrc & IRQ_RI) { + /* receive interrupt */ + while (status & SR_RBS) { + sja1000_rx(dev); + status = priv->read_reg(dev, REG_SR); + } + } + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { + /* error interrupt */ + if (sja1000_err(dev, isrc, status)) + break; + } + } + + if (priv->post_irq) + priv->post_irq(dev); + + if (n >= SJA1000_MAX_IRQ) + dev_dbg(ND2D(dev), "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} +EXPORT_SYMBOL_GPL(sja1000_interrupt); + +static int sja1000_open(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + int err; + + /* set chip into reset mode */ + set_reset_mode(dev); + + /* determine and set bittime */ + err = can_set_bittiming(dev); + if (err) + return err; + + /* register interrupt handler, if not done by the device driver */ + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) { + err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED, + dev->name, (void *)dev); + if (err) + return -EAGAIN; + } + + /* init and start chi */ + sja1000_start(dev); + priv->open_time = jiffies; + + netif_start_queue(dev); + + return 0; +} + +static int sja1000_close(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + + set_reset_mode(dev); + netif_stop_queue(dev); + priv->open_time = 0; + can_close_cleanup(dev); + + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) + free_irq(dev->irq, (void *)dev); + + return 0; +} + +struct net_device *alloc_sja1000dev(int sizeof_priv) +{ + struct net_device *dev; + struct sja1000_priv *priv; + + dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + priv->dev = dev; + + if (sizeof_priv) + priv->priv = (void *)priv + sizeof(struct sja1000_priv); + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_sja1000dev); + +void free_sja1000dev(struct net_device *dev) +{ + free_candev(dev); +} +EXPORT_SYMBOL_GPL(free_sja1000dev); + +static const struct net_device_ops sja1000_netdev_ops = { + .ndo_open = sja1000_open, + .ndo_stop = sja1000_close, + .ndo_start_xmit = sja1000_start_xmit, +}; + +int register_sja1000dev(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + int err; + + if (!sja1000_probe_chip(dev)) + return -ENODEV; + + dev->flags |= IFF_ECHO; /* we support local echo */ + + dev->netdev_ops = &sja1000_netdev_ops; + + priv->can.bittiming_const = &sja1000_bittiming_const; + priv->can.do_set_bittiming = sja1000_set_bittiming; + priv->can.do_get_state = sja1000_get_state; + priv->can.do_set_mode = sja1000_set_mode; + priv->dev = dev; + + err = register_candev(dev); + if (err) { + printk(KERN_INFO + "%s: registering netdev failed\n", DRV_NAME); + free_netdev(dev); + return err; + } + + set_reset_mode(dev); + chipset_init(dev); + return 0; +} +EXPORT_SYMBOL_GPL(register_sja1000dev); + +void unregister_sja1000dev(struct net_device *dev) +{ + set_reset_mode(dev); + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_sja1000dev); + +static __init int sja1000_init(void) +{ + printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME); + + return 0; +} + +module_init(sja1000_init); + +static __exit void sja1000_exit(void) +{ + printk(KERN_INFO "%s: driver removed\n", DRV_NAME); +} + +module_exit(sja1000_exit); diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h new file mode 100644 index 0000000..60d4cd6 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000.h @@ -0,0 +1,177 @@ +/* + * sja1000.h - Philips SJA1000 network device driver + * + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to <socketcan-users@lists.berlios.de> + * + */ + +#ifndef SJA1000_DEV_H +#define SJA1000_DEV_H + +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> + +#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */ + +/* SJA1000 registers - manual section 6.4 (Pelican Mode) */ +#define REG_MOD 0x00 +#define REG_CMR 0x01 +#define REG_SR 0x02 +#define REG_IR 0x03 +#define REG_IER 0x04 +#define REG_ALC 0x0B +#define REG_ECC 0x0C +#define REG_EWL 0x0D +#define REG_RXERR 0x0E +#define REG_TXERR 0x0F +#define REG_ACCC0 0x10 +#define REG_ACCC1 0x11 +#define REG_ACCC2 0x12 +#define REG_ACCC3 0x13 +#define REG_ACCM0 0x14 +#define REG_ACCM1 0x15 +#define REG_ACCM2 0x16 +#define REG_ACCM3 0x17 +#define REG_RMC 0x1D +#define REG_RBSA 0x1E + +/* Common registers - manual section 6.5 */ +#define REG_BTR0 0x06 +#define REG_BTR1 0x07 +#define REG_OCR 0x08 +#define REG_CDR 0x1F + +#define REG_FI 0x10 +#define SFF_BUF 0x13 +#define EFF_BUF 0x15 + +#define FI_FF 0x80 +#define FI_RTR 0x40 + +#define REG_ID1 0x11 +#define REG_ID2 0x12 +#define REG_ID3 0x13 +#define REG_ID4 0x14 + +#define CAN_RAM 0x20 + +/* mode register */ +#define MOD_RM 0x01 +#define MOD_LOM 0x02 +#define MOD_STM 0x04 +#define MOD_AFM 0x08 +#define MOD_SM 0x10 + +/* commands */ +#define CMD_SRR 0x10 +#define CMD_CDO 0x08 +#define CMD_RRB 0x04 +#define CMD_AT 0x02 +#define CMD_TR 0x01 + +/* interrupt sources */ +#define IRQ_BEI 0x80 +#define IRQ_ALI 0x40 +#define IRQ_EPI 0x20 +#define IRQ_WUI 0x10 +#define IRQ_DOI 0x08 +#define IRQ_EI 0x04 +#define IRQ_TI 0x02 +#define IRQ_RI 0x01 +#define IRQ_ALL 0xFF +#define IRQ_OFF 0x00 + +/* status register content */ +#define SR_BS 0x80 +#define SR_ES 0x40 +#define SR_TS 0x20 +#define SR_RS 0x10 +#define SR_TCS 0x08 +#define SR_TBS 0x04 +#define SR_DOS 0x02 +#define SR_RBS 0x01 + +#define SR_CRIT (SR_BS|SR_ES) + +/* ECC register */ +#define ECC_SEG 0x1F +#define ECC_DIR 0x20 +#define ECC_ERR 6 +#define ECC_BIT 0x00 +#define ECC_FORM 0x40 +#define ECC_STUFF 0x80 +#define ECC_MASK 0xc0 + +/* + * Flags for sja1000priv.flags + */ +#define SJA1000_CUSTOM_IRQ_HANDLER 0x1 + +/* + * SJA1000 private data structure + */ +struct sja1000_priv { + struct can_priv can; /* must be the first member! */ + long open_time; + struct sk_buff *echo_skb; + + u8 (*read_reg) (struct net_device *dev, int reg); + void (*write_reg) (struct net_device *dev, int reg, u8 val); + void (*pre_irq) (struct net_device *dev); + void (*post_irq) (struct net_device *dev); + + void *priv; /* for board-specific data */ + struct net_device *dev; + + u8 ocr; + u8 cdr; + u32 flags; +}; + +struct net_device *alloc_sja1000dev(int sizeof_priv); +void free_sja1000dev(struct net_device *dev); +int register_sja1000dev(struct net_device *dev); +void unregister_sja1000dev(struct net_device *dev); + +irqreturn_t sja1000_interrupt(int irq, void *dev_id); + +#endif /* SJA1000_DEV_H */ -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 5/8] can: SJA1000 generic platform bus driver 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 6/8] can: SJA1000 driver for EMS PCI cards Wolfgang Grandegger 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet 1 sibling, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev Cc: linux-kernel, Wolfgang Grandegger, Sascha Hauer, Marc Kleine-Budde, Oliver Hartkopp This driver adds support for the SJA1000 chips connected to the "platform bus", which can be found on various embedded systems. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> --- drivers/net/can/Kconfig | 10 ++ drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/sja1000_platform.c | 169 ++++++++++++++++++++++++++++ include/linux/can/platform/sja1000.h | 32 +++++ 4 files changed, 212 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/sja1000_platform.c create mode 100644 include/linux/can/platform/sja1000.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 78a412b..d6dee40 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -46,6 +46,16 @@ config CAN_SJA1000 It can send and receive any kinds of CAN frames (SFF/EFF/RTR) with a single (simple) filter setup. +config CAN_SJA1000_PLATFORM + depends on CAN_SJA1000 + tristate "Generic Platform Bus based SJA1000 driver" + ---help--- + This driver adds support for the SJA1000 chips connected to + the "platform bus" (Linux abstraction for directly to the + processor attached devices). Which can be found on various + boards from Phytec (http://www.phytec.de) like the PCM027, + PCM038. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 893ab2c..5115cc9 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_CAN_SJA1000) += sja1000.o +obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c new file mode 100644 index 0000000..a6c5d76 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005 Sascha Hauer, Pengutronix + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_platform" + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_LICENSE("GPL v2"); + +static u8 sp_read_reg(struct net_device *dev, int reg) +{ + return ioread8((void __iomem *)(dev->base_addr + reg)); +} + +static void sp_write_reg(struct net_device *dev, int reg, u8 val) +{ + iowrite8(val, (void __iomem *)(dev->base_addr + reg)); +} + +static int sp_probe(struct platform_device *pdev) +{ + int err, irq; + void __iomem *addr; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res_mem, *res_irq; + struct sja1000_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -ENODEV; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_mem || !res_irq) { + err = -ENODEV; + goto exit; + } + + if (!request_mem_region(res_mem->start, + res_mem->end - res_mem->start + 1, + DRV_NAME)) { + err = -EBUSY; + goto exit; + } + + addr = ioremap_nocache(res_mem->start, + res_mem->end - res_mem->start + 1); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + irq = res_irq->start; + if (res_irq->flags & IRQF_TRIGGER_MASK) + set_irq_type(irq, res_irq->flags & IRQF_TRIGGER_MASK); + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + priv = netdev_priv(dev); + + priv->read_reg = sp_read_reg; + priv->write_reg = sp_write_reg; + priv->can.bittiming.clock = pdata->clock; + priv->ocr = pdata->ocr; + priv->cdr = pdata->cdr; + + dev->irq = irq; + dev->base_addr = (unsigned long)addr; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + + dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n", + DRV_NAME, dev->base_addr, dev->irq); + return 0; + + exit_free: + free_sja1000dev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res_mem->start, res_mem->end - res_mem->start + 1); + exit: + return err; +} + +static int sp_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct resource *res; + + unregister_sja1000dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + if (dev->base_addr) + iounmap((void __iomem *)dev->base_addr); + + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sp_driver = { + .probe = sp_probe, + .remove = sp_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sp_init(void) +{ + return platform_driver_register(&sp_driver); +} + +static void __exit sp_exit(void) +{ + platform_driver_unregister(&sp_driver); +} + +module_init(sp_init); +module_exit(sp_exit); diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h new file mode 100644 index 0000000..37966e6 --- /dev/null +++ b/include/linux/can/platform/sja1000.h @@ -0,0 +1,32 @@ +#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_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 + +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_ */ -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 6/8] can: SJA1000 driver for EMS PCI cards 2009-02-19 19:01 ` [PATCH 5/8] can: SJA1000 generic platform bus driver Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 7/8] can: SJA1000 driver for Kvaser " Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger, Sebastian Haas, Markus Plessing The patch adds support for the one or two channel CPC-PCI and CPC-PCIe cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). Signed-off-by: Sebastian Haas <haas@ems-wuensche.com> Signed-off-by: Markus Plessing <plessing@ems-wuensche.com> Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> --- drivers/net/can/Kconfig | 7 + drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/ems_pci.c | 328 +++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/ems_pci.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d6dee40..4e6887e 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -56,6 +56,13 @@ config CAN_SJA1000_PLATFORM boards from Phytec (http://www.phytec.de) like the PCM027, PCM038. +config CAN_EMS_PCI + tristate "EMS CPC-PCI and CPC-PCIe Card" + depends on PCI && CAN_SJA1000 + ---help--- + This driver is for the one or two channel CPC-PCI and CPC-PCIe + cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 5115cc9..7772dd2 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000.o obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o +obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c new file mode 100644 index 0000000..6076375 --- /dev/null +++ b/drivers/net/can/sja1000/ems_pci.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com> + * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "ems_pci" + +MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>"); +MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe CAN cards"); +MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe CAN card"); +MODULE_LICENSE("GPL v2"); + +#define EMS_PCI_MAX_CHAN 2 + +struct ems_pci_card { + int channels; + + struct pci_dev *pci_dev; + struct net_device *net_dev[EMS_PCI_MAX_CHAN]; + + void __iomem *conf_addr; + void __iomem *base_addr; +}; + +#define EMS_PCI_CAN_CLOCK (16000000 / 2) + +/* + * Register definitions and descriptions are from LinCAN 0.3.3. + * + * PSB4610 PITA-2 bridge control registers + */ +#define PITA2_ICR 0x00 /* Interrupt Control Register */ +#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */ +#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */ + +#define PITA2_MISC 0x1c /* Miscellaneous Register */ +#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */ + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode , push-pull and the correct polarity. + */ +#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) +#define EMS_PCI_MEM_SIZE 4096 /* Size of the remapped io-memory */ +#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */ +#define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */ + +#define EMS_PCI_PORT_BYTES 0x4 /* Each register occupies 4 bytes */ + +#define EMS_PCI_VENDOR_ID 0x110a /* PCI device and vendor ID */ +#define EMS_PCI_DEVICE_ID 0x2104 + +static struct pci_device_id ems_pci_tbl[] = { + {EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ems_pci_tbl); + +/* + * Helper to read internal registers from card logic (not CAN) + */ +static u8 ems_pci_readb(struct ems_pci_card *card, unsigned int port) +{ + return readb((void __iomem *)card->base_addr + + (port * EMS_PCI_PORT_BYTES)); +} + +static u8 ems_pci_read_reg(struct net_device *dev, int port) +{ + return readb((void __iomem *)dev->base_addr + + (port * EMS_PCI_PORT_BYTES)); +} + +static void ems_pci_write_reg(struct net_device *dev, int port, u8 val) +{ + writeb(val, (void __iomem *)dev->base_addr + + (port * EMS_PCI_PORT_BYTES)); +} + +static void ems_pci_post_irq(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct ems_pci_card *card = (struct ems_pci_card *)priv->priv; + + /* reset int flag of pita */ + writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, card->conf_addr + + PITA2_ICR); +} + +/* + * Check if a CAN controller is present at the specified location + * by trying to set 'em into the PeliCAN mode + */ +static inline int ems_pci_check_chan(struct net_device *dev) +{ + unsigned char res; + + /* Make sure SJA1000 is in reset mode */ + ems_pci_write_reg(dev, REG_MOD, 1); + + ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + res = ems_pci_read_reg(dev, REG_CDR); + + if (res == CDR_PELICAN) + return 1; + + return 0; +} + +static void ems_pci_del_card(struct pci_dev *pdev) +{ + struct ems_pci_card *card = pci_get_drvdata(pdev); + struct net_device *dev; + int i = 0; + + for (i = 0; i < card->channels; i++) { + dev = card->net_dev[i]; + + if (!dev) + continue; + + dev_info(&pdev->dev, "Removing %s.\n", dev->name); + unregister_sja1000dev(dev); + free_sja1000dev(dev); + } + + if (card->base_addr != NULL) + pci_iounmap(card->pci_dev, card->base_addr); + + if (card->conf_addr != NULL) + pci_iounmap(card->pci_dev, card->conf_addr); + + kfree(card); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static void ems_pci_card_reset(struct ems_pci_card *card) +{ + /* Request board reset */ + writeb(0, card->base_addr); +} + +/* + * Probe PCI device for EMS CAN signature and register each available + * CAN channel to SJA1000 Socket-CAN subsystem. + */ +static int __devinit ems_pci_add_card(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct sja1000_priv *priv; + struct net_device *dev; + struct ems_pci_card *card; + int err, i; + + /* Enabling PCI device */ + if (pci_enable_device(pdev) < 0) { + dev_err(&pdev->dev, "Enabling PCI device failed\n"); + return -ENODEV; + } + + /* Allocating card structures to hold addresses, ... */ + card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL); + if (card == NULL) { + dev_err(&pdev->dev, "Unable to allocate memory\n"); + pci_disable_device(pdev); + return -ENOMEM; + } + + pci_set_drvdata(pdev, card); + + card->pci_dev = pdev; + + card->channels = 0; + + /* Remap PITA configuration space, and controller memory area */ + card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE); + if (card->conf_addr == NULL) { + err = -ENOMEM; + + goto failure_cleanup; + } + + card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE); + if (card->base_addr == NULL) { + err = -ENOMEM; + + goto failure_cleanup; + } + + /* Configure PITA-2 parallel interface (enable MUX) */ + writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC); + + /* Check for unique EMS CAN signature */ + if (ems_pci_readb(card, 0) != 0x55 || + ems_pci_readb(card, 1) != 0xAA || + ems_pci_readb(card, 2) != 0x01 || + ems_pci_readb(card, 3) != 0xCB || + ems_pci_readb(card, 4) != 0x11) { + dev_err(&pdev->dev, "Not EMS Dr. Thomas Wuensche interface\n"); + + err = -ENODEV; + goto failure_cleanup; + } + + ems_pci_card_reset(card); + + /* Detect available channels */ + for (i = 0; i < EMS_PCI_MAX_CHAN; i++) { + dev = alloc_sja1000dev(0); + if (dev == NULL) { + err = -ENOMEM; + goto failure_cleanup; + } + + card->net_dev[i] = dev; + priv = netdev_priv(dev); + priv->priv = card; + + dev->irq = pdev->irq; + dev->base_addr = (unsigned long)(card->base_addr + + EMS_PCI_CAN_BASE_OFFSET + + (i * EMS_PCI_CAN_CTRL_SIZE)); + + /* Check if channel is present */ + if (ems_pci_check_chan(dev)) { + priv->read_reg = ems_pci_read_reg; + priv->write_reg = ems_pci_write_reg; + priv->post_irq = ems_pci_post_irq; + priv->can.bittiming.clock = EMS_PCI_CAN_CLOCK; + priv->ocr = EMS_PCI_OCR; + priv->cdr = EMS_PCI_CDR; + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Enable interrupts from card */ + writel(PITA2_ICR_INT0_EN, card->conf_addr + PITA2_ICR); + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "Registering device failed " + "(err=%d)\n", err); + free_sja1000dev(dev); + goto failure_cleanup; + } + + card->channels++; + + dev_info(&pdev->dev, "Channel #%d at %#lX, irq %d\n", + i + 1, dev->base_addr, + dev->irq); + } else { + free_sja1000dev(dev); + } + } + + return 0; + +failure_cleanup: + dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err); + + ems_pci_del_card(pdev); + + return err; +} + +static struct pci_driver ems_pci_driver = { + .name = DRV_NAME, + .id_table = ems_pci_tbl, + .probe = ems_pci_add_card, + .remove = ems_pci_del_card, +}; + +static int __init ems_pci_init(void) +{ + return pci_register_driver(&ems_pci_driver); +} + +static void __exit ems_pci_exit(void) +{ + pci_unregister_driver(&ems_pci_driver); +} + +module_init(ems_pci_init); +module_exit(ems_pci_exit); + -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 7/8] can: SJA1000 driver for Kvaser PCI cards 2009-02-19 19:01 ` [PATCH 6/8] can: SJA1000 driver for EMS PCI cards Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 8/8] can: Driver for the Freescale MSCAN controller Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger, Per Dalen The patch adds support for the PCI cards: PCIcan and PCIcanx (1, 2 or 4 channel) from Kvaser (http://www.kvaser.com). Signed-off-by: Per Dalen <per.dalen@cnw.se> Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> --- drivers/net/can/Kconfig | 7 + drivers/net/can/sja1000/Makefile | 1 + drivers/net/can/sja1000/kvaser_pci.c | 415 ++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/sja1000/kvaser_pci.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 4e6887e..b1c4ae5 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -63,6 +63,13 @@ config CAN_EMS_PCI This driver is for the one or two channel CPC-PCI and CPC-PCIe cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). +config CAN_KVASER_PCI + tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" + depends on PCI && CAN_SJA1000 + ---help--- + This driver is for the the PCIcanx and PCIcan cards (1, 2 or + 4 channel) from Kvaser (http://www.kvaser.com). + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 7772dd2..d6c631f 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000.o obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o +obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c new file mode 100644 index 0000000..0b01ea3 --- /dev/null +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se> + * + * Parts of this software are based on (derived) the following: + * + * - Kvaser linux driver, version 4.72 BETA + * Copyright (C) 2002-2007 KVASER AB + * + * - Lincan driver, version 0.3.3, OCERA project + * Copyright (C) 2004 Pavel Pisa + * Copyright (C) 2001 Arnaud Westenberg + * + * - Socketcan SJA1000 drivers + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "kvaser_pci" + +MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>"); +MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards"); +MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card"); +MODULE_LICENSE("GPL v2"); + +#define MAX_NO_OF_CHANNELS 4 /* max no of channels on + a single card */ + +struct kvaser_pci { + int channel; + struct pci_dev *pci_dev; + struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1]; + void __iomem *conf_addr; + void __iomem *res_addr; + int no_channels; + u8 xilinx_ver; +}; + +#define KVASER_PCI_CAN_CLOCK (16000000 / 2) + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode , push-pull and the correct polarity. + */ +#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 0 + * (meaning divide-by-2), the Pelican bit, and the clock-off bit + * (you will have no need for CLKOUT anyway). + */ +#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +/* + * These register values are valid for revision 14 of the Xilinx logic. + */ +#define XILINX_VERINT 7 /* Lower nibble simulate interrupts, + high nibble version number. */ + +#define XILINX_PRESUMED_VERSION 14 + +/* + * Important S5920 registers + */ +#define S5920_INTCSR 0x38 +#define S5920_PTCR 0x60 +#define INTCSR_ADDON_INTENABLE_M 0x2000 + + +#define KVASER_PCI_PORT_BYTES 0x20 + +#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */ +#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */ +#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */ + +#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */ +#define KVASER_PCI_DEVICE_ID1 0x8406 + +#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */ +#define KVASER_PCI_DEVICE_ID2 0x0008 + +static struct pci_device_id kvaser_pci_tbl[] = { + {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,}, + {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,}, + { 0,} +}; + +MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl); + +static u8 kvaser_pci_read_reg(struct net_device *dev, int port) +{ + return ioread8((void __iomem *)(dev->base_addr + port)); +} + +static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val) +{ + iowrite8(val, (void __iomem *)(dev->base_addr + port)); +} + +static void kvaser_pci_disable_irq(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct kvaser_pci *board = priv->priv; + u32 tmp; + + /* Disable interrupts from card */ + tmp = ioread32(board->conf_addr + S5920_INTCSR); + tmp &= ~INTCSR_ADDON_INTENABLE_M; + iowrite32(tmp, board->conf_addr + S5920_INTCSR); +} + +static void kvaser_pci_enable_irq(struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct kvaser_pci *board = priv->priv; + u32 tmp; + + /* Enable interrupts from card */ + tmp = ioread32(board->conf_addr + S5920_INTCSR); + tmp |= INTCSR_ADDON_INTENABLE_M; + iowrite32(tmp, board->conf_addr + S5920_INTCSR); +} + +static int number_of_sja1000_chip(void __iomem *base_addr) +{ + u8 status; + int i; + + for (i = 0; i < MAX_NO_OF_CHANNELS; i++) { + /* reset chip */ + iowrite8(MOD_RM, base_addr + + (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + status = ioread8(base_addr + + (i * KVASER_PCI_PORT_BYTES) + REG_MOD); + udelay(10); + /* check reset bit */ + if (!(status & MOD_RM)) + break; + } + + return i; +} + +static void kvaser_pci_del_chan(struct net_device *dev) +{ + struct sja1000_priv *priv; + struct kvaser_pci *board; + int i; + + if (!dev) + return; + priv = netdev_priv(dev); + if (!priv) + return; + board = priv->priv; + if (!board) + return; + + dev_info(&board->pci_dev->dev, "Removing device %s\n", + dev->name); + + for (i = 0; i < board->no_channels - 1; i++) { + if (board->slave_dev[i]) { + dev_info(&board->pci_dev->dev, "Removing device %s\n", + board->slave_dev[i]->name); + unregister_sja1000dev(board->slave_dev[i]); + free_sja1000dev(board->slave_dev[i]); + } + } + unregister_sja1000dev(dev); + + /* Disable PCI interrupts */ + kvaser_pci_disable_irq(dev); + + pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr); + pci_iounmap(board->pci_dev, board->conf_addr); + pci_iounmap(board->pci_dev, board->res_addr); + + free_sja1000dev(dev); +} + +static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, + struct net_device **master_dev, + void __iomem *conf_addr, + void __iomem *res_addr, + unsigned long base_addr) +{ + struct net_device *dev; + struct sja1000_priv *priv; + struct kvaser_pci *board; + int err, init_step; + + dev = alloc_sja1000dev(sizeof(struct kvaser_pci)); + if (dev == NULL) + return -ENOMEM; + + priv = netdev_priv(dev); + board = priv->priv; + + board->pci_dev = pdev; + board->channel = channel; + + /*S5920*/ + board->conf_addr = conf_addr; + + /*XILINX board wide address*/ + board->res_addr = res_addr; + + if (channel == 0) { + board->xilinx_ver = + ioread8(board->res_addr + XILINX_VERINT) >> 4; + init_step = 2; + + /* Assert PTADR# - we're in passive mode so the other bits are + not important */ + iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR); + + /* Disable interrupts from card */ + kvaser_pci_disable_irq(dev); + /* Enable interrupts from card */ + kvaser_pci_enable_irq(dev); + } else { + struct sja1000_priv *master_priv = netdev_priv(*master_dev); + struct kvaser_pci *master_board = master_priv->priv; + master_board->slave_dev[channel - 1] = dev; + master_board->no_channels = channel + 1; + board->xilinx_ver = master_board->xilinx_ver; + } + + dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES; + + priv->read_reg = kvaser_pci_read_reg; + priv->write_reg = kvaser_pci_write_reg; + + priv->can.bittiming.clock = KVASER_PCI_CAN_CLOCK; + + priv->ocr = KVASER_PCI_OCR; + priv->cdr = KVASER_PCI_CDR; + + /* Register and setup interrupt handling */ + dev->irq = pdev->irq; + init_step = 4; + + dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n", + dev->base_addr, board->conf_addr, dev->irq); + + SET_NETDEV_DEV(dev, &pdev->dev); + + /* Register SJA1000 device */ + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "Registering device failed (err=%d)\n", + err); + goto failure; + } + + if (channel == 0) + *master_dev = dev; + + return 0; + +failure: + kvaser_pci_del_chan(dev); + return err; +} + +static int __devinit kvaser_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct net_device *master_dev = NULL; + struct sja1000_priv *priv; + struct kvaser_pci *board; + int no_channels; + void __iomem *base_addr = NULL; + void __iomem *conf_addr = NULL; + void __iomem *res_addr = NULL; + int i; + + dev_info(&pdev->dev, "initializing device %04x:%04x\n", + pdev->vendor, pdev->device); + + err = pci_enable_device(pdev); + if (err) + goto failure; + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto failure_release_pci; + + /*S5920*/ + conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE); + if (conf_addr == NULL) { + err = -ENODEV; + goto failure_iounmap; + } + + /*XILINX board wide address*/ + res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE); + if (res_addr == NULL) { + err = -ENOMEM; + goto failure_iounmap; + } + + base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE); + if (base_addr == NULL) { + err = -ENOMEM; + goto failure_iounmap; + } + + no_channels = number_of_sja1000_chip(base_addr); + if (no_channels == 0) { + err = -ENOMEM; + goto failure_iounmap; + } + + for (i = 0; i < no_channels; i++) { + err = kvaser_pci_add_chan(pdev, i, &master_dev, + conf_addr, res_addr, + (unsigned long)base_addr); + if (err) + goto failure_cleanup; + } + + priv = netdev_priv(master_dev); + board = priv->priv; + + dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n", + board->xilinx_ver, board->no_channels); + + pci_set_drvdata(pdev, master_dev); + return 0; + +failure_cleanup: + kvaser_pci_del_chan(master_dev); + +failure_iounmap: + if (conf_addr == NULL) + pci_iounmap(pdev, conf_addr); + if (res_addr == NULL) + pci_iounmap(pdev, res_addr); + if (base_addr == NULL) + pci_iounmap(pdev, base_addr); + + pci_release_regions(pdev); + +failure_release_pci: + pci_disable_device(pdev); + +failure: + return err; + +} + +static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + kvaser_pci_del_chan(dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver kvaser_pci_driver = { + .name = DRV_NAME, + .id_table = kvaser_pci_tbl, + .probe = kvaser_pci_init_one, + .remove = __devexit_p(kvaser_pci_remove_one), +}; + +static int __init kvaser_pci_init(void) +{ + return pci_register_driver(&kvaser_pci_driver); +} + +static void __exit kvaser_pci_exit(void) +{ + pci_unregister_driver(&kvaser_pci_driver); +} + +module_init(kvaser_pci_init); +module_exit(kvaser_pci_exit); -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 8/8] can: Driver for the Freescale MSCAN controller 2009-02-19 19:01 ` [PATCH 7/8] can: SJA1000 driver for Kvaser " Wolfgang Grandegger @ 2009-02-19 19:01 ` Wolfgang Grandegger 0 siblings, 0 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-19 19:01 UTC (permalink / raw) To: netdev; +Cc: linux-kernel, Wolfgang Grandegger This patch adds the generic Socket-CAN driver for the MSCAN controller available on some Freescale processor chips like the PowerPC MPC5200, MPC512x or the m86k MC68HC12. It also includes an OF platform driver for the MPC5200. Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> --- drivers/net/can/Kconfig | 20 + drivers/net/can/Makefile | 1 + drivers/net/can/mscan/Makefile | 9 + drivers/net/can/mscan/mpc52xx_can.c | 293 +++++++++++++++ drivers/net/can/mscan/mscan.c | 681 +++++++++++++++++++++++++++++++++++ drivers/net/can/mscan/mscan.h | 237 ++++++++++++ 6 files changed, 1241 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/mscan/Makefile create mode 100644 drivers/net/can/mscan/mpc52xx_can.c create mode 100644 drivers/net/can/mscan/mscan.c create mode 100644 drivers/net/can/mscan/mscan.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index b1c4ae5..ac5ff41 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -70,6 +70,26 @@ config CAN_KVASER_PCI This driver is for the the PCIcanx and PCIcan cards (1, 2 or 4 channel) from Kvaser (http://www.kvaser.com). +config CAN_MSCAN + depends on CAN_DEV && (PPC || M68K || M68KNOMMU) + tristate "Freescale MSCAN based chips" + ---help--- + The Motorola Scalable Controller Area Network (MSCAN) definition + is based on the MSCAN12 definition which is the specific + implementation of the Motorola Scalable CAN concept targeted for + the Motorola MC68HC12 Microcontroller Family. + +config CAN_MPC52XX + tristate "Freescale MPC5200 onboard CAN controller" + depends on CAN_MSCAN && PPC_MPC52xx + default N + ---help--- + If you say yes here you get support for Freescale MPC5200 + onboard dualCAN controller. + + This driver can also be built as a module. If so, the module + will be called mpc52xx_can. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 728d83f..fc2010f 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o sysfs.o obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_MSCAN) += mscan/ ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile new file mode 100644 index 0000000..b90890f --- /dev/null +++ b/drivers/net/can/mscan/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the MSCAN controller drivers. +# + +obj-$(CONFIG_CAN_MPC52XX) += mscan-mpc52xx.o + +mscan-mpc52xx-y := mscan.o mpc52xx_can.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/mscan/mpc52xx_can.c b/drivers/net/can/mscan/mpc52xx_can.c new file mode 100644 index 0000000..8878a59 --- /dev/null +++ b/drivers/net/can/mscan/mpc52xx_can.c @@ -0,0 +1,293 @@ +/* + * CAN bus driver for the Freescale MPC52xx embedded CPU. + * + * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <linux/io.h> +#include <asm/mpc52xx.h> + +#include "mscan.h" + + +#define DRV_NAME "mpc52xx_can" + +static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = { + { .compatible = "fsl,mpc5200-cdm", }, + { .compatible = "fsl,mpc5200b-cdm", }, + {} +}; + +/* + * Get the frequency of the external oscillator clock connected + * to the SYS_XTAL_IN pin, or retrun 0 if it cannot be determined. + */ +static unsigned int __devinit mpc52xx_can_xtal_freq(struct device_node *np) +{ + struct mpc52xx_cdm __iomem *cdm; + struct device_node *np_cdm; + unsigned int freq; + u32 val; + + freq = mpc52xx_find_ipb_freq(np); + if (!freq) + return 0; + + /* + * Detemine SYS_XTAL_IN frequency from the clock domain settings + */ + np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids); + cdm = of_iomap(np_cdm, 0); + of_node_put(np_cdm); + if (!np_cdm) { + printk(KERN_ERR "%s() failed abnormally\n", __func__); + return 0; + } + + if (in_8(&cdm->ipb_clk_sel) & 0x1) + freq *= 2; + val = in_be32(&cdm->rstcfg); + if (val & (1 << 5)) + freq *= 8; + else + freq *= 4; + if (val & (1 << 6)) + freq /= 12; + else + freq /= 16; + + iounmap(cdm); + + return freq; +} + +/* + * Get frequency of the MSCAN clock source + * + * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK) + * can be selected. According to the MPC5200 user's manual, the oscillator + * clock is the better choice as it has less jitter but due to a hardware + * bug, it can not be selected for the old MPC5200 Rev. A chips. + */ + +static unsigned int __devinit mpc52xx_can_clock_freq(struct device_node *np, + int clock_src) +{ + unsigned int pvr; + + pvr = mfspr(SPRN_PVR); + + if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011) + return mpc52xx_find_ipb_freq(np); + + return mpc52xx_can_xtal_freq(np); +} + +static int __devinit mpc52xx_can_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct device_node *np = ofdev->node; + struct net_device *dev; + struct can_priv *priv; + struct resource res; + void __iomem *base; + int err, irq, res_size, clock_src; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_err(&ofdev->dev, "invalid address\n"); + return err; + } + + res_size = res.end - res.start + 1; + + if (!request_mem_region(res.start, res_size, DRV_NAME)) { + dev_err(&ofdev->dev, "couldn't request %#x..%#x\n", + res.start, res.end); + return -EBUSY; + } + + base = ioremap_nocache(res.start, res_size); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n", + res.start, res.end); + err = -ENOMEM; + goto exit_release_mem; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_mscandev(); + if (!dev) { + err = -ENOMEM; + goto exit_dispose_irq; + } + + dev->base_addr = (unsigned long)base; + dev->irq = irq; + + priv = netdev_priv(dev); + + /* + * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock + * (IP_CLK) can be selected as MSCAN clock source. According to + * the MPC5200 user's manual, the oscillator clock is the better + * choice as it has less jitter. For this reason, it is selected + * by default. + */ + if (of_get_property(np, "clock-ipb", NULL)) + clock_src = MSCAN_CLKSRC_BUS; + else + clock_src = MSCAN_CLKSRC_XTAL; + priv->bittiming.clock = mpc52xx_can_clock_freq(np, clock_src); + if (!priv->bittiming.clock) { + dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n"); + err = -ENODEV; + goto exit_free_mscan; + } + + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_mscandev(dev, clock_src); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_mscan; + } + + dev_set_drvdata(&ofdev->dev, dev); + + dev_info(&ofdev->dev, "MSCAN at 0x%lx, irq %d, clock %d Hz\n", + dev->base_addr, dev->irq, priv->bittiming.clock); + + return 0; + +exit_free_mscan: + free_candev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + release_mem_region(res.start, res_size); + + return err; +} + +static int __devexit mpc52xx_can_remove(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct device_node *np = ofdev->node; + struct resource res; + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_mscandev(dev); + iounmap((void __iomem *)dev->base_addr); + irq_dispose_mapping(dev->irq); + free_candev(dev); + + of_address_to_resource(np, 0, &res); + release_mem_region(res.start, res.end - res.start + 1); + + return 0; +} + +static struct mscan_regs saved_regs; +static int mpc52xx_can_suspend(struct of_device *ofdev, pm_message_t state) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + + _memcpy_fromio(&saved_regs, regs, sizeof(*regs)); + + return 0; +} + +static int mpc52xx_can_resume(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + + regs->canctl0 |= MSCAN_INITRQ; + while ((regs->canctl1 & MSCAN_INITAK) == 0) + udelay(10); + + regs->canctl1 = saved_regs.canctl1; + regs->canbtr0 = saved_regs.canbtr0; + regs->canbtr1 = saved_regs.canbtr1; + regs->canidac = saved_regs.canidac; + + /* restore masks, buffers etc. */ + _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0, + sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0)); + + regs->canctl0 &= ~MSCAN_INITRQ; + regs->cantbsel = saved_regs.cantbsel; + regs->canrier = saved_regs.canrier; + regs->cantier = saved_regs.cantier; + regs->canctl0 = saved_regs.canctl0; + + return 0; +} + +static struct of_device_id __devinitdata mpc52xx_can_table[] = { + {.compatible = "fsl,mpc5200-mscan"}, + {.compatible = "fsl,mpc5200b-mscan"}, + {}, +}; + +static struct of_platform_driver mpc52xx_can_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx_can", + .probe = mpc52xx_can_probe, + .remove = __devexit_p(mpc52xx_can_remove), + .suspend = mpc52xx_can_suspend, + .resume = mpc52xx_can_resume, + .match_table = mpc52xx_can_table, +}; + +static int __init mpc52xx_can_init(void) +{ + return of_register_platform_driver(&mpc52xx_can_driver); +} +module_init(mpc52xx_can_init); + +static void __exit mpc52xx_can_exit(void) +{ + return of_unregister_platform_driver(&mpc52xx_can_driver); +}; +module_exit(mpc52xx_can_exit); + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("Freescale MPC5200 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c new file mode 100644 index 0000000..bb136d0 --- /dev/null +++ b/drivers/net/can/mscan/mscan.c @@ -0,0 +1,681 @@ +/* + * CAN bus driver for the alone generic (as possible as) MSCAN controller. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/list.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/io.h> + +#include "mscan.h" + +#define MSCAN_NORMAL_MODE 0 +#define MSCAN_SLEEP_MODE MSCAN_SLPRQ +#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ) +#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ) +#define MSCAN_SET_MODE_RETRIES 255 + +#define BTR0_BRP_MASK 0x3f +#define BTR0_SJW_SHIFT 6 +#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT) + +#define BTR1_TSEG1_MASK 0xf +#define BTR1_TSEG2_SHIFT 4 +#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT) +#define BTR1_SAM_SHIFT 7 + +#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK) +#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \ + BTR0_SJW_MASK) + +#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK) +#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \ + BTR1_TSEG2_MASK) +#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT) + +static struct can_bittiming_const mscan_bittiming_const = { + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +struct mscan_state { + u8 mode; + u8 canrier; + u8 cantier; +}; + +#define TX_QUEUE_SIZE 3 + +struct tx_queue_entry { + struct list_head list; + u8 mask; + u8 id; +}; + +struct mscan_priv { + struct can_priv can; + long open_time; + unsigned long flags; + u8 shadow_statflg; + u8 shadow_canrier; + u8 cur_pri; + u8 tx_active; + + struct list_head tx_head; + struct tx_queue_entry tx_queue[TX_QUEUE_SIZE]; + struct napi_struct napi; +}; + +#define F_RX_PROGRESS 0 +#define F_TX_PROGRESS 1 +#define F_TX_WAIT_ALL 2 + +static enum can_state state_map[] = { + CAN_STATE_ACTIVE, + CAN_STATE_BUS_WARNING, + CAN_STATE_BUS_PASSIVE, + CAN_STATE_BUS_OFF +}; + +static int mscan_set_mode(struct net_device *dev, u8 mode) +{ + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct mscan_priv *priv = netdev_priv(dev); + int ret = 0; + int i; + u8 canctl1; + + if (mode != MSCAN_NORMAL_MODE) { + + if (priv->tx_active) { + /* Abort transfers before going to sleep */ + out_8(®s->cantier, 0); + out_8(®s->cantarq, priv->tx_active); + out_8(®s->cantier, priv->tx_active); + } + + canctl1 = in_8(®s->canctl1); + if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) { + out_8(®s->canctl0, + in_8(®s->canctl0) | MSCAN_SLPRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_SLPAK) + break; + udelay(100); + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + } + if (!ret) + priv->can.state = CAN_STATE_SLEEPING; + + if (!ret && (mode & MSCAN_INITRQ) + && (canctl1 & MSCAN_INITAK) == 0) { + out_8(®s->canctl0, + in_8(®s->canctl0) | MSCAN_INITRQ); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + if (in_8(®s->canctl1) & MSCAN_INITAK) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + } + if (!ret) + priv->can.state = CAN_STATE_STOPPED; + + if (!ret && (mode & MSCAN_CSWAI)) + out_8(®s->canctl0, + in_8(®s->canctl0) | MSCAN_CSWAI); + + } else { + canctl1 = in_8(®s->canctl1); + if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) { + out_8(®s->canctl0, in_8(®s->canctl0) & + ~(MSCAN_SLPRQ | MSCAN_INITRQ)); + for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) { + canctl1 = in_8(®s->canctl1); + if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK))) + break; + } + if (i >= MSCAN_SET_MODE_RETRIES) + ret = -ENODEV; + else + priv->can.state = CAN_STATE_ACTIVE; + } + } + return ret; +} + +static int mscan_start(struct net_device *dev) +{ + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + u8 canrflg; + int err; + + out_8(®s->canrier, 0); + + INIT_LIST_HEAD(&priv->tx_head); + priv->cur_pri = 0; + priv->tx_active = 0; + priv->shadow_canrier = 0; + priv->flags = 0; + + err = mscan_set_mode(dev, MSCAN_NORMAL_MODE); + if (err) + return err; + + canrflg = in_8(®s->canrflg); + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + out_8(®s->cantier, 0); + + /* Enable receive interrupts. */ + out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE | + MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0); + + return 0; +} + +static int mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct can_frame *frame = (struct can_frame *)skb->data; + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct mscan_priv *priv = netdev_priv(dev); + int i, rtr, buf_id; + u32 can_id; + + if (frame->can_dlc > 8) + return -EINVAL; + + out_8(®s->cantier, 0); + + i = ~priv->tx_active & MSCAN_TXE; + buf_id = ffs(i) - 1; + switch (hweight8(i)) { + case 0: + netif_stop_queue(dev); + dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + case 1: + /* if buf_id < 3, then current frame will be send out of order, + since buffer with lower id have higher priority (hell..) */ + if (buf_id < 3) + priv->cur_pri++; + if (priv->cur_pri == 0xff) + set_bit(F_TX_WAIT_ALL, &priv->flags); + netif_stop_queue(dev); + case 2: + set_bit(F_TX_PROGRESS, &priv->flags); + } + out_8(®s->cantbsel, i); + + rtr = frame->can_id & CAN_RTR_FLAG; + + if (frame->can_id & CAN_EFF_FLAG) { + can_id = (frame->can_id & CAN_EFF_MASK) << 1; + if (rtr) + can_id |= 1; + out_be16(®s->tx.idr3_2, can_id); + + can_id >>= 16; + can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3); + } else { + can_id = (frame->can_id & CAN_SFF_MASK) << 5; + if (rtr) + can_id |= 1 << 4; + } + out_be16(®s->tx.idr1_0, can_id); + + if (!rtr) { + void __iomem *data = ®s->tx.dsr1_0; + u16 *payload = (u16 *) frame->data; + /*Its safe to write into dsr[dlc+1] */ + for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + out_be16(data, *payload++); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + } + + out_8(®s->tx.dlr, frame->can_dlc); + out_8(®s->tx.tbpr, priv->cur_pri); + + /* Start transmission. */ + out_8(®s->cantflg, 1 << buf_id); + + if (!test_bit(F_TX_PROGRESS, &priv->flags)) + dev->trans_start = jiffies; + + list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); + + can_put_echo_skb(skb, dev, buf_id); + + /* Enable interrupt. */ + priv->tx_active |= 1 << buf_id; + out_8(®s->cantier, priv->tx_active); + + return NETDEV_TX_OK; +} + +static inline int check_set_state(struct net_device *dev, u8 canrflg) +{ + struct mscan_priv *priv = netdev_priv(dev); + enum can_state state; + int ret = 0; + + if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF) + return 0; + + state = state_map[max(MSCAN_STATE_RX(canrflg), + MSCAN_STATE_TX(canrflg))]; + if (priv->can.state < state) + ret = 1; + if (state == CAN_STATE_BUS_OFF) + can_bus_off(dev); + priv->can.state = state; + return ret; +} + +static int mscan_rx_poll(struct napi_struct *napi, int quota) +{ + struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi); + struct net_device *dev = napi->dev; + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct net_device_stats *stats = &dev->stats; + int npackets = 0; + int ret = 1; + struct sk_buff *skb; + struct can_frame *frame; + u32 can_id; + u8 canrflg; + int i; + + while (npackets < quota && ((canrflg = in_8(®s->canrflg)) & + (MSCAN_RXF | MSCAN_ERR_IF))) { + + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (!skb) { + if (printk_ratelimit()) + dev_notice(ND2D(dev), "packet dropped\n"); + stats->rx_dropped++; + out_8(®s->canrflg, canrflg); + continue; + } + + frame = (struct can_frame *)skb_put(skb, sizeof(*frame)); + memset(frame, 0, sizeof(*frame)); + + if (canrflg & MSCAN_RXF) { + can_id = in_be16(®s->rx.idr1_0); + if (can_id & (1 << 3)) { + frame->can_id = CAN_EFF_FLAG; + can_id = ((can_id << 16) | + in_be16(®s->rx.idr3_2)); + can_id = ((can_id & 0xffe00000) | + ((can_id & 0x7ffff) << 2)) >> 2; + } else { + can_id >>= 4; + frame->can_id = 0; + } + + frame->can_id |= can_id >> 1; + if (can_id & 1) + frame->can_id |= CAN_RTR_FLAG; + frame->can_dlc = in_8(®s->rx.dlr) & 0xf; + + if (!(frame->can_id & CAN_RTR_FLAG)) { + void __iomem *data = ®s->rx.dsr1_0; + u16 *payload = (u16 *) frame->data; + for (i = 0; i < (frame->can_dlc + 1) / 2; i++) { + *payload++ = in_be16(data); + data += 2 + _MSCAN_RESERVED_DSR_SIZE; + } + } + + out_8(®s->canrflg, MSCAN_RXF); + dev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += frame->can_dlc; + } else if (canrflg & MSCAN_ERR_IF) { + dev_dbg(ND2D(dev), "error interrupt (canrflg=%#x)\n", + canrflg); + frame->can_id = CAN_ERR_FLAG; + + if (canrflg & MSCAN_OVRIF) { + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + } else + frame->data[1] = 0; + + if (check_set_state(dev, canrflg)) { + frame->can_id |= CAN_ERR_CRTL; + switch (priv->can.state) { + case CAN_STATE_BUS_WARNING: + if ((priv->shadow_statflg & + MSCAN_RSTAT_MSK) < + (canrflg & MSCAN_RSTAT_MSK)) + frame->data[1] |= + CAN_ERR_CRTL_RX_WARNING; + + if ((priv->shadow_statflg & + MSCAN_TSTAT_MSK) < + (canrflg & MSCAN_TSTAT_MSK)) + frame->data[1] |= + CAN_ERR_CRTL_TX_WARNING; + break; + case CAN_STATE_BUS_PASSIVE: + frame->data[1] |= + CAN_ERR_CRTL_RX_PASSIVE; + break; + case CAN_STATE_BUS_OFF: + frame->can_id |= CAN_ERR_BUSOFF; + frame->can_id &= ~CAN_ERR_CRTL; + break; + default: + break; + } + } + priv->shadow_statflg = canrflg & MSCAN_STAT_MSK; + frame->can_dlc = CAN_ERR_DLC; + out_8(®s->canrflg, MSCAN_ERR_IF); + } + + npackets++; + skb->dev = dev; + skb->protocol = __constant_htons(ETH_P_CAN); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_receive_skb(skb); + } + + if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) { + napi_complete(&priv->napi); + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + ret = 0; + } + return ret; +} + +static irqreturn_t mscan_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct net_device_stats *stats = &dev->stats; + u8 cantier, cantflg, canrflg; + irqreturn_t ret = IRQ_NONE; + + cantier = in_8(®s->cantier) & MSCAN_TXE; + cantflg = in_8(®s->cantflg) & cantier; + + if (cantier && cantflg) { + + struct list_head *tmp, *pos; + + list_for_each_safe(pos, tmp, &priv->tx_head) { + struct tx_queue_entry *entry = + list_entry(pos, struct tx_queue_entry, list); + u8 mask = entry->mask; + + if (!(cantflg & mask)) + continue; + + out_8(®s->cantbsel, mask); + stats->tx_bytes += in_8(®s->tx.dlr); + stats->tx_packets++; + can_get_echo_skb(dev, entry->id); + priv->tx_active &= ~mask; + list_del(pos); + } + + if (list_empty(&priv->tx_head)) { + clear_bit(F_TX_WAIT_ALL, &priv->flags); + clear_bit(F_TX_PROGRESS, &priv->flags); + priv->cur_pri = 0; + } else + dev->trans_start = jiffies; + + if (!test_bit(F_TX_WAIT_ALL, &priv->flags)) + netif_wake_queue(dev); + + out_8(®s->cantier, priv->tx_active); + ret = IRQ_HANDLED; + } + + canrflg = in_8(®s->canrflg); + if ((canrflg & ~MSCAN_STAT_MSK) && + !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) { + if (canrflg & ~MSCAN_STAT_MSK) { + priv->shadow_canrier = in_8(®s->canrier); + out_8(®s->canrier, 0); + napi_schedule(&priv->napi); + ret = IRQ_HANDLED; + } else + clear_bit(F_RX_PROGRESS, &priv->flags); + } + return ret; +} + +static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) +{ + + struct mscan_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_SLEEP: + case CAN_MODE_STOP: + netif_stop_queue(dev); + mscan_set_mode(dev, + (mode == + CAN_MODE_STOP) ? MSCAN_INIT_MODE : + MSCAN_SLEEP_MODE); + break; + case CAN_MODE_START: + if (priv->can.state <= CAN_STATE_BUS_OFF) + mscan_set_mode(dev, MSCAN_INIT_MODE); + ret = mscan_start(dev); + if (ret) + break; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static int mscan_do_set_bittiming(struct net_device *dev) +{ + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct mscan_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw); + btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) | + BTR1_SET_TSEG2(bt->phase_seg2) | + BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)); + + dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + out_8(®s->canbtr0, btr0); + out_8(®s->canbtr1, btr1); + + return 0; +} + +static int mscan_open(struct net_device *dev) +{ + int ret; + struct mscan_priv *priv = netdev_priv(dev); + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + + /* determine and set bittime */ + ret = can_set_bittiming(dev); + if (ret) + return ret; + + napi_enable(&priv->napi); + ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev); + + if (ret < 0) { + napi_disable(&priv->napi); + printk(KERN_ERR "%s - failed to attach interrupt\n", + dev->name); + return ret; + } + + priv->open_time = jiffies; + + out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN); + + ret = mscan_start(dev); + if (ret) + return ret; + + netif_start_queue(dev); + + return 0; +} + +static int mscan_close(struct net_device *dev) +{ + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + struct mscan_priv *priv = netdev_priv(dev); + + napi_disable(&priv->napi); + + out_8(®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); + priv->open_time = 0; + + return 0; +} + +static const struct net_device_ops mscan_netdev_ops = { + .ndo_open = mscan_open, + .ndo_stop = mscan_close, + .ndo_start_xmit = mscan_start_xmit, +}; + +int register_mscandev(struct net_device *dev, int clock_src) +{ + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + u8 ctl1; + + ctl1 = in_8(®s->canctl1); + if (clock_src) + ctl1 |= MSCAN_CLKSRC; + else + ctl1 &= ~MSCAN_CLKSRC; + + ctl1 |= MSCAN_CANE; + out_8(®s->canctl1, ctl1); + udelay(100); + + /* acceptance mask/acceptance code (accept everything) */ + out_be16(®s->canidar1_0, 0); + out_be16(®s->canidar3_2, 0); + out_be16(®s->canidar5_4, 0); + out_be16(®s->canidar7_6, 0); + + out_be16(®s->canidmr1_0, 0xffff); + out_be16(®s->canidmr3_2, 0xffff); + out_be16(®s->canidmr5_4, 0xffff); + out_be16(®s->canidmr7_6, 0xffff); + /* Two 32 bit Acceptance Filters */ + out_8(®s->canidac, MSCAN_AF_32BIT); + + mscan_set_mode(dev, MSCAN_INIT_MODE); + + return register_candev(dev); +} +EXPORT_SYMBOL_GPL(register_mscandev); + +void unregister_mscandev(struct net_device *dev) +{ + struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr; + mscan_set_mode(dev, MSCAN_INIT_MODE); + out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_CANE); + unregister_candev(dev); +} +EXPORT_SYMBOL_GPL(unregister_mscandev); + +struct net_device *alloc_mscandev(void) +{ + struct net_device *dev; + struct mscan_priv *priv; + int i; + + dev = alloc_candev(sizeof(struct mscan_priv)); + if (!dev) + return NULL; + priv = netdev_priv(dev); + + dev->netdev_ops = &mscan_netdev_ops; + + dev->flags |= IFF_ECHO; /* we support local echo */ + + netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8); + + priv->can.bittiming_const = &mscan_bittiming_const; + priv->can.do_set_bittiming = mscan_do_set_bittiming; + priv->can.do_set_mode = mscan_do_set_mode; + + for (i = 0; i < TX_QUEUE_SIZE; i++) { + priv->tx_queue[i].id = i; + priv->tx_queue[i].mask = 1 << i; + } + + return dev; +} +EXPORT_SYMBOL_GPL(alloc_mscandev); + +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips"); diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h new file mode 100644 index 0000000..81a2eb5 --- /dev/null +++ b/drivers/net/can/mscan/mscan.h @@ -0,0 +1,237 @@ +/* + * Definitions of consts/structs to drive the Freescale MSCAN. + * + * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>, + * Varma Electronics Oy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MSCAN_H__ +#define __MSCAN_H__ + +#include <linux/types.h> + +/* MSCAN control register 0 (CANCTL0) bits */ +#define MSCAN_RXFRM 0x80 +#define MSCAN_RXACT 0x40 +#define MSCAN_CSWAI 0x20 +#define MSCAN_SYNCH 0x10 +#define MSCAN_TIME 0x08 +#define MSCAN_WUPE 0x04 +#define MSCAN_SLPRQ 0x02 +#define MSCAN_INITRQ 0x01 + +/* MSCAN control register 1 (CANCTL1) bits */ +#define MSCAN_CANE 0x80 +#define MSCAN_CLKSRC 0x40 +#define MSCAN_LOOPB 0x20 +#define MSCAN_LISTEN 0x10 +#define MSCAN_WUPM 0x04 +#define MSCAN_SLPAK 0x02 +#define MSCAN_INITAK 0x01 + +/* Use the MPC5200 MSCAN variant? */ +#ifdef CONFIG_PPC +#define MSCAN_FOR_MPC5200 +#endif + +#ifdef MSCAN_FOR_MPC5200 +#define MSCAN_CLKSRC_BUS 0 +#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC +#else +#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC +#define MSCAN_CLKSRC_XTAL 0 +#endif + +/* MSCAN receiver flag register (CANRFLG) bits */ +#define MSCAN_WUPIF 0x80 +#define MSCAN_CSCIF 0x40 +#define MSCAN_RSTAT1 0x20 +#define MSCAN_RSTAT0 0x10 +#define MSCAN_TSTAT1 0x08 +#define MSCAN_TSTAT0 0x04 +#define MSCAN_OVRIF 0x02 +#define MSCAN_RXF 0x01 +#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF) +#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0) +#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK) + +#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \ + MSCAN_TSTAT1 | MSCAN_TSTAT0) +#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2) +#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4) +#define MSCAN_STATE_ACTIVE 0 +#define MSCAN_STATE_WARNING 1 +#define MSCAN_STATE_PASSIVE 2 +#define MSCAN_STATE_BUSOFF 3 + +/* MSCAN receiver interrupt enable register (CANRIER) bits */ +#define MSCAN_WUPIE 0x80 +#define MSCAN_CSCIE 0x40 +#define MSCAN_RSTATE1 0x20 +#define MSCAN_RSTATE0 0x10 +#define MSCAN_TSTATE1 0x08 +#define MSCAN_TSTATE0 0x04 +#define MSCAN_OVRIE 0x02 +#define MSCAN_RXFIE 0x01 + +/* MSCAN transmitter flag register (CANTFLG) bits */ +#define MSCAN_TXE2 0x04 +#define MSCAN_TXE1 0x02 +#define MSCAN_TXE0 0x01 +#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0) + +/* MSCAN transmitter interrupt enable register (CANTIER) bits */ +#define MSCAN_TXIE2 0x04 +#define MSCAN_TXIE1 0x02 +#define MSCAN_TXIE0 0x01 +#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0) + +/* MSCAN transmitter message abort request (CANTARQ) bits */ +#define MSCAN_ABTRQ2 0x04 +#define MSCAN_ABTRQ1 0x02 +#define MSCAN_ABTRQ0 0x01 + +/* MSCAN transmitter message abort ack (CANTAAK) bits */ +#define MSCAN_ABTAK2 0x04 +#define MSCAN_ABTAK1 0x02 +#define MSCAN_ABTAK0 0x01 + +/* MSCAN transmit buffer selection (CANTBSEL) bits */ +#define MSCAN_TX2 0x04 +#define MSCAN_TX1 0x02 +#define MSCAN_TX0 0x01 + +/* MSCAN ID acceptance control register (CANIDAC) bits */ +#define MSCAN_IDAM1 0x20 +#define MSCAN_IDAM0 0x10 +#define MSCAN_IDHIT2 0x04 +#define MSCAN_IDHIT1 0x02 +#define MSCAN_IDHIT0 0x01 + +#define MSCAN_AF_32BIT 0x00 +#define MSCAN_AF_16BIT MSCAN_IDAM0 +#define MSCAN_AF_8BIT MSCAN_IDAM1 +#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1) +#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1)) + +/* MSCAN Miscellaneous Register (CANMISC) bits */ +#define MSCAN_BOHOLD 0x01 + +#ifdef MSCAN_FOR_MPC5200 +#define _MSCAN_RESERVED_(n, num) u8 _res##n[num] +#define _MSCAN_RESERVED_DSR_SIZE 2 +#else +#define _MSCAN_RESERVED_(n, num) +#define _MSCAN_RESERVED_DSR_SIZE 0 +#endif + +/* Structure of the hardware registers */ +struct mscan_regs { + /* (see doco S12MSCANV3/D) MPC5200 MSCAN */ + u8 canctl0; /* + 0x00 0x00 */ + u8 canctl1; /* + 0x01 0x01 */ + _MSCAN_RESERVED_(1, 2); /* + 0x02 */ + u8 canbtr0; /* + 0x04 0x02 */ + u8 canbtr1; /* + 0x05 0x03 */ + _MSCAN_RESERVED_(2, 2); /* + 0x06 */ + u8 canrflg; /* + 0x08 0x04 */ + u8 canrier; /* + 0x09 0x05 */ + _MSCAN_RESERVED_(3, 2); /* + 0x0a */ + u8 cantflg; /* + 0x0c 0x06 */ + u8 cantier; /* + 0x0d 0x07 */ + _MSCAN_RESERVED_(4, 2); /* + 0x0e */ + u8 cantarq; /* + 0x10 0x08 */ + u8 cantaak; /* + 0x11 0x09 */ + _MSCAN_RESERVED_(5, 2); /* + 0x12 */ + u8 cantbsel; /* + 0x14 0x0a */ + u8 canidac; /* + 0x15 0x0b */ + u8 reserved; /* + 0x16 0x0c */ + _MSCAN_RESERVED_(6, 5); /* + 0x17 */ +#ifndef MSCAN_FOR_MPC5200 + u8 canmisc; /* 0x0d */ +#endif + u8 canrxerr; /* + 0x1c 0x0e */ + u8 cantxerr; /* + 0x1d 0x0f */ + _MSCAN_RESERVED_(7, 2); /* + 0x1e */ + u16 canidar1_0; /* + 0x20 0x10 */ + _MSCAN_RESERVED_(8, 2); /* + 0x22 */ + u16 canidar3_2; /* + 0x24 0x12 */ + _MSCAN_RESERVED_(9, 2); /* + 0x26 */ + u16 canidmr1_0; /* + 0x28 0x14 */ + _MSCAN_RESERVED_(10, 2); /* + 0x2a */ + u16 canidmr3_2; /* + 0x2c 0x16 */ + _MSCAN_RESERVED_(11, 2); /* + 0x2e */ + u16 canidar5_4; /* + 0x30 0x18 */ + _MSCAN_RESERVED_(12, 2); /* + 0x32 */ + u16 canidar7_6; /* + 0x34 0x1a */ + _MSCAN_RESERVED_(13, 2); /* + 0x36 */ + u16 canidmr5_4; /* + 0x38 0x1c */ + _MSCAN_RESERVED_(14, 2); /* + 0x3a */ + u16 canidmr7_6; /* + 0x3c 0x1e */ + _MSCAN_RESERVED_(15, 2); /* + 0x3e */ + struct { + u16 idr1_0; /* + 0x40 0x20 */ + _MSCAN_RESERVED_(16, 2); /* + 0x42 */ + u16 idr3_2; /* + 0x44 0x22 */ + _MSCAN_RESERVED_(17, 2); /* + 0x46 */ + u16 dsr1_0; /* + 0x48 0x24 */ + _MSCAN_RESERVED_(18, 2); /* + 0x4a */ + u16 dsr3_2; /* + 0x4c 0x26 */ + _MSCAN_RESERVED_(19, 2); /* + 0x4e */ + u16 dsr5_4; /* + 0x50 0x28 */ + _MSCAN_RESERVED_(20, 2); /* + 0x52 */ + u16 dsr7_6; /* + 0x54 0x2a */ + _MSCAN_RESERVED_(21, 2); /* + 0x56 */ + u8 dlr; /* + 0x58 0x2c */ + u8:8; /* + 0x59 0x2d */ + _MSCAN_RESERVED_(22, 2); /* + 0x5a */ + u16 time; /* + 0x5c 0x2e */ + } rx; + _MSCAN_RESERVED_(23, 2); /* + 0x5e */ + struct { + u16 idr1_0; /* + 0x60 0x30 */ + _MSCAN_RESERVED_(24, 2); /* + 0x62 */ + u16 idr3_2; /* + 0x64 0x32 */ + _MSCAN_RESERVED_(25, 2); /* + 0x66 */ + u16 dsr1_0; /* + 0x68 0x34 */ + _MSCAN_RESERVED_(26, 2); /* + 0x6a */ + u16 dsr3_2; /* + 0x6c 0x36 */ + _MSCAN_RESERVED_(27, 2); /* + 0x6e */ + u16 dsr5_4; /* + 0x70 0x38 */ + _MSCAN_RESERVED_(28, 2); /* + 0x72 */ + u16 dsr7_6; /* + 0x74 0x3a */ + _MSCAN_RESERVED_(29, 2); /* + 0x76 */ + u8 dlr; /* + 0x78 0x3c */ + u8 tbpr; /* + 0x79 0x3d */ + _MSCAN_RESERVED_(30, 2); /* + 0x7a */ + u16 time; /* + 0x7c 0x3e */ + } tx; + _MSCAN_RESERVED_(31, 2); /* + 0x7e */ +} __attribute__ ((packed)); + +#undef _MSCAN_RESERVED_ +#define MSCAN_REGION sizeof(struct mscan) + +struct net_device *alloc_mscandev(void); +/* clock_src: + * 1 = The MSCAN clock source is the onchip Bus Clock. + * 0 = The MSCAN clock source is the chip Oscillator Clock. + */ +extern int register_mscandev(struct net_device *dev, int clock_src); +extern void unregister_mscandev(struct net_device *dev); + +#endif /* __MSCAN_H__ */ -- 1.5.6.6 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 4/8] can: Driver for the SJA1000 CAN controller 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 5/8] can: SJA1000 generic platform bus driver Wolfgang Grandegger @ 2009-02-20 0:14 ` Jonathan Corbet 2009-02-20 8:46 ` David Miller ` (2 more replies) 1 sibling, 3 replies; 21+ messages in thread From: Jonathan Corbet @ 2009-02-20 0:14 UTC (permalink / raw) To: Wolfgang Grandegger; +Cc: netdev, linux-kernel, Oliver Hartkopp I won't be able to look at all of these... > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > index d609895..78a412b 100644 > --- a/drivers/net/can/Kconfig > +++ b/drivers/net/can/Kconfig > @@ -35,6 +35,17 @@ config CAN_CALC_BITTIMING > files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". > If unsure, say Y. > > +config CAN_SJA1000 > + depends on CAN_DEV > + tristate "Philips SJA1000" > + ---help--- > + The SJA1000 is one of the top CAN controllers out there. As it > + has a multiplexed interface it fits directly to 8051 > + microcontrollers or into the PC I/O port space. The SJA1000 > + is a full CAN controller, with shadow registers for RX and TX. > + It can send and receive any kinds of CAN frames (SFF/EFF/RTR) > + with a single (simple) filter setup. This sounds more like advertising text. But what people need to know is whether they should enable it or not. [...] > diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c > new file mode 100644 > index 0000000..6fe516d > --- /dev/null > +++ b/drivers/net/can/sja1000/sja1000.c > @@ -0,0 +1,681 @@ [...] > +static void sja1000_start(struct net_device *dev) > +{ > + struct sja1000_priv *priv = netdev_priv(dev); > + > + /* leave reset mode */ > + if (priv->can.state != CAN_STATE_STOPPED) > + set_reset_mode(dev); > + > + /* Clear error counters and error code capture */ > + priv->write_reg(dev, REG_TXERR, 0x0); > + priv->write_reg(dev, REG_RXERR, 0x0); > + priv->read_reg(dev, REG_ECC); > + > + /* leave reset mode */ > + set_normal_mode(dev); > +} It's about here that I begin to wonder about locking again. What is preventing concurrent access to the device? [...] > +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); Interesting, here we do have a lock. What is it protecting? *state?? It can't be the device registers, since they are accessed without locks in many other places. > + 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; > +} [...] > +/* > + * transmit a CAN message > + * message layout in the sk_buff should be like this: > + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 > + * [ can-id ] [flags] [len] [can data (up to 8 bytes] > + */ > +static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct sja1000_priv *priv = netdev_priv(dev); > + struct net_device_stats *stats = &dev->stats; > + struct can_frame *cf = (struct can_frame *)skb->data; > + uint8_t fi; > + uint8_t dlc; > + canid_t id; > + uint8_t dreg; > + int i; > + > + netif_stop_queue(dev); > + > + fi = dlc = cf->can_dlc; > + id = cf->can_id; > + > + if (id & CAN_RTR_FLAG) > + fi |= FI_RTR; > + > + if (id & CAN_EFF_FLAG) { > + fi |= FI_FF; > + dreg = EFF_BUF; > + priv->write_reg(dev, REG_FI, fi); > + priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); > + priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8)); > + priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5); > + priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3); > + } else { > + dreg = SFF_BUF; > + priv->write_reg(dev, REG_FI, fi); > + priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3); > + priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5); > + } > + > + for (i = 0; i < dlc; i++) > + priv->write_reg(dev, dreg++, cf->data[i]); > + > + stats->tx_bytes += dlc; > + dev->trans_start = jiffies; > + > + can_put_echo_skb(skb, dev, 0); Hmm...looking back at can_put_echo_skb(), I see that it expects dev->priv to point to a struct can_priv. Here, though, we see it pointing to a struct sja1000_prive instead. I begin to suspect dangerous trickery going on behind our backs... > + > + priv->write_reg(dev, REG_CMR, CMD_TR); > + > + return 0; > +} [...] > +irqreturn_t sja1000_interrupt(int irq, void *dev_id) > +{ > + struct net_device *dev = (struct net_device *)dev_id; > + struct sja1000_priv *priv = netdev_priv(dev); > + struct net_device_stats *stats = &dev->stats; > + uint8_t isrc, status; > + int n = 0; > + > + /* Shared interrupts and IRQ off? */ > + if (priv->read_reg(dev, REG_IER) == IRQ_OFF) > + return IRQ_NONE; > + > + if (priv->pre_irq) > + priv->pre_irq(dev); > + > + while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) { > + n++; > + status = priv->read_reg(dev, REG_SR); > + > + if (isrc & IRQ_WUI) { > + /* wake-up interrupt */ > + priv->can.can_stats.wakeup++; > + } > + if (isrc & IRQ_TI) { > + /* transmission complete interrupt */ > + stats->tx_packets++; > + can_get_echo_skb(dev, 0); > + netif_wake_queue(dev); > + } > + if (isrc & IRQ_RI) { > + /* receive interrupt */ > + while (status & SR_RBS) { > + sja1000_rx(dev); > + status = priv->read_reg(dev, REG_SR); > + } > + } > + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { > + /* error interrupt */ > + if (sja1000_err(dev, isrc, status)) > + break; > + } > + } > + > + if (priv->post_irq) > + priv->post_irq(dev); > + > + if (n >= SJA1000_MAX_IRQ) > + dev_dbg(ND2D(dev), "%d messages handled in ISR", n); > + > + return (n) ? IRQ_HANDLED : IRQ_NONE; > +} > +EXPORT_SYMBOL_GPL(sja1000_interrupt); You used spin_lock_irq(&irq_lock) above, but the interrupt handler doesn't take that lock? So (above) you could acquire the lock while the interrupt handler is running? I hate to keep asking this question, but...what does that lock protect? [...] > +static int sja1000_close(struct net_device *dev) > +{ > + struct sja1000_priv *priv = netdev_priv(dev); > + > + set_reset_mode(dev); > + netif_stop_queue(dev); > + priv->open_time = 0; > + can_close_cleanup(dev); What happens if your device interrupts right here? Maybe you should disconnect the handler earlier? > + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) > + free_irq(dev->irq, (void *)dev); > + > + return 0; > +} [...] > +int register_sja1000dev(struct net_device *dev) > +{ > + struct sja1000_priv *priv = netdev_priv(dev); > + int err; > + > + if (!sja1000_probe_chip(dev)) > + return -ENODEV; > + > + dev->flags |= IFF_ECHO; /* we support local echo */ > + > + dev->netdev_ops = &sja1000_netdev_ops; > + > + 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); Here we've registered our device with the CAN and networking core... > + if (err) { > + printk(KERN_INFO > + "%s: registering netdev failed\n", DRV_NAME); > + free_netdev(dev); > + return err; > + } > + > + set_reset_mode(dev); > + chipset_init(dev); ...but only here have we gotten it ready to operate. If the higher levels decide to do something with your device in the mean time, will the right thing happen? > + return 0; > +} > +EXPORT_SYMBOL_GPL(register_sja1000dev); [...] > diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h > new file mode 100644 > index 0000000..60d4cd6 > --- /dev/null > +++ b/drivers/net/can/sja1000/sja1000.h [...] > +/* > + * SJA1000 private data structure > + */ > +struct sja1000_priv { > + struct can_priv can; /* must be the first member! */ AHA! I knew it! This kind of pointer trickery is fragile and dangerous, please don't do it. Much better would be something like: dev->priv = &dev_specific_priv->can; Then the higher layers know they have a proper struct can_priv pointer. Then you can use container_of() at this level to get the outer structure pointer. Much more robust and in line with normal kernel coding idiom. > + long open_time; > + struct sk_buff *echo_skb; > + > + u8 (*read_reg) (struct net_device *dev, int reg); > + void (*write_reg) (struct net_device *dev, int reg, u8 val); > + void (*pre_irq) (struct net_device *dev); > + void (*post_irq) (struct net_device *dev); > + > + void *priv; /* for board-specific data */ > + struct net_device *dev; > + > + u8 ocr; > + u8 cdr; > + u32 flags; ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 4/8] can: Driver for the SJA1000 CAN controller 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet @ 2009-02-20 8:46 ` David Miller 2009-02-20 9:35 ` Wolfgang Grandegger 2009-05-01 18:21 ` Wolfgang Grandegger 2 siblings, 0 replies; 21+ messages in thread From: David Miller @ 2009-02-20 8:46 UTC (permalink / raw) To: corbet; +Cc: wg, netdev, linux-kernel, oliver.hartkopp From: Jonathan Corbet <corbet@lwn.net> Date: Thu, 19 Feb 2009 17:14:40 -0700 > I won't be able to look at all of these... > > > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > > index d609895..78a412b 100644 > > --- a/drivers/net/can/Kconfig > > +++ b/drivers/net/can/Kconfig > > @@ -35,6 +35,17 @@ config CAN_CALC_BITTIMING > > files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". > > If unsure, say Y. > > > > +config CAN_SJA1000 > > + depends on CAN_DEV > > + tristate "Philips SJA1000" > > + ---help--- > > + The SJA1000 is one of the top CAN controllers out there. As it > > + has a multiplexed interface it fits directly to 8051 > > + microcontrollers or into the PC I/O port space. The SJA1000 > > + is a full CAN controller, with shadow registers for RX and TX. > > + It can send and receive any kinds of CAN frames (SFF/EFF/RTR) > > + with a single (simple) filter setup. > > This sounds more like advertising text. But what people need to know is > whether they should enable it or not. Totally agreed, this Kconfig help text is inappropriate at best. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 4/8] can: Driver for the SJA1000 CAN controller 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet 2009-02-20 8:46 ` David Miller @ 2009-02-20 9:35 ` Wolfgang Grandegger 2009-05-01 18:21 ` Wolfgang Grandegger 2 siblings, 0 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-20 9:35 UTC (permalink / raw) To: Jonathan Corbet; +Cc: netdev, linux-kernel, Oliver Hartkopp Hi Jonathan, Jonathan Corbet wrote: > I won't be able to look at all of these... OK, I will check the others for similar issues. >> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig >> index d609895..78a412b 100644 >> --- a/drivers/net/can/Kconfig >> +++ b/drivers/net/can/Kconfig >> @@ -35,6 +35,17 @@ config CAN_CALC_BITTIMING >> files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". >> If unsure, say Y. >> >> +config CAN_SJA1000 >> + depends on CAN_DEV >> + tristate "Philips SJA1000" >> + ---help--- >> + The SJA1000 is one of the top CAN controllers out there. As it >> + has a multiplexed interface it fits directly to 8051 >> + microcontrollers or into the PC I/O port space. The SJA1000 >> + is a full CAN controller, with shadow registers for RX and TX. >> + It can send and receive any kinds of CAN frames (SFF/EFF/RTR) >> + with a single (simple) filter setup. > > This sounds more like advertising text. But what people need to know is > whether they should enable it or not. Yes, "Enables support for the SJA1000 CAN controller from Philips or NXP" should be sufficient. > [...] > >> diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c >> new file mode 100644 >> index 0000000..6fe516d >> --- /dev/null >> +++ b/drivers/net/can/sja1000/sja1000.c >> @@ -0,0 +1,681 @@ > > [...] > >> +static void sja1000_start(struct net_device *dev) >> +{ >> + struct sja1000_priv *priv = netdev_priv(dev); >> + >> + /* leave reset mode */ >> + if (priv->can.state != CAN_STATE_STOPPED) >> + set_reset_mode(dev); >> + >> + /* Clear error counters and error code capture */ >> + priv->write_reg(dev, REG_TXERR, 0x0); >> + priv->write_reg(dev, REG_RXERR, 0x0); >> + priv->read_reg(dev, REG_ECC); >> + >> + /* leave reset mode */ >> + set_normal_mode(dev); >> +} > > It's about here that I begin to wonder about locking again. What is > preventing concurrent access to the device? The device is usually stopped when this function is called but I will check for corner cases due to the restart feature. > [...] > >> +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); > > Interesting, here we do have a lock. What is it protecting? *state?? It > can't be the device registers, since they are accessed without locks in > many other places. This lock is indeed required to protect priv->can.irq_lock not be changed by the ISR. But it should be a lock private for the SJA1000. > >> + 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; >> +} > > [...] > >> +/* >> + * transmit a CAN message >> + * message layout in the sk_buff should be like this: >> + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 >> + * [ can-id ] [flags] [len] [can data (up to 8 bytes] >> + */ >> +static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev) >> +{ >> + struct sja1000_priv *priv = netdev_priv(dev); >> + struct net_device_stats *stats = &dev->stats; >> + struct can_frame *cf = (struct can_frame *)skb->data; >> + uint8_t fi; >> + uint8_t dlc; >> + canid_t id; >> + uint8_t dreg; >> + int i; >> + >> + netif_stop_queue(dev); >> + >> + fi = dlc = cf->can_dlc; >> + id = cf->can_id; >> + >> + if (id & CAN_RTR_FLAG) >> + fi |= FI_RTR; >> + >> + if (id & CAN_EFF_FLAG) { >> + fi |= FI_FF; >> + dreg = EFF_BUF; >> + priv->write_reg(dev, REG_FI, fi); >> + priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16)); >> + priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8)); >> + priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5); >> + priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3); >> + } else { >> + dreg = SFF_BUF; >> + priv->write_reg(dev, REG_FI, fi); >> + priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3); >> + priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5); >> + } >> + >> + for (i = 0; i < dlc; i++) >> + priv->write_reg(dev, dreg++, cf->data[i]); >> + >> + stats->tx_bytes += dlc; >> + dev->trans_start = jiffies; >> + >> + can_put_echo_skb(skb, dev, 0); > > Hmm...looking back at can_put_echo_skb(), I see that it expects dev->priv > to point to a struct can_priv. Here, though, we see it pointing to a > struct sja1000_prive instead. I begin to suspect dangerous trickery going > on behind our backs... I see it coming... >> + >> + priv->write_reg(dev, REG_CMR, CMD_TR); >> + >> + return 0; >> +} > > [...] > >> +irqreturn_t sja1000_interrupt(int irq, void *dev_id) >> +{ >> + struct net_device *dev = (struct net_device *)dev_id; >> + struct sja1000_priv *priv = netdev_priv(dev); >> + struct net_device_stats *stats = &dev->stats; >> + uint8_t isrc, status; >> + int n = 0; >> + >> + /* Shared interrupts and IRQ off? */ >> + if (priv->read_reg(dev, REG_IER) == IRQ_OFF) >> + return IRQ_NONE; >> + >> + if (priv->pre_irq) >> + priv->pre_irq(dev); >> + >> + while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) { >> + n++; >> + status = priv->read_reg(dev, REG_SR); >> + >> + if (isrc & IRQ_WUI) { >> + /* wake-up interrupt */ >> + priv->can.can_stats.wakeup++; >> + } >> + if (isrc & IRQ_TI) { >> + /* transmission complete interrupt */ >> + stats->tx_packets++; >> + can_get_echo_skb(dev, 0); >> + netif_wake_queue(dev); >> + } >> + if (isrc & IRQ_RI) { >> + /* receive interrupt */ >> + while (status & SR_RBS) { >> + sja1000_rx(dev); >> + status = priv->read_reg(dev, REG_SR); >> + } >> + } >> + if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { >> + /* error interrupt */ >> + if (sja1000_err(dev, isrc, status)) >> + break; >> + } >> + } >> + >> + if (priv->post_irq) >> + priv->post_irq(dev); >> + >> + if (n >= SJA1000_MAX_IRQ) >> + dev_dbg(ND2D(dev), "%d messages handled in ISR", n); >> + >> + return (n) ? IRQ_HANDLED : IRQ_NONE; >> +} >> +EXPORT_SYMBOL_GPL(sja1000_interrupt); > > You used spin_lock_irq(&irq_lock) above, but the interrupt handler doesn't > take that lock? So (above) you could acquire the lock while the interrupt > handler is running? I hate to keep asking this question, but...what does > that lock protect? That's wrong, indeed. > [...] > >> +static int sja1000_close(struct net_device *dev) >> +{ >> + struct sja1000_priv *priv = netdev_priv(dev); >> + >> + set_reset_mode(dev); >> + netif_stop_queue(dev); >> + priv->open_time = 0; >> + can_close_cleanup(dev); > > What happens if your device interrupts right here? Maybe you should > disconnect the handler earlier? It will not interrupt here because set_reset_mode(dev) already disabled the interrupts. >> + if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) >> + free_irq(dev->irq, (void *)dev); >> + >> + return 0; >> +} > > [...] > >> +int register_sja1000dev(struct net_device *dev) >> +{ >> + struct sja1000_priv *priv = netdev_priv(dev); >> + int err; >> + >> + if (!sja1000_probe_chip(dev)) >> + return -ENODEV; >> + >> + dev->flags |= IFF_ECHO; /* we support local echo */ >> + >> + dev->netdev_ops = &sja1000_netdev_ops; >> + >> + 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); > > Here we've registered our device with the CAN and networking core... > >> + if (err) { >> + printk(KERN_INFO >> + "%s: registering netdev failed\n", DRV_NAME); >> + free_netdev(dev); >> + return err; >> + } >> + >> + set_reset_mode(dev); >> + chipset_init(dev); > > ...but only here have we gotten it ready to operate. If the higher levels > decide to do something with your device in the mean time, will the right > thing happen? Right, these two lines must be moved before register_candev(). > >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(register_sja1000dev); > > [...] > >> diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h >> new file mode 100644 >> index 0000000..60d4cd6 >> --- /dev/null >> +++ b/drivers/net/can/sja1000/sja1000.h > > [...] > >> +/* >> + * SJA1000 private data structure >> + */ >> +struct sja1000_priv { >> + struct can_priv can; /* must be the first member! */ > > AHA! I knew it! > > This kind of pointer trickery is fragile and dangerous, please don't do > it. Much better would be something like: > > dev->priv = &dev_specific_priv->can; > > Then the higher layers know they have a proper struct can_priv pointer. > Then you can use container_of() at this level to get the outer structure > pointer. Much more robust and in line with normal kernel coding idiom. Our approach allows a more elegant usage and is still used in the kernel but I agree, it's more error-prone. I will come up with a revised patch a.s.a.p. Thanks. Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 4/8] can: Driver for the SJA1000 CAN controller 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet 2009-02-20 8:46 ` David Miller 2009-02-20 9:35 ` Wolfgang Grandegger @ 2009-05-01 18:21 ` Wolfgang Grandegger 2 siblings, 0 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-05-01 18:21 UTC (permalink / raw) To: Jonathan Corbet; +Cc: netdev, linux-kernel, Oliver Hartkopp I'm now back fixing the open issues of this patch series... Jonathan Corbet wrote: [snip] >> +/* >> + * SJA1000 private data structure >> + */ >> +struct sja1000_priv { >> + struct can_priv can; /* must be the first member! */ > > AHA! I knew it! > > This kind of pointer trickery is fragile and dangerous, please don't do > it. Much better would be something like: > > dev->priv = &dev_specific_priv->can; > > Then the higher layers know they have a proper struct can_priv pointer. > Then you can use container_of() at this level to get the outer structure > pointer. Much more robust and in line with normal kernel coding idiom. Unfortunately, the "struct net_device" does not have a "priv" field. Using struct can_priv *can = netdev_priv(dev); struct sja1000_priv *sja1000 = candev_priv(dev); instead is awkward. That's what we had at first. Any other more elegant solution in mind? Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-19 19:01 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger @ 2009-02-19 23:49 ` Jonathan Corbet 2009-02-20 8:39 ` Wolfgang Grandegger 2009-02-20 9:44 ` Patrick McHardy 2 siblings, 1 reply; 21+ messages in thread From: Jonathan Corbet @ 2009-02-19 23:49 UTC (permalink / raw) To: Wolfgang Grandegger Cc: netdev, linux-kernel, Wolfgang Grandegger, Oliver Hartkopp On Thu, 19 Feb 2009 20:01:17 +0100 Wolfgang Grandegger <wg@grandegger.com> wrote: > +/* > + * Bit-timing calculation derived from: > + * > + * Code based on LinCAN sources and H8S2638 project > + * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz > + * Copyright 2005 Stanislav Marek > + * email: pisa@cmp.felk.cvut.cz > + */ > +static int can_update_spt(const struct can_bittiming_const *btc, > + int sampl_pt, int tseg, int *tseg1, int *tseg2) > +{ > + *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; > + if (*tseg2 < btc->tseg2_min) > + *tseg2 = btc->tseg2_min; > + if (*tseg2 > btc->tseg2_max) > + *tseg2 = btc->tseg2_max; > + *tseg1 = tseg - *tseg2; > + if (*tseg1 > btc->tseg1_max) { > + *tseg1 = btc->tseg1_max; > + *tseg2 = tseg - *tseg1; > + } > + return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); > +} I can only assume that this calculation means something to somebody. I guess there's no hope for those of use too lazy to go read the Bosch spec, where, I assume, this kind of stuff is described. > +static int can_calc_bittiming(struct net_device *dev) This function, too, is pretty well impenetrable. I couldn't possibly try to tell you if it's even remotely correct. Some comments might be nice. > +/* > + * 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); This is the first mention I see of a lock in this file. What are your locking rules? There doesn't seem to be a lot of locking going on... In particular, nothing in this file uses 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); I do believe that function is *freeing* space...? The allocation function initializes a timer, but here we do nothing to ensure that the timer is not running. Is there a rule somewhere that I missed? > +/* > + * Local echo of CAN messages > + * > + * CAN network devices *should* support a local echo functionality > + * (see Documentation/networking/can.txt). To test the handling of CAN > + * interfaces that do not support the local echo both driver types are > + * implemented. In the case that the driver does not support the echo > + * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core > + * to perform the echo as a fallback solution. > + */ > + > +static void can_flush_echo_skb(struct net_device *dev) > +{ > + struct can_priv *priv = netdev_priv(dev); > + struct net_device_stats *stats = &dev->stats; > + int i; > + > + for (i = 0; i < CAN_ECHO_SKB_MAX; i++) { > + if (priv->echo_skb[i]) { > + kfree_skb(priv->echo_skb[i]); > + priv->echo_skb[i] = NULL; > + stats->tx_dropped++; > + stats->tx_aborted_errors++; > + } > + } > +} What lock is protecting priv->echo_skb? > +/* > + * Put the skb on the stack to be looped backed locally lateron > + * > + * The function is typically called in the start_xmit function > + * of the device driver. > + */ > +void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx) > +{ > + struct can_priv *priv = netdev_priv(dev); > + > + /* set flag whether this packet has to be looped back */ > + if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) { > + kfree_skb(skb); > + return; > + } The comment says "set flag", but the code potentially frees the skb and aborts. [...] > +/* > + * CAN device restart for bus-off recovery > + */ > +int can_restart_now(struct net_device *dev) > +{ > + struct can_priv *priv = netdev_priv(dev); > + struct net_device_stats *stats = &dev->stats; > + struct sk_buff *skb; > + struct can_frame *cf; > + int err; > + > + if (netif_carrier_ok(dev)) > + netif_carrier_off(dev); > + > + /* Cancel restart in progress */ > + if (priv->timer.expires) { > + del_timer(&priv->timer); > + priv->timer.expires = 0; /* mark inactive timer */ > + } Are you sure you don't want del_timer_sync() there? What keeps you from racing with the timer? [...] > +/* > + * Cleanup function before the device gets closed. > + * > + * This functions should be called in the close function of the device > + * driver. > + */ > +void can_close_cleanup(struct net_device *dev) > +{ > + struct can_priv *priv = netdev_priv(dev); > + > + if (priv->timer.expires) { > + del_timer(&priv->timer); > + priv->timer.expires = 0; > + } > + > + can_flush_echo_skb(dev); > +} > +EXPORT_SYMBOL_GPL(can_close_cleanup); You definitely want del_timer_sync() here. You could potentially return with the timer still running. > diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c > new file mode 100644 > index 0000000..746f641 > --- /dev/null > +++ b/drivers/net/can/sysfs.c Most of this file looks like typical sysfs cruft. Again, though, I wonder about your locking... > +/* use same locking rules as GIF* ioctl's */ > +static ssize_t can_dev_show(struct device *d, > + struct device_attribute *attr, char *buf, > + ssize_t (*fmt)(struct net_device *, char *)) > +{ > + struct net_device *dev = to_net_dev(d); > + ssize_t ret = -EINVAL; > + > + read_lock(&dev_base_lock); > + if (dev_isalive(dev)) > + ret = (*fmt)(dev, buf); > + read_unlock(&dev_base_lock); > + > + return ret; > +} I'm not sure what the GIF* ioctl() locking rules are, but do they really give you properly-serialized access to your device? dev_base_lock seems like an unlikely choice, somehow, but maybe I'm missing something. [...] > +/* use same locking and permission rules as SIF* ioctl's */ > +static ssize_t can_dev_store(struct device *d, struct device_attribute *attr, > + const char *buf, size_t len, > + int (*set)(struct net_device *, unsigned long)) > +{ > + struct net_device *dev = to_net_dev(d); > + unsigned long new; > + int ret = -EINVAL; > + > + if (!capable(CAP_NET_ADMIN)) > + return -EPERM; > + > + ret = strict_strtoul(buf, 0, &new); > + if (ret) > + goto out; > + > + rtnl_lock(); > + if (dev_isalive(dev)) { > + ret = (*set)(dev, new); > + if (!ret) > + ret = len; > + } > + rtnl_unlock(); > +out: > + return ret; > +} Here we're using a different (global) lock? Me confused... [...] > diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h > new file mode 100644 > index 0000000..e21f2fa > --- /dev/null > +++ b/drivers/net/can/sysfs.h > @@ -0,0 +1,24 @@ > +/* > + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the version 2 of the GNU General Public License > + * as published by the Free Software Foundation > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#ifndef CAN_SYSFS_H > +#define CAN_SYSFS_H > + > +void can_create_sysfs(struct net_device *dev); > +void can_remove_sysfs(struct net_device *dev); > + > +#endif /* CAN_SYSFS_H */ > diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h > new file mode 100644 > index 0000000..078ac03 > --- /dev/null > +++ b/include/linux/can/dev.h > @@ -0,0 +1,136 @@ > +/* > + * linux/can/dev.h > + * > + * Definitions for the CAN network device driver interface > + * > + * Copyright (C) 2006 Andrey Volkov <avolkov@varma-el.com> > + * Varma Electronics Oy > + * > + * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com> Nothing in 2009? :) > + * > + * Send feedback to <socketcan-users@lists.berlios.de> > + */ > + > +#ifndef CAN_DEV_H > +#define CAN_DEV_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; > +}; It sure would be nice (again) to know what all these parameters are. > +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; > +}; Ditto. [...] > +/* > + * CAN common private data > + */ > +#define CAN_ECHO_SKB_MAX 4 > + > +struct can_priv { > + struct can_device_stats can_stats; > + > + struct can_bittiming bittiming; > + struct can_bittiming_const *bittiming_const; > + > + spinlock_t irq_lock; ...protecting what...? > + enum can_state state; > + u32 ctrlmode; > + > + int restart_ms; > + struct timer_list 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); > +}; > + > +#define ND2D(_ndev) (_ndev->dev.parent) Does this macro really improve the clarity of the code? ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-19 23:49 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Jonathan Corbet @ 2009-02-20 8:39 ` Wolfgang Grandegger 0 siblings, 0 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-20 8:39 UTC (permalink / raw) To: Jonathan Corbet; +Cc: netdev, linux-kernel, Oliver Hartkopp Jonathan Corbet wrote: > On Thu, 19 Feb 2009 20:01:17 +0100 > Wolfgang Grandegger <wg@grandegger.com> wrote: > >> +/* >> + * Bit-timing calculation derived from: >> + * >> + * Code based on LinCAN sources and H8S2638 project >> + * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz >> + * Copyright 2005 Stanislav Marek >> + * email: pisa@cmp.felk.cvut.cz >> + */ >> +static int can_update_spt(const struct can_bittiming_const *btc, >> + int sampl_pt, int tseg, int *tseg1, int *tseg2) >> +{ >> + *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; >> + if (*tseg2 < btc->tseg2_min) >> + *tseg2 = btc->tseg2_min; >> + if (*tseg2 > btc->tseg2_max) >> + *tseg2 = btc->tseg2_max; >> + *tseg1 = tseg - *tseg2; >> + if (*tseg1 > btc->tseg1_max) { >> + *tseg1 = btc->tseg1_max; >> + *tseg2 = tseg - *tseg1; >> + } >> + return 1000 * (tseg + 1 - *tseg2) / (tseg + 1); >> +} > > I can only assume that this calculation means something to somebody. I > guess there's no hope for those of use too lazy to go read the Bosch spec, > where, I assume, this kind of stuff is described. The purpose and quirks of this bit-timing calculation are described briefly in the Kconfig entry for CONFIG_CAN_CALC_BITTIMING and in Documentation/networking/can.txt which I quote below: "6.5.2 Setting the CAN bit-timing The CAN bit-timing parameters can always be specified in a hardware independent format as proposed in the Bosch CAN 2.0 specification using the SYSFS files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". The SYSFS files are described in the previous chapter. If the kernel option CONFIG_CAN_CALC_BITTIMING is enabled, CIA recommended CAN bit-timing parameters will be calculated for the bit- rate written to the SYSFS file "bitrate" when the device gets started. Note that this works fine for the most common CAN controllers with standard bit-rates but may *fail* for exotic bit-rates or CAN source clock frequencies. Disabling 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 available in the SYSFS directory "can_bittiming" as well with the name prefix "hw_"." The user just sets the bit-rate and this function will determine proper bit-timing parameters. >> +static int can_calc_bittiming(struct net_device *dev) > > This function, too, is pretty well impenetrable. I couldn't possibly try > to tell you if it's even remotely correct. Some comments might be nice. Unfortunately, there are no simple speed settings like 100 bps FD for Ethernet. Setting proper CAN bit-timing might be tricky and in rare cases you even may need the help of a real CAN expert, including myself :-(. Furthermore, setting the hardware bit-timing is very hardware *dependent* and we tried to address this issue using a hardware *independent* format as proposed by the Bosch specification. Well, maybe I should document our approach in more detail. >> +/* >> + * 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); > > This is the first mention I see of a lock in this file. What are your > locking rules? There doesn't seem to be a lot of locking going on... In > particular, nothing in this file uses irq_lock. I see, the locking needs some more careful analysis. Thanks for pointing that out. > >> + 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); > > I do believe that function is *freeing* space...? Of course, will fix. > The allocation function initializes a timer, but here we do nothing to > ensure that the timer is not running. Is there a rule somewhere that I > missed? If the device is freed when its still open, the network layer calls the close function of the device, which will do to proper cleanup including deleting the timer. >> +/* >> + * Local echo of CAN messages >> + * >> + * CAN network devices *should* support a local echo functionality >> + * (see Documentation/networking/can.txt). To test the handling of CAN >> + * interfaces that do not support the local echo both driver types are >> + * implemented. In the case that the driver does not support the echo >> + * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core >> + * to perform the echo as a fallback solution. >> + */ >> + >> +static void can_flush_echo_skb(struct net_device *dev) >> +{ >> + struct can_priv *priv = netdev_priv(dev); >> + struct net_device_stats *stats = &dev->stats; >> + int i; >> + >> + for (i = 0; i < CAN_ECHO_SKB_MAX; i++) { >> + if (priv->echo_skb[i]) { >> + kfree_skb(priv->echo_skb[i]); >> + priv->echo_skb[i] = NULL; >> + stats->tx_dropped++; >> + stats->tx_aborted_errors++; >> + } >> + } >> +} > > What lock is protecting priv->echo_skb? This code is called when the device is stopped, but I need to double-check. > >> +/* >> + * Put the skb on the stack to be looped backed locally lateron >> + * >> + * The function is typically called in the start_xmit function >> + * of the device driver. >> + */ >> +void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx) >> +{ >> + struct can_priv *priv = netdev_priv(dev); >> + >> + /* set flag whether this packet has to be looped back */ >> + if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) { >> + kfree_skb(skb); >> + return; >> + } > > The comment says "set flag", but the code potentially frees the skb and aborts. Will fix the comment. > [...] > >> +/* >> + * CAN device restart for bus-off recovery >> + */ >> +int can_restart_now(struct net_device *dev) >> +{ >> + struct can_priv *priv = netdev_priv(dev); >> + struct net_device_stats *stats = &dev->stats; >> + struct sk_buff *skb; >> + struct can_frame *cf; >> + int err; >> + >> + if (netif_carrier_ok(dev)) >> + netif_carrier_off(dev); >> + >> + /* Cancel restart in progress */ >> + if (priv->timer.expires) { >> + del_timer(&priv->timer); >> + priv->timer.expires = 0; /* mark inactive timer */ >> + } > > Are you sure you don't want del_timer_sync() there? What keeps you from > racing with the timer? Right, will fix. > [...] > >> +/* >> + * Cleanup function before the device gets closed. >> + * >> + * This functions should be called in the close function of the device >> + * driver. >> + */ >> +void can_close_cleanup(struct net_device *dev) >> +{ >> + struct can_priv *priv = netdev_priv(dev); >> + >> + if (priv->timer.expires) { >> + del_timer(&priv->timer); >> + priv->timer.expires = 0; >> + } >> + >> + can_flush_echo_skb(dev); >> +} >> +EXPORT_SYMBOL_GPL(can_close_cleanup); > > You definitely want del_timer_sync() here. You could potentially return > with the timer still running. > >> diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c >> new file mode 100644 >> index 0000000..746f641 >> --- /dev/null >> +++ b/drivers/net/can/sysfs.c > > Most of this file looks like typical sysfs cruft. Again, though, I wonder > about your locking... > >> +/* use same locking rules as GIF* ioctl's */ >> +static ssize_t can_dev_show(struct device *d, >> + struct device_attribute *attr, char *buf, >> + ssize_t (*fmt)(struct net_device *, char *)) >> +{ >> + struct net_device *dev = to_net_dev(d); >> + ssize_t ret = -EINVAL; >> + >> + read_lock(&dev_base_lock); >> + if (dev_isalive(dev)) >> + ret = (*fmt)(dev, buf); >> + read_unlock(&dev_base_lock); >> + >> + return ret; >> +} > > I'm not sure what the GIF* ioctl() locking rules are, but do they really > give you properly-serialized access to your device? dev_base_lock seems > like an unlikely choice, somehow, but maybe I'm missing something. This locking code is from http://lxr.linux.no/linux+v2.6.28.6/net/core/net-sysfs.c#L34 accessing SYSFS files for the network device in a similar way. > [...] > >> +/* use same locking and permission rules as SIF* ioctl's */ >> +static ssize_t can_dev_store(struct device *d, struct device_attribute *attr, >> + const char *buf, size_t len, >> + int (*set)(struct net_device *, unsigned long)) >> +{ >> + struct net_device *dev = to_net_dev(d); >> + unsigned long new; >> + int ret = -EINVAL; >> + >> + if (!capable(CAP_NET_ADMIN)) >> + return -EPERM; >> + >> + ret = strict_strtoul(buf, 0, &new); >> + if (ret) >> + goto out; >> + >> + rtnl_lock(); >> + if (dev_isalive(dev)) { >> + ret = (*set)(dev, new); >> + if (!ret) >> + ret = len; >> + } >> + rtnl_unlock(); >> +out: >> + return ret; >> +} > > Here we're using a different (global) lock? Me confused... See http://lxr.linux.no/linux+v2.6.28.6/net/core/net-sysfs.c#L63 > > [...] > >> diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h >> new file mode 100644 >> index 0000000..e21f2fa >> --- /dev/null >> +++ b/drivers/net/can/sysfs.h >> @@ -0,0 +1,24 @@ >> +/* >> + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the version 2 of the GNU General Public License >> + * as published by the Free Software Foundation >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA >> + */ >> + >> +#ifndef CAN_SYSFS_H >> +#define CAN_SYSFS_H >> + >> +void can_create_sysfs(struct net_device *dev); >> +void can_remove_sysfs(struct net_device *dev); >> + >> +#endif /* CAN_SYSFS_H */ >> diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h >> new file mode 100644 >> index 0000000..078ac03 >> --- /dev/null >> +++ b/include/linux/can/dev.h >> @@ -0,0 +1,136 @@ >> +/* >> + * linux/can/dev.h >> + * >> + * Definitions for the CAN network device driver interface >> + * >> + * Copyright (C) 2006 Andrey Volkov <avolkov@varma-el.com> >> + * Varma Electronics Oy >> + * >> + * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com> > > Nothing in 2009? :) Will fix. > >> + * >> + * Send feedback to <socketcan-users@lists.berlios.de> >> + */ >> + >> +#ifndef CAN_DEV_H >> +#define CAN_DEV_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; >> +}; > > It sure would be nice (again) to know what all these parameters are. > >> +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; >> +}; > > Ditto. OK, will add some documentation. > [...] > >> +/* >> + * CAN common private data >> + */ >> +#define CAN_ECHO_SKB_MAX 4 >> + >> +struct can_priv { >> + struct can_device_stats can_stats; >> + >> + struct can_bittiming bittiming; >> + struct can_bittiming_const *bittiming_const; >> + >> + spinlock_t irq_lock; > > ...protecting what...? This is not OK, indeed. It is currently just used by the SJA1000 driver. Either it can be moved to the SJA1000 private struct or it is required for some missing locking. I will check. >> + enum can_state state; >> + u32 ctrlmode; >> + >> + int restart_ms; >> + struct timer_list 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); >> +}; >> + >> +#define ND2D(_ndev) (_ndev->dev.parent) > > Does this macro really improve the clarity of the code? IMO, yes, but a more descriptive name should be used, e.g. NDEV2DEV. Thanks, Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-19 19:01 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger 2009-02-19 23:49 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Jonathan Corbet @ 2009-02-20 9:44 ` Patrick McHardy 2009-02-21 15:09 ` Wolfgang Grandegger 2 siblings, 1 reply; 21+ messages in thread From: Patrick McHardy @ 2009-02-20 9:44 UTC (permalink / raw) To: Wolfgang Grandegger; +Cc: netdev, linux-kernel, Oliver Hartkopp Wolfgang Grandegger wrote: > The CAN network device driver interface provides a generic interface to > setup, configure and monitor CAN network devices. It exports a set of > common data structures and 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. > > Furthermore adds a SYSFS interface to set and get CAN device properties. > When the CAN device is registered, a set of SYSFS files is created in > "/sys/class/net/canX/". These files allow to set and get device > properties like bit-timing parameters, state, controller mode and CAN > statistics. Is sysfs really the best choice for this? It has the usual problems, you can't change multiple parameters at once atomically. Now, I don't know whether this is actually (and currently) a problem for CAN, but given that we already have a generic netlink interface for creating network interfaces that can easily support this, what are the reasons for adding a new interface instead of using the existing one? ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-20 9:44 ` Patrick McHardy @ 2009-02-21 15:09 ` Wolfgang Grandegger 2009-02-23 9:55 ` Patrick McHardy 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-21 15:09 UTC (permalink / raw) To: Patrick McHardy; +Cc: netdev, linux-kernel, Oliver Hartkopp Patrick McHardy wrote: > Wolfgang Grandegger wrote: >> The CAN network device driver interface provides a generic interface to >> setup, configure and monitor CAN network devices. It exports a set of >> common data structures and 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. >> >> Furthermore adds a SYSFS interface to set and get CAN device properties. >> When the CAN device is registered, a set of SYSFS files is created in >> "/sys/class/net/canX/". These files allow to set and get device >> properties like bit-timing parameters, state, controller mode and CAN >> statistics. > > Is sysfs really the best choice for this? It has the usual problems, > you can't change multiple parameters at once atomically. Now, I don't > know whether this is actually (and currently) a problem for CAN, but > given that we already have a generic netlink interface for creating > network interfaces that can easily support this, what are the reasons > for adding a new interface instead of using the existing one? You refer to the API described at http://lwn.net/Articles/236919/, I assume. Well, I know that netlink is used to define routes in the kernel or VLANs but I was not aware that it can also be used to configure real network devices. What a pity. At a first glance, I could also be used for CAN, of course. There are the following requirements: 1. Set and get CAN device properties like bit-rate and controller mode. 2. Show CAN bus state (active, error-passive or bus-off). 3. Get device constants like clock frequency and bit-timing parameters. 4. Dump CAN device statistics. 5. Trigger device restart if CAN bus-off state has been detected. Not sure yet, if the interface if good for all purposes. Especially point 5. might be better handled by an IOCTL. And we need a user space tool, e.g. canconfig, to handle the user requests and communicate with the kernel side. We will follow that promising route. Any further help or link is appreciated. Thanks. Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-21 15:09 ` Wolfgang Grandegger @ 2009-02-23 9:55 ` Patrick McHardy 2009-02-23 14:57 ` Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Patrick McHardy @ 2009-02-23 9:55 UTC (permalink / raw) To: Wolfgang Grandegger; +Cc: netdev, linux-kernel, Oliver Hartkopp Wolfgang Grandegger wrote: > Patrick McHardy wrote: >> Is sysfs really the best choice for this? It has the usual problems, >> you can't change multiple parameters at once atomically. Now, I don't >> know whether this is actually (and currently) a problem for CAN, but >> given that we already have a generic netlink interface for creating >> network interfaces that can easily support this, what are the reasons >> for adding a new interface instead of using the existing one? > > You refer to the API described at http://lwn.net/Articles/236919/, I > assume. Exactly. > Well, I know that netlink is used to define routes in the kernel > or VLANs but I was not aware that it can also be used to configure real > network devices. What a pity. At a first glance, I could also be used > for CAN, of course. There are the following requirements: > > 1. Set and get CAN device properties like bit-rate and controller mode. > 2. Show CAN bus state (active, error-passive or bus-off). > 3. Get device constants like clock frequency and bit-timing parameters. > 4. Dump CAN device statistics. > 5. Trigger device restart if CAN bus-off state has been detected. > > Not sure yet, if the interface if good for all purposes. Especially > point 5. might be better handled by an IOCTL. The first points sound pretty regular. The last one - just triggering it is no problem of course, but since I don't know how the detection works, I can't really tell whether its suitable. > And we need a user space > tool, e.g. canconfig, to handle the user requests and communicate with > the kernel side. Thats pretty standard :) > We will follow that promising route. Any further help > or link is appreciated. Thanks. It should be quite easy to move your interface on top. A few examples that might be useful are vlan, ip_gre and macvlan. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-23 9:55 ` Patrick McHardy @ 2009-02-23 14:57 ` Wolfgang Grandegger 2009-02-24 9:38 ` Patrick McHardy 0 siblings, 1 reply; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-23 14:57 UTC (permalink / raw) To: Patrick McHardy; +Cc: netdev, linux-kernel, Oliver Hartkopp Patrick McHardy wrote: > Wolfgang Grandegger wrote: >> Patrick McHardy wrote: >>> Is sysfs really the best choice for this? It has the usual problems, >>> you can't change multiple parameters at once atomically. Now, I don't >>> know whether this is actually (and currently) a problem for CAN, but >>> given that we already have a generic netlink interface for creating >>> network interfaces that can easily support this, what are the reasons >>> for adding a new interface instead of using the existing one? >> >> You refer to the API described at http://lwn.net/Articles/236919/, I >> assume. > > Exactly. > >> Well, I know that netlink is used to define routes in the kernel >> or VLANs but I was not aware that it can also be used to configure real >> network devices. What a pity. At a first glance, I could also be used >> for CAN, of course. There are the following requirements: >> >> 1. Set and get CAN device properties like bit-rate and controller mode. >> 2. Show CAN bus state (active, error-passive or bus-off). >> 3. Get device constants like clock frequency and bit-timing parameters. >> 4. Dump CAN device statistics. >> 5. Trigger device restart if CAN bus-off state has been detected. >> >> Not sure yet, if the interface if good for all purposes. Especially >> point 5. might be better handled by an IOCTL. > > The first points sound pretty regular. The last one - just triggering > it is no problem of course, but since I don't know how the detection > works, I can't really tell whether its suitable. CAN errors and state changes might be delivered as CAN error messages to the receiving socket/application, like normal messages. When the application realizes the state change to CAN bus-off, it may want to trigger a bus-off recovery (controller restart). The CAN controller HW enters bus-off, when too much errors occurred on the bus. No more messages can then be sent of received and therefore the driver calls netif_carrier_off(). Is there already an interface for the user to restart? An appropriate IOCTL request would be a simply option, but they are generally deprecated, AFAIK. Using netlink would be more cumbersome, as with SYSFS. >> And we need a user space >> tool, e.g. canconfig, to handle the user requests and communicate with >> the kernel side. > > Thats pretty standard :) For the iproute2 utility "ip", mainliy a netlink_can.c would be required. But a dedicated tool for CAN seems more appropriate to me. >> We will follow that promising route. Any further help >> or link is appreciated. Thanks. > > It should be quite easy to move your interface on top. A few examples > that might be useful are vlan, ip_gre and macvlan. Also the drivers dummy and ipb are quite useful. Using netlink seems straight-forward, indeed, but it requires still some effort. Hope to find some time soon. Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-23 14:57 ` Wolfgang Grandegger @ 2009-02-24 9:38 ` Patrick McHardy 2009-02-24 16:29 ` Wolfgang Grandegger 0 siblings, 1 reply; 21+ messages in thread From: Patrick McHardy @ 2009-02-24 9:38 UTC (permalink / raw) To: Wolfgang Grandegger; +Cc: netdev, linux-kernel, Oliver Hartkopp Wolfgang Grandegger wrote: >>> 1. Set and get CAN device properties like bit-rate and controller mode. >>> 2. Show CAN bus state (active, error-passive or bus-off). >>> 3. Get device constants like clock frequency and bit-timing parameters. >>> 4. Dump CAN device statistics. >>> 5. Trigger device restart if CAN bus-off state has been detected. >>> >>> Not sure yet, if the interface if good for all purposes. Especially >>> point 5. might be better handled by an IOCTL. >> The first points sound pretty regular. The last one - just triggering >> it is no problem of course, but since I don't know how the detection >> works, I can't really tell whether its suitable. > > CAN errors and state changes might be delivered as CAN error messages to > the receiving socket/application, like normal messages. When the > application realizes the state change to CAN bus-off, it may want to > trigger a bus-off recovery (controller restart). The CAN controller HW > enters bus-off, when too much errors occurred on the bus. No more > messages can then be sent of received and therefore the driver calls > netif_carrier_off(). Is there already an interface for the user to > restart? An appropriate IOCTL request would be a simply option, but they > are generally deprecated, AFAIK. Using netlink would be more cumbersome, > as with SYSFS. You could consider triggering the restart automatically, similar to the netdev watchdog. In fact, you could simply use that one. If you really want to trigger this from userspace, setting the device DOWN and UP again seems like the most appropriate way to me (even though not very nice to use from an application POV). >>> And we need a user space >>> tool, e.g. canconfig, to handle the user requests and communicate with >>> the kernel side. >> Thats pretty standard :) > > For the iproute2 utility "ip", mainliy a netlink_can.c would be > required. But a dedicated tool for CAN seems more appropriate to me. I don't know the CAN specifics. One goal of the rtnl_link API was to reduce to amount of userspace tools for link configuration though, so please only create a new tool if there's really a good reason. From my POV it would be preferrable to have link configuration in iproute for direct use by users and possibly also support in libnl for use by other applications. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/8] can: CAN Network device driver and SYSFS interface 2009-02-24 9:38 ` Patrick McHardy @ 2009-02-24 16:29 ` Wolfgang Grandegger 0 siblings, 0 replies; 21+ messages in thread From: Wolfgang Grandegger @ 2009-02-24 16:29 UTC (permalink / raw) To: Patrick McHardy; +Cc: netdev, linux-kernel, Oliver Hartkopp Patrick McHardy wrote: > Wolfgang Grandegger wrote: >>>> 1. Set and get CAN device properties like bit-rate and controller mode. >>>> 2. Show CAN bus state (active, error-passive or bus-off). >>>> 3. Get device constants like clock frequency and bit-timing parameters. >>>> 4. Dump CAN device statistics. >>>> 5. Trigger device restart if CAN bus-off state has been detected. >>>> >>>> Not sure yet, if the interface if good for all purposes. Especially >>>> point 5. might be better handled by an IOCTL. >>> The first points sound pretty regular. The last one - just triggering >>> it is no problem of course, but since I don't know how the detection >>> works, I can't really tell whether its suitable. >> >> CAN errors and state changes might be delivered as CAN error messages to >> the receiving socket/application, like normal messages. When the >> application realizes the state change to CAN bus-off, it may want to >> trigger a bus-off recovery (controller restart). The CAN controller HW >> enters bus-off, when too much errors occurred on the bus. No more >> messages can then be sent of received and therefore the driver calls >> netif_carrier_off(). Is there already an interface for the user to >> restart? An appropriate IOCTL request would be a simply option, but they >> are generally deprecated, AFAIK. Using netlink would be more cumbersome, >> as with SYSFS. > > You could consider triggering the restart automatically, similar > to the netdev watchdog. In fact, you could simply use that one. Automatic restart is already support by this patch series but we do not use the watchdog, because it would trigger on normal transmit timeouts as well. But maybe we can suppress them by fiddling with the watchdog's timeout value. > If you really want to trigger this from userspace, setting the > device DOWN and UP again seems like the most appropriate way to > me (even though not very nice to use from an application POV). That's an option, but it might be a bit heavy, though. >>>> And we need a user space >>>> tool, e.g. canconfig, to handle the user requests and communicate with >>>> the kernel side. >>> Thats pretty standard :) >> >> For the iproute2 utility "ip", mainliy a netlink_can.c would be >> required. But a dedicated tool for CAN seems more appropriate to me. > > I don't know the CAN specifics. One goal of the rtnl_link API > was to reduce to amount of userspace tools for link configuration > though, so please only create a new tool if there's really a good > reason. From my POV it would be preferrable to have link configuration > in iproute for direct use by users and possibly also support in > libnl for use by other applications. OK, it also depends on what we would like the tool finally to perform, apart from setting and getting device properties. On idea is to move the rather heavy calculation of bit-timing parameters out of the kernel to the user-space configuration tool. Wolfgang. ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2009-05-01 18:21 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-02-19 19:01 [PATCH 0/8] can: CAN network device driver interface and drivers Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 1/8] can: Documentation for the CAN device driver interface Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 2/8] can: Update MAINTAINERS and CREDITS file Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 5/8] can: SJA1000 generic platform bus driver Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 6/8] can: SJA1000 driver for EMS PCI cards Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 7/8] can: SJA1000 driver for Kvaser " Wolfgang Grandegger 2009-02-19 19:01 ` [PATCH 8/8] can: Driver for the Freescale MSCAN controller Wolfgang Grandegger 2009-02-20 0:14 ` [PATCH 4/8] can: Driver for the SJA1000 CAN controller Jonathan Corbet 2009-02-20 8:46 ` David Miller 2009-02-20 9:35 ` Wolfgang Grandegger 2009-05-01 18:21 ` Wolfgang Grandegger 2009-02-19 23:49 ` [PATCH 3/8] can: CAN Network device driver and SYSFS interface Jonathan Corbet 2009-02-20 8:39 ` Wolfgang Grandegger 2009-02-20 9:44 ` Patrick McHardy 2009-02-21 15:09 ` Wolfgang Grandegger 2009-02-23 9:55 ` Patrick McHardy 2009-02-23 14:57 ` Wolfgang Grandegger 2009-02-24 9:38 ` Patrick McHardy 2009-02-24 16:29 ` Wolfgang Grandegger
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).