Merge tag 'linux-can-next-for-4.13-20170404' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next
Marc Kleine-Budde says:
====================
pull-request: can-next 2017-03-03
this is a pull request of 5 patches for net-next/master.
There are two patches by Yegor Yefremov which convert the ti_hecc
driver into a DT only driver, as there is no in-tree user of the old
platform driver interface anymore. The next patch by Mario Kicherer
adds network namespace support to the can subsystem. The last two
patches by Akshay Bhat add support for the holt_hi311x SPI CAN driver.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/Documentation/devicetree/bindings/net/can/holt_hi311x.txt b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
new file mode 100644
index 0000000..23aa94e
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
@@ -0,0 +1,24 @@
+* Holt HI-311X stand-alone CAN controller device tree bindings
+
+Required properties:
+ - compatible: Should be one of the following:
+ - "holt,hi3110" for HI-3110
+ - reg: SPI chip select.
+ - clocks: The clock feeding the CAN controller.
+ - interrupt-parent: The parent interrupt controller.
+ - interrupts: Should contain IRQ line for the CAN controller.
+
+Optional properties:
+ - vdd-supply: Regulator that powers the CAN controller.
+ - xceiver-supply: Regulator that powers the CAN transceiver.
+
+Example:
+ can0: can@1 {
+ compatible = "holt,hi3110";
+ reg = <1>;
+ clocks = <&clk32m>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <13 IRQ_TYPE_EDGE_RISING>;
+ vdd-supply = <®5v0>;
+ xceiver-supply = <®5v0>;
+ };
diff --git a/Documentation/devicetree/bindings/net/can/ti_hecc.txt b/Documentation/devicetree/bindings/net/can/ti_hecc.txt
new file mode 100644
index 0000000..e0f0a7c
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/ti_hecc.txt
@@ -0,0 +1,32 @@
+Texas Instruments High End CAN Controller (HECC)
+================================================
+
+This file provides information, what the device node
+for the hecc interface contains.
+
+Required properties:
+- compatible: "ti,am3517-hecc"
+- reg: addresses and lengths of the register spaces for 'hecc', 'hecc-ram'
+ and 'mbx'
+- reg-names :"hecc", "hecc-ram", "mbx"
+- interrupts: interrupt mapping for the hecc interrupts sources
+- clocks: clock phandles (see clock bindings for details)
+
+Optional properties:
+- ti,use-hecc1int: if provided configures HECC to produce all interrupts
+ on HECC1INT interrupt line. By default HECC0INT interrupt
+ line will be used.
+- xceiver-supply: regulator that powers the CAN transceiver
+
+Example:
+
+For am3517evm board:
+ hecc: can@5c050000 {
+ compatible = "ti,am3517-hecc";
+ reg = <0x5c050000 0x80>,
+ <0x5c053000 0x180>,
+ <0x5c052000 0x200>;
+ reg-names = "hecc", "hecc-ram", "mbx";
+ interrupts = <24>;
+ clocks = <&hecc_ck>;
+ };
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 148cae5..8f2e0dd 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -1,6 +1,12 @@
menu "CAN SPI interfaces"
depends on SPI
+config CAN_HI311X
+ tristate "Holt HI311x SPI CAN controllers"
+ depends on CAN_DEV && SPI && HAS_DMA
+ ---help---
+ Driver for the Holt HI311x SPI CAN controllers.
+
config CAN_MCP251X
tristate "Microchip MCP251x SPI CAN controllers"
depends on HAS_DMA
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index 0e86040..f59fa37 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -3,4 +3,5 @@
#
+obj-$(CONFIG_CAN_HI311X) += hi311x.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
new file mode 100644
index 0000000..5590c55
--- /dev/null
+++ b/drivers/net/can/spi/hi311x.c
@@ -0,0 +1,1076 @@
+/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface
+ *
+ * Copyright(C) Timesys Corporation 2016
+ *
+ * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver
+ * Copyright 2009 Christian Pellegrin EVOL S.r.l.
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+
+#define HI3110_MASTER_RESET 0x56
+#define HI3110_READ_CTRL0 0xD2
+#define HI3110_READ_CTRL1 0xD4
+#define HI3110_READ_STATF 0xE2
+#define HI3110_WRITE_CTRL0 0x14
+#define HI3110_WRITE_CTRL1 0x16
+#define HI3110_WRITE_INTE 0x1C
+#define HI3110_WRITE_BTR0 0x18
+#define HI3110_WRITE_BTR1 0x1A
+#define HI3110_READ_BTR0 0xD6
+#define HI3110_READ_BTR1 0xD8
+#define HI3110_READ_INTF 0xDE
+#define HI3110_READ_ERR 0xDC
+#define HI3110_READ_FIFO_WOTIME 0x48
+#define HI3110_WRITE_FIFO 0x12
+#define HI3110_READ_MESSTAT 0xDA
+#define HI3110_READ_REC 0xEA
+#define HI3110_READ_TEC 0xEC
+
+#define HI3110_CTRL0_MODE_MASK (7 << 5)
+#define HI3110_CTRL0_NORMAL_MODE (0 << 5)
+#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5)
+#define HI3110_CTRL0_MONITOR_MODE (2 << 5)
+#define HI3110_CTRL0_SLEEP_MODE (3 << 5)
+#define HI3110_CTRL0_INIT_MODE (4 << 5)
+
+#define HI3110_CTRL1_TXEN BIT(7)
+
+#define HI3110_INT_RXTMP BIT(7)
+#define HI3110_INT_RXFIFO BIT(6)
+#define HI3110_INT_TXCPLT BIT(5)
+#define HI3110_INT_BUSERR BIT(4)
+#define HI3110_INT_MCHG BIT(3)
+#define HI3110_INT_WAKEUP BIT(2)
+#define HI3110_INT_F1MESS BIT(1)
+#define HI3110_INT_F0MESS BIT(0)
+
+#define HI3110_ERR_BUSOFF BIT(7)
+#define HI3110_ERR_TXERRP BIT(6)
+#define HI3110_ERR_RXERRP BIT(5)
+#define HI3110_ERR_BITERR BIT(4)
+#define HI3110_ERR_FRMERR BIT(3)
+#define HI3110_ERR_CRCERR BIT(2)
+#define HI3110_ERR_ACKERR BIT(1)
+#define HI3110_ERR_STUFERR BIT(0)
+#define HI3110_ERR_PROTOCOL_MASK (0x1F)
+#define HI3110_ERR_PASSIVE_MASK (0x60)
+
+#define HI3110_STAT_RXFMTY BIT(1)
+#define HI3110_STAT_BUSOFF BIT(2)
+#define HI3110_STAT_ERRP BIT(3)
+#define HI3110_STAT_ERRW BIT(4)
+
+#define HI3110_BTR0_SJW_SHIFT 6
+#define HI3110_BTR0_BRP_SHIFT 0
+
+#define HI3110_BTR1_SAMP_3PERBIT (1 << 7)
+#define HI3110_BTR1_SAMP_1PERBIT (0 << 7)
+#define HI3110_BTR1_TSEG2_SHIFT 4
+#define HI3110_BTR1_TSEG1_SHIFT 0
+
+#define HI3110_FIFO_WOTIME_TAG_OFF 0
+#define HI3110_FIFO_WOTIME_ID_OFF 1
+#define HI3110_FIFO_WOTIME_DLC_OFF 5
+#define HI3110_FIFO_WOTIME_DAT_OFF 6
+
+#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7)
+#define HI3110_FIFO_WOTIME_ID_RTR BIT(0)
+
+#define HI3110_FIFO_TAG_OFF 0
+#define HI3110_FIFO_ID_OFF 1
+#define HI3110_FIFO_STD_DLC_OFF 3
+#define HI3110_FIFO_STD_DATA_OFF 4
+#define HI3110_FIFO_EXT_DLC_OFF 5
+#define HI3110_FIFO_EXT_DATA_OFF 6
+
+#define HI3110_CAN_MAX_DATA_LEN 8
+#define HI3110_RX_BUF_LEN 15
+#define HI3110_TX_STD_BUF_LEN 12
+#define HI3110_TX_EXT_BUF_LEN 14
+#define HI3110_CAN_FRAME_MAX_BITS 128
+#define HI3110_EFF_FLAGS 0x18 /* IDE + SRR */
+
+#define HI3110_TX_ECHO_SKB_MAX 1
+
+#define HI3110_OST_DELAY_MS (10)
+
+#define DEVICE_NAME "hi3110"
+
+static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */
+module_param(hi3110_enable_dma, int, 0444);
+MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)");
+
+static const struct can_bittiming_const hi3110_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+enum hi3110_model {
+ CAN_HI3110_HI3110 = 0x3110,
+};
+
+struct hi3110_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+ enum hi3110_model model;
+
+ struct mutex hi3110_lock; /* SPI device lock */
+
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct restart_work;
+
+ int force_quit;
+ int after_suspend;
+#define HI3110_AFTER_SUSPEND_UP 1
+#define HI3110_AFTER_SUSPEND_DOWN 2
+#define HI3110_AFTER_SUSPEND_POWER 4
+#define HI3110_AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+ struct regulator *power;
+ struct regulator *transceiver;
+ struct clk *clk;
+};
+
+static void hi3110_clean(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+
+ if (priv->tx_skb || priv->tx_len)
+ net->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ if (priv->tx_len)
+ can_free_echo_skb(priv->net, 0);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+}
+
+/* Note about handling of error return of hi3110_spi_trans: accessing
+ * registers via SPI is not really different conceptually than using
+ * normal I/O assembler instructions, although it's much more
+ * complicated from a practical POV. So it's not advisable to always
+ * check the return value of this function. Imagine that every
+ * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
+ * error();", it would be a great mess (well there are some situation
+ * when exception handling C++ like could be useful after all). So we
+ * just check that transfers are OK at the beginning of our
+ * conversation with the chip and to avoid doing really nasty things
+ * (like injecting bogus packets in the network stack).
+ */
+static int hi3110_spi_trans(struct spi_device *spi, int len)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = len,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ spi_message_init(&m);
+
+ if (hi3110_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret)
+ dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);
+ return ret;
+}
+
+static u8 hi3110_cmd(struct spi_device *spi, u8 command)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = command;
+ dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command);
+
+ return hi3110_spi_trans(spi, 1);
+}
+
+static u8 hi3110_read(struct spi_device *spi, u8 command)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ u8 val = 0;
+
+ priv->spi_tx_buf[0] = command;
+ hi3110_spi_trans(spi, 2);
+ val = priv->spi_rx_buf[1];
+
+ return val;
+}
+
+static void hi3110_write(struct spi_device *spi, u8 reg, u8 val)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = reg;
+ priv->spi_tx_buf[1] = val;
+ hi3110_spi_trans(spi, 2);
+}
+
+static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = HI3110_WRITE_FIFO;
+ memcpy(priv->spi_tx_buf + 1, buf, len);
+ hi3110_spi_trans(spi, len + 1);
+}
+
+static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame)
+{
+ u8 buf[HI3110_TX_EXT_BUF_LEN];
+
+ buf[HI3110_FIFO_TAG_OFF] = 0;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ /* Extended frame */
+ buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21;
+ buf[HI3110_FIFO_ID_OFF + 1] =
+ (((frame->can_id & CAN_EFF_MASK) >> 13) & 0xe0) |
+ HI3110_EFF_FLAGS |
+ (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07);
+ buf[HI3110_FIFO_ID_OFF + 2] =
+ (frame->can_id & CAN_EFF_MASK) >> 7;
+ buf[HI3110_FIFO_ID_OFF + 3] =
+ ((frame->can_id & CAN_EFF_MASK) << 1) |
+ ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0);
+
+ buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc;
+
+ memcpy(buf + HI3110_FIFO_EXT_DATA_OFF,
+ frame->data, frame->can_dlc);
+
+ hi3110_hw_tx_frame(spi, buf, HI3110_TX_EXT_BUF_LEN -
+ (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc));
+ } else {
+ /* Standard frame */
+ buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3;
+ buf[HI3110_FIFO_ID_OFF + 1] =
+ ((frame->can_id & CAN_SFF_MASK) << 5) |
+ ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0);
+
+ buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc;
+
+ memcpy(buf + HI3110_FIFO_STD_DATA_OFF,
+ frame->data, frame->can_dlc);
+
+ hi3110_hw_tx_frame(spi, buf, HI3110_TX_STD_BUF_LEN -
+ (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc));
+ }
+}
+
+static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME;
+ hi3110_spi_trans(spi, HI3110_RX_BUF_LEN);
+ memcpy(buf, priv->spi_rx_buf + 1, HI3110_RX_BUF_LEN - 1);
+}
+
+static void hi3110_hw_rx(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u8 buf[HI3110_RX_BUF_LEN - 1];
+
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+
+ hi3110_hw_rx_frame(spi, buf);
+ if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) {
+ /* IDE is recessive (1), indicating extended 29-bit frame */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |=
+ (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) |
+ (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) |
+ ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) |
+ (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) |
+ (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1);
+ } else {
+ /* IDE is dominant (0), frame indicating standard 11-bit */
+ frame->can_id =
+ (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) |
+ ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5);
+ }
+
+ /* Data length */
+ frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F);
+
+ if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR)
+ frame->can_id |= CAN_RTR_FLAG;
+ else
+ memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF,
+ frame->can_dlc);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+}
+
+static void hi3110_hw_sleep(struct spi_device *spi)
+{
+ hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE);
+}
+
+static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ if (priv->tx_skb || priv->tx_len) {
+ dev_err(&spi->dev, "hard_xmit called while tx busy\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ hi3110_clean(net);
+ /* We have to delay work since SPI I/O may sleep */
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->restart_tx = 1;
+ if (priv->can.restart_ms == 0)
+ priv->after_suspend = HI3110_AFTER_SUSPEND_RESTART;
+ queue_work(priv->wq, &priv->restart_work);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int hi3110_get_berr_counter(const struct net_device *net,
+ struct can_berr_counter *bec)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ bec->txerr = hi3110_read(spi, HI3110_READ_TEC);
+ bec->rxerr = hi3110_read(spi, HI3110_READ_REC);
+
+ return 0;
+}
+
+static int hi3110_set_normal_mode(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ u8 reg = 0;
+
+ hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR |
+ HI3110_INT_RXFIFO | HI3110_INT_TXCPLT);
+
+ /* Enable TX */
+ hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ reg = HI3110_CTRL0_LOOPBACK_MODE;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ reg = HI3110_CTRL0_MONITOR_MODE;
+ else
+ reg = HI3110_CTRL0_NORMAL_MODE;
+
+ hi3110_write(spi, HI3110_WRITE_CTRL0, reg);
+
+ /* Wait for the device to enter the mode */
+ mdelay(HI3110_OST_DELAY_MS);
+ reg = hi3110_read(spi, HI3110_READ_CTRL0);
+ if ((reg & HI3110_CTRL0_MODE_MASK) != reg)
+ return -EBUSY;
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ return 0;
+}
+
+static int hi3110_do_set_bittiming(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ hi3110_write(spi, HI3110_WRITE_BTR0,
+ ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) |
+ ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT));
+
+ hi3110_write(spi, HI3110_WRITE_BTR1,
+ (priv->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES ?
+ HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) |
+ ((bt->phase_seg1 + bt->prop_seg - 1)
+ << HI3110_BTR1_TSEG1_SHIFT) |
+ ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT));
+
+ dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n",
+ hi3110_read(spi, HI3110_READ_BTR0),
+ hi3110_read(spi, HI3110_READ_BTR1));
+
+ return 0;
+}
+
+static int hi3110_setup(struct net_device *net)
+{
+ hi3110_do_set_bittiming(net);
+ return 0;
+}
+
+static int hi3110_hw_reset(struct spi_device *spi)
+{
+ u8 reg;
+ int ret;
+
+ /* Wait for oscillator startup timer after power up */
+ mdelay(HI3110_OST_DELAY_MS);
+
+ ret = hi3110_cmd(spi, HI3110_MASTER_RESET);
+ if (ret)
+ return ret;
+
+ /* Wait for oscillator startup timer after reset */
+ mdelay(HI3110_OST_DELAY_MS);
+
+ reg = hi3110_read(spi, HI3110_READ_CTRL0);
+ if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE)
+ return -ENODEV;
+
+ /* As per the datasheet it appears the error flags are
+ * not cleared on reset. Explicitly clear them by performing a read
+ */
+ hi3110_read(spi, HI3110_READ_ERR);
+
+ return 0;
+}
+
+static int hi3110_hw_probe(struct spi_device *spi)
+{
+ u8 statf;
+
+ hi3110_hw_reset(spi);
+
+ /* Confirm correct operation by checking against reset values
+ * in datasheet
+ */
+ statf = hi3110_read(spi, HI3110_READ_STATF);
+
+ dev_dbg(&spi->dev, "statf: %02X\n", statf);
+
+ if (statf != 0x82)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int hi3110_power_enable(struct regulator *reg, int enable)
+{
+ if (IS_ERR_OR_NULL(reg))
+ return 0;
+
+ if (enable)
+ return regulator_enable(reg);
+ else
+ return regulator_disable(reg);
+}
+
+static int hi3110_stop(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ close_candev(net);
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, priv);
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+
+ mutex_lock(&priv->hi3110_lock);
+
+ /* Disable transmit, interrupts and clear flags */
+ hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0);
+ hi3110_write(spi, HI3110_WRITE_INTE, 0x0);
+ hi3110_read(spi, HI3110_READ_INTF);
+
+ hi3110_clean(net);
+
+ hi3110_hw_sleep(spi);
+
+ hi3110_power_enable(priv->transceiver, 0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ mutex_unlock(&priv->hi3110_lock);
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static void hi3110_tx_work_handler(struct work_struct *ws)
+{
+ struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ struct can_frame *frame;
+
+ mutex_lock(&priv->hi3110_lock);
+ if (priv->tx_skb) {
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ hi3110_clean(net);
+ } else {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ hi3110_hw_tx(spi, frame);
+ priv->tx_len = 1 + frame->can_dlc;
+ can_put_echo_skb(priv->tx_skb, net, 0);
+ priv->tx_skb = NULL;
+ }
+ }
+ mutex_unlock(&priv->hi3110_lock);
+}
+
+static void hi3110_restart_work_handler(struct work_struct *ws)
+{
+ struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+ restart_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+
+ mutex_lock(&priv->hi3110_lock);
+ if (priv->after_suspend) {
+ hi3110_hw_reset(spi);
+ hi3110_setup(net);
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_RESTART) {
+ hi3110_set_normal_mode(spi);
+ } else if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ hi3110_clean(net);
+ hi3110_set_normal_mode(spi);
+ netif_wake_queue(net);
+ } else {
+ hi3110_hw_sleep(spi);
+ }
+ priv->after_suspend = 0;
+ priv->force_quit = 0;
+ }
+
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ hi3110_hw_reset(spi);
+ hi3110_setup(net);
+ hi3110_clean(net);
+ hi3110_set_normal_mode(spi);
+ netif_wake_queue(net);
+ }
+ mutex_unlock(&priv->hi3110_lock);
+}
+
+static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
+{
+ struct hi3110_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+
+ mutex_lock(&priv->hi3110_lock);
+
+ while (!priv->force_quit) {
+ enum can_state new_state;
+ u8 intf, eflag, statf;
+
+ while (!(HI3110_STAT_RXFMTY &
+ (statf = hi3110_read(spi, HI3110_READ_STATF)))) {
+ hi3110_hw_rx(spi);
+ }
+
+ intf = hi3110_read(spi, HI3110_READ_INTF);
+ eflag = hi3110_read(spi, HI3110_READ_ERR);
+ /* Update can state */
+ if (eflag & HI3110_ERR_BUSOFF)
+ new_state = CAN_STATE_BUS_OFF;
+ else if (eflag & HI3110_ERR_PASSIVE_MASK)
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ else if (statf & HI3110_STAT_ERRW)
+ new_state = CAN_STATE_ERROR_WARNING;
+ else
+ new_state = CAN_STATE_ERROR_ACTIVE;
+
+ if (new_state != priv->can.state) {
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state rx_state, tx_state;
+ u8 rxerr, txerr;
+
+ skb = alloc_can_err_skb(net, &cf);
+ if (!skb)
+ break;
+
+ txerr = hi3110_read(spi, HI3110_READ_TEC);
+ rxerr = hi3110_read(spi, HI3110_READ_REC);
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+ tx_state = txerr >= rxerr ? new_state : 0;
+ rx_state = txerr <= rxerr ? new_state : 0;
+ can_change_state(net, cf, tx_state, rx_state);
+ netif_rx_ni(skb);
+
+ if (new_state == CAN_STATE_BUS_OFF) {
+ can_bus_off(net);
+ if (priv->can.restart_ms == 0) {
+ priv->force_quit = 1;
+ hi3110_hw_sleep(spi);
+ break;
+ }
+ }
+ }
+
+ /* Update bus errors */
+ if ((intf & HI3110_INT_BUSERR) &&
+ (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) {
+ struct can_frame *cf;
+ struct sk_buff *skb;
+
+ /* Check for protocol errors */
+ if (eflag & HI3110_ERR_PROTOCOL_MASK) {
+ skb = alloc_can_err_skb(net, &cf);
+ if (!skb)
+ break;
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+ priv->can.can_stats.bus_error++;
+ priv->net->stats.rx_errors++;
+ if (eflag & HI3110_ERR_BITERR)
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ else if (eflag & HI3110_ERR_FRMERR)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ else if (eflag & HI3110_ERR_STUFERR)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ else if (eflag & HI3110_ERR_CRCERR)
+ cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ else if (eflag & HI3110_ERR_ACKERR)
+ cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+
+ cf->data[6] = hi3110_read(spi, HI3110_READ_TEC);
+ cf->data[7] = hi3110_read(spi, HI3110_READ_REC);
+ netdev_dbg(priv->net, "Bus Error\n");
+ netif_rx_ni(skb);
+ }
+ }
+
+ if (intf == 0)
+ break;
+
+ if (intf & HI3110_INT_TXCPLT) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += priv->tx_len - 1;
+ can_led_event(net, CAN_LED_EVENT_TX);
+ if (priv->tx_len) {
+ can_get_echo_skb(net, 0);
+ priv->tx_len = 0;
+ }
+ netif_wake_queue(net);
+ }
+ }
+ mutex_unlock(&priv->hi3110_lock);
+ return IRQ_HANDLED;
+}
+
+static int hi3110_open(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->hi3110_lock);
+ hi3110_power_enable(priv->transceiver, 1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist,
+ flags, DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto out_close;
+ }
+
+ priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
+ 0);
+ if (!priv->wq) {
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+ INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
+ INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
+
+ ret = hi3110_hw_reset(spi);
+ if (ret)
+ goto out_free_wq;
+
+ ret = hi3110_setup(net);
+ if (ret)
+ goto out_free_wq;
+
+ ret = hi3110_set_normal_mode(spi);
+ if (ret)
+ goto out_free_wq;
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+ netif_wake_queue(net);
+ mutex_unlock(&priv->hi3110_lock);
+
+ return 0;
+
+ out_free_wq:
+ destroy_workqueue(priv->wq);
+ out_free_irq:
+ free_irq(spi->irq, priv);
+ hi3110_hw_sleep(spi);
+ out_close:
+ hi3110_power_enable(priv->transceiver, 0);
+ close_candev(net);
+ mutex_unlock(&priv->hi3110_lock);
+ return ret;
+}
+
+static const struct net_device_ops hi3110_netdev_ops = {
+ .ndo_open = hi3110_open,
+ .ndo_stop = hi3110_stop,
+ .ndo_start_xmit = hi3110_hard_start_xmit,
+};
+
+static const struct of_device_id hi3110_of_match[] = {
+ {
+ .compatible = "holt,hi3110",
+ .data = (void *)CAN_HI3110_HI3110,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hi3110_of_match);
+
+static const struct spi_device_id hi3110_id_table[] = {
+ {
+ .name = "hi3110",
+ .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, hi3110_id_table);
+
+static int hi3110_can_probe(struct spi_device *spi)
+{
+ const struct of_device_id *of_id = of_match_device(hi3110_of_match,
+ &spi->dev);
+ struct net_device *net;
+ struct hi3110_priv *priv;
+ struct clk *clk;
+ int freq, ret;
+
+ clk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&spi->dev, "no CAN clock source defined\n");
+ return PTR_ERR(clk);
+ }
+ freq = clk_get_rate(clk);
+
+ /* Sanity check */
+ if (freq > 40000000)
+ return -ERANGE;
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(struct hi3110_priv), HI3110_TX_ECHO_SKB_MAX);
+ if (!net)
+ return -ENOMEM;
+
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_free;
+ }
+
+ net->netdev_ops = &hi3110_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &hi3110_bittiming_const;
+ priv->can.do_set_mode = hi3110_do_set_mode;
+ priv->can.do_get_berr_counter = hi3110_get_berr_counter;
+ priv->can.clock.freq = freq / 2;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING;
+
+ if (of_id)
+ priv->model = (enum hi3110_model)of_id->data;
+ else
+ priv->model = spi_get_device_id(spi)->driver_data;
+ priv->net = net;
+ priv->clk = clk;
+
+ spi_set_drvdata(spi, priv);
+
+ /* Configure the SPI bus */
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
+ priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
+ if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
+ (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
+ ret = -EPROBE_DEFER;
+ goto out_clk;
+ }
+
+ ret = hi3110_power_enable(priv->power, 1);
+ if (ret)
+ goto out_clk;
+
+ priv->spi = spi;
+ mutex_init(&priv->hi3110_lock);
+
+ /* If requested, allocate DMA buffers */
+ if (hi3110_enable_dma) {
+ spi->dev.coherent_dma_mask = ~0;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ * that much and share it between Tx and Rx DMA buffers.
+ */
+ priv->spi_tx_buf = dmam_alloc_coherent(&spi->dev,
+ PAGE_SIZE,
+ &priv->spi_tx_dma,
+ GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ hi3110_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!hi3110_enable_dma) {
+ priv->spi_tx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN,
+ GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_probe;
+ }
+ priv->spi_rx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN,
+ GFP_KERNEL);
+
+ if (!priv->spi_rx_buf) {
+ ret = -ENOMEM;
+ goto error_probe;
+ }
+ }
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ ret = hi3110_hw_probe(spi);
+ if (ret) {
+ if (ret == -ENODEV)
+ dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n",
+ priv->model);
+ goto error_probe;
+ }
+ hi3110_hw_sleep(spi);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ devm_can_led_init(net);
+ netdev_info(net, "%x successfully initialized.\n", priv->model);
+
+ return 0;
+
+ error_probe:
+ hi3110_power_enable(priv->power, 0);
+
+ out_clk:
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
+
+ out_free:
+ free_candev(net);
+
+ dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+ return ret;
+}
+
+static int hi3110_can_remove(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+
+ hi3110_power_enable(priv->power, 0);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ free_candev(net);
+
+ return 0;
+}
+
+static int __maybe_unused hi3110_can_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+
+ /* Note: at this point neither IST nor workqueues are running.
+ * open/stop cannot be called anyway so locking is not needed
+ */
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ hi3110_hw_sleep(spi);
+ hi3110_power_enable(priv->transceiver, 0);
+ priv->after_suspend = HI3110_AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = HI3110_AFTER_SUSPEND_DOWN;
+ }
+
+ if (!IS_ERR_OR_NULL(priv->power)) {
+ regulator_disable(priv->power);
+ priv->after_suspend |= HI3110_AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused hi3110_can_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_POWER)
+ hi3110_power_enable(priv->power, 1);
+
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) {
+ hi3110_power_enable(priv->transceiver, 1);
+ queue_work(priv->wq, &priv->restart_work);
+ } else {
+ priv->after_suspend = 0;
+ }
+
+ priv->force_quit = 0;
+ enable_irq(spi->irq);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend, hi3110_can_resume);
+
+static struct spi_driver hi3110_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = hi3110_of_match,
+ .pm = &hi3110_can_pm_ops,
+ },
+ .id_table = hi3110_id_table,
+ .probe = hi3110_can_probe,
+ .remove = hi3110_can_remove,
+};
+
+module_spi_driver(hi3110_can_driver);
+
+MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
+MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>");
+MODULE_DESCRIPTION("Holt HI-3110 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 6749b18..b8aac53 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -17,25 +17,6 @@
*
*/
-/*
- * Your platform definitions should specify module ram offsets and interrupt
- * number to use as follows:
- *
- * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = {
- * .scc_hecc_offset = 0,
- * .scc_ram_offset = 0x3000,
- * .hecc_ram_offset = 0x3000,
- * .mbx_offset = 0x2000,
- * .int_line = 0,
- * .revision = 1,
- * .transceiver_switch = hecc_phy_control,
- * };
- *
- * Please see include/linux/can/platform/ti_hecc.h for description of
- * above fields.
- *
- */
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
@@ -46,11 +27,13 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
-#include <linux/can/platform/ti_hecc.h>
#define DRV_NAME "ti_hecc"
#define HECC_MODULE_VERSION "0.7"
@@ -214,15 +197,14 @@
struct net_device *ndev;
struct clk *clk;
void __iomem *base;
- u32 scc_ram_offset;
- u32 hecc_ram_offset;
- u32 mbx_offset;
- u32 int_line;
+ void __iomem *hecc_ram;
+ void __iomem *mbx;
+ bool use_hecc1int;
spinlock_t mbx_lock; /* CANME register needs protection */
u32 tx_head;
u32 tx_tail;
u32 rx_next;
- void (*transceiver_switch)(int);
+ struct regulator *reg_xceiver;
};
static inline int get_tx_head_mb(struct ti_hecc_priv *priv)
@@ -242,20 +224,18 @@
static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val)
{
- __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4);
+ __raw_writel(val, priv->hecc_ram + mbxno * 4);
}
static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno,
u32 reg, u32 val)
{
- __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 +
- reg);
+ __raw_writel(val, priv->mbx + mbxno * 0x10 + reg);
}
static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg)
{
- return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 +
- reg);
+ return __raw_readl(priv->mbx + mbxno * 0x10 + reg);
}
static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val)
@@ -311,11 +291,16 @@
return 0;
}
-static void ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv,
- int on)
+static int ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv,
+ int on)
{
- if (priv->transceiver_switch)
- priv->transceiver_switch(on);
+ if (!priv->reg_xceiver)
+ return 0;
+
+ if (on)
+ return regulator_enable(priv->reg_xceiver);
+ else
+ return regulator_disable(priv->reg_xceiver);
}
static void ti_hecc_reset(struct net_device *ndev)
@@ -409,7 +394,7 @@
/* Prevent message over-write & Enable interrupts */
hecc_write(priv, HECC_CANOPC, HECC_SET_REG);
- if (priv->int_line) {
+ if (priv->use_hecc1int) {
hecc_write(priv, HECC_CANMIL, HECC_SET_REG);
hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK |
HECC_CANGIM_I1EN | HECC_CANGIM_SIL);
@@ -760,7 +745,7 @@
unsigned long ack, flags;
int_status = hecc_read(priv,
- (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0);
+ (priv->use_hecc1int) ? HECC_CANGIF1 : HECC_CANGIF0);
if (!int_status)
return IRQ_NONE;
@@ -806,7 +791,7 @@
}
/* clear all interrupt conditions - read back to avoid spurious ints */
- if (priv->int_line) {
+ if (priv->use_hecc1int) {
hecc_write(priv, HECC_CANGIF1, HECC_SET_REG);
int_status = hecc_read(priv, HECC_CANGIF1);
} else {
@@ -872,58 +857,87 @@
.ndo_change_mtu = can_change_mtu,
};
+static const struct of_device_id ti_hecc_dt_ids[] = {
+ {
+ .compatible = "ti,am3517-hecc",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_hecc_dt_ids);
+
static int ti_hecc_probe(struct platform_device *pdev)
{
struct net_device *ndev = (struct net_device *)0;
struct ti_hecc_priv *priv;
- struct ti_hecc_platform_data *pdata;
- struct resource *mem, *irq;
- void __iomem *addr;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res, *irq;
+ struct regulator *reg_xceiver;
int err = -ENODEV;
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data\n");
- goto probe_exit;
+ if (!IS_ENABLED(CONFIG_OF) || !np)
+ return -EINVAL;
+
+ reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
+ if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ else if (IS_ERR(reg_xceiver))
+ reg_xceiver = NULL;
+
+ ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX);
+ if (!ndev) {
+ dev_err(&pdev->dev, "alloc_candev failed\n");
+ return -ENOMEM;
+ }
+ priv = netdev_priv(ndev);
+
+ /* handle hecc memory */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc");
+ if (!res) {
+ dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc\n");
+ return -EINVAL;
}
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No mem resources\n");
- goto probe_exit;
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!priv->base) {
+ dev_err(&pdev->dev, "hecc ioremap failed\n");
+ return -ENOMEM;
}
+
+ /* handle hecc-ram memory */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc-ram");
+ if (!res) {
+ dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc-ram\n");
+ return -EINVAL;
+ }
+
+ priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res);
+ if (!priv->hecc_ram) {
+ dev_err(&pdev->dev, "hecc-ram ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ /* handle mbx memory */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbx");
+ if (!res) {
+ dev_err(&pdev->dev, "can't get IORESOURCE_MEM mbx\n");
+ return -EINVAL;
+ }
+
+ priv->mbx = devm_ioremap_resource(&pdev->dev, res);
+ if (!priv->mbx) {
+ dev_err(&pdev->dev, "mbx ioremap failed\n");
+ return -ENOMEM;
+ }
+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "No irq resource\n");
goto probe_exit;
}
- if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
- dev_err(&pdev->dev, "HECC region already claimed\n");
- err = -EBUSY;
- goto probe_exit;
- }
- addr = ioremap(mem->start, resource_size(mem));
- if (!addr) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -ENOMEM;
- goto probe_exit_free_region;
- }
- ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX);
- if (!ndev) {
- dev_err(&pdev->dev, "alloc_candev failed\n");
- err = -ENOMEM;
- goto probe_exit_iounmap;
- }
-
- priv = netdev_priv(ndev);
priv->ndev = ndev;
- priv->base = addr;
- priv->scc_ram_offset = pdata->scc_ram_offset;
- priv->hecc_ram_offset = pdata->hecc_ram_offset;
- priv->mbx_offset = pdata->mbx_offset;
- priv->int_line = pdata->int_line;
- priv->transceiver_switch = pdata->transceiver_switch;
+ priv->reg_xceiver = reg_xceiver;
+ priv->use_hecc1int = of_property_read_bool(np, "ti,use-hecc1int");
priv->can.bittiming_const = &ti_hecc_bittiming_const;
priv->can.do_set_mode = ti_hecc_do_set_mode;
@@ -971,32 +985,23 @@
clk_put(priv->clk);
probe_exit_candev:
free_candev(ndev);
-probe_exit_iounmap:
- iounmap(addr);
-probe_exit_free_region:
- release_mem_region(mem->start, resource_size(mem));
probe_exit:
return err;
}
static int ti_hecc_remove(struct platform_device *pdev)
{
- struct resource *res;
struct net_device *ndev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(ndev);
unregister_candev(ndev);
clk_disable_unprepare(priv->clk);
clk_put(priv->clk);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iounmap(priv->base);
- release_mem_region(res->start, resource_size(res));
free_candev(ndev);
return 0;
}
-
#ifdef CONFIG_PM
static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state)
{
@@ -1045,6 +1050,7 @@
static struct platform_driver ti_hecc_driver = {
.driver = {
.name = DRV_NAME,
+ .of_match_table = ti_hecc_dt_ids,
},
.probe = ti_hecc_probe,
.remove = ti_hecc_remove,
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index df08a41..319a0da 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -45,12 +45,13 @@
extern int can_proto_register(const struct can_proto *cp);
extern void can_proto_unregister(const struct can_proto *cp);
-int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+int can_rx_register(struct net *net, struct net_device *dev,
+ canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *),
void *data, char *ident, struct sock *sk);
-extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
- canid_t mask,
+extern void can_rx_unregister(struct net *net, struct net_device *dev,
+ canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *),
void *data);
diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h
deleted file mode 100644
index a52f47c..0000000
--- a/include/linux/can/platform/ti_hecc.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _CAN_PLATFORM_TI_HECC_H
-#define _CAN_PLATFORM_TI_HECC_H
-
-/*
- * TI HECC (High End CAN Controller) driver platform header
- *
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed as is WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-/**
- * struct hecc_platform_data - HECC Platform Data
- *
- * @scc_hecc_offset: mostly 0 - should really never change
- * @scc_ram_offset: SCC RAM offset
- * @hecc_ram_offset: HECC RAM offset
- * @mbx_offset: Mailbox RAM offset
- * @int_line: Interrupt line to use - 0 or 1
- * @version: version for future use
- * @transceiver_switch: platform specific callback fn for transceiver control
- *
- * Platform data structure to get all platform specific settings.
- * this structure also accounts the fact that the IP may have different
- * RAM and mailbox offsets for different SOC's
- */
-struct ti_hecc_platform_data {
- u32 scc_hecc_offset;
- u32 scc_ram_offset;
- u32 hecc_ram_offset;
- u32 mbx_offset;
- u32 int_line;
- u32 version;
- void (*transceiver_switch) (int);
-};
-#endif /* !_CAN_PLATFORM_TI_HECC_H */
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index af8fe8a..fe80bb4 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -27,6 +27,7 @@
#include <net/netns/nftables.h>
#include <net/netns/xfrm.h>
#include <net/netns/mpls.h>
+#include <net/netns/can.h>
#include <linux/ns_common.h>
#include <linux/idr.h>
#include <linux/skbuff.h>
@@ -141,6 +142,9 @@
#if IS_ENABLED(CONFIG_MPLS)
struct netns_mpls mpls;
#endif
+#if IS_ENABLED(CONFIG_CAN)
+ struct netns_can can;
+#endif
struct sock *diag_nlsk;
atomic_t fnhe_genid;
};
diff --git a/include/net/netns/can.h b/include/net/netns/can.h
new file mode 100644
index 0000000..e8beba7
--- /dev/null
+++ b/include/net/netns/can.h
@@ -0,0 +1,31 @@
+/*
+ * can in net namespaces
+ */
+
+#ifndef __NETNS_CAN_H__
+#define __NETNS_CAN_H__
+
+#include <linux/spinlock.h>
+
+struct dev_rcv_lists;
+
+struct netns_can {
+#if IS_ENABLED(CONFIG_PROC_FS)
+ struct proc_dir_entry *proc_dir;
+ struct proc_dir_entry *pde_version;
+ struct proc_dir_entry *pde_stats;
+ struct proc_dir_entry *pde_reset_stats;
+ struct proc_dir_entry *pde_rcvlist_all;
+ struct proc_dir_entry *pde_rcvlist_fil;
+ struct proc_dir_entry *pde_rcvlist_inv;
+ struct proc_dir_entry *pde_rcvlist_sff;
+ struct proc_dir_entry *pde_rcvlist_eff;
+ struct proc_dir_entry *pde_rcvlist_err;
+#endif
+
+ /* receive filters subscribed for 'all' CAN devices */
+ struct dev_rcv_lists *can_rx_alldev_list;
+ spinlock_t can_rcvlists_lock;
+};
+
+#endif /* __NETNS_CAN_H__ */
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 5488e4a..abf7d85 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -75,9 +75,7 @@
module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
-/* receive filters subscribed for 'all' CAN devices */
-struct dev_rcv_lists can_rx_alldev_list;
-static DEFINE_SPINLOCK(can_rcvlists_lock);
+static int can_net_id;
static struct kmem_cache *rcv_cache __read_mostly;
@@ -145,9 +143,6 @@
if (protocol < 0 || protocol >= CAN_NPROTO)
return -EINVAL;
- if (!net_eq(net, &init_net))
- return -EAFNOSUPPORT;
-
cp = can_get_proto(protocol);
#ifdef CONFIG_MODULES
@@ -331,10 +326,11 @@
* af_can rx path
*/
-static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net *net,
+ struct net_device *dev)
{
if (!dev)
- return &can_rx_alldev_list;
+ return net->can.can_rx_alldev_list;
else
return (struct dev_rcv_lists *)dev->ml_priv;
}
@@ -467,9 +463,9 @@
* -ENOMEM on missing cache mem to create subscription entry
* -ENODEV unknown device
*/
-int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
- void (*func)(struct sk_buff *, void *), void *data,
- char *ident, struct sock *sk)
+int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
+ canid_t mask, void (*func)(struct sk_buff *, void *),
+ void *data, char *ident, struct sock *sk)
{
struct receiver *r;
struct hlist_head *rl;
@@ -481,13 +477,16 @@
if (dev && dev->type != ARPHRD_CAN)
return -ENODEV;
+ if (dev && !net_eq(net, dev_net(dev)))
+ return -ENODEV;
+
r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
if (!r)
return -ENOMEM;
- spin_lock(&can_rcvlists_lock);
+ spin_lock(&net->can.can_rcvlists_lock);
- d = find_dev_rcv_lists(dev);
+ d = find_dev_rcv_lists(net, dev);
if (d) {
rl = find_rcv_list(&can_id, &mask, d);
@@ -510,7 +509,7 @@
err = -ENODEV;
}
- spin_unlock(&can_rcvlists_lock);
+ spin_unlock(&net->can.can_rcvlists_lock);
return err;
}
@@ -540,8 +539,9 @@
* Description:
* Removes subscription entry depending on given (subscription) values.
*/
-void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
- void (*func)(struct sk_buff *, void *), void *data)
+void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
+ canid_t mask, void (*func)(struct sk_buff *, void *),
+ void *data)
{
struct receiver *r = NULL;
struct hlist_head *rl;
@@ -550,9 +550,12 @@
if (dev && dev->type != ARPHRD_CAN)
return;
- spin_lock(&can_rcvlists_lock);
+ if (dev && !net_eq(net, dev_net(dev)))
+ return;
- d = find_dev_rcv_lists(dev);
+ spin_lock(&net->can.can_rcvlists_lock);
+
+ d = find_dev_rcv_lists(net, dev);
if (!d) {
pr_err("BUG: receive list not found for "
"dev %s, id %03X, mask %03X\n",
@@ -598,7 +601,7 @@
}
out:
- spin_unlock(&can_rcvlists_lock);
+ spin_unlock(&net->can.can_rcvlists_lock);
/* schedule the receiver item for deletion */
if (r) {
@@ -696,10 +699,10 @@
rcu_read_lock();
/* deliver the packet to sockets listening on all devices */
- matches = can_rcv_filter(&can_rx_alldev_list, skb);
+ matches = can_rcv_filter(dev_net(dev)->can.can_rx_alldev_list, skb);
/* find receive list for this device */
- d = find_dev_rcv_lists(dev);
+ d = find_dev_rcv_lists(dev_net(dev), dev);
if (d)
matches += can_rcv_filter(d, skb);
@@ -719,9 +722,6 @@
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
- if (unlikely(!net_eq(dev_net(dev), &init_net)))
- goto drop;
-
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CAN_MTU ||
cfd->len > CAN_MAX_DLEN,
@@ -743,9 +743,6 @@
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
- if (unlikely(!net_eq(dev_net(dev), &init_net)))
- goto drop;
-
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CANFD_MTU ||
cfd->len > CANFD_MAX_DLEN,
@@ -835,9 +832,6 @@
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct dev_rcv_lists *d;
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
@@ -855,7 +849,7 @@
break;
case NETDEV_UNREGISTER:
- spin_lock(&can_rcvlists_lock);
+ spin_lock(&dev_net(dev)->can.can_rcvlists_lock);
d = dev->ml_priv;
if (d) {
@@ -869,7 +863,7 @@
pr_err("can: notifier: receive list not found for dev "
"%s\n", dev->name);
- spin_unlock(&can_rcvlists_lock);
+ spin_unlock(&dev_net(dev)->can.can_rcvlists_lock);
break;
}
@@ -877,6 +871,40 @@
return NOTIFY_DONE;
}
+static int can_pernet_init(struct net *net)
+{
+ net->can.can_rcvlists_lock =
+ __SPIN_LOCK_UNLOCKED(net->can.can_rcvlists_lock);
+ net->can.can_rx_alldev_list =
+ kzalloc(sizeof(struct dev_rcv_lists), GFP_KERNEL);
+
+ if (IS_ENABLED(CONFIG_PROC_FS))
+ can_init_proc(net);
+
+ return 0;
+}
+
+static void can_pernet_exit(struct net *net)
+{
+ struct net_device *dev;
+
+ if (IS_ENABLED(CONFIG_PROC_FS))
+ can_remove_proc(net);
+
+ /* remove created dev_rcv_lists from still registered CAN devices */
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->type == ARPHRD_CAN && dev->ml_priv) {
+ struct dev_rcv_lists *d = dev->ml_priv;
+
+ BUG_ON(d->entries);
+ kfree(d);
+ dev->ml_priv = NULL;
+ }
+ }
+ rcu_read_unlock();
+}
+
/*
* af_can module init/exit functions
*/
@@ -902,6 +930,13 @@
.notifier_call = can_notifier,
};
+static struct pernet_operations can_pernet_ops __read_mostly = {
+ .init = can_pernet_init,
+ .exit = can_pernet_exit,
+ .id = &can_net_id,
+ .size = 0,
+};
+
static __init int can_init(void)
{
/* check for correct padding to be able to use the structs similarly */
@@ -912,8 +947,6 @@
pr_info("can: controller area network core (" CAN_VERSION_STRING ")\n");
- memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
-
rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
0, 0, NULL);
if (!rcv_cache)
@@ -925,9 +958,10 @@
setup_timer(&can_stattimer, can_stat_update, 0);
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
}
- can_init_proc();
}
+ register_pernet_subsys(&can_pernet_ops);
+
/* protocol register */
sock_register(&can_family_ops);
register_netdevice_notifier(&can_netdev_notifier);
@@ -939,13 +973,9 @@
static __exit void can_exit(void)
{
- struct net_device *dev;
-
if (IS_ENABLED(CONFIG_PROC_FS)) {
if (stats_timer)
del_timer_sync(&can_stattimer);
-
- can_remove_proc();
}
/* protocol unregister */
@@ -954,19 +984,7 @@
unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN);
- /* remove created dev_rcv_lists from still registered CAN devices */
- rcu_read_lock();
- for_each_netdev_rcu(&init_net, dev) {
- if (dev->type == ARPHRD_CAN && dev->ml_priv) {
-
- struct dev_rcv_lists *d = dev->ml_priv;
-
- BUG_ON(d->entries);
- kfree(d);
- dev->ml_priv = NULL;
- }
- }
- rcu_read_unlock();
+ unregister_pernet_subsys(&can_pernet_ops);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
diff --git a/net/can/af_can.h b/net/can/af_can.h
index b86f512..f273c9d 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -114,8 +114,8 @@
extern struct dev_rcv_lists can_rx_alldev_list;
/* function prototypes for the CAN networklayer procfs (proc.c) */
-void can_init_proc(void);
-void can_remove_proc(void);
+void can_init_proc(struct net *net);
+void can_remove_proc(struct net *net);
void can_stat_update(unsigned long data);
/* structures and variables from af_can.c needed in proc.c for reading */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 95d13b2..1976629 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -764,8 +764,8 @@
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{
if (op->rx_reg_dev == dev) {
- can_rx_unregister(dev, op->can_id, REGMASK(op->can_id),
- bcm_rx_handler, op);
+ can_rx_unregister(&init_net, dev, op->can_id,
+ REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */
op->rx_reg_dev = NULL;
@@ -808,7 +808,7 @@
}
}
} else
- can_rx_unregister(NULL, op->can_id,
+ can_rx_unregister(&init_net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
@@ -1222,7 +1222,8 @@
dev = dev_get_by_index(&init_net, ifindex);
if (dev) {
- err = can_rx_register(dev, op->can_id,
+ err = can_rx_register(&init_net, dev,
+ op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
"bcm", sk);
@@ -1232,7 +1233,7 @@
}
} else
- err = can_rx_register(NULL, op->can_id,
+ err = can_rx_register(&init_net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk);
if (err) {
@@ -1528,7 +1529,7 @@
}
}
} else
- can_rx_unregister(NULL, op->can_id,
+ can_rx_unregister(&init_net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
diff --git a/net/can/gw.c b/net/can/gw.c
index 7056a1a..3c117a33 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -440,14 +440,14 @@
static inline int cgw_register_filter(struct cgw_job *gwj)
{
- return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
+ return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
gwj, "gw", NULL);
}
static inline void cgw_unregister_filter(struct cgw_job *gwj)
{
- can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
+ can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
}
diff --git a/net/can/proc.c b/net/can/proc.c
index 85ef7bb..9a8d54d 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -62,17 +62,6 @@
#define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
#define CAN_PROC_RCVLIST_ERR "rcvlist_err"
-static struct proc_dir_entry *can_dir;
-static struct proc_dir_entry *pde_version;
-static struct proc_dir_entry *pde_stats;
-static struct proc_dir_entry *pde_reset_stats;
-static struct proc_dir_entry *pde_rcvlist_all;
-static struct proc_dir_entry *pde_rcvlist_fil;
-static struct proc_dir_entry *pde_rcvlist_inv;
-static struct proc_dir_entry *pde_rcvlist_sff;
-static struct proc_dir_entry *pde_rcvlist_eff;
-static struct proc_dir_entry *pde_rcvlist_err;
-
static int user_reset;
static const char rx_list_name[][8] = {
@@ -351,20 +340,21 @@
static int can_rcvlist_proc_show(struct seq_file *m, void *v)
{
/* double cast to prevent GCC warning */
- int idx = (int)(long)m->private;
+ int idx = (int)(long)PDE_DATA(m->file->f_inode);
struct net_device *dev;
struct dev_rcv_lists *d;
+ struct net *net = m->private;
seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]);
rcu_read_lock();
/* receive list for 'all' CAN devices (dev == NULL) */
- d = &can_rx_alldev_list;
+ d = net->can.can_rx_alldev_list;
can_rcvlist_proc_show_one(m, idx, NULL, d);
/* receive list for registered CAN devices */
- for_each_netdev_rcu(&init_net, dev) {
+ for_each_netdev_rcu(net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv)
can_rcvlist_proc_show_one(m, idx, dev, dev->ml_priv);
}
@@ -377,7 +367,7 @@
static int can_rcvlist_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, can_rcvlist_proc_show, PDE_DATA(inode));
+ return single_open_net(inode, file, can_rcvlist_proc_show);
}
static const struct file_operations can_rcvlist_proc_fops = {
@@ -417,6 +407,7 @@
{
struct net_device *dev;
struct dev_rcv_lists *d;
+ struct net *net = m->private;
/* RX_SFF */
seq_puts(m, "\nreceive list 'rx_sff':\n");
@@ -424,11 +415,11 @@
rcu_read_lock();
/* sff receive list for 'all' CAN devices (dev == NULL) */
- d = &can_rx_alldev_list;
+ d = net->can.can_rx_alldev_list;
can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));
/* sff receive list for registered CAN devices */
- for_each_netdev_rcu(&init_net, dev) {
+ for_each_netdev_rcu(net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
d = dev->ml_priv;
can_rcvlist_proc_show_array(m, dev, d->rx_sff,
@@ -444,7 +435,7 @@
static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, can_rcvlist_sff_proc_show, NULL);
+ return single_open_net(inode, file, can_rcvlist_sff_proc_show);
}
static const struct file_operations can_rcvlist_sff_proc_fops = {
@@ -460,6 +451,7 @@
{
struct net_device *dev;
struct dev_rcv_lists *d;
+ struct net *net = m->private;
/* RX_EFF */
seq_puts(m, "\nreceive list 'rx_eff':\n");
@@ -467,11 +459,11 @@
rcu_read_lock();
/* eff receive list for 'all' CAN devices (dev == NULL) */
- d = &can_rx_alldev_list;
+ d = net->can.can_rx_alldev_list;
can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff));
/* eff receive list for registered CAN devices */
- for_each_netdev_rcu(&init_net, dev) {
+ for_each_netdev_rcu(net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
d = dev->ml_priv;
can_rcvlist_proc_show_array(m, dev, d->rx_eff,
@@ -487,7 +479,7 @@
static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, can_rcvlist_eff_proc_show, NULL);
+ return single_open_net(inode, file, can_rcvlist_eff_proc_show);
}
static const struct file_operations can_rcvlist_eff_proc_fops = {
@@ -499,81 +491,85 @@
};
/*
- * proc utility functions
- */
-
-static void can_remove_proc_readentry(const char *name)
-{
- if (can_dir)
- remove_proc_entry(name, can_dir);
-}
-
-/*
* can_init_proc - create main CAN proc directory and procfs entries
*/
-void can_init_proc(void)
+void can_init_proc(struct net *net)
{
/* create /proc/net/can directory */
- can_dir = proc_mkdir("can", init_net.proc_net);
+ net->can.proc_dir = proc_net_mkdir(net, "can", net->proc_net);
- if (!can_dir) {
- pr_info("can: failed to create /proc/net/can.\n");
+ if (!net->can.proc_dir) {
+ printk(KERN_INFO "can: failed to create /proc/net/can . "
+ "CONFIG_PROC_FS missing?\n");
return;
}
/* own procfs entries from the AF_CAN core */
- pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir,
- &can_version_proc_fops);
- pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir,
- &can_stats_proc_fops);
- pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir,
- &can_reset_stats_proc_fops);
- pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir,
- &can_rcvlist_proc_fops, (void *)RX_ERR);
- pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir,
- &can_rcvlist_proc_fops, (void *)RX_ALL);
- pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir,
- &can_rcvlist_proc_fops, (void *)RX_FIL);
- pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
- &can_rcvlist_proc_fops, (void *)RX_INV);
- pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
- &can_rcvlist_eff_proc_fops);
- pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
- &can_rcvlist_sff_proc_fops);
+ net->can.pde_version = proc_create(CAN_PROC_VERSION, 0644,
+ net->can.proc_dir,
+ &can_version_proc_fops);
+ net->can.pde_stats = proc_create(CAN_PROC_STATS, 0644,
+ net->can.proc_dir,
+ &can_stats_proc_fops);
+ net->can.pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644,
+ net->can.proc_dir,
+ &can_reset_stats_proc_fops);
+ net->can.pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_proc_fops,
+ (void *)RX_ERR);
+ net->can.pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_proc_fops,
+ (void *)RX_ALL);
+ net->can.pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_proc_fops,
+ (void *)RX_FIL);
+ net->can.pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_proc_fops,
+ (void *)RX_INV);
+ net->can.pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_eff_proc_fops);
+ net->can.pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644,
+ net->can.proc_dir,
+ &can_rcvlist_sff_proc_fops);
}
/*
* can_remove_proc - remove procfs entries and main CAN proc directory
*/
-void can_remove_proc(void)
+void can_remove_proc(struct net *net)
{
- if (pde_version)
- can_remove_proc_readentry(CAN_PROC_VERSION);
+ if (net->can.pde_version)
+ remove_proc_entry(CAN_PROC_VERSION, net->can.proc_dir);
- if (pde_stats)
- can_remove_proc_readentry(CAN_PROC_STATS);
+ if (net->can.pde_stats)
+ remove_proc_entry(CAN_PROC_STATS, net->can.proc_dir);
- if (pde_reset_stats)
- can_remove_proc_readentry(CAN_PROC_RESET_STATS);
+ if (net->can.pde_reset_stats)
+ remove_proc_entry(CAN_PROC_RESET_STATS, net->can.proc_dir);
- if (pde_rcvlist_err)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
+ if (net->can.pde_rcvlist_err)
+ remove_proc_entry(CAN_PROC_RCVLIST_ERR, net->can.proc_dir);
- if (pde_rcvlist_all)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
+ if (net->can.pde_rcvlist_all)
+ remove_proc_entry(CAN_PROC_RCVLIST_ALL, net->can.proc_dir);
- if (pde_rcvlist_fil)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
+ if (net->can.pde_rcvlist_fil)
+ remove_proc_entry(CAN_PROC_RCVLIST_FIL, net->can.proc_dir);
- if (pde_rcvlist_inv)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
+ if (net->can.pde_rcvlist_inv)
+ remove_proc_entry(CAN_PROC_RCVLIST_INV, net->can.proc_dir);
- if (pde_rcvlist_eff)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
+ if (net->can.pde_rcvlist_eff)
+ remove_proc_entry(CAN_PROC_RCVLIST_EFF, net->can.proc_dir);
- if (pde_rcvlist_sff)
- can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
+ if (net->can.pde_rcvlist_sff)
+ remove_proc_entry(CAN_PROC_RCVLIST_SFF, net->can.proc_dir);
- if (can_dir)
- remove_proc_entry("can", init_net.proc_net);
+ if (net->can.proc_dir)
+ remove_proc_entry("can", net->proc_net);
}
diff --git a/net/can/raw.c b/net/can/raw.c
index 6dc546a..864c80d 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -181,20 +181,21 @@
kfree_skb(skb);
}
-static int raw_enable_filters(struct net_device *dev, struct sock *sk,
- struct can_filter *filter, int count)
+static int raw_enable_filters(struct net *net, struct net_device *dev,
+ struct sock *sk, struct can_filter *filter,
+ int count)
{
int err = 0;
int i;
for (i = 0; i < count; i++) {
- err = can_rx_register(dev, filter[i].can_id,
+ err = can_rx_register(net, dev, filter[i].can_id,
filter[i].can_mask,
raw_rcv, sk, "raw", sk);
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
- can_rx_unregister(dev, filter[i].can_id,
+ can_rx_unregister(net, dev, filter[i].can_id,
filter[i].can_mask,
raw_rcv, sk);
break;
@@ -204,57 +205,62 @@
return err;
}
-static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
- can_err_mask_t err_mask)
+static int raw_enable_errfilter(struct net *net, struct net_device *dev,
+ struct sock *sk, can_err_mask_t err_mask)
{
int err = 0;
if (err_mask)
- err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
+ err = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG,
raw_rcv, sk, "raw", sk);
return err;
}
-static void raw_disable_filters(struct net_device *dev, struct sock *sk,
- struct can_filter *filter, int count)
+static void raw_disable_filters(struct net *net, struct net_device *dev,
+ struct sock *sk, struct can_filter *filter,
+ int count)
{
int i;
for (i = 0; i < count; i++)
- can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
- raw_rcv, sk);
+ can_rx_unregister(net, dev, filter[i].can_id,
+ filter[i].can_mask, raw_rcv, sk);
}
-static inline void raw_disable_errfilter(struct net_device *dev,
+static inline void raw_disable_errfilter(struct net *net,
+ struct net_device *dev,
struct sock *sk,
can_err_mask_t err_mask)
{
if (err_mask)
- can_rx_unregister(dev, 0, err_mask | CAN_ERR_FLAG,
+ can_rx_unregister(net, dev, 0, err_mask | CAN_ERR_FLAG,
raw_rcv, sk);
}
-static inline void raw_disable_allfilters(struct net_device *dev,
+static inline void raw_disable_allfilters(struct net *net,
+ struct net_device *dev,
struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
- raw_disable_filters(dev, sk, ro->filter, ro->count);
- raw_disable_errfilter(dev, sk, ro->err_mask);
+ raw_disable_filters(net, dev, sk, ro->filter, ro->count);
+ raw_disable_errfilter(net, dev, sk, ro->err_mask);
}
-static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
+static int raw_enable_allfilters(struct net *net, struct net_device *dev,
+ struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
int err;
- err = raw_enable_filters(dev, sk, ro->filter, ro->count);
+ err = raw_enable_filters(net, dev, sk, ro->filter, ro->count);
if (!err) {
- err = raw_enable_errfilter(dev, sk, ro->err_mask);
+ err = raw_enable_errfilter(net, dev, sk, ro->err_mask);
if (err)
- raw_disable_filters(dev, sk, ro->filter, ro->count);
+ raw_disable_filters(net, dev, sk, ro->filter,
+ ro->count);
}
return err;
@@ -267,7 +273,7 @@
struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
struct sock *sk = &ro->sk;
- if (!net_eq(dev_net(dev), &init_net))
+ if (!net_eq(dev_net(dev), sock_net(sk)))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
@@ -282,7 +288,7 @@
lock_sock(sk);
/* remove current filters & unregister */
if (ro->bound)
- raw_disable_allfilters(dev, sk);
+ raw_disable_allfilters(dev_net(dev), dev, sk);
if (ro->count > 1)
kfree(ro->filter);
@@ -358,13 +364,13 @@
if (ro->ifindex) {
struct net_device *dev;
- dev = dev_get_by_index(&init_net, ro->ifindex);
+ dev = dev_get_by_index(sock_net(sk), ro->ifindex);
if (dev) {
- raw_disable_allfilters(dev, sk);
+ raw_disable_allfilters(dev_net(dev), dev, sk);
dev_put(dev);
}
} else
- raw_disable_allfilters(NULL, sk);
+ raw_disable_allfilters(sock_net(sk), NULL, sk);
}
if (ro->count > 1)
@@ -404,7 +410,7 @@
if (addr->can_ifindex) {
struct net_device *dev;
- dev = dev_get_by_index(&init_net, addr->can_ifindex);
+ dev = dev_get_by_index(sock_net(sk), addr->can_ifindex);
if (!dev) {
err = -ENODEV;
goto out;
@@ -420,13 +426,13 @@
ifindex = dev->ifindex;
/* filters set by default/setsockopt */
- err = raw_enable_allfilters(dev, sk);
+ err = raw_enable_allfilters(sock_net(sk), dev, sk);
dev_put(dev);
} else {
ifindex = 0;
/* filters set by default/setsockopt */
- err = raw_enable_allfilters(NULL, sk);
+ err = raw_enable_allfilters(sock_net(sk), NULL, sk);
}
if (!err) {
@@ -435,13 +441,15 @@
if (ro->ifindex) {
struct net_device *dev;
- dev = dev_get_by_index(&init_net, ro->ifindex);
+ dev = dev_get_by_index(sock_net(sk),
+ ro->ifindex);
if (dev) {
- raw_disable_allfilters(dev, sk);
+ raw_disable_allfilters(dev_net(dev),
+ dev, sk);
dev_put(dev);
}
} else
- raw_disable_allfilters(NULL, sk);
+ raw_disable_allfilters(sock_net(sk), NULL, sk);
}
ro->ifindex = ifindex;
ro->bound = 1;
@@ -517,15 +525,16 @@
lock_sock(sk);
if (ro->bound && ro->ifindex)
- dev = dev_get_by_index(&init_net, ro->ifindex);
+ dev = dev_get_by_index(sock_net(sk), ro->ifindex);
if (ro->bound) {
/* (try to) register the new filters */
if (count == 1)
- err = raw_enable_filters(dev, sk, &sfilter, 1);
+ err = raw_enable_filters(sock_net(sk), dev, sk,
+ &sfilter, 1);
else
- err = raw_enable_filters(dev, sk, filter,
- count);
+ err = raw_enable_filters(sock_net(sk), dev, sk,
+ filter, count);
if (err) {
if (count > 1)
kfree(filter);
@@ -533,7 +542,8 @@
}
/* remove old filter registrations */
- raw_disable_filters(dev, sk, ro->filter, ro->count);
+ raw_disable_filters(sock_net(sk), dev, sk, ro->filter,
+ ro->count);
}
/* remove old filter space */
@@ -569,18 +579,20 @@
lock_sock(sk);
if (ro->bound && ro->ifindex)
- dev = dev_get_by_index(&init_net, ro->ifindex);
+ dev = dev_get_by_index(sock_net(sk), ro->ifindex);
/* remove current error mask */
if (ro->bound) {
/* (try to) register the new err_mask */
- err = raw_enable_errfilter(dev, sk, err_mask);
+ err = raw_enable_errfilter(sock_net(sk), dev, sk,
+ err_mask);
if (err)
goto out_err;
/* remove old err_mask registration */
- raw_disable_errfilter(dev, sk, ro->err_mask);
+ raw_disable_errfilter(sock_net(sk), dev, sk,
+ ro->err_mask);
}
/* link new err_mask to the socket */
@@ -741,7 +753,7 @@
return -EINVAL;
}
- dev = dev_get_by_index(&init_net, ifindex);
+ dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev)
return -ENXIO;