Merge tag 'usb-serial-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes: USB-serial updates for v4.3-rc1 Here's a fix for a long-standing issue with the pl2303 divisor calculations that affects some non-standard baudrates that were enabled in v3.18. Adding support for newer Edgeport devices and firmware required changes to the io_ti driver and also exposed some issues with the driver's current firmware handling. Included is also a URL comment-typo fix. Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
@@ -1365,7 +1365,7 @@
|
|||||||
#define FTDI_CTI_NANO_PID 0xF60B
|
#define FTDI_CTI_NANO_PID 0xF60B
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de
|
* ZeitControl cardsystems GmbH rfid-readers http://zeitcontrol.de
|
||||||
*/
|
*/
|
||||||
/* TagTracer MIFARE*/
|
/* TagTracer MIFARE*/
|
||||||
#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0
|
#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0
|
||||||
|
@@ -71,6 +71,25 @@ struct product_info {
|
|||||||
__u8 hardware_type; /* Type of hardware */
|
__u8 hardware_type; /* Type of hardware */
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Edgeport firmware header
|
||||||
|
*
|
||||||
|
* "build_number" has been set to 0 in all three of the images I have
|
||||||
|
* seen, and Digi Tech Support suggests that it is safe to ignore it.
|
||||||
|
*
|
||||||
|
* "length" is the number of bytes of actual data following the header.
|
||||||
|
*
|
||||||
|
* "checksum" is the low order byte resulting from adding the values of
|
||||||
|
* all the data bytes.
|
||||||
|
*/
|
||||||
|
struct edgeport_fw_hdr {
|
||||||
|
u8 major_version;
|
||||||
|
u8 minor_version;
|
||||||
|
__le16 build_number;
|
||||||
|
__le16 length;
|
||||||
|
u8 checksum;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct edgeport_port {
|
struct edgeport_port {
|
||||||
__u16 uart_base;
|
__u16 uart_base;
|
||||||
__u16 dma_address;
|
__u16 dma_address;
|
||||||
@@ -101,6 +120,9 @@ struct edgeport_serial {
|
|||||||
struct mutex es_lock;
|
struct mutex es_lock;
|
||||||
int num_ports_open;
|
int num_ports_open;
|
||||||
struct usb_serial *serial;
|
struct usb_serial *serial;
|
||||||
|
struct delayed_work heartbeat_work;
|
||||||
|
int fw_version;
|
||||||
|
bool use_heartbeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -187,10 +209,6 @@ static const struct usb_device_id id_table_combined[] = {
|
|||||||
|
|
||||||
MODULE_DEVICE_TABLE(usb, id_table_combined);
|
MODULE_DEVICE_TABLE(usb, id_table_combined);
|
||||||
|
|
||||||
static unsigned char OperationalMajorVersion;
|
|
||||||
static unsigned char OperationalMinorVersion;
|
|
||||||
static unsigned short OperationalBuildNumber;
|
|
||||||
|
|
||||||
static int closing_wait = EDGE_CLOSING_WAIT;
|
static int closing_wait = EDGE_CLOSING_WAIT;
|
||||||
static bool ignore_cpu_rev;
|
static bool ignore_cpu_rev;
|
||||||
static int default_uart_mode; /* RS232 */
|
static int default_uart_mode; /* RS232 */
|
||||||
@@ -209,6 +227,26 @@ static void edge_send(struct usb_serial_port *port, struct tty_struct *tty);
|
|||||||
static int edge_create_sysfs_attrs(struct usb_serial_port *port);
|
static int edge_create_sysfs_attrs(struct usb_serial_port *port);
|
||||||
static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
|
static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some release of Edgeport firmware "down3.bin" after version 4.80
|
||||||
|
* introduced code to automatically disconnect idle devices on some
|
||||||
|
* Edgeport models after periods of inactivity, typically ~60 seconds.
|
||||||
|
* This occurs without regard to whether ports on the device are open
|
||||||
|
* or not. Digi International Tech Support suggested:
|
||||||
|
*
|
||||||
|
* 1. Adding driver "heartbeat" code to reset the firmware timer by
|
||||||
|
* requesting a descriptor record every 15 seconds, which should be
|
||||||
|
* effective with newer firmware versions that require it, and benign
|
||||||
|
* with older versions that do not. In practice 40 seconds seems often
|
||||||
|
* enough.
|
||||||
|
* 2. The heartbeat code is currently required only on Edgeport/416 models.
|
||||||
|
*/
|
||||||
|
#define FW_HEARTBEAT_VERSION_CUTOFF ((4 << 8) + 80)
|
||||||
|
#define FW_HEARTBEAT_SECS 40
|
||||||
|
|
||||||
|
/* Timeouts in msecs: firmware downloads take longer */
|
||||||
|
#define TI_VSEND_TIMEOUT_DEFAULT 1000
|
||||||
|
#define TI_VSEND_TIMEOUT_FW_DOWNLOAD 10000
|
||||||
|
|
||||||
static int ti_vread_sync(struct usb_device *dev, __u8 request,
|
static int ti_vread_sync(struct usb_device *dev, __u8 request,
|
||||||
__u16 value, __u16 index, u8 *data, int size)
|
__u16 value, __u16 index, u8 *data, int size)
|
||||||
@@ -228,14 +266,14 @@ static int ti_vread_sync(struct usb_device *dev, __u8 request,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ti_vsend_sync(struct usb_device *dev, __u8 request,
|
static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value,
|
||||||
__u16 value, __u16 index, u8 *data, int size)
|
u16 index, u8 *data, int size, int timeout)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
|
status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
|
||||||
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
|
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
|
||||||
value, index, data, size, 1000);
|
value, index, data, size, timeout);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
if (status != size) {
|
if (status != size) {
|
||||||
@@ -250,7 +288,8 @@ static int send_cmd(struct usb_device *dev, __u8 command,
|
|||||||
__u8 moduleid, __u16 value, u8 *data,
|
__u8 moduleid, __u16 value, u8 *data,
|
||||||
int size)
|
int size)
|
||||||
{
|
{
|
||||||
return ti_vsend_sync(dev, command, value, moduleid, data, size);
|
return ti_vsend_sync(dev, command, value, moduleid, data, size,
|
||||||
|
TI_VSEND_TIMEOUT_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clear tx/rx buffers and fifo in TI UMP */
|
/* clear tx/rx buffers and fifo in TI UMP */
|
||||||
@@ -378,9 +417,9 @@ static int write_boot_mem(struct edgeport_serial *serial,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
status = ti_vsend_sync(serial->serial->dev,
|
status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
|
||||||
UMPC_MEMORY_WRITE, buffer[i],
|
buffer[i], (u16)(i + start_address), NULL,
|
||||||
(__u16)(i + start_address), NULL, 0);
|
0, TI_VSEND_TIMEOUT_DEFAULT);
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -421,10 +460,9 @@ static int write_i2c_mem(struct edgeport_serial *serial,
|
|||||||
* regardless of host byte order.
|
* regardless of host byte order.
|
||||||
*/
|
*/
|
||||||
be_start_address = swab16((u16)start_address);
|
be_start_address = swab16((u16)start_address);
|
||||||
status = ti_vsend_sync(serial->serial->dev,
|
status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
|
||||||
UMPC_MEMORY_WRITE, (__u16)address_type,
|
(u16)address_type, be_start_address,
|
||||||
be_start_address,
|
buffer, write_length, TI_VSEND_TIMEOUT_DEFAULT);
|
||||||
buffer, write_length);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_dbg(dev, "%s - ERROR %d\n", __func__, status);
|
dev_dbg(dev, "%s - ERROR %d\n", __func__, status);
|
||||||
return status;
|
return status;
|
||||||
@@ -454,9 +492,8 @@ static int write_i2c_mem(struct edgeport_serial *serial,
|
|||||||
*/
|
*/
|
||||||
be_start_address = swab16((u16)start_address);
|
be_start_address = swab16((u16)start_address);
|
||||||
status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
|
status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
|
||||||
(__u16)address_type,
|
(u16)address_type, be_start_address, buffer,
|
||||||
be_start_address,
|
write_length, TI_VSEND_TIMEOUT_DEFAULT);
|
||||||
buffer, write_length);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(dev, "%s - ERROR %d\n", __func__, status);
|
dev_err(dev, "%s - ERROR %d\n", __func__, status);
|
||||||
return status;
|
return status;
|
||||||
@@ -748,18 +785,17 @@ exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Build firmware header used for firmware update */
|
/* Build firmware header used for firmware update */
|
||||||
static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
|
static int build_i2c_fw_hdr(u8 *header, struct device *dev,
|
||||||
|
const struct firmware *fw)
|
||||||
{
|
{
|
||||||
__u8 *buffer;
|
__u8 *buffer;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
int i;
|
int i;
|
||||||
int err;
|
|
||||||
__u8 cs = 0;
|
__u8 cs = 0;
|
||||||
struct ti_i2c_desc *i2c_header;
|
struct ti_i2c_desc *i2c_header;
|
||||||
struct ti_i2c_image_header *img_header;
|
struct ti_i2c_image_header *img_header;
|
||||||
struct ti_i2c_firmware_rec *firmware_rec;
|
struct ti_i2c_firmware_rec *firmware_rec;
|
||||||
const struct firmware *fw;
|
struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
|
||||||
const char *fw_name = "edgeport/down3.bin";
|
|
||||||
|
|
||||||
/* In order to update the I2C firmware we must change the type 2 record
|
/* In order to update the I2C firmware we must change the type 2 record
|
||||||
* to type 0xF2. This will force the UMP to come up in Boot Mode.
|
* to type 0xF2. This will force the UMP to come up in Boot Mode.
|
||||||
@@ -782,24 +818,11 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
|
|||||||
// Set entire image of 0xffs
|
// Set entire image of 0xffs
|
||||||
memset(buffer, 0xff, buffer_size);
|
memset(buffer, 0xff, buffer_size);
|
||||||
|
|
||||||
err = request_firmware(&fw, fw_name, dev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(dev, "Failed to load image \"%s\" err %d\n",
|
|
||||||
fw_name, err);
|
|
||||||
kfree(buffer);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save Download Version Number */
|
|
||||||
OperationalMajorVersion = fw->data[0];
|
|
||||||
OperationalMinorVersion = fw->data[1];
|
|
||||||
OperationalBuildNumber = fw->data[2] | (fw->data[3] << 8);
|
|
||||||
|
|
||||||
/* Copy version number into firmware record */
|
/* Copy version number into firmware record */
|
||||||
firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
|
firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
|
||||||
|
|
||||||
firmware_rec->Ver_Major = OperationalMajorVersion;
|
firmware_rec->Ver_Major = fw_hdr->major_version;
|
||||||
firmware_rec->Ver_Minor = OperationalMinorVersion;
|
firmware_rec->Ver_Minor = fw_hdr->minor_version;
|
||||||
|
|
||||||
/* Pointer to fw_down memory image */
|
/* Pointer to fw_down memory image */
|
||||||
img_header = (struct ti_i2c_image_header *)&fw->data[4];
|
img_header = (struct ti_i2c_image_header *)&fw->data[4];
|
||||||
@@ -808,8 +831,6 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
|
|||||||
&fw->data[4 + sizeof(struct ti_i2c_image_header)],
|
&fw->data[4 + sizeof(struct ti_i2c_image_header)],
|
||||||
le16_to_cpu(img_header->Length));
|
le16_to_cpu(img_header->Length));
|
||||||
|
|
||||||
release_firmware(fw);
|
|
||||||
|
|
||||||
for (i=0; i < buffer_size; i++) {
|
for (i=0; i < buffer_size; i++) {
|
||||||
cs = (__u8)(cs + buffer[i]);
|
cs = (__u8)(cs + buffer[i]);
|
||||||
}
|
}
|
||||||
@@ -823,8 +844,8 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
|
|||||||
i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK;
|
i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK;
|
||||||
i2c_header->Size = cpu_to_le16(buffer_size);
|
i2c_header->Size = cpu_to_le16(buffer_size);
|
||||||
i2c_header->CheckSum = cs;
|
i2c_header->CheckSum = cs;
|
||||||
firmware_rec->Ver_Major = OperationalMajorVersion;
|
firmware_rec->Ver_Major = fw_hdr->major_version;
|
||||||
firmware_rec->Ver_Minor = OperationalMinorVersion;
|
firmware_rec->Ver_Minor = fw_hdr->minor_version;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -925,13 +946,49 @@ static int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc)
|
|||||||
return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev);
|
return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_fw_sanity(struct edgeport_serial *serial,
|
||||||
|
const struct firmware *fw)
|
||||||
|
{
|
||||||
|
u16 length_total;
|
||||||
|
u8 checksum = 0;
|
||||||
|
int pos;
|
||||||
|
struct device *dev = &serial->serial->interface->dev;
|
||||||
|
struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
|
||||||
|
|
||||||
|
if (fw->size < sizeof(struct edgeport_fw_hdr)) {
|
||||||
|
dev_err(dev, "incomplete fw header\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
length_total = le16_to_cpu(fw_hdr->length) +
|
||||||
|
sizeof(struct edgeport_fw_hdr);
|
||||||
|
|
||||||
|
if (fw->size != length_total) {
|
||||||
|
dev_err(dev, "bad fw size (expected: %u, got: %zu)\n",
|
||||||
|
length_total, fw->size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pos = sizeof(struct edgeport_fw_hdr); pos < fw->size; ++pos)
|
||||||
|
checksum += fw->data[pos];
|
||||||
|
|
||||||
|
if (checksum != fw_hdr->checksum) {
|
||||||
|
dev_err(dev, "bad fw checksum (expected: 0x%x, got: 0x%x)\n",
|
||||||
|
fw_hdr->checksum, checksum);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DownloadTIFirmware - Download run-time operating firmware to the TI5052
|
* DownloadTIFirmware - Download run-time operating firmware to the TI5052
|
||||||
*
|
*
|
||||||
* This routine downloads the main operating code into the TI5052, using the
|
* This routine downloads the main operating code into the TI5052, using the
|
||||||
* boot code already burned into E2PROM or ROM.
|
* boot code already burned into E2PROM or ROM.
|
||||||
*/
|
*/
|
||||||
static int download_fw(struct edgeport_serial *serial)
|
static int download_fw(struct edgeport_serial *serial,
|
||||||
|
const struct firmware *fw)
|
||||||
{
|
{
|
||||||
struct device *dev = &serial->serial->dev->dev;
|
struct device *dev = &serial->serial->dev->dev;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
@@ -940,6 +997,14 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
struct usb_interface_descriptor *interface;
|
struct usb_interface_descriptor *interface;
|
||||||
int download_cur_ver;
|
int download_cur_ver;
|
||||||
int download_new_ver;
|
int download_new_ver;
|
||||||
|
struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
|
||||||
|
|
||||||
|
if (check_fw_sanity(serial, fw))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* If on-board version is newer, "fw_version" will be updated below. */
|
||||||
|
serial->fw_version = (fw_hdr->major_version << 8) +
|
||||||
|
fw_hdr->minor_version;
|
||||||
|
|
||||||
/* This routine is entered by both the BOOT mode and the Download mode
|
/* This routine is entered by both the BOOT mode and the Download mode
|
||||||
* We can determine which code is running by the reading the config
|
* We can determine which code is running by the reading the config
|
||||||
@@ -1047,14 +1112,13 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
version in I2c */
|
version in I2c */
|
||||||
download_cur_ver = (firmware_version->Ver_Major << 8) +
|
download_cur_ver = (firmware_version->Ver_Major << 8) +
|
||||||
(firmware_version->Ver_Minor);
|
(firmware_version->Ver_Minor);
|
||||||
download_new_ver = (OperationalMajorVersion << 8) +
|
download_new_ver = (fw_hdr->major_version << 8) +
|
||||||
(OperationalMinorVersion);
|
(fw_hdr->minor_version);
|
||||||
|
|
||||||
dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n",
|
dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n",
|
||||||
__func__, firmware_version->Ver_Major,
|
__func__, firmware_version->Ver_Major,
|
||||||
firmware_version->Ver_Minor,
|
firmware_version->Ver_Minor,
|
||||||
OperationalMajorVersion,
|
fw_hdr->major_version, fw_hdr->minor_version);
|
||||||
OperationalMinorVersion);
|
|
||||||
|
|
||||||
/* Check if we have an old version in the I2C and
|
/* Check if we have an old version in the I2C and
|
||||||
update if necessary */
|
update if necessary */
|
||||||
@@ -1063,8 +1127,8 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
__func__,
|
__func__,
|
||||||
firmware_version->Ver_Major,
|
firmware_version->Ver_Major,
|
||||||
firmware_version->Ver_Minor,
|
firmware_version->Ver_Minor,
|
||||||
OperationalMajorVersion,
|
fw_hdr->major_version,
|
||||||
OperationalMinorVersion);
|
fw_hdr->minor_version);
|
||||||
|
|
||||||
record = kmalloc(1, GFP_KERNEL);
|
record = kmalloc(1, GFP_KERNEL);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
@@ -1129,7 +1193,8 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
/* Reset UMP -- Back to BOOT MODE */
|
/* Reset UMP -- Back to BOOT MODE */
|
||||||
status = ti_vsend_sync(serial->serial->dev,
|
status = ti_vsend_sync(serial->serial->dev,
|
||||||
UMPC_HARDWARE_RESET,
|
UMPC_HARDWARE_RESET,
|
||||||
0, 0, NULL, 0);
|
0, 0, NULL, 0,
|
||||||
|
TI_VSEND_TIMEOUT_DEFAULT);
|
||||||
|
|
||||||
dev_dbg(dev, "%s - HARDWARE RESET return %d\n", __func__, status);
|
dev_dbg(dev, "%s - HARDWARE RESET return %d\n", __func__, status);
|
||||||
|
|
||||||
@@ -1139,6 +1204,9 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
kfree(rom_desc);
|
kfree(rom_desc);
|
||||||
kfree(ti_manuf_desc);
|
kfree(ti_manuf_desc);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
} else {
|
||||||
|
/* Same or newer fw version is already loaded */
|
||||||
|
serial->fw_version = download_cur_ver;
|
||||||
}
|
}
|
||||||
kfree(firmware_version);
|
kfree(firmware_version);
|
||||||
}
|
}
|
||||||
@@ -1177,7 +1245,7 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
* UMP Ram to I2C and the firmware will update the
|
* UMP Ram to I2C and the firmware will update the
|
||||||
* record type from 0xf2 to 0x02.
|
* record type from 0xf2 to 0x02.
|
||||||
*/
|
*/
|
||||||
status = build_i2c_fw_hdr(header, dev);
|
status = build_i2c_fw_hdr(header, dev, fw);
|
||||||
if (status) {
|
if (status) {
|
||||||
kfree(vheader);
|
kfree(vheader);
|
||||||
kfree(header);
|
kfree(header);
|
||||||
@@ -1229,7 +1297,9 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
|
|
||||||
/* Tell firmware to copy download image into I2C */
|
/* Tell firmware to copy download image into I2C */
|
||||||
status = ti_vsend_sync(serial->serial->dev,
|
status = ti_vsend_sync(serial->serial->dev,
|
||||||
UMPC_COPY_DNLD_TO_I2C, 0, 0, NULL, 0);
|
UMPC_COPY_DNLD_TO_I2C,
|
||||||
|
0, 0, NULL, 0,
|
||||||
|
TI_VSEND_TIMEOUT_FW_DOWNLOAD);
|
||||||
|
|
||||||
dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, status);
|
dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, status);
|
||||||
if (status) {
|
if (status) {
|
||||||
@@ -1278,9 +1348,6 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
__u8 cs = 0;
|
__u8 cs = 0;
|
||||||
__u8 *buffer;
|
__u8 *buffer;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
int err;
|
|
||||||
const struct firmware *fw;
|
|
||||||
const char *fw_name = "edgeport/down3.bin";
|
|
||||||
|
|
||||||
/* Validate Hardware version number
|
/* Validate Hardware version number
|
||||||
* Read Manufacturing Descriptor from TI Based Edgeport
|
* Read Manufacturing Descriptor from TI Based Edgeport
|
||||||
@@ -1328,16 +1395,7 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
|
|
||||||
/* Initialize the buffer to 0xff (pad the buffer) */
|
/* Initialize the buffer to 0xff (pad the buffer) */
|
||||||
memset(buffer, 0xff, buffer_size);
|
memset(buffer, 0xff, buffer_size);
|
||||||
|
|
||||||
err = request_firmware(&fw, fw_name, dev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(dev, "Failed to load image \"%s\" err %d\n",
|
|
||||||
fw_name, err);
|
|
||||||
kfree(buffer);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
memcpy(buffer, &fw->data[4], fw->size - 4);
|
memcpy(buffer, &fw->data[4], fw->size - 4);
|
||||||
release_firmware(fw);
|
|
||||||
|
|
||||||
for (i = sizeof(struct ti_i2c_image_header);
|
for (i = sizeof(struct ti_i2c_image_header);
|
||||||
i < buffer_size; i++) {
|
i < buffer_size; i++) {
|
||||||
@@ -1352,7 +1410,9 @@ static int download_fw(struct edgeport_serial *serial)
|
|||||||
header->CheckSum = cs;
|
header->CheckSum = cs;
|
||||||
|
|
||||||
/* Download the operational code */
|
/* Download the operational code */
|
||||||
dev_dbg(dev, "%s - Downloading operational code image (TI UMP)\n", __func__);
|
dev_dbg(dev, "%s - Downloading operational code image version %d.%d (TI UMP)\n",
|
||||||
|
__func__,
|
||||||
|
fw_hdr->major_version, fw_hdr->minor_version);
|
||||||
status = download_code(serial, buffer, buffer_size);
|
status = download_code(serial, buffer, buffer_size);
|
||||||
|
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
@@ -2373,10 +2433,44 @@ static void edge_break(struct tty_struct *tty, int break_state)
|
|||||||
__func__, status);
|
__func__, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void edge_heartbeat_schedule(struct edgeport_serial *edge_serial)
|
||||||
|
{
|
||||||
|
if (!edge_serial->use_heartbeat)
|
||||||
|
return;
|
||||||
|
|
||||||
|
schedule_delayed_work(&edge_serial->heartbeat_work,
|
||||||
|
FW_HEARTBEAT_SECS * HZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void edge_heartbeat_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct edgeport_serial *serial;
|
||||||
|
struct ti_i2c_desc *rom_desc;
|
||||||
|
|
||||||
|
serial = container_of(work, struct edgeport_serial,
|
||||||
|
heartbeat_work.work);
|
||||||
|
|
||||||
|
rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
|
||||||
|
|
||||||
|
/* Descriptor address request is enough to reset the firmware timer */
|
||||||
|
if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
|
||||||
|
rom_desc)) {
|
||||||
|
dev_err(&serial->serial->interface->dev,
|
||||||
|
"%s - Incomplete heartbeat\n", __func__);
|
||||||
|
}
|
||||||
|
kfree(rom_desc);
|
||||||
|
|
||||||
|
edge_heartbeat_schedule(serial);
|
||||||
|
}
|
||||||
|
|
||||||
static int edge_startup(struct usb_serial *serial)
|
static int edge_startup(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
struct edgeport_serial *edge_serial;
|
struct edgeport_serial *edge_serial;
|
||||||
int status;
|
int status;
|
||||||
|
const struct firmware *fw;
|
||||||
|
const char *fw_name = "edgeport/down3.bin";
|
||||||
|
struct device *dev = &serial->interface->dev;
|
||||||
|
u16 product_id;
|
||||||
|
|
||||||
/* create our private serial structure */
|
/* create our private serial structure */
|
||||||
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
|
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
|
||||||
@@ -2387,12 +2481,35 @@ static int edge_startup(struct usb_serial *serial)
|
|||||||
edge_serial->serial = serial;
|
edge_serial->serial = serial;
|
||||||
usb_set_serial_data(serial, edge_serial);
|
usb_set_serial_data(serial, edge_serial);
|
||||||
|
|
||||||
status = download_fw(edge_serial);
|
status = request_firmware(&fw, fw_name, dev);
|
||||||
|
if (status) {
|
||||||
|
dev_err(dev, "Failed to load image \"%s\" err %d\n",
|
||||||
|
fw_name, status);
|
||||||
|
kfree(edge_serial);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = download_fw(edge_serial, fw);
|
||||||
|
release_firmware(fw);
|
||||||
if (status) {
|
if (status) {
|
||||||
kfree(edge_serial);
|
kfree(edge_serial);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
product_id = le16_to_cpu(
|
||||||
|
edge_serial->serial->dev->descriptor.idProduct);
|
||||||
|
|
||||||
|
/* Currently only the EP/416 models require heartbeat support */
|
||||||
|
if (edge_serial->fw_version > FW_HEARTBEAT_VERSION_CUTOFF) {
|
||||||
|
if (product_id == ION_DEVICE_ID_TI_EDGEPORT_416 ||
|
||||||
|
product_id == ION_DEVICE_ID_TI_EDGEPORT_416B) {
|
||||||
|
edge_serial->use_heartbeat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
|
||||||
|
edge_heartbeat_schedule(edge_serial);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2402,7 +2519,10 @@ static void edge_disconnect(struct usb_serial *serial)
|
|||||||
|
|
||||||
static void edge_release(struct usb_serial *serial)
|
static void edge_release(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
kfree(usb_get_serial_data(serial));
|
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&edge_serial->heartbeat_work);
|
||||||
|
kfree(edge_serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int edge_port_probe(struct usb_serial_port *port)
|
static int edge_port_probe(struct usb_serial_port *port)
|
||||||
@@ -2506,6 +2626,25 @@ static int edge_remove_sysfs_attrs(struct usb_serial_port *port)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int edge_suspend(struct usb_serial *serial, pm_message_t message)
|
||||||
|
{
|
||||||
|
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&edge_serial->heartbeat_work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int edge_resume(struct usb_serial *serial)
|
||||||
|
{
|
||||||
|
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
|
||||||
|
|
||||||
|
edge_heartbeat_schedule(edge_serial);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct usb_serial_driver edgeport_1port_device = {
|
static struct usb_serial_driver edgeport_1port_device = {
|
||||||
.driver = {
|
.driver = {
|
||||||
@@ -2538,6 +2677,10 @@ static struct usb_serial_driver edgeport_1port_device = {
|
|||||||
.read_int_callback = edge_interrupt_callback,
|
.read_int_callback = edge_interrupt_callback,
|
||||||
.read_bulk_callback = edge_bulk_in_callback,
|
.read_bulk_callback = edge_bulk_in_callback,
|
||||||
.write_bulk_callback = edge_bulk_out_callback,
|
.write_bulk_callback = edge_bulk_out_callback,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.suspend = edge_suspend,
|
||||||
|
.resume = edge_resume,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_serial_driver edgeport_2port_device = {
|
static struct usb_serial_driver edgeport_2port_device = {
|
||||||
@@ -2571,6 +2714,10 @@ static struct usb_serial_driver edgeport_2port_device = {
|
|||||||
.read_int_callback = edge_interrupt_callback,
|
.read_int_callback = edge_interrupt_callback,
|
||||||
.read_bulk_callback = edge_bulk_in_callback,
|
.read_bulk_callback = edge_bulk_in_callback,
|
||||||
.write_bulk_callback = edge_bulk_out_callback,
|
.write_bulk_callback = edge_bulk_out_callback,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.suspend = edge_suspend,
|
||||||
|
.resume = edge_resume,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_serial_driver * const serial_drivers[] = {
|
static struct usb_serial_driver * const serial_drivers[] = {
|
||||||
|
@@ -362,21 +362,38 @@ static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4],
|
|||||||
static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
|
static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4],
|
||||||
speed_t baud)
|
speed_t baud)
|
||||||
{
|
{
|
||||||
unsigned int tmp;
|
unsigned int baseline, mantissa, exponent;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apparently the formula is:
|
* Apparently the formula is:
|
||||||
* baudrate = 12M * 32 / (2^buf[1]) / buf[0]
|
* baudrate = 12M * 32 / (mantissa * 4^exponent)
|
||||||
|
* where
|
||||||
|
* mantissa = buf[8:0]
|
||||||
|
* exponent = buf[11:9]
|
||||||
*/
|
*/
|
||||||
tmp = 12000000 * 32 / baud;
|
baseline = 12000000 * 32;
|
||||||
|
mantissa = baseline / baud;
|
||||||
|
if (mantissa == 0)
|
||||||
|
mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */
|
||||||
|
exponent = 0;
|
||||||
|
while (mantissa >= 512) {
|
||||||
|
if (exponent < 7) {
|
||||||
|
mantissa >>= 2; /* divide by 4 */
|
||||||
|
exponent++;
|
||||||
|
} else {
|
||||||
|
/* Exponent is maxed. Trim mantissa and leave. */
|
||||||
|
mantissa = 511;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buf[3] = 0x80;
|
buf[3] = 0x80;
|
||||||
buf[2] = 0;
|
buf[2] = 0;
|
||||||
buf[1] = (tmp >= 256);
|
buf[1] = exponent << 1 | mantissa >> 8;
|
||||||
while (tmp >= 256) {
|
buf[0] = mantissa & 0xff;
|
||||||
tmp >>= 2;
|
|
||||||
buf[1] <<= 1;
|
/* Calculate and return the exact baud rate. */
|
||||||
}
|
baud = (baseline / mantissa) >> (exponent << 1);
|
||||||
buf[0] = tmp;
|
|
||||||
|
|
||||||
return baud;
|
return baud;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user