Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b03cfc5b authored by Mario Huettel's avatar Mario Huettel Committed by Marc Kleine-Budde
Browse files

can: m_can: Enable M_CAN version dependent initialization



This patch adapts the initialization of the M_CAN. So it can be used
with all versions >= 3.0.x.

Changes:
* Added version element to m_can_priv structure to hold M_CAN version.
* Renamed bittiming structs for version 3.0.x
* Added new bittiming structs for version >= 3.1.x
* Function alloc_m_can_dev takes 2 new arguments. The TX FIFO size and the
  base address of the module.
* Chip configuration for CAN_CTRLMODE_LOOPBACK is changed: Enabled
  CCCR_MON bit. In combination with TEST_LBCK it activates the internal
  loopback mode. Leaving CCCR_MON '0' results in external loopback mode.
* Clocks are temporarily enabled by platform_propbe function in order to
  allow read access to the Core Release register and the Control Register.
  Registers are used to detect M_CAN version and optional Non-ISO Feature.

Initialization of M_CAN for version >= 3.1.x:
* TX FIFO of M_CAN is used to transmit frames. The driver does not need to
  stop the tx queue after each frame sent.
* Initialization of TX Event FIFO is added.
* NON-ISO is fixed for all M_CAN versions < 3.2.x. Version 3.2.x _can_ have
  the NISO (Non-ISO) bit which can switch the mode of the M_CAN to Non-ISO
  mode. This bit does not have to be writeable. Therefore it is checked.
  If it is writable Non-ISO support is added to the controllers supported
  CAN modes.

New Functions:
* Function to check the Core Release version. The read value determines the
  behaviour of the driver.
* Function to check if the NISO bit for version >= 3.2.x is implemented.

Signed-off-by: default avatarMario Huettel <mario.huettel@gmx.net>
Reviewed-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Tested-by: default avatarQuentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 5e1bd15a
Loading
Loading
Loading
Loading
+269 −71
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>

#include <linux/iopoll.h>
#include <linux/can/dev.h>

/* napi related */
@@ -107,6 +107,14 @@ enum m_can_mram_cfg {
	MRAM_CFG_NUM,
};

/* Core Release Register (CREL) */
#define CREL_REL_SHIFT		28
#define CREL_REL_MASK		(0xF << CREL_REL_SHIFT)
#define CREL_STEP_SHIFT		24
#define CREL_STEP_MASK		(0xF << CREL_STEP_SHIFT)
#define CREL_SUBSTEP_SHIFT	20
#define CREL_SUBSTEP_MASK	(0xF << CREL_SUBSTEP_SHIFT)

