^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* cypress_firmware.c is part of the DVB USB library.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * see dvb-usb-init.c for copyright information.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * This file contains functions for downloading the firmware to Cypress FX 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * and 2 based devices.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/usb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/firmware.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include "cypress_firmware.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) struct usb_cypress_controller {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) u8 id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) const char *name; /* name of the usb controller */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) u16 cs_reg; /* needs to be restarted,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * when the firmware has been downloaded */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static const struct usb_cypress_controller cypress[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * load a firmware packet to the device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) u8 len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static int cypress_get_hexline(const struct firmware *fw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) struct hexline *hx, int *pos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) u8 *b = (u8 *) &fw->data[*pos];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) int data_offs = 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) if (*pos >= fw->size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) memset(hx, 0, sizeof(struct hexline));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) hx->len = b[0];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if ((*pos + hx->len + 4) >= fw->size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) hx->addr = b[1] | (b[2] << 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) hx->type = b[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) if (hx->type == 0x04) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) /* b[4] and b[5] are the Extended linear address record data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * field */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) hx->addr |= (b[4] << 24) | (b[5] << 16);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) memcpy(hx->data, &b[data_offs], hx->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) hx->chk = b[hx->len + data_offs];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) *pos += hx->len + 5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) return *pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) int cypress_load_firmware(struct usb_device *udev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) const struct firmware *fw, int type)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) struct hexline *hx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) int ret, pos = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) hx = kmalloc(sizeof(*hx), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) if (!hx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) /* stop the CPU */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) hx->data[0] = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (ret != 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) KBUILD_MODNAME, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) goto err_kfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) /* write firmware to memory */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) ret = cypress_get_hexline(fw, hx, &pos);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) goto err_kfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) else if (ret == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) goto err_kfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) } else if (ret != hx->len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) dev_err(&udev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) "%s: error while transferring firmware (transferred size=%d, block size=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) KBUILD_MODNAME, ret, hx->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) goto err_kfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) /* start the CPU */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) hx->data[0] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) if (ret != 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) dev_err(&udev->dev, "%s: CPU start failed=%d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) KBUILD_MODNAME, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) goto err_kfree;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) err_kfree:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) kfree(hx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) EXPORT_SYMBOL(cypress_load_firmware);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) MODULE_DESCRIPTION("Cypress firmware download");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) MODULE_LICENSE("GPL");