linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 1/4] can: m_can: Create a m_can platform framework
@ 2019-03-01 18:50 Dan Murphy
  2019-03-01 18:50 ` [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev Dan Murphy
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Dan Murphy @ 2019-03-01 18:50 UTC (permalink / raw)
  To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy

Create a m_can platform framework that peripherial
devices can register to and use common code and register sets.
The peripherial devices may provide read/write and configuration
support of the IP.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/

 drivers/net/can/m_can/Kconfig          |  13 +-
 drivers/net/can/m_can/Makefile         |   1 +
 drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
 drivers/net/can/m_can/m_can.h          | 110 ++++
 drivers/net/can/m_can/m_can_platform.c | 198 +++++++
 5 files changed, 681 insertions(+), 343 deletions(-)
 create mode 100644 drivers/net/can/m_can/m_can.h
 create mode 100644 drivers/net/can/m_can/m_can_platform.c

diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
index 04f20dd39007..f7119fd72df4 100644
--- a/drivers/net/can/m_can/Kconfig
+++ b/drivers/net/can/m_can/Kconfig
@@ -1,5 +1,14 @@
 config CAN_M_CAN
+	tristate "Bosch M_CAN support"
+	---help---
+	  Say Y here if you want support for Bosch M_CAN controller framework.
+	  This is common support for devices that embed the Bosch M_CAN IP.
+
+config CAN_M_CAN_PLATFORM
+	tristate "Bosch M_CAN support for io-mapped devices"
 	depends on HAS_IOMEM
-	tristate "Bosch M_CAN devices"
+	depends on CAN_M_CAN
 	---help---
-	  Say Y here if you want to support for Bosch M_CAN controller.
+	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
+	  This support is for devices that have the Bosch M_CAN controller
+	  IP embedded into the device and the IP is IO Mapped to the processor.
diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
index 8bbd7f24f5be..057bbcdb3c74 100644
--- a/drivers/net/can/m_can/Makefile
+++ b/drivers/net/can/m_can/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_CAN_M_CAN) += m_can.o
+obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 9b449400376b..b37d0886f9cb 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1,20 +1,14 @@
-/*
- * CAN bus driver for Bosch M_CAN controller
- *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
- *	Dong Aisheng <b29396@freescale.com>
- *
- * Bosch M_CAN user manual can be obtained from:
+// SPDX-License-Identifier: GPL-2.0
+// CAN bus driver for Bosch M_CAN controller
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+//      Dong Aisheng <b29396@freescale.com>
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+/* Bosch M_CAN user manual can be obtained from:
  * http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/
  * mcan_users_manual_v302.pdf
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
  */
 
-#include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -28,11 +22,7 @@
 #include <linux/can/dev.h>
 #include <linux/pinctrl/consumer.h>
 
-/* napi related */
-#define M_CAN_NAPI_WEIGHT	64
-
-/* message ram configuration data length */
-#define MRAM_CFG_LEN	8
+#include "m_can.h"
 
 /* registers definition */
 enum m_can_reg {
@@ -86,28 +76,11 @@ enum m_can_reg {
 	M_CAN_TXEFA	= 0xf8,
 };
 
-/* m_can lec values */
-enum m_can_lec_type {
-	LEC_NO_ERROR = 0,
-	LEC_STUFF_ERROR,
-	LEC_FORM_ERROR,
-	LEC_ACK_ERROR,
-	LEC_BIT1_ERROR,
-	LEC_BIT0_ERROR,
-	LEC_CRC_ERROR,
-	LEC_UNUSED,
-};
+/* napi related */
+#define M_CAN_NAPI_WEIGHT	64
 
-enum m_can_mram_cfg {
-	MRAM_SIDF = 0,
-	MRAM_XIDF,
-	MRAM_RXF0,
-	MRAM_RXF1,
-	MRAM_RXB,
-	MRAM_TXE,
-	MRAM_TXB,
-	MRAM_CFG_NUM,
-};
+/* message ram configuration data length */
+#define MRAM_CFG_LEN	8
 
 /* Core Release Register (CREL) */
 #define CREL_REL_SHIFT		28
@@ -343,77 +316,83 @@ enum m_can_mram_cfg {
 #define TX_BUF_MM_MASK		(0xff << TX_BUF_MM_SHIFT)
 
 /* Tx event FIFO Element */
-/* E1 */
 #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT
 #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT)
 
-/* address offset and element number for each FIFO/Buffer in the Message RAM */
-struct mram_cfg {
-	u16 off;
-	u8  num;
-};
-
-/* m_can private data structure */
-struct m_can_priv {
-	struct can_priv can;	/* must be the first member */
-	struct napi_struct napi;
-	struct net_device *dev;
-	struct device *device;
-	struct clk *hclk;
-	struct clk *cclk;
-	void __iomem *base;
-	u32 irqstatus;
-	int version;
-
-	/* message ram configuration */
-	void __iomem *mram_base;
-	struct mram_cfg mcfg[MRAM_CFG_NUM];
-};
+static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
+{
+	if (priv->ops->read_reg)
+		return priv->ops->read_reg(priv, reg);
+	else
+		return -EINVAL;
+}
 
-static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg)
+static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
 {
-	return readl(priv->base + reg);
+	if (priv->ops->write_reg)
+		return priv->ops->write_reg(priv, reg, val);
+	else
+		return -EINVAL;
 }
 
-static inline void m_can_write(const struct m_can_priv *priv,
-			       enum m_can_reg reg, u32 val)
+static u32 m_can_fifo_read(struct m_can_priv *priv,
+			   u32 fgi, unsigned int offset)
 {
-	writel(val, priv->base + reg);
+	u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
+			  offset;
+
+	if (priv->ops->read_fifo)
+		return priv->ops->read_fifo(priv, addr_offset);
+	else
+		return -EINVAL;
 }
 
-static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
-				  u32 fgi, unsigned int offset)
+static u32 m_can_fifo_write(struct m_can_priv *priv,
+			    u32 fpi, unsigned int offset, u32 val)
 {
-	return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
-		     fgi * RXF0_ELEMENT_SIZE + offset);
+	u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
+			  offset;
+
+	if (priv->ops->write_fifo)
+		return priv->ops->write_fifo(priv, addr_offset, val);
+	else
+		return -EINVAL;
 }
 
-static inline void m_can_fifo_write(const struct m_can_priv *priv,
-				    u32 fpi, unsigned int offset, u32 val)
+static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
+				   u32 fpi, u32 val)
 {
-	writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
-	       fpi * TXB_ELEMENT_SIZE + offset);
+	if (priv->ops->write_fifo)
+		return priv->ops->write_fifo(priv, fpi, val);
+	else
+		return 0;
 }
 
-static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
-				      u32 fgi,
-				      u32 offset) {
-	return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
-			fgi * TXE_ELEMENT_SIZE + offset);
+static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
+{
+	u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
+			  offset;
+
+	if (priv->ops->read_fifo)
+		return priv->ops->read_fifo(priv, addr_offset);
+	else
+		return -EINVAL;
 }
 
-static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
+static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
 {
 		return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
 }
 
-static inline void m_can_config_endisable(const struct m_can_priv *priv,
-					  bool enable)
+void m_can_config_endisable(struct m_can_priv *priv, bool enable)
 {
 	u32 cccr = m_can_read(priv, M_CAN_CCCR);
 	u32 timeout = 10;
 	u32 val = 0;
 
+	if (cccr & CCCR_CSR)
+		cccr &= ~CCCR_CSR;
+
 	if (enable) {
 		/* enable m_can configuration */
 		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
@@ -430,7 +409,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
 
 	while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
 		if (timeout == 0) {
-			netdev_warn(priv->dev, "Failed to init module\n");
+			netdev_warn(priv->net, "Failed to init module\n");
 			return;
 		}
 		timeout--;
@@ -438,13 +417,13 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
 	}
 }
 
-static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
+static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
 {
 	/* Only interrupt line 0 is used in this driver */
 	m_can_write(priv, M_CAN_ILE, ILE_EINT0);
 }
 
-static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
+static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
 {
 	m_can_write(priv, M_CAN_ILE, 0x0);
 }
@@ -633,9 +612,12 @@ static int m_can_clk_start(struct m_can_priv *priv)
 {
 	int err;
 
-	err = pm_runtime_get_sync(priv->device);
+	if (priv->pm_clock_support == 0)
+		return 0;
+
+	err = pm_runtime_get_sync(priv->dev);
 	if (err < 0) {
-		pm_runtime_put_noidle(priv->device);
+		pm_runtime_put_noidle(priv->dev);
 		return err;
 	}
 
@@ -644,7 +626,8 @@ static int m_can_clk_start(struct m_can_priv *priv)
 
 static void m_can_clk_stop(struct m_can_priv *priv)
 {
-	pm_runtime_put_sync(priv->device);
+	if (priv->pm_clock_support)
+		pm_runtime_put_sync(priv->dev);
 }
 
 static int m_can_get_berr_counter(const struct net_device *dev,
@@ -811,9 +794,8 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
 	return work_done;
 }
 
-static int m_can_poll(struct napi_struct *napi, int quota)
+static int m_can_rx_handler(struct net_device *dev, int quota)
 {
-	struct net_device *dev = napi->dev;
 	struct m_can_priv *priv = netdev_priv(dev);
 	int work_done = 0;
 	u32 irqstatus, psr;
@@ -831,13 +813,33 @@ static int m_can_poll(struct napi_struct *napi, int quota)
 
 	if (irqstatus & IR_RF0N)
 		work_done += m_can_do_rx_poll(dev, (quota - work_done));
+end:
+	return work_done;
+}
+
+static int m_can_rx_peripherial(struct net_device *dev)
+{
+	struct m_can_priv *priv = netdev_priv(dev);
+
+	m_can_rx_handler(dev, 1);
+
+	m_can_enable_all_interrupts(priv);
+
+	return 0;
+}
+
+static int m_can_poll(struct napi_struct *napi, int quota)
+{
+	struct net_device *dev = napi->dev;
+	struct m_can_priv *priv = netdev_priv(dev);
+	int work_done;
 
+	work_done = m_can_rx_handler(dev, quota);
 	if (work_done < quota) {
 		napi_complete_done(napi, work_done);
 		m_can_enable_all_interrupts(priv);
 	}
 
-end:
 	return work_done;
 }
 
@@ -894,6 +896,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
 	if (ir & IR_ALL_INT)
 		m_can_write(priv, M_CAN_IR, ir);
 
+	if (priv->ops->clr_dev_interrupts)
+		priv->ops->clr_dev_interrupts(priv);
+
 	/* schedule NAPI in case of
 	 * - rx IRQ
 	 * - state change IRQ
@@ -902,7 +907,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
 	if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
 		priv->irqstatus = ir;
 		m_can_disable_all_interrupts(priv);
-		napi_schedule(&priv->napi);
+		if (!priv->is_peripherial)
+			napi_schedule(&priv->napi);
+		else
+			m_can_rx_peripherial(dev);
 	}
 
 	if (priv->version == 30) {
@@ -1155,6 +1163,9 @@ static void m_can_chip_config(struct net_device *dev)
 	m_can_set_bittiming(dev);
 
 	m_can_config_endisable(priv, false);
+
+	if (priv->ops->device_init)
+		priv->ops->device_init(priv);
 }
 
 static void m_can_start(struct net_device *dev)
@@ -1188,20 +1199,17 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
  * else it returns the release and step coded as:
  * return value = 10 * <release> + 1 * <step>
  */
-static int m_can_check_core_release(void __iomem *m_can_base)
+static int m_can_check_core_release(struct m_can_priv *priv)
 {
 	u32 crel_reg;
 	u8 rel;
 	u8 step;
 	int res;
-	struct m_can_priv temp_priv = {
-		.base = m_can_base
-	};
 
 	/* Read Core Release Version and split into version number
 	 * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
 	 */
-	crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
+	crel_reg = m_can_read(priv, M_CAN_CREL);
 	rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
 	step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
 
@@ -1219,18 +1227,26 @@ static int m_can_check_core_release(void __iomem *m_can_base)
 /* Selectable Non ISO support only in version 3.2.x
  * This function checks if the bit is writable.
  */
-static bool m_can_niso_supported(const struct m_can_priv *priv)
+static bool m_can_niso_supported(struct m_can_priv *priv)
 {
-	u32 cccr_reg, cccr_poll;
-	int niso_timeout;
+	u32 cccr_reg, cccr_poll = 0;
+	int niso_timeout = -ETIMEDOUT;
+	int i;
 
 	m_can_config_endisable(priv, true);
 	cccr_reg = m_can_read(priv, M_CAN_CCCR);
 	cccr_reg |= CCCR_NISO;
 	m_can_write(priv, M_CAN_CCCR, cccr_reg);
 
-	niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
-					  (cccr_poll == cccr_reg), 0, 10);
+	for (i = 0; i <= 10; i++) {
+		cccr_poll = m_can_read(priv, M_CAN_CCCR);
+		if (cccr_poll == cccr_reg) {
+			niso_timeout = 0;
+			break;
+		}
+
+		usleep_range(1, 5);
+	}
 
 	/* Clear NISO */
 	cccr_reg &= ~(CCCR_NISO);
@@ -1242,107 +1258,95 @@ static bool m_can_niso_supported(const struct m_can_priv *priv)
 	return !niso_timeout;
 }
 
-static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
-			   void __iomem *addr)
+static int m_can_dev_setup(struct m_can_priv *m_can_dev)
 {
-	struct m_can_priv *priv;
+	struct net_device *dev = m_can_dev->net;
 	int m_can_version;
 
-	m_can_version = m_can_check_core_release(addr);
+	m_can_version = m_can_check_core_release(m_can_dev);
 	/* return if unsupported version */
 	if (!m_can_version) {
-		dev_err(&pdev->dev, "Unsupported version number: %2d",
+		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
 			m_can_version);
 		return -EINVAL;
 	}
 
-	priv = netdev_priv(dev);
-	netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
+	if (!m_can_dev->is_peripherial)
+		netif_napi_add(dev, &m_can_dev->napi,
+			       m_can_poll, M_CAN_NAPI_WEIGHT);
 
 	/* Shared properties of all M_CAN versions */
-	priv->version = m_can_version;
-	priv->dev = dev;
-	priv->base = addr;
-	priv->can.do_set_mode = m_can_set_mode;
-	priv->can.do_get_berr_counter = m_can_get_berr_counter;
+	m_can_dev->version = m_can_version;
+	m_can_dev->can.do_set_mode = m_can_set_mode;
+	m_can_dev->can.do_get_berr_counter = m_can_get_berr_counter;
 
 	/* Set M_CAN supported operations */
-	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+	m_can_dev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
 					CAN_CTRLMODE_LISTENONLY |
 					CAN_CTRLMODE_BERR_REPORTING |
 					CAN_CTRLMODE_FD;
 
 	/* Set properties depending on M_CAN version */
-	switch (priv->version) {
+	switch (m_can_dev->version) {
 	case 30:
 		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
 		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
-		priv->can.bittiming_const = &m_can_bittiming_const_30X;
-		priv->can.data_bittiming_const =
+		if (m_can_dev->bit_timing)
+			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+		else
+			m_can_dev->can.bittiming_const =
+				&m_can_bittiming_const_30X;
+		if (m_can_dev->data_timing)
+			m_can_dev->can.data_bittiming_const =
+				m_can_dev->data_timing;
+		else
+			m_can_dev->can.data_bittiming_const =
 				&m_can_data_bittiming_const_30X;
 		break;
 	case 31:
 		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
 		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
-		priv->can.bittiming_const = &m_can_bittiming_const_31X;
-		priv->can.data_bittiming_const =
+		if (m_can_dev->bit_timing)
+			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+		else
+			m_can_dev->can.bittiming_const =
+				&m_can_bittiming_const_31X;
+		if (m_can_dev->data_timing)
+			m_can_dev->can.data_bittiming_const =
+				m_can_dev->data_timing;
+		else
+			m_can_dev->can.data_bittiming_const =
 				&m_can_data_bittiming_const_31X;
 		break;
 	case 32:
-		priv->can.bittiming_const = &m_can_bittiming_const_31X;
-		priv->can.data_bittiming_const =
+		if (m_can_dev->bit_timing)
+			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
+		else
+			m_can_dev->can.bittiming_const =
+				&m_can_bittiming_const_31X;
+
+		if (m_can_dev->data_timing)
+			m_can_dev->can.data_bittiming_const =
+				m_can_dev->data_timing;
+		else
+			m_can_dev->can.data_bittiming_const =
 				&m_can_data_bittiming_const_31X;
-		priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
+
+		m_can_dev->can.ctrlmode_supported |=
+						(m_can_niso_supported(m_can_dev)
 						? CAN_CTRLMODE_FD_NON_ISO
 						: 0);
 		break;
 	default:
-		dev_err(&pdev->dev, "Unsupported version number: %2d",
-			priv->version);
+		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
+			m_can_dev->version);
 		return -EINVAL;
 	}
 
-	return 0;
-}
-
-static int m_can_open(struct net_device *dev)
-{
-	struct m_can_priv *priv = netdev_priv(dev);
-	int err;
-
-	err = m_can_clk_start(priv);
-	if (err)
-		return err;
-
-	/* open the can device */
-	err = open_candev(dev);
-	if (err) {
-		netdev_err(dev, "failed to open can device\n");
-		goto exit_disable_clks;
-	}
-
-	/* register interrupt handler */
-	err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
-			  dev);
-	if (err < 0) {
-		netdev_err(dev, "failed to request interrupt\n");
-		goto exit_irq_fail;
-	}
-
-	/* start the m_can controller */
-	m_can_start(dev);
-
-	can_led_event(dev, CAN_LED_EVENT_OPEN);
-	napi_enable(&priv->napi);
-	netif_start_queue(dev);
+	if (m_can_dev->ops->device_init)
+		m_can_dev->ops->device_init(m_can_dev);
 
 	return 0;
-
-exit_irq_fail:
-	close_candev(dev);
-exit_disable_clks:
-	m_can_clk_stop(priv);
-	return err;
 }
 
 static void m_can_stop(struct net_device *dev)
@@ -1361,10 +1365,17 @@ static int m_can_close(struct net_device *dev)
 	struct m_can_priv *priv = netdev_priv(dev);
 
 	netif_stop_queue(dev);
-	napi_disable(&priv->napi);
+	if (!priv->is_peripherial)
+		napi_disable(&priv->napi);
 	m_can_stop(dev);
 	m_can_clk_stop(priv);
 	free_irq(dev->irq, dev);
+
+	if (priv->is_peripherial) {
+		destroy_workqueue(priv->wq);
+		priv->wq = NULL;
+	}
+
 	close_candev(dev);
 	can_led_event(dev, CAN_LED_EVENT_STOP);
 
@@ -1385,18 +1396,15 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
 	return !!priv->can.echo_skb[next_idx];
 }
 
-static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
-				    struct net_device *dev)
+static void m_can_tx_handler(struct m_can_priv *priv)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
-	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+	struct canfd_frame *cf = (struct canfd_frame *)priv->tx_skb->data;
+	struct net_device *dev = priv->net;
+	struct sk_buff *skb = priv->tx_skb;
 	u32 id, cccr, fdflags;
 	int i;
 	int putidx;
 
-	if (can_dropped_invalid_skb(dev, skb))
-		return NETDEV_TX_OK;
-
 	/* Generate ID field for TX buffer Element */
 	/* Common to all supported M_CAN versions */
 	if (cf->can_id & CAN_EFF_FLAG) {
@@ -1451,7 +1459,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 			netif_stop_queue(dev);
 			netdev_warn(dev,
 				    "TX queue active although FIFO is full.");
-			return NETDEV_TX_BUSY;
+			return;
 		}
 
 		/* get put index for frame */
@@ -1492,14 +1500,100 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 		m_can_write(priv, M_CAN_TXBAR, (1 << putidx));
 
 		/* stop network queue if fifo full */
-			if (m_can_tx_fifo_full(priv) ||
-			    m_can_next_echo_skb_occupied(dev, putidx))
-				netif_stop_queue(dev);
+		if (m_can_tx_fifo_full(priv) ||
+		    m_can_next_echo_skb_occupied(dev, putidx))
+			netif_stop_queue(dev);
+	}
+}
+
+static void m_can_tx_work_queue(struct work_struct *ws)
+{
+	struct m_can_priv *priv = container_of(ws, struct m_can_priv,
+						tx_work);
+	m_can_tx_handler(priv);
+}
+
+static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
+				    struct net_device *dev)
+{
+	struct m_can_priv *priv = netdev_priv(dev);
+
+	if (can_dropped_invalid_skb(dev, skb))
+		return NETDEV_TX_OK;
+
+	priv->tx_skb = skb;
+	if (priv->is_peripherial) {
+		netif_stop_queue(priv->net);
+		queue_work(priv->wq, &priv->tx_work);
+	} else {
+		m_can_tx_handler(priv);
 	}
 
 	return NETDEV_TX_OK;
 }
 
+static int m_can_open(struct net_device *dev)
+{
+	struct m_can_priv *priv = netdev_priv(dev);
+	int err;
+
+	err = m_can_clk_start(priv);
+	if (err)
+		return err;
+
+	/* open the can device */
+	err = open_candev(dev);
+	if (err) {
+		netdev_err(dev, "failed to open can device\n");
+		goto exit_disable_clks;
+	}
+
+	/* register interrupt handler */
+	if (priv->is_peripherial) {
+		priv->wq = alloc_workqueue("mcan_wq",
+					   WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
+		if (!priv->wq) {
+			err = -ENOMEM;
+			goto out_wq_fail;
+		}
+
+		INIT_WORK(&priv->tx_work, m_can_tx_work_queue);
+
+		err = request_threaded_irq(dev->irq, NULL, m_can_isr,
+					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+					   dev->name, dev);
+	} else {
+		err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
+				  dev);
+	}
+
+	if (err < 0) {
+		netdev_err(dev, "failed to request interrupt\n");
+		goto exit_irq_fail;
+	}
+
+	/* start the m_can controller */
+	m_can_start(dev);
+
+	can_led_event(dev, CAN_LED_EVENT_OPEN);
+
+	if (!priv->is_peripherial)
+		napi_enable(&priv->napi);
+
+	netif_start_queue(dev);
+
+	return 0;
+
+exit_irq_fail:
+	if (priv->is_peripherial)
+		destroy_workqueue(priv->wq);
+out_wq_fail:
+	close_candev(dev);
+exit_disable_clks:
+	m_can_clk_stop(priv);
+	return err;
+}
+
 static const struct net_device_ops m_can_netdev_ops = {
 	.ndo_open = m_can_open,
 	.ndo_stop = m_can_close,
@@ -1515,20 +1609,6 @@ static int register_m_can_dev(struct net_device *dev)
 	return register_candev(dev);
 }
 
-static void m_can_init_ram(struct m_can_priv *priv)
-{
-	int end, i, start;
-
-	/* initialize the entire Message RAM in use to avoid possible
-	 * ECC/parity checksum errors when reading an uninitialized buffer
-	 */
-	start = priv->mcfg[MRAM_SIDF].off;
-	end = priv->mcfg[MRAM_TXB].off +
-		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
-	for (i = start; i < end; i += 4)
-		writel(0x0, priv->mram_base + i);
-}
-
 static void m_can_of_parse_mram(struct m_can_priv *priv,
 				const u32 *mram_config_vals)
 {
@@ -1556,9 +1636,8 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
 	priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
 			(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
 
-	dev_dbg(priv->device,
-		"mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
-		priv->mram_base,
+	dev_dbg(priv->dev,
+		"sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
 		priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
 		priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
 		priv->mcfg[MRAM_RXF0].off, priv->mcfg[MRAM_RXF0].num,
@@ -1566,63 +1645,55 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
 		priv->mcfg[MRAM_RXB].off, priv->mcfg[MRAM_RXB].num,
 		priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
 		priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
-
-	m_can_init_ram(priv);
 }
 
-static int m_can_plat_probe(struct platform_device *pdev)
+void m_can_init_ram(struct m_can_priv *priv)
 {
-	struct net_device *dev;
-	struct m_can_priv *priv;
-	struct resource *res;
-	void __iomem *addr;
-	void __iomem *mram_addr;
-	struct clk *hclk, *cclk;
-	int irq, ret;
-	struct device_node *np;
-	u32 mram_config_vals[MRAM_CFG_LEN];
-	u32 tx_fifo_size;
-
-	np = pdev->dev.of_node;
+	int end, i, start;
 
-	hclk = devm_clk_get(&pdev->dev, "hclk");
-	cclk = devm_clk_get(&pdev->dev, "cclk");
+	/* initialize the entire Message RAM in use to avoid possible
+	 * ECC/parity checksum errors when reading an uninitialized buffer
+	 */
+	start = priv->mcfg[MRAM_SIDF].off;
+	end = priv->mcfg[MRAM_TXB].off +
+		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
 
-	if (IS_ERR(hclk) || IS_ERR(cclk)) {
-		dev_err(&pdev->dev, "no clock found\n");
-		ret = -ENODEV;
-		goto failed_ret;
-	}
+	for (i = start; i < end; i += 4)
+		m_can_fifo_write_no_off(priv, i, 0x0);
+}
+EXPORT_SYMBOL_GPL(m_can_init_ram);
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	irq = platform_get_irq_byname(pdev, "int0");
+int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
+{
+	int ret = 0;
 
-	if (IS_ERR(addr) || irq < 0) {
-		ret = -EINVAL;
-		goto failed_ret;
-	}
+	m_can_dev->hclk = devm_clk_get(m_can_dev->dev, "hclk");
+	m_can_dev->cclk = devm_clk_get(m_can_dev->dev, "cclk");
 
-	/* message ram could be shared */
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
-	if (!res) {
+	if (IS_ERR(m_can_dev->cclk)) {
+		dev_err(m_can_dev->dev, "no clock found\n");
 		ret = -ENODEV;
-		goto failed_ret;
 	}
 
-	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-	if (!mram_addr) {
-		ret = -ENOMEM;
-		goto failed_ret;
-	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
 
-	/* get message ram configuration */
-	ret = of_property_read_u32_array(np, "bosch,mram-cfg",
-					 mram_config_vals,
-					 sizeof(mram_config_vals) / 4);
+struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
+{
+	struct m_can_priv *class_dev = NULL;
+	u32 mram_config_vals[MRAM_CFG_LEN];
+	struct net_device *net_dev;
+	u32 tx_fifo_size;
+	int ret;
+
+	ret = fwnode_property_read_u32_array(dev_fwnode(dev),
+					     "bosch,mram-cfg",
+					     mram_config_vals,
+					     sizeof(mram_config_vals) / 4);
 	if (ret) {
-		dev_err(&pdev->dev, "Could not get Message RAM configuration.");
-		goto failed_ret;
+		dev_err(dev, "Could not get Message RAM configuration.");
+		goto out;
 	}
 
 	/* Get TX FIFO size
@@ -1631,66 +1702,74 @@ static int m_can_plat_probe(struct platform_device *pdev)
 	tx_fifo_size = mram_config_vals[7];
 
 	/* allocate the m_can device */
-	dev = alloc_candev(sizeof(*priv), tx_fifo_size);
-	if (!dev) {
-		ret = -ENOMEM;
-		goto failed_ret;
+	net_dev = alloc_candev(sizeof(*class_dev), tx_fifo_size);
+	if (!net_dev) {
+		dev_err(dev, "Failed to allocate CAN device");
+		goto out;
 	}
 
-	priv = netdev_priv(dev);
-	dev->irq = irq;
-	priv->device = &pdev->dev;
-	priv->hclk = hclk;
-	priv->cclk = cclk;
-	priv->can.clock.freq = clk_get_rate(cclk);
-	priv->mram_base = mram_addr;
+	class_dev = netdev_priv(net_dev);
+	if (!class_dev) {
+		dev_err(dev, "Failed to init netdev private");
+		goto out;
+	}
 
-	platform_set_drvdata(pdev, dev);
-	SET_NETDEV_DEV(dev, &pdev->dev);
+	class_dev->net = net_dev;
+	class_dev->dev = dev;
+	SET_NETDEV_DEV(net_dev, dev);
 
-	/* Enable clocks. Necessary to read Core Release in order to determine
-	 * M_CAN version
-	 */
-	pm_runtime_enable(&pdev->dev);
-	ret = m_can_clk_start(priv);
-	if (ret)
-		goto pm_runtime_fail;
+	m_can_of_parse_mram(class_dev, mram_config_vals);
+out:
+	return class_dev;
+}
+EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
+
+int m_can_class_register(struct m_can_priv *m_can_dev)
+{
+	int ret;
 
-	ret = m_can_dev_setup(pdev, dev, addr);
+	if (m_can_dev->pm_clock_support) {
+		pm_runtime_enable(m_can_dev->dev);
+		ret = m_can_clk_start(m_can_dev);
+		if (ret)
+			goto pm_runtime_fail;
+	}
+
+	ret = m_can_dev_setup(m_can_dev);
 	if (ret)
 		goto clk_disable;
 
-	ret = register_m_can_dev(dev);
+	ret = register_m_can_dev(m_can_dev->net);
 	if (ret) {
-		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
-			KBUILD_MODNAME, ret);
+		dev_err(m_can_dev->dev, "registering %s failed (err=%d)\n",
+			m_can_dev->net->name, ret);
 		goto clk_disable;
 	}
 
-	m_can_of_parse_mram(priv, mram_config_vals);
-
-	devm_can_led_init(dev);
+	devm_can_led_init(m_can_dev->net);
 
-	of_can_transceiver(dev);
+	of_can_transceiver(m_can_dev->net);
 
-	dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
-		 KBUILD_MODNAME, dev->irq, priv->version);
+	dev_info(m_can_dev->dev, "%s device registered (irq=%d, version=%d)\n",
+		 KBUILD_MODNAME, m_can_dev->net->irq, m_can_dev->version);
 
 	/* Probe finished
 	 * Stop clocks. They will be reactivated once the M_CAN device is opened
 	 */
 clk_disable:
-	m_can_clk_stop(priv);
+	m_can_clk_stop(m_can_dev);
 pm_runtime_fail:
 	if (ret) {
-		pm_runtime_disable(&pdev->dev);
-		free_candev(dev);
+		if (m_can_dev->pm_clock_support)
+			pm_runtime_disable(m_can_dev->dev);
+		free_candev(m_can_dev->net);
 	}
-failed_ret:
+
 	return ret;
 }
+EXPORT_SYMBOL_GPL(m_can_class_register);
 
-static __maybe_unused int m_can_suspend(struct device *dev)
+int m_can_class_suspend(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
 	struct m_can_priv *priv = netdev_priv(ndev);
@@ -1708,8 +1787,9 @@ static __maybe_unused int m_can_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(m_can_class_suspend);
 
-static __maybe_unused int m_can_resume(struct device *dev)
+int m_can_class_resume(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
 	struct m_can_priv *priv = netdev_priv(ndev);
@@ -1733,79 +1813,19 @@ static __maybe_unused int m_can_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(m_can_class_resume);
 
-static void unregister_m_can_dev(struct net_device *dev)
+void m_can_class_unregister(struct m_can_priv *m_can_dev)
 {
-	unregister_candev(dev);
-}
+	unregister_candev(m_can_dev->net);
 
-static int m_can_plat_remove(struct platform_device *pdev)
-{
-	struct net_device *dev = platform_get_drvdata(pdev);
+	m_can_clk_stop(m_can_dev);
 
-	unregister_m_can_dev(dev);
-
-	pm_runtime_disable(&pdev->dev);
-
-	platform_set_drvdata(pdev, NULL);
-
-	free_candev(dev);
-
-	return 0;
-}
-
-static int __maybe_unused m_can_runtime_suspend(struct device *dev)
-{
-	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *priv = netdev_priv(ndev);
-
-	clk_disable_unprepare(priv->cclk);
-	clk_disable_unprepare(priv->hclk);
-
-	return 0;
-}
-
-static int __maybe_unused m_can_runtime_resume(struct device *dev)
-{
-	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *priv = netdev_priv(ndev);
-	int err;
-
-	err = clk_prepare_enable(priv->hclk);
-	if (err)
-		return err;
-
-	err = clk_prepare_enable(priv->cclk);
-	if (err)
-		clk_disable_unprepare(priv->hclk);
-
-	return err;
+	free_candev(m_can_dev->net);
 }
-
-static const struct dev_pm_ops m_can_pmops = {
-	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
-			   m_can_runtime_resume, NULL)
-	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
-};
-
-static const struct of_device_id m_can_of_table[] = {
-	{ .compatible = "bosch,m_can", .data = NULL },
-	{ /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, m_can_of_table);
-
-static struct platform_driver m_can_plat_driver = {
-	.driver = {
-		.name = KBUILD_MODNAME,
-		.of_match_table = m_can_of_table,
-		.pm     = &m_can_pmops,
-	},
-	.probe = m_can_plat_probe,
-	.remove = m_can_plat_remove,
-};
-
-module_platform_driver(m_can_plat_driver);
+EXPORT_SYMBOL_GPL(m_can_class_unregister);
 
 MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
new file mode 100644
index 000000000000..70ab7bba063d
--- /dev/null
+++ b/drivers/net/can/m_can/m_can.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* CAN bus driver for Bosch M_CAN controller
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _CAN_M_CAN_H_
+#define _CAN_M_CAN_H_
+
+#include <linux/can/core.h>
+#include <linux/can/led.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/delay.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/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/can/dev.h>
+#include <linux/pinctrl/consumer.h>
+
+/* m_can lec values */
+enum m_can_lec_type {
+	LEC_NO_ERROR = 0,
+	LEC_STUFF_ERROR,
+	LEC_FORM_ERROR,
+	LEC_ACK_ERROR,
+	LEC_BIT1_ERROR,
+	LEC_BIT0_ERROR,
+	LEC_CRC_ERROR,
+	LEC_UNUSED,
+};
+
+enum m_can_mram_cfg {
+	MRAM_SIDF = 0,
+	MRAM_XIDF,
+	MRAM_RXF0,
+	MRAM_RXF1,
+	MRAM_RXB,
+	MRAM_TXE,
+	MRAM_TXB,
+	MRAM_CFG_NUM,
+};
+
+/* address offset and element number for each FIFO/Buffer in the Message RAM */
+struct mram_cfg {
+	u16 off;
+	u8  num;
+};
+
+struct m_can_priv;
+struct m_can_ops {
+	/* Device specific call backs */
+	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);
+	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
+	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
+	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
+	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
+			  int val);
+	int (*device_init)(struct m_can_priv *m_can_class);
+};
+
+struct m_can_priv {
+	struct can_priv can;
+	struct napi_struct napi;
+	struct net_device *net;
+	struct device *dev;
+	struct clk *hclk;
+	struct clk *cclk;
+
+	struct workqueue_struct *wq;
+	struct work_struct tx_work;
+	struct sk_buff *tx_skb;
+
+	struct can_bittiming_const *bit_timing;
+	struct can_bittiming_const *data_timing;
+
+	struct m_can_ops *ops;
+
+	void *device_data;
+
+	int version;
+	int freq;
+	u32 irqstatus;
+
+	int pm_clock_support;
+	int is_peripherial;
+
+	struct mram_cfg mcfg[MRAM_CFG_NUM];
+};
+
+struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
+int m_can_class_register(struct m_can_priv *m_can_dev);
+void m_can_class_unregister(struct m_can_priv *m_can_dev);
+int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
+void m_can_init_ram(struct m_can_priv *priv);
+void m_can_config_endisable(struct m_can_priv *priv, bool enable);
+
+int m_can_class_suspend(struct device *dev);
+int m_can_class_resume(struct device *dev);
+#endif	/* _CAN_M_H_ */
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
new file mode 100644
index 000000000000..92d989ce032a
--- /dev/null
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+// IOMapped CAN bus driver for Bosch M_CAN controller
+// Copyright (C) 2014 Freescale Semiconductor, Inc.
+//	Dong Aisheng <b29396@freescale.com>
+//
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/platform_device.h>
+
+#include "m_can.h"
+
+struct m_can_plat_priv {
+	void __iomem *base;
+	void __iomem *mram_base;
+};
+
+static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
+{
+	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+	return readl(priv->base + reg);
+}
+
+static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
+{
+	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+	return readl(priv->mram_base + offset);
+}
+
+static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
+{
+	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+	writel(val, priv->base + reg);
+
+	return 0;
+}
+
+static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
+{
+	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
+
+	writel(val, priv->mram_base + offset);
+
+	return 0;
+}
+
+static struct m_can_ops m_can_plat_ops = {
+	.read_reg = iomap_read_reg,
+	.write_reg = iomap_write_reg,
+	.write_fifo = iomap_write_fifo,
+	.read_fifo = iomap_read_fifo,
+};
+
+static int m_can_plat_probe(struct platform_device *pdev)
+{
+	struct m_can_priv *mcan_class;
+	struct m_can_plat_priv *priv;
+	struct resource *res;
+	void __iomem *addr;
+	void __iomem *mram_addr;
+	int irq, ret = 0;
+
+	mcan_class = m_can_class_allocate_dev(&pdev->dev);
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mcan_class->device_data = priv;
+
+	m_can_class_get_clocks(mcan_class);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
+	addr = devm_ioremap_resource(&pdev->dev, res);
+	irq = platform_get_irq_byname(pdev, "int0");
+	if (IS_ERR(addr) || irq < 0) {
+		ret = -EINVAL;
+		goto failed_ret;
+	}
+
+	/* message ram could be shared */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
+	if (!res) {
+		ret = -ENODEV;
+		goto failed_ret;
+	}
+
+	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!mram_addr) {
+		ret = -ENOMEM;
+		goto failed_ret;
+	}
+
+	priv->base = addr;
+	priv->mram_base = mram_addr;
+
+	mcan_class->net->irq = irq;
+	mcan_class->pm_clock_support = 1;
+	mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
+	mcan_class->dev = &pdev->dev;
+
+	mcan_class->ops = &m_can_plat_ops;
+
+	mcan_class->is_peripherial = false;
+
+	platform_set_drvdata(pdev, mcan_class->dev);
+
+	m_can_init_ram(mcan_class);
+
+	ret = m_can_class_register(mcan_class);
+
+failed_ret:
+	return ret;
+}
+
+static __maybe_unused int m_can_suspend(struct device *dev)
+{
+	return m_can_class_suspend(dev);
+}
+
+static __maybe_unused int m_can_resume(struct device *dev)
+{
+	return m_can_class_resume(dev);
+}
+
+static int m_can_plat_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct m_can_priv *mcan_class = netdev_priv(dev);
+
+	m_can_class_unregister(mcan_class);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static int __maybe_unused m_can_runtime_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct m_can_priv *mcan_class = netdev_priv(ndev);
+
+	m_can_class_suspend(dev);
+
+	clk_disable_unprepare(mcan_class->cclk);
+	clk_disable_unprepare(mcan_class->hclk);
+
+	return 0;
+}
+
+static int __maybe_unused m_can_runtime_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct m_can_priv *mcan_class = netdev_priv(ndev);
+	int err;
+
+	err = clk_prepare_enable(mcan_class->hclk);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(mcan_class->cclk);
+	if (err)
+		clk_disable_unprepare(mcan_class->hclk);
+
+	m_can_class_resume(dev);
+
+	return err;
+}
+
+static const struct dev_pm_ops m_can_pmops = {
+	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
+			   m_can_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
+};
+
+static const struct of_device_id m_can_of_table[] = {
+	{ .compatible = "bosch,m_can", .data = NULL },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, m_can_of_table);
+
+static struct platform_driver m_can_plat_driver = {
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.of_match_table = m_can_of_table,
+		.pm     = &m_can_pmops,
+	},
+	.probe = m_can_plat_probe,
+	.remove = m_can_plat_remove,
+};
+
+module_platform_driver(m_can_plat_driver);
+
+MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");
-- 
2.20.1.390.gb5101f9297


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev
  2019-03-01 18:50 [PATCH v6 1/4] can: m_can: Create a m_can platform framework Dan Murphy
@ 2019-03-01 18:50 ` Dan Murphy
  2019-03-04 17:31   ` Wolfgang Grandegger
  2019-03-01 18:50 ` [PATCH v6 3/4] dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver Dan Murphy
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-01 18:50 UTC (permalink / raw)
  To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy

