summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/mxuport.c170
1 files changed, 152 insertions, 18 deletions
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 61d4c6e1a9ce..35123572ca4d 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -6,10 +6,20 @@
* Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch>
*
* Supports the following Moxa USB to serial converters:
- * 2 ports : UPort 1250, UPort 1250I
- * 4 ports : UPort 1410, UPort 1450, UPort 1450I
- * 8 ports : UPort 1610-8, UPort 1650-8
- * 16 ports : UPort 1610-16, UPort 1650-16
+ *
+ * UPort G1:
+ * UPort 1250, UPort 1250I
+ * UPort 1410, UPort 1450, UPort 1450I
+ * UPort 1610-8, UPort 1650-8
+ * UPort 1610-16, UPort 1650-16
+ * UPort G2:
+ * UPort 1250-G2, UPort 1250I-G2
+ * UPort 1410-G2, UPort 1450-G2, UPort 1450I-G2
+ * UPort 1610-8-G2, UPort 1650-8-G2
+ * UPort 1650-8-G2-Hub, UPort 1650I-8-G2
+ * Platform UART:
+ * MU250U, MU450U, MU850U
+ * MUx50U-3, MUx50U-5, MUx50U-6, MUx50U-7
*/
#include <linux/kernel.h>
@@ -37,6 +47,24 @@
#define MX_UPORT1610_16_PID 0x1613
#define MX_UPORT1650_16_PID 0x1653
+#define MX_UPORT1250_G2_PID 0x1252
+#define MX_UPORT1250I_G2_PID 0x1253
+#define MX_UPORT1410_G2_PID 0x1411
+#define MX_UPORT1450_G2_PID 0x1452
+#define MX_UPORT1450I_G2_PID 0x1453
+#define MX_UPORT1610_8_G2_PID 0x1619
+#define MX_UPORT1650_8_G2_PID 0x1659
+#define MX_UPORT1650_8_G2_HUB_PID 0x165a
+#define MX_UPORT1650I_8_G2_PID 0x165b
+
+#define MX_MU250U_PID 0x0250
+#define MX_MU450U_PID 0x0450
+#define MX_MU850U_PID 0x0850
+#define MX_MUX50U_6_PID 0x7002
+#define MX_MUX50U_3_PID 0x7003
+#define MX_MUX50U_5_PID 0x7004
+#define MX_MUX50U_7_PID 0x7005
+
/* Definitions for USB info */
#define HEADER_SIZE 4
#define EVENT_LENGTH 8
@@ -46,6 +74,13 @@
#define MX_FW_VER_BYTE1_OFF 0x20
#define MX_FW_VER_BYTE2_OFF 0x24
#define MX_FW_VER_BYTE3_OFF 0x28
+#define MX_MUX50U_FW_VER_BYTE1_OFF 0x86
+#define MX_MUX50U_FW_VER_BYTE2_OFF 0x88
+#define MX_MUX50U_FW_VER_BYTE3_OFF 0x8a
+
+#define MX_SYS_REG_REMAP_OFF 0x20
+#define MX_PWR_REMAP_MASK 0x03
+#define MX_REMAP_TO_SRAM 0x02
struct mxuport_fw_version {
u8 major;
@@ -60,6 +95,12 @@ static const u16 mxuport_fw_ver_offsets[] = {
MX_FW_VER_BYTE3_OFF,
};
+static const u16 mxuport_mux50u_fw_ver_offsets[] = {
+ MX_MUX50U_FW_VER_BYTE1_OFF,
+ MX_MUX50U_FW_VER_BYTE2_OFF,
+ MX_MUX50U_FW_VER_BYTE3_OFF,
+};
+
/* Definitions for USB vendor request */
#define RQ_VENDOR_NONE 0x00
#define RQ_VENDOR_SET_BAUD 0x01 /* Set baud rate */
@@ -96,6 +137,8 @@ static const u16 mxuport_fw_ver_offsets[] = {
#define RQ_VENDOR_RESET_DEVICE 0x23 /* Try to reset the device */
#define RQ_VENDOR_QUERY_FW_CONFIG 0x24
+#define RQ_VENDOR_GET_SYS_REG 0x34
+
#define RQ_VENDOR_GET_VERSION 0x81 /* Get firmware version */
#define RQ_VENDOR_GET_PAGE 0x82 /* Read flash page */
#define RQ_VENDOR_GET_ROM_PROC 0x83 /* Get ROM process state */
@@ -170,21 +213,44 @@ struct mxuport_port {
#define MX_PORTS_OFFSET 1
#define MX_PORTS(n) (((n) - MX_PORTS_OFFSET) & MX_PORTS_MASK)
-#define MX_DEVICE(pid, ports) \
+#define MX_FAMILY_MASK GENMASK(5, 4)
+#define MX_UPORT_G1 0
+#define MX_UPORT_G2 BIT(4)
+#define MX_PLATFORM_UART BIT(5)
+#define MX_FAMILY(info) ((info) & MX_FAMILY_MASK)
+#define MX_DEVICE_INFO(ports, family) (MX_PORTS(ports) | (family))
+
+#define MX_DEVICE(pid, ports, family) \
{ USB_DEVICE(MX_USBSERIAL_VID, pid), \
- .driver_info = MX_PORTS(ports) }
+ .driver_info = MX_DEVICE_INFO(ports, family) }
/* Table of devices that work with this driver */
static const struct usb_device_id mxuport_idtable[] = {
- MX_DEVICE(MX_UPORT1250_PID, 2),
- MX_DEVICE(MX_UPORT1250I_PID, 2),
- MX_DEVICE(MX_UPORT1410_PID, 4),
- MX_DEVICE(MX_UPORT1450_PID, 4),
- MX_DEVICE(MX_UPORT1450I_PID, 4),
- MX_DEVICE(MX_UPORT1610_8_PID, 8),
- MX_DEVICE(MX_UPORT1650_8_PID, 8),
- MX_DEVICE(MX_UPORT1610_16_PID, 16),
- MX_DEVICE(MX_UPORT1650_16_PID, 16),
+ MX_DEVICE(MX_UPORT1250_PID, 2, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1250I_PID, 2, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1410_PID, 4, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1450_PID, 4, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1450I_PID, 4, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1610_8_PID, 8, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1650_8_PID, 8, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1610_16_PID, 16, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1650_16_PID, 16, MX_UPORT_G1),
+ MX_DEVICE(MX_UPORT1250_G2_PID, 2, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1250I_G2_PID, 2, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1410_G2_PID, 4, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1450_G2_PID, 4, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1450I_G2_PID, 4, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1610_8_G2_PID, 8, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1650_8_G2_PID, 8, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1650_8_G2_HUB_PID, 8, MX_UPORT_G2),
+ MX_DEVICE(MX_UPORT1650I_8_G2_PID, 8, MX_UPORT_G2),
+ MX_DEVICE(MX_MU250U_PID, 2, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MU450U_PID, 4, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MU850U_PID, 8, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MUX50U_6_PID, 6, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MUX50U_3_PID, 3, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MUX50U_5_PID, 5, MX_PLATFORM_UART),
+ MX_DEVICE(MX_MUX50U_7_PID, 7, MX_PLATFORM_UART),
{} /* Terminating entry */
};
@@ -1000,6 +1066,56 @@ static int mxuport_parse_fw_version(const struct firmware *fw,
return 0;
}
+static bool mxuport_is_mux50u_device(unsigned long features)
+{
+ switch (MX_FAMILY(features)) {
+ case MX_UPORT_G2:
+ case MX_PLATFORM_UART:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mxuport_get_fw_name(unsigned long features, u16 productid,
+ char *buf, size_t size)
+{
+ switch (MX_FAMILY(features)) {
+ case MX_UPORT_G2:
+ strscpy(buf, "moxa/moxa-up-mux50u.fw", size);
+ break;
+ case MX_PLATFORM_UART:
+ strscpy(buf, "moxa/moxa-pf-mux50u.fw", size);
+ break;
+ default:
+ snprintf(buf, size, "moxa/moxa-%04x.fw", productid);
+ break;
+ }
+}
+
+static int mxuport_fw_running_from_sram(struct usb_serial *serial, bool *from_sram)
+{
+ int err;
+ u32 reg;
+ u8 *buf;
+
+ buf = kzalloc_objs(*buf, sizeof(reg));
+ if (!buf)
+ return -ENOMEM;
+
+ err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_SYS_REG,
+ MX_SYS_REG_REMAP_OFF, 0, buf, sizeof(reg));
+ if (err < 0)
+ goto out;
+
+ reg = get_unaligned_be32(buf);
+ *from_sram = (((reg >> 24) & MX_PWR_REMAP_MASK) == MX_REMAP_TO_SRAM);
+ err = 0;
+out:
+ kfree(buf);
+ return err;
+}
+
/* Get the version of the firmware currently running. */
static int mxuport_get_fw_version(struct usb_serial *serial,
struct mxuport_fw_version *version)
@@ -1080,7 +1196,10 @@ static int mxuport_probe(struct usb_serial *serial,
{
u16 productid = le16_to_cpu(serial->dev->descriptor.idProduct);
struct mxuport_fw_version version, local_ver;
+ unsigned long features = id->driver_info;
const struct firmware *fw_p = NULL;
+ const u16 *fw_version_offsets;
+ bool download_fw, from_sram;
char buf[32];
int err;
@@ -1098,7 +1217,12 @@ static int mxuport_probe(struct usb_serial *serial,
dev_dbg(&serial->interface->dev, "Device firmware version v%u.%u.%u\n",
version.major, version.minor, version.build);
- snprintf(buf, sizeof(buf) - 1, "moxa/moxa-%04x.fw", productid);
+ if (mxuport_is_mux50u_device(features))
+ fw_version_offsets = mxuport_mux50u_fw_ver_offsets;
+ else
+ fw_version_offsets = mxuport_fw_ver_offsets;
+
+ mxuport_get_fw_name(features, productid, buf, sizeof(buf));
err = request_firmware(&fw_p, buf, &serial->interface->dev);
if (err) {
@@ -1108,7 +1232,7 @@ static int mxuport_probe(struct usb_serial *serial,
/* Use the firmware already in the device */
err = 0;
} else {
- err = mxuport_parse_fw_version(fw_p, mxuport_fw_ver_offsets, &local_ver);
+ err = mxuport_parse_fw_version(fw_p, fw_version_offsets, &local_ver);
if (err) {
dev_err(&serial->interface->dev,
"Firmware %s is too short\n", buf);
@@ -1119,7 +1243,17 @@ static int mxuport_probe(struct usb_serial *serial,
"Available firmware version v%u.%u.%u\n",
local_ver.major, local_ver.minor, local_ver.build);
- if (local_ver.value > version.value) {
+ if (mxuport_is_mux50u_device(features)) {
+ err = mxuport_fw_running_from_sram(serial, &from_sram);
+ if (err)
+ goto out;
+
+ download_fw = !from_sram;
+ } else {
+ download_fw = local_ver.value > version.value;
+ }
+
+ if (download_fw) {
err = mxuport_download_fw(serial, fw_p);
if (err)
goto out;