Merge tag 'kgdb-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt...
[linux-2.6-microblaze.git] / drivers / net / can / flexcan.c
index 57f3635..7734229 100644 (file)
@@ -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>
 /* 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.
  */
 #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 |
@@ -635,15 +649,19 @@ static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
 
 static int flexcan_clks_enable(const struct flexcan_priv *priv)
 {
-       int err;
+       int err = 0;
 
-       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;
 }
@@ -1198,6 +1216,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                }
        }
 
+       if (handled)
+               can_rx_offload_irq_finish(&priv->offload);
+
        return handled;
 }
 
@@ -1401,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 =
@@ -1774,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);
@@ -1782,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);
@@ -1803,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);
@@ -2039,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;
@@ -2064,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) {
@@ -2089,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)) {
@@ -2133,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;
@@ -2170,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;
 }
@@ -2322,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);