/* Data Bit Timing & Prescaler Register (DBTP) */
#define DBTP_TDC		BIT(23)
#define DBTP_DBRP_SHIFT		16
@@ -342,6 +350,7 @@ struct m_can_priv {
	struct clk *cclk;
	void __iomem *base;
	u32 irqstatus;
	int version;

	/* message ram configuration */
	void __iomem *mram_base;
@@ -833,7 +842,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static const struct can_bittiming_const m_can_bittiming_const = {
static const struct can_bittiming_const m_can_bittiming_const_30X = {
	.name = KBUILD_MODNAME,
	.tseg1_min = 2,		/* Time segment 1 = prop_seg + phase_seg1 */
	.tseg1_max = 64,
@@ -845,7 +854,7 @@ static const struct can_bittiming_const m_can_bittiming_const = {
	.brp_inc = 1,
};

static const struct can_bittiming_const m_can_data_bittiming_const = {
static const struct can_bittiming_const m_can_data_bittiming_const_30X = {
	.name = KBUILD_MODNAME,
	.tseg1_min = 2,		/* Time segment 1 = prop_seg + phase_seg1 */
	.tseg1_max = 16,
@@ -857,6 +866,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = {
	.brp_inc = 1,
};

static const struct can_bittiming_const m_can_bittiming_const_31X = {
	.name = KBUILD_MODNAME,
	.tseg1_min = 2,		/* Time segment 1 = prop_seg + phase_seg1 */
	.tseg1_max = 256,
	.tseg2_min = 1,		/* Time segment 2 = phase_seg2 */
	.tseg2_max = 128,
	.sjw_max = 128,
	.brp_min = 1,
	.brp_max = 512,
	.brp_inc = 1,
};

static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
	.name = KBUILD_MODNAME,
	.tseg1_min = 1,		/* Time segment 1 = prop_seg + phase_seg1 */
	.tseg1_max = 32,
	.tseg2_min = 1,		/* Time segment 2 = phase_seg2 */
	.tseg2_max = 16,
	.sjw_max = 16,
	.brp_min = 1,
	.brp_max = 32,
	.brp_inc = 1,
};

static int m_can_set_bittiming(struct net_device *dev)
{
	struct m_can_priv *priv = netdev_priv(dev);
@@ -928,29 +961,53 @@ static void m_can_chip_config(struct net_device *dev)
		     priv->mcfg[MRAM_RXF1].off);

	cccr = m_can_read(priv, M_CAN_CCCR);
	cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
		(CCCR_CME_MASK << CCCR_CME_SHIFT));
	test = m_can_read(priv, M_CAN_TEST);
	test &= ~TEST_LBCK;
	if (priv->version == 30) {
	/* Version 3.0.x */

	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
		cccr |= CCCR_MON;
		cccr &= ~(CCCR_TEST | CCCR_MON |
			(CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
			(CCCR_CME_MASK << CCCR_CME_SHIFT));

		if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
			cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;

	} else {
	/* Version 3.1.x or 3.2.x */
		cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE);

		/* Only 3.2.x has NISO Bit implemented */
		if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
			cccr |= CCCR_NISO;

		if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
			cccr |= (CCCR_BRSE | CCCR_FDOE);
	}

	/* Loopback Mode */
	if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
		cccr |= CCCR_TEST;
		cccr |= CCCR_TEST | CCCR_MON;
		test |= TEST_LBCK;
	}

	if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
		cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
	/* Enable Monitoring (all versions) */
	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
		cccr |= CCCR_MON;

	/* Write config */
	m_can_write(priv, M_CAN_CCCR, cccr);
	m_can_write(priv, M_CAN_TEST, test);

	/* enable interrupts */
	/* Enable interrupts */
	m_can_write(priv, M_CAN_IR, IR_ALL_INT);
	if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
		m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC_30X);
		if (priv->version == 30)
			m_can_write(priv, M_CAN_IE, IR_ALL_INT &
				    ~(IR_ERR_LEC_30X));
		else
			m_can_write(priv, M_CAN_IE, IR_ALL_INT &
				    ~(IR_ERR_LEC_31X));
	else
		m_can_write(priv, M_CAN_IE, IR_ALL_INT);

@@ -994,33 +1051,140 @@ static void free_m_can_dev(struct net_device *dev)
	free_candev(dev);
}

static struct net_device *alloc_m_can_dev(void)
/* Checks core release number of M_CAN
 * returns 0 if an unsupported device is detected
 * 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)
{
	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);
	rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
	step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);

	if (rel == 3) {
		/* M_CAN v3.x.y: create return value */
		res = 30 + step;
	} else {
		/* Unsupported M_CAN version */
		res = 0;
	}

	return res;
}

/* 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)
{
	u32 cccr_reg, cccr_poll;
	int niso_timeout;

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

	/* Clear NISO */
	cccr_reg &= ~(CCCR_NISO);
	m_can_write(priv, M_CAN_CCCR, cccr_reg);

	m_can_config_endisable(priv, false);

	/* return false if time out (-ETIMEDOUT), else return true */
	return !niso_timeout;
}

