/*
* CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter
*
* Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/netdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include "pcan_usb_core.h"
#include "pcan_usb_pro.h"
#include "pcan_ucan.h"
MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter");
MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
#define PCAN_USBPROFD_CHANNEL_COUNT 2
#define PCAN_USBFD_CHANNEL_COUNT 1
/* PCAN-USB Pro FD adapter internal clock (Hz) */
#define PCAN_UFD_CRYSTAL_HZ 80000000
#define PCAN_UFD_CMD_BUFFER_SIZE 512
#define PCAN_UFD_LOSPD_PKT_SIZE 64
/* PCAN-USB Pro FD command timeout (ms.) */
#define PCAN_UFD_CMD_TIMEOUT_MS 1000
/* PCAN-USB Pro FD rx/tx buffers size */
#define PCAN_UFD_RX_BUFFER_SIZE 2048
#define PCAN_UFD_TX_BUFFER_SIZE 512
/* read some versions info from the hw devcie */
struct __packed pcan_ufd_fw_info {
__le16 size_of; /* sizeof this */
__le16 type; /* type of this structure */
u8 hw_type; /* Type of hardware (HW_TYPE_xxx) */
u8 bl_version[3]; /* Bootloader version */
u8 hw_version; /* Hardware version (PCB) */
u8 fw_version[3]; /* Firmware version */
__le32 dev_id[2]; /* "device id" per CAN */
__le32 ser_no; /* S/N */
__le32 flags; /* special functions */
};
/* handle device specific info used by the netdevices */
struct pcan_usb_fd_if {
struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL];
struct pcan_ufd_fw_info fw_info;
struct peak_time_ref time_ref;
int cm_ignore_count;
int dev_opened_count;
};
/* device information */
struct pcan_usb_fd_device {
struct peak_usb_device dev;
struct can_berr_counter bec;
struct pcan_usb_fd_if *usb_if;
u8 *cmd_buffer_addr;
};
/* Extended USB commands (non uCAN commands) */
/* Clock Modes command */
#define PCAN_UFD_CMD_CLK_SET 0x80
#define PCAN_UFD_CLK_80MHZ 0x0
#define PCAN_UFD_CLK_60MHZ 0x1
#define PCAN_UFD_CLK_40MHZ 0x2
#define PCAN_UFD_CLK_30MHZ 0x3
#define PCAN_UFD_CLK_24MHZ 0x4
#define PCAN_UFD_CLK_20MHZ 0x5
#define PCAN_UFD_CLK_DEF PCAN_UFD_CLK_80MHZ
struct __packed pcan_ufd_clock {
__le16 opcode_channel;
u8 mode;
u8 unused[5];
};
/* LED control command */
#define PCAN_UFD_CMD_LED_SET 0x86
#define PCAN_UFD_LED_DEV 0x00
#define PCAN_UFD_LED_FAST 0x01
#define PCAN_UFD_LED_SLOW 0x02
#define PCAN_UFD_LED_ON 0x03
#define PCAN_UFD_LED_OFF 0x04
#define PCAN_UFD_LED_DEF PCAN_UFD_LED_DEV
struct __packed pcan_ufd_led {
__le16 opcode_channel;
u8 mode;
u8 unused[5];
};
/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */
#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000
struct __packed pcan_ufd_options {
__le16 opcode_channel;
__le16 ucan_mask;
u16 unused;
__le16 usb_mask;
};
/* Extended usage of uCAN messages for PCAN-USB Pro FD */
#define PCAN_UFD_MSG_CALIBRATION 0x100
struct __packed pcan_ufd_ts_msg {
__le16 size;
__le16 type;
__le32 ts_low;
__le32 ts_high;
__le16 usb_frame_index;
u16 unused;
};
#define PCAN_UFD_MSG_OVERRUN 0x101
#define PCAN_UFD_OVMSG_CHANNEL(o) ((o)->channel & 0xf)
struct __packed pcan_ufd_ovr_msg {
__le16 size;
__le16 type;
__le32 ts_low;
__le32 ts_high;
u8 channel;
u8 unused[3];
};
static inline int pufd_omsg_get_channel(struct pcan_ufd_ovr_msg *om)
{
return om->channel & 0xf;
}
/* Clock mode frequency values */
static const u32 pcan_usb_fd_clk_freq[6] = {
[PCAN_UFD_CLK_80MHZ] = 80000000,
[PCAN_UFD_CLK_60MHZ] = 60000000,
[PCAN_UFD_CLK_40MHZ] = 40000000,
[PCAN_UFD_CLK_30MHZ] = 30000000,
[PCAN_UFD_CLK_24MHZ] = 24000000,
[PCAN_UFD_CLK_20MHZ] = 20000000
};
/* return a device USB interface */
static inline
struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev)
{
struct pcan_usb_fd_device *pdev =
container_of(dev, struct pcan_usb_fd_device, dev);
return pdev->usb_if;
}
/* return a device USB commands buffer */
static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev)
{
struct pcan_usb_fd_device *pdev =
container_of(dev, struct pcan_usb_fd_device, dev);
return pdev->cmd_buffer_addr;
}
/* send PCAN-USB Pro FD commands synchronously */
static int pcan_usb_fd_send_cmd(