mirror of
https://github.com/S3NEO/android_kernel_samsung_msm8226.git
synced 2024-11-07 03:47:13 +00:00
bif: bif-core: add BIF NVM object writing support
Add a function to the bif-core driver which provides a means to write new BIF data objects into the non-volatile memory (NVM) of a BIF slave. Also add functions which can be used to overwrite or delete an existing BIF object. Change-Id: I6cd48409c696bd60a4d52b0fac8782c58d744df1 Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
parent
387d5fb5be
commit
2cf302f2a7
3 changed files with 456 additions and 39 deletions
|
@ -329,6 +329,25 @@ order to retrieve the set of BIF objects within a slave which match certain
|
|||
criteria. bif_object_put() is used to free the memory allocated by
|
||||
bif_object_match_get().
|
||||
|
||||
BIF object manipulation in slave non-volatile memory:
|
||||
-----------------------------------------------------
|
||||
int bif_object_write(struct bif_slave *slave, u8 type, u8 version, u16
|
||||
manufacturer_id, const u8 *data, int data_len);
|
||||
|
||||
int bif_object_overwrite(struct bif_slave *slave,
|
||||
struct bif_object *object, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len);
|
||||
|
||||
int bif_object_delete(struct bif_slave *slave, const struct bif_object *object);
|
||||
|
||||
bif_object_write() can be used to write a new BIF data object into the NVM of
|
||||
a given slave. The new object is added to the end of the NVM object list.
|
||||
bif_object_overwrite() can be used to overwrite an existing BIF data object
|
||||
in the NVM of a slave. The new object data must be the same size as the
|
||||
existing object data. bif_object_delete() can be used to delete a object from
|
||||
the NVM object list and shift all of the objects after it in order to fill the
|
||||
deleted object's space.
|
||||
|
||||
Get or set the BIF bus state or period:
|
||||
---------------------------------------
|
||||
|
||||
|
|
|
@ -833,6 +833,45 @@ static void bif_slave_ctrl_unlock(struct bif_slave *slave)
|
|||
bif_ctrl_unlock(&slave->ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* bif_crc_ccitt() - calculate the CRC-CCITT CRC value of the data specified
|
||||
* @buffer: Data to calculate the CRC of
|
||||
* @len: Length of the data buffer in bytes
|
||||
*
|
||||
* MIPI-BIF specifies the usage of CRC-CCITT for BIF data objects. This
|
||||
* function performs the CRC calculation while taking into account the bit
|
||||
* ordering used by BIF.
|
||||
*/
|
||||
u16 bif_crc_ccitt(const u8 *buffer, unsigned int len)
|
||||
{
|
||||
u16 crc = 0xFFFF;
|
||||
|
||||
while (len--) {
|
||||
crc = crc_ccitt_byte(crc, bitrev8(*buffer));
|
||||
buffer++;
|
||||
}
|
||||
return bitrev16(crc);
|
||||
}
|
||||
EXPORT_SYMBOL(bif_crc_ccitt);
|
||||
|
||||
static u16 bif_object_crc_ccitt(const struct bif_object *object)
|
||||
{
|
||||
u16 crc = 0xFFFF;
|
||||
int i;
|
||||
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->type));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->version));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id >> 8));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->length >> 8));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->length));
|
||||
|
||||
for (i = 0; i < object->length - 8; i++)
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->data[i]));
|
||||
|
||||
return bitrev16(crc);
|
||||
}
|
||||
|
||||
static int bif_check_task(struct bif_slave *slave, unsigned int task)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(slave)) {
|
||||
|
@ -1860,6 +1899,382 @@ void bif_object_put(struct bif_object *object)
|
|||
}
|
||||
EXPORT_SYMBOL(bif_object_put);
|
||||
|
||||
/* Copies the contents of object into buf following MIPI-BIF formatting. */
|
||||
static void bif_object_flatten(u8 *buf, const struct bif_object *object)
|
||||
{
|
||||
buf[0] = object->type;
|
||||
buf[1] = object->version;
|
||||
buf[2] = object->manufacturer_id >> 8;
|
||||
buf[3] = object->manufacturer_id;
|
||||
buf[4] = object->length >> 8;
|
||||
buf[5] = object->length;
|
||||
memcpy(&buf[6], object->data, object->length - 8);
|
||||
buf[object->length - 2] = object->crc >> 8;
|
||||
buf[object->length - 1] = object->crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* bif_object_write() - writes a new BIF object at the end of the object list in
|
||||
* the non-volatile memory of a slave
|
||||
* @slave: BIF slave handle
|
||||
* @type: Type of the object
|
||||
* @version: Version of the object
|
||||
* @manufacturer_id: Manufacturer ID number allocated by MIPI
|
||||
* @data: Data contained in the object
|
||||
* @data_len: Length of the data
|
||||
*
|
||||
* Returns 0 on success or errno on failure. This function will fail if the NVM
|
||||
* lock points to an offset after the BIF object list terminator (0x00).
|
||||
*/
|
||||
int bif_object_write(struct bif_slave *slave, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len)
|
||||
{
|
||||
struct bif_object *object;
|
||||
struct bif_object *tail_object;
|
||||
struct bif_nvm_function *nvm;
|
||||
int rc;
|
||||
int add_null = 0;
|
||||
u16 offset = 0;
|
||||
u8 *buf;
|
||||
|
||||
if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(data)) {
|
||||
pr_err("Invalid input pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nvm = slave->sdev->nvm_function;
|
||||
if (!nvm) {
|
||||
pr_err("BIF slave has no NVM function\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bif_slave_ctrl_lock(slave);
|
||||
if (nvm->object_count > 0) {
|
||||
tail_object = list_entry(nvm->object_list.prev,
|
||||
struct bif_object, list);
|
||||
offset = tail_object->addr - nvm->nvm_base_address
|
||||
+ tail_object->length;
|
||||
}
|
||||
|
||||
if (offset < nvm->nvm_lock_offset) {
|
||||
pr_err("Cannot write BIF object to NVM because the end of the object list is locked (end=%d < lock=%d)\n",
|
||||
offset, nvm->nvm_lock_offset);
|
||||
rc = -EPERM;
|
||||
goto error_unlock;
|
||||
} else if (offset + data_len + 8 > nvm->nvm_size) {
|
||||
pr_err("Cannot write BIF object to NVM because there is not enough remaining space (size=%d > remaining=%d)\n",
|
||||
data_len + 8, nvm->nvm_size - offset);
|
||||
rc = -EINVAL;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (offset + data_len + 8 < nvm->nvm_size)
|
||||
add_null = 1;
|
||||
object = kzalloc(sizeof(*object), GFP_KERNEL);
|
||||
if (!object) {
|
||||
pr_err("kzalloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
object->data = kzalloc(data_len, GFP_KERNEL);
|
||||
if (!object->data) {
|
||||
pr_err("kzalloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto free_object;
|
||||
}
|
||||
|
||||
buf = kzalloc(data_len + 8 + add_null, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_err("kzalloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto free_data;
|
||||
}
|
||||
|
||||
object->type = type;
|
||||
object->version = version;
|
||||
object->manufacturer_id = manufacturer_id;
|
||||
object->length = data_len + 8;
|
||||
memcpy(object->data, data, data_len);
|
||||
object->crc = bif_object_crc_ccitt(object);
|
||||
object->addr = offset + nvm->nvm_base_address;
|
||||
|
||||
bif_object_flatten(buf, object);
|
||||
if (add_null)
|
||||
buf[object->length] = BIF_OBJ_END_OF_LIST;
|
||||
|
||||
rc = _bif_slave_nvm_raw_write(slave->sdev, offset, buf,
|
||||
object->length + add_null);
|
||||
if (rc < 0) {
|
||||
pr_err("NVM write failed, rc=%d\n", rc);
|
||||
kfree(buf);
|
||||
goto free_data;
|
||||
}
|
||||
kfree(buf);
|
||||
|
||||
list_add_tail(&object->list, &nvm->object_list);
|
||||
nvm->object_count++;
|
||||
|
||||
bif_slave_ctrl_unlock(slave);
|
||||
|
||||
return rc;
|
||||
|
||||
free_data:
|
||||
kfree(object->data);
|
||||
free_object:
|
||||
kfree(object);
|
||||
error_unlock:
|
||||
bif_slave_ctrl_unlock(slave);
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(bif_object_write);
|
||||
|
||||
/*
|
||||
* Returns a pointer to the internal object referenced by a consumer object
|
||||
* if it exists. Returns NULL if the internal object cannot be found.
|
||||
*/
|
||||
static struct bif_object *bif_object_consumer_search(
|
||||
struct bif_nvm_function *nvm, const struct bif_object *consumer_object)
|
||||
{
|
||||
struct bif_object *object = NULL;
|
||||
struct bif_object *search_object;
|
||||
|
||||
/*
|
||||
* Internal struct in object linked list is pointed to by consumer
|
||||
* object list.prev.
|
||||
*/
|
||||
search_object = list_entry(consumer_object->list.prev,
|
||||
struct bif_object, list);
|
||||
|
||||
list_for_each_entry(object, &nvm->object_list, list) {
|
||||
if (object == search_object)
|
||||
break;
|
||||
}
|
||||
|
||||
if (object != search_object)
|
||||
return NULL;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* bif_object_overwrite() - overwrites an existing BIF object found in the
|
||||
* non-volatile memory of a slave
|
||||
* @slave: BIF slave handle
|
||||
* @object: Existing object in the slave to overwrite
|
||||
* @type: Type of the object
|
||||
* @version: Version of the object
|
||||
* @manufacturer_id: Manufacturer ID number allocated by MIPI
|
||||
* @data: Data contained in the object
|
||||
* @data_len: Length of the data
|
||||
*
|
||||
* Returns 0 on success or errno on failure. The data stored within 'object'
|
||||
* is updated to the new values upon success. The new data written to the
|
||||
* object must have exactly the same length as the old data (i.e.
|
||||
* data_len == object->length - 8).
|
||||
*
|
||||
* This function will fail if the NVM lock points to an offset after the
|
||||
* beginning of the existing BIF object.
|
||||
*/
|
||||
int bif_object_overwrite(struct bif_slave *slave,
|
||||
struct bif_object *object, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len)
|
||||
{
|
||||
struct bif_object *edit_object = NULL;
|
||||
struct bif_nvm_function *nvm;
|
||||
int rc;
|
||||
u16 crc;
|
||||
u8 *buf;
|
||||
|
||||
if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(object)
|
||||
|| IS_ERR_OR_NULL(data)) {
|
||||
pr_err("Invalid input pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nvm = slave->sdev->nvm_function;
|
||||
if (!nvm) {
|
||||
pr_err("BIF slave has no NVM function\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (data_len + 8 != object->length) {
|
||||
pr_err("New data length=%d is different from existing length=%d\n",
|
||||
data_len, object->length - 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bif_slave_ctrl_lock(slave);
|
||||
|
||||
edit_object = bif_object_consumer_search(nvm, object);
|
||||
if (!edit_object) {
|
||||
pr_err("BIF object not found within slave\n");
|
||||
rc = -EINVAL;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (edit_object->addr - nvm->nvm_base_address < nvm->nvm_lock_offset) {
|
||||
pr_err("Cannot overwrite BIF object in NVM because some portion of it is locked\n");
|
||||
rc = -EPERM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
buf = kzalloc(data_len + 8, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_err("kzalloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
buf[0] = type;
|
||||
buf[1] = version;
|
||||
buf[2] = manufacturer_id >> 8;
|
||||
buf[3] = manufacturer_id;
|
||||
buf[4] = (data_len + 8) >> 8;
|
||||
buf[5] = data_len + 8;
|
||||
memcpy(&buf[6], data, data_len);
|
||||
crc = bif_crc_ccitt(buf, data_len + 6);
|
||||
buf[data_len + 6] = crc >> 8;
|
||||
buf[data_len + 7] = crc;
|
||||
|
||||
rc = _bif_slave_nvm_raw_write(slave->sdev,
|
||||
object->addr - nvm->nvm_base_address, buf, data_len + 8);
|
||||
if (rc < 0) {
|
||||
pr_err("NVM write failed, rc=%d\n", rc);
|
||||
kfree(buf);
|
||||
goto error_unlock;
|
||||
}
|
||||
kfree(buf);
|
||||
|
||||
/* Update internal object struct. */
|
||||
edit_object->type = type;
|
||||
edit_object->version = version;
|
||||
edit_object->manufacturer_id = manufacturer_id;
|
||||
edit_object->length = data_len + 8;
|
||||
memcpy(edit_object->data, data, data_len);
|
||||
edit_object->crc = crc;
|
||||
|
||||
/* Update consumer object struct. */
|
||||
object->type = type;
|
||||
object->version = version;
|
||||
object->manufacturer_id = manufacturer_id;
|
||||
object->length = data_len + 8;
|
||||
memcpy(object->data, data, data_len);
|
||||
object->crc = crc;
|
||||
|
||||
error_unlock:
|
||||
bif_slave_ctrl_unlock(slave);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(bif_object_overwrite);
|
||||
|
||||
/**
|
||||
* bif_object_delete() - deletes an existing BIF object found in the
|
||||
* non-volatile memory of a slave. Objects found in the
|
||||
* object list in the NVM of the slave are shifted forward
|
||||
* in order to fill the hole left by the deleted object
|
||||
* @slave: BIF slave handle
|
||||
* @object: Existing object in the slave to delete
|
||||
*
|
||||
* Returns 0 on success or errno on failure. bif_object_put() must still be
|
||||
* called after this function in order to free the memory in the consumer
|
||||
* 'object' struct pointer.
|
||||
*
|
||||
* This function will fail if the NVM lock points to an offset after the
|
||||
* beginning of the existing BIF object.
|
||||
*/
|
||||
int bif_object_delete(struct bif_slave *slave, const struct bif_object *object)
|
||||
{
|
||||
struct bif_object *del_object = NULL;
|
||||
struct bif_object *tail_object;
|
||||
struct bif_nvm_function *nvm;
|
||||
bool found = false;
|
||||
int pos = 0;
|
||||
int rc;
|
||||
u8 *buf;
|
||||
|
||||
if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(object)) {
|
||||
pr_err("Invalid input pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nvm = slave->sdev->nvm_function;
|
||||
if (!nvm) {
|
||||
pr_err("BIF slave has no NVM function\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bif_slave_ctrl_lock(slave);
|
||||
|
||||
del_object = bif_object_consumer_search(nvm, object);
|
||||
if (!del_object) {
|
||||
pr_err("BIF object not found within slave\n");
|
||||
rc = -EINVAL;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (del_object->addr - nvm->nvm_base_address < nvm->nvm_lock_offset) {
|
||||
pr_err("Cannot delete BIF object in NVM because some portion of it is locked\n");
|
||||
rc = -EPERM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
buf = kmalloc(nvm->nvm_size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_err("kzalloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the contents of objects after the one to be deleted into a flat
|
||||
* array.
|
||||
*/
|
||||
list_for_each_entry(tail_object, &nvm->object_list, list) {
|
||||
if (found) {
|
||||
bif_object_flatten(&buf[pos], tail_object);
|
||||
pos += tail_object->length;
|
||||
} else if (tail_object == del_object) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the list terminator. */
|
||||
buf[pos++] = BIF_OBJ_END_OF_LIST;
|
||||
|
||||
rc = _bif_slave_nvm_raw_write(slave->sdev,
|
||||
del_object->addr - nvm->nvm_base_address, buf, pos);
|
||||
if (rc < 0) {
|
||||
pr_err("NVM write failed, rc=%d\n", rc);
|
||||
kfree(buf);
|
||||
goto error_unlock;
|
||||
}
|
||||
kfree(buf);
|
||||
|
||||
/* Update the addresses of the objects after the one to be deleted. */
|
||||
found = false;
|
||||
list_for_each_entry(tail_object, &nvm->object_list, list) {
|
||||
if (found)
|
||||
tail_object->addr -= del_object->length;
|
||||
else if (tail_object == del_object)
|
||||
found = true;
|
||||
}
|
||||
|
||||
list_del(&del_object->list);
|
||||
kfree(del_object->data);
|
||||
kfree(del_object);
|
||||
nvm->object_count--;
|
||||
|
||||
error_unlock:
|
||||
bif_slave_ctrl_unlock(slave);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(bif_object_delete);
|
||||
|
||||
/**
|
||||
* bif_slave_read() - read contiguous memory values from a BIF slave
|
||||
* @slave: BIF slave handle
|
||||
|
@ -2529,45 +2944,6 @@ static int bif_initialize_slave_control_function(struct bif_slave_dev *sdev,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* bif_crc_ccitt() - calculate the CRC-CCITT CRC value of the data specified
|
||||
* @buffer: Data to calculate the CRC of
|
||||
* @len: Length of the data buffer in bytes
|
||||
*
|
||||
* MIPI-BIF specifies the usage of CRC-CCITT for BIF data objects. This
|
||||
* function performs the CRC calculation while taking into account the bit
|
||||
* ordering used by BIF.
|
||||
*/
|
||||
u16 bif_crc_ccitt(const u8 *buffer, unsigned int len)
|
||||
{
|
||||
u16 crc = 0xFFFF;
|
||||
|
||||
while (len--) {
|
||||
crc = crc_ccitt_byte(crc, bitrev8(*buffer));
|
||||
buffer++;
|
||||
}
|
||||
return bitrev16(crc);
|
||||
}
|
||||
EXPORT_SYMBOL(bif_crc_ccitt);
|
||||
|
||||
static u16 bif_object_crc_ccitt(const struct bif_object *object)
|
||||
{
|
||||
u16 crc = 0xFFFF;
|
||||
int i;
|
||||
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->type));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->version));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id >> 8));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->manufacturer_id));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->length >> 8));
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->length));
|
||||
|
||||
for (i = 0; i < object->length - 8; i++)
|
||||
crc = crc_ccitt_byte(crc, bitrev8(object->data[i]));
|
||||
|
||||
return bitrev16(crc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the specified function is an NVM function and if it is, then
|
||||
* instantiate NVM function data for the slave and read all objects.
|
||||
|
|
|
@ -564,6 +564,15 @@ int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset, u8 *buf,
|
|||
int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset, u8 *buf,
|
||||
int len);
|
||||
|
||||
int bif_object_write(struct bif_slave *slave, u8 type, u8 version, u16
|
||||
manufacturer_id, const u8 *data, int data_len);
|
||||
|
||||
int bif_object_overwrite(struct bif_slave *slave,
|
||||
struct bif_object *object, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len);
|
||||
|
||||
int bif_object_delete(struct bif_slave *slave, const struct bif_object *object);
|
||||
|
||||
int bif_slave_is_present(struct bif_slave *slave);
|
||||
|
||||
int bif_slave_is_selected(struct bif_slave *slave);
|
||||
|
@ -664,6 +673,19 @@ static inline int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset,
|
|||
u8 *buf, int len)
|
||||
{ return -EPERM; }
|
||||
|
||||
static inline int bif_object_write(struct bif_slave *slave, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len)
|
||||
{ return -EPERM; }
|
||||
|
||||
static inline int bif_object_overwrite(struct bif_slave *slave,
|
||||
struct bif_object *object, u8 type, u8 version,
|
||||
u16 manufacturer_id, const u8 *data, int data_len)
|
||||
{ return -EPERM; }
|
||||
|
||||
static inline int bif_object_delete(struct bif_slave *slave,
|
||||
const struct bif_object *object)
|
||||
{ return -EPERM; }
|
||||
|
||||
static inline int bif_slave_is_present(struct bif_slave *slave)
|
||||
{ return -EPERM; }
|
||||
|
||||
|
|
Loading…
Reference in a new issue