static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
					  void __iomem *addr, u32 tx_fifo_size)
{
	struct net_device *dev;
	struct m_can_priv *priv;
	int m_can_version;
	unsigned int echo_buffer_count;

	dev = alloc_candev(sizeof(*priv), 1);
	if (!dev)
		return NULL;
	m_can_version = m_can_check_core_release(addr);
	/* return if unsupported version */
	if (!m_can_version) {
		dev = NULL;
		goto return_dev;
	}

	/* If version < 3.1.x, then only one echo buffer is used */
	echo_buffer_count = ((m_can_version == 30)
				? 1U
				: (unsigned int)tx_fifo_size);

	dev = alloc_candev(sizeof(*priv), echo_buffer_count);
	if (!dev) {
		dev = NULL;
		goto return_dev;
	}
	priv = netdev_priv(dev);
	netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);

	/* Shared properties of all M_CAN versions */
	priv->version = m_can_version;
	priv->dev = dev;
	priv->can.bittiming_const = &m_can_bittiming_const;
	priv->can.data_bittiming_const = &m_can_data_bittiming_const;
	priv->base = addr;
	priv->can.do_set_mode = m_can_set_mode;
	priv->can.do_get_berr_counter = m_can_get_berr_counter;

	/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */
	can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);

	/* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */
	/* Set M_CAN supported operations */
	priv->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) {
	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 =
				&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 =
				&m_can_data_bittiming_const_31X;
		break;
	case 32:
		priv->can.bittiming_const = &m_can_bittiming_const_31X;
		priv->can.data_bittiming_const =
				&m_can_data_bittiming_const_31X;
		priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
						? CAN_CTRLMODE_FD_NON_ISO
						: 0);
		break;
	default:
		/* Unsupported device: free candev */
		free_m_can_dev(dev);
		dev_err(&pdev->dev, "Unsupported version number: %2d",
			priv->version);
		dev = NULL;
		break;
	}

return_dev:
	return dev;
}

@@ -1167,58 +1331,37 @@ static int register_m_can_dev(struct net_device *dev)
	return register_candev(dev);
}

static int m_can_of_parse_mram(struct platform_device *pdev,
			       struct m_can_priv *priv)
static void m_can_of_parse_mram(struct m_can_priv *priv,
				const u32 *mram_config_vals)
{
	struct device_node *np = pdev->dev.of_node;
	struct resource *res;
	void __iomem *addr;
	u32 out_val[MRAM_CFG_LEN];
	int i, start, end, ret;

	/* message ram could be shared */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
	if (!res)
		return -ENODEV;

	addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!addr)
		return -ENOMEM;
	int i, start, end;

	/* get message ram configuration */
	ret = of_property_read_u32_array(np, "bosch,mram-cfg",
					 out_val, sizeof(out_val) / 4);
	if (ret) {
		dev_err(&pdev->dev, "can not get message ram configuration\n");
		return -ENODEV;
	}

	priv->mram_base = addr;
	priv->mcfg[MRAM_SIDF].off = out_val[0];
	priv->mcfg[MRAM_SIDF].num = out_val[1];
	priv->mcfg[MRAM_SIDF].off = mram_config_vals[0];
	priv->mcfg[MRAM_SIDF].num = mram_config_vals[1];
	priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off +
			priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE;
	priv->mcfg[MRAM_XIDF].num = out_val[2];
	priv->mcfg[MRAM_XIDF].num = mram_config_vals[2];
	priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off +
			priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE;
	priv->mcfg[MRAM_RXF0].num = out_val[3] &
	priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] &
			(RXFC_FS_MASK >> RXFC_FS_SHIFT);
	priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off +
			priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE;
	priv->mcfg[MRAM_RXF1].num = out_val[4] &
	priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] &
			(RXFC_FS_MASK >> RXFC_FS_SHIFT);
	priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off +
			priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE;
	priv->mcfg[MRAM_RXB].num = out_val[5];
	priv->mcfg[MRAM_RXB].num = mram_config_vals[5];
	priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off +
			priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE;
	priv->mcfg[MRAM_TXE].num = out_val[6];
	priv->mcfg[MRAM_TXE].num = mram_config_vals[6];
	priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off +
			priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE;
	priv->mcfg[MRAM_TXB].num = out_val[7] &
	priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
			(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);

	dev_dbg(&pdev->dev, "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",
	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,
		priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
		priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
@@ -1237,7 +1380,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev,
	for (i = start; i < end; i += 4)
		writel(0x0, priv->mram_base + i);

	return 0;
}

