summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/can/at91_can.c69
1 files changed, 55 insertions, 14 deletions
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 2b972812b2d1..121ede663e20 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -127,6 +127,7 @@ enum at91_mb_mode {
enum at91_devtype {
AT91_DEVTYPE_SAM9263,
+ AT91_DEVTYPE_SAM9X5,
};
struct at91_devtype_data {
@@ -163,6 +164,12 @@ static const struct at91_devtype_data at91_devtype_data[] __devinitconst = {
.rx_last = 11,
.tx_shift = 2,
},
+ [AT91_DEVTYPE_SAM9X5] = {
+ .rx_first = 0,
+ .rx_split = 4,
+ .rx_last = 5,
+ .tx_shift = 1,
+ },
};
static struct can_bittiming_const at91_bittiming_const = {
@@ -184,6 +191,7 @@ static inline int at91_is_sam##_model(const struct at91_priv *priv) \
}
AT91_IS(9263);
+AT91_IS(9X5);
static inline unsigned int get_mb_rx_first(const struct at91_priv *priv)
{
@@ -991,6 +999,29 @@ static void at91_irq_err_state(struct net_device *dev,
at91_write(priv, AT91_IER, reg_ier);
}
+static int at91_get_state_by_bec(const struct net_device *dev,
+ enum can_state *state)
+{
+ struct can_berr_counter bec;
+ int err;
+
+ err = at91_get_berr_counter(dev, &bec);
+ if (err)
+ return err;
+
+ if (bec.txerr < 96 && bec.rxerr < 96)
+ *state = CAN_STATE_ERROR_ACTIVE;
+ else if (bec.txerr < 128 && bec.rxerr < 128)
+ *state = CAN_STATE_ERROR_WARNING;
+ else if (bec.txerr < 256 && bec.rxerr < 256)
+ *state = CAN_STATE_ERROR_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_OFF;
+
+ return 0;
+}
+
+
static void at91_irq_err(struct net_device *dev)
{
struct at91_priv *priv = netdev_priv(dev);
@@ -998,21 +1029,28 @@ static void at91_irq_err(struct net_device *dev)
struct can_frame *cf;
enum can_state new_state;
u32 reg_sr;
+ int err;
- reg_sr = at91_read(priv, AT91_SR);
-
- /* we need to look at the unmasked reg_sr */
- if (unlikely(reg_sr & AT91_IRQ_BOFF))
- new_state = CAN_STATE_BUS_OFF;
- else if (unlikely(reg_sr & AT91_IRQ_ERRP))
- new_state = CAN_STATE_ERROR_PASSIVE;
- else if (unlikely(reg_sr & AT91_IRQ_WARN))
- new_state = CAN_STATE_ERROR_WARNING;
- else if (likely(reg_sr & AT91_IRQ_ERRA))
- new_state = CAN_STATE_ERROR_ACTIVE;
- else {
- netdev_err(dev, "BUG! hardware in undefined state\n");
- return;
+ if (at91_is_sam9263(priv)) {
+ reg_sr = at91_read(priv, AT91_SR);
+
+ /* we need to look at the unmasked reg_sr */
+ if (unlikely(reg_sr & AT91_IRQ_BOFF))
+ new_state = CAN_STATE_BUS_OFF;
+ else if (unlikely(reg_sr & AT91_IRQ_ERRP))
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ else if (unlikely(reg_sr & AT91_IRQ_WARN))
+ new_state = CAN_STATE_ERROR_WARNING;
+ else if (likely(reg_sr & AT91_IRQ_ERRA))
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ else {
+ netdev_err(dev, "BUG! hardware in undefined state\n");
+ return;
+ }
+ } else {
+ err = at91_get_state_by_bec(dev, &new_state);
+ if (err)
+ return;
}
/* state hasn't changed */
@@ -1330,6 +1368,9 @@ static const struct platform_device_id at91_can_id_table[] = {
.name = "at91_can",
.driver_data = AT91_DEVTYPE_SAM9263,
}, {
+ .name = "at91sam9x5_can",
+ .driver_data = AT91_DEVTYPE_SAM9X5,
+ }, {
/* sentinel */
}
};