Commit 906e0e68 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

can: gs_usb: add switchable termination support

The candleLight community is working on switchable termination support
for the candleLight firmware. As the the Linux CAN framework supports
switchable termination add this feature to the gs_usb driver.

Devices supporting the feature should set the
GS_CAN_FEATURE_TERMINATION and implement the
GS_USB_BREQ_SET_TERMINATION and GS_USB_BREQ_GET_TERMINATION control
messages.

For now the driver assumes for activated termination the standard
termination value of 120Ω.

Link: https://lore.kernel.org/all/20220923074114.662045-1-mkl@pengutronix.de
Link: https://github.com/candle-usb/candleLight_fw/issues/92
Link: https://github.com/candle-usb/candleLight_fw/pull/109
Link: https://github.com/candle-usb/candleLight_fw/pull/108
Cc: Daniel Trevitz <daniel.trevitz@wika.com>
Cc: Ryan Edwards <ryan.edwards@gmail.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 68822f4e
......@@ -64,6 +64,8 @@ enum gs_usb_breq {
GS_USB_BREQ_SET_USER_ID,
GS_USB_BREQ_DATA_BITTIMING,
GS_USB_BREQ_BT_CONST_EXT,
GS_USB_BREQ_SET_TERMINATION,
GS_USB_BREQ_GET_TERMINATION,
};
enum gs_can_mode {
......@@ -87,6 +89,14 @@ enum gs_can_identify_mode {
GS_CAN_IDENTIFY_ON
};
enum gs_can_termination_state {
GS_CAN_TERMINATION_STATE_OFF = 0,
GS_CAN_TERMINATION_STATE_ON
};
#define GS_USB_TERMINATION_DISABLED CAN_TERMINATION_DISABLED
#define GS_USB_TERMINATION_ENABLED 120
/* data types passed between host and device */
/* The firmware on the original USB2CAN by Geschwister Schneider
......@@ -123,6 +133,7 @@ struct gs_device_config {
#define GS_CAN_MODE_FD BIT(8)
/* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */
/* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */
/* GS_CAN_FEATURE_TERMINATION BIT(11) */
struct gs_device_mode {
__le32 mode;
......@@ -147,6 +158,10 @@ struct gs_identify_mode {
__le32 mode;
} __packed;
struct gs_device_termination_state {
__le32 state;
} __packed;
#define GS_CAN_FEATURE_LISTEN_ONLY BIT(0)
#define GS_CAN_FEATURE_LOOP_BACK BIT(1)
#define GS_CAN_FEATURE_TRIPLE_SAMPLE BIT(2)
......@@ -158,7 +173,8 @@ struct gs_identify_mode {
#define GS_CAN_FEATURE_FD BIT(8)
#define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9)
#define GS_CAN_FEATURE_BT_CONST_EXT BIT(10)
#define GS_CAN_FEATURE_MASK GENMASK(10, 0)
#define GS_CAN_FEATURE_TERMINATION BIT(11)
#define GS_CAN_FEATURE_MASK GENMASK(11, 0)
/* internal quirks - keep in GS_CAN_FEATURE space for now */
......@@ -1080,6 +1096,52 @@ static const struct ethtool_ops gs_usb_ethtool_ops = {
.get_ts_info = gs_usb_get_ts_info,
};
static int gs_usb_get_termination(struct net_device *netdev, u16 *term)
{
struct gs_can *dev = netdev_priv(netdev);
struct gs_device_termination_state term_state;
int rc;
rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0,
GS_USB_BREQ_GET_TERMINATION,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0,
&term_state, sizeof(term_state), 1000,
GFP_KERNEL);
if (rc)
return rc;
if (term_state.state == cpu_to_le32(GS_CAN_TERMINATION_STATE_ON))
*term = GS_USB_TERMINATION_ENABLED;
else
*term = GS_USB_TERMINATION_DISABLED;
return 0;
}
static int gs_usb_set_termination(struct net_device *netdev, u16 term)
{
struct gs_can *dev = netdev_priv(netdev);
struct gs_device_termination_state term_state;
if (term == GS_USB_TERMINATION_ENABLED)
term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_ON);
else
term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF);
return usb_control_msg_send(interface_to_usbdev(dev->iface), 0,
GS_USB_BREQ_SET_TERMINATION,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
dev->channel, 0,
&term_state, sizeof(term_state), 1000,
GFP_KERNEL);
}
static const u16 gs_usb_termination_const[] = {
GS_USB_TERMINATION_DISABLED,
GS_USB_TERMINATION_ENABLED
};
static struct gs_can *gs_make_candev(unsigned int channel,
struct usb_interface *intf,
struct gs_device_config *dconf)
......@@ -1174,6 +1236,21 @@ static struct gs_can *gs_make_candev(unsigned int channel,
dev->can.do_set_data_bittiming = gs_usb_set_data_bittiming;
}
if (feature & GS_CAN_FEATURE_TERMINATION) {
rc = gs_usb_get_termination(netdev, &dev->can.termination);
if (rc) {
dev->feature &= ~GS_CAN_FEATURE_TERMINATION;
dev_info(&intf->dev,
"Disabling termination support for channel %d (%pe)\n",
channel, ERR_PTR(rc));
} else {
dev->can.termination_const = gs_usb_termination_const;
dev->can.termination_const_cnt = ARRAY_SIZE(gs_usb_termination_const);
dev->can.do_set_termination = gs_usb_set_termination;
}
}
/* The CANtact Pro from LinkLayer Labs is based on the
* LPC54616 µC, which is affected by the NXP LPC USB transfer
* erratum. However, the current firmware (version 2) doesn't
......
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