Rename the common m_can_priv class structure to
m_can_classdev as this is more descriptive.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v6 - No changes only rebase changes possibly can squash into the first patch - 
https://lore.kernel.org/patchwork/patch/1042444/

 drivers/net/can/m_can/m_can.c          | 94 +++++++++++++-------------
 drivers/net/can/m_can/m_can.h          | 28 ++++----
 drivers/net/can/m_can/m_can_platform.c | 16 ++---
 3 files changed, 69 insertions(+), 69 deletions(-)

diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index b37d0886f9cb..817e623c8a32 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -319,7 +319,7 @@ enum m_can_reg {
 #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT
 #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT)
 
-static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
+static u32 m_can_read(struct m_can_classdev *priv, enum m_can_reg reg)
 {
 	if (priv->ops->read_reg)
 		return priv->ops->read_reg(priv, reg);
@@ -327,7 +327,7 @@ static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
 		return -EINVAL;
 }
 
-static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
+static int m_can_write(struct m_can_classdev *priv, enum m_can_reg reg, u32 val)
 {
 	if (priv->ops->write_reg)
 		return priv->ops->write_reg(priv, reg, val);
@@ -335,7 +335,7 @@ static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
 		return -EINVAL;
 }
 
-static u32 m_can_fifo_read(struct m_can_priv *priv,
+static u32 m_can_fifo_read(struct m_can_classdev *priv,
 			   u32 fgi, unsigned int offset)
 {
 	u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
@@ -347,7 +347,7 @@ static u32 m_can_fifo_read(struct m_can_priv *priv,
 		return -EINVAL;
 }
 
-static u32 m_can_fifo_write(struct m_can_priv *priv,
+static u32 m_can_fifo_write(struct m_can_classdev *priv,
 			    u32 fpi, unsigned int offset, u32 val)
 {
 	u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
@@ -359,7 +359,7 @@ static u32 m_can_fifo_write(struct m_can_priv *priv,
 		return -EINVAL;
 }
 
-static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
+static u32 m_can_fifo_write_no_off(struct m_can_classdev *priv,
 				   u32 fpi, u32 val)
 {
 	if (priv->ops->write_fifo)
@@ -368,7 +368,7 @@ static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
 		return 0;
 }
 
-static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
+static u32 m_can_txe_fifo_read(struct m_can_classdev *priv, u32 fgi, u32 offset)
 {
 	u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
 			  offset;
@@ -379,12 +379,12 @@ static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
 		return -EINVAL;
 }
 
-static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
+static inline bool m_can_tx_fifo_full(struct m_can_classdev *priv)
 {
 		return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
 }
 
-void m_can_config_endisable(struct m_can_priv *priv, bool enable)
+void m_can_config_endisable(struct m_can_classdev *priv, bool enable)
 {
 	u32 cccr = m_can_read(priv, M_CAN_CCCR);
 	u32 timeout = 10;
@@ -417,13 +417,13 @@ void m_can_config_endisable(struct m_can_priv *priv, bool enable)
 	}
 }
 
-static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
+static inline void m_can_enable_all_interrupts(struct m_can_classdev *priv)
 {
 	/* Only interrupt line 0 is used in this driver */
 	m_can_write(priv, M_CAN_ILE, ILE_EINT0);
 }
 
-static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
+static inline void m_can_disable_all_interrupts(struct m_can_classdev *priv)
 {
 	m_can_write(priv, M_CAN_ILE, 0x0);
 }
@@ -431,7 +431,7 @@ static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
 static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
 {
 	struct net_device_stats *stats = &dev->stats;
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	struct canfd_frame *cf;
 	struct sk_buff *skb;
 	u32 id, fgi, dlc;
@@ -488,7 +488,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
 
 static int m_can_do_rx_poll(struct net_device *dev, int quota)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	u32 pkts = 0;
 	u32 rxfs;
 
@@ -541,7 +541,7 @@ static int m_can_handle_lost_msg(struct net_device *dev)
 static int m_can_handle_lec_err(struct net_device *dev,
 				enum m_can_lec_type lec_type)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 	struct can_frame *cf;
 	struct sk_buff *skb;
@@ -598,7 +598,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
 static int __m_can_get_berr_counter(const struct net_device *dev,
 				    struct can_berr_counter *bec)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	unsigned int ecr;
 
 	ecr = m_can_read(priv, M_CAN_ECR);
@@ -608,7 +608,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev,
 	return 0;
 }
 
-static int m_can_clk_start(struct m_can_priv *priv)
+static int m_can_clk_start(struct m_can_classdev *priv)
 {
 	int err;
 
@@ -624,7 +624,7 @@ static int m_can_clk_start(struct m_can_priv *priv)
 	return 0;
 }
 
-static void m_can_clk_stop(struct m_can_priv *priv)
+static void m_can_clk_stop(struct m_can_classdev *priv)
 {
 	if (priv->pm_clock_support)
 		pm_runtime_put_sync(priv->dev);
@@ -633,7 +633,7 @@ static void m_can_clk_stop(struct m_can_priv *priv)
 static int m_can_get_berr_counter(const struct net_device *dev,
 				  struct can_berr_counter *bec)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int err;
 
 	err = m_can_clk_start(priv);
@@ -650,7 +650,7 @@ static int m_can_get_berr_counter(const struct net_device *dev,
 static int m_can_handle_state_change(struct net_device *dev,
 				     enum can_state new_state)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 	struct can_frame *cf;
 	struct sk_buff *skb;
@@ -724,7 +724,7 @@ static int m_can_handle_state_change(struct net_device *dev,
 
 static int m_can_handle_state_errors(struct net_device *dev, u32 psr)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int work_done = 0;
 
 	if ((psr & PSR_EW) &&
@@ -777,7 +777,7 @@ static inline bool is_lec_err(u32 psr)
 static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
 				   u32 psr)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int work_done = 0;
 
 	if (irqstatus & IR_RF0L)
@@ -796,7 +796,7 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
 
 static int m_can_rx_handler(struct net_device *dev, int quota)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int work_done = 0;
 	u32 irqstatus, psr;
 
@@ -819,7 +819,7 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
 
 static int m_can_rx_peripherial(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 
 	m_can_rx_handler(dev, 1);
 
@@ -831,7 +831,7 @@ static int m_can_rx_peripherial(struct net_device *dev)
 static int m_can_poll(struct napi_struct *napi, int quota)
 {
 	struct net_device *dev = napi->dev;
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int work_done;
 
 	work_done = m_can_rx_handler(dev, quota);
@@ -851,7 +851,7 @@ static void m_can_echo_tx_event(struct net_device *dev)
 	int i = 0;
 	unsigned int msg_mark;
 
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 
 	/* read tx event fifo status */
@@ -884,7 +884,7 @@ static void m_can_echo_tx_event(struct net_device *dev)
 static irqreturn_t m_can_isr(int irq, void *dev_id)
 {
 	struct net_device *dev = (struct net_device *)dev_id;
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 	u32 ir;
 
@@ -985,7 +985,7 @@ static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
 
 static int m_can_set_bittiming(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	const struct can_bittiming *bt = &priv->can.bittiming;
 	const struct can_bittiming *dbt = &priv->can.data_bittiming;
 	u16 brp, sjw, tseg1, tseg2;
@@ -1058,7 +1058,7 @@ static int m_can_set_bittiming(struct net_device *dev)
  */
 static void m_can_chip_config(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	u32 cccr, test;
 
 	m_can_config_endisable(priv, true);
@@ -1170,7 +1170,7 @@ static void m_can_chip_config(struct net_device *dev)
 
 static void m_can_start(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 
 	/* basic m_can configuration */
 	m_can_chip_config(dev);
@@ -1199,7 +1199,7 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
  * else it returns the release and step coded as:
  * return value = 10 * <release> + 1 * <step>
  */
-static int m_can_check_core_release(struct m_can_priv *priv)
+static int m_can_check_core_release(struct m_can_classdev *priv)
 {
 	u32 crel_reg;
 	u8 rel;
@@ -1227,7 +1227,7 @@ static int m_can_check_core_release(struct m_can_priv *priv)
 /* Selectable Non ISO support only in version 3.2.x
  * This function checks if the bit is writable.
  */
-static bool m_can_niso_supported(struct m_can_priv *priv)
+static bool m_can_niso_supported(struct m_can_classdev *priv)
 {
 	u32 cccr_reg, cccr_poll = 0;
 	int niso_timeout = -ETIMEDOUT;
@@ -1258,7 +1258,7 @@ static bool m_can_niso_supported(struct m_can_priv *priv)
 	return !niso_timeout;
 }
 
-static int m_can_dev_setup(struct m_can_priv *m_can_dev)
+static int m_can_dev_setup(struct m_can_classdev *m_can_dev)
 {
 	struct net_device *dev = m_can_dev->net;
 	int m_can_version;
@@ -1351,7 +1351,7 @@ static int m_can_dev_setup(struct m_can_priv *m_can_dev)
 
 static void m_can_stop(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 
 	/* disable all interrupts */
 	m_can_disable_all_interrupts(priv);
@@ -1362,7 +1362,7 @@ static void m_can_stop(struct net_device *dev)
 
 static int m_can_close(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 
 	netif_stop_queue(dev);
 	if (!priv->is_peripherial)
@@ -1384,7 +1384,7 @@ static int m_can_close(struct net_device *dev)
 
 static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	/*get wrap around for loopback skb index */
 	unsigned int wrap = priv->can.echo_skb_max;
 	int next_idx;
@@ -1396,7 +1396,7 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
 	return !!priv->can.echo_skb[next_idx];
 }
 
-static void m_can_tx_handler(struct m_can_priv *priv)
+static void m_can_tx_handler(struct m_can_classdev *priv)
 {
 	struct canfd_frame *cf = (struct canfd_frame *)priv->tx_skb->data;
 	struct net_device *dev = priv->net;
@@ -1508,7 +1508,7 @@ static void m_can_tx_handler(struct m_can_priv *priv)
 
 static void m_can_tx_work_queue(struct work_struct *ws)
 {
-	struct m_can_priv *priv = container_of(ws, struct m_can_priv,
+	struct m_can_classdev *priv = container_of(ws, struct m_can_classdev,
 						tx_work);
 	m_can_tx_handler(priv);
 }
@@ -1516,7 +1516,7 @@ static void m_can_tx_work_queue(struct work_struct *ws)
 static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 				    struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 
 	if (can_dropped_invalid_skb(dev, skb))
 		return NETDEV_TX_OK;
@@ -1534,7 +1534,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 
 static int m_can_open(struct net_device *dev)
 {
-	struct m_can_priv *priv = netdev_priv(dev);
+	struct m_can_classdev *priv = netdev_priv(dev);
 	int err;
 
 	err = m_can_clk_start(priv);
@@ -1609,7 +1609,7 @@ static int register_m_can_dev(struct net_device *dev)
 	return register_candev(dev);
 }
 
-static void m_can_of_parse_mram(struct m_can_priv *priv,
+static void m_can_of_parse_mram(struct m_can_classdev *priv,
 				const u32 *mram_config_vals)
 {
 	priv->mcfg[MRAM_SIDF].off = mram_config_vals[0];
@@ -1647,7 +1647,7 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
 		priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
 }
 
-void m_can_init_ram(struct m_can_priv *priv)
+void m_can_init_ram(struct m_can_classdev *priv)
 {
 	int end, i, start;
 
@@ -1663,7 +1663,7 @@ void m_can_init_ram(struct m_can_priv *priv)
 }
 EXPORT_SYMBOL_GPL(m_can_init_ram);
 
-int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
+int m_can_class_get_clocks(struct m_can_classdev *m_can_dev)
 {
 	int ret = 0;
 
@@ -1679,9 +1679,9 @@ int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
 }
 EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
 
-struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
+struct m_can_classdev *m_can_class_allocate_dev(struct device *dev)
 {
-	struct m_can_priv *class_dev = NULL;
+	struct m_can_classdev *class_dev = NULL;
 	u32 mram_config_vals[MRAM_CFG_LEN];
 	struct net_device *net_dev;
 	u32 tx_fifo_size;
@@ -1724,7 +1724,7 @@ struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
 
-int m_can_class_register(struct m_can_priv *m_can_dev)
+int m_can_class_register(struct m_can_classdev *m_can_dev)
 {
 	int ret;
 
@@ -1772,7 +1772,7 @@ EXPORT_SYMBOL_GPL(m_can_class_register);
 int m_can_class_suspend(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *priv = netdev_priv(ndev);
+	struct m_can_classdev *priv = netdev_priv(ndev);
 
 	if (netif_running(ndev)) {
 		netif_stop_queue(ndev);
@@ -1792,7 +1792,7 @@ EXPORT_SYMBOL_GPL(m_can_class_suspend);
 int m_can_class_resume(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *priv = netdev_priv(ndev);
+	struct m_can_classdev *priv = netdev_priv(ndev);
 
 	pinctrl_pm_select_default_state(dev);
 
@@ -1815,7 +1815,7 @@ int m_can_class_resume(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(m_can_class_resume);
 
-void m_can_class_unregister(struct m_can_priv *m_can_dev)
+void m_can_class_unregister(struct m_can_classdev *m_can_dev)
 {
 	unregister_candev(m_can_dev->net);
 
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index 70ab7bba063d..b04b1427e9c4 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -57,19 +57,19 @@ struct mram_cfg {
 	u8  num;
 };
 
-struct m_can_priv;
+struct m_can_classdev;
 struct m_can_ops {
 	/* Device specific call backs */
-	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);
-	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
-	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
-	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
-	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
+	int (*clr_dev_interrupts)(struct m_can_classdev *m_can_class);
+	u32 (*read_reg)(struct m_can_classdev *m_can_class, int reg);
+	int (*write_reg)(struct m_can_classdev *m_can_class, int reg, int val);
+	u32 (*read_fifo)(struct m_can_classdev *m_can_class, int addr_offset);
+	int (*write_fifo)(struct m_can_classdev *m_can_class, int addr_offset,
 			  int val);
-	int (*device_init)(struct m_can_priv *m_can_class);
+	int (*device_init)(struct m_can_classdev *m_can_class);
 };
 
-struct m_can_priv {
+struct m_can_classdev {
 	struct can_priv can;
 	struct napi_struct napi;
 	struct net_device *net;
@@ -98,12 +98,12 @@ struct m_can_priv {
 	struct mram_cfg mcfg[MRAM_CFG_NUM];
 };
 
-struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
-int m_can_class_register(struct m_can_priv *m_can_dev);
-void m_can_class_unregister(struct m_can_priv *m_can_dev);
-int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
-void m_can_init_ram(struct m_can_priv *priv);
-void m_can_config_endisable(struct m_can_priv *priv, bool enable);
+struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
+int m_can_class_register(struct m_can_classdev *m_can_dev);
+void m_can_class_unregister(struct m_can_classdev *m_can_dev);
+int m_can_class_get_clocks(struct m_can_classdev *m_can_dev);
+void m_can_init_ram(struct m_can_classdev *priv);
+void m_can_config_endisable(struct m_can_classdev *priv, bool enable);
 
 int m_can_class_suspend(struct device *dev);
 int m_can_class_resume(struct device *dev);
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index 92d989ce032a..ae88d03cccd6 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -14,21 +14,21 @@ struct m_can_plat_priv {
 	void __iomem *mram_base;
 };
 
-static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
+static u32 iomap_read_reg(struct m_can_classdev *m_can_class, int reg)
 {
 	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
 
 	return readl(priv->base + reg);
 }
 
-static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
+static u32 iomap_read_fifo(struct m_can_classdev *m_can_class, int offset)
 {
 	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
 
 	return readl(priv->mram_base + offset);
 }
 
-static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
+static int iomap_write_reg(struct m_can_classdev *m_can_class, int reg, int val)
 {
 	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
 
@@ -37,7 +37,7 @@ static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
 	return 0;
 }
 
-static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
+static int iomap_write_fifo(struct m_can_classdev *m_can_class, int offset, int val)
 {
 	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
 
@@ -55,7 +55,7 @@ static struct m_can_ops m_can_plat_ops = {
 
 static int m_can_plat_probe(struct platform_device *pdev)
 {
-	struct m_can_priv *mcan_class;
+	struct m_can_classdev *mcan_class;
 	struct m_can_plat_priv *priv;
 	struct resource *res;
 	void __iomem *addr;
@@ -127,7 +127,7 @@ static __maybe_unused int m_can_resume(struct device *dev)
 static int m_can_plat_remove(struct platform_device *pdev)
 {
 	struct net_device *dev = platform_get_drvdata(pdev);
-	struct m_can_priv *mcan_class = netdev_priv(dev);
+	struct m_can_classdev *mcan_class = netdev_priv(dev);
 
 	m_can_class_unregister(mcan_class);
 
@@ -139,7 +139,7 @@ static int m_can_plat_remove(struct platform_device *pdev)
 static int __maybe_unused m_can_runtime_suspend(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *mcan_class = netdev_priv(ndev);
+	struct m_can_classdev *mcan_class = netdev_priv(ndev);
 
 	m_can_class_suspend(dev);
 
@@ -152,7 +152,7 @@ static int __maybe_unused m_can_runtime_suspend(struct device *dev)
 static int __maybe_unused m_can_runtime_resume(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
-	struct m_can_priv *mcan_class = netdev_priv(ndev);
+	struct m_can_classdev *mcan_class = netdev_priv(ndev);
 	int err;
 
 	err = clk_prepare_enable(mcan_class->hclk);
-- 
2.20.1.390.gb5101f9297


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v6 3/4] dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver
  2019-03-01 18:50 [PATCH v6 1/4] can: m_can: Create a m_can platform framework Dan Murphy
  2019-03-01 18:50 ` [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev Dan Murphy
@ 2019-03-01 18:50 ` Dan Murphy
  2019-03-01 18:50 ` [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel Dan Murphy
  2019-03-04 16:56 ` [PATCH v6 1/4] can: m_can: Create a m_can platform framework Wolfgang Grandegger
  3 siblings, 0 replies; 16+ messages in thread
From: Dan Murphy @ 2019-03-01 18:50 UTC (permalink / raw)
  To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy

DT binding documentation for TI TCAN4x5x driver.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v6 - No changes - https://lore.kernel.org/patchwork/patch/1042445/

 .../devicetree/bindings/net/can/tcan4x5x.txt  | 37 +++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/can/tcan4x5x.txt

diff --git a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt
new file mode 100644
index 000000000000..781c19887538
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt
@@ -0,0 +1,37 @@
+Texas Instruments TCAN4x5x CAN Controller
+================================================
+
+This file provides device node information for the TCAN4x5x interface contains.
+
+Required properties:
+	- compatible: "ti,tcan4x5x"
+	- reg: 0
+	- #address-cells: 1
+	- #size-cells: 0
+	- spi-max-frequency: Maximum frequency of the SPI bus the chip can
+			     operate at should be less than or equal to 18 MHz.
+	- data-ready-gpios: Interrupt GPIO for data and error reporting.
+	- device-wake-gpios: Wake up GPIO to wake up the TCAN device.
+	- device-state-gpios: Input GPIO that indicates if the device is in
+			      a sleep state or if the device is active.
+
+See Documentation/devicetree/bindings/net/can/m_can.txt for additional
+required property details.
+
+Optional properties:
+	- reset-gpios: Hardwired output GPIO. If not defined then software
+		       reset.
+
+Example:
+tcan4x5x: tcan4x5x@0 {
+		compatible = "ti,tcan4x5x";
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		spi-max-frequency = <10000000>;
+		bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
+		data-ready-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
+		device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
+		device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
+};
-- 
2.20.1.390.gb5101f9297


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel
  2019-03-01 18:50 [PATCH v6 1/4] can: m_can: Create a m_can platform framework Dan Murphy
  2019-03-01 18:50 ` [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev Dan Murphy
  2019-03-01 18:50 ` [PATCH v6 3/4] dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver Dan Murphy
@ 2019-03-01 18:50 ` Dan Murphy
  2019-03-04 18:29   ` Wolfgang Grandegger
  2019-03-04 16:56 ` [PATCH v6 1/4] can: m_can: Create a m_can platform framework Wolfgang Grandegger
  3 siblings, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-01 18:50 UTC (permalink / raw)
  To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel, Dan Murphy

Add the TCAN4x5x SPI CAN driver.  This device uses the
Bosch MCAN IP core along with a SPI interface map.
Register to the MCAN common core code to manage the MCAN IP.

This device has a special method to indicate a write/read
operation on the data payload.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v6 - Updated Kconfig help and added REGMAP_SPI dependency, fixed coding style issues,
removed setting GPIO high in wake routine, change -EIO to return ret in init and
setup functions - https://lore.kernel.org/patchwork/patch/1042443/

 drivers/net/can/m_can/Kconfig    |   9 +
 drivers/net/can/m_can/Makefile   |   1 +
 drivers/net/can/m_can/tcan4x5x.c | 529 +++++++++++++++++++++++++++++++
 3 files changed, 539 insertions(+)
 create mode 100644 drivers/net/can/m_can/tcan4x5x.c

diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
index f7119fd72df4..cf4f32765efa 100644
--- a/drivers/net/can/m_can/Kconfig
+++ b/drivers/net/can/m_can/Kconfig
@@ -12,3 +12,12 @@ config CAN_M_CAN_PLATFORM
 	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
 	  This support is for devices that have the Bosch M_CAN controller
 	  IP embedded into the device and the IP is IO Mapped to the processor.
+
+config CAN_M_CAN_TCAN4X5X
+	depends on CAN_M_CAN
+	depends on REGMAP_SPI
+	tristate "TCAN4X5X M_CAN device"
+	---help---
+	  Say Y here if you want support for Texas Instruments TCAN4x5x
+	  M_CAN controller.  This device is a peripherial device that uses the
+	  SPI bus for communication.
diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
index 057bbcdb3c74..e77f0eccff97 100644
--- a/drivers/net/can/m_can/Makefile
+++ b/drivers/net/can/m_can/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_CAN_M_CAN) += m_can.o
 obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
+obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
new file mode 100644
index 000000000000..a91b976bb522
--- /dev/null
+++ b/drivers/net/can/m_can/tcan4x5x.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPI to CAN driver for the Texas Instruments TCAN4x5x
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+
+#include "m_can.h"
+
+#define DEVICE_NAME "tcan4x5x"
+#define TCAN4X5X_EXT_CLK_DEF	40000000
+
+#define TCAN4X5X_DEV_ID0	0x00
+#define TCAN4X5X_DEV_ID1	0x04
+#define TCAN4X5X_REV		0x08
+#define TCAN4X5X_STATUS		0x0C
+#define TCAN4X5X_ERROR_STATUS	0x10
+#define TCAN4X5X_CONTROL	0x14
+
+#define TCAN4X5X_CONFIG		0x800
+#define TCAN4X5X_TS_PRESCALE	0x804
+#define TCAN4X5X_TEST_REG	0x808
+#define TCAN4X5X_INT_FLAGS	0x820
+#define TCAN4X5X_MCAN_INT_REG	0x824
+#define TCAN4X5X_INT_EN		0x830
+
+/* Interrupt bits */
+#define TCAN4X5X_CANBUSTERMOPEN_INT_EN	BIT(30)
+#define TCAN4X5X_CANHCANL_INT_EN	BIT(29)
+#define TCAN4X5X_CANHBAT_INT_EN		BIT(28)
+#define TCAN4X5X_CANLGND_INT_EN		BIT(27)
+#define TCAN4X5X_CANBUSOPEN_INT_EN	BIT(26)
+#define TCAN4X5X_CANBUSGND_INT_EN	BIT(25)
+#define TCAN4X5X_CANBUSBAT_INT_EN	BIT(24)
+#define TCAN4X5X_UVSUP_INT_EN		BIT(22)
+#define TCAN4X5X_UVIO_INT_EN		BIT(21)
+#define TCAN4X5X_TSD_INT_EN		BIT(19)
+#define TCAN4X5X_ECCERR_INT_EN		BIT(16)
+#define TCAN4X5X_CANINT_INT_EN		BIT(15)
+#define TCAN4X5X_LWU_INT_EN		BIT(14)
+#define TCAN4X5X_CANSLNT_INT_EN		BIT(10)
+#define TCAN4X5X_CANDOM_INT_EN		BIT(8)
+#define TCAN4X5X_CANBUS_ERR_INT_EN	BIT(5)
+#define TCAN4X5X_BUS_FAULT		BIT(4)
+#define TCAN4X5X_MCAN_INT		BIT(1)
+#define TCAN4X5X_ENABLE_TCAN_INT	(TCAN4X5X_MCAN_INT | \
+					TCAN4X5X_BUS_FAULT | \
+					TCAN4X5X_CANBUS_ERR_INT_EN | \
+					TCAN4X5X_CANINT_INT_EN)
+
+/* MCAN Interrupt bits */
+#define TCAN4X5X_MCAN_IR_ARA		BIT(29)
+#define TCAN4X5X_MCAN_IR_PED		BIT(28)
+#define TCAN4X5X_MCAN_IR_PEA		BIT(27)
+#define TCAN4X5X_MCAN_IR_WD		BIT(26)
+#define TCAN4X5X_MCAN_IR_BO		BIT(25)
+#define TCAN4X5X_MCAN_IR_EW		BIT(24)
+#define TCAN4X5X_MCAN_IR_EP		BIT(23)
+#define TCAN4X5X_MCAN_IR_ELO		BIT(22)
+#define TCAN4X5X_MCAN_IR_BEU		BIT(21)
+#define TCAN4X5X_MCAN_IR_BEC		BIT(20)
+#define TCAN4X5X_MCAN_IR_DRX		BIT(19)
+#define TCAN4X5X_MCAN_IR_TOO		BIT(18)
+#define TCAN4X5X_MCAN_IR_MRAF		BIT(17)
+#define TCAN4X5X_MCAN_IR_TSW		BIT(16)
+#define TCAN4X5X_MCAN_IR_TEFL		BIT(15)
+#define TCAN4X5X_MCAN_IR_TEFF		BIT(14)
+#define TCAN4X5X_MCAN_IR_TEFW		BIT(13)
+#define TCAN4X5X_MCAN_IR_TEFN		BIT(12)
+#define TCAN4X5X_MCAN_IR_TFE		BIT(11)
+#define TCAN4X5X_MCAN_IR_TCF		BIT(10)
+#define TCAN4X5X_MCAN_IR_TC		BIT(9)
+#define TCAN4X5X_MCAN_IR_HPM		BIT(8)
+#define TCAN4X5X_MCAN_IR_RF1L		BIT(7)
+#define TCAN4X5X_MCAN_IR_RF1F		BIT(6)
+#define TCAN4X5X_MCAN_IR_RF1W		BIT(5)
+#define TCAN4X5X_MCAN_IR_RF1N		BIT(4)
+#define TCAN4X5X_MCAN_IR_RF0L		BIT(3)
+#define TCAN4X5X_MCAN_IR_RF0F		BIT(2)
+#define TCAN4X5X_MCAN_IR_RF0W		BIT(1)
+#define TCAN4X5X_MCAN_IR_RF0N		BIT(0)
+#define TCAN4X5X_ENABLE_MCAN_INT	(TCAN4X5X_MCAN_IR_TC | \
+					TCAN4X5X_MCAN_IR_RF0N | \
+					TCAN4X5X_MCAN_IR_RF1N | \
+					TCAN4X5X_MCAN_IR_RF0F | \
+					TCAN4X5X_MCAN_IR_RF1F)
+#define TCAN4X5X_MRAM_START	0x8000
+#define TCAN4X5X_MCAN_OFFSET	0x1000
+#define TCAN4X5X_MAX_REGISTER	0x8fff
+
+#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
+#define TCAN4X5X_SET_ALL_INT	0xffffffff
+
+#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
+#define TCAN4X5X_READ_CMD	(0x41 << 24)
+
+#define TCAN4X5X_MODE_SEL_MASK		(BIT(7) | BIT(6))
+#define TCAN4X5X_MODE_SLEEP		0x00
+#define TCAN4X5X_MODE_STANDBY		BIT(6)
+#define TCAN4X5X_MODE_NORMAL		BIT(7)
+
+#define TCAN4X5X_SW_RESET	BIT(2)
+
+#define TCAN4X5X_MCAN_CONFIGURED	BIT(5)
+#define TCAN4X5X_WATCHDOG_EN		BIT(3)
+#define TCAN4X5X_WD_60_MS_TIMER		0
+#define TCAN4X5X_WD_600_MS_TIMER	BIT(28)
+#define TCAN4X5X_WD_3_S_TIMER		BIT(29)
+#define TCAN4X5X_WD_6_S_TIMER		(BIT(28) | BIT(29))
+
+struct tcan4x5x_priv {
+	struct regmap *regmap;
+	struct spi_device *spi;
+	struct mutex tcan4x5x_lock; /* SPI device lock */
+
+	struct m_can_classdev *mcan_dev;
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *interrupt_gpio;
+	struct gpio_desc *device_wake_gpio;
+	struct gpio_desc *device_state_gpio;
+	struct regulator *power;
+
+	/* Register based ip */
+	int mram_start;
+	int reg_offset;
+};
+
+static struct can_bittiming_const tcan4x5x_bittiming_const = {
+	.name = DEVICE_NAME,
+	.tseg1_min = 2,
+	.tseg1_max = 31,
+	.tseg2_min = 2,
+	.tseg2_max = 16,
+	.sjw_max = 16,
+	.brp_min = 1,
+	.brp_max = 32,
+	.brp_inc = 1,
+};
+
+static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
+	.name = DEVICE_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 32,
+	.tseg2_min = 1,
+	.tseg2_max = 16,
+	.sjw_max = 16,
+	.brp_min = 1,
+	.brp_max = 32,
+	.brp_inc = 1,
+};
+
+static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
+{
+	int wake_state = 0;
+
+	if (priv->device_state_gpio)
+		wake_state = gpiod_get_value(priv->device_state_gpio);
+
+	if (priv->device_wake_gpio && wake_state) {
+		gpiod_set_value(priv->device_wake_gpio, 0);
+		usleep_range(5, 50);
+		gpiod_set_value(priv->device_wake_gpio, 1);
+	}
+}
+
+static int regmap_spi_gather_write(void *context, const void *reg,
+				   size_t reg_len, const void *val,
+				   size_t val_len)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_message m;
+	u32 addr;
+	struct spi_transfer t[2] = {
+		{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
+		{ .tx_buf = val, .len = val_len, },
+	};
+
+	addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	return spi_sync(spi, &m);
+}
+
+static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
+{
+	u16 *reg = (u16 *)(data);
+	const u32 *val = data + 4;
+
+	return regmap_spi_gather_write(context, reg, 4, val, count);
+}
+
+static int regmap_spi_async_write(void *context,
+				  const void *reg, size_t reg_len,
+				  const void *val, size_t val_len,
+				  struct regmap_async *a)
+{
+	return -ENOTSUPP;
+}
+
+static struct regmap_async *regmap_spi_async_alloc(void)
+{
+	return NULL;
+}
+
+static int tcan4x5x_regmap_read(void *context,
+				const void *reg, size_t reg_size,
+				void *val, size_t val_size)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
+
+	return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
+}
+
+static struct regmap_bus tcan4x5x_bus = {
+	.write = tcan4x5x_regmap_write,
+	.gather_write = regmap_spi_gather_write,
+	.async_write = regmap_spi_async_write,
+	.async_alloc = regmap_spi_async_alloc,
+	.read = tcan4x5x_regmap_read,
+	.read_flag_mask = 0x00,
+	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+static u32 tcan4x5x_read_reg(struct m_can_classdev *m_can_class, int reg)
+{
+	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+	u32 val;
+
+	tcan4x5x_check_wake(priv);
+
+	regmap_read(priv->regmap, priv->reg_offset + reg, &val);
+
+	return val;
+}
+
+static u32 tcan4x5x_read_fifo(struct m_can_classdev *m_can_class,
+			      int addr_offset)
+{
+	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+	u32 val;
+
+	tcan4x5x_check_wake(priv);
+
+	regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
+
+	return val;
+}
+
+static int tcan4x5x_write_reg(struct m_can_classdev *m_can_class,
+			      int reg, int val)
+{
+	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+	tcan4x5x_check_wake(priv);
+
+	return regmap_write(priv->regmap, priv->reg_offset + reg, val);
+}
+
+static int tcan4x5x_write_fifo(struct m_can_classdev *m_can_class,
+			       int addr_offset, int val)
+{
+	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+	tcan4x5x_check_wake(priv);
+
+	return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
+}
+
+static int tcan4x5x_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 tcan4x5x_write_tcan_reg(struct m_can_classdev *m_can_class,
+				   int reg, int val)
+{
+	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
+
+	tcan4x5x_check_wake(priv);
+
+	return regmap_write(priv->regmap, reg, val);
+}
+
+static int tcan4x5x_clear_interrupts(struct m_can_classdev *class_dev)
+{
+	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+	int ret;
+
+	tcan4x5x_check_wake(tcan4x5x);
+
+	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_STATUS,
+				      TCAN4X5X_CLEAR_ALL_INT);
+	if (ret)
+		return ret;
+
+	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_MCAN_INT_REG,
+				      TCAN4X5X_ENABLE_MCAN_INT);
+	if (ret)
+		return ret;
+
+	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_FLAGS,
+				      TCAN4X5X_CLEAR_ALL_INT);
+	if (ret)
+		return ret;
+
+	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_ERROR_STATUS,
+				      TCAN4X5X_CLEAR_ALL_INT);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static int tcan4x5x_init(struct m_can_classdev *class_dev)
+{
+	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+	int ret;
+
+	tcan4x5x_check_wake(tcan4x5x);
+
+	ret = tcan4x5x_clear_interrupts(class_dev);
+	if (ret)
+		return ret;
+
+	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_EN,
+				      TCAN4X5X_ENABLE_TCAN_INT);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
+				 TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
+	if (ret)
+		return ret;
+
+	/* Zero out the MCAN buffers */
+	m_can_init_ram(class_dev);
+
+	return ret;
+}
+
+static int tcan4x5x_parse_config(struct m_can_classdev *class_dev)
+{
+	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
+
+	tcan4x5x->reset_gpio = devm_gpiod_get_optional(class_dev->dev,
+						       "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(tcan4x5x->reset_gpio))
+		tcan4x5x->reset_gpio = NULL;
+
+	tcan4x5x->device_wake_gpio = devm_gpiod_get_optional(class_dev->dev,
+							     "device-wake",
+							     GPIOD_OUT_HIGH);
+	if (IS_ERR(tcan4x5x->device_wake_gpio))
+		tcan4x5x->device_wake_gpio = NULL;
+
+	tcan4x5x->device_state_gpio = devm_gpiod_get_optional(class_dev->dev,
+							      "device-state",
+							      GPIOD_IN);
+	if (IS_ERR(tcan4x5x->device_state_gpio))
+		tcan4x5x->device_state_gpio = NULL;
+
+	tcan4x5x->interrupt_gpio = devm_gpiod_get(class_dev->dev,
+						  "data-ready", GPIOD_IN);
+	if (IS_ERR(tcan4x5x->interrupt_gpio)) {
+		dev_err(class_dev->dev, "data-ready gpio not defined\n");
+		return -EINVAL;
+	}
+
+	class_dev->net->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
+
+	tcan4x5x->power = devm_regulator_get_optional(class_dev->dev,
+						      "vsup");
+	if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	return 0;
+}
+
+static const struct regmap_config tcan4x5x_regmap = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.cache_type = REGCACHE_NONE,
+	.max_register = TCAN4X5X_MAX_REGISTER,
+};
+
+static struct m_can_ops tcan4x5x_ops = {
+	.device_init = tcan4x5x_init,
+	.read_reg = tcan4x5x_read_reg,
+	.write_reg = tcan4x5x_write_reg,
+	.write_fifo = tcan4x5x_write_fifo,
+	.read_fifo = tcan4x5x_read_fifo,
+	.clr_dev_interrupts = tcan4x5x_clear_interrupts,
+};
+
+static int tcan4x5x_can_probe(struct spi_device *spi)
+{
+	struct tcan4x5x_priv *priv;
+	struct m_can_classdev *mcan_class;
+	int freq, ret;
+
+	mcan_class = m_can_class_allocate_dev(&spi->dev);
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mcan_class->device_data = priv;
+
+	m_can_class_get_clocks(mcan_class);
+	if (IS_ERR(mcan_class->cclk)) {
+		dev_err(&spi->dev, "no CAN clock source defined\n");
+		freq = TCAN4X5X_EXT_CLK_DEF;
+	} else {
+		freq = clk_get_rate(mcan_class->cclk);
+	}
+
+	/* Sanity check */
+	if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
+		return -ERANGE;
+
+	priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
+	priv->mram_start = TCAN4X5X_MRAM_START;
+	priv->spi = spi;
+	priv->mcan_dev = mcan_class;
+
+	mcan_class->pm_clock_support = 0;
+	mcan_class->can.clock.freq = freq;
+	mcan_class->dev = &spi->dev;
+	mcan_class->ops = &tcan4x5x_ops;
+	mcan_class->is_peripherial = true;
+	mcan_class->bit_timing = &tcan4x5x_bittiming_const;
+	mcan_class->data_timing = &tcan4x5x_data_bittiming_const;
+
+	spi_set_drvdata(spi, priv);
+
+	ret = tcan4x5x_parse_config(mcan_class);
+	if (ret)
+		goto out_clk;
+
+	/* Configure the SPI bus */
+	spi->bits_per_word = 32;
+	ret = spi_setup(spi);
+	if (ret)
+		goto out_clk;
+
+	priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
+					&spi->dev, &tcan4x5x_regmap);
+
+	mutex_init(&priv->tcan4x5x_lock);
+
+	tcan4x5x_power_enable(priv->power, 1);
+
+	ret = m_can_class_register(mcan_class);
+	if (ret)
+		goto out_power;
+
+	netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
+	return 0;
+
+out_power:
+	tcan4x5x_power_enable(priv->power, 0);
+out_clk:
+	if (!IS_ERR(mcan_class->cclk)) {
+		clk_disable_unprepare(mcan_class->cclk);
+		clk_disable_unprepare(mcan_class->hclk);
+	}
+
+	dev_err(&spi->dev, "Probe failed, err=%d\n", ret);
+	return ret;
+}
+
+static int tcan4x5x_can_remove(struct spi_device *spi)
+{
+	struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
+
+	tcan4x5x_power_enable(priv->power, 0);
+
+	m_can_class_unregister(priv->mcan_dev);
+
+	return 0;
+}
+
+static const struct of_device_id tcan4x5x_of_match[] = {
+	{ .compatible = "ti,tcan4x5x", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
+
+static const struct spi_device_id tcan4x5x_id_table[] = {
+	{
+		.name		= "tcan4x5x",
+		.driver_data	= 0,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
+
+static struct spi_driver tcan4x5x_can_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = tcan4x5x_of_match,
+		.pm = NULL,
+	},
+	.id_table = tcan4x5x_id_table,
+	.probe = tcan4x5x_can_probe,
+	.remove = tcan4x5x_can_remove,
+};
+module_spi_driver(tcan4x5x_can_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1.390.gb5101f9297


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-01 18:50 [PATCH v6 1/4] can: m_can: Create a m_can platform framework Dan Murphy
                   ` (2 preceding siblings ...)
  2019-03-01 18:50 ` [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel Dan Murphy
@ 2019-03-04 16:56 ` Wolfgang Grandegger
  2019-03-04 17:22   ` Dan Murphy
  2019-03-04 19:43   ` Dan Murphy
  3 siblings, 2 replies; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 16:56 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Hello Dan,

the series already looks quite good. I still realized a few (minor)
issues while browsing the patch/code...

Am 01.03.19 um 19:50 schrieb Dan Murphy:
> Create a m_can platform framework that peripherial
> devices can register to and use common code and register sets.
> The peripherial devices may provide read/write and configuration
> support of the IP.
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> 
> v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
> issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
> priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
> Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
> Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/
> 
>  drivers/net/can/m_can/Kconfig          |  13 +-
>  drivers/net/can/m_can/Makefile         |   1 +
>  drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
>  drivers/net/can/m_can/m_can.h          | 110 ++++
>  drivers/net/can/m_can/m_can_platform.c | 198 +++++++
>  5 files changed, 681 insertions(+), 343 deletions(-)
>  create mode 100644 drivers/net/can/m_can/m_can.h
>  create mode 100644 drivers/net/can/m_can/m_can_platform.c
> 
> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
> index 04f20dd39007..f7119fd72df4 100644
> --- a/drivers/net/can/m_can/Kconfig
> +++ b/drivers/net/can/m_can/Kconfig
> @@ -1,5 +1,14 @@
>  config CAN_M_CAN
> +	tristate "Bosch M_CAN support"
> +	---help---
> +	  Say Y here if you want support for Bosch M_CAN controller framework.
> +	  This is common support for devices that embed the Bosch M_CAN IP.
> +
> +config CAN_M_CAN_PLATFORM
> +	tristate "Bosch M_CAN support for io-mapped devices"
>  	depends on HAS_IOMEM
> -	tristate "Bosch M_CAN devices"
> +	depends on CAN_M_CAN
>  	---help---
> -	  Say Y here if you want to support for Bosch M_CAN controller.
> +	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
> +	  This support is for devices that have the Bosch M_CAN controller
> +	  IP embedded into the device and the IP is IO Mapped to the processor.
> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
> index 8bbd7f24f5be..057bbcdb3c74 100644
> --- a/drivers/net/can/m_can/Makefile
> +++ b/drivers/net/can/m_can/Makefile
> @@ -3,3 +3,4 @@
>  #
>  
>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
> +obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index 9b449400376b..b37d0886f9cb 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -1,20 +1,14 @@
> -/*
> - * CAN bus driver for Bosch M_CAN controller
> - *
> - * Copyright (C) 2014 Freescale Semiconductor, Inc.
> - *	Dong Aisheng <b29396@freescale.com>
> - *
> - * Bosch M_CAN user manual can be obtained from:
> +// SPDX-License-Identifier: GPL-2.0
> +// CAN bus driver for Bosch M_CAN controller
> +// Copyright (C) 2014 Freescale Semiconductor, Inc.
> +//      Dong Aisheng <b29396@freescale.com>
> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
> +
> +/* Bosch M_CAN user manual can be obtained from:
>   * http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/
>   * mcan_users_manual_v302.pdf
> - *
> - * This file is licensed under the terms of the GNU General Public
> - * License version 2. This program is licensed "as is" without any
> - * warranty of any kind, whether express or implied.
>   */
>  
> -#include <linux/clk.h>
> -#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -28,11 +22,7 @@
>  #include <linux/can/dev.h>
>  #include <linux/pinctrl/consumer.h>
>  
> -/* napi related */
> -#define M_CAN_NAPI_WEIGHT	64
> -
> -/* message ram configuration data length */
> -#define MRAM_CFG_LEN	8
> +#include "m_can.h"
>  
>  /* registers definition */
>  enum m_can_reg {
> @@ -86,28 +76,11 @@ enum m_can_reg {
>  	M_CAN_TXEFA	= 0xf8,
>  };
>  
> -/* m_can lec values */
> -enum m_can_lec_type {
> -	LEC_NO_ERROR = 0,
> -	LEC_STUFF_ERROR,
> -	LEC_FORM_ERROR,
> -	LEC_ACK_ERROR,
> -	LEC_BIT1_ERROR,
> -	LEC_BIT0_ERROR,
> -	LEC_CRC_ERROR,
> -	LEC_UNUSED,
> -};
> +/* napi related */
> +#define M_CAN_NAPI_WEIGHT	64
>  
> -enum m_can_mram_cfg {
> -	MRAM_SIDF = 0,
> -	MRAM_XIDF,
> -	MRAM_RXF0,
> -	MRAM_RXF1,
> -	MRAM_RXB,
> -	MRAM_TXE,
> -	MRAM_TXB,
> -	MRAM_CFG_NUM,
> -};
> +/* message ram configuration data length */
> +#define MRAM_CFG_LEN	8
>  
>  /* Core Release Register (CREL) */
>  #define CREL_REL_SHIFT		28
> @@ -343,77 +316,83 @@ enum m_can_mram_cfg {
>  #define TX_BUF_MM_MASK		(0xff << TX_BUF_MM_SHIFT)
>  
>  /* Tx event FIFO Element */
> -/* E1 */

I also don't understand what "E1" means... we should keep taht line anyway!

>  #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT
>  #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT)
>  
> -/* address offset and element number for each FIFO/Buffer in the Message RAM */
> -struct mram_cfg {
> -	u16 off;
> -	u8  num;
> -};
> -
> -/* m_can private data structure */
> -struct m_can_priv {
> -	struct can_priv can;	/* must be the first member */
> -	struct napi_struct napi;
> -	struct net_device *dev;
> -	struct device *device;
> -	struct clk *hclk;
> -	struct clk *cclk;
> -	void __iomem *base;
> -	u32 irqstatus;
> -	int version;
> -
> -	/* message ram configuration */
> -	void __iomem *mram_base;
> -	struct mram_cfg mcfg[MRAM_CFG_NUM];
> -};
> +static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
> +{
> +	if (priv->ops->read_reg)
> +		return priv->ops->read_reg(priv, reg);
> +	else
> +		return -EINVAL;
> +}
>  
> -static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg)
> +static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
>  {
> -	return readl(priv->base + reg);
> +	if (priv->ops->write_reg)
> +		return priv->ops->write_reg(priv, reg, val);
> +	else
> +		return -EINVAL;
>  }
>  
> -static inline void m_can_write(const struct m_can_priv *priv,
> -			       enum m_can_reg reg, u32 val)
> +static u32 m_can_fifo_read(struct m_can_priv *priv,
> +			   u32 fgi, unsigned int offset)
>  {
> -	writel(val, priv->base + reg);
> +	u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
> +			  offset;
> +
> +	if (priv->ops->read_fifo)
> +		return priv->ops->read_fifo(priv, addr_offset);
> +	else
> +		return -EINVAL;
>  }
>  
> -static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
> -				  u32 fgi, unsigned int offset)
> +static u32 m_can_fifo_write(struct m_can_priv *priv,
> +			    u32 fpi, unsigned int offset, u32 val)
>  {
> -	return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
> -		     fgi * RXF0_ELEMENT_SIZE + offset);
> +	u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
> +			  offset;
> +
> +	if (priv->ops->write_fifo)
> +		return priv->ops->write_fifo(priv, addr_offset, val);
> +	else
> +		return -EINVAL;
>  }
>  
> -static inline void m_can_fifo_write(const struct m_can_priv *priv,
> -				    u32 fpi, unsigned int offset, u32 val)
> +static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
> +				   u32 fpi, u32 val)
>  {
> -	writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
> -	       fpi * TXB_ELEMENT_SIZE + offset);
> +	if (priv->ops->write_fifo)
> +		return priv->ops->write_fifo(priv, fpi, val);
> +	else
> +		return 0;
>  }
>  
> -static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
> -				      u32 fgi,
> -				      u32 offset) {
> -	return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
> -			fgi * TXE_ELEMENT_SIZE + offset);
> +static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
> +{
> +	u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
> +			  offset;
> +
> +	if (priv->ops->read_fifo)
> +		return priv->ops->read_fifo(priv, addr_offset);
> +	else
> +		return -EINVAL;
>  }
>  
> -static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
> +static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
>  {
>  		return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
>  }
>  
> -static inline void m_can_config_endisable(const struct m_can_priv *priv,
> -					  bool enable)
> +void m_can_config_endisable(struct m_can_priv *priv, bool enable)
>  {
>  	u32 cccr = m_can_read(priv, M_CAN_CCCR);
>  	u32 timeout = 10;
>  	u32 val = 0;
>  
> +	if (cccr & CCCR_CSR)
> +		cccr &= ~CCCR_CSR;
> +

Is that a bug fix? Or why do we need it?

>  	if (enable) {
>  		/* enable m_can configuration */
>  		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
> @@ -430,7 +409,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>  
>  	while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
>  		if (timeout == 0) {
> -			netdev_warn(priv->dev, "Failed to init module\n");
> +			netdev_warn(priv->net, "Failed to init module\n");
>  			return;
>  		}
>  		timeout--;
> @@ -438,13 +417,13 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>  	}
>  }
>  
> -static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
> +static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
>  {
>  	/* Only interrupt line 0 is used in this driver */
>  	m_can_write(priv, M_CAN_ILE, ILE_EINT0);
>  }
>  
> -static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
> +static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
>  {
>  	m_can_write(priv, M_CAN_ILE, 0x0);
>  }
> @@ -633,9 +612,12 @@ static int m_can_clk_start(struct m_can_priv *priv)
>  {
>  	int err;
>  
> -	err = pm_runtime_get_sync(priv->device);
> +	if (priv->pm_clock_support == 0)
> +		return 0;
> +
> +	err = pm_runtime_get_sync(priv->dev);
>  	if (err < 0) {
> -		pm_runtime_put_noidle(priv->device);
> +		pm_runtime_put_noidle(priv->dev);
>  		return err;
>  	}
>  
> @@ -644,7 +626,8 @@ static int m_can_clk_start(struct m_can_priv *priv)
>  
>  static void m_can_clk_stop(struct m_can_priv *priv)
>  {
> -	pm_runtime_put_sync(priv->device);
> +	if (priv->pm_clock_support)
> +		pm_runtime_put_sync(priv->dev);
>  }
>  
>  static int m_can_get_berr_counter(const struct net_device *dev,
> @@ -811,9 +794,8 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
>  	return work_done;
>  }
>  
> -static int m_can_poll(struct napi_struct *napi, int quota)
> +static int m_can_rx_handler(struct net_device *dev, int quota)
>  {
> -	struct net_device *dev = napi->dev;
>  	struct m_can_priv *priv = netdev_priv(dev);
>  	int work_done = 0;
>  	u32 irqstatus, psr;
> @@ -831,13 +813,33 @@ static int m_can_poll(struct napi_struct *napi, int quota)
>  
>  	if (irqstatus & IR_RF0N)
>  		work_done += m_can_do_rx_poll(dev, (quota - work_done));
> +end:
> +	return work_done;
> +}
> +
> +static int m_can_rx_peripherial(struct net_device *dev)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +
> +	m_can_rx_handler(dev, 1);
> +
> +	m_can_enable_all_interrupts(priv);
> +
> +	return 0;
> +}
> +
> +static int m_can_poll(struct napi_struct *napi, int quota)
> +{
> +	struct net_device *dev = napi->dev;
> +	struct m_can_priv *priv = netdev_priv(dev);
> +	int work_done;
>  
> +	work_done = m_can_rx_handler(dev, quota);
>  	if (work_done < quota) {
>  		napi_complete_done(napi, work_done);
>  		m_can_enable_all_interrupts(priv);
>  	}
>  
> -end:
>  	return work_done;
>  }
>  
> @@ -894,6 +896,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>  	if (ir & IR_ALL_INT)
>  		m_can_write(priv, M_CAN_IR, ir);
>  
> +	if (priv->ops->clr_dev_interrupts)
> +		priv->ops->clr_dev_interrupts(priv);
> +
>  	/* schedule NAPI in case of
>  	 * - rx IRQ
>  	 * - state change IRQ
> @@ -902,7 +907,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>  	if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
>  		priv->irqstatus = ir;
>  		m_can_disable_all_interrupts(priv);
> -		napi_schedule(&priv->napi);
> +		if (!priv->is_peripherial)
> +			napi_schedule(&priv->napi);
> +		else
> +			m_can_rx_peripherial(dev);
>  	}
>  
>  	if (priv->version == 30) {
> @@ -1155,6 +1163,9 @@ static void m_can_chip_config(struct net_device *dev)
>  	m_can_set_bittiming(dev);
>  
>  	m_can_config_endisable(priv, false);
> +
> +	if (priv->ops->device_init)
> +		priv->ops->device_init(priv);
>  }
>  
>  static void m_can_start(struct net_device *dev)
> @@ -1188,20 +1199,17 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
>   * else it returns the release and step coded as:
>   * return value = 10 * <release> + 1 * <step>
>   */
> -static int m_can_check_core_release(void __iomem *m_can_base)
> +static int m_can_check_core_release(struct m_can_priv *priv)
>  {
>  	u32 crel_reg;
>  	u8 rel;
>  	u8 step;
>  	int res;
> -	struct m_can_priv temp_priv = {
> -		.base = m_can_base
> -	};
>  
>  	/* Read Core Release Version and split into version number
>  	 * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
>  	 */
> -	crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
> +	crel_reg = m_can_read(priv, M_CAN_CREL);
>  	rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
>  	step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
>  
> @@ -1219,18 +1227,26 @@ static int m_can_check_core_release(void __iomem *m_can_base)
>  /* Selectable Non ISO support only in version 3.2.x
>   * This function checks if the bit is writable.
>   */
> -static bool m_can_niso_supported(const struct m_can_priv *priv)
> +static bool m_can_niso_supported(struct m_can_priv *priv)
>  {
> -	u32 cccr_reg, cccr_poll;
> -	int niso_timeout;
> +	u32 cccr_reg, cccr_poll = 0;
> +	int niso_timeout = -ETIMEDOUT;
> +	int i;
>  
>  	m_can_config_endisable(priv, true);
>  	cccr_reg = m_can_read(priv, M_CAN_CCCR);
>  	cccr_reg |= CCCR_NISO;
>  	m_can_write(priv, M_CAN_CCCR, cccr_reg);
>  
> -	niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
> -					  (cccr_poll == cccr_reg), 0, 10);
> +	for (i = 0; i <= 10; i++) {
> +		cccr_poll = m_can_read(priv, M_CAN_CCCR);
> +		if (cccr_poll == cccr_reg) {
> +			niso_timeout = 0;
> +			break;
> +		}
> +
> +		usleep_range(1, 5);
> +	}
>  
>  	/* Clear NISO */
>  	cccr_reg &= ~(CCCR_NISO);
> @@ -1242,107 +1258,95 @@ static bool m_can_niso_supported(const struct m_can_priv *priv)
>  	return !niso_timeout;
>  }
>  
> -static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
> -			   void __iomem *addr)
> +static int m_can_dev_setup(struct m_can_priv *m_can_dev)
>  {
> -	struct m_can_priv *priv;
> +	struct net_device *dev = m_can_dev->net;
>  	int m_can_version;
>  
> -	m_can_version = m_can_check_core_release(addr);
> +	m_can_version = m_can_check_core_release(m_can_dev);
>  	/* return if unsupported version */
>  	if (!m_can_version) {
> -		dev_err(&pdev->dev, "Unsupported version number: %2d",
> +		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
>  			m_can_version);
>  		return -EINVAL;
>  	}
>  
> -	priv = netdev_priv(dev);
> -	netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
> +	if (!m_can_dev->is_peripherial)
> +		netif_napi_add(dev, &m_can_dev->napi,
> +			       m_can_poll, M_CAN_NAPI_WEIGHT);
>  
>  	/* Shared properties of all M_CAN versions */
> -	priv->version = m_can_version;
> -	priv->dev = dev;
> -	priv->base = addr;
> -	priv->can.do_set_mode = m_can_set_mode;
> -	priv->can.do_get_berr_counter = m_can_get_berr_counter;
> +	m_can_dev->version = m_can_version;
> +	m_can_dev->can.do_set_mode = m_can_set_mode;
> +	m_can_dev->can.do_get_berr_counter = m_can_get_berr_counter;
>  
>  	/* Set M_CAN supported operations */
> -	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> +	m_can_dev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>  					CAN_CTRLMODE_LISTENONLY |
>  					CAN_CTRLMODE_BERR_REPORTING |
>  					CAN_CTRLMODE_FD;
>  
>  	/* Set properties depending on M_CAN version */
> -	switch (priv->version) {
> +	switch (m_can_dev->version) {
>  	case 30:
>  		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
>  		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
> -		priv->can.bittiming_const = &m_can_bittiming_const_30X;
> -		priv->can.data_bittiming_const =
> +		if (m_can_dev->bit_timing)
> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
> +		else
> +			m_can_dev->can.bittiming_const =
> +				&m_can_bittiming_const_30X;

		m_can_dev->can.bittiming_const = m_can_dev->bit_timing ?
			m_can_dev->bit_timing : &m_can_bittiming_const_30X;

Is more compact. Here and below.

> +		if (m_can_dev->data_timing)
> +			m_can_dev->can.data_bittiming_const =
> +				m_can_dev->data_timing;
> +		else
> +			m_can_dev->can.data_bittiming_const =
>  				&m_can_data_bittiming_const_30X;
>  		break;
>  	case 31:
>  		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
>  		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
> -		priv->can.bittiming_const = &m_can_bittiming_const_31X;
> -		priv->can.data_bittiming_const =
> +		if (m_can_dev->bit_timing)
> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
> +		else
> +			m_can_dev->can.bittiming_const =
> +				&m_can_bittiming_const_31X;
> +		if (m_can_dev->data_timing)
> +			m_can_dev->can.data_bittiming_const =
> +				m_can_dev->data_timing;
> +		else
> +			m_can_dev->can.data_bittiming_const =
>  				&m_can_data_bittiming_const_31X;
>  		break;
>  	case 32:
> -		priv->can.bittiming_const = &m_can_bittiming_const_31X;
> -		priv->can.data_bittiming_const =
> +		if (m_can_dev->bit_timing)
> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
> +		else
> +			m_can_dev->can.bittiming_const =
> +				&m_can_bittiming_const_31X;
> +
> +		if (m_can_dev->data_timing)
> +			m_can_dev->can.data_bittiming_const =
> +				m_can_dev->data_timing;
> +		else
> +			m_can_dev->can.data_bittiming_const =
>  				&m_can_data_bittiming_const_31X;
> -		priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
> +
> +		m_can_dev->can.ctrlmode_supported |=
> +						(m_can_niso_supported(m_can_dev)
>  						? CAN_CTRLMODE_FD_NON_ISO
>  						: 0);
>  		break;
>  	default:
> -		dev_err(&pdev->dev, "Unsupported version number: %2d",
> -			priv->version);
> +		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
> +			m_can_dev->version);
>  		return -EINVAL;
>  	}
>  
> -	return 0;
> -}
> -
> -static int m_can_open(struct net_device *dev)
> -{
> -	struct m_can_priv *priv = netdev_priv(dev);
> -	int err;
> -
> -	err = m_can_clk_start(priv);
> -	if (err)
> -		return err;
> -
> -	/* open the can device */
> -	err = open_candev(dev);
> -	if (err) {
> -		netdev_err(dev, "failed to open can device\n");
> -		goto exit_disable_clks;
> -	}
> -
> -	/* register interrupt handler */
> -	err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
> -			  dev);
> -	if (err < 0) {
> -		netdev_err(dev, "failed to request interrupt\n");
> -		goto exit_irq_fail;
> -	}
> -
> -	/* start the m_can controller */
> -	m_can_start(dev);
> -
> -	can_led_event(dev, CAN_LED_EVENT_OPEN);
> -	napi_enable(&priv->napi);
> -	netif_start_queue(dev);
> +	if (m_can_dev->ops->device_init)
> +		m_can_dev->ops->device_init(m_can_dev);
>  
>  	return 0;
> -
> -exit_irq_fail:
> -	close_candev(dev);
> -exit_disable_clks:
> -	m_can_clk_stop(priv);
> -	return err;
>  }
>  
>  static void m_can_stop(struct net_device *dev)
> @@ -1361,10 +1365,17 @@ static int m_can_close(struct net_device *dev)
>  	struct m_can_priv *priv = netdev_priv(dev);
>  
>  	netif_stop_queue(dev);
> -	napi_disable(&priv->napi);
> +	if (!priv->is_peripherial)
> +		napi_disable(&priv->napi);
>  	m_can_stop(dev);
>  	m_can_clk_stop(priv);
>  	free_irq(dev->irq, dev);
> +
> +	if (priv->is_peripherial) {
> +		destroy_workqueue(priv->wq);
> +		priv->wq = NULL;

Could you pease use the name "priv->tx_wq".

> +	}
> +
>  	close_candev(dev);
>  	can_led_event(dev, CAN_LED_EVENT_STOP);
>  
> @@ -1385,18 +1396,15 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
>  	return !!priv->can.echo_skb[next_idx];
>  }
>  
> -static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
> -				    struct net_device *dev)
> +static void m_can_tx_handler(struct m_can_priv *priv)
>  {
> -	struct m_can_priv *priv = netdev_priv(dev);
> -	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> +	struct canfd_frame *cf = (struct canfd_frame *)priv->tx_skb->data;
> +	struct net_device *dev = priv->net;
> +	struct sk_buff *skb = priv->tx_skb;
>  	u32 id, cccr, fdflags;
>  	int i;
>  	int putidx;
>  
> -	if (can_dropped_invalid_skb(dev, skb))
> -		return NETDEV_TX_OK;
> -
>  	/* Generate ID field for TX buffer Element */
>  	/* Common to all supported M_CAN versions */
>  	if (cf->can_id & CAN_EFF_FLAG) {
> @@ -1451,7 +1459,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>  			netif_stop_queue(dev);
>  			netdev_warn(dev,
>  				    "TX queue active although FIFO is full.");
> -			return NETDEV_TX_BUSY;
> +			return;

m_can_start_xmit() doesn't return NETDEV_TX_BUSY but NETDEV_TX_OK and
the queue is stopped! Also the skb is not freed! The code states 
"/* This shouldn't happen */" but then it just prints a warning. Did
you see that message?

>  		}
>  
>  		/* get put index for frame */
> @@ -1492,14 +1500,100 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>  		m_can_write(priv, M_CAN_TXBAR, (1 << putidx));
>  
>  		/* stop network queue if fifo full */
> -			if (m_can_tx_fifo_full(priv) ||
> -			    m_can_next_echo_skb_occupied(dev, putidx))
> -				netif_stop_queue(dev);
> +		if (m_can_tx_fifo_full(priv) ||
> +		    m_can_next_echo_skb_occupied(dev, putidx))
> +			netif_stop_queue(dev);
> +	}
> +}
> +
> +static void m_can_tx_work_queue(struct work_struct *ws)
> +{
> +	struct m_can_priv *priv = container_of(ws, struct m_can_priv,
> +						tx_work);
> +	m_can_tx_handler(priv);
> +}
> +
> +static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
> +				    struct net_device *dev)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +
> +	if (can_dropped_invalid_skb(dev, skb))
> +		return NETDEV_TX_OK;
> +
> +	priv->tx_skb = skb;
> +	if (priv->is_peripherial) {
> +		netif_stop_queue(priv->net);
> +		queue_work(priv->wq, &priv->tx_work);
> +	} else {
> +		m_can_tx_handler(priv);
>  	}
>  
>  	return NETDEV_TX_OK;
>  }
>  
> +static int m_can_open(struct net_device *dev)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +	int err;
> +
> +	err = m_can_clk_start(priv);
> +	if (err)
> +		return err;
> +
> +	/* open the can device */
> +	err = open_candev(dev);
> +	if (err) {
> +		netdev_err(dev, "failed to open can device\n");
> +		goto exit_disable_clks;
> +	}
> +
> +	/* register interrupt handler */
> +	if (priv->is_peripherial) {
> +		priv->wq = alloc_workqueue("mcan_wq",
> +					   WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
> +		if (!priv->wq) {
> +			err = -ENOMEM;
> +			goto out_wq_fail;
> +		}
> +
> +		INIT_WORK(&priv->tx_work, m_can_tx_work_queue);
> +
> +		err = request_threaded_irq(dev->irq, NULL, m_can_isr,
> +					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
> +					   dev->name, dev);
> +	} else {
> +		err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
> +				  dev);
> +	}
> +
> +	if (err < 0) {
> +		netdev_err(dev, "failed to request interrupt\n");
> +		goto exit_irq_fail;
> +	}
> +
> +	/* start the m_can controller */
> +	m_can_start(dev);
> +
> +	can_led_event(dev, CAN_LED_EVENT_OPEN);
> +
> +	if (!priv->is_peripherial)
> +		napi_enable(&priv->napi);
> +
> +	netif_start_queue(dev);
> +
> +	return 0;
> +
> +exit_irq_fail:
> +	if (priv->is_peripherial)
> +		destroy_workqueue(priv->wq);
> +out_wq_fail:
> +	close_candev(dev);
> +exit_disable_clks:
> +	m_can_clk_stop(priv);
> +	return err;
> +}
> +
>  static const struct net_device_ops m_can_netdev_ops = {
>  	.ndo_open = m_can_open,
>  	.ndo_stop = m_can_close,
> @@ -1515,20 +1609,6 @@ static int register_m_can_dev(struct net_device *dev)
>  	return register_candev(dev);
>  }
>  
> -static void m_can_init_ram(struct m_can_priv *priv)
> -{
> -	int end, i, start;
> -
> -	/* initialize the entire Message RAM in use to avoid possible
> -	 * ECC/parity checksum errors when reading an uninitialized buffer
> -	 */
> -	start = priv->mcfg[MRAM_SIDF].off;
> -	end = priv->mcfg[MRAM_TXB].off +
> -		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
> -	for (i = start; i < end; i += 4)
> -		writel(0x0, priv->mram_base + i);
> -}
> -
>  static void m_can_of_parse_mram(struct m_can_priv *priv,
>  				const u32 *mram_config_vals)
>  {
> @@ -1556,9 +1636,8 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
>  	priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
>  			(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
>  
> -	dev_dbg(priv->device,
> -		"mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
> -		priv->mram_base,
> +	dev_dbg(priv->dev,
> +		"sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
>  		priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
>  		priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
>  		priv->mcfg[MRAM_RXF0].off, priv->mcfg[MRAM_RXF0].num,
> @@ -1566,63 +1645,55 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
>  		priv->mcfg[MRAM_RXB].off, priv->mcfg[MRAM_RXB].num,
>  		priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
>  		priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
> -
> -	m_can_init_ram(priv);
>  }
>  
> -static int m_can_plat_probe(struct platform_device *pdev)
> +void m_can_init_ram(struct m_can_priv *priv)
>  {
> -	struct net_device *dev;
> -	struct m_can_priv *priv;
> -	struct resource *res;
> -	void __iomem *addr;
> -	void __iomem *mram_addr;
> -	struct clk *hclk, *cclk;
> -	int irq, ret;
> -	struct device_node *np;
> -	u32 mram_config_vals[MRAM_CFG_LEN];
> -	u32 tx_fifo_size;
> -
> -	np = pdev->dev.of_node;
> +	int end, i, start;
>  
> -	hclk = devm_clk_get(&pdev->dev, "hclk");
> -	cclk = devm_clk_get(&pdev->dev, "cclk");
> +	/* initialize the entire Message RAM in use to avoid possible
> +	 * ECC/parity checksum errors when reading an uninitialized buffer
> +	 */
> +	start = priv->mcfg[MRAM_SIDF].off;
> +	end = priv->mcfg[MRAM_TXB].off +
> +		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
>  
> -	if (IS_ERR(hclk) || IS_ERR(cclk)) {
> -		dev_err(&pdev->dev, "no clock found\n");
> -		ret = -ENODEV;
> -		goto failed_ret;
> -	}
> +	for (i = start; i < end; i += 4)
> +		m_can_fifo_write_no_off(priv, i, 0x0);
> +}
> +EXPORT_SYMBOL_GPL(m_can_init_ram);
>  
> -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
> -	addr = devm_ioremap_resource(&pdev->dev, res);
> -	irq = platform_get_irq_byname(pdev, "int0");
> +int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
> +{
> +	int ret = 0;
>  
> -	if (IS_ERR(addr) || irq < 0) {
> -		ret = -EINVAL;
> -		goto failed_ret;
> -	}
> +	m_can_dev->hclk = devm_clk_get(m_can_dev->dev, "hclk");
> +	m_can_dev->cclk = devm_clk_get(m_can_dev->dev, "cclk");
>  
> -	/* message ram could be shared */
> -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
> -	if (!res) {
> +	if (IS_ERR(m_can_dev->cclk)) {
> +		dev_err(m_can_dev->dev, "no clock found\n");
>  		ret = -ENODEV;
> -		goto failed_ret;
>  	}
>  
> -	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> -	if (!mram_addr) {
> -		ret = -ENOMEM;
> -		goto failed_ret;
> -	}
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
>  
> -	/* get message ram configuration */
> -	ret = of_property_read_u32_array(np, "bosch,mram-cfg",
> -					 mram_config_vals,
> -					 sizeof(mram_config_vals) / 4);
> +struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
> +{
> +	struct m_can_priv *class_dev = NULL;
> +	u32 mram_config_vals[MRAM_CFG_LEN];
> +	struct net_device *net_dev;
> +	u32 tx_fifo_size;
> +	int ret;
> +
> +	ret = fwnode_property_read_u32_array(dev_fwnode(dev),
> +					     "bosch,mram-cfg",
> +					     mram_config_vals,
> +					     sizeof(mram_config_vals) / 4);
>  	if (ret) {
> -		dev_err(&pdev->dev, "Could not get Message RAM configuration.");
> -		goto failed_ret;
> +		dev_err(dev, "Could not get Message RAM configuration.");
> +		goto out;
>  	}
>  
>  	/* Get TX FIFO size
> @@ -1631,66 +1702,74 @@ static int m_can_plat_probe(struct platform_device *pdev)
>  	tx_fifo_size = mram_config_vals[7];
>  
>  	/* allocate the m_can device */
> -	dev = alloc_candev(sizeof(*priv), tx_fifo_size);
> -	if (!dev) {
> -		ret = -ENOMEM;
> -		goto failed_ret;
> +	net_dev = alloc_candev(sizeof(*class_dev), tx_fifo_size);
> +	if (!net_dev) {
> +		dev_err(dev, "Failed to allocate CAN device");
> +		goto out;
>  	}
>  
> -	priv = netdev_priv(dev);
> -	dev->irq = irq;
> -	priv->device = &pdev->dev;
> -	priv->hclk = hclk;
> -	priv->cclk = cclk;
> -	priv->can.clock.freq = clk_get_rate(cclk);
> -	priv->mram_base = mram_addr;
> +	class_dev = netdev_priv(net_dev);
> +	if (!class_dev) {
> +		dev_err(dev, "Failed to init netdev private");
> +		goto out;
> +	}
>  
> -	platform_set_drvdata(pdev, dev);
> -	SET_NETDEV_DEV(dev, &pdev->dev);
> +	class_dev->net = net_dev;
> +	class_dev->dev = dev;
> +	SET_NETDEV_DEV(net_dev, dev);
>  
> -	/* Enable clocks. Necessary to read Core Release in order to determine
> -	 * M_CAN version
> -	 */
> -	pm_runtime_enable(&pdev->dev);
> -	ret = m_can_clk_start(priv);
> -	if (ret)
> -		goto pm_runtime_fail;
> +	m_can_of_parse_mram(class_dev, mram_config_vals);
> +out:
> +	return class_dev;
> +}
> +EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
> +
> +int m_can_class_register(struct m_can_priv *m_can_dev)
> +{
> +	int ret;
>  
> -	ret = m_can_dev_setup(pdev, dev, addr);
> +	if (m_can_dev->pm_clock_support) {
> +		pm_runtime_enable(m_can_dev->dev);
> +		ret = m_can_clk_start(m_can_dev);
> +		if (ret)
> +			goto pm_runtime_fail;
> +	}
> +
> +	ret = m_can_dev_setup(m_can_dev);
>  	if (ret)
>  		goto clk_disable;
>  
> -	ret = register_m_can_dev(dev);
> +	ret = register_m_can_dev(m_can_dev->net);
>  	if (ret) {
> -		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
> -			KBUILD_MODNAME, ret);
> +		dev_err(m_can_dev->dev, "registering %s failed (err=%d)\n",
> +			m_can_dev->net->name, ret);
>  		goto clk_disable;
>  	}
>  
> -	m_can_of_parse_mram(priv, mram_config_vals);
> -
> -	devm_can_led_init(dev);
> +	devm_can_led_init(m_can_dev->net);
>  
> -	of_can_transceiver(dev);
> +	of_can_transceiver(m_can_dev->net);
>  
> -	dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
> -		 KBUILD_MODNAME, dev->irq, priv->version);
> +	dev_info(m_can_dev->dev, "%s device registered (irq=%d, version=%d)\n",
> +		 KBUILD_MODNAME, m_can_dev->net->irq, m_can_dev->version);
>  
>  	/* Probe finished
>  	 * Stop clocks. They will be reactivated once the M_CAN device is opened
>  	 */
>  clk_disable:
> -	m_can_clk_stop(priv);
> +	m_can_clk_stop(m_can_dev);
>  pm_runtime_fail:
>  	if (ret) {
> -		pm_runtime_disable(&pdev->dev);
> -		free_candev(dev);
> +		if (m_can_dev->pm_clock_support)
> +			pm_runtime_disable(m_can_dev->dev);
> +		free_candev(m_can_dev->net);
>  	}
> -failed_ret:
> +
>  	return ret;
>  }
> +EXPORT_SYMBOL_GPL(m_can_class_register);
>  
> -static __maybe_unused int m_can_suspend(struct device *dev)
> +int m_can_class_suspend(struct device *dev)
>  {
>  	struct net_device *ndev = dev_get_drvdata(dev);
>  	struct m_can_priv *priv = netdev_priv(ndev);
> @@ -1708,8 +1787,9 @@ static __maybe_unused int m_can_suspend(struct device *dev)
>  
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(m_can_class_suspend);
>  
> -static __maybe_unused int m_can_resume(struct device *dev)
> +int m_can_class_resume(struct device *dev)
>  {
>  	struct net_device *ndev = dev_get_drvdata(dev);
>  	struct m_can_priv *priv = netdev_priv(ndev);
> @@ -1733,79 +1813,19 @@ static __maybe_unused int m_can_resume(struct device *dev)
>  
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(m_can_class_resume);
>  
> -static void unregister_m_can_dev(struct net_device *dev)
> +void m_can_class_unregister(struct m_can_priv *m_can_dev)
>  {
> -	unregister_candev(dev);
> -}
> +	unregister_candev(m_can_dev->net);
>  
> -static int m_can_plat_remove(struct platform_device *pdev)
> -{
> -	struct net_device *dev = platform_get_drvdata(pdev);
> +	m_can_clk_stop(m_can_dev);
>  
> -	unregister_m_can_dev(dev);
> -
> -	pm_runtime_disable(&pdev->dev);
> -
> -	platform_set_drvdata(pdev, NULL);
> -
> -	free_candev(dev);
> -
> -	return 0;
> -}
> -
> -static int __maybe_unused m_can_runtime_suspend(struct device *dev)
> -{
> -	struct net_device *ndev = dev_get_drvdata(dev);
> -	struct m_can_priv *priv = netdev_priv(ndev);
> -
> -	clk_disable_unprepare(priv->cclk);
> -	clk_disable_unprepare(priv->hclk);
> -
> -	return 0;
> -}
> -
> -static int __maybe_unused m_can_runtime_resume(struct device *dev)
> -{
> -	struct net_device *ndev = dev_get_drvdata(dev);
> -	struct m_can_priv *priv = netdev_priv(ndev);
> -	int err;
> -
> -	err = clk_prepare_enable(priv->hclk);
> -	if (err)
> -		return err;
> -
> -	err = clk_prepare_enable(priv->cclk);
> -	if (err)
> -		clk_disable_unprepare(priv->hclk);
> -
> -	return err;
> +	free_candev(m_can_dev->net);
>  }
> -
> -static const struct dev_pm_ops m_can_pmops = {
> -	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
> -			   m_can_runtime_resume, NULL)
> -	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
> -};
> -
> -static const struct of_device_id m_can_of_table[] = {
> -	{ .compatible = "bosch,m_can", .data = NULL },
> -	{ /* sentinel */ },
> -};
> -MODULE_DEVICE_TABLE(of, m_can_of_table);
> -
> -static struct platform_driver m_can_plat_driver = {
> -	.driver = {
> -		.name = KBUILD_MODNAME,
> -		.of_match_table = m_can_of_table,
> -		.pm     = &m_can_pmops,
> -	},
> -	.probe = m_can_plat_probe,
> -	.remove = m_can_plat_remove,
> -};
> -
> -module_platform_driver(m_can_plat_driver);
> +EXPORT_SYMBOL_GPL(m_can_class_unregister);
>  
>  MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
>  MODULE_LICENSE("GPL v2");
>  MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");
> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> new file mode 100644
> index 000000000000..70ab7bba063d
> --- /dev/null
> +++ b/drivers/net/can/m_can/m_can.h
> @@ -0,0 +1,110 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* CAN bus driver for Bosch M_CAN controller
> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> + */
> +
> +#ifndef _CAN_M_CAN_H_
> +#define _CAN_M_CAN_H_
> +
> +#include <linux/can/core.h>
> +#include <linux/can/led.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/freezer.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/clk.h>
> +#include <linux/delay.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/pm_runtime.h>
> +#include <linux/iopoll.h>
> +#include <linux/can/dev.h>
> +#include <linux/pinctrl/consumer.h>
> +
> +/* m_can lec values */
> +enum m_can_lec_type {
> +	LEC_NO_ERROR = 0,
> +	LEC_STUFF_ERROR,
> +	LEC_FORM_ERROR,
> +	LEC_ACK_ERROR,
> +	LEC_BIT1_ERROR,
> +	LEC_BIT0_ERROR,
> +	LEC_CRC_ERROR,
> +	LEC_UNUSED,
> +};
> +
> +enum m_can_mram_cfg {
> +	MRAM_SIDF = 0,
> +	MRAM_XIDF,
> +	MRAM_RXF0,
> +	MRAM_RXF1,
> +	MRAM_RXB,
> +	MRAM_TXE,
> +	MRAM_TXB,
> +	MRAM_CFG_NUM,
> +};
> +
> +/* address offset and element number for each FIFO/Buffer in the Message RAM */
> +struct mram_cfg {
> +	u16 off;
> +	u8  num;
> +};
> +
> +struct m_can_priv;
> +struct m_can_ops {
> +	/* Device specific call backs */
> +	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);

Why not just "clear_interrupt"... to be consistant with the names below.

> +	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
> +	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
> +	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
> +	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
> +			  int val);
> +	int (*device_init)(struct m_can_priv *m_can_class);
> +};
> +
> +struct m_can_priv {
> +	struct can_priv can;
> +	struct napi_struct napi;
> +	struct net_device *net;
> +	struct device *dev;
> +	struct clk *hclk;
> +	struct clk *cclk;
> +
> +	struct workqueue_struct *wq;

s/wq/tx_wq/ as already suggested above.

> +	struct work_struct tx_work;
> +	struct sk_buff *tx_skb;
> +
> +	struct can_bittiming_const *bit_timing;
> +	struct can_bittiming_const *data_timing;
> +
> +	struct m_can_ops *ops;
> +
> +	void *device_data;
> +
> +	int version;
> +	int freq;
> +	u32 irqstatus;
> +
> +	int pm_clock_support;

A "bool" would be more appropriate, I think.

> +	int is_peripherial;
> +
> +	struct mram_cfg mcfg[MRAM_CFG_NUM];
> +};
> +
> +struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
> +int m_can_class_register(struct m_can_priv *m_can_dev);
> +void m_can_class_unregister(struct m_can_priv *m_can_dev);
> +int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
> +void m_can_init_ram(struct m_can_priv *priv);
> +void m_can_config_endisable(struct m_can_priv *priv, bool enable);
> +
> +int m_can_class_suspend(struct device *dev);
> +int m_can_class_resume(struct device *dev);
> +#endif	/* _CAN_M_H_ */
> diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
> new file mode 100644
> index 000000000000..92d989ce032a
> --- /dev/null
> +++ b/drivers/net/can/m_can/m_can_platform.c
> @@ -0,0 +1,198 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// IOMapped CAN bus driver for Bosch M_CAN controller
> +// Copyright (C) 2014 Freescale Semiconductor, Inc.
> +//	Dong Aisheng <b29396@freescale.com>
> +//
> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#include <linux/platform_device.h>
> +
> +#include "m_can.h"
> +
> +struct m_can_plat_priv {
> +	void __iomem *base;
> +	void __iomem *mram_base;
> +};
> +
> +static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
> +{
> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
> +
> +	return readl(priv->base + reg);
> +}
> +
> +static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
> +{
> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
> +
> +	return readl(priv->mram_base + offset);
> +}
> +
> +static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
> +{
> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
> +
> +	writel(val, priv->base + reg);
> +
> +	return 0;
> +}
> +
> +static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
> +{
> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
> +
> +	writel(val, priv->mram_base + offset);
> +
> +	return 0;
> +}
> +
> +static struct m_can_ops m_can_plat_ops = {
> +	.read_reg = iomap_read_reg,
> +	.write_reg = iomap_write_reg,
> +	.write_fifo = iomap_write_fifo,
> +	.read_fifo = iomap_read_fifo,
> +};
> +
> +static int m_can_plat_probe(struct platform_device *pdev)
> +{
> +	struct m_can_priv *mcan_class;
> +	struct m_can_plat_priv *priv;
> +	struct resource *res;
> +	void __iomem *addr;
> +	void __iomem *mram_addr;
> +	int irq, ret = 0;
> +
> +	mcan_class = m_can_class_allocate_dev(&pdev->dev);
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	mcan_class->device_data = priv;
> +
> +	m_can_class_get_clocks(mcan_class);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
> +	addr = devm_ioremap_resource(&pdev->dev, res);
> +	irq = platform_get_irq_byname(pdev, "int0");
> +	if (IS_ERR(addr) || irq < 0) {
> +		ret = -EINVAL;
> +		goto failed_ret;
> +	}
> +
> +	/* message ram could be shared */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
> +	if (!res) {
> +		ret = -ENODEV;
> +		goto failed_ret;
> +	}
> +
> +	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!mram_addr) {
> +		ret = -ENOMEM;
> +		goto failed_ret;
> +	}
> +
> +	priv->base = addr;
> +	priv->mram_base = mram_addr;
> +
> +	mcan_class->net->irq = irq;
> +	mcan_class->pm_clock_support = 1;
> +	mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
> +	mcan_class->dev = &pdev->dev;
> +
> +	mcan_class->ops = &m_can_plat_ops;
> +
> +	mcan_class->is_peripherial = false;
> +
> +	platform_set_drvdata(pdev, mcan_class->dev);
> +
> +	m_can_init_ram(mcan_class);
> +
> +	ret = m_can_class_register(mcan_class);
> +
> +failed_ret:
> +	return ret;
> +}
> +
> +static __maybe_unused int m_can_suspend(struct device *dev)
> +{
> +	return m_can_class_suspend(dev);
> +}
> +
> +static __maybe_unused int m_can_resume(struct device *dev)
> +{
> +	return m_can_class_resume(dev);
> +}
> +
> +static int m_can_plat_remove(struct platform_device *pdev)
> +{
> +	struct net_device *dev = platform_get_drvdata(pdev);
> +	struct m_can_priv *mcan_class = netdev_priv(dev);
> +
> +	m_can_class_unregister(mcan_class);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused m_can_runtime_suspend(struct device *dev)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct m_can_priv *mcan_class = netdev_priv(ndev);
> +
> +	m_can_class_suspend(dev);
> +
> +	clk_disable_unprepare(mcan_class->cclk);
> +	clk_disable_unprepare(mcan_class->hclk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused m_can_runtime_resume(struct device *dev)
> +{
> +	struct net_device *ndev = dev_get_drvdata(dev);
> +	struct m_can_priv *mcan_class = netdev_priv(ndev);
> +	int err;
> +
> +	err = clk_prepare_enable(mcan_class->hclk);
> +	if (err)
> +		return err;
> +
> +	err = clk_prepare_enable(mcan_class->cclk);
> +	if (err)
> +		clk_disable_unprepare(mcan_class->hclk);
> +
> +	m_can_class_resume(dev);
> +
> +	return err;
> +}
> +
> +static const struct dev_pm_ops m_can_pmops = {
> +	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
> +			   m_can_runtime_resume, NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
> +};
> +
> +static const struct of_device_id m_can_of_table[] = {
> +	{ .compatible = "bosch,m_can", .data = NULL },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, m_can_of_table);
> +
> +static struct platform_driver m_can_plat_driver = {
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +		.of_match_table = m_can_of_table,
> +		.pm     = &m_can_pmops,
> +	},
> +	.probe = m_can_plat_probe,
> +	.remove = m_can_plat_remove,
> +};
> +
> +module_platform_driver(m_can_plat_driver);
> +
> +MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");
> 

Wolfgang.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-04 16:56 ` [PATCH v6 1/4] can: m_can: Create a m_can platform framework Wolfgang Grandegger
@ 2019-03-04 17:22   ` Dan Murphy
  2019-03-04 18:13     ` Wolfgang Grandegger
  2019-03-04 19:43   ` Dan Murphy
  1 sibling, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-04 17:22 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Wolfgang

On 3/4/19 10:56 AM, Wolfgang Grandegger wrote:
> Hello Dan,
> 
> the series already looks quite good. I still realized a few (minor)
> issues while browsing the patch/code...
> 

Thanks for the review.  It is getting there.

> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>> Create a m_can platform framework that peripherial
>> devices can register to and use common code and register sets.
>> The peripherial devices may provide read/write and configuration
>> support of the IP.
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ---
>>
>> v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
>> issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
>> priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
>> Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
>> Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/
>>
>>  drivers/net/can/m_can/Kconfig          |  13 +-
>>  drivers/net/can/m_can/Makefile         |   1 +
>>  drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
>>  drivers/net/can/m_can/m_can.h          | 110 ++++
>>  drivers/net/can/m_can/m_can_platform.c | 198 +++++++
>>  5 files changed, 681 insertions(+), 343 deletions(-)
>>  create mode 100644 drivers/net/can/m_can/m_can.h
>>  create mode 100644 drivers/net/can/m_can/m_can_platform.c
>>
>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>> index 04f20dd39007..f7119fd72df4 100644
>> --- a/drivers/net/can/m_can/Kconfig
>> +++ b/drivers/net/can/m_can/Kconfig
>> @@ -1,5 +1,14 @@
>>  config CAN_M_CAN
>> +	tristate "Bosch M_CAN support"
>> +	---help---
>> +	  Say Y here if you want support for Bosch M_CAN controller framework.
>> +	  This is common support for devices that embed the Bosch M_CAN IP.
>> +
>> +config CAN_M_CAN_PLATFORM
>> +	tristate "Bosch M_CAN support for io-mapped devices"
>>  	depends on HAS_IOMEM
>> -	tristate "Bosch M_CAN devices"
>> +	depends on CAN_M_CAN
>>  	---help---
>> -	  Say Y here if you want to support for Bosch M_CAN controller.
>> +	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>> +	  This support is for devices that have the Bosch M_CAN controller
>> +	  IP embedded into the device and the IP is IO Mapped to the processor.
>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>> index 8bbd7f24f5be..057bbcdb3c74 100644
>> --- a/drivers/net/can/m_can/Makefile
>> +++ b/drivers/net/can/m_can/Makefile
>> @@ -3,3 +3,4 @@
>>  #
>>  
>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>> +obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>> index 9b449400376b..b37d0886f9cb 100644
>> --- a/drivers/net/can/m_can/m_can.c
>> +++ b/drivers/net/can/m_can/m_can.c
>> @@ -1,20 +1,14 @@
>> -/*
>> - * CAN bus driver for Bosch M_CAN controller
>> - *
>> - * Copyright (C) 2014 Freescale Semiconductor, Inc.
>> - *	Dong Aisheng <b29396@freescale.com>
>> - *
>> - * Bosch M_CAN user manual can be obtained from:
>> +// SPDX-License-Identifier: GPL-2.0
>> +// CAN bus driver for Bosch M_CAN controller
>> +// Copyright (C) 2014 Freescale Semiconductor, Inc.
>> +//      Dong Aisheng <b29396@freescale.com>
>> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +/* Bosch M_CAN user manual can be obtained from:
>>   * http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/
>>   * mcan_users_manual_v302.pdf
>> - *
>> - * This file is licensed under the terms of the GNU General Public
>> - * License version 2. This program is licensed "as is" without any
>> - * warranty of any kind, whether express or implied.
>>   */
>>  
>> -#include <linux/clk.h>
>> -#include <linux/delay.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/io.h>
>>  #include <linux/kernel.h>
>> @@ -28,11 +22,7 @@
>>  #include <linux/can/dev.h>
>>  #include <linux/pinctrl/consumer.h>
>>  
>> -/* napi related */
>> -#define M_CAN_NAPI_WEIGHT	64
>> -
>> -/* message ram configuration data length */
>> -#define MRAM_CFG_LEN	8
>> +#include "m_can.h"
>>  
>>  /* registers definition */
>>  enum m_can_reg {
>> @@ -86,28 +76,11 @@ enum m_can_reg {
>>  	M_CAN_TXEFA	= 0xf8,
>>  };
>>  
>> -/* m_can lec values */
>> -enum m_can_lec_type {
>> -	LEC_NO_ERROR = 0,
>> -	LEC_STUFF_ERROR,
>> -	LEC_FORM_ERROR,
>> -	LEC_ACK_ERROR,
>> -	LEC_BIT1_ERROR,
>> -	LEC_BIT0_ERROR,
>> -	LEC_CRC_ERROR,
>> -	LEC_UNUSED,
>> -};
>> +/* napi related */
>> +#define M_CAN_NAPI_WEIGHT	64
>>  
>> -enum m_can_mram_cfg {
>> -	MRAM_SIDF = 0,
>> -	MRAM_XIDF,
>> -	MRAM_RXF0,
>> -	MRAM_RXF1,
>> -	MRAM_RXB,
>> -	MRAM_TXE,
>> -	MRAM_TXB,
>> -	MRAM_CFG_NUM,
>> -};
>> +/* message ram configuration data length */
>> +#define MRAM_CFG_LEN	8
>>  
>>  /* Core Release Register (CREL) */
>>  #define CREL_REL_SHIFT		28
>> @@ -343,77 +316,83 @@ enum m_can_mram_cfg {
>>  #define TX_BUF_MM_MASK		(0xff << TX_BUF_MM_SHIFT)
>>  
>>  /* Tx event FIFO Element */
>> -/* E1 */
> 
> I also don't understand what "E1" means... we should keep taht line anyway!
> 

OK I can add it back I believe it means event FIFO 1.  Thought is was unneeded

>>  #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT
>>  #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT)
>>  
>> -/* address offset and element number for each FIFO/Buffer in the Message RAM */
>> -struct mram_cfg {
>> -	u16 off;
>> -	u8  num;
>> -};
>> -
>> -/* m_can private data structure */
>> -struct m_can_priv {
>> -	struct can_priv can;	/* must be the first member */
>> -	struct napi_struct napi;
>> -	struct net_device *dev;
>> -	struct device *device;
>> -	struct clk *hclk;
>> -	struct clk *cclk;
>> -	void __iomem *base;
>> -	u32 irqstatus;
>> -	int version;
>> -
>> -	/* message ram configuration */
>> -	void __iomem *mram_base;
>> -	struct mram_cfg mcfg[MRAM_CFG_NUM];
>> -};
>> +static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
>> +{
>> +	if (priv->ops->read_reg)
>> +		return priv->ops->read_reg(priv, reg);
>> +	else
>> +		return -EINVAL;
>> +}
>>  
>> -static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg)
>> +static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
>>  {
>> -	return readl(priv->base + reg);
>> +	if (priv->ops->write_reg)
>> +		return priv->ops->write_reg(priv, reg, val);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline void m_can_write(const struct m_can_priv *priv,
>> -			       enum m_can_reg reg, u32 val)
>> +static u32 m_can_fifo_read(struct m_can_priv *priv,
>> +			   u32 fgi, unsigned int offset)
>>  {
>> -	writel(val, priv->base + reg);
>> +	u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->read_fifo)
>> +		return priv->ops->read_fifo(priv, addr_offset);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
>> -				  u32 fgi, unsigned int offset)
>> +static u32 m_can_fifo_write(struct m_can_priv *priv,
>> +			    u32 fpi, unsigned int offset, u32 val)
>>  {
>> -	return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
>> -		     fgi * RXF0_ELEMENT_SIZE + offset);
>> +	u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->write_fifo)
>> +		return priv->ops->write_fifo(priv, addr_offset, val);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline void m_can_fifo_write(const struct m_can_priv *priv,
>> -				    u32 fpi, unsigned int offset, u32 val)
>> +static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
>> +				   u32 fpi, u32 val)
>>  {
>> -	writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
>> -	       fpi * TXB_ELEMENT_SIZE + offset);
>> +	if (priv->ops->write_fifo)
>> +		return priv->ops->write_fifo(priv, fpi, val);
>> +	else
>> +		return 0;
>>  }
>>  
>> -static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
>> -				      u32 fgi,
>> -				      u32 offset) {
>> -	return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
>> -			fgi * TXE_ELEMENT_SIZE + offset);
>> +static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
>> +{
>> +	u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->read_fifo)
>> +		return priv->ops->read_fifo(priv, addr_offset);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
>> +static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
>>  {
>>  		return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
>>  }
>>  
>> -static inline void m_can_config_endisable(const struct m_can_priv *priv,
>> -					  bool enable)
>> +void m_can_config_endisable(struct m_can_priv *priv, bool enable)
>>  {
>>  	u32 cccr = m_can_read(priv, M_CAN_CCCR);
>>  	u32 timeout = 10;
>>  	u32 val = 0;
>>  
>> +	if (cccr & CCCR_CSR)
>> +		cccr &= ~CCCR_CSR;
>> +
> 
> Is that a bug fix? Or why do we need it?

I will take a look again at it to see if it is needed for both io mapped and perp devices.


> 
>>  	if (enable) {
>>  		/* enable m_can configuration */
>>  		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
>> @@ -430,7 +409,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>>  
>>  	while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
>>  		if (timeout == 0) {
>> -			netdev_warn(priv->dev, "Failed to init module\n");
>> +			netdev_warn(priv->net, "Failed to init module\n");
>>  			return;
>>  		}
>>  		timeout--;
>> @@ -438,13 +417,13 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>>  	}
>>  }
>>  
>> -static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
>> +static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
>>  {
>>  	/* Only interrupt line 0 is used in this driver */
>>  	m_can_write(priv, M_CAN_ILE, ILE_EINT0);
>>  }
>>  
>> -static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
>> +static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
>>  {
>>  	m_can_write(priv, M_CAN_ILE, 0x0);
>>  }
>> @@ -633,9 +612,12 @@ static int m_can_clk_start(struct m_can_priv *priv)
>>  {
>>  	int err;
>>  
>> -	err = pm_runtime_get_sync(priv->device);
>> +	if (priv->pm_clock_support == 0)
>> +		return 0;
>> +
>> +	err = pm_runtime_get_sync(priv->dev);
>>  	if (err < 0) {
>> -		pm_runtime_put_noidle(priv->device);
>> +		pm_runtime_put_noidle(priv->dev);
>>  		return err;
>>  	}
>>  
>> @@ -644,7 +626,8 @@ static int m_can_clk_start(struct m_can_priv *priv)
>>  
>>  static void m_can_clk_stop(struct m_can_priv *priv)
>>  {
>> -	pm_runtime_put_sync(priv->device);
>> +	if (priv->pm_clock_support)
>> +		pm_runtime_put_sync(priv->dev);
>>  }
>>  
>>  static int m_can_get_berr_counter(const struct net_device *dev,
>> @@ -811,9 +794,8 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
>>  	return work_done;
>>  }
>>  
>> -static int m_can_poll(struct napi_struct *napi, int quota)
>> +static int m_can_rx_handler(struct net_device *dev, int quota)
>>  {
>> -	struct net_device *dev = napi->dev;
>>  	struct m_can_priv *priv = netdev_priv(dev);
>>  	int work_done = 0;
>>  	u32 irqstatus, psr;
>> @@ -831,13 +813,33 @@ static int m_can_poll(struct napi_struct *napi, int quota)
>>  
>>  	if (irqstatus & IR_RF0N)
>>  		work_done += m_can_do_rx_poll(dev, (quota - work_done));
>> +end:
>> +	return work_done;
>> +}
>> +
>> +static int m_can_rx_peripherial(struct net_device *dev)
>> +{
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +
>> +	m_can_rx_handler(dev, 1);
>> +
>> +	m_can_enable_all_interrupts(priv);
>> +
>> +	return 0;
>> +}
>> +
>> +static int m_can_poll(struct napi_struct *napi, int quota)
>> +{
>> +	struct net_device *dev = napi->dev;
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +	int work_done;
>>  
>> +	work_done = m_can_rx_handler(dev, quota);
>>  	if (work_done < quota) {
>>  		napi_complete_done(napi, work_done);
>>  		m_can_enable_all_interrupts(priv);
>>  	}
>>  
>> -end:
>>  	return work_done;
>>  }
>>  
>> @@ -894,6 +896,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>>  	if (ir & IR_ALL_INT)
>>  		m_can_write(priv, M_CAN_IR, ir);
>>  
>> +	if (priv->ops->clr_dev_interrupts)
>> +		priv->ops->clr_dev_interrupts(priv);
>> +
>>  	/* schedule NAPI in case of
>>  	 * - rx IRQ
>>  	 * - state change IRQ
>> @@ -902,7 +907,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>>  	if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
>>  		priv->irqstatus = ir;
>>  		m_can_disable_all_interrupts(priv);
>> -		napi_schedule(&priv->napi);
>> +		if (!priv->is_peripherial)
>> +			napi_schedule(&priv->napi);
>> +		else
>> +			m_can_rx_peripherial(dev);
>>  	}
>>  
>>  	if (priv->version == 30) {
>> @@ -1155,6 +1163,9 @@ static void m_can_chip_config(struct net_device *dev)
>>  	m_can_set_bittiming(dev);
>>  
>>  	m_can_config_endisable(priv, false);
>> +
>> +	if (priv->ops->device_init)
>> +		priv->ops->device_init(priv);
>>  }
>>  
>>  static void m_can_start(struct net_device *dev)
>> @@ -1188,20 +1199,17 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
>>   * else it returns the release and step coded as:
>>   * return value = 10 * <release> + 1 * <step>
>>   */
>> -static int m_can_check_core_release(void __iomem *m_can_base)
>> +static int m_can_check_core_release(struct m_can_priv *priv)
>>  {
>>  	u32 crel_reg;
>>  	u8 rel;
>>  	u8 step;
>>  	int res;
>> -	struct m_can_priv temp_priv = {
>> -		.base = m_can_base
>> -	};
>>  
>>  	/* Read Core Release Version and split into version number
>>  	 * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
>>  	 */
>> -	crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
>> +	crel_reg = m_can_read(priv, M_CAN_CREL);
>>  	rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
>>  	step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
>>  
>> @@ -1219,18 +1227,26 @@ static int m_can_check_core_release(void __iomem *m_can_base)
>>  /* Selectable Non ISO support only in version 3.2.x
>>   * This function checks if the bit is writable.
>>   */
>> -static bool m_can_niso_supported(const struct m_can_priv *priv)
>> +static bool m_can_niso_supported(struct m_can_priv *priv)
>>  {
>> -	u32 cccr_reg, cccr_poll;
>> -	int niso_timeout;
>> +	u32 cccr_reg, cccr_poll = 0;
>> +	int niso_timeout = -ETIMEDOUT;
>> +	int i;
>>  
>>  	m_can_config_endisable(priv, true);
>>  	cccr_reg = m_can_read(priv, M_CAN_CCCR);
>>  	cccr_reg |= CCCR_NISO;
>>  	m_can_write(priv, M_CAN_CCCR, cccr_reg);
>>  
>> -	niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
>> -					  (cccr_poll == cccr_reg), 0, 10);
>> +	for (i = 0; i <= 10; i++) {
>> +		cccr_poll = m_can_read(priv, M_CAN_CCCR);
>> +		if (cccr_poll == cccr_reg) {
>> +			niso_timeout = 0;
>> +			break;
>> +		}
>> +
>> +		usleep_range(1, 5);
>> +	}
>>  
>>  	/* Clear NISO */
>>  	cccr_reg &= ~(CCCR_NISO);
>> @@ -1242,107 +1258,95 @@ static bool m_can_niso_supported(const struct m_can_priv *priv)
>>  	return !niso_timeout;
>>  }
>>  
>> -static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
>> -			   void __iomem *addr)
>> +static int m_can_dev_setup(struct m_can_priv *m_can_dev)
>>  {
>> -	struct m_can_priv *priv;
>> +	struct net_device *dev = m_can_dev->net;
>>  	int m_can_version;
>>  
>> -	m_can_version = m_can_check_core_release(addr);
>> +	m_can_version = m_can_check_core_release(m_can_dev);
>>  	/* return if unsupported version */
>>  	if (!m_can_version) {
>> -		dev_err(&pdev->dev, "Unsupported version number: %2d",
>> +		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
>>  			m_can_version);
>>  		return -EINVAL;
>>  	}
>>  
>> -	priv = netdev_priv(dev);
>> -	netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
>> +	if (!m_can_dev->is_peripherial)
>> +		netif_napi_add(dev, &m_can_dev->napi,
>> +			       m_can_poll, M_CAN_NAPI_WEIGHT);
>>  
>>  	/* Shared properties of all M_CAN versions */
>> -	priv->version = m_can_version;
>> -	priv->dev = dev;
>> -	priv->base = addr;
>> -	priv->can.do_set_mode = m_can_set_mode;
>> -	priv->can.do_get_berr_counter = m_can_get_berr_counter;
>> +	m_can_dev->version = m_can_version;
>> +	m_can_dev->can.do_set_mode = m_can_set_mode;
>> +	m_can_dev->can.do_get_berr_counter = m_can_get_berr_counter;
>>  
>>  	/* Set M_CAN supported operations */
>> -	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>> +	m_can_dev->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>>  					CAN_CTRLMODE_LISTENONLY |
>>  					CAN_CTRLMODE_BERR_REPORTING |
>>  					CAN_CTRLMODE_FD;
>>  
>>  	/* Set properties depending on M_CAN version */
>> -	switch (priv->version) {
>> +	switch (m_can_dev->version) {
>>  	case 30:
>>  		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
>>  		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
>> -		priv->can.bittiming_const = &m_can_bittiming_const_30X;
>> -		priv->can.data_bittiming_const =
>> +		if (m_can_dev->bit_timing)
>> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
>> +		else
>> +			m_can_dev->can.bittiming_const =
>> +				&m_can_bittiming_const_30X;
> 
> 		m_can_dev->can.bittiming_const = m_can_dev->bit_timing ?
> 			m_can_dev->bit_timing : &m_can_bittiming_const_30X;
> 
> Is more compact. Here and below.

Ack

> 
>> +		if (m_can_dev->data_timing)
>> +			m_can_dev->can.data_bittiming_const =
>> +				m_can_dev->data_timing;
>> +		else
>> +			m_can_dev->can.data_bittiming_const =
>>  				&m_can_data_bittiming_const_30X;
>>  		break;
>>  	case 31:
>>  		/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
>>  		can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
>> -		priv->can.bittiming_const = &m_can_bittiming_const_31X;
>> -		priv->can.data_bittiming_const =
>> +		if (m_can_dev->bit_timing)
>> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
>> +		else
>> +			m_can_dev->can.bittiming_const =
>> +				&m_can_bittiming_const_31X;
>> +		if (m_can_dev->data_timing)
>> +			m_can_dev->can.data_bittiming_const =
>> +				m_can_dev->data_timing;
>> +		else
>> +			m_can_dev->can.data_bittiming_const =
>>  				&m_can_data_bittiming_const_31X;
>>  		break;
>>  	case 32:
>> -		priv->can.bittiming_const = &m_can_bittiming_const_31X;
>> -		priv->can.data_bittiming_const =
>> +		if (m_can_dev->bit_timing)
>> +			m_can_dev->can.bittiming_const = m_can_dev->bit_timing;
>> +		else
>> +			m_can_dev->can.bittiming_const =
>> +				&m_can_bittiming_const_31X;
>> +
>> +		if (m_can_dev->data_timing)
>> +			m_can_dev->can.data_bittiming_const =
>> +				m_can_dev->data_timing;
>> +		else
>> +			m_can_dev->can.data_bittiming_const =
>>  				&m_can_data_bittiming_const_31X;
>> -		priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
>> +
>> +		m_can_dev->can.ctrlmode_supported |=
>> +						(m_can_niso_supported(m_can_dev)
>>  						? CAN_CTRLMODE_FD_NON_ISO
>>  						: 0);
>>  		break;
>>  	default:
>> -		dev_err(&pdev->dev, "Unsupported version number: %2d",
>> -			priv->version);
>> +		dev_err(m_can_dev->dev, "Unsupported version number: %2d",
>> +			m_can_dev->version);
>>  		return -EINVAL;
>>  	}
>>  
>> -	return 0;
>> -}
>> -
>> -static int m_can_open(struct net_device *dev)
>> -{
>> -	struct m_can_priv *priv = netdev_priv(dev);
>> -	int err;
>> -
>> -	err = m_can_clk_start(priv);
>> -	if (err)
>> -		return err;
>> -
>> -	/* open the can device */
>> -	err = open_candev(dev);
>> -	if (err) {
>> -		netdev_err(dev, "failed to open can device\n");
>> -		goto exit_disable_clks;
>> -	}
>> -
>> -	/* register interrupt handler */
>> -	err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
>> -			  dev);
>> -	if (err < 0) {
>> -		netdev_err(dev, "failed to request interrupt\n");
>> -		goto exit_irq_fail;
>> -	}
>> -
>> -	/* start the m_can controller */
>> -	m_can_start(dev);
>> -
>> -	can_led_event(dev, CAN_LED_EVENT_OPEN);
>> -	napi_enable(&priv->napi);
>> -	netif_start_queue(dev);
>> +	if (m_can_dev->ops->device_init)
>> +		m_can_dev->ops->device_init(m_can_dev);
>>  
>>  	return 0;
>> -
>> -exit_irq_fail:
>> -	close_candev(dev);
>> -exit_disable_clks:
>> -	m_can_clk_stop(priv);
>> -	return err;
>>  }
>>  
>>  static void m_can_stop(struct net_device *dev)
>> @@ -1361,10 +1365,17 @@ static int m_can_close(struct net_device *dev)
>>  	struct m_can_priv *priv = netdev_priv(dev);
>>  
>>  	netif_stop_queue(dev);
>> -	napi_disable(&priv->napi);
>> +	if (!priv->is_peripherial)
>> +		napi_disable(&priv->napi);
>>  	m_can_stop(dev);
>>  	m_can_clk_stop(priv);
>>  	free_irq(dev->irq, dev);
>> +
>> +	if (priv->is_peripherial) {
>> +		destroy_workqueue(priv->wq);
>> +		priv->wq = NULL;
> 
> Could you pease use the name "priv->tx_wq".
> 

ACK

>> +	}
>> +
>>  	close_candev(dev);
>>  	can_led_event(dev, CAN_LED_EVENT_STOP);
>>  
>> @@ -1385,18 +1396,15 @@ static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
>>  	return !!priv->can.echo_skb[next_idx];
>>  }
>>  
>> -static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>> -				    struct net_device *dev)
>> +static void m_can_tx_handler(struct m_can_priv *priv)
>>  {
>> -	struct m_can_priv *priv = netdev_priv(dev);
>> -	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
>> +	struct canfd_frame *cf = (struct canfd_frame *)priv->tx_skb->data;
>> +	struct net_device *dev = priv->net;
>> +	struct sk_buff *skb = priv->tx_skb;
>>  	u32 id, cccr, fdflags;
>>  	int i;
>>  	int putidx;
>>  
>> -	if (can_dropped_invalid_skb(dev, skb))
>> -		return NETDEV_TX_OK;
>> -
>>  	/* Generate ID field for TX buffer Element */
>>  	/* Common to all supported M_CAN versions */
>>  	if (cf->can_id & CAN_EFF_FLAG) {
>> @@ -1451,7 +1459,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>>  			netif_stop_queue(dev);
>>  			netdev_warn(dev,
>>  				    "TX queue active although FIFO is full.");
>> -			return NETDEV_TX_BUSY;
>> +			return;
> 
> m_can_start_xmit() doesn't return NETDEV_TX_BUSY but NETDEV_TX_OK and
> the queue is stopped! Also the skb is not freed! The code states 
> "/* This shouldn't happen */" but then it just prints a warning. Did
> you see that message?
> 

No I have not seen this warning but I will re-check to be sure.

>>  		}
>>  
>>  		/* get put index for frame */
>> @@ -1492,14 +1500,100 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>>  		m_can_write(priv, M_CAN_TXBAR, (1 << putidx));
>>  
>>  		/* stop network queue if fifo full */
>> -			if (m_can_tx_fifo_full(priv) ||
>> -			    m_can_next_echo_skb_occupied(dev, putidx))
>> -				netif_stop_queue(dev);
>> +		if (m_can_tx_fifo_full(priv) ||
>> +		    m_can_next_echo_skb_occupied(dev, putidx))
>> +			netif_stop_queue(dev);
>> +	}
>> +}
>> +
>> +static void m_can_tx_work_queue(struct work_struct *ws)
>> +{
>> +	struct m_can_priv *priv = container_of(ws, struct m_can_priv,
>> +						tx_work);
>> +	m_can_tx_handler(priv);
>> +}
>> +
>> +static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>> +				    struct net_device *dev)
>> +{
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +
>> +	if (can_dropped_invalid_skb(dev, skb))
>> +		return NETDEV_TX_OK;
>> +
>> +	priv->tx_skb = skb;
>> +	if (priv->is_peripherial) {
>> +		netif_stop_queue(priv->net);
>> +		queue_work(priv->wq, &priv->tx_work);
>> +	} else {
>> +		m_can_tx_handler(priv);
>>  	}
>>  
>>  	return NETDEV_TX_OK;
>>  }
>>  
>> +static int m_can_open(struct net_device *dev)
>> +{
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +	int err;
>> +
>> +	err = m_can_clk_start(priv);
>> +	if (err)
>> +		return err;
>> +
>> +	/* open the can device */
>> +	err = open_candev(dev);
>> +	if (err) {
>> +		netdev_err(dev, "failed to open can device\n");
>> +		goto exit_disable_clks;
>> +	}
>> +
>> +	/* register interrupt handler */
>> +	if (priv->is_peripherial) {
>> +		priv->wq = alloc_workqueue("mcan_wq",
>> +					   WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
>> +		if (!priv->wq) {
>> +			err = -ENOMEM;
>> +			goto out_wq_fail;
>> +		}
>> +
>> +		INIT_WORK(&priv->tx_work, m_can_tx_work_queue);
>> +
>> +		err = request_threaded_irq(dev->irq, NULL, m_can_isr,
>> +					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
>> +					   dev->name, dev);
>> +	} else {
>> +		err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
>> +				  dev);
>> +	}
>> +
>> +	if (err < 0) {
>> +		netdev_err(dev, "failed to request interrupt\n");
>> +		goto exit_irq_fail;
>> +	}
>> +
>> +	/* start the m_can controller */
>> +	m_can_start(dev);
>> +
>> +	can_led_event(dev, CAN_LED_EVENT_OPEN);
>> +
>> +	if (!priv->is_peripherial)
>> +		napi_enable(&priv->napi);
>> +
>> +	netif_start_queue(dev);
>> +
>> +	return 0;
>> +
>> +exit_irq_fail:
>> +	if (priv->is_peripherial)
>> +		destroy_workqueue(priv->wq);
>> +out_wq_fail:
>> +	close_candev(dev);
>> +exit_disable_clks:
>> +	m_can_clk_stop(priv);
>> +	return err;
>> +}
>> +
>>  static const struct net_device_ops m_can_netdev_ops = {
>>  	.ndo_open = m_can_open,
>>  	.ndo_stop = m_can_close,
>> @@ -1515,20 +1609,6 @@ static int register_m_can_dev(struct net_device *dev)
>>  	return register_candev(dev);
>>  }
>>  
>> -static void m_can_init_ram(struct m_can_priv *priv)
>> -{
>> -	int end, i, start;
>> -
>> -	/* initialize the entire Message RAM in use to avoid possible
>> -	 * ECC/parity checksum errors when reading an uninitialized buffer
>> -	 */
>> -	start = priv->mcfg[MRAM_SIDF].off;
>> -	end = priv->mcfg[MRAM_TXB].off +
>> -		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
>> -	for (i = start; i < end; i += 4)
>> -		writel(0x0, priv->mram_base + i);
>> -}
>> -
>>  static void m_can_of_parse_mram(struct m_can_priv *priv,
>>  				const u32 *mram_config_vals)
>>  {
>> @@ -1556,9 +1636,8 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
>>  	priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
>>  			(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
>>  
>> -	dev_dbg(priv->device,
>> -		"mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
>> -		priv->mram_base,
>> +	dev_dbg(priv->dev,
>> +		"sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
>>  		priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
>>  		priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
>>  		priv->mcfg[MRAM_RXF0].off, priv->mcfg[MRAM_RXF0].num,
>> @@ -1566,63 +1645,55 @@ static void m_can_of_parse_mram(struct m_can_priv *priv,
>>  		priv->mcfg[MRAM_RXB].off, priv->mcfg[MRAM_RXB].num,
>>  		priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
>>  		priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
>> -
>> -	m_can_init_ram(priv);
>>  }
>>  
>> -static int m_can_plat_probe(struct platform_device *pdev)
>> +void m_can_init_ram(struct m_can_priv *priv)
>>  {
>> -	struct net_device *dev;
>> -	struct m_can_priv *priv;
>> -	struct resource *res;
>> -	void __iomem *addr;
>> -	void __iomem *mram_addr;
>> -	struct clk *hclk, *cclk;
>> -	int irq, ret;
>> -	struct device_node *np;
>> -	u32 mram_config_vals[MRAM_CFG_LEN];
>> -	u32 tx_fifo_size;
>> -
>> -	np = pdev->dev.of_node;
>> +	int end, i, start;
>>  
>> -	hclk = devm_clk_get(&pdev->dev, "hclk");
>> -	cclk = devm_clk_get(&pdev->dev, "cclk");
>> +	/* initialize the entire Message RAM in use to avoid possible
>> +	 * ECC/parity checksum errors when reading an uninitialized buffer
>> +	 */
>> +	start = priv->mcfg[MRAM_SIDF].off;
>> +	end = priv->mcfg[MRAM_TXB].off +
>> +		priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
>>  
>> -	if (IS_ERR(hclk) || IS_ERR(cclk)) {
>> -		dev_err(&pdev->dev, "no clock found\n");
>> -		ret = -ENODEV;
>> -		goto failed_ret;
>> -	}
>> +	for (i = start; i < end; i += 4)
>> +		m_can_fifo_write_no_off(priv, i, 0x0);
>> +}
>> +EXPORT_SYMBOL_GPL(m_can_init_ram);
>>  
>> -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
>> -	addr = devm_ioremap_resource(&pdev->dev, res);
>> -	irq = platform_get_irq_byname(pdev, "int0");
>> +int m_can_class_get_clocks(struct m_can_priv *m_can_dev)
>> +{
>> +	int ret = 0;
>>  
>> -	if (IS_ERR(addr) || irq < 0) {
>> -		ret = -EINVAL;
>> -		goto failed_ret;
>> -	}
>> +	m_can_dev->hclk = devm_clk_get(m_can_dev->dev, "hclk");
>> +	m_can_dev->cclk = devm_clk_get(m_can_dev->dev, "cclk");
>>  
>> -	/* message ram could be shared */
>> -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
>> -	if (!res) {
>> +	if (IS_ERR(m_can_dev->cclk)) {
>> +		dev_err(m_can_dev->dev, "no clock found\n");
>>  		ret = -ENODEV;
>> -		goto failed_ret;
>>  	}
>>  
>> -	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> -	if (!mram_addr) {
>> -		ret = -ENOMEM;
>> -		goto failed_ret;
>> -	}
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
>>  
>> -	/* get message ram configuration */
>> -	ret = of_property_read_u32_array(np, "bosch,mram-cfg",
>> -					 mram_config_vals,
>> -					 sizeof(mram_config_vals) / 4);
>> +struct m_can_priv *m_can_class_allocate_dev(struct device *dev)
>> +{
>> +	struct m_can_priv *class_dev = NULL;
>> +	u32 mram_config_vals[MRAM_CFG_LEN];
>> +	struct net_device *net_dev;
>> +	u32 tx_fifo_size;
>> +	int ret;
>> +
>> +	ret = fwnode_property_read_u32_array(dev_fwnode(dev),
>> +					     "bosch,mram-cfg",
>> +					     mram_config_vals,
>> +					     sizeof(mram_config_vals) / 4);
>>  	if (ret) {
>> -		dev_err(&pdev->dev, "Could not get Message RAM configuration.");
>> -		goto failed_ret;
>> +		dev_err(dev, "Could not get Message RAM configuration.");
>> +		goto out;
>>  	}
>>  
>>  	/* Get TX FIFO size
>> @@ -1631,66 +1702,74 @@ static int m_can_plat_probe(struct platform_device *pdev)
>>  	tx_fifo_size = mram_config_vals[7];
>>  
>>  	/* allocate the m_can device */
>> -	dev = alloc_candev(sizeof(*priv), tx_fifo_size);
>> -	if (!dev) {
>> -		ret = -ENOMEM;
>> -		goto failed_ret;
>> +	net_dev = alloc_candev(sizeof(*class_dev), tx_fifo_size);
>> +	if (!net_dev) {
>> +		dev_err(dev, "Failed to allocate CAN device");
>> +		goto out;
>>  	}
>>  
>> -	priv = netdev_priv(dev);
>> -	dev->irq = irq;
>> -	priv->device = &pdev->dev;
>> -	priv->hclk = hclk;
>> -	priv->cclk = cclk;
>> -	priv->can.clock.freq = clk_get_rate(cclk);
>> -	priv->mram_base = mram_addr;
>> +	class_dev = netdev_priv(net_dev);
>> +	if (!class_dev) {
>> +		dev_err(dev, "Failed to init netdev private");
>> +		goto out;
>> +	}
>>  
>> -	platform_set_drvdata(pdev, dev);
>> -	SET_NETDEV_DEV(dev, &pdev->dev);
>> +	class_dev->net = net_dev;
>> +	class_dev->dev = dev;
>> +	SET_NETDEV_DEV(net_dev, dev);
>>  
>> -	/* Enable clocks. Necessary to read Core Release in order to determine
>> -	 * M_CAN version
>> -	 */
>> -	pm_runtime_enable(&pdev->dev);
>> -	ret = m_can_clk_start(priv);
>> -	if (ret)
>> -		goto pm_runtime_fail;
>> +	m_can_of_parse_mram(class_dev, mram_config_vals);
>> +out:
>> +	return class_dev;
>> +}
>> +EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
>> +
>> +int m_can_class_register(struct m_can_priv *m_can_dev)
>> +{
>> +	int ret;
>>  
>> -	ret = m_can_dev_setup(pdev, dev, addr);
>> +	if (m_can_dev->pm_clock_support) {
>> +		pm_runtime_enable(m_can_dev->dev);
>> +		ret = m_can_clk_start(m_can_dev);
>> +		if (ret)
>> +			goto pm_runtime_fail;
>> +	}
>> +
>> +	ret = m_can_dev_setup(m_can_dev);
>>  	if (ret)
>>  		goto clk_disable;
>>  
>> -	ret = register_m_can_dev(dev);
>> +	ret = register_m_can_dev(m_can_dev->net);
>>  	if (ret) {
>> -		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
>> -			KBUILD_MODNAME, ret);
>> +		dev_err(m_can_dev->dev, "registering %s failed (err=%d)\n",
>> +			m_can_dev->net->name, ret);
>>  		goto clk_disable;
>>  	}
>>  
>> -	m_can_of_parse_mram(priv, mram_config_vals);
>> -
>> -	devm_can_led_init(dev);
>> +	devm_can_led_init(m_can_dev->net);
>>  
>> -	of_can_transceiver(dev);
>> +	of_can_transceiver(m_can_dev->net);
>>  
>> -	dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
>> -		 KBUILD_MODNAME, dev->irq, priv->version);
>> +	dev_info(m_can_dev->dev, "%s device registered (irq=%d, version=%d)\n",
>> +		 KBUILD_MODNAME, m_can_dev->net->irq, m_can_dev->version);
>>  
>>  	/* Probe finished
>>  	 * Stop clocks. They will be reactivated once the M_CAN device is opened
>>  	 */
>>  clk_disable:
>> -	m_can_clk_stop(priv);
>> +	m_can_clk_stop(m_can_dev);
>>  pm_runtime_fail:
>>  	if (ret) {
>> -		pm_runtime_disable(&pdev->dev);
>> -		free_candev(dev);
>> +		if (m_can_dev->pm_clock_support)
>> +			pm_runtime_disable(m_can_dev->dev);
>> +		free_candev(m_can_dev->net);
>>  	}
>> -failed_ret:
>> +
>>  	return ret;
>>  }
>> +EXPORT_SYMBOL_GPL(m_can_class_register);
>>  
>> -static __maybe_unused int m_can_suspend(struct device *dev)
>> +int m_can_class_suspend(struct device *dev)
>>  {
>>  	struct net_device *ndev = dev_get_drvdata(dev);
>>  	struct m_can_priv *priv = netdev_priv(ndev);
>> @@ -1708,8 +1787,9 @@ static __maybe_unused int m_can_suspend(struct device *dev)
>>  
>>  	return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(m_can_class_suspend);
>>  
>> -static __maybe_unused int m_can_resume(struct device *dev)
>> +int m_can_class_resume(struct device *dev)
>>  {
>>  	struct net_device *ndev = dev_get_drvdata(dev);
>>  	struct m_can_priv *priv = netdev_priv(ndev);
>> @@ -1733,79 +1813,19 @@ static __maybe_unused int m_can_resume(struct device *dev)
>>  
>>  	return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(m_can_class_resume);
>>  
>> -static void unregister_m_can_dev(struct net_device *dev)
>> +void m_can_class_unregister(struct m_can_priv *m_can_dev)
>>  {
>> -	unregister_candev(dev);
>> -}
>> +	unregister_candev(m_can_dev->net);
>>  
>> -static int m_can_plat_remove(struct platform_device *pdev)
>> -{
>> -	struct net_device *dev = platform_get_drvdata(pdev);
>> +	m_can_clk_stop(m_can_dev);
>>  
>> -	unregister_m_can_dev(dev);
>> -
>> -	pm_runtime_disable(&pdev->dev);
>> -
>> -	platform_set_drvdata(pdev, NULL);
>> -
>> -	free_candev(dev);
>> -
>> -	return 0;
>> -}
>> -
>> -static int __maybe_unused m_can_runtime_suspend(struct device *dev)
>> -{
>> -	struct net_device *ndev = dev_get_drvdata(dev);
>> -	struct m_can_priv *priv = netdev_priv(ndev);
>> -
>> -	clk_disable_unprepare(priv->cclk);
>> -	clk_disable_unprepare(priv->hclk);
>> -
>> -	return 0;
>> -}
>> -
>> -static int __maybe_unused m_can_runtime_resume(struct device *dev)
>> -{
>> -	struct net_device *ndev = dev_get_drvdata(dev);
>> -	struct m_can_priv *priv = netdev_priv(ndev);
>> -	int err;
>> -
>> -	err = clk_prepare_enable(priv->hclk);
>> -	if (err)
>> -		return err;
>> -
>> -	err = clk_prepare_enable(priv->cclk);
>> -	if (err)
>> -		clk_disable_unprepare(priv->hclk);
>> -
>> -	return err;
>> +	free_candev(m_can_dev->net);
>>  }
>> -
>> -static const struct dev_pm_ops m_can_pmops = {
>> -	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
>> -			   m_can_runtime_resume, NULL)
>> -	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
>> -};
>> -
>> -static const struct of_device_id m_can_of_table[] = {
>> -	{ .compatible = "bosch,m_can", .data = NULL },
>> -	{ /* sentinel */ },
>> -};
>> -MODULE_DEVICE_TABLE(of, m_can_of_table);
>> -
>> -static struct platform_driver m_can_plat_driver = {
>> -	.driver = {
>> -		.name = KBUILD_MODNAME,
>> -		.of_match_table = m_can_of_table,
>> -		.pm     = &m_can_pmops,
>> -	},
>> -	.probe = m_can_plat_probe,
>> -	.remove = m_can_plat_remove,
>> -};
>> -
>> -module_platform_driver(m_can_plat_driver);
>> +EXPORT_SYMBOL_GPL(m_can_class_unregister);
>>  
>>  MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
>> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
>>  MODULE_LICENSE("GPL v2");
>>  MODULE_DESCRIPTION("CAN bus driver for Bosch M_CAN controller");
>> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
>> new file mode 100644
>> index 000000000000..70ab7bba063d
>> --- /dev/null
>> +++ b/drivers/net/can/m_can/m_can.h
>> @@ -0,0 +1,110 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* CAN bus driver for Bosch M_CAN controller
>> + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>> + */
>> +
>> +#ifndef _CAN_M_CAN_H_
>> +#define _CAN_M_CAN_H_
>> +
>> +#include <linux/can/core.h>
>> +#include <linux/can/led.h>
>> +#include <linux/completion.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/freezer.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.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/pm_runtime.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/can/dev.h>
>> +#include <linux/pinctrl/consumer.h>
>> +
>> +/* m_can lec values */
>> +enum m_can_lec_type {
>> +	LEC_NO_ERROR = 0,
>> +	LEC_STUFF_ERROR,
>> +	LEC_FORM_ERROR,
>> +	LEC_ACK_ERROR,
>> +	LEC_BIT1_ERROR,
>> +	LEC_BIT0_ERROR,
>> +	LEC_CRC_ERROR,
>> +	LEC_UNUSED,
>> +};
>> +
>> +enum m_can_mram_cfg {
>> +	MRAM_SIDF = 0,
>> +	MRAM_XIDF,
>> +	MRAM_RXF0,
>> +	MRAM_RXF1,
>> +	MRAM_RXB,
>> +	MRAM_TXE,
>> +	MRAM_TXB,
>> +	MRAM_CFG_NUM,
>> +};
>> +
>> +/* address offset and element number for each FIFO/Buffer in the Message RAM */
>> +struct mram_cfg {
>> +	u16 off;
>> +	u8  num;
>> +};
>> +
>> +struct m_can_priv;
>> +struct m_can_ops {
>> +	/* Device specific call backs */
>> +	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);
> 
> Why not just "clear_interrupt"... to be consistant with the names below.

I wanted to be clear in the M_CAN code that these are device interrupts and not M_CAN interrupts.

I can change it to clear_interrupt if you think it makes more sense.

> 
>> +	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
>> +	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
>> +	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
>> +	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
>> +			  int val);
>> +	int (*device_init)(struct m_can_priv *m_can_class);
>> +};
>> +
>> +struct m_can_priv {
>> +	struct can_priv can;
>> +	struct napi_struct napi;
>> +	struct net_device *net;
>> +	struct device *dev;
>> +	struct clk *hclk;
>> +	struct clk *cclk;
>> +
>> +	struct workqueue_struct *wq;
> 
> s/wq/tx_wq/ as already suggested above.
> 

Ack

>> +	struct work_struct tx_work;
>> +	struct sk_buff *tx_skb;
>> +
>> +	struct can_bittiming_const *bit_timing;
>> +	struct can_bittiming_const *data_timing;
>> +
>> +	struct m_can_ops *ops;
>> +
>> +	void *device_data;
>> +
>> +	int version;
>> +	int freq;
>> +	u32 irqstatus;
>> +
>> +	int pm_clock_support;
> 
> A "bool" would be more appropriate, I think.


I was abiding by this checkpatch warning I got on the is_peripherial.

CHECK: Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384
#94: FILE: drivers/net/can/m_can/m_can.h:94:
+	bool is_peripherial;


Dan

<snip>

> 
>> +	int is_peripherial;
>> +
>> +	struct mram_cfg mcfg[MRAM_CFG_NUM];
>> +};
>> +
>> +struct m_can_priv *m_can_class_allocate_dev(struct device *dev);
>> +int m_can_class_register(struct m_can_priv *m_can_dev);
>> +void m_can_class_unregister(struct m_can_priv *m_can_dev);
>> +int m_can_class_get_clocks(struct m_can_priv *m_can_dev);
>> +void m_can_init_ram(struct m_can_priv *priv);
>> +void m_can_config_endisable(struct m_can_priv *priv, bool enable);
>> +
>> +int m_can_class_suspend(struct device *dev);
>> +int m_can_class_resume(struct device *dev);
>> +#endif	/* _CAN_M_H_ */
>> diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
>> new file mode 100644
>> index 000000000000..92d989ce032a
>> --- /dev/null
>> +++ b/drivers/net/can/m_can/m_can_platform.c
>> @@ -0,0 +1,198 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// IOMapped CAN bus driver for Bosch M_CAN controller
>> +// Copyright (C) 2014 Freescale Semiconductor, Inc.
>> +//	Dong Aisheng <b29396@freescale.com>
>> +//
>> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#include <linux/platform_device.h>
>> +
>> +#include "m_can.h"
>> +
>> +struct m_can_plat_priv {
>> +	void __iomem *base;
>> +	void __iomem *mram_base;
>> +};
>> +
>> +static u32 iomap_read_reg(struct m_can_priv *m_can_class, int reg)
>> +{
>> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
>> +
>> +	return readl(priv->base + reg);
>> +}
>> +
>> +static u32 iomap_read_fifo(struct m_can_priv *m_can_class, int offset)
>> +{
>> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
>> +
>> +	return readl(priv->mram_base + offset);
>> +}
>> +
>> +static int iomap_write_reg(struct m_can_priv *m_can_class, int reg, int val)
>> +{
>> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
>> +
>> +	writel(val, priv->base + reg);
>> +
>> +	return 0;
>> +}
>> +
>> +static int iomap_write_fifo(struct m_can_priv *m_can_class, int offset, int val)
>> +{
>> +	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
>> +
>> +	writel(val, priv->mram_base + offset);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct m_can_ops m_can_plat_ops = {
>> +	.read_reg = iomap_read_reg,
>> +	.write_reg = iomap_write_reg,
>> +	.write_fifo = iomap_write_fifo,
>> +	.read_fifo = iomap_read_fifo,
>> +};
>> +
>> +static int m_can_plat_probe(struct platform_device *pdev)
>> +{
>> +	struct m_can_priv *mcan_class;
>> +	struct m_can_plat_priv *priv;
>> +	struct resource *res;
>> +	void __iomem *addr;
>> +	void __iomem *mram_addr;
>> +	int irq, ret = 0;
>> +
>> +	mcan_class = m_can_class_allocate_dev(&pdev->dev);
>> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	mcan_class->device_data = priv;
>> +
>> +	m_can_class_get_clocks(mcan_class);
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
>> +	addr = devm_ioremap_resource(&pdev->dev, res);
>> +	irq = platform_get_irq_byname(pdev, "int0");
>> +	if (IS_ERR(addr) || irq < 0) {
>> +		ret = -EINVAL;
>> +		goto failed_ret;
>> +	}
>> +
>> +	/* message ram could be shared */
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
>> +	if (!res) {
>> +		ret = -ENODEV;
>> +		goto failed_ret;
>> +	}
>> +
>> +	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +	if (!mram_addr) {
>> +		ret = -ENOMEM;
>> +		goto failed_ret;
>> +	}
>> +
>> +	priv->base = addr;
>> +	priv->mram_base = mram_addr;
>> +
>> +	mcan_class->net->irq = irq;
>> +	mcan_class->pm_clock_support = 1;
>> +	mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
>> +	mcan_class->dev = &pdev->dev;
>> +
>> +	mcan_class->ops = &m_can_plat_ops;
>> +
>> +	mcan_class->is_peripherial = false;
>> +
>> +	platform_set_drvdata(pdev, mcan_class->dev);
>> +
>> +	m_can_init_ram(mcan_class);
>> +
>> +	ret = m_can_class_register(mcan_class);
>> +
>> +failed_ret:
>> +	return ret;
>> +}
>> +
>> +static __maybe_unused int m_can_suspend(struct device *dev)
>> +{
>> +	return m_can_class_suspend(dev);
>> +}
>> +
>> +static __maybe_unused int m_can_resume(struct device *dev)
>> +{
>> +	return m_can_class_resume(dev);
>> +}
>> +
>> +static int m_can_plat_remove(struct platform_device *pdev)
>> +{
>> +	struct net_device *dev = platform_get_drvdata(pdev);
>> +	struct m_can_priv *mcan_class = netdev_priv(dev);
>> +
>> +	m_can_class_unregister(mcan_class);
>> +
>> +	platform_set_drvdata(pdev, NULL);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused m_can_runtime_suspend(struct device *dev)
>> +{
>> +	struct net_device *ndev = dev_get_drvdata(dev);
>> +	struct m_can_priv *mcan_class = netdev_priv(ndev);
>> +
>> +	m_can_class_suspend(dev);
>> +
>> +	clk_disable_unprepare(mcan_class->cclk);
>> +	clk_disable_unprepare(mcan_class->hclk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused m_can_runtime_resume(struct device *dev)
>> +{
>> +	struct net_device *ndev = dev_get_drvdata(dev);
>> +	struct m_can_priv *mcan_class = netdev_priv(ndev);
>> +	int err;
>> +
>> +	err = clk_prepare_enable(mcan_class->hclk);
>> +	if (err)
>> +		return err;
>> +
>> +	err = clk_prepare_enable(mcan_class->cclk);
>> +	if (err)
>> +		clk_disable_unprepare(mcan_class->hclk);
>> +
>> +	m_can_class_resume(dev);
>> +
>> +	return err;
>> +}
>> +
>> +static const struct dev_pm_ops m_can_pmops = {
>> +	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
>> +			   m_can_runtime_resume, NULL)
>> +	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
>> +};
>> +
>> +static const struct of_device_id m_can_of_table[] = {
>> +	{ .compatible = "bosch,m_can", .data = NULL },
>> +	{ /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, m_can_of_table);
>> +
>> +static struct platform_driver m_can_plat_driver = {
>> +	.driver = {
>> +		.name = KBUILD_MODNAME,
>> +		.of_match_table = m_can_of_table,
>> +		.pm     = &m_can_pmops,
>> +	},
>> +	.probe = m_can_plat_probe,
>> +	.remove = m_can_plat_remove,
>> +};
>> +
>> +module_platform_driver(m_can_plat_driver);
>> +
>> +MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
>> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");
>>
> 
> Wolfgang.
> 


-- 
------------------
Dan Murphy

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev
  2019-03-01 18:50 ` [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev Dan Murphy
@ 2019-03-04 17:31   ` Wolfgang Grandegger
  2019-03-04 18:14     ` Dan Murphy
  0 siblings, 1 reply; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 17:31 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Hello,

there are still "LTL" after renaming m_can_priv to m_can_classdev.

Am 01.03.19 um 19:50 schrieb Dan Murphy:
> Rename the common m_can_priv class structure to
> m_can_classdev as this is more descriptive.

If you change the struct name, I think you should also change the
variable name to something more meaning full e.g.:

  struct m_can_classdev *cdev

instead of

  struct m_can_classdev *priv


... snip ...

Wolfgang.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-04 17:22   ` Dan Murphy
@ 2019-03-04 18:13     ` Wolfgang Grandegger
  2019-03-04 19:12       ` Dan Murphy
  0 siblings, 1 reply; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 18:13 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel



Am 04.03.19 um 18:22 schrieb Dan Murphy:
> Wolfgang
> 
> On 3/4/19 10:56 AM, Wolfgang Grandegger wrote:
>> Hello Dan,
>>
>> the series already looks quite good. I still realized a few (minor)
>> issues while browsing the patch/code...
>>
> 
> Thanks for the review.  It is getting there.
> 
>> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>>> Create a m_can platform framework that peripherial
>>> devices can register to and use common code and register sets.
>>> The peripherial devices may provide read/write and configuration
>>> support of the IP.
>>>
>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>>> ---
>>>
>>> v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
>>> issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
>>> priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
>>> Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
>>> Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/
>>>
>>>  drivers/net/can/m_can/Kconfig          |  13 +-
>>>  drivers/net/can/m_can/Makefile         |   1 +
>>>  drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
>>>  drivers/net/can/m_can/m_can.h          | 110 ++++
>>>  drivers/net/can/m_can/m_can_platform.c | 198 +++++++
>>>  5 files changed, 681 insertions(+), 343 deletions(-)
>>>  create mode 100644 drivers/net/can/m_can/m_can.h
>>>  create mode 100644 drivers/net/can/m_can/m_can_platform.c
>>>
>>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>>> index 04f20dd39007..f7119fd72df4 100644
>>> --- a/drivers/net/can/m_can/Kconfig
>>> +++ b/drivers/net/can/m_can/Kconfig
>>> @@ -1,5 +1,14 @@
>>>  config CAN_M_CAN
>>> +	tristate "Bosch M_CAN support"
>>> +	---help---
>>> +	  Say Y here if you want support for Bosch M_CAN controller framework.
>>> +	  This is common support for devices that embed the Bosch M_CAN IP.
>>> +
>>> +config CAN_M_CAN_PLATFORM
>>> +	tristate "Bosch M_CAN support for io-mapped devices"
>>>  	depends on HAS_IOMEM
>>> -	tristate "Bosch M_CAN devices"
>>> +	depends on CAN_M_CAN
>>>  	---help---
>>> -	  Say Y here if you want to support for Bosch M_CAN controller.
>>> +	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>>> +	  This support is for devices that have the Bosch M_CAN controller
>>> +	  IP embedded into the device and the IP is IO Mapped to the processor.
>>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>>> index 8bbd7f24f5be..057bbcdb3c74 100644
>>> --- a/drivers/net/can/m_can/Makefile
>>> +++ b/drivers/net/can/m_can/Makefile
>>> @@ -3,3 +3,4 @@
>>>  #
>>>  
>>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>>> +obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>>> index 9b449400376b..b37d0886f9cb 100644
>>> --- a/drivers/net/can/m_can/m_can.c
>>> +++ b/drivers/net/can/m_can/m_can.c

...snip...

>>> @@ -1451,7 +1459,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>>>  			netif_stop_queue(dev);
>>>  			netdev_warn(dev,
>>>  				    "TX queue active although FIFO is full.");
>>> -			return NETDEV_TX_BUSY;
>>> +			return;
>>
>> m_can_start_xmit() doesn't return NETDEV_TX_BUSY but NETDEV_TX_OK and
>> the queue is stopped! Also the skb is not freed! The code states 
>> "/* This shouldn't happen */" but then it just prints a warning. Did
>> you see that message?
>>
> 
> No I have not seen this warning but I will re-check to be sure.

If we don't return NETDEV_TX_BUSY but NETDEV_TX_OK, we must handle it
differently.

...snip...

>>> +struct m_can_priv;
>>> +struct m_can_ops {
>>> +	/* Device specific call backs */
>>> +	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);
>>
>> Why not just "clear_interrupt"... to be consistant with the names below.
> 
> I wanted to be clear in the M_CAN code that these are device interrupts and not M_CAN interrupts.
> 
> I can change it to clear_interrupt if you think it makes more sense.

Well, like for "read_reg" etc, I think it's clear that it's a
device-specific function/ops:

	cdev->read_reg
	cdev->clear_interrupt

>>> +	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
>>> +	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
>>> +	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
>>> +	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
>>> +			  int val);
>>> +	int (*device_init)(struct m_can_priv *m_can_class);

And the same here:

	cdev->init

>>> +	int pm_clock_support;
>>
>> A "bool" would be more appropriate, I think.
> 
> 
> I was abiding by this checkpatch warning I got on the is_peripherial.
> 
> CHECK: Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384
> #94: FILE: drivers/net/can/m_can/m_can.h:94:
> +	bool is_peripherial;
> 

Ah, right! I was also surprised to get that warning. The kernel is full
of bool's, but well, we should make "checkpatch" happy (and Linus).

Wolfgang

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev
  2019-03-04 17:31   ` Wolfgang Grandegger
@ 2019-03-04 18:14     ` Dan Murphy
  2019-03-04 18:26       ` Wolfgang Grandegger
  0 siblings, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-04 18:14 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Wolfgang

On 3/4/19 11:31 AM, Wolfgang Grandegger wrote:
> Hello,
> 
> there are still "LTL" after renaming m_can_priv to m_can_classdev.
> 

Yes I know but some of those become unreadable if I break them apart.

But if you want me to break them I can do it.

For instance
	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;

will become

	struct m_can_plat_priv *priv =
				(struct m_can_plat_priv *)m_can_class->device_data;

> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>> Rename the common m_can_priv class structure to
>> m_can_classdev as this is more descriptive.
> 
> If you change the struct name, I think you should also change the
> variable name to something more meaning full e.g.:
> 
>   struct m_can_classdev *cdev
> 
> instead of
> 
>   struct m_can_classdev *priv
> 
> 

Ack.  This change will grow this patch as s/priv/cdev is used 250x

Dan

> ... snip ...
> 
> Wolfgang.
> 


-- 
------------------
Dan Murphy

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev
  2019-03-04 18:14     ` Dan Murphy
@ 2019-03-04 18:26       ` Wolfgang Grandegger
  0 siblings, 0 replies; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 18:26 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel



Am 04.03.19 um 19:14 schrieb Dan Murphy:
> Wolfgang
> 
> On 3/4/19 11:31 AM, Wolfgang Grandegger wrote:
>> Hello,
>>
>> there are still "LTL" after renaming m_can_priv to m_can_classdev.
>>
> 
> Yes I know but some of those become unreadable if I break them apart.
> 
> But if you want me to break them I can do it.
> 
> For instance
> 	struct m_can_plat_priv *priv = (struct m_can_plat_priv *)m_can_class->device_data;
> 
> will become
> 
> 	struct m_can_plat_priv *priv =
> 				(struct m_can_plat_priv *)m_can_class->device_data;

Perfect! See also my comment below.

> 
>> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>>> Rename the common m_can_priv class structure to
>>> m_can_classdev as this is more descriptive.
>>
>> If you change the struct name, I think you should also change the
>> variable name to something more meaning full e.g.:
>>
>>   struct m_can_classdev *cdev
>>
>> instead of
>>
>>   struct m_can_classdev *priv
>>
>>
> 
> Ack.  This change will grow this patch as s/priv/cdev is used 250x

That's the price for changing the name. As we have it in a separate
patch, it should be OK.

Ideally, also m_can_platform.c and tcan4x5x.c should then use that name
for "struct m_can_classdev".

Wolfgang.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel
  2019-03-01 18:50 ` [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel Dan Murphy
@ 2019-03-04 18:29   ` Wolfgang Grandegger
  2019-03-04 19:07     ` Dan Murphy
  0 siblings, 1 reply; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 18:29 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Hello Dan,

Am 01.03.19 um 19:50 schrieb Dan Murphy:
> Add the TCAN4x5x SPI CAN driver.  This device uses the
> Bosch MCAN IP core along with a SPI interface map.
> Register to the MCAN common core code to manage the MCAN IP.
> 
> This device has a special method to indicate a write/read
> operation on the data payload.
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> 
> v6 - Updated Kconfig help and added REGMAP_SPI dependency, fixed coding style issues,
> removed setting GPIO high in wake routine, change -EIO to return ret in init and
> setup functions - https://lore.kernel.org/patchwork/patch/1042443/
> 
>  drivers/net/can/m_can/Kconfig    |   9 +
>  drivers/net/can/m_can/Makefile   |   1 +
>  drivers/net/can/m_can/tcan4x5x.c | 529 +++++++++++++++++++++++++++++++
>  3 files changed, 539 insertions(+)
>  create mode 100644 drivers/net/can/m_can/tcan4x5x.c
> 
> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
> index f7119fd72df4..cf4f32765efa 100644
> --- a/drivers/net/can/m_can/Kconfig
> +++ b/drivers/net/can/m_can/Kconfig
> @@ -12,3 +12,12 @@ config CAN_M_CAN_PLATFORM
>  	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>  	  This support is for devices that have the Bosch M_CAN controller
>  	  IP embedded into the device and the IP is IO Mapped to the processor.
> +
> +config CAN_M_CAN_TCAN4X5X
> +	depends on CAN_M_CAN
> +	depends on REGMAP_SPI
> +	tristate "TCAN4X5X M_CAN device"
> +	---help---
> +	  Say Y here if you want support for Texas Instruments TCAN4x5x
> +	  M_CAN controller.  This device is a peripherial device that uses the
> +	  SPI bus for communication.
> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
> index 057bbcdb3c74..e77f0eccff97 100644
> --- a/drivers/net/can/m_can/Makefile
> +++ b/drivers/net/can/m_can/Makefile
> @@ -4,3 +4,4 @@
>  
>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>  obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
> +obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
> diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
> new file mode 100644
> index 000000000000..a91b976bb522
> --- /dev/null
> +++ b/drivers/net/can/m_can/tcan4x5x.c
> @@ -0,0 +1,529 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +
> +#include <linux/regulator/consumer.h>
> +#include <linux/gpio/consumer.h>
> +
> +#include "m_can.h"
> +
> +#define DEVICE_NAME "tcan4x5x"
> +#define TCAN4X5X_EXT_CLK_DEF	40000000
> +
> +#define TCAN4X5X_DEV_ID0	0x00
> +#define TCAN4X5X_DEV_ID1	0x04
> +#define TCAN4X5X_REV		0x08
> +#define TCAN4X5X_STATUS		0x0C
> +#define TCAN4X5X_ERROR_STATUS	0x10
> +#define TCAN4X5X_CONTROL	0x14
> +
> +#define TCAN4X5X_CONFIG		0x800
> +#define TCAN4X5X_TS_PRESCALE	0x804
> +#define TCAN4X5X_TEST_REG	0x808
> +#define TCAN4X5X_INT_FLAGS	0x820
> +#define TCAN4X5X_MCAN_INT_REG	0x824
> +#define TCAN4X5X_INT_EN		0x830
> +
> +/* Interrupt bits */
> +#define TCAN4X5X_CANBUSTERMOPEN_INT_EN	BIT(30)
> +#define TCAN4X5X_CANHCANL_INT_EN	BIT(29)
> +#define TCAN4X5X_CANHBAT_INT_EN		BIT(28)
> +#define TCAN4X5X_CANLGND_INT_EN		BIT(27)
> +#define TCAN4X5X_CANBUSOPEN_INT_EN	BIT(26)
> +#define TCAN4X5X_CANBUSGND_INT_EN	BIT(25)
> +#define TCAN4X5X_CANBUSBAT_INT_EN	BIT(24)
> +#define TCAN4X5X_UVSUP_INT_EN		BIT(22)
> +#define TCAN4X5X_UVIO_INT_EN		BIT(21)
> +#define TCAN4X5X_TSD_INT_EN		BIT(19)
> +#define TCAN4X5X_ECCERR_INT_EN		BIT(16)
> +#define TCAN4X5X_CANINT_INT_EN		BIT(15)
> +#define TCAN4X5X_LWU_INT_EN		BIT(14)
> +#define TCAN4X5X_CANSLNT_INT_EN		BIT(10)
> +#define TCAN4X5X_CANDOM_INT_EN		BIT(8)
> +#define TCAN4X5X_CANBUS_ERR_INT_EN	BIT(5)
> +#define TCAN4X5X_BUS_FAULT		BIT(4)
> +#define TCAN4X5X_MCAN_INT		BIT(1)
> +#define TCAN4X5X_ENABLE_TCAN_INT	(TCAN4X5X_MCAN_INT | \
> +					TCAN4X5X_BUS_FAULT | \
> +					TCAN4X5X_CANBUS_ERR_INT_EN | \
> +					TCAN4X5X_CANINT_INT_EN)
> +
> +/* MCAN Interrupt bits */
> +#define TCAN4X5X_MCAN_IR_ARA		BIT(29)
> +#define TCAN4X5X_MCAN_IR_PED		BIT(28)
> +#define TCAN4X5X_MCAN_IR_PEA		BIT(27)
> +#define TCAN4X5X_MCAN_IR_WD		BIT(26)
> +#define TCAN4X5X_MCAN_IR_BO		BIT(25)
> +#define TCAN4X5X_MCAN_IR_EW		BIT(24)
> +#define TCAN4X5X_MCAN_IR_EP		BIT(23)
> +#define TCAN4X5X_MCAN_IR_ELO		BIT(22)
> +#define TCAN4X5X_MCAN_IR_BEU		BIT(21)
> +#define TCAN4X5X_MCAN_IR_BEC		BIT(20)
> +#define TCAN4X5X_MCAN_IR_DRX		BIT(19)
> +#define TCAN4X5X_MCAN_IR_TOO		BIT(18)
> +#define TCAN4X5X_MCAN_IR_MRAF		BIT(17)
> +#define TCAN4X5X_MCAN_IR_TSW		BIT(16)
> +#define TCAN4X5X_MCAN_IR_TEFL		BIT(15)
> +#define TCAN4X5X_MCAN_IR_TEFF		BIT(14)
> +#define TCAN4X5X_MCAN_IR_TEFW		BIT(13)
> +#define TCAN4X5X_MCAN_IR_TEFN		BIT(12)
> +#define TCAN4X5X_MCAN_IR_TFE		BIT(11)
> +#define TCAN4X5X_MCAN_IR_TCF		BIT(10)
> +#define TCAN4X5X_MCAN_IR_TC		BIT(9)
> +#define TCAN4X5X_MCAN_IR_HPM		BIT(8)
> +#define TCAN4X5X_MCAN_IR_RF1L		BIT(7)
> +#define TCAN4X5X_MCAN_IR_RF1F		BIT(6)
> +#define TCAN4X5X_MCAN_IR_RF1W		BIT(5)
> +#define TCAN4X5X_MCAN_IR_RF1N		BIT(4)
> +#define TCAN4X5X_MCAN_IR_RF0L		BIT(3)
> +#define TCAN4X5X_MCAN_IR_RF0F		BIT(2)
> +#define TCAN4X5X_MCAN_IR_RF0W		BIT(1)
> +#define TCAN4X5X_MCAN_IR_RF0N		BIT(0)
> +#define TCAN4X5X_ENABLE_MCAN_INT	(TCAN4X5X_MCAN_IR_TC | \
> +					TCAN4X5X_MCAN_IR_RF0N | \
> +					TCAN4X5X_MCAN_IR_RF1N | \
> +					TCAN4X5X_MCAN_IR_RF0F | \
> +					TCAN4X5X_MCAN_IR_RF1F)
> +#define TCAN4X5X_MRAM_START	0x8000
> +#define TCAN4X5X_MCAN_OFFSET	0x1000
> +#define TCAN4X5X_MAX_REGISTER	0x8fff
> +
> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
> +
> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
> +
> +#define TCAN4X5X_MODE_SEL_MASK		(BIT(7) | BIT(6))
> +#define TCAN4X5X_MODE_SLEEP		0x00
> +#define TCAN4X5X_MODE_STANDBY		BIT(6)
> +#define TCAN4X5X_MODE_NORMAL		BIT(7)
> +
> +#define TCAN4X5X_SW_RESET	BIT(2)
> +
> +#define TCAN4X5X_MCAN_CONFIGURED	BIT(5)
> +#define TCAN4X5X_WATCHDOG_EN		BIT(3)
> +#define TCAN4X5X_WD_60_MS_TIMER		0
> +#define TCAN4X5X_WD_600_MS_TIMER	BIT(28)
> +#define TCAN4X5X_WD_3_S_TIMER		BIT(29)
> +#define TCAN4X5X_WD_6_S_TIMER		(BIT(28) | BIT(29))
> +
> +struct tcan4x5x_priv {
> +	struct regmap *regmap;
> +	struct spi_device *spi;
> +	struct mutex tcan4x5x_lock; /* SPI device lock */
> +
> +	struct m_can_classdev *mcan_dev;
> +
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *interrupt_gpio;
> +	struct gpio_desc *device_wake_gpio;
> +	struct gpio_desc *device_state_gpio;
> +	struct regulator *power;
> +
> +	/* Register based ip */
> +	int mram_start;
> +	int reg_offset;
> +};
> +
> +static struct can_bittiming_const tcan4x5x_bittiming_const = {
> +	.name = DEVICE_NAME,
> +	.tseg1_min = 2,
> +	.tseg1_max = 31,
> +	.tseg2_min = 2,
> +	.tseg2_max = 16,
> +	.sjw_max = 16,
> +	.brp_min = 1,
> +	.brp_max = 32,
> +	.brp_inc = 1,
> +};
> +
> +static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
> +	.name = DEVICE_NAME,
> +	.tseg1_min = 1,
> +	.tseg1_max = 32,
> +	.tseg2_min = 1,
> +	.tseg2_max = 16,
> +	.sjw_max = 16,
> +	.brp_min = 1,
> +	.brp_max = 32,
> +	.brp_inc = 1,
> +};
> +
> +static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
> +{
> +	int wake_state = 0;
> +
> +	if (priv->device_state_gpio)
> +		wake_state = gpiod_get_value(priv->device_state_gpio);
> +
> +	if (priv->device_wake_gpio && wake_state) {
> +		gpiod_set_value(priv->device_wake_gpio, 0);
> +		usleep_range(5, 50);
> +		gpiod_set_value(priv->device_wake_gpio, 1);
> +	}
> +}
> +
> +static int regmap_spi_gather_write(void *context, const void *reg,
> +				   size_t reg_len, const void *val,
> +				   size_t val_len)
> +{
> +	struct device *dev = context;
> +	struct spi_device *spi = to_spi_device(dev);
> +	struct spi_message m;
> +	u32 addr;
> +	struct spi_transfer t[2] = {
> +		{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
> +		{ .tx_buf = val, .len = val_len, },
> +	};
> +
> +	addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t[0], &m);
> +	spi_message_add_tail(&t[1], &m);
> +
> +	return spi_sync(spi, &m);
> +}
> +
> +static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
> +{
> +	u16 *reg = (u16 *)(data);
> +	const u32 *val = data + 4;
> +
> +	return regmap_spi_gather_write(context, reg, 4, val, count);
> +}
> +
> +static int regmap_spi_async_write(void *context,
> +				  const void *reg, size_t reg_len,
> +				  const void *val, size_t val_len,
> +				  struct regmap_async *a)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static struct regmap_async *regmap_spi_async_alloc(void)
> +{
> +	return NULL;
> +}
> +
> +static int tcan4x5x_regmap_read(void *context,
> +				const void *reg, size_t reg_size,
> +				void *val, size_t val_size)
> +{
> +	struct device *dev = context;
> +	struct spi_device *spi = to_spi_device(dev);
> +
> +	u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
> +
> +	return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
> +}
> +
> +static struct regmap_bus tcan4x5x_bus = {
> +	.write = tcan4x5x_regmap_write,
> +	.gather_write = regmap_spi_gather_write,
> +	.async_write = regmap_spi_async_write,
> +	.async_alloc = regmap_spi_async_alloc,
> +	.read = tcan4x5x_regmap_read,
> +	.read_flag_mask = 0x00,
> +	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
> +	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
> +};
> +
> +static u32 tcan4x5x_read_reg(struct m_can_classdev *m_can_class, int reg)
> +{
> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
> +	u32 val;
> +
> +	tcan4x5x_check_wake(priv);
> +
> +	regmap_read(priv->regmap, priv->reg_offset + reg, &val);
> +
> +	return val;
> +}
> +
> +static u32 tcan4x5x_read_fifo(struct m_can_classdev *m_can_class,
> +			      int addr_offset)
> +{
> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
> +	u32 val;
> +
> +	tcan4x5x_check_wake(priv);
> +
> +	regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
> +
> +	return val;
> +}
> +
> +static int tcan4x5x_write_reg(struct m_can_classdev *m_can_class,
> +			      int reg, int val)
> +{
> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
> +
> +	tcan4x5x_check_wake(priv);
> +
> +	return regmap_write(priv->regmap, priv->reg_offset + reg, val);
> +}
> +
> +static int tcan4x5x_write_fifo(struct m_can_classdev *m_can_class,
> +			       int addr_offset, int val)
> +{
> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
> +
> +	tcan4x5x_check_wake(priv);
> +
> +	return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
> +}
> +
> +static int tcan4x5x_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 tcan4x5x_write_tcan_reg(struct m_can_classdev *m_can_class,
> +				   int reg, int val)
> +{
> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
> +
> +	tcan4x5x_check_wake(priv);
> +
> +	return regmap_write(priv->regmap, reg, val);
> +}
> +
> +static int tcan4x5x_clear_interrupts(struct m_can_classdev *class_dev)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
> +	int ret;
> +
> +	tcan4x5x_check_wake(tcan4x5x);
> +
> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_STATUS,
> +				      TCAN4X5X_CLEAR_ALL_INT);
> +	if (ret)
> +		return ret;
> +
> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_MCAN_INT_REG,
> +				      TCAN4X5X_ENABLE_MCAN_INT);
> +	if (ret)
> +		return ret;
> +
> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_FLAGS,
> +				      TCAN4X5X_CLEAR_ALL_INT);
> +	if (ret)
> +		return ret;
> +
> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_ERROR_STATUS,
> +				      TCAN4X5X_CLEAR_ALL_INT);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}

Hm, handling the interrupt seems to be a quite heavy operation. How does
the device and system digest small messages (dlc=0) coming in at a very
high rate?

Any chance to optimize accesses be reading more than on word?

Wolfgang.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel
  2019-03-04 18:29   ` Wolfgang Grandegger
@ 2019-03-04 19:07     ` Dan Murphy
  2019-03-04 19:33       ` Wolfgang Grandegger
  0 siblings, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-04 19:07 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Wolfgang

On 3/4/19 12:29 PM, Wolfgang Grandegger wrote:
> Hello Dan,
> 
> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>> Add the TCAN4x5x SPI CAN driver.  This device uses the
>> Bosch MCAN IP core along with a SPI interface map.
>> Register to the MCAN common core code to manage the MCAN IP.
>>
>> This device has a special method to indicate a write/read
>> operation on the data payload.
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ---
>>
>> v6 - Updated Kconfig help and added REGMAP_SPI dependency, fixed coding style issues,
>> removed setting GPIO high in wake routine, change -EIO to return ret in init and
>> setup functions - https://lore.kernel.org/patchwork/patch/1042443/
>>
>>  drivers/net/can/m_can/Kconfig    |   9 +
>>  drivers/net/can/m_can/Makefile   |   1 +
>>  drivers/net/can/m_can/tcan4x5x.c | 529 +++++++++++++++++++++++++++++++
>>  3 files changed, 539 insertions(+)
>>  create mode 100644 drivers/net/can/m_can/tcan4x5x.c
>>
>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>> index f7119fd72df4..cf4f32765efa 100644
>> --- a/drivers/net/can/m_can/Kconfig
>> +++ b/drivers/net/can/m_can/Kconfig
>> @@ -12,3 +12,12 @@ config CAN_M_CAN_PLATFORM
>>  	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>>  	  This support is for devices that have the Bosch M_CAN controller
>>  	  IP embedded into the device and the IP is IO Mapped to the processor.
>> +
>> +config CAN_M_CAN_TCAN4X5X
>> +	depends on CAN_M_CAN
>> +	depends on REGMAP_SPI
>> +	tristate "TCAN4X5X M_CAN device"
>> +	---help---
>> +	  Say Y here if you want support for Texas Instruments TCAN4x5x
>> +	  M_CAN controller.  This device is a peripherial device that uses the
>> +	  SPI bus for communication.
>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>> index 057bbcdb3c74..e77f0eccff97 100644
>> --- a/drivers/net/can/m_can/Makefile
>> +++ b/drivers/net/can/m_can/Makefile
>> @@ -4,3 +4,4 @@
>>  
>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>>  obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>> +obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
>> diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
>> new file mode 100644
>> index 000000000000..a91b976bb522
>> --- /dev/null
>> +++ b/drivers/net/can/m_can/tcan4x5x.c
>> @@ -0,0 +1,529 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
>> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#include <linux/regmap.h>
>> +#include <linux/spi/spi.h>
>> +
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/gpio/consumer.h>
>> +
>> +#include "m_can.h"
>> +
>> +#define DEVICE_NAME "tcan4x5x"
>> +#define TCAN4X5X_EXT_CLK_DEF	40000000
>> +
>> +#define TCAN4X5X_DEV_ID0	0x00
>> +#define TCAN4X5X_DEV_ID1	0x04
>> +#define TCAN4X5X_REV		0x08
>> +#define TCAN4X5X_STATUS		0x0C
>> +#define TCAN4X5X_ERROR_STATUS	0x10
>> +#define TCAN4X5X_CONTROL	0x14
>> +
>> +#define TCAN4X5X_CONFIG		0x800
>> +#define TCAN4X5X_TS_PRESCALE	0x804
>> +#define TCAN4X5X_TEST_REG	0x808
>> +#define TCAN4X5X_INT_FLAGS	0x820
>> +#define TCAN4X5X_MCAN_INT_REG	0x824
>> +#define TCAN4X5X_INT_EN		0x830
>> +
>> +/* Interrupt bits */
>> +#define TCAN4X5X_CANBUSTERMOPEN_INT_EN	BIT(30)
>> +#define TCAN4X5X_CANHCANL_INT_EN	BIT(29)
>> +#define TCAN4X5X_CANHBAT_INT_EN		BIT(28)
>> +#define TCAN4X5X_CANLGND_INT_EN		BIT(27)
>> +#define TCAN4X5X_CANBUSOPEN_INT_EN	BIT(26)
>> +#define TCAN4X5X_CANBUSGND_INT_EN	BIT(25)
>> +#define TCAN4X5X_CANBUSBAT_INT_EN	BIT(24)
>> +#define TCAN4X5X_UVSUP_INT_EN		BIT(22)
>> +#define TCAN4X5X_UVIO_INT_EN		BIT(21)
>> +#define TCAN4X5X_TSD_INT_EN		BIT(19)
>> +#define TCAN4X5X_ECCERR_INT_EN		BIT(16)
>> +#define TCAN4X5X_CANINT_INT_EN		BIT(15)
>> +#define TCAN4X5X_LWU_INT_EN		BIT(14)
>> +#define TCAN4X5X_CANSLNT_INT_EN		BIT(10)
>> +#define TCAN4X5X_CANDOM_INT_EN		BIT(8)
>> +#define TCAN4X5X_CANBUS_ERR_INT_EN	BIT(5)
>> +#define TCAN4X5X_BUS_FAULT		BIT(4)
>> +#define TCAN4X5X_MCAN_INT		BIT(1)
>> +#define TCAN4X5X_ENABLE_TCAN_INT	(TCAN4X5X_MCAN_INT | \
>> +					TCAN4X5X_BUS_FAULT | \
>> +					TCAN4X5X_CANBUS_ERR_INT_EN | \
>> +					TCAN4X5X_CANINT_INT_EN)
>> +
>> +/* MCAN Interrupt bits */
>> +#define TCAN4X5X_MCAN_IR_ARA		BIT(29)
>> +#define TCAN4X5X_MCAN_IR_PED		BIT(28)
>> +#define TCAN4X5X_MCAN_IR_PEA		BIT(27)
>> +#define TCAN4X5X_MCAN_IR_WD		BIT(26)
>> +#define TCAN4X5X_MCAN_IR_BO		BIT(25)
>> +#define TCAN4X5X_MCAN_IR_EW		BIT(24)
>> +#define TCAN4X5X_MCAN_IR_EP		BIT(23)
>> +#define TCAN4X5X_MCAN_IR_ELO		BIT(22)
>> +#define TCAN4X5X_MCAN_IR_BEU		BIT(21)
>> +#define TCAN4X5X_MCAN_IR_BEC		BIT(20)
>> +#define TCAN4X5X_MCAN_IR_DRX		BIT(19)
>> +#define TCAN4X5X_MCAN_IR_TOO		BIT(18)
>> +#define TCAN4X5X_MCAN_IR_MRAF		BIT(17)
>> +#define TCAN4X5X_MCAN_IR_TSW		BIT(16)
>> +#define TCAN4X5X_MCAN_IR_TEFL		BIT(15)
>> +#define TCAN4X5X_MCAN_IR_TEFF		BIT(14)
>> +#define TCAN4X5X_MCAN_IR_TEFW		BIT(13)
>> +#define TCAN4X5X_MCAN_IR_TEFN		BIT(12)
>> +#define TCAN4X5X_MCAN_IR_TFE		BIT(11)
>> +#define TCAN4X5X_MCAN_IR_TCF		BIT(10)
>> +#define TCAN4X5X_MCAN_IR_TC		BIT(9)
>> +#define TCAN4X5X_MCAN_IR_HPM		BIT(8)
>> +#define TCAN4X5X_MCAN_IR_RF1L		BIT(7)
>> +#define TCAN4X5X_MCAN_IR_RF1F		BIT(6)
>> +#define TCAN4X5X_MCAN_IR_RF1W		BIT(5)
>> +#define TCAN4X5X_MCAN_IR_RF1N		BIT(4)
>> +#define TCAN4X5X_MCAN_IR_RF0L		BIT(3)
>> +#define TCAN4X5X_MCAN_IR_RF0F		BIT(2)
>> +#define TCAN4X5X_MCAN_IR_RF0W		BIT(1)
>> +#define TCAN4X5X_MCAN_IR_RF0N		BIT(0)
>> +#define TCAN4X5X_ENABLE_MCAN_INT	(TCAN4X5X_MCAN_IR_TC | \
>> +					TCAN4X5X_MCAN_IR_RF0N | \
>> +					TCAN4X5X_MCAN_IR_RF1N | \
>> +					TCAN4X5X_MCAN_IR_RF0F | \
>> +					TCAN4X5X_MCAN_IR_RF1F)
>> +#define TCAN4X5X_MRAM_START	0x8000
>> +#define TCAN4X5X_MCAN_OFFSET	0x1000
>> +#define TCAN4X5X_MAX_REGISTER	0x8fff
>> +
>> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
>> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
>> +
>> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
>> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
>> +
>> +#define TCAN4X5X_MODE_SEL_MASK		(BIT(7) | BIT(6))
>> +#define TCAN4X5X_MODE_SLEEP		0x00
>> +#define TCAN4X5X_MODE_STANDBY		BIT(6)
>> +#define TCAN4X5X_MODE_NORMAL		BIT(7)
>> +
>> +#define TCAN4X5X_SW_RESET	BIT(2)
>> +
>> +#define TCAN4X5X_MCAN_CONFIGURED	BIT(5)
>> +#define TCAN4X5X_WATCHDOG_EN		BIT(3)
>> +#define TCAN4X5X_WD_60_MS_TIMER		0
>> +#define TCAN4X5X_WD_600_MS_TIMER	BIT(28)
>> +#define TCAN4X5X_WD_3_S_TIMER		BIT(29)
>> +#define TCAN4X5X_WD_6_S_TIMER		(BIT(28) | BIT(29))
>> +
>> +struct tcan4x5x_priv {
>> +	struct regmap *regmap;
>> +	struct spi_device *spi;
>> +	struct mutex tcan4x5x_lock; /* SPI device lock */
>> +
>> +	struct m_can_classdev *mcan_dev;
>> +
>> +	struct gpio_desc *reset_gpio;
>> +	struct gpio_desc *interrupt_gpio;
>> +	struct gpio_desc *device_wake_gpio;
>> +	struct gpio_desc *device_state_gpio;
>> +	struct regulator *power;
>> +
>> +	/* Register based ip */
>> +	int mram_start;
>> +	int reg_offset;
>> +};
>> +
>> +static struct can_bittiming_const tcan4x5x_bittiming_const = {
>> +	.name = DEVICE_NAME,
>> +	.tseg1_min = 2,
>> +	.tseg1_max = 31,
>> +	.tseg2_min = 2,
>> +	.tseg2_max = 16,
>> +	.sjw_max = 16,
>> +	.brp_min = 1,
>> +	.brp_max = 32,
>> +	.brp_inc = 1,
>> +};
>> +
>> +static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
>> +	.name = DEVICE_NAME,
>> +	.tseg1_min = 1,
>> +	.tseg1_max = 32,
>> +	.tseg2_min = 1,
>> +	.tseg2_max = 16,
>> +	.sjw_max = 16,
>> +	.brp_min = 1,
>> +	.brp_max = 32,
>> +	.brp_inc = 1,
>> +};
>> +
>> +static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
>> +{
>> +	int wake_state = 0;
>> +
>> +	if (priv->device_state_gpio)
>> +		wake_state = gpiod_get_value(priv->device_state_gpio);
>> +
>> +	if (priv->device_wake_gpio && wake_state) {
>> +		gpiod_set_value(priv->device_wake_gpio, 0);
>> +		usleep_range(5, 50);
>> +		gpiod_set_value(priv->device_wake_gpio, 1);
>> +	}
>> +}
>> +
>> +static int regmap_spi_gather_write(void *context, const void *reg,
>> +				   size_t reg_len, const void *val,
>> +				   size_t val_len)
>> +{
>> +	struct device *dev = context;
>> +	struct spi_device *spi = to_spi_device(dev);
>> +	struct spi_message m;
>> +	u32 addr;
>> +	struct spi_transfer t[2] = {
>> +		{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
>> +		{ .tx_buf = val, .len = val_len, },
>> +	};
>> +
>> +	addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
>> +
>> +	spi_message_init(&m);
>> +	spi_message_add_tail(&t[0], &m);
>> +	spi_message_add_tail(&t[1], &m);
>> +
>> +	return spi_sync(spi, &m);
>> +}
>> +
>> +static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
>> +{
>> +	u16 *reg = (u16 *)(data);
>> +	const u32 *val = data + 4;
>> +
>> +	return regmap_spi_gather_write(context, reg, 4, val, count);
>> +}
>> +
>> +static int regmap_spi_async_write(void *context,
>> +				  const void *reg, size_t reg_len,
>> +				  const void *val, size_t val_len,
>> +				  struct regmap_async *a)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static struct regmap_async *regmap_spi_async_alloc(void)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static int tcan4x5x_regmap_read(void *context,
>> +				const void *reg, size_t reg_size,
>> +				void *val, size_t val_size)
>> +{
>> +	struct device *dev = context;
>> +	struct spi_device *spi = to_spi_device(dev);
>> +
>> +	u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
>> +
>> +	return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
>> +}
>> +
>> +static struct regmap_bus tcan4x5x_bus = {
>> +	.write = tcan4x5x_regmap_write,
>> +	.gather_write = regmap_spi_gather_write,
>> +	.async_write = regmap_spi_async_write,
>> +	.async_alloc = regmap_spi_async_alloc,
>> +	.read = tcan4x5x_regmap_read,
>> +	.read_flag_mask = 0x00,
>> +	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
>> +	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
>> +};
>> +
>> +static u32 tcan4x5x_read_reg(struct m_can_classdev *m_can_class, int reg)
>> +{
>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>> +	u32 val;
>> +
>> +	tcan4x5x_check_wake(priv);
>> +
>> +	regmap_read(priv->regmap, priv->reg_offset + reg, &val);
>> +
>> +	return val;
>> +}
>> +
>> +static u32 tcan4x5x_read_fifo(struct m_can_classdev *m_can_class,
>> +			      int addr_offset)
>> +{
>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>> +	u32 val;
>> +
>> +	tcan4x5x_check_wake(priv);
>> +
>> +	regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
>> +
>> +	return val;
>> +}
>> +
>> +static int tcan4x5x_write_reg(struct m_can_classdev *m_can_class,
>> +			      int reg, int val)
>> +{
>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>> +
>> +	tcan4x5x_check_wake(priv);
>> +
>> +	return regmap_write(priv->regmap, priv->reg_offset + reg, val);
>> +}
>> +
>> +static int tcan4x5x_write_fifo(struct m_can_classdev *m_can_class,
>> +			       int addr_offset, int val)
>> +{
>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>> +
>> +	tcan4x5x_check_wake(priv);
>> +
>> +	return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
>> +}
>> +
>> +static int tcan4x5x_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 tcan4x5x_write_tcan_reg(struct m_can_classdev *m_can_class,
>> +				   int reg, int val)
>> +{
>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>> +
>> +	tcan4x5x_check_wake(priv);
>> +
>> +	return regmap_write(priv->regmap, reg, val);
>> +}
>> +
>> +static int tcan4x5x_clear_interrupts(struct m_can_classdev *class_dev)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
>> +	int ret;
>> +
>> +	tcan4x5x_check_wake(tcan4x5x);
>> +
>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_STATUS,
>> +				      TCAN4X5X_CLEAR_ALL_INT);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_MCAN_INT_REG,
>> +				      TCAN4X5X_ENABLE_MCAN_INT);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_FLAGS,
>> +				      TCAN4X5X_CLEAR_ALL_INT);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_ERROR_STATUS,
>> +				      TCAN4X5X_CLEAR_ALL_INT);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return ret;
>> +}
> 
> Hm, handling the interrupt seems to be a quite heavy operation. How does
> the device and system digest small messages (dlc=0) coming in at a very
> high rate?
> 
> Any chance to optimize accesses be reading more than on word?
> 

The device does support writing consecutive registers.  The MCAN_INT and INT_FLAGS are consecutive
and the STATUS and ERROR_STATUS are consecutive.  So we would still need 2 writes so not sure that this will
optimize enough for your concern.

Also the m_can code is missing bulk writes which I did not want to introduce at this time.
My concern was more functionality and maintaining current io mapped functionality.

Bulk writes for peripheral devices is preferred.

Dan

> Wolfgang.
> 


-- 
------------------
Dan Murphy

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-04 18:13     ` Wolfgang Grandegger
@ 2019-03-04 19:12       ` Dan Murphy
  2019-03-05  2:48         ` Joe Perches
  0 siblings, 1 reply; 16+ messages in thread
From: Dan Murphy @ 2019-03-04 19:12 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Wolfgang

On 3/4/19 12:13 PM, Wolfgang Grandegger wrote:
> 
> 
> Am 04.03.19 um 18:22 schrieb Dan Murphy:
>> Wolfgang
>>
>> On 3/4/19 10:56 AM, Wolfgang Grandegger wrote:
>>> Hello Dan,
>>>
>>> the series already looks quite good. I still realized a few (minor)
>>> issues while browsing the patch/code...
>>>
>>
>> Thanks for the review.  It is getting there.
>>
>>> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>>>> Create a m_can platform framework that peripherial
>>>> devices can register to and use common code and register sets.
>>>> The peripherial devices may provide read/write and configuration
>>>> support of the IP.
>>>>
>>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>>>> ---
>>>>
>>>> v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
>>>> issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
>>>> priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
>>>> Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
>>>> Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/
>>>>
>>>>  drivers/net/can/m_can/Kconfig          |  13 +-
>>>>  drivers/net/can/m_can/Makefile         |   1 +
>>>>  drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
>>>>  drivers/net/can/m_can/m_can.h          | 110 ++++
>>>>  drivers/net/can/m_can/m_can_platform.c | 198 +++++++
>>>>  5 files changed, 681 insertions(+), 343 deletions(-)
>>>>  create mode 100644 drivers/net/can/m_can/m_can.h
>>>>  create mode 100644 drivers/net/can/m_can/m_can_platform.c
>>>>
>>>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>>>> index 04f20dd39007..f7119fd72df4 100644
>>>> --- a/drivers/net/can/m_can/Kconfig
>>>> +++ b/drivers/net/can/m_can/Kconfig
>>>> @@ -1,5 +1,14 @@
>>>>  config CAN_M_CAN
>>>> +	tristate "Bosch M_CAN support"
>>>> +	---help---
>>>> +	  Say Y here if you want support for Bosch M_CAN controller framework.
>>>> +	  This is common support for devices that embed the Bosch M_CAN IP.
>>>> +
>>>> +config CAN_M_CAN_PLATFORM
>>>> +	tristate "Bosch M_CAN support for io-mapped devices"
>>>>  	depends on HAS_IOMEM
>>>> -	tristate "Bosch M_CAN devices"
>>>> +	depends on CAN_M_CAN
>>>>  	---help---
>>>> -	  Say Y here if you want to support for Bosch M_CAN controller.
>>>> +	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>>>> +	  This support is for devices that have the Bosch M_CAN controller
>>>> +	  IP embedded into the device and the IP is IO Mapped to the processor.
>>>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>>>> index 8bbd7f24f5be..057bbcdb3c74 100644
>>>> --- a/drivers/net/can/m_can/Makefile
>>>> +++ b/drivers/net/can/m_can/Makefile
>>>> @@ -3,3 +3,4 @@
>>>>  #
>>>>  
>>>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>>>> +obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>>>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>>>> index 9b449400376b..b37d0886f9cb 100644
>>>> --- a/drivers/net/can/m_can/m_can.c
>>>> +++ b/drivers/net/can/m_can/m_can.c
> 
> ...snip...
> 
>>>> @@ -1451,7 +1459,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>>>>  			netif_stop_queue(dev);
>>>>  			netdev_warn(dev,
>>>>  				    "TX queue active although FIFO is full.");
>>>> -			return NETDEV_TX_BUSY;
>>>> +			return;
>>>
>>> m_can_start_xmit() doesn't return NETDEV_TX_BUSY but NETDEV_TX_OK and
>>> the queue is stopped! Also the skb is not freed! The code states 
>>> "/* This shouldn't happen */" but then it just prints a warning. Did
>>> you see that message?
>>>
>>
>> No I have not seen this warning but I will re-check to be sure.
> 
> If we don't return NETDEV_TX_BUSY but NETDEV_TX_OK, we must handle it
> differently.
> 

OK.  I see the diff between the perp and io mapped.  I will update the code
appropriately.


> ...snip...
> 
>>>> +struct m_can_priv;
>>>> +struct m_can_ops {
>>>> +	/* Device specific call backs */
>>>> +	int (*clr_dev_interrupts)(struct m_can_priv *m_can_class);
>>>
>>> Why not just "clear_interrupt"... to be consistant with the names below.
>>
>> I wanted to be clear in the M_CAN code that these are device interrupts and not M_CAN interrupts.
>>
>> I can change it to clear_interrupt if you think it makes more sense.
> 
> Well, like for "read_reg" etc, I think it's clear that it's a
> device-specific function/ops:
> 
> 	cdev->read_reg
> 	cdev->clear_interrupt
> 

OK

>>>> +	u32 (*read_reg)(struct m_can_priv *m_can_class, int reg);
>>>> +	int (*write_reg)(struct m_can_priv *m_can_class, int reg, int val);
>>>> +	u32 (*read_fifo)(struct m_can_priv *m_can_class, int addr_offset);
>>>> +	int (*write_fifo)(struct m_can_priv *m_can_class, int addr_offset,
>>>> +			  int val);
>>>> +	int (*device_init)(struct m_can_priv *m_can_class);
> 
> And the same here:
> 
> 	cdev->init
> 

OK

>>>> +	int pm_clock_support;
>>>
>>> A "bool" would be more appropriate, I think.
>>
>>
>> I was abiding by this checkpatch warning I got on the is_peripherial.
>>
>> CHECK: Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384
>> #94: FILE: drivers/net/can/m_can/m_can.h:94:
>> +	bool is_peripherial;
>>
> 
> Ah, right! I was also surprised to get that warning. The kernel is full
> of bool's, but well, we should make "checkpatch" happy (and Linus).
> 
> Wolfgang
> 


-- 
------------------
Dan Murphy

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel
  2019-03-04 19:07     ` Dan Murphy
@ 2019-03-04 19:33       ` Wolfgang Grandegger
  0 siblings, 0 replies; 16+ messages in thread
From: Wolfgang Grandegger @ 2019-03-04 19:33 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel

Hello Dan,

Am 04.03.19 um 20:07 schrieb Dan Murphy:
> Wolfgang
> 
> On 3/4/19 12:29 PM, Wolfgang Grandegger wrote:
>> Hello Dan,
>>
>> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>>> Add the TCAN4x5x SPI CAN driver.  This device uses the
>>> Bosch MCAN IP core along with a SPI interface map.
>>> Register to the MCAN common core code to manage the MCAN IP.
>>>
>>> This device has a special method to indicate a write/read
>>> operation on the data payload.
>>>
>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>>> ---
>>>
>>> v6 - Updated Kconfig help and added REGMAP_SPI dependency, fixed coding style issues,
>>> removed setting GPIO high in wake routine, change -EIO to return ret in init and
>>> setup functions - https://lore.kernel.org/patchwork/patch/1042443/
>>>
>>>  drivers/net/can/m_can/Kconfig    |   9 +
>>>  drivers/net/can/m_can/Makefile   |   1 +
>>>  drivers/net/can/m_can/tcan4x5x.c | 529 +++++++++++++++++++++++++++++++
>>>  3 files changed, 539 insertions(+)
>>>  create mode 100644 drivers/net/can/m_can/tcan4x5x.c
>>>
>>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>>> index f7119fd72df4..cf4f32765efa 100644
>>> --- a/drivers/net/can/m_can/Kconfig
>>> +++ b/drivers/net/can/m_can/Kconfig
>>> @@ -12,3 +12,12 @@ config CAN_M_CAN_PLATFORM
>>>  	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>>>  	  This support is for devices that have the Bosch M_CAN controller
>>>  	  IP embedded into the device and the IP is IO Mapped to the processor.
>>> +
>>> +config CAN_M_CAN_TCAN4X5X
>>> +	depends on CAN_M_CAN
>>> +	depends on REGMAP_SPI
>>> +	tristate "TCAN4X5X M_CAN device"
>>> +	---help---
>>> +	  Say Y here if you want support for Texas Instruments TCAN4x5x
>>> +	  M_CAN controller.  This device is a peripherial device that uses the
>>> +	  SPI bus for communication.
>>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>>> index 057bbcdb3c74..e77f0eccff97 100644
>>> --- a/drivers/net/can/m_can/Makefile
>>> +++ b/drivers/net/can/m_can/Makefile
>>> @@ -4,3 +4,4 @@
>>>  
>>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>>>  obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>>> +obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
>>> diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
>>> new file mode 100644
>>> index 000000000000..a91b976bb522
>>> --- /dev/null
>>> +++ b/drivers/net/can/m_can/tcan4x5x.c
>>> @@ -0,0 +1,529 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
>>> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>>> +
>>> +#include <linux/regmap.h>
>>> +#include <linux/spi/spi.h>
>>> +
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/gpio/consumer.h>
>>> +
>>> +#include "m_can.h"
>>> +
>>> +#define DEVICE_NAME "tcan4x5x"
>>> +#define TCAN4X5X_EXT_CLK_DEF	40000000
>>> +
>>> +#define TCAN4X5X_DEV_ID0	0x00
>>> +#define TCAN4X5X_DEV_ID1	0x04
>>> +#define TCAN4X5X_REV		0x08
>>> +#define TCAN4X5X_STATUS		0x0C
>>> +#define TCAN4X5X_ERROR_STATUS	0x10
>>> +#define TCAN4X5X_CONTROL	0x14
>>> +
>>> +#define TCAN4X5X_CONFIG		0x800
>>> +#define TCAN4X5X_TS_PRESCALE	0x804
>>> +#define TCAN4X5X_TEST_REG	0x808
>>> +#define TCAN4X5X_INT_FLAGS	0x820
>>> +#define TCAN4X5X_MCAN_INT_REG	0x824
>>> +#define TCAN4X5X_INT_EN		0x830
>>> +
>>> +/* Interrupt bits */
>>> +#define TCAN4X5X_CANBUSTERMOPEN_INT_EN	BIT(30)
>>> +#define TCAN4X5X_CANHCANL_INT_EN	BIT(29)
>>> +#define TCAN4X5X_CANHBAT_INT_EN		BIT(28)
>>> +#define TCAN4X5X_CANLGND_INT_EN		BIT(27)
>>> +#define TCAN4X5X_CANBUSOPEN_INT_EN	BIT(26)
>>> +#define TCAN4X5X_CANBUSGND_INT_EN	BIT(25)
>>> +#define TCAN4X5X_CANBUSBAT_INT_EN	BIT(24)
>>> +#define TCAN4X5X_UVSUP_INT_EN		BIT(22)
>>> +#define TCAN4X5X_UVIO_INT_EN		BIT(21)
>>> +#define TCAN4X5X_TSD_INT_EN		BIT(19)
>>> +#define TCAN4X5X_ECCERR_INT_EN		BIT(16)
>>> +#define TCAN4X5X_CANINT_INT_EN		BIT(15)
>>> +#define TCAN4X5X_LWU_INT_EN		BIT(14)
>>> +#define TCAN4X5X_CANSLNT_INT_EN		BIT(10)
>>> +#define TCAN4X5X_CANDOM_INT_EN		BIT(8)
>>> +#define TCAN4X5X_CANBUS_ERR_INT_EN	BIT(5)
>>> +#define TCAN4X5X_BUS_FAULT		BIT(4)
>>> +#define TCAN4X5X_MCAN_INT		BIT(1)
>>> +#define TCAN4X5X_ENABLE_TCAN_INT	(TCAN4X5X_MCAN_INT | \
>>> +					TCAN4X5X_BUS_FAULT | \
>>> +					TCAN4X5X_CANBUS_ERR_INT_EN | \
>>> +					TCAN4X5X_CANINT_INT_EN)
>>> +
>>> +/* MCAN Interrupt bits */
>>> +#define TCAN4X5X_MCAN_IR_ARA		BIT(29)
>>> +#define TCAN4X5X_MCAN_IR_PED		BIT(28)
>>> +#define TCAN4X5X_MCAN_IR_PEA		BIT(27)
>>> +#define TCAN4X5X_MCAN_IR_WD		BIT(26)
>>> +#define TCAN4X5X_MCAN_IR_BO		BIT(25)
>>> +#define TCAN4X5X_MCAN_IR_EW		BIT(24)
>>> +#define TCAN4X5X_MCAN_IR_EP		BIT(23)
>>> +#define TCAN4X5X_MCAN_IR_ELO		BIT(22)
>>> +#define TCAN4X5X_MCAN_IR_BEU		BIT(21)
>>> +#define TCAN4X5X_MCAN_IR_BEC		BIT(20)
>>> +#define TCAN4X5X_MCAN_IR_DRX		BIT(19)
>>> +#define TCAN4X5X_MCAN_IR_TOO		BIT(18)
>>> +#define TCAN4X5X_MCAN_IR_MRAF		BIT(17)
>>> +#define TCAN4X5X_MCAN_IR_TSW		BIT(16)
>>> +#define TCAN4X5X_MCAN_IR_TEFL		BIT(15)
>>> +#define TCAN4X5X_MCAN_IR_TEFF		BIT(14)
>>> +#define TCAN4X5X_MCAN_IR_TEFW		BIT(13)
>>> +#define TCAN4X5X_MCAN_IR_TEFN		BIT(12)
>>> +#define TCAN4X5X_MCAN_IR_TFE		BIT(11)
>>> +#define TCAN4X5X_MCAN_IR_TCF		BIT(10)
>>> +#define TCAN4X5X_MCAN_IR_TC		BIT(9)
>>> +#define TCAN4X5X_MCAN_IR_HPM		BIT(8)
>>> +#define TCAN4X5X_MCAN_IR_RF1L		BIT(7)
>>> +#define TCAN4X5X_MCAN_IR_RF1F		BIT(6)
>>> +#define TCAN4X5X_MCAN_IR_RF1W		BIT(5)
>>> +#define TCAN4X5X_MCAN_IR_RF1N		BIT(4)
>>> +#define TCAN4X5X_MCAN_IR_RF0L		BIT(3)
>>> +#define TCAN4X5X_MCAN_IR_RF0F		BIT(2)
>>> +#define TCAN4X5X_MCAN_IR_RF0W		BIT(1)
>>> +#define TCAN4X5X_MCAN_IR_RF0N		BIT(0)
>>> +#define TCAN4X5X_ENABLE_MCAN_INT	(TCAN4X5X_MCAN_IR_TC | \
>>> +					TCAN4X5X_MCAN_IR_RF0N | \
>>> +					TCAN4X5X_MCAN_IR_RF1N | \
>>> +					TCAN4X5X_MCAN_IR_RF0F | \
>>> +					TCAN4X5X_MCAN_IR_RF1F)
>>> +#define TCAN4X5X_MRAM_START	0x8000
>>> +#define TCAN4X5X_MCAN_OFFSET	0x1000
>>> +#define TCAN4X5X_MAX_REGISTER	0x8fff
>>> +
>>> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
>>> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
>>> +
>>> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
>>> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
>>> +
>>> +#define TCAN4X5X_MODE_SEL_MASK		(BIT(7) | BIT(6))
>>> +#define TCAN4X5X_MODE_SLEEP		0x00
>>> +#define TCAN4X5X_MODE_STANDBY		BIT(6)
>>> +#define TCAN4X5X_MODE_NORMAL		BIT(7)
>>> +
>>> +#define TCAN4X5X_SW_RESET	BIT(2)
>>> +
>>> +#define TCAN4X5X_MCAN_CONFIGURED	BIT(5)
>>> +#define TCAN4X5X_WATCHDOG_EN		BIT(3)
>>> +#define TCAN4X5X_WD_60_MS_TIMER		0
>>> +#define TCAN4X5X_WD_600_MS_TIMER	BIT(28)
>>> +#define TCAN4X5X_WD_3_S_TIMER		BIT(29)
>>> +#define TCAN4X5X_WD_6_S_TIMER		(BIT(28) | BIT(29))
>>> +
>>> +struct tcan4x5x_priv {
>>> +	struct regmap *regmap;
>>> +	struct spi_device *spi;
>>> +	struct mutex tcan4x5x_lock; /* SPI device lock */
>>> +
>>> +	struct m_can_classdev *mcan_dev;
>>> +
>>> +	struct gpio_desc *reset_gpio;
>>> +	struct gpio_desc *interrupt_gpio;
>>> +	struct gpio_desc *device_wake_gpio;
>>> +	struct gpio_desc *device_state_gpio;
>>> +	struct regulator *power;
>>> +
>>> +	/* Register based ip */
>>> +	int mram_start;
>>> +	int reg_offset;
>>> +};
>>> +
>>> +static struct can_bittiming_const tcan4x5x_bittiming_const = {
>>> +	.name = DEVICE_NAME,
>>> +	.tseg1_min = 2,
>>> +	.tseg1_max = 31,
>>> +	.tseg2_min = 2,
>>> +	.tseg2_max = 16,
>>> +	.sjw_max = 16,
>>> +	.brp_min = 1,
>>> +	.brp_max = 32,
>>> +	.brp_inc = 1,
>>> +};
>>> +
>>> +static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
>>> +	.name = DEVICE_NAME,
>>> +	.tseg1_min = 1,
>>> +	.tseg1_max = 32,
>>> +	.tseg2_min = 1,
>>> +	.tseg2_max = 16,
>>> +	.sjw_max = 16,
>>> +	.brp_min = 1,
>>> +	.brp_max = 32,
>>> +	.brp_inc = 1,
>>> +};
>>> +
>>> +static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
>>> +{
>>> +	int wake_state = 0;
>>> +
>>> +	if (priv->device_state_gpio)
>>> +		wake_state = gpiod_get_value(priv->device_state_gpio);
>>> +
>>> +	if (priv->device_wake_gpio && wake_state) {
>>> +		gpiod_set_value(priv->device_wake_gpio, 0);
>>> +		usleep_range(5, 50);
>>> +		gpiod_set_value(priv->device_wake_gpio, 1);
>>> +	}
>>> +}
>>> +
>>> +static int regmap_spi_gather_write(void *context, const void *reg,
>>> +				   size_t reg_len, const void *val,
>>> +				   size_t val_len)
>>> +{
>>> +	struct device *dev = context;
>>> +	struct spi_device *spi = to_spi_device(dev);
>>> +	struct spi_message m;
>>> +	u32 addr;
>>> +	struct spi_transfer t[2] = {
>>> +		{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
>>> +		{ .tx_buf = val, .len = val_len, },
>>> +	};
>>> +
>>> +	addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
>>> +
>>> +	spi_message_init(&m);
>>> +	spi_message_add_tail(&t[0], &m);
>>> +	spi_message_add_tail(&t[1], &m);
>>> +
>>> +	return spi_sync(spi, &m);
>>> +}
>>> +
>>> +static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
>>> +{
>>> +	u16 *reg = (u16 *)(data);
>>> +	const u32 *val = data + 4;
>>> +
>>> +	return regmap_spi_gather_write(context, reg, 4, val, count);
>>> +}
>>> +
>>> +static int regmap_spi_async_write(void *context,
>>> +				  const void *reg, size_t reg_len,
>>> +				  const void *val, size_t val_len,
>>> +				  struct regmap_async *a)
>>> +{
>>> +	return -ENOTSUPP;
>>> +}
>>> +
>>> +static struct regmap_async *regmap_spi_async_alloc(void)
>>> +{
>>> +	return NULL;
>>> +}
>>> +
>>> +static int tcan4x5x_regmap_read(void *context,
>>> +				const void *reg, size_t reg_size,
>>> +				void *val, size_t val_size)
>>> +{
>>> +	struct device *dev = context;
>>> +	struct spi_device *spi = to_spi_device(dev);
>>> +
>>> +	u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
>>> +
>>> +	return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
>>> +}
>>> +
>>> +static struct regmap_bus tcan4x5x_bus = {
>>> +	.write = tcan4x5x_regmap_write,
>>> +	.gather_write = regmap_spi_gather_write,
>>> +	.async_write = regmap_spi_async_write,
>>> +	.async_alloc = regmap_spi_async_alloc,
>>> +	.read = tcan4x5x_regmap_read,
>>> +	.read_flag_mask = 0x00,
>>> +	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
>>> +	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
>>> +};
>>> +
>>> +static u32 tcan4x5x_read_reg(struct m_can_classdev *m_can_class, int reg)
>>> +{
>>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>>> +	u32 val;
>>> +
>>> +	tcan4x5x_check_wake(priv);
>>> +
>>> +	regmap_read(priv->regmap, priv->reg_offset + reg, &val);
>>> +
>>> +	return val;
>>> +}
>>> +
>>> +static u32 tcan4x5x_read_fifo(struct m_can_classdev *m_can_class,
>>> +			      int addr_offset)
>>> +{
>>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>>> +	u32 val;
>>> +
>>> +	tcan4x5x_check_wake(priv);
>>> +
>>> +	regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
>>> +
>>> +	return val;
>>> +}
>>> +
>>> +static int tcan4x5x_write_reg(struct m_can_classdev *m_can_class,
>>> +			      int reg, int val)
>>> +{
>>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>>> +
>>> +	tcan4x5x_check_wake(priv);
>>> +
>>> +	return regmap_write(priv->regmap, priv->reg_offset + reg, val);
>>> +}
>>> +
>>> +static int tcan4x5x_write_fifo(struct m_can_classdev *m_can_class,
>>> +			       int addr_offset, int val)
>>> +{
>>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>>> +
>>> +	tcan4x5x_check_wake(priv);
>>> +
>>> +	return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
>>> +}
>>> +
>>> +static int tcan4x5x_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 tcan4x5x_write_tcan_reg(struct m_can_classdev *m_can_class,
>>> +				   int reg, int val)
>>> +{
>>> +	struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)m_can_class->device_data;
>>> +
>>> +	tcan4x5x_check_wake(priv);
>>> +
>>> +	return regmap_write(priv->regmap, reg, val);
>>> +}
>>> +
>>> +static int tcan4x5x_clear_interrupts(struct m_can_classdev *class_dev)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = (struct tcan4x5x_priv *)class_dev->device_data;
>>> +	int ret;
>>> +
>>> +	tcan4x5x_check_wake(tcan4x5x);
>>> +
>>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_STATUS,
>>> +				      TCAN4X5X_CLEAR_ALL_INT);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_MCAN_INT_REG,
>>> +				      TCAN4X5X_ENABLE_MCAN_INT);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_INT_FLAGS,
>>> +				      TCAN4X5X_CLEAR_ALL_INT);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = tcan4x5x_write_tcan_reg(class_dev, TCAN4X5X_ERROR_STATUS,
>>> +				      TCAN4X5X_CLEAR_ALL_INT);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	return ret;
>>> +}
>>
>> Hm, handling the interrupt seems to be a quite heavy operation. How does
>> the device and system digest small messages (dlc=0) coming in at a very
>> high rate?
>>
>> Any chance to optimize accesses be reading more than on word?
>>
> 
> The device does support writing consecutive registers.  The MCAN_INT and INT_FLAGS are consecutive
> and the STATUS and ERROR_STATUS are consecutive.  So we would still need 2 writes so not sure that this will
> optimize enough for your concern.

Yes, that's what I realized as well. Two SPI transfers instead of four
is already an improvement.

> Also the m_can code is missing bulk writes which I did not want to introduce at this time.
> My concern was more functionality and maintaining current io mapped functionality.

That's fine. I'm just curious.

> Bulk writes for peripheral devices is preferred.

Of course.

Wolfgang.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-04 16:56 ` [PATCH v6 1/4] can: m_can: Create a m_can platform framework Wolfgang Grandegger
  2019-03-04 17:22   ` Dan Murphy
@ 2019-03-04 19:43   ` Dan Murphy
  1 sibling, 0 replies; 16+ messages in thread
From: Dan Murphy @ 2019-03-04 19:43 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel

On 3/4/19 10:56 AM, Wolfgang Grandegger wrote:
> Hello Dan,
> 
> the series already looks quite good. I still realized a few (minor)
> issues while browsing the patch/code...
> 
> Am 01.03.19 um 19:50 schrieb Dan Murphy:
>> Create a m_can platform framework that peripherial
>> devices can register to and use common code and register sets.
>> The peripherial devices may provide read/write and configuration
>> support of the IP.
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ---
>>
>> v6 - Squashed platform patch to this patch for bissectablity, fixed coding style
>> issues, updated Kconfig help, placed mcan reg offsets back into c file, renamed
>> priv->skb to priv->tx_skb and cleared perp interrupts at ISR start -
>> Patch 1 comments - https://lore.kernel.org/patchwork/patch/1042446/
>> Patch 2 comments - https://lore.kernel.org/patchwork/patch/1042442/
>>
>>  drivers/net/can/m_can/Kconfig          |  13 +-
>>  drivers/net/can/m_can/Makefile         |   1 +
>>  drivers/net/can/m_can/m_can.c          | 702 +++++++++++++------------
>>  drivers/net/can/m_can/m_can.h          | 110 ++++
>>  drivers/net/can/m_can/m_can_platform.c | 198 +++++++
>>  5 files changed, 681 insertions(+), 343 deletions(-)
>>  create mode 100644 drivers/net/can/m_can/m_can.h
>>  create mode 100644 drivers/net/can/m_can/m_can_platform.c
>>
>> diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig
>> index 04f20dd39007..f7119fd72df4 100644
>> --- a/drivers/net/can/m_can/Kconfig
>> +++ b/drivers/net/can/m_can/Kconfig
>> @@ -1,5 +1,14 @@
>>  config CAN_M_CAN
>> +	tristate "Bosch M_CAN support"
>> +	---help---
>> +	  Say Y here if you want support for Bosch M_CAN controller framework.
>> +	  This is common support for devices that embed the Bosch M_CAN IP.
>> +
>> +config CAN_M_CAN_PLATFORM
>> +	tristate "Bosch M_CAN support for io-mapped devices"
>>  	depends on HAS_IOMEM
>> -	tristate "Bosch M_CAN devices"
>> +	depends on CAN_M_CAN
>>  	---help---
>> -	  Say Y here if you want to support for Bosch M_CAN controller.
>> +	  Say Y here if you want support for IO Mapped Bosch M_CAN controller.
>> +	  This support is for devices that have the Bosch M_CAN controller
>> +	  IP embedded into the device and the IP is IO Mapped to the processor.
>> diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile
>> index 8bbd7f24f5be..057bbcdb3c74 100644
>> --- a/drivers/net/can/m_can/Makefile
>> +++ b/drivers/net/can/m_can/Makefile
>> @@ -3,3 +3,4 @@
>>  #
>>  
>>  obj-$(CONFIG_CAN_M_CAN) += m_can.o
>> +obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>> index 9b449400376b..b37d0886f9cb 100644
>> --- a/drivers/net/can/m_can/m_can.c
>> +++ b/drivers/net/can/m_can/m_can.c
>> @@ -1,20 +1,14 @@
>> -/*
>> - * CAN bus driver for Bosch M_CAN controller
>> - *
>> - * Copyright (C) 2014 Freescale Semiconductor, Inc.
>> - *	Dong Aisheng <b29396@freescale.com>
>> - *
>> - * Bosch M_CAN user manual can be obtained from:
>> +// SPDX-License-Identifier: GPL-2.0
>> +// CAN bus driver for Bosch M_CAN controller
>> +// Copyright (C) 2014 Freescale Semiconductor, Inc.
>> +//      Dong Aisheng <b29396@freescale.com>
>> +// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +/* Bosch M_CAN user manual can be obtained from:
>>   * http://www.bosch-semiconductors.de/media/pdf_1/ipmodules_1/m_can/
>>   * mcan_users_manual_v302.pdf
>> - *
>> - * This file is licensed under the terms of the GNU General Public
>> - * License version 2. This program is licensed "as is" without any
>> - * warranty of any kind, whether express or implied.
>>   */
>>  
>> -#include <linux/clk.h>
>> -#include <linux/delay.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/io.h>
>>  #include <linux/kernel.h>
>> @@ -28,11 +22,7 @@
>>  #include <linux/can/dev.h>
>>  #include <linux/pinctrl/consumer.h>
>>  
>> -/* napi related */
>> -#define M_CAN_NAPI_WEIGHT	64
>> -
>> -/* message ram configuration data length */
>> -#define MRAM_CFG_LEN	8
>> +#include "m_can.h"
>>  
>>  /* registers definition */
>>  enum m_can_reg {
>> @@ -86,28 +76,11 @@ enum m_can_reg {
>>  	M_CAN_TXEFA	= 0xf8,
>>  };
>>  
>> -/* m_can lec values */
>> -enum m_can_lec_type {
>> -	LEC_NO_ERROR = 0,
>> -	LEC_STUFF_ERROR,
>> -	LEC_FORM_ERROR,
>> -	LEC_ACK_ERROR,
>> -	LEC_BIT1_ERROR,
>> -	LEC_BIT0_ERROR,
>> -	LEC_CRC_ERROR,
>> -	LEC_UNUSED,
>> -};
>> +/* napi related */
>> +#define M_CAN_NAPI_WEIGHT	64
>>  
>> -enum m_can_mram_cfg {
>> -	MRAM_SIDF = 0,
>> -	MRAM_XIDF,
>> -	MRAM_RXF0,
>> -	MRAM_RXF1,
>> -	MRAM_RXB,
>> -	MRAM_TXE,
>> -	MRAM_TXB,
>> -	MRAM_CFG_NUM,
>> -};
>> +/* message ram configuration data length */
>> +#define MRAM_CFG_LEN	8
>>  
>>  /* Core Release Register (CREL) */
>>  #define CREL_REL_SHIFT		28
>> @@ -343,77 +316,83 @@ enum m_can_mram_cfg {
>>  #define TX_BUF_MM_MASK		(0xff << TX_BUF_MM_SHIFT)
>>  
>>  /* Tx event FIFO Element */
>> -/* E1 */
> 
> I also don't understand what "E1" means... we should keep taht line anyway!
> 
>>  #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT
>>  #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT)
>>  
>> -/* address offset and element number for each FIFO/Buffer in the Message RAM */
>> -struct mram_cfg {
>> -	u16 off;
>> -	u8  num;
>> -};
>> -
>> -/* m_can private data structure */
>> -struct m_can_priv {
>> -	struct can_priv can;	/* must be the first member */
>> -	struct napi_struct napi;
>> -	struct net_device *dev;
>> -	struct device *device;
>> -	struct clk *hclk;
>> -	struct clk *cclk;
>> -	void __iomem *base;
>> -	u32 irqstatus;
>> -	int version;
>> -
>> -	/* message ram configuration */
>> -	void __iomem *mram_base;
>> -	struct mram_cfg mcfg[MRAM_CFG_NUM];
>> -};
>> +static u32 m_can_read(struct m_can_priv *priv, enum m_can_reg reg)
>> +{
>> +	if (priv->ops->read_reg)
>> +		return priv->ops->read_reg(priv, reg);
>> +	else
>> +		return -EINVAL;
>> +}
>>  
>> -static inline u32 m_can_read(const struct m_can_priv *priv, enum m_can_reg reg)
>> +static int m_can_write(struct m_can_priv *priv, enum m_can_reg reg, u32 val)
>>  {
>> -	return readl(priv->base + reg);
>> +	if (priv->ops->write_reg)
>> +		return priv->ops->write_reg(priv, reg, val);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline void m_can_write(const struct m_can_priv *priv,
>> -			       enum m_can_reg reg, u32 val)
>> +static u32 m_can_fifo_read(struct m_can_priv *priv,
>> +			   u32 fgi, unsigned int offset)
>>  {
>> -	writel(val, priv->base + reg);
>> +	u32 addr_offset = priv->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->read_fifo)
>> +		return priv->ops->read_fifo(priv, addr_offset);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
>> -				  u32 fgi, unsigned int offset)
>> +static u32 m_can_fifo_write(struct m_can_priv *priv,
>> +			    u32 fpi, unsigned int offset, u32 val)
>>  {
>> -	return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
>> -		     fgi * RXF0_ELEMENT_SIZE + offset);
>> +	u32 addr_offset = priv->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->write_fifo)
>> +		return priv->ops->write_fifo(priv, addr_offset, val);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline void m_can_fifo_write(const struct m_can_priv *priv,
>> -				    u32 fpi, unsigned int offset, u32 val)
>> +static u32 m_can_fifo_write_no_off(struct m_can_priv *priv,
>> +				   u32 fpi, u32 val)
>>  {
>> -	writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
>> -	       fpi * TXB_ELEMENT_SIZE + offset);
>> +	if (priv->ops->write_fifo)
>> +		return priv->ops->write_fifo(priv, fpi, val);
>> +	else
>> +		return 0;
>>  }
>>  
>> -static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
>> -				      u32 fgi,
>> -				      u32 offset) {
>> -	return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
>> -			fgi * TXE_ELEMENT_SIZE + offset);
>> +static u32 m_can_txe_fifo_read(struct m_can_priv *priv, u32 fgi, u32 offset)
>> +{
>> +	u32 addr_offset = priv->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
>> +			  offset;
>> +
>> +	if (priv->ops->read_fifo)
>> +		return priv->ops->read_fifo(priv, addr_offset);
>> +	else
>> +		return -EINVAL;
>>  }
>>  
>> -static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
>> +static inline bool m_can_tx_fifo_full(struct m_can_priv *priv)
>>  {
>>  		return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
>>  }
>>  
>> -static inline void m_can_config_endisable(const struct m_can_priv *priv,
>> -					  bool enable)
>> +void m_can_config_endisable(struct m_can_priv *priv, bool enable)
>>  {
>>  	u32 cccr = m_can_read(priv, M_CAN_CCCR);
>>  	u32 timeout = 10;
>>  	u32 val = 0;
>>  
>> +	if (cccr & CCCR_CSR)
>> +		cccr &= ~CCCR_CSR;
>> +
> 
> Is that a bug fix? Or why do we need it?
> 
>>  	if (enable) {
>>  		/* enable m_can configuration */
>>  		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
>> @@ -430,7 +409,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>>  
>>  	while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
>>  		if (timeout == 0) {
>> -			netdev_warn(priv->dev, "Failed to init module\n");
>> +			netdev_warn(priv->net, "Failed to init module\n");
>>  			return;
>>  		}
>>  		timeout--;
>> @@ -438,13 +417,13 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
>>  	}
>>  }
>>  
>> -static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
>> +static inline void m_can_enable_all_interrupts(struct m_can_priv *priv)
>>  {
>>  	/* Only interrupt line 0 is used in this driver */
>>  	m_can_write(priv, M_CAN_ILE, ILE_EINT0);
>>  }
>>  
>> -static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
>> +static inline void m_can_disable_all_interrupts(struct m_can_priv *priv)
>>  {
>>  	m_can_write(priv, M_CAN_ILE, 0x0);
>>  }
>> @@ -633,9 +612,12 @@ static int m_can_clk_start(struct m_can_priv *priv)
>>  {
>>  	int err;
>>  
>> -	err = pm_runtime_get_sync(priv->device);
>> +	if (priv->pm_clock_support == 0)
>> +		return 0;
>> +
>> +	err = pm_runtime_get_sync(priv->dev);
>>  	if (err < 0) {
>> -		pm_runtime_put_noidle(priv->device);
>> +		pm_runtime_put_noidle(priv->dev);
>>  		return err;
>>  	}
>>  
>> @@ -644,7 +626,8 @@ static int m_can_clk_start(struct m_can_priv *priv)
>>  
>>  static void m_can_clk_stop(struct m_can_priv *priv)
>>  {
>> -	pm_runtime_put_sync(priv->device);
>> +	if (priv->pm_clock_support)
>> +		pm_runtime_put_sync(priv->dev);
>>  }
>>  
>>  static int m_can_get_berr_counter(const struct net_device *dev,
>> @@ -811,9 +794,8 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
>>  	return work_done;
>>  }
>>  
>> -static int m_can_poll(struct napi_struct *napi, int quota)
>> +static int m_can_rx_handler(struct net_device *dev, int quota)
>>  {
>> -	struct net_device *dev = napi->dev;
>>  	struct m_can_priv *priv = netdev_priv(dev);
>>  	int work_done = 0;
>>  	u32 irqstatus, psr;
>> @@ -831,13 +813,33 @@ static int m_can_poll(struct napi_struct *napi, int quota)
>>  
>>  	if (irqstatus & IR_RF0N)
>>  		work_done += m_can_do_rx_poll(dev, (quota - work_done));
>> +end:
>> +	return work_done;
>> +}
>> +
>> +static int m_can_rx_peripherial(struct net_device *dev)
>> +{
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +
>> +	m_can_rx_handler(dev, 1);
>> +
>> +	m_can_enable_all_interrupts(priv);
>> +
>> +	return 0;
>> +}
>> +
>> +static int m_can_poll(struct napi_struct *napi, int quota)
>> +{
>> +	struct net_device *dev = napi->dev;
>> +	struct m_can_priv *priv = netdev_priv(dev);
>> +	int work_done;
>>  
>> +	work_done = m_can_rx_handler(dev, quota);
>>  	if (work_done < quota) {
>>  		napi_complete_done(napi, work_done);
>>  		m_can_enable_all_interrupts(priv);
>>  	}
>>  
>> -end:
>>  	return work_done;
>>  }
>>  
>> @@ -894,6 +896,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>>  	if (ir & IR_ALL_INT)
>>  		m_can_write(priv, M_CAN_IR, ir);
>>  
>> +	if (priv->ops->clr_dev_interrupts)
>> +		priv->ops->clr_dev_interrupts(priv);
>> +
>>  	/* schedule NAPI in case of
>>  	 * - rx IRQ
>>  	 * - state change IRQ
>> @@ -902,7 +907,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>>  	if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
>>  		priv->irqstatus = ir;
>>  		m_can_disable_all_interrupts(priv);
>> -		napi_schedule(&priv->napi);
>> +		if (!priv->is_peripherial)
>> +			napi_schedule(&priv->napi);
>> +		else
>> +			m_can_rx_peripherial(dev);
>>  	}
>>  
>>  	if (priv->version == 30) {
>> @@ -1155,6 +1163,9 @@ static void m_can_chip_config(struct net_device *dev)
>>  	m_can_set_bittiming(dev);
>>  
>>  	m_can_config_endisable(priv, false);
>> +
>> +	if (priv->ops->device_init)
>> +		priv->ops->device_init(priv);
>>  }
>>  
>>  static void m_can_start(struct net_device *dev)
>> @@ -1188,20 +1199,17 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
>>   * else it returns the release and step coded as:
>>   * return value = 10 * <release> + 1 * <step>
>>   */
>> -static int m_can_check_core_release(void __iomem *m_can_base)
>> +static int m_can_check_core_release(struct m_can_priv *priv)
>>  {
>>  	u32 crel_reg;
>>  	u8 rel;
>>  	u8 step;
>>  	int res;
>> -	struct m_can_priv temp_priv = {
>> -		.base = m_can_base
>> -	};
>>  
>>  	/* Read Core Release Version and split into version number
>>  	 * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
>>  	 */
>> -	crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
>> +	crel_reg = m_can_read(priv, M_CAN_CREL);
>>  	rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
>>  	step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
>>  
>> @@ -1219,18 +1227,26 @@ static int m_can_check_core_release(void __iomem *m_can_base)
>>  /* Selectable Non ISO support only in version 3.2.x
>>   * This function checks if the bit is writable.
>>   */
>> -static bool m_can_niso_supported(const struct m_can_priv *priv)
>> +static bool m_can_niso_supported(struct m_can_priv *priv)
>>  {
>> -	u32 cccr_reg, cccr_poll;
>> -	int niso_timeout;
>> +	u32 cccr_reg, cccr_poll = 0;
>> +	int niso_timeout = -ETIMEDOUT;
>> +	int i;
>>  
>>  	m_can_config_endisable(priv, true);
>>  	cccr_reg = m_can_read(priv, M_CAN_CCCR);
>>  	cccr_reg |= CCCR_NISO;
>>  	m_can_write(priv, M_CAN_CCCR, cccr_reg);
>>  
>> -	niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
>> -					  (cccr_poll == cccr_reg), 0, 10);
>> +	for (i = 0; i <= 10; i++) {
>> +		cccr_poll = m_can_read(priv, M_CAN_CCCR);
>> +		if (cccr_poll == cccr_reg) {
>> +			niso_timeout = 0;
>> +			break;
>> +		}
>> +
>> +		usleep_range(1, 5);
>> +	}

I wanted to point out to you that I did not add back the readl_timeout but I did add a break
and a timer to try to mimic the original code.

Dan

<snip>
-- 
------------------
Dan Murphy

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v6 1/4] can: m_can: Create a m_can platform framework
  2019-03-04 19:12       ` Dan Murphy
@ 2019-03-05  2:48         ` Joe Perches
  0 siblings, 0 replies; 16+ messages in thread
From: Joe Perches @ 2019-03-05  2:48 UTC (permalink / raw)
  To: Dan Murphy, Wolfgang Grandegger, mkl, davem
  Cc: linux-can, netdev, linux-kernel

On Mon, 2019-03-04 at 13:12 -0600, Dan Murphy wrote:
> On 3/4/19 12:13 PM, Wolfgang Grandegger wrote:
> > Am 04.03.19 um 18:22 schrieb Dan Murphy:
> > > > > +	int pm_clock_support;
> > > > 
> > > > A "bool" would be more appropriate, I think.
> > > 
> > > I was abiding by this checkpatch warning I got on the is_peripherial.
> > > 
> > > CHECK: Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384
> > > #94: FILE: drivers/net/can/m_can/m_can.h:94:
> > > +	bool is_peripherial;
> > > 
> > 
> > Ah, right! I was also surprised to get that warning. The kernel is full
> > of bool's, but well, we should make "checkpatch" happy (and Linus).

That check has been removed from checkpatch by

commit 7967656ffbfa493f5546c0f18bf8a28f702c4baa
Author: Jason Gunthorpe <jgg@ziepe.ca>
Date:   Fri Jan 18 15:50:47 2019 -0700

    coding-style: Clarify the expectations around bool
    
    There has been some confusion since checkpatch started warning about bool
    use in structures, and people have been avoiding using it.
    
    Many people feel there is still a legitimate place for bool in structures,
    so provide some guidance on bool usage derived from the entire thread that
    spawned the checkpatch warning.
    
    Link: https://lkml.kernel.org/r/CA+55aFwVZk1OfB9T2v014PTAKFhtVan_Zj2dOjnCy3x6E4UJfA@mail.gmail.com



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2019-03-05  2:49 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-01 18:50 [PATCH v6 1/4] can: m_can: Create a m_can platform framework Dan Murphy
2019-03-01 18:50 ` [PATCH v6 2/4] can: m_can: Rename m_can_priv to m_can_classdev Dan Murphy
2019-03-04 17:31   ` Wolfgang Grandegger
2019-03-04 18:14     ` Dan Murphy
2019-03-04 18:26       ` Wolfgang Grandegger
2019-03-01 18:50 ` [PATCH v6 3/4] dt-bindings: can: tcan4x5x: Add DT bindings for TCAN4x5X driver Dan Murphy
2019-03-01 18:50 ` [PATCH v6 4/4] can: tcan4x5x: Add tcan4x5x driver to the kernel Dan Murphy
2019-03-04 18:29   ` Wolfgang Grandegger
2019-03-04 19:07     ` Dan Murphy
2019-03-04 19:33       ` Wolfgang Grandegger
2019-03-04 16:56 ` [PATCH v6 1/4] can: m_can: Create a m_can platform framework Wolfgang Grandegger
2019-03-04 17:22   ` Dan Murphy
2019-03-04 18:13     ` Wolfgang Grandegger
2019-03-04 19:12       ` Dan Murphy
2019-03-05  2:48         ` Joe Perches
2019-03-04 19:43   ` Dan Murphy

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).