162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2008 Sensoray Company Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/usb.h>
962306a36Sopenharmony_ci#include <linux/firmware.h>
1062306a36Sopenharmony_ci#include <cypress_firmware.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistruct fw_config {
1362306a36Sopenharmony_ci	u16 vendor;
1462306a36Sopenharmony_ci	u16 product;
1562306a36Sopenharmony_ci	const char * const fw_name1;
1662306a36Sopenharmony_ci	const char * const fw_name2;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct fw_config fw_configs[] = {
2062306a36Sopenharmony_ci	{ 0x1943, 0xa250, "go7007/s2250-1.fw", "go7007/s2250-2.fw" },
2162306a36Sopenharmony_ci	{ 0x093b, 0xa002, "go7007/px-m402u.fw", NULL },
2262306a36Sopenharmony_ci	{ 0x093b, 0xa004, "go7007/px-tv402u.fw", NULL },
2362306a36Sopenharmony_ci	{ 0x0eb1, 0x6666, "go7007/lr192.fw", NULL },
2462306a36Sopenharmony_ci	{ 0x0eb1, 0x6668, "go7007/wis-startrek.fw", NULL },
2562306a36Sopenharmony_ci	{ 0, 0, NULL, NULL }
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/s2250-1.fw");
2862306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/s2250-2.fw");
2962306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/px-m402u.fw");
3062306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/px-tv402u.fw");
3162306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/lr192.fw");
3262306a36Sopenharmony_ciMODULE_FIRMWARE("go7007/wis-startrek.fw");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int go7007_loader_probe(struct usb_interface *interface,
3562306a36Sopenharmony_ci				const struct usb_device_id *id)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct usb_device *usbdev;
3862306a36Sopenharmony_ci	const struct firmware *fw;
3962306a36Sopenharmony_ci	u16 vendor, product;
4062306a36Sopenharmony_ci	const char *fw1, *fw2;
4162306a36Sopenharmony_ci	int ret;
4262306a36Sopenharmony_ci	int i;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	usbdev = usb_get_dev(interface_to_usbdev(interface));
4562306a36Sopenharmony_ci	if (!usbdev)
4662306a36Sopenharmony_ci		goto failed2;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (usbdev->descriptor.bNumConfigurations != 1) {
4962306a36Sopenharmony_ci		dev_err(&interface->dev, "can't handle multiple config\n");
5062306a36Sopenharmony_ci		goto failed2;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	vendor = le16_to_cpu(usbdev->descriptor.idVendor);
5462306a36Sopenharmony_ci	product = le16_to_cpu(usbdev->descriptor.idProduct);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (i = 0; fw_configs[i].fw_name1; i++)
5762306a36Sopenharmony_ci		if (fw_configs[i].vendor == vendor &&
5862306a36Sopenharmony_ci		    fw_configs[i].product == product)
5962306a36Sopenharmony_ci			break;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Should never happen */
6262306a36Sopenharmony_ci	if (fw_configs[i].fw_name1 == NULL)
6362306a36Sopenharmony_ci		goto failed2;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	fw1 = fw_configs[i].fw_name1;
6662306a36Sopenharmony_ci	fw2 = fw_configs[i].fw_name2;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	dev_info(&interface->dev, "loading firmware %s\n", fw1);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (request_firmware(&fw, fw1, &usbdev->dev)) {
7162306a36Sopenharmony_ci		dev_err(&interface->dev,
7262306a36Sopenharmony_ci			"unable to load firmware from file \"%s\"\n", fw1);
7362306a36Sopenharmony_ci		goto failed2;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
7662306a36Sopenharmony_ci	release_firmware(fw);
7762306a36Sopenharmony_ci	if (0 != ret) {
7862306a36Sopenharmony_ci		dev_err(&interface->dev, "loader download failed\n");
7962306a36Sopenharmony_ci		goto failed2;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (fw2 == NULL)
8362306a36Sopenharmony_ci		return 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (request_firmware(&fw, fw2, &usbdev->dev)) {
8662306a36Sopenharmony_ci		dev_err(&interface->dev,
8762306a36Sopenharmony_ci			"unable to load firmware from file \"%s\"\n", fw2);
8862306a36Sopenharmony_ci		goto failed2;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	ret = cypress_load_firmware(usbdev, fw, CYPRESS_FX2);
9162306a36Sopenharmony_ci	release_firmware(fw);
9262306a36Sopenharmony_ci	if (0 != ret) {
9362306a36Sopenharmony_ci		dev_err(&interface->dev, "firmware download failed\n");
9462306a36Sopenharmony_ci		goto failed2;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cifailed2:
9962306a36Sopenharmony_ci	usb_put_dev(usbdev);
10062306a36Sopenharmony_ci	dev_err(&interface->dev, "probe failed\n");
10162306a36Sopenharmony_ci	return -ENODEV;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void go7007_loader_disconnect(struct usb_interface *interface)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	dev_info(&interface->dev, "disconnect\n");
10762306a36Sopenharmony_ci	usb_put_dev(interface_to_usbdev(interface));
10862306a36Sopenharmony_ci	usb_set_intfdata(interface, NULL);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic const struct usb_device_id go7007_loader_ids[] = {
11262306a36Sopenharmony_ci	{ USB_DEVICE(0x1943, 0xa250) },
11362306a36Sopenharmony_ci	{ USB_DEVICE(0x093b, 0xa002) },
11462306a36Sopenharmony_ci	{ USB_DEVICE(0x093b, 0xa004) },
11562306a36Sopenharmony_ci	{ USB_DEVICE(0x0eb1, 0x6666) },
11662306a36Sopenharmony_ci	{ USB_DEVICE(0x0eb1, 0x6668) },
11762306a36Sopenharmony_ci	{}                          /* Terminating entry */
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, go7007_loader_ids);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct usb_driver go7007_loader_driver = {
12362306a36Sopenharmony_ci	.name		= "go7007-loader",
12462306a36Sopenharmony_ci	.probe		= go7007_loader_probe,
12562306a36Sopenharmony_ci	.disconnect	= go7007_loader_disconnect,
12662306a36Sopenharmony_ci	.id_table	= go7007_loader_ids,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cimodule_usb_driver(go7007_loader_driver);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciMODULE_AUTHOR("");
13262306a36Sopenharmony_ciMODULE_DESCRIPTION("firmware loader for go7007-usb");
13362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
134