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: Angelo Dureghello <angelo@kernel-space.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Angelo Dureghello 2021-07-02 11:48:41 +02:00 коммит произвёл Marc Kleine-Budde
Родитель 896e7f3e74
Коммит d9cead75b1
1 изменённых файлов: 104 добавлений и 20 удалений

Просмотреть файл

@ -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;
err = clk_prepare_enable(priv->clk_ipg);
if (err)
return err;
if (priv->clk_ipg) {
err = clk_prepare_enable(priv->clk_ipg);
if (err)
return err;
}
err = clk_prepare_enable(priv->clk_per);
if (err)
clk_disable_unprepare(priv->clk_ipg);
if (priv->clk_per) {
err = clk_prepare_enable(priv->clk_per);
if (err)
clk_disable_unprepare(priv->clk_ipg);
}
return err;
}
@ -1404,8 +1422,12 @@ 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;
priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
(sizeof(priv->regs->mb[1]) / priv->mb_size);
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);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
priv->tx_mb_reserved =
@ -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);