From 47e3485af0a7a65547a3267021851d4ea6474d09 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 13 Jan 2015 16:23:11 +0200 Subject: can: c_can: use regmap_update_bits() to modify RAMINIT register use of regmap_read() and regmap_write() in c_can_hw_raminit_syscon() is not safe as the RAMINIT register can be shared between different drivers at least for TI SoCs. To make the modification atomic we switch to using regmap_update_bits(). regmap_update_bits() skips writing to the register if it's read content is the same as what is going to be written. This causes an issue for us when we need to clear the DONE bit with the initial condition START:0, DONE:1 as DONE bit must be written with 1 to clear it. So we defer the clearing of DONE bit to later when we set the START bit. There we are sure that START bit is changed from 0 to 1 so the write of 1 to already set DONE bit will happen. Signed-off-by: Roger Quadros Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_platform.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/net/can/c_can') diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index f363972cd77d..e36d10520e24 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -103,27 +103,34 @@ static void c_can_hw_raminit_syscon(const struct c_can_priv *priv, bool enable) mask = 1 << raminit->bits.start | 1 << raminit->bits.done; regmap_read(raminit->syscon, raminit->reg, &ctrl); - /* We clear the done and start bit first. The start bit is + /* We clear the start bit first. The start bit is * looking at the 0 -> transition, but is not self clearing; - * And we clear the init done bit as well. * NOTE: DONE must be written with 1 to clear it. + * We can't clear the DONE bit here using regmap_update_bits() + * as it will bypass the write if initial condition is START:0 DONE:1 + * e.g. on DRA7 which needs START pulse. */ - ctrl &= ~(1 << raminit->bits.start); - ctrl |= 1 << raminit->bits.done; - regmap_write(raminit->syscon, raminit->reg, ctrl); + ctrl &= ~mask; /* START = 0, DONE = 0 */ + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); - ctrl &= ~(1 << raminit->bits.done); - c_can_hw_raminit_wait_syscon(priv, mask, ctrl); + /* check if START bit is 0. Ignore DONE bit for now + * as it can be either 0 or 1. + */ + c_can_hw_raminit_wait_syscon(priv, 1 << raminit->bits.start, ctrl); if (enable) { - /* Set start bit and wait for the done bit. */ + /* Clear DONE bit & set START bit. */ ctrl |= 1 << raminit->bits.start; - regmap_write(raminit->syscon, raminit->reg, ctrl); - + /* DONE must be written with 1 to clear it */ + ctrl |= 1 << raminit->bits.done; + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); + /* prevent further clearing of DONE bit */ + ctrl &= ~(1 << raminit->bits.done); /* clear START bit if start pulse is needed */ if (raminit->needs_pulse) { ctrl &= ~(1 << raminit->bits.start); - regmap_write(raminit->syscon, raminit->reg, ctrl); + regmap_update_bits(raminit->syscon, raminit->reg, + mask, ctrl); } ctrl |= 1 << raminit->bits.done; -- cgit v1.2.3 From 7ffd7b4e169d619e66928fe5d997723f2c6f1056 Mon Sep 17 00:00:00 2001 From: Viktor Babrian Date: Sun, 18 Jan 2015 20:01:40 +0100 Subject: can: c_can: end pending transmission on network stop (ifdown) Put controller into init mode in network stop to end pending transmissions. The issue is observed in cases when transmitted frame is not acked. Signed-off-by: Viktor Babrian Cc: linux-stable Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/can/c_can') diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index f94a9fa60488..c672c4dcffac 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -615,6 +615,9 @@ static void c_can_stop(struct net_device *dev) c_can_irq_control(priv, false); + /* put ctrl to init on stop to end ongoing transmission */ + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_INIT); + /* deactivate pins */ pinctrl_pm_select_sleep_state(dev->dev.parent); priv->can.state = CAN_STATE_STOPPED; -- cgit v1.2.3