diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-03-12 11:40:15 -0700 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-03-12 11:40:15 -0700 | 
| commit | 192c028b6ac972df25fd624f94a94d038fbdb66c (patch) | |
| tree | 4dd9d13ffd239e4d7c61401f892989742c671fa8 /drivers/net/can/flexcan.c | |
| parent | ea1990c3796e7550e6f240983f2d1b8e5ecf3891 (diff) | |
| parent | fa389e220254c69ffae0d403eac4146171062d08 (diff) | |
Merge 3.14-rc6 into usb-next
We want the USB fixes in here as well.
Diffstat (limited to 'drivers/net/can/flexcan.c')
| -rw-r--r-- | drivers/net/can/flexcan.c | 172 | 
1 files changed, 131 insertions, 41 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 320bef2dba42..61376abdab39 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -144,6 +144,8 @@  #define FLEXCAN_MB_CODE_MASK		(0xf0ffffff) +#define FLEXCAN_TIMEOUT_US             (50) +  /*   * FLEXCAN hardware feature flags   * @@ -262,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)  { @@ -269,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, @@ -709,19 +796,14 @@ static int flexcan_chip_start(struct net_device *dev)  	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); @@ -788,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; @@ -810,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;  } @@ -825,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; @@ -866,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); @@ -875,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: @@ -945,12 +1028,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); @@ -967,14 +1054,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); @@ -1104,9 +1192,10 @@ 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; @@ -1117,8 +1206,11 @@ static int 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); @@ -1139,9 +1231,7 @@ 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 */  | 
