Commit b240eacd authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

[media] drx-j: get rid of drx_driver.c

This file contains just the firmware load code, that it is also
somewhat duplicated at drxj.c.

Move the code into there. Latter patches will remove the
duplicated code.
Acked-by: default avatarDevin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent ad55f6c8
drx39xyj-objs := drx39xxj.o drx_driver.o drx39xxj_dummy.o drxj.o drx_dap_fasi.o drx39xyj-objs := drx39xxj.o drx39xxj_dummy.o drxj.o drx_dap_fasi.o
obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o
......
/*
Generic DRX functionality, DRX driver core.
Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Trident Microsystems nor Hauppauge Computer Works
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
/*------------------------------------------------------------------------------
INCLUDE FILES
------------------------------------------------------------------------------*/
#include "drx_driver.h"
#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_PATCH 0
/*
* DEFINES
*/
/*
* MICROCODE RELATED DEFINES
*/
/* Magic word for checking correct Endianess of microcode data */
#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L'))
/* CRC flag in ucode header, flags field. */
#define DRX_UCODE_CRC_FLAG (0x0001)
/*
* Maximum size of buffer used to verify the microcode.
* Must be an even number
*/
#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
#if DRX_UCODE_MAX_BUF_SIZE & 1
#error DRX_UCODE_MAX_BUF_SIZE must be an even number
#endif
/*
* Power mode macros
*/
#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \
(mode == DRX_POWER_MODE_10) || \
(mode == DRX_POWER_MODE_11) || \
(mode == DRX_POWER_MODE_12) || \
(mode == DRX_POWER_MODE_13) || \
(mode == DRX_POWER_MODE_14) || \
(mode == DRX_POWER_MODE_15) || \
(mode == DRX_POWER_MODE_16) || \
(mode == DRX_POWER_DOWN))
/*------------------------------------------------------------------------------
STRUCTURES
------------------------------------------------------------------------------*/
/**
* struct drxu_code_block_hdr - Structure of the microcode block headers
*
* @addr: Destination address of the data in this block
* @size: Size of the block data following this header counted in
* 16 bits words
* @CRC: CRC value of the data block, only valid if CRC flag is
* set.
*/
struct drxu_code_block_hdr {
u32 addr;
u16 size;
u16 flags;
u16 CRC;
};
/*------------------------------------------------------------------------------
FUNCTIONS
------------------------------------------------------------------------------*/
/*
* Microcode related functions
*/
/**
* drx_u_code_compute_crc - Compute CRC of block of microcode data.
* @block_data: Pointer to microcode data.
* @nr_words: Size of microcode block (number of 16 bits words).
*
* returns The computed CRC residue.
*/
static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
{
u16 i = 0;
u16 j = 0;
u32 crc_word = 0;
u32 carry = 0;
while (i < nr_words) {
crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data));
for (j = 0; j < 16; j++) {
crc_word <<= 1;
if (carry != 0)
crc_word ^= 0x80050000UL;
carry = crc_word & 0x80000000UL;
}
i++;
block_data += (sizeof(u16));
}
return (u16)(crc_word >> 16);
}
/**
* drx_check_firmware - checks if the loaded firmware is valid
*
* @demod: demod structure
* @mc_data: pointer to the start of the firmware
* @size: firmware size
*/
static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
unsigned size)
{
struct drxu_code_block_hdr block_hdr;
int i;
unsigned count = 2 * sizeof(u16);
u32 mc_dev_type, mc_version, mc_base_version;
u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16)));
/*
* Scan microcode blocks first for version info
* and firmware check
*/
/* Clear version block */
DRX_ATTR_MCRECORD(demod).aux_type = 0;
DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
DRX_ATTR_MCRECORD(demod).mc_version = 0;
DRX_ATTR_MCRECORD(demod).mc_base_version = 0;
for (i = 0; i < mc_nr_of_blks; i++) {
if (count + 3 * sizeof(u16) + sizeof(u32) > size)
goto eof;
/* Process block header */
block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u32);
block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
count, block_hdr.addr, block_hdr.size, block_hdr.flags,
block_hdr.CRC);
if (block_hdr.flags & 0x8) {
u8 *auxblk = ((void *)mc_data) + block_hdr.addr;
u16 auxtype;
if (block_hdr.addr + sizeof(u16) > size)
goto eof;
auxtype = be16_to_cpu(*(u32 *)(auxblk));
/* Aux block. Check type */
if (DRX_ISMCVERTYPE(auxtype)) {
if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size)
goto eof;
auxblk += sizeof(u16);
mc_dev_type = be32_to_cpu(*(u32 *)(auxblk));
auxblk += sizeof(u32);
mc_version = be32_to_cpu(*(u32 *)(auxblk));
auxblk += sizeof(u32);
mc_base_version = be32_to_cpu(*(u32 *)(auxblk));
DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
DRX_ATTR_MCRECORD(demod).mc_version = mc_version;
DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version;
pr_info("Firmware dev %x, ver %x, base ver %x\n",
mc_dev_type, mc_version, mc_base_version);
}
} else if (count + block_hdr.size * sizeof(u16) > size)
goto eof;
count += block_hdr.size * sizeof(u16);
}
return 0;
eof:
pr_err("Firmware is truncated at pos %u/%u\n", count, size);
return -EINVAL;
}
/**
* drx_ctrl_u_code - Handle microcode upload or verify.
* @dev_addr: Address of device.
* @mc_info: Pointer to information about microcode data.
* @action: Either UCODE_UPLOAD or UCODE_VERIFY
*
* This function returns:
* 0:
* - In case of UCODE_UPLOAD: code is successfully uploaded.
* - In case of UCODE_VERIFY: image on device is equal to
* image provided to this control function.
* -EIO:
* - In case of UCODE_UPLOAD: I2C error.
* - In case of UCODE_VERIFY: I2C error or image on device
* is not equal to image provided to this control function.
* -EINVAL:
* - Invalid arguments.
* - Provided image is corrupt
*/
static int drx_ctrl_u_code(struct drx_demod_instance *demod,
struct drxu_code_info *mc_info,
enum drxu_code_action action)
{
struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
int rc;
u16 i = 0;
u16 mc_nr_of_blks = 0;
u16 mc_magic_word = 0;
const u8 *mc_data_init = NULL;
u8 *mc_data = NULL;
unsigned size;
char *mc_file = mc_info->mc_file;
/* Check arguments */
if (!mc_info || !mc_file)
return -EINVAL;
if (!demod->firmware) {
const struct firmware *fw = NULL;
rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
if (rc < 0) {
pr_err("Couldn't read firmware %s\n", mc_file);
return -ENOENT;
}
demod->firmware = fw;
if (demod->firmware->size < 2 * sizeof(u16)) {
rc = -EINVAL;
pr_err("Firmware is too short!\n");
goto release;
}
pr_info("Firmware %s, size %zu\n",
mc_file, demod->firmware->size);
}
mc_data_init = demod->firmware->data;
size = demod->firmware->size;
mc_data = (void *)mc_data_init;
/* Check data */
mc_magic_word = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
rc = -EINVAL;
pr_err("Firmware magic word doesn't match\n");
goto release;
}
if (action == UCODE_UPLOAD) {
rc = drx_check_firmware(demod, (u8 *)mc_data_init, size);
if (rc)
goto release;
/* After scanning, validate the microcode.
It is also valid if no validation control exists.
*/
rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL);
if (rc != 0 && rc != -ENOTSUPP) {
pr_err("Validate ucode not supported\n");
return rc;
}
pr_info("Uploading firmware %s\n", mc_file);
} else if (action == UCODE_VERIFY) {
pr_info("Verifying if firmware upload was ok.\n");
}
/* Process microcode blocks */
for (i = 0; i < mc_nr_of_blks; i++) {
struct drxu_code_block_hdr block_hdr;
u16 mc_block_nr_bytes = 0;
/* Process block header */
block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u32);
block_hdr.size = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
(unsigned)(mc_data - mc_data_init), block_hdr.addr,
block_hdr.size, block_hdr.flags, block_hdr.CRC);
/* Check block header on:
- data larger than 64Kb
- if CRC enabled check CRC
*/
if ((block_hdr.size > 0x7FFF) ||
(((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
(block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size)))
) {
/* Wrong data ! */
rc = -EINVAL;
pr_err("firmware CRC is wrong\n");
goto release;
}
if (!block_hdr.size)
continue;
mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16));
/* Perform the desired action */
switch (action) {
case UCODE_UPLOAD: /* Upload microcode */
if (demod->my_access_funct->write_block_func(dev_addr,
block_hdr.addr,
mc_block_nr_bytes,
mc_data, 0x0000)) {
rc = -EIO;
pr_err("error writing firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
goto release;
}
break;
case UCODE_VERIFY: { /* Verify uploaded microcode */
int result = 0;
u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
u32 bytes_to_comp = 0;
u32 bytes_left = mc_block_nr_bytes;
u32 curr_addr = block_hdr.addr;
u8 *curr_ptr = mc_data;
while (bytes_left != 0) {
if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
else
bytes_to_comp = bytes_left;
if (demod->my_access_funct->
read_block_func(dev_addr,
curr_addr,
(u16)bytes_to_comp,
(u8 *)mc_data_buffer,
0x0000)) {
pr_err("error reading firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
return -EIO;
}
result =drxbsp_hst_memcmp(curr_ptr,
mc_data_buffer,
bytes_to_comp);
if (result) {
pr_err("error verifying firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
return -EIO;
}
curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
curr_ptr =&(curr_ptr[bytes_to_comp]);
bytes_left -=((u32) bytes_to_comp);
}
break;
}
default:
return -EINVAL;
break;
}
mc_data += mc_block_nr_bytes;
}
return 0;
release:
release_firmware(demod->firmware);
demod->firmware = NULL;
return rc;
}
/*============================================================================*/
/**
* drx_ctrl_version - Build list of version information.
* @demod: A pointer to a demodulator instance.
* @version_list: Pointer to linked list of versions.
*
* This function returns:
* 0: Version information stored in version_list
* -EINVAL: Invalid arguments.
*/
static int drx_ctrl_version(struct drx_demod_instance *demod,
struct drx_version_list **version_list)
{
static char drx_driver_core_module_name[] = "Core driver";
static char drx_driver_core_version_text[] =
DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
static struct drx_version drx_driver_core_version;
static struct drx_version_list drx_driver_core_version_list;
struct drx_version_list *demod_version_list = NULL;
int return_status = -EIO;
/* Check arguments */
if (version_list == NULL)
return -EINVAL;
/* Get version info list from demod */
return_status = (*(demod->my_demod_funct->ctrl_func)) (demod,
DRX_CTRL_VERSION,
(void *)
&demod_version_list);
/* Always fill in the information of the driver SW . */
drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE;
drx_driver_core_version.module_name = drx_driver_core_module_name;
drx_driver_core_version.v_major = VERSION_MAJOR;
drx_driver_core_version.v_minor = VERSION_MINOR;
drx_driver_core_version.v_patch = VERSION_PATCH;
drx_driver_core_version.v_string = drx_driver_core_version_text;
drx_driver_core_version_list.version = &drx_driver_core_version;
drx_driver_core_version_list.next = (struct drx_version_list *) (NULL);
if ((return_status == 0) && (demod_version_list != NULL)) {
/* Append versioninfo from driver to versioninfo from demod */
/* Return version info in "bottom-up" order. This way, multiple
devices can be handled without using malloc. */
struct drx_version_list *current_list_element = demod_version_list;
while (current_list_element->next != NULL)
current_list_element = current_list_element->next;
current_list_element->next = &drx_driver_core_version_list;
*version_list = demod_version_list;
} else {
/* Just return versioninfo from driver */
*version_list = &drx_driver_core_version_list;
}
return 0;
}
/*
* Exported functions
*/
/**
* drx_open - Open a demodulator instance.
* @demod: A pointer to a demodulator instance.
*
* This function returns:
* 0: Opened demod instance with succes.
* -EIO: Driver not initialized or unable to initialize
* demod.
* -EINVAL: Demod instance has invalid content.
*
*/
int drx_open(struct drx_demod_instance *demod)
{
int status = 0;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) ||
(demod->my_i2c_dev_addr == NULL) ||
(demod->my_common_attr->is_opened)) {
return -EINVAL;
}
status = (*(demod->my_demod_funct->open_func)) (demod);
if (status == 0)
demod->my_common_attr->is_opened = true;
return status;
}
/*============================================================================*/
/**
* drx_close - Close device
* @demod: A pointer to a demodulator instance.
*
* Free resources occupied by device instance.
* Put device into sleep mode.
*
* This function returns:
* 0: Closed demod instance with succes.
* -EIO: Driver not initialized or error during close
* demod.
* -EINVAL: Demod instance has invalid content.
*/
int drx_close(struct drx_demod_instance *demod)
{
int status = 0;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) ||
(demod->my_i2c_dev_addr == NULL) ||
(!demod->my_common_attr->is_opened)) {
return -EINVAL;
}
status = (*(demod->my_demod_funct->close_func)) (demod);
DRX_ATTR_ISOPENED(demod) = false;
return status;
}
/**
* drx_ctrl - Control the device.
* @demod: A pointer to a demodulator instance.
* @ctrl: Reference to desired control function.
* @ctrl_data: Pointer to data structure for control function.
*
* Data needed or returned by the control function is stored in ctrl_data.
*
* This function returns:
* 0: Control function completed successfully.
* -EIO: Driver not initialized or error during control demod.
* -EINVAL: Demod instance or ctrl_data has invalid content.
* -ENOTSUPP: Specified control function is not available.
*/
int drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data)
{
int status = -EIO;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL)
) {
return -EINVAL;
}
if (((!demod->my_common_attr->is_opened) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION))
) {
return -EINVAL;
}
if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) &&
(ctrl != DRX_CTRL_POWER_MODE) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) &&
(ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION)
)
) {
return -ENOTSUPP;
}
/* Fixed control functions */
switch (ctrl) {
/*======================================================================*/
case DRX_CTRL_NOP:
/* No operation */
return 0;
break;
/*======================================================================*/
case DRX_CTRL_VERSION:
return drx_ctrl_version(demod, (struct drx_version_list **)ctrl_data);
break;
/*======================================================================*/
default:
/* Do nothing */
break;
}
/* Virtual functions */
/* First try calling function from derived class */
status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data);
if (status == -ENOTSUPP) {
/* Now try calling a the base class function */
switch (ctrl) {
/*===================================================================*/
case DRX_CTRL_LOAD_UCODE:
return drx_ctrl_u_code(demod,
(struct drxu_code_info *)ctrl_data,
UCODE_UPLOAD);
break;
/*===================================================================*/
case DRX_CTRL_VERIFY_UCODE:
{
return drx_ctrl_u_code(demod,
(struct drxu_code_info *)ctrl_data,
UCODE_VERIFY);
}
break;
/*===================================================================*/
default:
pr_err("control %d not supported\n", ctrl);
return -ENOTSUPP;
}
} else {
return status;
}
return 0;
}
...@@ -39,6 +39,7 @@ INCLUDE FILES ...@@ -39,6 +39,7 @@ INCLUDE FILES
#include "drxj.h" #include "drxj.h"
#include "drxj_map.h" #include "drxj_map.h"
#include "drx_driver.h"
/*============================================================================*/ /*============================================================================*/
/*=== DEFINES ================================================================*/ /*=== DEFINES ================================================================*/
...@@ -309,6 +310,40 @@ DEFINES ...@@ -309,6 +310,40 @@ DEFINES
#define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0 #define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0
#define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1 #define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1
/*
* MICROCODE RELATED DEFINES
*/
/* Magic word for checking correct Endianess of microcode data */
#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L'))
/* CRC flag in ucode header, flags field. */
#define DRX_UCODE_CRC_FLAG (0x0001)
/*
* Maximum size of buffer used to verify the microcode.
* Must be an even number
*/
#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
#if DRX_UCODE_MAX_BUF_SIZE & 1
#error DRX_UCODE_MAX_BUF_SIZE must be an even number
#endif
/*
* Power mode macros
*/
#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \
(mode == DRX_POWER_MODE_10) || \
(mode == DRX_POWER_MODE_11) || \
(mode == DRX_POWER_MODE_12) || \
(mode == DRX_POWER_MODE_13) || \
(mode == DRX_POWER_MODE_14) || \
(mode == DRX_POWER_MODE_15) || \
(mode == DRX_POWER_MODE_16) || \
(mode == DRX_POWER_DOWN))
#ifdef DRXJ_SPLIT_UCODE_UPLOAD #ifdef DRXJ_SPLIT_UCODE_UPLOAD
/*============================================================================*/ /*============================================================================*/
/*=== MICROCODE RELATED DEFINES ==============================================*/ /*=== MICROCODE RELATED DEFINES ==============================================*/
...@@ -1050,20 +1085,25 @@ struct drxj_hi_cmd { ...@@ -1050,20 +1085,25 @@ struct drxj_hi_cmd {
u16 param6; u16 param6;
}; };
#ifdef DRXJ_SPLIT_UCODE_UPLOAD
/*============================================================================*/ /*============================================================================*/
/*=== MICROCODE RELATED STRUCTURES ===========================================*/ /*=== MICROCODE RELATED STRUCTURES ===========================================*/
/*============================================================================*/ /*============================================================================*/
/**
* struct drxu_code_block_hdr - Structure of the microcode block headers
*
* @addr: Destination address of the data in this block
* @size: Size of the block data following this header counted in
* 16 bits words
* @CRC: CRC value of the data block, only valid if CRC flag is
* set.
*/
struct drxu_code_block_hdr { struct drxu_code_block_hdr {
u32 addr; u32 addr;
u16 size; u16 size;
u16 flags; /* bit[15..2]=reserved, u16 flags;
bit[1]= compression on/off
bit[0]= CRC on/off */
u16 CRC; u16 CRC;
}; };
#endif /* DRXJ_SPLIT_UCODE_UPLOAD */
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
FUNCTIONS FUNCTIONS
...@@ -20607,3 +20647,550 @@ drxj_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data) ...@@ -20607,3 +20647,550 @@ drxj_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data)
} }
return 0; return 0;
} }
/*
* Microcode related functions
*/
/**
* drx_u_code_compute_crc - Compute CRC of block of microcode data.
* @block_data: Pointer to microcode data.
* @nr_words: Size of microcode block (number of 16 bits words).
*
* returns The computed CRC residue.
*/
static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
{
u16 i = 0;
u16 j = 0;
u32 crc_word = 0;
u32 carry = 0;
while (i < nr_words) {
crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data));
for (j = 0; j < 16; j++) {
crc_word <<= 1;
if (carry != 0)
crc_word ^= 0x80050000UL;
carry = crc_word & 0x80000000UL;
}
i++;
block_data += (sizeof(u16));
}
return (u16)(crc_word >> 16);
}
/**
* drx_check_firmware - checks if the loaded firmware is valid
*
* @demod: demod structure
* @mc_data: pointer to the start of the firmware
* @size: firmware size
*/
static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
unsigned size)
{
struct drxu_code_block_hdr block_hdr;
int i;
unsigned count = 2 * sizeof(u16);
u32 mc_dev_type, mc_version, mc_base_version;
u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16)));
/*
* Scan microcode blocks first for version info
* and firmware check
*/
/* Clear version block */
DRX_ATTR_MCRECORD(demod).aux_type = 0;
DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
DRX_ATTR_MCRECORD(demod).mc_version = 0;
DRX_ATTR_MCRECORD(demod).mc_base_version = 0;
for (i = 0; i < mc_nr_of_blks; i++) {
if (count + 3 * sizeof(u16) + sizeof(u32) > size)
goto eof;
/* Process block header */
block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u32);
block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count));
count += sizeof(u16);
pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
count, block_hdr.addr, block_hdr.size, block_hdr.flags,
block_hdr.CRC);
if (block_hdr.flags & 0x8) {
u8 *auxblk = ((void *)mc_data) + block_hdr.addr;
u16 auxtype;
if (block_hdr.addr + sizeof(u16) > size)
goto eof;
auxtype = be16_to_cpu(*(u32 *)(auxblk));
/* Aux block. Check type */
if (DRX_ISMCVERTYPE(auxtype)) {
if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size)
goto eof;
auxblk += sizeof(u16);
mc_dev_type = be32_to_cpu(*(u32 *)(auxblk));
auxblk += sizeof(u32);
mc_version = be32_to_cpu(*(u32 *)(auxblk));
auxblk += sizeof(u32);
mc_base_version = be32_to_cpu(*(u32 *)(auxblk));
DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
DRX_ATTR_MCRECORD(demod).mc_version = mc_version;
DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version;
pr_info("Firmware dev %x, ver %x, base ver %x\n",
mc_dev_type, mc_version, mc_base_version);
}
} else if (count + block_hdr.size * sizeof(u16) > size)
goto eof;
count += block_hdr.size * sizeof(u16);
}
return 0;
eof:
pr_err("Firmware is truncated at pos %u/%u\n", count, size);
return -EINVAL;
}
/**
* drx_ctrl_u_code - Handle microcode upload or verify.
* @dev_addr: Address of device.
* @mc_info: Pointer to information about microcode data.
* @action: Either UCODE_UPLOAD or UCODE_VERIFY
*
* This function returns:
* 0:
* - In case of UCODE_UPLOAD: code is successfully uploaded.
* - In case of UCODE_VERIFY: image on device is equal to
* image provided to this control function.
* -EIO:
* - In case of UCODE_UPLOAD: I2C error.
* - In case of UCODE_VERIFY: I2C error or image on device
* is not equal to image provided to this control function.
* -EINVAL:
* - Invalid arguments.
* - Provided image is corrupt
*/
static int drx_ctrl_u_code(struct drx_demod_instance *demod,
struct drxu_code_info *mc_info,
enum drxu_code_action action)
{
struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
int rc;
u16 i = 0;
u16 mc_nr_of_blks = 0;
u16 mc_magic_word = 0;
const u8 *mc_data_init = NULL;
u8 *mc_data = NULL;
unsigned size;
char *mc_file = mc_info->mc_file;
/* Check arguments */
if (!mc_info || !mc_file)
return -EINVAL;
if (!demod->firmware) {
const struct firmware *fw = NULL;
rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
if (rc < 0) {
pr_err("Couldn't read firmware %s\n", mc_file);
return -ENOENT;
}
demod->firmware = fw;
if (demod->firmware->size < 2 * sizeof(u16)) {
rc = -EINVAL;
pr_err("Firmware is too short!\n");
goto release;
}
pr_info("Firmware %s, size %zu\n",
mc_file, demod->firmware->size);
}
mc_data_init = demod->firmware->data;
size = demod->firmware->size;
mc_data = (void *)mc_data_init;
/* Check data */
mc_magic_word = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
rc = -EINVAL;
pr_err("Firmware magic word doesn't match\n");
goto release;
}
if (action == UCODE_UPLOAD) {
rc = drx_check_firmware(demod, (u8 *)mc_data_init, size);
if (rc)
goto release;
/* After scanning, validate the microcode.
It is also valid if no validation control exists.
*/
rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL);
if (rc != 0 && rc != -ENOTSUPP) {
pr_err("Validate ucode not supported\n");
return rc;
}
pr_info("Uploading firmware %s\n", mc_file);
} else if (action == UCODE_VERIFY) {
pr_info("Verifying if firmware upload was ok.\n");
}
/* Process microcode blocks */
for (i = 0; i < mc_nr_of_blks; i++) {
struct drxu_code_block_hdr block_hdr;
u16 mc_block_nr_bytes = 0;
/* Process block header */
block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u32);
block_hdr.size = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data));
mc_data += sizeof(u16);
pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
(unsigned)(mc_data - mc_data_init), block_hdr.addr,
block_hdr.size, block_hdr.flags, block_hdr.CRC);
/* Check block header on:
- data larger than 64Kb
- if CRC enabled check CRC
*/
if ((block_hdr.size > 0x7FFF) ||
(((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
(block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size)))
) {
/* Wrong data ! */
rc = -EINVAL;
pr_err("firmware CRC is wrong\n");
goto release;
}
if (!block_hdr.size)
continue;
mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16));
/* Perform the desired action */
switch (action) {
case UCODE_UPLOAD: /* Upload microcode */
if (demod->my_access_funct->write_block_func(dev_addr,
block_hdr.addr,
mc_block_nr_bytes,
mc_data, 0x0000)) {
rc = -EIO;
pr_err("error writing firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
goto release;
}
break;
case UCODE_VERIFY: { /* Verify uploaded microcode */
int result = 0;
u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
u32 bytes_to_comp = 0;
u32 bytes_left = mc_block_nr_bytes;
u32 curr_addr = block_hdr.addr;
u8 *curr_ptr = mc_data;
while (bytes_left != 0) {
if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
else
bytes_to_comp = bytes_left;
if (demod->my_access_funct->
read_block_func(dev_addr,
curr_addr,
(u16)bytes_to_comp,
(u8 *)mc_data_buffer,
0x0000)) {
pr_err("error reading firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
return -EIO;
}
result =drxbsp_hst_memcmp(curr_ptr,
mc_data_buffer,
bytes_to_comp);
if (result) {
pr_err("error verifying firmware at pos %u\n",
(unsigned)(mc_data - mc_data_init));
return -EIO;
}
curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
curr_ptr =&(curr_ptr[bytes_to_comp]);
bytes_left -=((u32) bytes_to_comp);
}
break;
}
default:
return -EINVAL;
break;
}
mc_data += mc_block_nr_bytes;
}
return 0;
release:
release_firmware(demod->firmware);
demod->firmware = NULL;
return rc;
}
/*============================================================================*/
/**
* drx_ctrl_version - Build list of version information.
* @demod: A pointer to a demodulator instance.
* @version_list: Pointer to linked list of versions.
*
* This function returns:
* 0: Version information stored in version_list
* -EINVAL: Invalid arguments.
*/
static int drx_ctrl_version(struct drx_demod_instance *demod,
struct drx_version_list **version_list)
{
static char drx_driver_core_module_name[] = "Core driver";
static char drx_driver_core_version_text[] =
DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
static struct drx_version drx_driver_core_version;
static struct drx_version_list drx_driver_core_version_list;
struct drx_version_list *demod_version_list = NULL;
int return_status = -EIO;
/* Check arguments */
if (version_list == NULL)
return -EINVAL;
/* Get version info list from demod */
return_status = (*(demod->my_demod_funct->ctrl_func)) (demod,
DRX_CTRL_VERSION,
(void *)
&demod_version_list);
/* Always fill in the information of the driver SW . */
drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE;
drx_driver_core_version.module_name = drx_driver_core_module_name;
drx_driver_core_version.v_major = VERSION_MAJOR;
drx_driver_core_version.v_minor = VERSION_MINOR;
drx_driver_core_version.v_patch = VERSION_PATCH;
drx_driver_core_version.v_string = drx_driver_core_version_text;
drx_driver_core_version_list.version = &drx_driver_core_version;
drx_driver_core_version_list.next = (struct drx_version_list *) (NULL);
if ((return_status == 0) && (demod_version_list != NULL)) {
/* Append versioninfo from driver to versioninfo from demod */
/* Return version info in "bottom-up" order. This way, multiple
devices can be handled without using malloc. */
struct drx_version_list *current_list_element = demod_version_list;
while (current_list_element->next != NULL)
current_list_element = current_list_element->next;
current_list_element->next = &drx_driver_core_version_list;
*version_list = demod_version_list;
} else {
/* Just return versioninfo from driver */
*version_list = &drx_driver_core_version_list;
}
return 0;
}
/*
* Exported functions
*/
/**
* drx_open - Open a demodulator instance.
* @demod: A pointer to a demodulator instance.
*
* This function returns:
* 0: Opened demod instance with succes.
* -EIO: Driver not initialized or unable to initialize
* demod.
* -EINVAL: Demod instance has invalid content.
*
*/
int drx_open(struct drx_demod_instance *demod)
{
int status = 0;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) ||
(demod->my_i2c_dev_addr == NULL) ||
(demod->my_common_attr->is_opened)) {
return -EINVAL;
}
status = (*(demod->my_demod_funct->open_func)) (demod);
if (status == 0)
demod->my_common_attr->is_opened = true;
return status;
}
/*============================================================================*/
/**
* drx_close - Close device
* @demod: A pointer to a demodulator instance.
*
* Free resources occupied by device instance.
* Put device into sleep mode.
*
* This function returns:
* 0: Closed demod instance with succes.
* -EIO: Driver not initialized or error during close
* demod.
* -EINVAL: Demod instance has invalid content.
*/
int drx_close(struct drx_demod_instance *demod)
{
int status = 0;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) ||
(demod->my_i2c_dev_addr == NULL) ||
(!demod->my_common_attr->is_opened)) {
return -EINVAL;
}
status = (*(demod->my_demod_funct->close_func)) (demod);
DRX_ATTR_ISOPENED(demod) = false;
return status;
}
/**
* drx_ctrl - Control the device.
* @demod: A pointer to a demodulator instance.
* @ctrl: Reference to desired control function.
* @ctrl_data: Pointer to data structure for control function.
*
* Data needed or returned by the control function is stored in ctrl_data.
*
* This function returns:
* 0: Control function completed successfully.
* -EIO: Driver not initialized or error during control demod.
* -EINVAL: Demod instance or ctrl_data has invalid content.
* -ENOTSUPP: Specified control function is not available.
*/
int drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data)
{
int status = -EIO;
if ((demod == NULL) ||
(demod->my_demod_funct == NULL) ||
(demod->my_common_attr == NULL) ||
(demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL)
) {
return -EINVAL;
}
if (((!demod->my_common_attr->is_opened) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION))
) {
return -EINVAL;
}
if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) &&
(ctrl != DRX_CTRL_POWER_MODE) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) &&
(ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION)
)
) {
return -ENOTSUPP;
}
/* Fixed control functions */
switch (ctrl) {
/*======================================================================*/
case DRX_CTRL_NOP:
/* No operation */
return 0;
break;
/*======================================================================*/
case DRX_CTRL_VERSION:
return drx_ctrl_version(demod, (struct drx_version_list **)ctrl_data);
break;
/*======================================================================*/
default:
/* Do nothing */
break;
}
/* Virtual functions */
/* First try calling function from derived class */
status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data);
if (status == -ENOTSUPP) {
/* Now try calling a the base class function */
switch (ctrl) {
/*===================================================================*/
case DRX_CTRL_LOAD_UCODE:
return drx_ctrl_u_code(demod,
(struct drxu_code_info *)ctrl_data,
UCODE_UPLOAD);
break;
/*===================================================================*/
case DRX_CTRL_VERIFY_UCODE:
{
return drx_ctrl_u_code(demod,
(struct drxu_code_info *)ctrl_data,
UCODE_VERIFY);
}
break;
/*===================================================================*/
default:
pr_err("control %d not supported\n", ctrl);
return -ENOTSUPP;
}
} else {
return status;
}
return 0;
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment