diff options
Diffstat (limited to 'drivers/net/can/flexcan.c')
| -rw-r--r-- | drivers/net/can/flexcan.c | 218 | 
1 files changed, 152 insertions, 66 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 71c677e651d..f425ec2c783 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -62,7 +62,7 @@  #define FLEXCAN_MCR_BCC			BIT(16)  #define FLEXCAN_MCR_LPRIO_EN		BIT(13)  #define FLEXCAN_MCR_AEN			BIT(12) -#define FLEXCAN_MCR_MAXMB(x)		((x) & 0xf) +#define FLEXCAN_MCR_MAXMB(x)		((x) & 0x1f)  #define FLEXCAN_MCR_IDAM_A		(0 << 8)  #define FLEXCAN_MCR_IDAM_B		(1 << 8)  #define FLEXCAN_MCR_IDAM_C		(2 << 8) @@ -144,6 +144,8 @@  #define FLEXCAN_MB_CODE_MASK		(0xf0ffffff) +#define FLEXCAN_TIMEOUT_US             (50) +  /*   * FLEXCAN hardware feature flags   * @@ -235,9 +237,12 @@ static const struct can_bittiming_const flexcan_bittiming_const = {  };  /* - * Abstract off the read/write for arm versus ppc. + * Abstract off the read/write for arm versus ppc. This + * assumes that PPC uses big-endian registers and everything + * else uses little-endian registers, independent of CPU + * endianess.   */ -#if defined(__BIG_ENDIAN) +#if defined(CONFIG_PPC)  static inline u32 flexcan_read(void __iomem *addr)  {  	return in_be32(addr); @@ -259,6 +264,22 @@ static inline void flexcan_write(u32 val, void __iomem *addr)  }  #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,  					      u32 reg_esr)  { @@ -266,26 +287,95 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,  		(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; +	unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;  	u32 reg;  	reg = flexcan_read(®s->mcr);  	reg &= ~FLEXCAN_MCR_MDIS;  	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; +	unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;  	u32 reg;  	reg = flexcan_read(®s->mcr);  	reg |= FLEXCAN_MCR_MDIS;  	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, @@ -702,24 +792,18 @@ static int flexcan_chip_start(struct net_device *dev)  {  	struct flexcan_priv *priv = netdev_priv(dev);  	struct flexcan_regs __iomem *regs = priv->base; -	unsigned int i;  	int err;  	u32 reg_mcr, reg_ctrl;  	/* enable module */ -	flexcan_chip_enable(priv); +	err = flexcan_chip_enable(priv); +	if (err) +		return err;  	/* 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); @@ -736,9 +820,11 @@ static int flexcan_chip_start(struct net_device *dev)  	 *  	 */  	reg_mcr = flexcan_read(®s->mcr); +	reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);  	reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |  		FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | -		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS; +		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | +		FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);  	netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);  	flexcan_write(reg_mcr, ®s->mcr); @@ -772,16 +858,9 @@ static int flexcan_chip_start(struct net_device *dev)  	netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);  	flexcan_write(reg_ctrl, ®s->ctrl); -	for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) { -		flexcan_write(0, ®s->cantxfg[i].can_ctrl); -		flexcan_write(0, ®s->cantxfg[i].can_id); -		flexcan_write(0, ®s->cantxfg[i].data[0]); -		flexcan_write(0, ®s->cantxfg[i].data[1]); - -		/* put MB into rx queue */ -		flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), -			®s->cantxfg[i].can_ctrl); -	} +	/* Abort any pending TX, mark Mailbox as INACTIVE */ +	flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), +		      ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);  	/* acceptance mask/acceptance code (accept everything) */  	flexcan_write(0x0, ®s->rxgmask); @@ -791,16 +870,14 @@ static int flexcan_chip_start(struct net_device *dev)  	if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)  		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 */ -	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; @@ -813,7 +890,9 @@ static int flexcan_chip_start(struct net_device *dev)  	return 0; - out: + out_transceiver_disable: +	flexcan_transceiver_disable(priv); + out_chip_disable:  	flexcan_chip_disable(priv);  	return err;  } @@ -828,18 +907,17 @@ static void flexcan_chip_stop(struct net_device *dev)  {  	struct flexcan_priv *priv = netdev_priv(dev);  	struct flexcan_regs __iomem *regs = priv->base; -	u32 reg; + +	/* freeze + disable module */ +	flexcan_chip_freeze(priv); +	flexcan_chip_disable(priv);  	/* Disable all interrupts */  	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;  	return; @@ -869,7 +947,7 @@ static int flexcan_open(struct net_device *dev)  	/* start chip and queuing */  	err = flexcan_chip_start(dev);  	if (err) -		goto out_close; +		goto out_free_irq;  	can_led_event(dev, CAN_LED_EVENT_OPEN); @@ -878,6 +956,8 @@ static int flexcan_open(struct net_device *dev)  	return 0; + out_free_irq: +	free_irq(dev->irq, dev);   out_close:  	close_candev(dev);   out_disable_per: @@ -931,6 +1011,7 @@ static const struct net_device_ops flexcan_netdev_ops = {  	.ndo_open	= flexcan_open,  	.ndo_stop	= flexcan_close,  	.ndo_start_xmit	= flexcan_start_xmit, +	.ndo_change_mtu = can_change_mtu,  };  static int register_flexcandev(struct net_device *dev) @@ -948,12 +1029,16 @@ static int register_flexcandev(struct net_device *dev)  		goto out_disable_ipg;  	/* 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_CTRL_CLK_SRC;  	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 */  	reg = flexcan_read(®s->mcr); @@ -970,14 +1055,15 @@ static int register_flexcandev(struct net_device *dev)  	if (!(reg & FLEXCAN_MCR_FEN)) {  		netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");  		err = -ENODEV; -		goto out_disable_per; +		goto out_chip_disable;  	}  	err = register_candev(dev); - out_disable_per:  	/* disable core and turn off clocks */ + out_chip_disable:  	flexcan_chip_disable(priv); + out_disable_per:  	clk_disable_unprepare(priv->clk_per);   out_disable_ipg:  	clk_disable_unprepare(priv->clk_ipg); @@ -991,9 +1077,9 @@ static void unregister_flexcandev(struct net_device *dev)  }  static const struct of_device_id flexcan_of_match[] = { -	{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, }, -	{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },  	{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, +	{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, +	{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },  	{ /* sentinel */ },  };  MODULE_DEVICE_TABLE(of, flexcan_of_match); @@ -1026,13 +1112,13 @@ static int flexcan_probe(struct platform_device *pdev)  			dev_err(&pdev->dev, "no ipg clock defined\n");  			return PTR_ERR(clk_ipg);  		} -		clock_freq = clk_get_rate(clk_ipg);  		clk_per = devm_clk_get(&pdev->dev, "per");  		if (IS_ERR(clk_per)) {  			dev_err(&pdev->dev, "no per clock defined\n");  			return PTR_ERR(clk_per);  		} +		clock_freq = clk_get_rate(clk_per);  	}  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1047,9 +1133,9 @@ static int flexcan_probe(struct platform_device *pdev)  	of_id = of_match_device(flexcan_of_match, &pdev->dev);  	if (of_id) {  		devtype_data = of_id->data; -	} else if (pdev->id_entry->driver_data) { +	} else if (platform_get_device_id(pdev)->driver_data) {  		devtype_data = (struct flexcan_devtype_data *) -			pdev->id_entry->driver_data; +			platform_get_device_id(pdev)->driver_data;  	} else {  		return -ENODEV;  	} @@ -1074,7 +1160,7 @@ static int flexcan_probe(struct platform_device *pdev)  	priv->dev = dev;  	priv->clk_ipg = clk_ipg;  	priv->clk_per = clk_per; -	priv->pdata = pdev->dev.platform_data; +	priv->pdata = dev_get_platdata(&pdev->dev);  	priv->devtype_data = devtype_data;  	priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); @@ -1107,21 +1193,24 @@ static int flexcan_probe(struct platform_device *pdev)  static int flexcan_remove(struct platform_device *pdev)  {  	struct net_device *dev = platform_get_drvdata(pdev); +	struct flexcan_priv *priv = netdev_priv(dev);  	unregister_flexcandev(dev); - +	netif_napi_del(&priv->napi);  	free_candev(dev);  	return 0;  } -#ifdef CONFIG_PM_SLEEP -static int flexcan_suspend(struct device *device) +static int __maybe_unused flexcan_suspend(struct device *device)  {  	struct net_device *dev = dev_get_drvdata(device);  	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)) {  		netif_stop_queue(dev); @@ -1132,7 +1221,7 @@ static int flexcan_suspend(struct device *device)  	return 0;  } -static int flexcan_resume(struct device *device) +static int __maybe_unused flexcan_resume(struct device *device)  {  	struct net_device *dev = dev_get_drvdata(device);  	struct flexcan_priv *priv = netdev_priv(dev); @@ -1142,11 +1231,8 @@ static int flexcan_resume(struct device *device)  		netif_device_attach(dev);  		netif_start_queue(dev);  	} -	flexcan_chip_enable(priv); - -	return 0; +	return flexcan_chip_enable(priv);  } -#endif /* CONFIG_PM_SLEEP */  static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);  | 
