|
@@ -144,6 +144,8 @@
|
|
|
|
|
|
|
|
#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)
|
|
#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)
|
|
|
|
|
|
|
|
|
|
+#define FLEXCAN_TIMEOUT_US (50)
|
|
|
|
|
+
|
|
|
/*
|
|
/*
|
|
|
* FLEXCAN hardware feature flags
|
|
* FLEXCAN hardware feature flags
|
|
|
*
|
|
*
|
|
@@ -262,6 +264,22 @@ static inline void flexcan_write(u32 val, void __iomem *addr)
|
|
|
}
|
|
}
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
|
|
|
|
|
+{
|
|
|
|
|
+ if (!priv->reg_xceiver)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
|
+ return regulator_enable(priv->reg_xceiver);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static inline int flexcan_transceiver_disable(const struct flexcan_priv *priv)
|
|
|
|
|
+{
|
|
|
|
|
+ if (!priv->reg_xceiver)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
|
+ return regulator_disable(priv->reg_xceiver);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
|
|
static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
|
|
|
u32 reg_esr)
|
|
u32 reg_esr)
|
|
|
{
|
|
{
|
|
@@ -269,26 +287,95 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
|
|
|
(reg_esr & FLEXCAN_ESR_ERR_BUS);
|
|
(reg_esr & FLEXCAN_ESR_ERR_BUS);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static inline void flexcan_chip_enable(struct flexcan_priv *priv)
|
|
|
|
|
|
|
+static int flexcan_chip_enable(struct flexcan_priv *priv)
|
|
|
{
|
|
{
|
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
|
|
|
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
|
|
u32 reg;
|
|
u32 reg;
|
|
|
|
|
|
|
|
reg = flexcan_read(®s->mcr);
|
|
reg = flexcan_read(®s->mcr);
|
|
|
reg &= ~FLEXCAN_MCR_MDIS;
|
|
reg &= ~FLEXCAN_MCR_MDIS;
|
|
|
flexcan_write(reg, ®s->mcr);
|
|
flexcan_write(reg, ®s->mcr);
|
|
|
|
|
|
|
|
- udelay(10);
|
|
|
|
|
|
|
+ while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
|
|
|
|
|
+ usleep_range(10, 20);
|
|
|
|
|
+
|
|
|
|
|
+ if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)
|
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static inline void flexcan_chip_disable(struct flexcan_priv *priv)
|
|
|
|
|
|
|
+static int flexcan_chip_disable(struct flexcan_priv *priv)
|
|
|
{
|
|
{
|
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
|
|
|
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
|
|
u32 reg;
|
|
u32 reg;
|
|
|
|
|
|
|
|
reg = flexcan_read(®s->mcr);
|
|
reg = flexcan_read(®s->mcr);
|
|
|
reg |= FLEXCAN_MCR_MDIS;
|
|
reg |= FLEXCAN_MCR_MDIS;
|
|
|
flexcan_write(reg, ®s->mcr);
|
|
flexcan_write(reg, ®s->mcr);
|
|
|
|
|
+
|
|
|
|
|
+ while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
|
|
|
|
|
+ usleep_range(10, 20);
|
|
|
|
|
+
|
|
|
|
|
+ if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
|
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int flexcan_chip_freeze(struct flexcan_priv *priv)
|
|
|
|
|
+{
|
|
|
|
|
+ struct flexcan_regs __iomem *regs = priv->base;
|
|
|
|
|
+ unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
|
|
|
|
|
+ u32 reg;
|
|
|
|
|
+
|
|
|
|
|
+ reg = flexcan_read(®s->mcr);
|
|
|
|
|
+ reg |= FLEXCAN_MCR_HALT;
|
|
|
|
|
+ flexcan_write(reg, ®s->mcr);
|
|
|
|
|
+
|
|
|
|
|
+ while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
|
|
|
|
|
+ usleep_range(100, 200);
|
|
|
|
|
+
|
|
|
|
|
+ if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
|
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
|
|
|
|
|
+{
|
|
|
|
|
+ struct flexcan_regs __iomem *regs = priv->base;
|
|
|
|
|
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
|
|
|
|
+ u32 reg;
|
|
|
|
|
+
|
|
|
|
|
+ reg = flexcan_read(®s->mcr);
|
|
|
|
|
+ reg &= ~FLEXCAN_MCR_HALT;
|
|
|
|
|
+ flexcan_write(reg, ®s->mcr);
|
|
|
|
|
+
|
|
|
|
|
+ while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
|
|
|
|
|
+ usleep_range(10, 20);
|
|
|
|
|
+
|
|
|
|
|
+ if (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)
|
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int flexcan_chip_softreset(struct flexcan_priv *priv)
|
|
|
|
|
+{
|
|
|
|
|
+ struct flexcan_regs __iomem *regs = priv->base;
|
|
|
|
|
+ unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
|
|
|
|
|
+
|
|
|
|
|
+ flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
|
|
|
|
|
+ while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST))
|
|
|
|
|
+ usleep_range(10, 20);
|
|
|
|
|
+
|
|
|
|
|
+ if (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST)
|
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int flexcan_get_berr_counter(const struct net_device *dev,
|
|
static int flexcan_get_berr_counter(const struct net_device *dev,
|
|
@@ -709,19 +796,14 @@ static int flexcan_chip_start(struct net_device *dev)
|
|
|
u32 reg_mcr, reg_ctrl;
|
|
u32 reg_mcr, reg_ctrl;
|
|
|
|
|
|
|
|
/* enable module */
|
|
/* enable module */
|
|
|
- flexcan_chip_enable(priv);
|
|
|
|
|
|
|
+ err = flexcan_chip_enable(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ return err;
|
|
|
|
|
|
|
|
/* soft reset */
|
|
/* soft reset */
|
|
|
- flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
|
|
|
|
|
- udelay(10);
|
|
|
|
|
-
|
|
|
|
|
- reg_mcr = flexcan_read(®s->mcr);
|
|
|
|
|
- if (reg_mcr & FLEXCAN_MCR_SOFTRST) {
|
|
|
|
|
- netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n",
|
|
|
|
|
- reg_mcr);
|
|
|
|
|
- err = -ENODEV;
|
|
|
|
|
- goto out;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ err = flexcan_chip_softreset(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ goto out_chip_disable;
|
|
|
|
|
|
|
|
flexcan_set_bittiming(dev);
|
|
flexcan_set_bittiming(dev);
|
|
|
|
|
|
|
@@ -788,16 +870,14 @@ static int flexcan_chip_start(struct net_device *dev)
|
|
|
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
|
|
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
|
|
|
flexcan_write(0x0, ®s->rxfgmask);
|
|
flexcan_write(0x0, ®s->rxfgmask);
|
|
|
|
|
|
|
|
- if (priv->reg_xceiver) {
|
|
|
|
|
- err = regulator_enable(priv->reg_xceiver);
|
|
|
|
|
- if (err)
|
|
|
|
|
- goto out;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ err = flexcan_transceiver_enable(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ goto out_chip_disable;
|
|
|
|
|
|
|
|
/* synchronize with the can bus */
|
|
/* synchronize with the can bus */
|
|
|
- reg_mcr = flexcan_read(®s->mcr);
|
|
|
|
|
- reg_mcr &= ~FLEXCAN_MCR_HALT;
|
|
|
|
|
- flexcan_write(reg_mcr, ®s->mcr);
|
|
|
|
|
|
|
+ err = flexcan_chip_unfreeze(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ goto out_transceiver_disable;
|
|
|
|
|
|
|
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
|
|
|
|
|
|
@@ -810,7 +890,9 @@ static int flexcan_chip_start(struct net_device *dev)
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
- out:
|
|
|
|
|
|
|
+ out_transceiver_disable:
|
|
|
|
|
+ flexcan_transceiver_disable(priv);
|
|
|
|
|
+ out_chip_disable:
|
|
|
flexcan_chip_disable(priv);
|
|
flexcan_chip_disable(priv);
|
|
|
return err;
|
|
return err;
|
|
|
}
|
|
}
|
|
@@ -825,18 +907,17 @@ static void flexcan_chip_stop(struct net_device *dev)
|
|
|
{
|
|
{
|
|
|
struct flexcan_priv *priv = netdev_priv(dev);
|
|
struct flexcan_priv *priv = netdev_priv(dev);
|
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
struct flexcan_regs __iomem *regs = priv->base;
|
|
|
- u32 reg;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ /* freeze + disable module */
|
|
|
|
|
+ flexcan_chip_freeze(priv);
|
|
|
|
|
+ flexcan_chip_disable(priv);
|
|
|
|
|
|
|
|
/* Disable all interrupts */
|
|
/* Disable all interrupts */
|
|
|
flexcan_write(0, ®s->imask1);
|
|
flexcan_write(0, ®s->imask1);
|
|
|
|
|
+ flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
|
|
|
|
|
+ ®s->ctrl);
|
|
|
|
|
|
|
|
- /* Disable + halt module */
|
|
|
|
|
- reg = flexcan_read(®s->mcr);
|
|
|
|
|
- reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
|
|
|
|
|
- flexcan_write(reg, ®s->mcr);
|
|
|
|
|
-
|
|
|
|
|
- if (priv->reg_xceiver)
|
|
|
|
|
- regulator_disable(priv->reg_xceiver);
|
|
|
|
|
|
|
+ flexcan_transceiver_disable(priv);
|
|
|
priv->can.state = CAN_STATE_STOPPED;
|
|
priv->can.state = CAN_STATE_STOPPED;
|
|
|
|
|
|
|
|
return;
|
|
return;
|
|
@@ -866,7 +947,7 @@ static int flexcan_open(struct net_device *dev)
|
|
|
/* start chip and queuing */
|
|
/* start chip and queuing */
|
|
|
err = flexcan_chip_start(dev);
|
|
err = flexcan_chip_start(dev);
|
|
|
if (err)
|
|
if (err)
|
|
|
- goto out_close;
|
|
|
|
|
|
|
+ goto out_free_irq;
|
|
|
|
|
|
|
|
can_led_event(dev, CAN_LED_EVENT_OPEN);
|
|
can_led_event(dev, CAN_LED_EVENT_OPEN);
|
|
|
|
|
|
|
@@ -875,6 +956,8 @@ static int flexcan_open(struct net_device *dev)
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
+ out_free_irq:
|
|
|
|
|
+ free_irq(dev->irq, dev);
|
|
|
out_close:
|
|
out_close:
|
|
|
close_candev(dev);
|
|
close_candev(dev);
|
|
|
out_disable_per:
|
|
out_disable_per:
|
|
@@ -945,12 +1028,16 @@ static int register_flexcandev(struct net_device *dev)
|
|
|
goto out_disable_ipg;
|
|
goto out_disable_ipg;
|
|
|
|
|
|
|
|
/* select "bus clock", chip must be disabled */
|
|
/* select "bus clock", chip must be disabled */
|
|
|
- flexcan_chip_disable(priv);
|
|
|
|
|
|
|
+ err = flexcan_chip_disable(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ goto out_disable_per;
|
|
|
reg = flexcan_read(®s->ctrl);
|
|
reg = flexcan_read(®s->ctrl);
|
|
|
reg |= FLEXCAN_CTRL_CLK_SRC;
|
|
reg |= FLEXCAN_CTRL_CLK_SRC;
|
|
|
flexcan_write(reg, ®s->ctrl);
|
|
flexcan_write(reg, ®s->ctrl);
|
|
|
|
|
|
|
|
- flexcan_chip_enable(priv);
|
|
|
|
|
|
|
+ err = flexcan_chip_enable(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ goto out_chip_disable;
|
|
|
|
|
|
|
|
/* set freeze, halt and activate FIFO, restrict register access */
|
|
/* set freeze, halt and activate FIFO, restrict register access */
|
|
|
reg = flexcan_read(®s->mcr);
|
|
reg = flexcan_read(®s->mcr);
|
|
@@ -967,14 +1054,15 @@ static int register_flexcandev(struct net_device *dev)
|
|
|
if (!(reg & FLEXCAN_MCR_FEN)) {
|
|
if (!(reg & FLEXCAN_MCR_FEN)) {
|
|
|
netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
|
|
netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
|
|
|
err = -ENODEV;
|
|
err = -ENODEV;
|
|
|
- goto out_disable_per;
|
|
|
|
|
|
|
+ goto out_chip_disable;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
err = register_candev(dev);
|
|
err = register_candev(dev);
|
|
|
|
|
|
|
|
- out_disable_per:
|
|
|
|
|
/* disable core and turn off clocks */
|
|
/* disable core and turn off clocks */
|
|
|
|
|
+ out_chip_disable:
|
|
|
flexcan_chip_disable(priv);
|
|
flexcan_chip_disable(priv);
|
|
|
|
|
+ out_disable_per:
|
|
|
clk_disable_unprepare(priv->clk_per);
|
|
clk_disable_unprepare(priv->clk_per);
|
|
|
out_disable_ipg:
|
|
out_disable_ipg:
|
|
|
clk_disable_unprepare(priv->clk_ipg);
|
|
clk_disable_unprepare(priv->clk_ipg);
|
|
@@ -1104,9 +1192,10 @@ static int flexcan_probe(struct platform_device *pdev)
|
|
|
static int flexcan_remove(struct platform_device *pdev)
|
|
static int flexcan_remove(struct platform_device *pdev)
|
|
|
{
|
|
{
|
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
|
|
|
+ struct flexcan_priv *priv = netdev_priv(dev);
|
|
|
|
|
|
|
|
unregister_flexcandev(dev);
|
|
unregister_flexcandev(dev);
|
|
|
-
|
|
|
|
|
|
|
+ netif_napi_del(&priv->napi);
|
|
|
free_candev(dev);
|
|
free_candev(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -1117,8 +1206,11 @@ static int flexcan_suspend(struct device *device)
|
|
|
{
|
|
{
|
|
|
struct net_device *dev = dev_get_drvdata(device);
|
|
struct net_device *dev = dev_get_drvdata(device);
|
|
|
struct flexcan_priv *priv = netdev_priv(dev);
|
|
struct flexcan_priv *priv = netdev_priv(dev);
|
|
|
|
|
+ int err;
|
|
|
|
|
|
|
|
- flexcan_chip_disable(priv);
|
|
|
|
|
|
|
+ err = flexcan_chip_disable(priv);
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ return err;
|
|
|
|
|
|
|
|
if (netif_running(dev)) {
|
|
if (netif_running(dev)) {
|
|
|
netif_stop_queue(dev);
|
|
netif_stop_queue(dev);
|
|
@@ -1139,9 +1231,7 @@ static int flexcan_resume(struct device *device)
|
|
|
netif_device_attach(dev);
|
|
netif_device_attach(dev);
|
|
|
netif_start_queue(dev);
|
|
netif_start_queue(dev);
|
|
|
}
|
|
}
|
|
|
- flexcan_chip_enable(priv);
|
|
|
|
|
-
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ return flexcan_chip_enable(priv);
|
|
|
}
|
|
}
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
|
|