static int m_can_plat_probe(struct platform_device *pdev)
@@ -1246,38 +1388,86 @@ static int m_can_plat_probe(struct platform_device *pdev)
	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;

	hclk = devm_clk_get(&pdev->dev, "hclk");
	cclk = devm_clk_get(&pdev->dev, "cclk");

	if (IS_ERR(hclk) || IS_ERR(cclk)) {
		dev_err(&pdev->dev, "no clock found\n");
		return -ENODEV;
		ret = -ENODEV;
		goto failed_ret;
	}

	/* Enable clocks. Necessary to read Core Release in order to determine
	 * M_CAN version
	 */
	ret = clk_prepare_enable(hclk);
	if (ret)
		goto disable_hclk_ret;

	ret = clk_prepare_enable(cclk);
	if (ret)
		goto disable_cclk_ret;

	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)
		return -EINVAL;

	/* allocate the m_can device */
	dev = alloc_m_can_dev();
	if (!dev)
		return -ENOMEM;
	if (IS_ERR(addr) || irq < 0) {
		ret = -EINVAL;
		goto disable_cclk_ret;
	}

	/* message ram could be shared */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
	if (!res) {
		ret = -ENODEV;
		goto disable_cclk_ret;
	}

	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!mram_addr) {
		ret = -ENOMEM;
		goto disable_cclk_ret;
	}

	/* get message ram configuration */
	ret = of_property_read_u32_array(np, "bosch,mram-cfg",
					 mram_config_vals,
					 sizeof(mram_config_vals) / 4);
	if (ret) {
		dev_err(&pdev->dev, "Could not get Message RAM configuration.");
		goto disable_cclk_ret;
	}

	/* Get TX FIFO size
	 * Defines the total amount of echo buffers for loopback
	 */
	tx_fifo_size = mram_config_vals[7];

	/* allocate the m_can device */
	dev = alloc_m_can_dev(pdev, addr, tx_fifo_size);
	if (!dev) {
		ret = -ENOMEM;
		goto disable_cclk_ret;
	}
	priv = netdev_priv(dev);
	dev->irq = irq;
	priv->base = addr;
	priv->device = &pdev->dev;
	priv->hclk = hclk;
	priv->cclk = cclk;
	priv->can.clock.freq = clk_get_rate(cclk);
	priv->mram_base = mram_addr;

	ret = m_can_of_parse_mram(pdev, priv);
	if (ret)
		goto failed_free_dev;
	m_can_of_parse_mram(priv, mram_config_vals);

	platform_set_drvdata(pdev, dev);
	SET_NETDEV_DEV(dev, &pdev->dev);
@@ -1291,13 +1481,21 @@ static int m_can_plat_probe(struct platform_device *pdev)

	devm_can_led_init(dev);

	dev_info(&pdev->dev, "%s device registered (irq=%d)\n",
		 KBUILD_MODNAME, dev->irq);
	dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
		 KBUILD_MODNAME, dev->irq, priv->version);

	return 0;
	/* Probe finished
	 * Stop clocks. They will be reactivated once the M_CAN device is opened
	 */
	goto disable_cclk_ret;

failed_free_dev:
	free_m_can_dev(dev);
disable_cclk_ret:
	clk_disable_unprepare(cclk);
disable_hclk_ret:
	clk_disable_unprepare(hclk);
failed_ret:
	return ret;
}