Commit d9cead75 authored by Angelo Dureghello's avatar Angelo Dureghello Committed by Marc Kleine-Budde
Browse files

can: flexcan: add mcf5441x support

Add flexcan support for NXP ColdFire mcf5441x family.

This flexcan module is quite similar to imx6 flexcan module, but
with some exceptions:

- 3 separate interrupt sources, MB, BOFF and ERR,
- implements 16 mb only,
- m68k architecture is not supporting devicetrees, so a
  platform data check/case has been added,
- ColdFire is m68k, so big-endian cpu, with a little-endian flexcan
  module.

Link: https://lore.kernel.org/r/20210702094841.327679-5-angelo@kernel-space.org


Signed-off-by: default avatarAngelo Dureghello <angelo@kernel-space.org>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 896e7f3e
Loading
Loading
Loading
Loading
+104 −20
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/can/platform/flexcan.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -208,18 +209,19 @@
/* FLEXCAN hardware feature flags
 *
 * Below is some version info we got:
 *    SOC   Version   IP-Version  Glitch- [TR]WRN_INT IRQ Err Memory err RTR rece-   FD Mode
 *    SOC   Version   IP-Version  Glitch- [TR]WRN_INT IRQ Err Memory err RTR rece-   FD Mode     MB
 *                                Filter? connected?  Passive detection  ption in MB Supported?
 *   MX25  FlexCAN2  03.00.00.00     no        no        no       no        no           no
 *   MX28  FlexCAN2  03.00.04.00    yes       yes        no       no        no           no
 *   MX35  FlexCAN2  03.00.00.00     no        no        no       no        no           no
 *   MX53  FlexCAN2  03.00.00.00    yes        no        no       no        no           no
 *   MX6s  FlexCAN3  10.00.12.00    yes       yes        no       no       yes           no
 *   MX8QM FlexCAN3  03.00.23.00    yes       yes        no       no       yes          yes
 *   MX8MP FlexCAN3  03.00.17.01    yes       yes        no      yes       yes          yes
 *   VF610 FlexCAN3  ?               no       yes        no      yes       yes?          no
 * LS1021A FlexCAN2  03.00.04.00     no       yes        no       no       yes           no
 * LX2160A FlexCAN3  03.00.23.00     no       yes        no      yes       yes          yes
 * MCF5441X FlexCAN2  ?               no       yes        no       no       yes           no     16
 *    MX25  FlexCAN2  03.00.00.00     no        no        no       no        no           no     64
 *    MX28  FlexCAN2  03.00.04.00    yes       yes        no       no        no           no     64
 *    MX35  FlexCAN2  03.00.00.00     no        no        no       no        no           no     64
 *    MX53  FlexCAN2  03.00.00.00    yes        no        no       no        no           no     64
 *    MX6s  FlexCAN3  10.00.12.00    yes       yes        no       no       yes           no     64
 *    MX8QM FlexCAN3  03.00.23.00    yes       yes        no       no       yes          yes     64
 *    MX8MP FlexCAN3  03.00.17.01    yes       yes        no      yes       yes          yes     64
 *    VF610 FlexCAN3  ?               no       yes        no      yes       yes?          no     64
 *  LS1021A FlexCAN2  03.00.04.00     no       yes        no       no       yes           no     64
 *  LX2160A FlexCAN3  03.00.23.00     no       yes        no      yes       yes          yes     64
 *
 * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
 */
@@ -246,6 +248,10 @@
#define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
/* Setup stop mode with SCU firmware to support wakeup */
#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11)
/* Setup 3 separate interrupts, main, boff and err */
#define FLEXCAN_QUIRK_NR_IRQ_3 BIT(12)
/* Setup 16 mailboxes */
#define FLEXCAN_QUIRK_NR_MB_16 BIT(13)

/* Structure of the message buffer */
struct flexcan_mb {
@@ -363,6 +369,9 @@ struct flexcan_priv {
	struct regulator *reg_xceiver;
	struct flexcan_stop_mode stm;

	int irq_boff;
	int irq_err;

	/* IPC handle when setup stop mode by System Controller firmware(scfw) */
	struct imx_sc_ipc *sc_ipc_handle;

@@ -371,6 +380,11 @@ struct flexcan_priv {
	void (*write)(u32 val, void __iomem *addr);
};

static const struct flexcan_devtype_data fsl_mcf5441x_devtype_data = {
	.quirks = FLEXCAN_QUIRK_BROKEN_PERR_STATE |
		FLEXCAN_QUIRK_NR_IRQ_3 | FLEXCAN_QUIRK_NR_MB_16,
};

static const struct flexcan_devtype_data fsl_p1010_devtype_data = {
	.quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE |
		FLEXCAN_QUIRK_BROKEN_PERR_STATE |
@@ -637,13 +651,17 @@ static int flexcan_clks_enable(const struct flexcan_priv *priv)
{
	int err;

	if (priv->clk_ipg) {
		err = clk_prepare_enable(priv->clk_ipg);
		if (err)
			return err;
	}

	if (priv->clk_per) {
		err = clk_prepare_enable(priv->clk_per);
		if (err)
			clk_disable_unprepare(priv->clk_ipg);
	}

	return err;
}
@@ -1404,6 +1422,10 @@ static int flexcan_rx_offload_setup(struct net_device *dev)
		priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
	else
		priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;

	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_NR_MB_16)
		priv->mb_count = 16;
	else
		priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
				 (sizeof(priv->regs->mb[1]) / priv->mb_size);

@@ -1777,6 +1799,18 @@ static int flexcan_open(struct net_device *dev)
	if (err)
		goto out_can_rx_offload_disable;

	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
		err = request_irq(priv->irq_boff,
				  flexcan_irq, IRQF_SHARED, dev->name, dev);
		if (err)
			goto out_free_irq;

		err = request_irq(priv->irq_err,
				  flexcan_irq, IRQF_SHARED, dev->name, dev);
		if (err)
			goto out_free_irq_boff;
	}

