The Myriad ma24xx in USB Intel Neural Compute Stick and Intel Neural Compute Stick 2 provides an API to accelerate AI inference calculations on the dedicated SHAVE VLIW vector co-processors, which are orchestrated by one or more LEON SPARC v8 real time cores. However, they need firmware to be loaded beforehand. An uninitialised Myriad ma24xx presents with a distinctive USB ID. After firmware loading, the device detaches from the USB bus and reattaches with a new device ID. It can then be claimed by the usermode driver. Signed-off-by: Rhys Kidd <rhyskidd@xxxxxxxxx> --- MAINTAINERS | 6 + drivers/usb/misc/Kconfig | 8 ++ drivers/usb/misc/Makefile | 1 + drivers/usb/misc/myriad-ma24xx-vsc.c | 171 +++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 drivers/usb/misc/myriad-ma24xx-vsc.c diff --git a/MAINTAINERS b/MAINTAINERS index a50e97a63bc8..2c3ab39ac26a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16682,6 +16682,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git S: Maintained F: sound/usb/midi.* +USB MYRIAD MA24XX FIRMWARE DRIVER +M: Rhys Kidd <rhyskidd@xxxxxxxxx> +L: linux-usb@xxxxxxxxxxxxxxx +S: Maintained +F: drivers/usb/misc/myriad-ma24xx-vsc.c + USB NETWORKING DRIVERS L: linux-usb@xxxxxxxxxxxxxxx S: Odd Fixes diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index bdae62b2ffe0..5d600fb8ac50 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -275,3 +275,11 @@ config USB_CHAOSKEY To compile this driver as a module, choose M here: the module will be called chaoskey. + +config USB_MYRIAD_MA24XX_VSC + tristate "USB Intel Myriad MA24xx firmware loading support" + select FW_LOADER + help + This driver loads firmware for Myriad ma24xx AI inference accelerators, as + found in the USB Intel Neural Compute Stick (ma2450) and Intel Neural + Compute Stick 2 (ma2485). \ No newline at end of file diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 109f54f5b9aa..e19d1348c5b6 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o +obj-$(CONFIG_USB_MYRIAD_MA24XX_VSC) += myriad-ma24xx-vsc.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o diff --git a/drivers/usb/misc/myriad-ma24xx-vsc.c b/drivers/usb/misc/myriad-ma24xx-vsc.c new file mode 100644 index 000000000000..f516c236a8f5 --- /dev/null +++ b/drivers/usb/misc/myriad-ma24xx-vsc.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for loading USB Myriad ma24xx firmware + * + * Copyright (C) 2018 Rhys Kidd <rhyskidd@xxxxxxxxx> + * + * The Myriad ma24xx in USB Intel Neural Compute Stick and Intel Neural + * Compute Stick 2 provides an API to accelerate AI inference calculations + * on the dedicated SHAVE VLIW vector co-processors, which are orchestrated + * by one or more LEON SPARC v8 real time cores. + * + * However, they need firmware to be loaded beforehand. An uninitialised + * Myriad ma24xx presents with a distinctive USB ID. After firmware + * loading, the device detaches from the USB bus and reattaches with a new + * device ID. It can then be claimed by the usermode driver. + * + * The firmware is non-free and must be extracted by the user. + */ + +/* Standard include files */ +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#define usb_dbg(usb_if, format, arg...) \ + dev_dbg(&(usb_if)->dev, format, ## arg) + +#define usb_err(usb_if, format, arg...) \ + dev_err(&(usb_if)->dev, format, ## arg) + +/* Version Information */ +#define DRIVER_AUTHOR "Rhys Kidd <rhyskidd@xxxxxxxxx>" +#define DRIVER_DESC "Driver for loading USB Myriad ma24xx firmware" +#define DRIVER_SHORT "myriad_ma24xx_vsc" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define FW_DIR "myriad/" +#define MA2450_FIRMWARE FW_DIR "mvncapi-2450.mvcmd" +#define MA2480_FIRMWARE FW_DIR "mvncapi-2480.mvcmd" + +MODULE_FIRMWARE(MA2450_FIRMWARE); +MODULE_FIRMWARE(MA2480_FIRMWARE); + +enum { + MA2450FW = 0, + MA2480FW +}; + +/* macros for struct usb_device_id */ +#define MYRIAD_CHIP_VERSION(x) \ + ((x)->driver_info & 0xf) + +#define MYRIAD_VID 0x03e7 /* Movidius Ltd, an Intel company */ +#define MA2450_PID 0x2150 /* ma2450 VSC loopback device, without fw */ +#define MA2485_PID 0x2485 /* ma2485 VSC loopback device, without fw */ + +#define MYRIAD_BUF_LEN 512 /* max size of USB_SPEED_HIGH packet */ +#define WRITE_TIMEOUT 2000 /* milliseconds */ + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(MYRIAD_VID, MA2450_PID), .driver_info = MA2450FW }, + { USB_DEVICE(MYRIAD_VID, MA2485_PID), .driver_info = MA2480FW }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static int myriad_ma24xx_vsc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *altsetting = intf->cur_altsetting; + struct usb_endpoint_descriptor *epd; + int out_ep, res, size; + const struct firmware *firmware = NULL; + const u8 *ptr; + int offset, ret = 0; + char *buf; + char *fw_family_name; + + usb_dbg(intf, "probe %s-%s", dev->product, dev->serial); + + /* Find the first bulk OUT endpoint and its packet size */ + res = usb_find_bulk_out_endpoint(altsetting, &epd); + if (res) { + usb_err(intf, "no OUT endpoint found"); + return res; + } + + out_ep = usb_endpoint_num(epd); + size = usb_endpoint_maxp(epd); + + /* Validate endpoint and size */ + if (size <= 0) { + usb_err(intf, "invalid size (%d)", size); + return -ENODEV; + } + + if (size > MYRIAD_BUF_LEN) { + usb_dbg(intf, "size reduced %d -> %d\n", size, MYRIAD_BUF_LEN); + size = MYRIAD_BUF_LEN; + } + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + switch (MYRIAD_CHIP_VERSION(id)) { + case MA2450FW: + fw_family_name = MA2450_FIRMWARE; + break; + case MA2480FW: + fw_family_name = MA2480_FIRMWARE; + break; + default: + usb_err(intf, "unsupported myriad ma24xx firmware family\n"); + return -ENODEV; + } + + if (request_firmware(&firmware, fw_family_name, &dev->dev) != 0) { + usb_err(intf, "unable to load myriad ma24xx firmware\n"); + ret = -ENODEV; + goto out; + } + + ptr = firmware->data; + + /* Handle the sending of firmware */ + usb_dbg(intf, "starting firmware load...\n"); + + for (offset = 0; offset < firmware->size; offset += size) { + int thislen = min_t(int, size, firmware->size - offset); + + memcpy(buf, firmware->data + offset, thislen); + + if (usb_bulk_msg(dev, + usb_sndbulkpipe(dev, out_ep), + buf, + thislen, + NULL, + WRITE_TIMEOUT) != 0) { + usb_err(intf, "failed to initialise myriad ma24xx firmware\n"); + ret = -ENODEV; + goto out; + } + } + usb_dbg(intf, "firmware uploaded (%zu bytes)\n", firmware->size); + +out: + kfree(buf); + release_firmware(firmware); + return ret; +} + +static void myriad_ma24xx_vsc_disconnect(struct usb_interface *intf) +{ +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver myriad_ma24xx_vsc_driver = { + .name = DRIVER_SHORT, + .probe = myriad_ma24xx_vsc_probe, + .disconnect = myriad_ma24xx_vsc_disconnect, + .id_table = id_table, +}; + +module_usb_driver(myriad_ma24xx_vsc_driver); -- 2.20.1