1355b4b2b5e83986132c77326e2b97780480b8d69Antti Palosaari/* cypress_firmware.c is part of the DVB USB library. 2b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * 3b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de) 4b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * see dvb-usb-init.c for copyright information. 5b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * 6b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * This file contains functions for downloading the firmware to Cypress FX 1 7b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * and 2 based devices. 8b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * 9b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari */ 10b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 1179a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil#include <linux/module.h> 1279a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil#include <linux/slab.h> 1379a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil#include <linux/usb.h> 1479a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil#include <linux/firmware.h> 15355b4b2b5e83986132c77326e2b97780480b8d69Antti Palosaari#include "cypress_firmware.h" 16b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 17b00a901801f671a48feac6048faeafe0979760e6Antti Palosaaristruct usb_cypress_controller { 18b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari u8 id; 19b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari const char *name; /* name of the usb controller */ 20b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari u16 cs_reg; /* needs to be restarted, 21b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * when the firmware has been downloaded */ 22b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari}; 23b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 24b00a901801f671a48feac6048faeafe0979760e6Antti Palosaaristatic const struct usb_cypress_controller cypress[] = { 25b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 }, 26b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 }, 27b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 }, 28b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari}; 29b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 30b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari/* 31b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari * load a firmware packet to the device 32b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari */ 33b00a901801f671a48feac6048faeafe0979760e6Antti Palosaaristatic int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data, 34b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari u8 len) 35b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari{ 36b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 37b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000); 38b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari} 39b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 4079a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuilstatic int cypress_get_hexline(const struct firmware *fw, 4179a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil struct hexline *hx, int *pos) 4279a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil{ 4379a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil u8 *b = (u8 *) &fw->data[*pos]; 4479a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil int data_offs = 4; 4579a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 4679a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil if (*pos >= fw->size) 4779a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil return 0; 4879a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 4979a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil memset(hx, 0, sizeof(struct hexline)); 5079a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil hx->len = b[0]; 5179a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 5279a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil if ((*pos + hx->len + 4) >= fw->size) 5379a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil return -EINVAL; 5479a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 5579a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil hx->addr = b[1] | (b[2] << 8); 5679a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil hx->type = b[3]; 5779a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 5879a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil if (hx->type == 0x04) { 5979a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil /* b[4] and b[5] are the Extended linear address record data 6079a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil * field */ 6179a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil hx->addr |= (b[4] << 24) | (b[5] << 16); 6279a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil } 6379a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 6479a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil memcpy(hx->data, &b[data_offs], hx->len); 6579a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil hx->chk = b[hx->len + data_offs]; 6679a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil *pos += hx->len + 5; 6779a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 6879a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil return *pos; 6979a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil} 7079a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil 7179a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuilint cypress_load_firmware(struct usb_device *udev, 72b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari const struct firmware *fw, int type) 73b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari{ 742347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari struct hexline *hx; 75b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari int ret, pos = 0; 76b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 772347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari hx = kmalloc(sizeof(struct hexline), GFP_KERNEL); 782347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari if (!hx) { 792347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari dev_err(&udev->dev, "%s: kmalloc() failed\n", KBUILD_MODNAME); 802347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari return -ENOMEM; 812347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari } 822347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari 83b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari /* stop the CPU */ 842347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari hx->data[0] = 1; 852347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1); 862347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari if (ret != 1) { 872347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari dev_err(&udev->dev, "%s: CPU stop failed=%d\n", 882347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari KBUILD_MODNAME, ret); 892347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = -EIO; 902347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari goto err_kfree; 912347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari } 922347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari 932347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari /* write firmware to memory */ 942347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari for (;;) { 9579a63c60a6a2ae589e44529401e0ab1150e9408aHans Verkuil ret = cypress_get_hexline(fw, hx, &pos); 962347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari if (ret < 0) 972347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari goto err_kfree; 982347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari else if (ret == 0) 992347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari break; 1002347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari 1012347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len); 1022347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari if (ret < 0) { 1032347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari goto err_kfree; 1042347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari } else if (ret != hx->len) { 10561356eea6742ad38ad9ecfd5c65e2662e4c9d8abAntti Palosaari dev_err(&udev->dev, 10661356eea6742ad38ad9ecfd5c65e2662e4c9d8abAntti Palosaari "%s: error while transferring firmware (transferred size=%d, block size=%d)\n", 1072347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari KBUILD_MODNAME, ret, hx->len); 1082347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = -EIO; 1092347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari goto err_kfree; 110b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari } 111b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari } 112b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 1132347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari /* start the CPU */ 1142347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari hx->data[0] = 0; 1152347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1); 1162347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari if (ret != 1) { 1172347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari dev_err(&udev->dev, "%s: CPU start failed=%d\n", 1182347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari KBUILD_MODNAME, ret); 119b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari ret = -EIO; 1202347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari goto err_kfree; 1212347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari } 122b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 1232347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari ret = 0; 1242347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaarierr_kfree: 1252347e6836ad2a5a2f7e62bd12b8f52fe15f04f74Antti Palosaari kfree(hx); 126b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari return ret; 127b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari} 12879a63c60a6a2ae589e44529401e0ab1150e9408aHans VerkuilEXPORT_SYMBOL(cypress_load_firmware); 129b00a901801f671a48feac6048faeafe0979760e6Antti Palosaari 130b00a901801f671a48feac6048faeafe0979760e6Antti PalosaariMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 131b00a901801f671a48feac6048faeafe0979760e6Antti PalosaariMODULE_DESCRIPTION("Cypress firmware download"); 132b00a901801f671a48feac6048faeafe0979760e6Antti PalosaariMODULE_LICENSE("GPL"); 133