	flexcan_chip_interrupts_enable(dev);

	can_led_event(dev, CAN_LED_EVENT_OPEN);
@@ -1785,6 +1819,10 @@ static int flexcan_open(struct net_device *dev)

	return 0;

 out_free_irq_boff:
	free_irq(priv->irq_boff, dev);
 out_free_irq:
	free_irq(dev->irq, dev);
 out_can_rx_offload_disable:
	can_rx_offload_disable(&priv->offload);
	flexcan_chip_stop(dev);
@@ -1806,6 +1844,12 @@ static int flexcan_close(struct net_device *dev)

	netif_stop_queue(dev);
	flexcan_chip_interrupts_disable(dev);

	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
		free_irq(priv->irq_err, dev);
		free_irq(priv->irq_boff, dev);
	}

	free_irq(dev->irq, dev);
	can_rx_offload_disable(&priv->offload);
	flexcan_chip_stop_disable_on_error(dev);
@@ -2042,14 +2086,26 @@ static const struct of_device_id flexcan_of_match[] = {
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);

static const struct platform_device_id flexcan_id_table[] = {
	{
		.name = "flexcan-mcf5441x",
		.driver_data = (kernel_ulong_t)&fsl_mcf5441x_devtype_data,
	}, {
		/* sentinel */
	},
};
MODULE_DEVICE_TABLE(platform, flexcan_id_table);

static int flexcan_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id;
	const struct flexcan_devtype_data *devtype_data;
	struct net_device *dev;
	struct flexcan_priv *priv;
	struct regulator *reg_xceiver;
	struct clk *clk_ipg = NULL, *clk_per = NULL;
	struct flexcan_regs __iomem *regs;
	struct flexcan_platform_data *pdata;
	int err, irq;
	u8 clk_src = 1;
	u32 clock_freq = 0;
@@ -2067,6 +2123,12 @@ static int flexcan_probe(struct platform_device *pdev)
				     "clock-frequency", &clock_freq);
		of_property_read_u8(pdev->dev.of_node,
				    "fsl,clk-source", &clk_src);
	} else {
		pdata = dev_get_platdata(&pdev->dev);
		if (pdata) {
			clock_freq = pdata->clock_frequency;
			clk_src = pdata->clk_src;
		}
	}

	if (!clock_freq) {
@@ -2092,7 +2154,14 @@ static int flexcan_probe(struct platform_device *pdev)
	if (IS_ERR(regs))
		return PTR_ERR(regs);

	devtype_data = of_device_get_match_data(&pdev->dev);
	of_id = of_match_device(flexcan_of_match, &pdev->dev);
	if (of_id)
		devtype_data = of_id->data;
	else if (platform_get_device_id(pdev)->driver_data)
		devtype_data = (struct flexcan_devtype_data *)
			platform_get_device_id(pdev)->driver_data;
	else
		return -ENODEV;

	if ((devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) &&
	    !(devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)) {
@@ -2136,6 +2205,19 @@ static int flexcan_probe(struct platform_device *pdev)
	priv->devtype_data = devtype_data;
	priv->reg_xceiver = reg_xceiver;

	if (devtype_data->quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
		priv->irq_boff = platform_get_irq(pdev, 1);
		if (priv->irq_boff <= 0) {
			err = -ENODEV;
			goto failed_platform_get_irq;
		}
		priv->irq_err = platform_get_irq(pdev, 2);
		if (priv->irq_err <= 0) {
			err = -ENODEV;
			goto failed_platform_get_irq;
		}
	}

	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) {
		priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD |
			CAN_CTRLMODE_FD_NON_ISO;
@@ -2173,6 +2255,7 @@ static int flexcan_probe(struct platform_device *pdev)
 failed_register:
	pm_runtime_put_noidle(&pdev->dev);
	pm_runtime_disable(&pdev->dev);
 failed_platform_get_irq:
	free_candev(dev);
	return err;
}
@@ -2325,6 +2408,7 @@ static struct platform_driver flexcan_driver = {
	},
	.probe = flexcan_probe,
	.remove = flexcan_remove,
	.id_table = flexcan_id_table,
};

module_platform_driver(flexcan_driver);