Commit ea8b33bd authored by Stephane Grosjean's avatar Stephane Grosjean Committed by Marc Kleine-Budde

can: pcan_usb: add support of rxerr/txerr counters

This patch adds the support of the rx/tx errors CAN counters to the
driver of the PCAN-USB PC-CAN interface from PEAK-System GmbH.

The PCAN-USB is capable of giving back the values of the rx/tx errors
counters, to provide more details and statistics to the linux-can layer.
Getting these values allows the driver to better tune CAN_ERR_CRTL_TX_xxx
and CAN_ERR_CRTL_RX_xxx bits in case of the interface enters any
CAN_STATE_ERROR_xxx state.
Signed-off-by: default avatarStephane Grosjean <s.grosjean@peak-system.com>
Link: https://lore.kernel.org/r/20191206153803.17725-3-s.grosjean@peak-system.comSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent a8bb19df
...@@ -41,6 +41,7 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); ...@@ -41,6 +41,7 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
#define PCAN_USB_CMD_SN 6 #define PCAN_USB_CMD_SN 6
#define PCAN_USB_CMD_REGISTER 9 #define PCAN_USB_CMD_REGISTER 9
#define PCAN_USB_CMD_EXT_VCC 10 #define PCAN_USB_CMD_EXT_VCC 10
#define PCAN_USB_CMD_ERR_FR 11
/* PCAN_USB_CMD_SET_BUS number arg */ /* PCAN_USB_CMD_SET_BUS number arg */
#define PCAN_USB_BUS_XCVER 2 #define PCAN_USB_BUS_XCVER 2
...@@ -82,6 +83,10 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); ...@@ -82,6 +83,10 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
#define PCAN_USB_ERROR_QOVR 0x40 #define PCAN_USB_ERROR_QOVR 0x40
#define PCAN_USB_ERROR_TXQFULL 0x80 #define PCAN_USB_ERROR_TXQFULL 0x80
#define PCAN_USB_ERROR_BUS (PCAN_USB_ERROR_BUS_LIGHT | \
PCAN_USB_ERROR_BUS_HEAVY | \
PCAN_USB_ERROR_BUS_OFF)
/* SJA1000 modes */ /* SJA1000 modes */
#define SJA1000_MODE_NORMAL 0x00 #define SJA1000_MODE_NORMAL 0x00
#define SJA1000_MODE_INIT 0x01 #define SJA1000_MODE_INIT 0x01
...@@ -101,11 +106,25 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); ...@@ -101,11 +106,25 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
#define PCAN_USB_REC_TS 4 #define PCAN_USB_REC_TS 4
#define PCAN_USB_REC_BUSEVT 5 #define PCAN_USB_REC_BUSEVT 5
/* CAN bus events notifications selection mask */
#define PCAN_USB_ERR_RXERR 0x02 /* ask for rxerr counter */
#define PCAN_USB_ERR_TXERR 0x04 /* ask for txerr counter */
/* This mask generates an usb packet each time the state of the bus changes.
* In other words, its interest is to know which side among rx and tx is
* responsible of the change of the bus state.
*/
#define PCAN_USB_BERR_MASK (PCAN_USB_ERR_RXERR | PCAN_USB_ERR_TXERR)
/* identify bus event packets with rx/tx error counters */
#define PCAN_USB_ERR_CNT 0x80
/* private to PCAN-USB adapter */ /* private to PCAN-USB adapter */
struct pcan_usb { struct pcan_usb {
struct peak_usb_device dev; struct peak_usb_device dev;
struct peak_time_ref time_ref; struct peak_time_ref time_ref;
struct timer_list restart_timer; struct timer_list restart_timer;
struct can_berr_counter bec;
}; };
/* incoming message context for decoding */ /* incoming message context for decoding */
...@@ -212,6 +231,16 @@ static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff) ...@@ -212,6 +231,16 @@ static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff)
PCAN_USB_BUS_SILENT_MODE, args); PCAN_USB_BUS_SILENT_MODE, args);
} }
/* send the cmd to be notified from bus errors */
static int pcan_usb_set_err_frame(struct peak_usb_device *dev, u8 err_mask)
{
u8 args[PCAN_USB_CMD_ARGS_LEN] = {
[0] = err_mask,
};
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_ERR_FR, PCAN_USB_SET, args);
}
static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff) static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff)
{ {
u8 args[PCAN_USB_CMD_ARGS_LEN] = { u8 args[PCAN_USB_CMD_ARGS_LEN] = {
...@@ -445,7 +474,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, ...@@ -445,7 +474,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
new_state = CAN_STATE_BUS_OFF; new_state = CAN_STATE_BUS_OFF;
break; break;
} }
if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { if (n & ~PCAN_USB_ERROR_BUS) {
/* /*
* trick to bypass next comparison and process other * trick to bypass next comparison and process other
* errors * errors
...@@ -469,7 +498,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, ...@@ -469,7 +498,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
new_state = CAN_STATE_ERROR_WARNING; new_state = CAN_STATE_ERROR_WARNING;
break; break;
} }
if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { if (n & ~PCAN_USB_ERROR_BUS) {
/* /*
* trick to bypass next comparison and process other * trick to bypass next comparison and process other
* errors * errors
...@@ -508,29 +537,50 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, ...@@ -508,29 +537,50 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
case CAN_STATE_ERROR_PASSIVE: case CAN_STATE_ERROR_PASSIVE:
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE | cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
CAN_ERR_CRTL_RX_PASSIVE; CAN_ERR_CRTL_TX_PASSIVE :
CAN_ERR_CRTL_RX_PASSIVE;
cf->data[6] = mc->pdev->bec.txerr;
cf->data[7] = mc->pdev->bec.rxerr;
mc->pdev->dev.can.can_stats.error_passive++; mc->pdev->dev.can.can_stats.error_passive++;
break; break;
case CAN_STATE_ERROR_WARNING: case CAN_STATE_ERROR_WARNING:
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] |= CAN_ERR_CRTL_TX_WARNING | cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
CAN_ERR_CRTL_RX_WARNING; CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING;
cf->data[6] = mc->pdev->bec.txerr;
cf->data[7] = mc->pdev->bec.rxerr;
mc->pdev->dev.can.can_stats.error_warning++; mc->pdev->dev.can.can_stats.error_warning++;
break; break;
case CAN_STATE_ERROR_ACTIVE: case CAN_STATE_ERROR_ACTIVE:
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_ACTIVE; cf->data[1] = CAN_ERR_CRTL_ACTIVE;
/* sync local copies of rxerr/txerr counters */
mc->pdev->bec.txerr = 0;
mc->pdev->bec.rxerr = 0;
break; break;
default: default:
/* CAN_STATE_MAX (trick to handle other errors) */ /* CAN_STATE_MAX (trick to handle other errors) */
cf->can_id |= CAN_ERR_CRTL; if (n & PCAN_USB_ERROR_TXQFULL)
cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; netdev_dbg(mc->netdev, "device Tx queue full)\n");
mc->netdev->stats.rx_over_errors++;
mc->netdev->stats.rx_errors++; if (n & PCAN_USB_ERROR_RXQOVR) {
netdev_dbg(mc->netdev, "data overrun interrupt\n");
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
mc->netdev->stats.rx_over_errors++;
mc->netdev->stats.rx_errors++;
}
cf->data[6] = mc->pdev->bec.txerr;
cf->data[7] = mc->pdev->bec.rxerr;
new_state = mc->pdev->dev.can.state; new_state = mc->pdev->dev.can.state;
break; break;
...@@ -552,6 +602,30 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, ...@@ -552,6 +602,30 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
return 0; return 0;
} }
/* decode bus event usb packet: first byte contains rxerr while 2nd one contains
* txerr.
*/
static int pcan_usb_handle_bus_evt(struct pcan_usb_msg_context *mc, u8 ir)
{
struct pcan_usb *pdev = mc->pdev;
/* acccording to the content of the packet */
switch (ir) {
case PCAN_USB_ERR_CNT:
/* save rx/tx error counters from in the device context */
pdev->bec.rxerr = mc->ptr[0];
pdev->bec.txerr = mc->ptr[1];
break;
default:
/* reserved */
break;
}
return 0;
}
/* /*
* decode non-data usb message * decode non-data usb message
*/ */
...@@ -606,9 +680,10 @@ static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc, ...@@ -606,9 +680,10 @@ static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc,
break; break;
case PCAN_USB_REC_BUSEVT: case PCAN_USB_REC_BUSEVT:
/* error frame/bus event */ /* bus event notifications (get rxerr/txerr) */
if (n & PCAN_USB_ERROR_TXQFULL) err = pcan_usb_handle_bus_evt(mc, n);
netdev_dbg(mc->netdev, "device Tx queue full)\n"); if (err)
return err;
break; break;
default: default:
netdev_err(mc->netdev, "unexpected function %u\n", f); netdev_err(mc->netdev, "unexpected function %u\n", f);
...@@ -792,20 +867,44 @@ static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb, ...@@ -792,20 +867,44 @@ static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb,
return 0; return 0;
} }
/* socket callback used to copy berr counters values received through USB */
static int pcan_usb_get_berr_counter(const struct net_device *netdev,
struct can_berr_counter *bec)
{
struct peak_usb_device *dev = netdev_priv(netdev);
struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
*bec = pdev->bec;
/* must return 0 */
return 0;
}
/* /*
* start interface * start interface
*/ */
static int pcan_usb_start(struct peak_usb_device *dev) static int pcan_usb_start(struct peak_usb_device *dev)
{ {
struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
int err;
/* number of bits used in timestamps read from adapter struct */ /* number of bits used in timestamps read from adapter struct */
peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb); peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb);
pdev->bec.rxerr = 0;
pdev->bec.txerr = 0;
/* be notified on error counter changes (if requested by user) */
if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
err = pcan_usb_set_err_frame(dev, PCAN_USB_BERR_MASK);
if (err)
netdev_warn(dev->netdev,
"Asking for BERR reporting error %u\n",
err);
}
/* if revision greater than 3, can put silent mode on/off */ /* if revision greater than 3, can put silent mode on/off */
if (dev->device_rev > 3) { if (dev->device_rev > 3) {
int err;
err = pcan_usb_set_silent(dev, err = pcan_usb_set_silent(dev,
dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
if (err) if (err)
...@@ -892,7 +991,8 @@ const struct peak_usb_adapter pcan_usb = { ...@@ -892,7 +991,8 @@ const struct peak_usb_adapter pcan_usb = {
.name = "PCAN-USB", .name = "PCAN-USB",
.device_id = PCAN_USB_PRODUCT_ID, .device_id = PCAN_USB_PRODUCT_ID,
.ctrl_count = 1, .ctrl_count = 1,
.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_BERR_REPORTING,
.clock = { .clock = {
.freq = PCAN_USB_CRYSTAL_HZ / 2 , .freq = PCAN_USB_CRYSTAL_HZ / 2 ,
}, },
...@@ -925,4 +1025,5 @@ const struct peak_usb_adapter pcan_usb = { ...@@ -925,4 +1025,5 @@ const struct peak_usb_adapter pcan_usb = {
.dev_encode_msg = pcan_usb_encode_msg, .dev_encode_msg = pcan_usb_encode_msg,
.dev_start = pcan_usb_start, .dev_start = pcan_usb_start,
.dev_restart_async = pcan_usb_restart_async, .dev_restart_async = pcan_usb_restart_async,
.do_get_berr_counter = pcan_usb_get_berr_counter,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment