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

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.orgSigned-off-by: default avatarAngelo Dureghello <angelo@kernel-space.org>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 896e7f3e
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/can/platform/flexcan.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -208,18 +209,19 @@ ...@@ -208,18 +209,19 @@
/* FLEXCAN hardware feature flags /* FLEXCAN hardware feature flags
* *
* Below is some version info we got: * 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? * Filter? connected? Passive detection ption in MB Supported?
* MX25 FlexCAN2 03.00.00.00 no no no no no no * MCF5441X FlexCAN2 ? no yes no no yes no 16
* MX28 FlexCAN2 03.00.04.00 yes yes no no no no * MX25 FlexCAN2 03.00.00.00 no no no no no no 64
* MX35 FlexCAN2 03.00.00.00 no no no no no no * MX28 FlexCAN2 03.00.04.00 yes yes no no no no 64
* MX53 FlexCAN2 03.00.00.00 yes no no no no no * MX35 FlexCAN2 03.00.00.00 no no no no no no 64
* MX6s FlexCAN3 10.00.12.00 yes yes no no yes no * MX53 FlexCAN2 03.00.00.00 yes no no no no no 64
* MX8QM FlexCAN3 03.00.23.00 yes yes no no yes yes * MX6s FlexCAN3 10.00.12.00 yes yes no no yes no 64
* MX8MP FlexCAN3 03.00.17.01 yes yes no yes yes yes * MX8QM FlexCAN3 03.00.23.00 yes yes no no yes yes 64
* VF610 FlexCAN3 ? no yes no yes yes? no * MX8MP FlexCAN3 03.00.17.01 yes yes no yes yes yes 64
* LS1021A FlexCAN2 03.00.04.00 no yes no no yes no * VF610 FlexCAN3 ? no yes no yes yes? no 64
* LX2160A FlexCAN3 03.00.23.00 no yes no yes yes yes * 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. * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
*/ */
...@@ -246,6 +248,10 @@ ...@@ -246,6 +248,10 @@
#define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10) #define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
/* Setup stop mode with SCU firmware to support wakeup */ /* Setup stop mode with SCU firmware to support wakeup */
#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11) #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 */ /* Structure of the message buffer */
struct flexcan_mb { struct flexcan_mb {
...@@ -363,6 +369,9 @@ struct flexcan_priv { ...@@ -363,6 +369,9 @@ struct flexcan_priv {
struct regulator *reg_xceiver; struct regulator *reg_xceiver;
struct flexcan_stop_mode stm; struct flexcan_stop_mode stm;
int irq_boff;
int irq_err;
/* IPC handle when setup stop mode by System Controller firmware(scfw) */ /* IPC handle when setup stop mode by System Controller firmware(scfw) */
struct imx_sc_ipc *sc_ipc_handle; struct imx_sc_ipc *sc_ipc_handle;
...@@ -371,6 +380,11 @@ struct flexcan_priv { ...@@ -371,6 +380,11 @@ struct flexcan_priv {
void (*write)(u32 val, void __iomem *addr); 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 = { static const struct flexcan_devtype_data fsl_p1010_devtype_data = {
.quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE | .quirks = FLEXCAN_QUIRK_BROKEN_WERR_STATE |
FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
...@@ -637,13 +651,17 @@ static int flexcan_clks_enable(const struct flexcan_priv *priv) ...@@ -637,13 +651,17 @@ static int flexcan_clks_enable(const struct flexcan_priv *priv)
{ {
int err; int err;
if (priv->clk_ipg) {
err = clk_prepare_enable(priv->clk_ipg); err = clk_prepare_enable(priv->clk_ipg);
if (err) if (err)
return err; return err;
}
if (priv->clk_per) {
err = clk_prepare_enable(priv->clk_per); err = clk_prepare_enable(priv->clk_per);
if (err) if (err)
clk_disable_unprepare(priv->clk_ipg); clk_disable_unprepare(priv->clk_ipg);
}
return err; return err;
} }
...@@ -1404,6 +1422,10 @@ static int flexcan_rx_offload_setup(struct net_device *dev) ...@@ -1404,6 +1422,10 @@ static int flexcan_rx_offload_setup(struct net_device *dev)
priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN; priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
else else
priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN; 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) + priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
(sizeof(priv->regs->mb[1]) / priv->mb_size); (sizeof(priv->regs->mb[1]) / priv->mb_size);
...@@ -1777,6 +1799,18 @@ static int flexcan_open(struct net_device *dev) ...@@ -1777,6 +1799,18 @@ static int flexcan_open(struct net_device *dev)
if (err) if (err)
goto out_can_rx_offload_disable; 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); flexcan_chip_interrupts_enable(dev);
can_led_event(dev, CAN_LED_EVENT_OPEN); can_led_event(dev, CAN_LED_EVENT_OPEN);
...@@ -1785,6 +1819,10 @@ static int flexcan_open(struct net_device *dev) ...@@ -1785,6 +1819,10 @@ static int flexcan_open(struct net_device *dev)
return 0; 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: out_can_rx_offload_disable:
can_rx_offload_disable(&priv->offload); can_rx_offload_disable(&priv->offload);
flexcan_chip_stop(dev); flexcan_chip_stop(dev);
...@@ -1806,6 +1844,12 @@ static int flexcan_close(struct net_device *dev) ...@@ -1806,6 +1844,12 @@ static int flexcan_close(struct net_device *dev)
netif_stop_queue(dev); netif_stop_queue(dev);
flexcan_chip_interrupts_disable(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); free_irq(dev->irq, dev);
can_rx_offload_disable(&priv->offload); can_rx_offload_disable(&priv->offload);
flexcan_chip_stop_disable_on_error(dev); flexcan_chip_stop_disable_on_error(dev);
...@@ -2042,14 +2086,26 @@ static const struct of_device_id flexcan_of_match[] = { ...@@ -2042,14 +2086,26 @@ static const struct of_device_id flexcan_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, 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) static int flexcan_probe(struct platform_device *pdev)
{ {
const struct of_device_id *of_id;
const struct flexcan_devtype_data *devtype_data; const struct flexcan_devtype_data *devtype_data;
struct net_device *dev; struct net_device *dev;
struct flexcan_priv *priv; struct flexcan_priv *priv;
struct regulator *reg_xceiver; struct regulator *reg_xceiver;
struct clk *clk_ipg = NULL, *clk_per = NULL; struct clk *clk_ipg = NULL, *clk_per = NULL;
struct flexcan_regs __iomem *regs; struct flexcan_regs __iomem *regs;
struct flexcan_platform_data *pdata;
int err, irq; int err, irq;
u8 clk_src = 1; u8 clk_src = 1;
u32 clock_freq = 0; u32 clock_freq = 0;
...@@ -2067,6 +2123,12 @@ static int flexcan_probe(struct platform_device *pdev) ...@@ -2067,6 +2123,12 @@ static int flexcan_probe(struct platform_device *pdev)
"clock-frequency", &clock_freq); "clock-frequency", &clock_freq);
of_property_read_u8(pdev->dev.of_node, of_property_read_u8(pdev->dev.of_node,
"fsl,clk-source", &clk_src); "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) { if (!clock_freq) {
...@@ -2092,7 +2154,14 @@ static int flexcan_probe(struct platform_device *pdev) ...@@ -2092,7 +2154,14 @@ static int flexcan_probe(struct platform_device *pdev)
if (IS_ERR(regs)) if (IS_ERR(regs))
return PTR_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) && if ((devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) &&
!(devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)) { !(devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)) {
...@@ -2136,6 +2205,19 @@ static int flexcan_probe(struct platform_device *pdev) ...@@ -2136,6 +2205,19 @@ static int flexcan_probe(struct platform_device *pdev)
priv->devtype_data = devtype_data; priv->devtype_data = devtype_data;
priv->reg_xceiver = reg_xceiver; 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) { if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) {
priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD | priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD |
CAN_CTRLMODE_FD_NON_ISO; CAN_CTRLMODE_FD_NON_ISO;
...@@ -2173,6 +2255,7 @@ static int flexcan_probe(struct platform_device *pdev) ...@@ -2173,6 +2255,7 @@ static int flexcan_probe(struct platform_device *pdev)
failed_register: failed_register:
pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
failed_platform_get_irq:
free_candev(dev); free_candev(dev);
return err; return err;
} }
...@@ -2325,6 +2408,7 @@ static struct platform_driver flexcan_driver = { ...@@ -2325,6 +2408,7 @@ static struct platform_driver flexcan_driver = {
}, },
.probe = flexcan_probe, .probe = flexcan_probe,
.remove = flexcan_remove, .remove = flexcan_remove,
.id_table = flexcan_id_table,
}; };
module_platform_driver(flexcan_driver); module_platform_driver(flexcan_driver);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment