| /* Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/bitrev.h> |
| #include <linux/crc-ccitt.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/of.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| #include <linux/workqueue.h> |
| #include <linux/bif/consumer.h> |
| #include <linux/bif/driver.h> |
| |
| /** |
| * struct bif_ctrl_dev - holds controller device specific information |
| * @list: Doubly-linked list parameter linking to other |
| * BIF controllers registered in the system |
| * @desc: Description structure for this BIF controller |
| * @mutex: Mutex lock that is used to ensure mutual |
| * exclusion between transactions performed on the |
| * BIF bus for this controller |
| * @ctrl_dev: Device pointer to the BIF controller device |
| * @driver_data: Private data used by the BIF controller |
| * @selected_sdev: Slave device that is currently selected on |
| * the BIF bus of this controller |
| * @bus_change_notifier: Head of a notifier list containing notifier |
| * blocks that are notified when the battery |
| * presence changes |
| * @enter_irq_mode_work: Work task that is scheduled after a transaction |
| * completes when there are consumers that are |
| * actively monitoring BIF slave interrupts |
| * @irq_count: This is a count of the total number of BIF slave |
| * interrupts that are currently being monitored |
| * for the BIF slaves connected to this BIF |
| * controller |
| * @irq_mode_delay_jiffies: Number of jiffies to wait before scheduling the |
| * enter IRQ mode task. Using a larger value |
| * helps to improve the performance of BIF |
| * consumers that perform many BIF transactions. |
| * Using a smaller value reduces the latency of |
| * BIF slave interrupts. |
| * @battery_present: Cached value of the battery presence. This is |
| * used to filter out spurious presence update |
| * calls when the battery presence state has not |
| * changed. |
| */ |
| struct bif_ctrl_dev { |
| struct list_head list; |
| struct bif_ctrl_desc *desc; |
| struct mutex mutex; |
| struct device *ctrl_dev; |
| void *driver_data; |
| struct bif_slave_dev *selected_sdev; |
| struct blocking_notifier_head bus_change_notifier; |
| struct delayed_work enter_irq_mode_work; |
| int irq_count; |
| int irq_mode_delay_jiffies; |
| bool battery_present; |
| }; |
| |
| /** |
| * struct bif_ctrl - handle used by BIF consumers for bus oriented BIF |
| * operations |
| * @bdev: Pointer to BIF controller device |
| * @exclusive_lock: Flag which indicates that the BIF consumer responsible |
| * for this handle has locked the BIF bus of this |
| * controller. BIF transactions from other consumers are |
| * blocked until the bus is unlocked. |
| */ |
| struct bif_ctrl { |
| struct bif_ctrl_dev *bdev; |
| bool exclusive_lock; |
| }; |
| |
| /** |
| * struct bif_slave_dev - holds BIF slave device information |
| * @list: Doubly-linked list parameter linking to other |
| * BIF slaves that have been enumerated |
| * @bdev: Pointer to the BIF controller device that this |
| * slave is physically connected to |
| * @slave_addr: 8-bit BIF DEV_ADR assigned to this slave |
| * @unique_id: 80-bit BIF unique ID of the slave |
| * @unique_id_bits_known: Number of bits of the UID that are currently |
| * known. This number starts is incremented during |
| * a UID search and must end at 80 if the slave |
| * responds to the search properly. |
| * @present: Boolean value showing if this slave is |
| * physically present in the system at a given |
| * point in time. The value is set to false if the |
| * battery pack containing the slave is |
| * disconnected. |
| * @l1_data: BIF DDB L1 data of the slave as read from the |
| * slave's memory |
| * @function_directory: Pointer to the BIF DDB L2 function directory |
| * list as read from the slave's memory |
| * @protocol_function: Pointer to constant protocol function data as |
| * well as software state information if the slave |
| * has a protocol function |
| * @slave_ctrl_function: Pointer to constant slave control function data |
| * as well as software state information if the |
| * slave has a slave control function |
| * @nvm_function: Pointer to constant non-volatile memory function |
| * data as well as software state information if |
| * the slave has a non-volatile memory function |
| * |
| * bif_slave_dev objects are stored indefinitely after enumeration in order to |
| * speed up battery reinsertion. Only a UID check is needed after inserting a |
| * battery assuming it has been enumerated before. |
| * |
| * unique_id bytes are stored such that unique_id[0] = MSB and |
| * unique_id[BIF_UNIQUE_ID_BYTE_LENGTH - 1] = LSB |
| */ |
| struct bif_slave_dev { |
| struct list_head list; |
| struct bif_ctrl_dev *bdev; |
| u8 slave_addr; |
| u8 unique_id[BIF_UNIQUE_ID_BYTE_LENGTH]; |
| int unique_id_bits_known; |
| bool present; |
| struct bif_ddb_l1_data l1_data; |
| struct bif_ddb_l2_data *function_directory; |
| struct bif_protocol_function *protocol_function; |
| struct bif_slave_control_function *slave_ctrl_function; |
| struct bif_nvm_function *nvm_function; |
| }; |
| |
| /** |
| * struct bif_slave - handle used by BIF consumers for slave oriented BIF |
| * operations |
| * @ctrl: Consumer BIF controller handle data |
| * @sdev: Pointer to BIF slave device |
| */ |
| struct bif_slave { |
| struct bif_ctrl ctrl; |
| struct bif_slave_dev *sdev; |
| }; |
| |
| /* Number of times to retry a full BIF transaction before returning an error. */ |
| #define BIF_TRANSACTION_RETRY_COUNT 5 |
| |
| static DEFINE_MUTEX(bif_ctrl_list_mutex); |
| static LIST_HEAD(bif_ctrl_list); |
| static DEFINE_MUTEX(bif_sdev_list_mutex); |
| static LIST_HEAD(bif_sdev_list); |
| |
| static u8 next_dev_addr = 0x02; |
| |
| #define DEBUG_PRINT_BUFFER_SIZE 256 |
| static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) |
| { |
| int pos = 0; |
| int i; |
| |
| for (i = 0; i < buf_len; i++) { |
| pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]); |
| if (i < buf_len - 1) |
| pos += scnprintf(str + pos, str_len - pos, ", "); |
| } |
| } |
| |
| static void bif_print_slave_data(struct bif_slave_dev *sdev) |
| { |
| char str[DEBUG_PRINT_BUFFER_SIZE]; |
| u8 *uid; |
| int i, j; |
| struct bif_object *object; |
| |
| if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH) |
| return; |
| |
| uid = sdev->unique_id; |
| pr_debug("BIF slave: 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", |
| uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], |
| uid[7], uid[8], uid[9]); |
| pr_debug(" present=%d, dev_adr=0x%02X\n", sdev->present, |
| sdev->slave_addr); |
| pr_debug(" revision=0x%02X, level=0x%02X, device class=0x%04X\n", |
| sdev->l1_data.revision, sdev->l1_data.level, |
| sdev->l1_data.device_class); |
| pr_debug(" manufacturer ID=0x%04X, product ID=0x%04X\n", |
| sdev->l1_data.manufacturer_id, sdev->l1_data.product_id); |
| pr_debug(" function directory length=%d\n", sdev->l1_data.length); |
| |
| for (i = 0; i < sdev->l1_data.length / 4; i++) { |
| pr_debug(" Function %d: type=0x%02X, version=0x%02X, pointer=0x%04X\n", |
| i, sdev->function_directory[i].function_type, |
| sdev->function_directory[i].function_version, |
| sdev->function_directory[i].function_pointer); |
| } |
| |
| if (sdev->nvm_function) { |
| pr_debug(" NVM function: pointer=0x%04X, task=%d, wr_buf_size=%d, nvm_base=0x%04X, nvm_size=%d, nvm_lock_offset=%d\n", |
| sdev->nvm_function->nvm_pointer, |
| sdev->nvm_function->slave_control_channel, |
| (sdev->nvm_function->write_buffer_size |
| ? sdev->nvm_function->write_buffer_size : 0), |
| sdev->nvm_function->nvm_base_address, |
| sdev->nvm_function->nvm_size, |
| sdev->nvm_function->nvm_lock_offset); |
| if (sdev->nvm_function->object_count) |
| pr_debug(" NVM objects:\n"); |
| i = 0; |
| list_for_each_entry(object, &sdev->nvm_function->object_list, |
| list) { |
| pr_debug(" Object %d - addr=0x%04X, data len=%d, type=0x%02X, version=0x%02X, manufacturer ID=0x%04X, crc=0x%04X\n", |
| i, object->addr, object->length - 8, |
| object->type, object->version, |
| object->manufacturer_id, object->crc); |
| for (j = 0; j < DIV_ROUND_UP(object->length - 8, 16); |
| j++) { |
| fill_string(str, DEBUG_PRINT_BUFFER_SIZE, |
| object->data + j * 16, |
| min(16, object->length - 8 - (j * 16))); |
| pr_debug(" data(0x%04X): %s\n", j * 16, |
| str); |
| } |
| i++; |
| } |
| } |
| } |
| |
| static void bif_print_slaves(void) |
| { |
| struct bif_slave_dev *sdev; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| /* Skip slaves without fully known UIDs. */ |
| if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH) |
| continue; |
| bif_print_slave_data(sdev); |
| } |
| |
| mutex_unlock(&bif_sdev_list_mutex); |
| } |
| |
| static struct bif_slave_dev *bif_add_slave(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| |
| sdev = kzalloc(sizeof(struct bif_slave_dev), GFP_KERNEL); |
| if (sdev == NULL) { |
| pr_err("Memory allocation failed for bif_slave_dev\n"); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| sdev->bdev = bdev; |
| INIT_LIST_HEAD(&sdev->list); |
| list_add_tail(&sdev->list, &bif_sdev_list); |
| |
| return sdev; |
| } |
| |
| static void bif_remove_slave(struct bif_slave_dev *sdev) |
| { |
| list_del(&sdev->list); |
| if (sdev->bdev->selected_sdev == sdev) |
| sdev->bdev->selected_sdev = NULL; |
| |
| if (sdev->slave_ctrl_function) |
| kfree(sdev->slave_ctrl_function->irq_notifier_list); |
| kfree(sdev->slave_ctrl_function); |
| kfree(sdev->protocol_function); |
| kfree(sdev->function_directory); |
| |
| kfree(sdev); |
| } |
| |
| /* This function assumes that the uid array is all 0 to start with. */ |
| static void set_uid_bit(u8 uid[BIF_UNIQUE_ID_BYTE_LENGTH], unsigned int bit, |
| unsigned int value) |
| { |
| u8 mask; |
| |
| if (bit >= BIF_UNIQUE_ID_BIT_LENGTH) |
| return; |
| |
| mask = 1 << (7 - (bit % 8)); |
| |
| uid[bit / 8] &= ~mask; |
| uid[bit / 8] |= value << (7 - (bit % 8)); |
| } |
| |
| static unsigned int get_uid_bit(u8 uid[BIF_UNIQUE_ID_BYTE_LENGTH], |
| unsigned int bit) |
| { |
| if (bit >= BIF_UNIQUE_ID_BIT_LENGTH) |
| return 0; |
| |
| return (uid[bit / 8] & (1 << (7 - (bit % 8)))) ? 1 : 0; |
| } |
| |
| static void bif_enter_irq_mode_work(struct work_struct *work) |
| { |
| struct delayed_work *dwork = to_delayed_work(work); |
| struct bif_ctrl_dev *bdev |
| = container_of(dwork, struct bif_ctrl_dev, enter_irq_mode_work); |
| int rc, i; |
| |
| mutex_lock(&bdev->mutex); |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| rc = bdev->desc->ops->set_bus_state(bdev, |
| BIF_BUS_STATE_INTERRUPT); |
| if (rc == 0) |
| break; |
| } |
| mutex_unlock(&bdev->mutex); |
| |
| /* Reschedule the task if the transaction failed. */ |
| if (rc) { |
| pr_err("Could not set BIF bus to interrupt mode, rc=%d\n", rc); |
| schedule_delayed_work(&bdev->enter_irq_mode_work, |
| bdev->irq_mode_delay_jiffies); |
| } |
| } |
| |
| static void bif_cancel_irq_mode_work(struct bif_ctrl_dev *bdev) |
| { |
| cancel_delayed_work(&bdev->enter_irq_mode_work); |
| } |
| |
| static void bif_schedule_irq_mode_work(struct bif_ctrl_dev *bdev) |
| { |
| if (bdev->irq_count > 0 && |
| bdev->desc->ops->get_bus_state(bdev) != BIF_BUS_STATE_INTERRUPT) |
| schedule_delayed_work(&bdev->enter_irq_mode_work, |
| bdev->irq_mode_delay_jiffies); |
| } |
| |
| static int _bif_select_slave_no_retry(struct bif_slave_dev *sdev) |
| { |
| struct bif_ctrl_dev *bdev = sdev->bdev; |
| int rc = 0; |
| int i; |
| |
| /* Check if the slave is already selected. */ |
| if (sdev->bdev->selected_sdev == sdev) |
| return 0; |
| |
| if (sdev->slave_addr) { |
| /* Select using DEV_ADR. */ |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA, |
| sdev->slave_addr); |
| if (!rc) |
| sdev->bdev->selected_sdev = sdev; |
| } else if (sdev->unique_id_bits_known == BIF_UNIQUE_ID_BIT_LENGTH) { |
| /* Select using full UID. */ |
| for (i = 0; i < BIF_UNIQUE_ID_BYTE_LENGTH - 1; i++) { |
| rc = bdev->desc->ops->bus_transaction(bdev, |
| BIF_TRANS_EDA, sdev->unique_id[i]); |
| if (rc) |
| goto out; |
| } |
| |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA, |
| sdev->unique_id[BIF_UNIQUE_ID_BYTE_LENGTH - 1]); |
| if (rc) |
| goto out; |
| } else { |
| pr_err("Cannot select slave because it has neither UID nor DEV_ADR.\n"); |
| return -EINVAL; |
| } |
| |
| sdev->bdev->selected_sdev = sdev; |
| |
| return 0; |
| out: |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| static int bif_select_slave(struct bif_slave_dev *sdev) |
| { |
| int rc = -EPERM; |
| int i; |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| rc = _bif_select_slave_no_retry(sdev); |
| if (rc == 0) |
| break; |
| /* Force slave reselection. */ |
| sdev->bdev->selected_sdev = NULL; |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Returns 1 if slave is selected, 0 if slave is not selected, or errno if |
| * error. |
| */ |
| static int bif_is_slave_selected(struct bif_ctrl_dev *bdev) |
| { |
| int rc = -EPERM; |
| int tack, i; |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| /* Attempt a transaction query. */ |
| rc = bdev->desc->ops->bus_transaction_read(bdev, BIF_TRANS_BC, |
| BIF_CMD_TQ, &tack); |
| if (rc == 0 || rc == -ETIMEDOUT) |
| break; |
| } |
| |
| if (rc == 0) |
| rc = 1; |
| else if (rc == -ETIMEDOUT) |
| rc = 0; |
| else |
| pr_err("BIF bus_transaction_read failed, rc=%d\n", rc); |
| |
| return rc; |
| } |
| |
| /* Read from a specified number of consecutive registers. */ |
| static int _bif_slave_read_no_retry(struct bif_slave_dev *sdev, u16 addr, |
| u8 *buf, int len) |
| { |
| struct bif_ctrl_dev *bdev = sdev->bdev; |
| int rc = 0; |
| int i, response; |
| |
| rc = bif_select_slave(sdev); |
| if (rc) |
| return rc; |
| |
| if (bdev->desc->ops->read_slave_registers) { |
| /* |
| * Use low level slave register read implementation in order to |
| * receive the benefits of BIF burst reads. |
| */ |
| rc = bdev->desc->ops->read_slave_registers(bdev, addr, buf, |
| len); |
| if (rc) |
| pr_debug("read_slave_registers failed, rc=%d\n", rc); |
| else |
| return rc; |
| /* |
| * Fall back on individual transactions if high level register |
| * read failed. |
| */ |
| } |
| |
| for (i = 0; i < len; i++) { |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_ERA, |
| addr >> 8); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = bdev->desc->ops->bus_transaction_read(bdev, BIF_TRANS_RRA, |
| addr & 0xFF, &response); |
| if (rc) { |
| pr_err("bus_transaction_read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| if (!(response & BIF_SLAVE_RD_ACK)) { |
| pr_err("BIF register read error=0x%02X\n", |
| response & BIF_SLAVE_RD_ERR); |
| return -EIO; |
| } |
| |
| buf[i] = response & BIF_SLAVE_RD_DATA; |
| addr++; |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Read from a specified number of consecutive registers. Retry the transaction |
| * several times in case of communcation failures. |
| */ |
| static int _bif_slave_read(struct bif_slave_dev *sdev, u16 addr, u8 *buf, |
| int len) |
| { |
| int rc = -EPERM; |
| int i; |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| rc = _bif_slave_read_no_retry(sdev, addr, buf, len); |
| if (rc == 0) |
| break; |
| /* Force slave reselection. */ |
| sdev->bdev->selected_sdev = NULL; |
| } |
| |
| return rc; |
| } |
| |
| /* Write to a specified number of consecutive registers. */ |
| static int _bif_slave_write_no_retry(struct bif_slave_dev *sdev, u16 addr, |
| u8 *buf, int len) |
| { |
| struct bif_ctrl_dev *bdev = sdev->bdev; |
| int rc = 0; |
| int i; |
| |
| rc = bif_select_slave(sdev); |
| if (rc) |
| return rc; |
| |
| if (bdev->desc->ops->write_slave_registers) { |
| /* |
| * Use low level slave register write implementation in order to |
| * receive the benefits of BIF burst writes. |
| */ |
| rc = bdev->desc->ops->write_slave_registers(bdev, addr, buf, |
| len); |
| if (rc) |
| pr_debug("write_slave_registers failed, rc=%d\n", rc); |
| else |
| return rc; |
| /* |
| * Fall back on individual transactions if high level register |
| * write failed. |
| */ |
| } |
| |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_ERA, addr >> 8); |
| if (rc) |
| goto out; |
| |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_WRA, addr & 0xFF); |
| if (rc) |
| goto out; |
| |
| for (i = 0; i < len; i++) { |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_WD, |
| buf[i]); |
| if (rc) |
| goto out; |
| } |
| |
| return 0; |
| out: |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* |
| * Write to a specified number of consecutive registers. Retry the transaction |
| * several times in case of communcation failures. |
| */ |
| static int _bif_slave_write(struct bif_slave_dev *sdev, u16 addr, u8 *buf, |
| int len) |
| { |
| int rc = -EPERM; |
| int i; |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| rc = _bif_slave_write_no_retry(sdev, addr, buf, len); |
| if (rc == 0) |
| break; |
| /* Force slave reselection. */ |
| sdev->bdev->selected_sdev = NULL; |
| } |
| |
| return rc; |
| } |
| |
| /* Perform a read-modify-write sequence on a single BIF slave register. */ |
| static int _bif_slave_masked_write(struct bif_slave_dev *sdev, u16 addr, u8 val, |
| u8 mask) |
| { |
| int rc; |
| u8 reg; |
| |
| rc = _bif_slave_read(sdev, addr, ®, 1); |
| if (rc) |
| return rc; |
| |
| reg = (reg & ~mask) | (val & mask); |
| |
| return _bif_slave_write(sdev, addr, ®, 1); |
| } |
| |
| static int _bif_check_task(struct bif_slave_dev *sdev, unsigned int task) |
| { |
| if (IS_ERR_OR_NULL(sdev)) { |
| pr_err("Invalid slave device handle=%ld\n", PTR_ERR(sdev)); |
| return -EINVAL; |
| } else if (!sdev->bdev) { |
| pr_err("BIF controller has been removed\n"); |
| return -ENXIO; |
| } else if (!sdev->slave_ctrl_function |
| || sdev->slave_ctrl_function->task_count == 0) { |
| pr_err("BIF slave does not support slave control\n"); |
| return -ENODEV; |
| } else if (task >= sdev->slave_ctrl_function->task_count) { |
| pr_err("Requested task: %u greater than max: %u for this slave\n", |
| task, sdev->slave_ctrl_function->task_count); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int _bif_task_is_busy(struct bif_slave_dev *sdev, unsigned int task) |
| { |
| int rc; |
| u16 addr; |
| u8 reg = 0; |
| |
| rc = _bif_check_task(sdev, task); |
| if (rc) { |
| pr_err("Invalid slave device or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Check the task busy state. */ |
| addr = SLAVE_CTRL_FUNC_TASK_BUSY_ADDR( |
| sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| rc = _bif_slave_read(sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| return (reg & BIT(task % SLAVE_CTRL_TASKS_PER_SET)) ? 1 : 0; |
| } |
| |
| static int _bif_enable_auto_task(struct bif_slave_dev *sdev, unsigned int task) |
| { |
| int rc; |
| u16 addr; |
| u8 mask; |
| |
| rc = _bif_check_task(sdev, task); |
| if (rc) { |
| pr_err("Invalid slave device or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Enable the auto task within the slave */ |
| mask = BIT(task % SLAVE_CTRL_TASKS_PER_SET); |
| addr = SLAVE_CTRL_FUNC_TASK_AUTO_TRIGGER_ADDR( |
| sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| if (task / SLAVE_CTRL_TASKS_PER_SET == 0) { |
| /* Set global auto task enable. */ |
| mask |= BIT(0); |
| } |
| rc = _bif_slave_masked_write(sdev, addr, 0xFF, mask); |
| if (rc) { |
| pr_err("BIF slave register masked write failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Set global auto task enable if task not in set 0. */ |
| if (task / SLAVE_CTRL_TASKS_PER_SET != 0) { |
| addr = SLAVE_CTRL_FUNC_TASK_AUTO_TRIGGER_ADDR( |
| sdev->slave_ctrl_function->slave_ctrl_pointer, 0); |
| rc = _bif_slave_masked_write(sdev, addr, 0xFF, BIT(0)); |
| if (rc) { |
| pr_err("BIF slave register masked write failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int _bif_disable_auto_task(struct bif_slave_dev *sdev, unsigned int task) |
| { |
| int rc; |
| u16 addr; |
| u8 mask; |
| |
| rc = _bif_check_task(sdev, task); |
| if (rc) { |
| pr_err("Invalid slave or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Disable the auto task within the slave */ |
| mask = BIT(task % SLAVE_CTRL_TASKS_PER_SET); |
| addr = SLAVE_CTRL_FUNC_TASK_AUTO_TRIGGER_ADDR( |
| sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| rc = _bif_slave_masked_write(sdev, addr, 0x00, mask); |
| if (rc) { |
| pr_err("BIF slave register masked write failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * The MIPI-BIF spec does not define a maximum time in which an NVM write must |
| * complete. The following delay and recheck count therefore represent |
| * arbitrary but reasonable values. |
| */ |
| #define NVM_WRITE_POLL_DELAY_MS 20 |
| #define NVM_WRITE_MAX_POLL_COUNT 50 |
| |
| static int _bif_slave_nvm_raw_write(struct bif_slave_dev *sdev, u16 offset, |
| u8 *buf, int len) |
| { |
| int rc = 0; |
| int write_len, poll_count, rc2; |
| u8 write_buf[3]; |
| |
| if (!sdev->nvm_function) { |
| pr_err("BIF slave has no NVM function\n"); |
| return -ENODEV; |
| } else if (offset + len > sdev->nvm_function->nvm_size) { |
| pr_err("write offset + len = %d > NVM size = %d\n", |
| offset + len, sdev->nvm_function->nvm_size); |
| return -EINVAL; |
| } else if (offset < sdev->nvm_function->nvm_lock_offset) { |
| pr_err("write offset = %d < first writable offset = %d\n", |
| offset, sdev->nvm_function->nvm_lock_offset); |
| return -EINVAL; |
| } |
| |
| rc = _bif_enable_auto_task(sdev, |
| sdev->nvm_function->slave_control_channel); |
| if (rc) { |
| pr_err("Failed to enable NVM auto task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| while (len > 0) { |
| write_len = sdev->nvm_function->write_buffer_size; |
| if (write_len == 0) |
| write_len = 256; |
| write_len = min(write_len, len); |
| |
| write_buf[0] = offset >> 8; |
| write_buf[1] = offset; |
| write_buf[2] = (write_len == 256) ? 0 : write_len; |
| |
| /* Write offset and size registers. */ |
| rc = _bif_slave_write(sdev, sdev->nvm_function->nvm_pointer + 6, |
| write_buf, 3); |
| if (rc) { |
| pr_err("BIF slave write failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| /* Write to NVM write buffer registers. */ |
| rc = _bif_slave_write(sdev, sdev->nvm_function->nvm_pointer + 9, |
| buf, write_len); |
| if (rc) { |
| pr_err("BIF slave write failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| /* |
| * Wait for completion of the NVM write which was auto-triggered |
| * by the register write of the last byte in the NVM write |
| * buffer. |
| */ |
| poll_count = NVM_WRITE_MAX_POLL_COUNT; |
| do { |
| msleep(NVM_WRITE_POLL_DELAY_MS); |
| rc = _bif_task_is_busy(sdev, |
| sdev->nvm_function->slave_control_channel); |
| poll_count--; |
| } while (rc > 0 && poll_count > 0); |
| |
| if (rc < 0) { |
| pr_err("Failed to check task state, rc=%d", rc); |
| goto done; |
| } else if (rc > 0) { |
| pr_err("BIF slave NVM write not completed after %d ms\n", |
| NVM_WRITE_POLL_DELAY_MS * NVM_WRITE_MAX_POLL_COUNT); |
| rc = -ETIMEDOUT; |
| goto done; |
| } |
| |
| len -= write_len; |
| offset += write_len; |
| buf += write_len; |
| } |
| |
| done: |
| rc2 = _bif_disable_auto_task(sdev, |
| sdev->nvm_function->slave_control_channel); |
| if (rc2) { |
| pr_err("Failed to disable NVM auto task, rc=%d\n", rc2); |
| return rc2; |
| } |
| |
| return rc; |
| } |
| |
| /* Takes a mutex if this consumer is not an exclusive bus user. */ |
| static void bif_ctrl_lock(struct bif_ctrl *ctrl) |
| { |
| if (!ctrl->exclusive_lock) { |
| mutex_lock(&ctrl->bdev->mutex); |
| bif_cancel_irq_mode_work(ctrl->bdev); |
| } |
| } |
| |
| /* Releases a mutex if this consumer is not an exclusive bus user. */ |
| static void bif_ctrl_unlock(struct bif_ctrl *ctrl) |
| { |
| if (!ctrl->exclusive_lock) { |
| bif_schedule_irq_mode_work(ctrl->bdev); |
| mutex_unlock(&ctrl->bdev->mutex); |
| } |
| } |
| |
| static void bif_slave_ctrl_lock(struct bif_slave *slave) |
| { |
| bif_ctrl_lock(&slave->ctrl); |
| } |
| |
| 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)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } |
| |
| return _bif_check_task(slave->sdev, task); |
| } |
| |
| /** |
| * bif_request_irq() - request a BIF slave IRQ by slave task number |
| * @slave: BIF slave handle |
| * @task: BIF task number of the IRQ inside of the slave. This |
| * corresponds to the slave control channel specified for a given |
| * BIF function inside of the slave. |
| * @nb: Notifier block to call when the IRQ fires |
| * |
| * This function registers a notifier block to call when the BIF slave interrupt |
| * is triggered and also enables the interrupt. The interrupt is enabled inside |
| * of the BIF slave's slave control function and also the BIF bus is put into |
| * interrupt mode. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_request_irq(struct bif_slave *slave, unsigned int task, |
| struct notifier_block *nb) |
| { |
| int rc; |
| u16 addr; |
| u8 reg, mask; |
| |
| rc = bif_check_task(slave, task); |
| if (rc) { |
| pr_err("Invalid slave or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| |
| rc = blocking_notifier_chain_register( |
| &slave->sdev->slave_ctrl_function->irq_notifier_list[task], nb); |
| if (rc) { |
| pr_err("Notifier registration failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| /* Enable the interrupt within the slave */ |
| mask = BIT(task % SLAVE_CTRL_TASKS_PER_SET); |
| addr = SLAVE_CTRL_FUNC_IRQ_EN_ADDR( |
| slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| if (task / SLAVE_CTRL_TASKS_PER_SET == 0) { |
| /* Set global interrupt enable. */ |
| mask |= BIT(0); |
| } |
| rc = _bif_slave_read(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register read failed, rc=%d\n", rc); |
| goto notifier_unregister; |
| } |
| reg |= mask; |
| rc = _bif_slave_write(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register write failed, rc=%d\n", rc); |
| goto notifier_unregister; |
| } |
| |
| /* Set global interrupt enable if task not in set 0. */ |
| if (task / SLAVE_CTRL_TASKS_PER_SET != 0) { |
| mask = BIT(0); |
| addr = SLAVE_CTRL_FUNC_IRQ_EN_ADDR( |
| slave->sdev->slave_ctrl_function->slave_ctrl_pointer, 0); |
| rc = _bif_slave_read(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register read failed, rc=%d\n", rc); |
| goto notifier_unregister; |
| } |
| reg |= mask; |
| rc = _bif_slave_write(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register write failed, rc=%d\n", rc); |
| goto notifier_unregister; |
| } |
| } |
| |
| rc = slave->sdev->bdev->desc->ops->set_bus_state(slave->sdev->bdev, |
| BIF_BUS_STATE_INTERRUPT); |
| if (rc) { |
| pr_err("Could not set BIF bus to interrupt mode, rc=%d\n", rc); |
| goto notifier_unregister; |
| } |
| |
| slave->sdev->bdev->irq_count++; |
| done: |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| |
| notifier_unregister: |
| blocking_notifier_chain_unregister( |
| &slave->sdev->slave_ctrl_function->irq_notifier_list[task], |
| nb); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| |
| } |
| EXPORT_SYMBOL(bif_request_irq); |
| |
| /** |
| * bif_free_irq() - free a BIF slave IRQ by slave task number |
| * @slave: BIF slave handle |
| * @task: BIF task number of the IRQ inside of the slave. This |
| * corresponds to the slave control channel specified for a given |
| * BIF function inside of the slave. |
| * @nb: Notifier block previously registered with this interrupt |
| * |
| * This function unregisters a notifier block that was previously registered |
| * with bif_request_irq(). |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_free_irq(struct bif_slave *slave, unsigned int task, |
| struct notifier_block *nb) |
| { |
| int rc; |
| u16 addr; |
| u8 reg; |
| |
| rc = bif_check_task(slave, task); |
| if (rc) { |
| pr_err("Invalid slave or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| |
| /* Disable the interrupt within the slave */ |
| reg = BIT(task % SLAVE_CTRL_TASKS_PER_SET); |
| addr = SLAVE_CTRL_FUNC_IRQ_CLEAR_ADDR( |
| slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| rc = _bif_slave_write(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register write failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| rc = blocking_notifier_chain_unregister( |
| &slave->sdev->slave_ctrl_function->irq_notifier_list[task], nb); |
| if (rc) { |
| pr_err("Notifier unregistration failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| slave->sdev->bdev->irq_count--; |
| |
| if (slave->sdev->bdev->irq_count == 0) { |
| bif_cancel_irq_mode_work(slave->sdev->bdev); |
| } else if (slave->sdev->bdev->irq_count < 0) { |
| pr_err("Unbalanced IRQ free.\n"); |
| rc = -EINVAL; |
| slave->sdev->bdev->irq_count = 0; |
| } |
| done: |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_free_irq); |
| |
| /** |
| * bif_trigger_task() - trigger a task within a BIF slave |
| * @slave: BIF slave handle |
| * @task: BIF task inside of the slave to trigger. This corresponds to |
| * the slave control channel specified for a given BIF function |
| * inside of the slave. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_trigger_task(struct bif_slave *slave, unsigned int task) |
| { |
| int rc; |
| u16 addr; |
| u8 reg; |
| |
| rc = bif_check_task(slave, task); |
| if (rc) { |
| pr_err("Invalid slave or task, rc=%d\n", rc); |
| return rc; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| |
| /* Trigger the task within the slave. */ |
| reg = BIT(task % SLAVE_CTRL_TASKS_PER_SET); |
| addr = SLAVE_CTRL_FUNC_TASK_TRIGGER_ADDR( |
| slave->sdev->slave_ctrl_function->slave_ctrl_pointer, task); |
| rc = _bif_slave_write(slave->sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register write failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| done: |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_trigger_task); |
| |
| /** |
| * bif_enable_auto_task() - enable task auto triggering for the specified task |
| * @slave: BIF slave handle |
| * @task: BIF task inside of the slave to configure for automatic |
| * triggering. This corresponds to the slave control channel |
| * specified for a given BIF function inside of the slave. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_enable_auto_task(struct bif_slave *slave, unsigned int task) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| rc = _bif_enable_auto_task(slave->sdev, task); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_enable_auto_task); |
| |
| /** |
| * bif_disable_auto_task() - disable task auto triggering for the specified task |
| * @slave: BIF slave handle |
| * @task: BIF task inside of the slave to stop automatic triggering on. |
| * This corresponds to the slave control channel specified for a |
| * given BIF function inside of the slave. |
| * |
| * This function should be called after bif_enable_auto_task() in a paired |
| * fashion. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_disable_auto_task(struct bif_slave *slave, unsigned int task) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| rc = _bif_disable_auto_task(slave->sdev, task); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_disable_auto_task); |
| |
| /** |
| * bif_task_is_busy() - checks the state of a BIF slave task |
| * @slave: BIF slave handle |
| * @task: BIF task inside of the slave to trigger. This corresponds to |
| * the slave control channel specified for a given BIF function |
| * inside of the slave. |
| * |
| * Returns 1 if the task is busy, 0 if it is not busy, and errno on error. |
| */ |
| int bif_task_is_busy(struct bif_slave *slave, unsigned int task) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| rc = _bif_task_is_busy(slave->sdev, task); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_task_is_busy); |
| |
| static int bif_slave_notify_irqs(struct bif_slave_dev *sdev, int set, u8 val) |
| { |
| int rc = 0; |
| int i, task; |
| |
| for (i = 0; i < SLAVE_CTRL_TASKS_PER_SET; i++) { |
| if (val & (1 << i)) { |
| task = set * SLAVE_CTRL_TASKS_PER_SET + i; |
| |
| rc = blocking_notifier_call_chain( |
| &sdev->slave_ctrl_function->irq_notifier_list[task], |
| task, sdev->bdev); |
| rc = notifier_to_errno(rc); |
| if (rc) |
| pr_err("Notification failed for task %d\n", |
| task); |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int bif_slave_handle_irq(struct bif_slave_dev *sdev) |
| { |
| struct bif_ctrl_dev *bdev = sdev->bdev; |
| bool resp = false; |
| int rc = 0; |
| int i; |
| u16 addr; |
| u8 reg; |
| |
| mutex_lock(&sdev->bdev->mutex); |
| bif_cancel_irq_mode_work(sdev->bdev); |
| |
| rc = bif_select_slave(sdev); |
| if (rc) { |
| pr_err("Could not select slave, rc=%d\n", rc); |
| goto done; |
| } |
| |
| /* Check overall slave interrupt status. */ |
| rc = bdev->desc->ops->bus_transaction_query(bdev, BIF_TRANS_BC, |
| BIF_CMD_ISTS, &resp); |
| if (rc) { |
| pr_err("Could not query slave interrupt status, rc=%d\n", rc); |
| goto done; |
| } |
| |
| if (resp) { |
| for (i = 0; i < sdev->slave_ctrl_function->task_count |
| / SLAVE_CTRL_TASKS_PER_SET; i++) { |
| addr = sdev->slave_ctrl_function->slave_ctrl_pointer |
| + 4 * i + 1; |
| rc = _bif_slave_read(sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register read failed, rc=%d\n", |
| rc); |
| goto done; |
| } |
| |
| /* Ensure that interrupts are pending in the set. */ |
| if (reg != 0x00) { |
| /* |
| * Release mutex before notifying consumers so |
| * that they can use the bus. |
| */ |
| mutex_unlock(&sdev->bdev->mutex); |
| rc = bif_slave_notify_irqs(sdev, i, reg); |
| if (rc) { |
| pr_err("BIF slave irq notification failed, rc=%d\n", |
| rc); |
| goto notification_failed; |
| } |
| mutex_lock(&sdev->bdev->mutex); |
| |
| rc = bif_select_slave(sdev); |
| if (rc) { |
| pr_err("Could not select slave, rc=%d\n", |
| rc); |
| goto done; |
| } |
| |
| /* Clear all interrupts in this set. */ |
| rc = _bif_slave_write(sdev, addr, ®, 1); |
| if (rc) { |
| pr_err("BIF slave register write failed, rc=%d\n", |
| rc); |
| goto done; |
| } |
| } |
| } |
| } |
| |
| done: |
| bif_schedule_irq_mode_work(sdev->bdev); |
| mutex_unlock(&sdev->bdev->mutex); |
| notification_failed: |
| if (rc == 0) |
| rc = resp; |
| return rc; |
| } |
| |
| /** |
| * bif_ctrl_notify_slave_irq() - notify the BIF framework that a slave interrupt |
| * was received by a BIF controller |
| * @bdev: BIF controller device pointer |
| * |
| * This function should only be called from a BIF controller driver. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_ctrl_notify_slave_irq(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| int rc = 0, handled = 0; |
| |
| if (IS_ERR_OR_NULL(bdev)) |
| return -EINVAL; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (sdev->bdev == bdev && sdev->present) { |
| rc = bif_slave_handle_irq(sdev); |
| if (rc < 0) { |
| pr_err("Could not handle BIF slave irq, rc=%d\n", |
| rc); |
| break; |
| } |
| handled += rc; |
| } |
| } |
| |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| if (handled == 0) |
| pr_info("Spurious BIF slave interrupt detected.\n"); |
| |
| if (rc > 0) |
| rc = 0; |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_notify_slave_irq); |
| |
| /** |
| * bif_ctrl_notify_battery_changed() - notify the BIF framework that a battery |
| * pack has been inserted or removed |
| * @bdev: BIF controller device pointer |
| * |
| * This function should only be called from a BIF controller driver. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_ctrl_notify_battery_changed(struct bif_ctrl_dev *bdev) |
| { |
| int rc = 0; |
| int present; |
| |
| if (IS_ERR_OR_NULL(bdev)) |
| return -EINVAL; |
| |
| if (bdev->desc->ops->get_battery_presence) { |
| present = bdev->desc->ops->get_battery_presence(bdev); |
| if (present < 0) { |
| pr_err("Could not determine battery presence, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| if (bdev->battery_present == !!present) |
| return 0; |
| |
| bdev->battery_present = present; |
| |
| rc = blocking_notifier_call_chain(&bdev->bus_change_notifier, |
| present ? BIF_BUS_EVENT_BATTERY_INSERTED |
| : BIF_BUS_EVENT_BATTERY_REMOVED, bdev); |
| if (rc) |
| pr_err("Call chain noification failed, rc=%d\n", rc); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_notify_battery_changed); |
| |
| /** |
| * bif_ctrl_signal_battery_changed() - notify the BIF framework that a battery |
| * pack has been inserted or removed |
| * @ctrl: BIF controller consumer handle |
| * |
| * This function should only be called by a BIF consumer driver on systems where |
| * the BIF controller driver is unable to determine when a battery is inserted |
| * or removed. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_ctrl_signal_battery_changed(struct bif_ctrl *ctrl) |
| { |
| if (IS_ERR_OR_NULL(ctrl)) |
| return -EINVAL; |
| |
| return bif_ctrl_notify_battery_changed(ctrl->bdev); |
| } |
| EXPORT_SYMBOL(bif_ctrl_signal_battery_changed); |
| |
| /** |
| * bif_ctrl_notifier_register() - register a notifier block to be called when |
| * a battery pack is inserted or removed |
| * @ctrl: BIF controller consumer handle |
| * |
| * The value passed into the notifier when it is called is one of |
| * enum bif_bus_event. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_ctrl_notifier_register(struct bif_ctrl *ctrl, struct notifier_block *nb) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) |
| return -EINVAL; |
| |
| rc = blocking_notifier_chain_register(&ctrl->bdev->bus_change_notifier, |
| nb); |
| if (rc) |
| pr_err("Notifier registration failed, rc=%d\n", rc); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_notifier_register); |
| |
| /** |
| * bif_ctrl_notifier_unregister() - unregister a battery status change notifier |
| * block that was previously registered |
| * @ctrl: BIF controller consumer handle |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_ctrl_notifier_unregister(struct bif_ctrl *ctrl, |
| struct notifier_block *nb) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) |
| return -EINVAL; |
| |
| rc = |
| blocking_notifier_chain_unregister(&ctrl->bdev->bus_change_notifier, |
| nb); |
| if (rc) |
| pr_err("Notifier unregistration failed, rc=%d\n", rc); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_notifier_unregister); |
| |
| /** |
| * bif_get_bus_handle() - returns the BIF controller consumer handle associated |
| * with a BIF slave handle |
| * @slave: BIF slave handle |
| * |
| * Note, bif_ctrl_put() should never be called for the pointer output by |
| * bif_get_bus_handle(). |
| */ |
| struct bif_ctrl *bif_get_bus_handle(struct bif_slave *slave) |
| { |
| if (IS_ERR_OR_NULL(slave)) |
| return ERR_PTR(-EINVAL); |
| |
| return &slave->ctrl; |
| } |
| EXPORT_SYMBOL(bif_get_bus_handle); |
| |
| /** |
| * bif_ctrl_count() - returns the number of registered BIF controllers |
| */ |
| int bif_ctrl_count(void) |
| { |
| struct bif_ctrl_dev *bdev; |
| int count = 0; |
| |
| mutex_lock(&bif_ctrl_list_mutex); |
| |
| list_for_each_entry(bdev, &bif_ctrl_list, list) { |
| count++; |
| } |
| mutex_unlock(&bif_ctrl_list_mutex); |
| |
| return count; |
| } |
| EXPORT_SYMBOL(bif_ctrl_count); |
| |
| /** |
| * bif_ctrl_get_by_id() - get a handle for the id'th BIF controller registered |
| * in the system |
| * @id: Arbitrary number associated with the BIF bus in the system |
| * |
| * id must be in the range [0, bif_ctrl_count() - 1]. This function should only |
| * need to be called by a BIF consumer that is unable to link to a given BIF |
| * controller via a device tree binding. |
| * |
| * Returns a BIF controller consumer handle if successful or an ERR_PTR if not. |
| */ |
| struct bif_ctrl *bif_ctrl_get_by_id(unsigned int id) |
| { |
| struct bif_ctrl_dev *bdev; |
| struct bif_ctrl_dev *bdev_found = NULL; |
| struct bif_ctrl *ctrl = ERR_PTR(-ENODEV); |
| |
| mutex_lock(&bif_ctrl_list_mutex); |
| |
| list_for_each_entry(bdev, &bif_ctrl_list, list) { |
| if (id == 0) { |
| bdev_found = bdev; |
| break; |
| } |
| id--; |
| } |
| mutex_unlock(&bif_ctrl_list_mutex); |
| |
| if (bdev_found) { |
| ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); |
| if (!ctrl) { |
| pr_err("Bus handle allocation failed\n"); |
| ctrl = ERR_PTR(-ENOMEM); |
| } else { |
| ctrl->bdev = bdev_found; |
| } |
| } |
| |
| return ctrl; |
| } |
| EXPORT_SYMBOL(bif_ctrl_get_by_id); |
| |
| /** |
| * bif_ctrl_get() - get a handle for the BIF controller that is linked to the |
| * consumer device in the device tree |
| * @consumer_dev: Pointer to the consumer's device |
| * |
| * In order to use this function, the BIF consumer's device must specify the |
| * "qcom,bif-ctrl" property in its device tree node which points to a BIF |
| * controller device node. |
| * |
| * Returns a BIF controller consumer handle if successful or an ERR_PTR if not. |
| * If the BIF controller linked to the consumer device has not yet probed, then |
| * ERR_PTR(-EPROBE_DEFER) is returned. |
| */ |
| struct bif_ctrl *bif_ctrl_get(struct device *consumer_dev) |
| { |
| struct device_node *ctrl_node = NULL; |
| struct bif_ctrl_dev *bdev_found = NULL; |
| struct bif_ctrl *ctrl = ERR_PTR(-EPROBE_DEFER); |
| struct bif_ctrl_dev *bdev = NULL; |
| |
| if (!consumer_dev || !consumer_dev->of_node) { |
| pr_err("Invalid device node\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| ctrl_node = of_parse_phandle(consumer_dev->of_node, "qcom,bif-ctrl", 0); |
| if (!ctrl_node) { |
| pr_err("Could not find qcom,bif-ctrl property in %s\n", |
| consumer_dev->of_node->full_name); |
| return ERR_PTR(-ENXIO); |
| } |
| |
| mutex_lock(&bif_ctrl_list_mutex); |
| list_for_each_entry(bdev, &bif_ctrl_list, list) { |
| if (bdev->ctrl_dev && bdev->ctrl_dev->of_node == ctrl_node) { |
| bdev_found = bdev; |
| break; |
| } |
| } |
| mutex_unlock(&bif_ctrl_list_mutex); |
| |
| if (bdev_found) { |
| ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); |
| if (!ctrl) { |
| pr_err("Bus handle allocation failed\n"); |
| ctrl = ERR_PTR(-ENOMEM); |
| } else { |
| ctrl->bdev = bdev_found; |
| } |
| } |
| |
| return ctrl; |
| } |
| EXPORT_SYMBOL(bif_ctrl_get); |
| |
| /** |
| * bif_ctrl_put() - frees a BIF controller handle |
| * @ctrl: BIF controller consumer handle |
| */ |
| void bif_ctrl_put(struct bif_ctrl *ctrl) |
| { |
| if (!IS_ERR_OR_NULL(ctrl) && ctrl->exclusive_lock) |
| mutex_unlock(&ctrl->bdev->mutex); |
| kfree(ctrl); |
| } |
| EXPORT_SYMBOL(bif_ctrl_put); |
| |
| static bool bif_slave_object_match(const struct bif_object *object, |
| const struct bif_match_criteria *criteria) |
| { |
| return (object->type == criteria->obj_type) |
| && (object->version == criteria->obj_version |
| || !(criteria->match_mask & BIF_MATCH_OBJ_VERSION)) |
| && (object->manufacturer_id == criteria->obj_manufacturer_id |
| || !(criteria->match_mask & BIF_MATCH_OBJ_MANUFACTURER_ID)); |
| } |
| |
| /* |
| * Returns true if all parameters are matched, otherwise false. |
| * function_type and function_version mean that their exists some function in |
| * the slave which has the specified type and subtype. ctrl == NULL is treated |
| * as a wildcard. |
| */ |
| static bool bif_slave_match(struct bif_ctrl *ctrl, |
| struct bif_slave_dev *sdev, const struct bif_match_criteria *criteria) |
| { |
| int i, type, version; |
| struct bif_object *object; |
| bool function_found = false; |
| bool object_found = false; |
| |
| if (ctrl && (ctrl->bdev != sdev->bdev)) |
| return false; |
| |
| if (!sdev->present |
| && (!(criteria->match_mask & BIF_MATCH_IGNORE_PRESENCE) |
| || ((criteria->match_mask & BIF_MATCH_IGNORE_PRESENCE) |
| && !criteria->ignore_presence))) |
| return false; |
| |
| if ((criteria->match_mask & BIF_MATCH_MANUFACTURER_ID) |
| && sdev->l1_data.manufacturer_id != criteria->manufacturer_id) |
| return false; |
| |
| if ((criteria->match_mask & BIF_MATCH_PRODUCT_ID) |
| && sdev->l1_data.product_id != criteria->product_id) |
| return false; |
| |
| if (criteria->match_mask & BIF_MATCH_FUNCTION_TYPE) { |
| if (!sdev->function_directory) |
| return false; |
| for (i = 0; i < sdev->l1_data.length / 4; i++) { |
| type = sdev->function_directory[i].function_type; |
| version = sdev->function_directory[i].function_version; |
| if (type == criteria->function_type && |
| (version == criteria->function_version |
| || !(criteria->match_mask |
| & BIF_MATCH_FUNCTION_VERSION))) { |
| function_found = true; |
| break; |
| } |
| } |
| if (!function_found) |
| return false; |
| } |
| |
| if (criteria->match_mask & BIF_MATCH_OBJ_TYPE) { |
| if (!sdev->nvm_function) |
| return false; |
| bif_ctrl_lock(ctrl); |
| list_for_each_entry(object, &sdev->nvm_function->object_list, |
| list) { |
| if (bif_slave_object_match(object, criteria)) { |
| object_found = true; |
| break; |
| } |
| } |
| bif_ctrl_unlock(ctrl); |
| if (!object_found) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * bif_slave_match_count() - returns the number of slaves associated with the |
| * specified BIF controller which fit the matching |
| * criteria |
| * @ctrl: BIF controller consumer handle |
| * @match_criteria: Matching criteria used to filter slaves |
| */ |
| int bif_slave_match_count(struct bif_ctrl *ctrl, |
| const struct bif_match_criteria *match_criteria) |
| { |
| struct bif_slave_dev *sdev; |
| int count = 0; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (bif_slave_match(ctrl, sdev, match_criteria)) |
| count++; |
| } |
| |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| return count; |
| } |
| EXPORT_SYMBOL(bif_slave_match_count); |
| |
| /** |
| * bif_slave_match_get() - get a slave handle for the id'th slave associated |
| * with the specified BIF controller which fits the |
| * matching criteria |
| * @ctrl: BIF controller consumer handle |
| * @id: Index into the set of matching slaves |
| * @match_criteria: Matching criteria used to filter slaves |
| * |
| * id must be in the range [0, bif_slave_match_count(ctrl, match_criteria) - 1]. |
| * |
| * Returns a BIF slave handle if successful or an ERR_PTR if not. |
| */ |
| struct bif_slave *bif_slave_match_get(struct bif_ctrl *ctrl, |
| unsigned int id, const struct bif_match_criteria *match_criteria) |
| { |
| struct bif_slave_dev *sdev; |
| struct bif_slave *slave = ERR_PTR(-ENODEV); |
| struct bif_slave_dev *sdev_found = NULL; |
| int count = 0; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (bif_slave_match(ctrl, sdev, match_criteria)) |
| count++; |
| if (count == id + 1) { |
| sdev_found = sdev; |
| break; |
| } |
| } |
| |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| if (sdev_found) { |
| slave = kzalloc(sizeof(*slave), GFP_KERNEL); |
| if (!slave) { |
| pr_err("Slave allocation failed\n"); |
| slave = ERR_PTR(-ENOMEM); |
| } else { |
| slave->sdev = sdev_found; |
| slave->ctrl.bdev = sdev_found->bdev; |
| } |
| } |
| |
| return slave; |
| } |
| EXPORT_SYMBOL(bif_slave_match_get); |
| |
| /** |
| * bif_slave_put() - frees a BIF slave handle |
| * @slave: BIF slave handle |
| */ |
| void bif_slave_put(struct bif_slave *slave) |
| { |
| if (!IS_ERR_OR_NULL(slave) && slave->ctrl.exclusive_lock) |
| mutex_unlock(&slave->sdev->bdev->mutex); |
| kfree(slave); |
| } |
| EXPORT_SYMBOL(bif_slave_put); |
| |
| /** |
| * bif_slave_find_function() - get the function pointer and version of a |
| * BIF function if it is present on the specified slave |
| * @slave: BIF slave handle |
| * @function: BIF function to search for inside of the slave |
| * @version: If the function is found, then 'version' is set to the |
| * version value of the function |
| * @function_pointer: If the function is found, then 'function_pointer' is set |
| * to the BIF slave address of the function |
| * |
| * Returns 0 for success or errno if an error occurred. If the function is not |
| * found in the slave, then -ENODEV is returned. |
| */ |
| int bif_slave_find_function(struct bif_slave *slave, u8 function, u8 *version, |
| u16 *function_pointer) |
| { |
| int rc = -ENODEV; |
| struct bif_ddb_l2_data *func; |
| int i; |
| |
| if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(version) |
| || IS_ERR_OR_NULL(function_pointer)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| func = slave->sdev->function_directory; |
| |
| for (i = 0; i < slave->sdev->l1_data.length / 4; i++) { |
| if (function == func[i].function_type) { |
| *version = func[i].function_version; |
| *function_pointer = func[i].function_pointer; |
| rc = 0; |
| break; |
| } |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_find_function); |
| |
| static bool bif_object_match(const struct bif_object *object, |
| const struct bif_obj_match_criteria *criteria) |
| { |
| return (object->type == criteria->type |
| || !(criteria->match_mask & BIF_OBJ_MATCH_TYPE)) |
| && (object->version == criteria->version |
| || !(criteria->match_mask & BIF_OBJ_MATCH_VERSION)) |
| && (object->manufacturer_id == criteria->manufacturer_id |
| || !(criteria->match_mask & BIF_OBJ_MATCH_MANUFACTURER_ID)); |
| } |
| |
| /** |
| * bif_object_match_count() - returns the number of objects associated with the |
| * specified BIF slave which fit the matching criteria |
| * @slave: BIF slave handle |
| * @match_criteria: Matching criteria used to filter objects |
| */ |
| int bif_object_match_count(struct bif_slave *slave, |
| const struct bif_obj_match_criteria *match_criteria) |
| { |
| struct bif_object *object; |
| int count = 0; |
| |
| if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(match_criteria)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| if (!slave->sdev->nvm_function) |
| return 0; |
| |
| bif_slave_ctrl_lock(slave); |
| list_for_each_entry(object, &slave->sdev->nvm_function->object_list, |
| list) { |
| if (bif_object_match(object, match_criteria)) |
| count++; |
| } |
| bif_slave_ctrl_unlock(slave); |
| |
| return count; |
| } |
| EXPORT_SYMBOL(bif_object_match_count); |
| |
| /** |
| * bif_object_match_get() - get a BIF object handle for the id'th object found |
| * in the non-volatile memory of the specified BIF slave |
| * which fits the matching criteria |
| * @slave: BIF slave handle |
| * @id: Index into the set of matching objects |
| * @match_criteria: Matching criteria used to filter objects |
| * |
| * id must be in range [0, bif_object_match_count(slave, match_criteria) - 1]. |
| * |
| * Returns a BIF object handle if successful or an ERR_PTR if not. This handle |
| * must be freed using bif_object_put() when it is no longer needed. |
| */ |
| struct bif_object *bif_object_match_get(struct bif_slave *slave, |
| unsigned int id, const struct bif_obj_match_criteria *match_criteria) |
| { |
| struct bif_object *object; |
| struct bif_object *object_found = NULL; |
| struct bif_object *object_consumer = ERR_PTR(-ENODEV); |
| int count = 0; |
| |
| if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(match_criteria)) { |
| pr_err("Invalid pointer input.\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| if (!slave->sdev->nvm_function) |
| return object_consumer; |
| |
| bif_slave_ctrl_lock(slave); |
| list_for_each_entry(object, &slave->sdev->nvm_function->object_list, |
| list) { |
| if (bif_object_match(object, match_criteria)) |
| count++; |
| if (count == id + 1) { |
| object_found = object; |
| break; |
| } |
| } |
| |
| if (object_found) { |
| object_consumer = kmemdup(object_found, |
| sizeof(*object_consumer), GFP_KERNEL); |
| if (!object_consumer) { |
| pr_err("out of memory\n"); |
| object_consumer = ERR_PTR(-ENOMEM); |
| goto done; |
| } |
| |
| object_consumer->data = kmemdup(object_found->data, |
| object_found->length - 8, GFP_KERNEL); |
| if (!object_consumer->data) { |
| pr_err("out of memory\n"); |
| kfree(object_consumer); |
| object_consumer = ERR_PTR(-ENOMEM); |
| goto done; |
| } |
| |
| /* |
| * Use prev pointer in consumer struct to point to original |
| * struct in the internal linked list. |
| */ |
| object_consumer->list.prev = &object_found->list; |
| } |
| |
| done: |
| bif_slave_ctrl_unlock(slave); |
| |
| return object_consumer; |
| |
| } |
| EXPORT_SYMBOL(bif_object_match_get); |
| |
| /** |
| * bif_object_put() - frees the memory allocated for a BIF object pointer |
| * returned by bif_object_match_get() |
| * @object: BIF object to free |
| */ |
| void bif_object_put(struct bif_object *object) |
| { |
| if (object) |
| kfree(object->data); |
| kfree(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 |
| * @addr: BIF slave address to begin reading at |
| * @buf: Buffer to fill with memory values |
| * @len: Number of byte to read |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_slave_read(struct bif_slave *slave, u16 addr, u8 *buf, int len) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(buf)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| |
| rc = _bif_slave_read(slave->sdev, addr, buf, len); |
| if (rc) |
| pr_err("BIF slave read failed, rc=%d\n", rc); |
| |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_read); |
| |
| /** |
| * bif_slave_write() - write contiguous memory values to a BIF slave |
| * @slave: BIF slave handle |
| * @addr: BIF slave address to begin writing at |
| * @buf: Buffer containing values to write |
| * @len: Number of byte to write |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf, int len) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave) || IS_ERR_OR_NULL(buf)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| |
| rc = _bif_slave_write(slave->sdev, addr, buf, len); |
| if (rc) |
| pr_err("BIF slave write failed, rc=%d\n", rc); |
| |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_write); |
| |
| /** |
| * bif_slave_nvm_raw_read() - read contiguous memory values from a BIF slave's |
| * non-volatile memory (NVM) |
| * @slave: BIF slave handle |
| * @offset: Offset from the beginning of BIF slave NVM to begin reading at |
| * @buf: Buffer to fill with memory values |
| * @len: Number of byte to read |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset, u8 *buf, |
| int len) |
| { |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } else if (IS_ERR_OR_NULL(buf)) { |
| pr_err("Invalid buffer pointer=%ld\n", PTR_ERR(buf)); |
| return -EINVAL; |
| } else if (!slave->sdev->nvm_function) { |
| pr_err("BIF slave has no NVM function\n"); |
| return -ENODEV; |
| } else if (offset + len > slave->sdev->nvm_function->nvm_size) { |
| pr_err("read offset + len = %d > NVM size = %d\n", |
| offset + len, slave->sdev->nvm_function->nvm_size); |
| return -EINVAL; |
| } |
| |
| return bif_slave_read(slave, |
| slave->sdev->nvm_function->nvm_base_address + offset, buf, len); |
| } |
| EXPORT_SYMBOL(bif_slave_nvm_raw_read); |
| |
| /** |
| * bif_slave_nvm_raw_write() - write contiguous memory values to a BIF slave's |
| * non-volatile memory (NVM) |
| * @slave: BIF slave handle |
| * @offset: Offset from the beginning of BIF slave NVM to begin writing at |
| * @buf: Buffer containing values to write |
| * @len: Number of byte to write |
| * |
| * Note that this function does *not* respect the MIPI-BIF object data |
| * formatting specification. It can cause corruption of the object data list |
| * stored in NVM if used improperly. |
| * |
| * Returns 0 for success or errno if an error occurred. |
| */ |
| int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset, u8 *buf, |
| int len) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave)); |
| return -EINVAL; |
| } else if (IS_ERR_OR_NULL(buf)) { |
| pr_err("Invalid buffer pointer=%ld\n", PTR_ERR(buf)); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| rc = _bif_slave_nvm_raw_write(slave->sdev, offset, buf, len); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_nvm_raw_write); |
| |
| /** |
| * bif_slave_is_present() - check if a slave is currently physically present |
| * in the system |
| * @slave: BIF slave handle |
| * |
| * Returns 1 if the slave is present, 0 if the slave is not present, or errno |
| * if an error occurred. |
| * |
| * This function can be used by BIF consumer drivers to check if their slave |
| * handles are still meaningful after battery reinsertion. |
| */ |
| int bif_slave_is_present(struct bif_slave *slave) |
| { |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| return slave->sdev->present; |
| } |
| EXPORT_SYMBOL(bif_slave_is_present); |
| |
| /** |
| * bif_slave_is_selected() - check if a slave is currently selected on the BIF |
| * bus |
| * @slave: BIF slave handle |
| * |
| * Returns 1 if the slave is selected, 0 if the slave is not selected, or errno |
| * if an error occurred. |
| * |
| * This function should not be required under normal circumstances since the |
| * bif-core framework ensures that slaves are always selected when needed. |
| * It would be most useful when used as a helper in conjunction with |
| * bif_ctrl_bus_lock() and the raw transaction functions. |
| */ |
| int bif_slave_is_selected(struct bif_slave *slave) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| if (slave->sdev->bdev->selected_sdev != slave->sdev) |
| return false; |
| |
| bif_slave_ctrl_lock(slave); |
| rc = bif_is_slave_selected(slave->sdev->bdev); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_is_selected); |
| |
| /** |
| * bif_slave_select() - select a slave on the BIF bus |
| * @slave: BIF slave handle |
| * |
| * Returns 0 on success or errno if an error occurred. |
| * |
| * This function should not be required under normal circumstances since the |
| * bif-core framework ensures that slaves are always selected when needed. |
| * It would be most useful when used as a helper in conjunction with |
| * bif_ctrl_bus_lock() and the raw transaction functions. |
| */ |
| int bif_slave_select(struct bif_slave *slave) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(slave)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_slave_ctrl_lock(slave); |
| slave->sdev->bdev->selected_sdev = NULL; |
| rc = bif_select_slave(slave->sdev); |
| bif_slave_ctrl_unlock(slave); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_slave_select); |
| |
| /** |
| * bif_ctrl_raw_transaction() - perform a raw BIF transaction on the bus which |
| * expects no slave response |
| * @ctrl: BIF controller consumer handle |
| * @transaction: BIF transaction to carry out. This should be one of the |
| * values in enum bif_transaction. |
| * @data: 8-bit data to use in the transaction. The meaning of |
| * this data depends upon the transaction that is to be |
| * performed. |
| * |
| * When performing a bus command (BC) transaction, values in enum |
| * bif_bus_command may be used for the data parameter. Additional manufacturer |
| * specific values may also be used in a BC transaction. |
| * |
| * Returns 0 on success or errno if an error occurred. |
| * |
| * This function should only need to be used when BIF transactions are required |
| * that are not handled by the bif-core directly. |
| */ |
| int bif_ctrl_raw_transaction(struct bif_ctrl *ctrl, int transaction, u8 data) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| |
| rc = ctrl->bdev->desc->ops->bus_transaction(ctrl->bdev, transaction, |
| data); |
| if (rc) |
| pr_err("BIF bus transaction failed, rc=%d\n", rc); |
| |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_raw_transaction); |
| |
| /** |
| * bif_ctrl_raw_transaction_read() - perform a raw BIF transaction on the bus |
| * which expects an RD or TACK slave response word |
| * @ctrl: BIF controller consumer handle |
| * @transaction: BIF transaction to carry out. This should be one of the |
| * values in enum bif_transaction. |
| * @data: 8-bit data to use in the transaction. The meaning of |
| * this data depends upon the transaction that is to be |
| * performed. |
| * @response: Pointer to an integer which is filled with the 11-bit |
| * slave response word upon success. The 11-bit format is |
| * (MSB to LSB) BCF, ACK, EOT, D7-D0. |
| * |
| * When performing a bus command (BC) transaction, values in enum |
| * bif_bus_command may be used for the data parameter. Additional manufacturer |
| * specific values may also be used in a BC transaction. |
| * |
| * Returns 0 on success or errno if an error occurred. |
| * |
| * This function should only need to be used when BIF transactions are required |
| * that are not handled by the bif-core directly. |
| */ |
| int bif_ctrl_raw_transaction_read(struct bif_ctrl *ctrl, int transaction, |
| u8 data, int *response) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(response)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| |
| rc = ctrl->bdev->desc->ops->bus_transaction_read(ctrl->bdev, |
| transaction, data, response); |
| if (rc) |
| pr_err("BIF bus transaction failed, rc=%d\n", rc); |
| |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_raw_transaction_read); |
| |
| /** |
| * bif_ctrl_raw_transaction_query() - perform a raw BIF transaction on the bus |
| * which expects a BQ slave response |
| * @ctrl: BIF controller consumer handle |
| * @transaction: BIF transaction to carry out. This should be one of the |
| * values in enum bif_transaction. |
| * @data: 8-bit data to use in the transaction. The meaning of |
| * this data depends upon the transaction that is to be |
| * performed. |
| * @query_response: Pointer to boolean which is set to true if a BQ pulse |
| * is receieved, or false if no BQ pulse is received before |
| * timing out. |
| * |
| * When performing a bus command (BC) transaction, values in enum |
| * bif_bus_command may be used for the data parameter. Additional manufacturer |
| * specific values may also be used in a BC transaction. |
| * |
| * Returns 0 on success or errno if an error occurred. |
| * |
| * This function should only need to be used when BIF transactions are required |
| * that are not handled by the bif-core directly. |
| */ |
| int bif_ctrl_raw_transaction_query(struct bif_ctrl *ctrl, int transaction, |
| u8 data, bool *query_response) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl) || IS_ERR_OR_NULL(query_response)) { |
| pr_err("Invalid pointer input.\n"); |
| return -EINVAL; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| |
| rc = ctrl->bdev->desc->ops->bus_transaction_query(ctrl->bdev, |
| transaction, data, query_response); |
| if (rc) |
| pr_err("BIF bus transaction failed, rc=%d\n", rc); |
| |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_raw_transaction_query); |
| |
| /** |
| * bif_ctrl_bus_lock() - lock the BIF bus of a controller for exclusive access |
| * @ctrl: BIF controller consumer handle |
| * |
| * This function should only need to be called in circumstances where a BIF |
| * consumer is issuing special BIF bus commands that have strict ordering |
| * requirements. |
| */ |
| void bif_ctrl_bus_lock(struct bif_ctrl *ctrl) |
| { |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return; |
| } |
| |
| if (ctrl->exclusive_lock) { |
| pr_err("BIF bus exclusive lock already held\n"); |
| return; |
| } |
| |
| mutex_lock(&ctrl->bdev->mutex); |
| ctrl->exclusive_lock = true; |
| bif_cancel_irq_mode_work(ctrl->bdev); |
| } |
| EXPORT_SYMBOL(bif_ctrl_bus_lock); |
| |
| /** |
| * bif_ctrl_bus_unlock() - lock the BIF bus of a controller that was previously |
| * locked for exclusive access |
| * @ctrl: BIF controller consumer handle |
| * |
| * This function must only be called after first calling bif_ctrl_bus_lock(). |
| */ |
| void bif_ctrl_bus_unlock(struct bif_ctrl *ctrl) |
| { |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return; |
| } |
| |
| if (!ctrl->exclusive_lock) { |
| pr_err("BIF bus exclusive lock not already held\n"); |
| return; |
| } |
| |
| ctrl->exclusive_lock = false; |
| bif_schedule_irq_mode_work(ctrl->bdev); |
| mutex_unlock(&ctrl->bdev->mutex); |
| } |
| EXPORT_SYMBOL(bif_ctrl_bus_unlock); |
| |
| /** |
| * bif_ctrl_measure_rid() - measure the battery pack Rid pull-down resistance |
| * in ohms |
| * @ctrl: BIF controller consumer handle |
| * |
| * Returns the resistance of the Rid resistor in ohms if successful or errno |
| * if an error occurred. |
| */ |
| int bif_ctrl_measure_rid(struct bif_ctrl *ctrl) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return -ENODEV; |
| } |
| |
| if (!ctrl->bdev->desc->ops->get_battery_rid) { |
| pr_err("Cannot measure Rid.\n"); |
| return -ENXIO; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| |
| rc = ctrl->bdev->desc->ops->get_battery_rid(ctrl->bdev); |
| if (rc < 0) |
| pr_err("Error during Rid measurement, rc=%d\n", rc); |
| |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_measure_rid); |
| |
| /** |
| * bif_ctrl_get_bus_period() - get the BIF bus period (tau_bif) in nanoseconds |
| * @ctrl: BIF controller consumer handle |
| * |
| * Returns the currently configured bus period in nanoseconds if successful or |
| * errno if an error occurred. |
| */ |
| int bif_ctrl_get_bus_period(struct bif_ctrl *ctrl) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return -ENODEV; |
| } |
| |
| if (!ctrl->bdev->desc->ops->get_bus_period) { |
| pr_err("Cannot get the BIF bus period.\n"); |
| return -ENXIO; |
| } |
| |
| rc = ctrl->bdev->desc->ops->get_bus_period(ctrl->bdev); |
| if (rc < 0) |
| pr_err("Error during bus period retrieval, rc=%d\n", rc); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_get_bus_period); |
| |
| /** |
| * bif_ctrl_set_bus_period() - set the BIF bus period (tau_bif) in nanoseconds |
| * @ctrl: BIF controller consumer handle |
| * @period_ns: BIF bus period in nanoseconds to use |
| * |
| * If the exact period is not supported by the BIF controller hardware, then the |
| * next larger supported period will be used. |
| * |
| * Returns 0 on success or errno if an error occurred. |
| */ |
| int bif_ctrl_set_bus_period(struct bif_ctrl *ctrl, int period_ns) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return -ENODEV; |
| } |
| |
| if (!ctrl->bdev->desc->ops->set_bus_period) { |
| pr_err("Cannot set the BIF bus period.\n"); |
| return -ENXIO; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| rc = ctrl->bdev->desc->ops->set_bus_period(ctrl->bdev, period_ns); |
| if (rc) |
| pr_err("Error during bus period configuration, rc=%d\n", rc); |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_set_bus_period); |
| |
| /** |
| * bif_ctrl_get_bus_state() - get the current state of the BIF bus |
| * @ctrl: BIF controller consumer handle |
| * |
| * Returns a bus state from enum bif_bus_state if successful or errno if an |
| * error occurred. |
| */ |
| int bif_ctrl_get_bus_state(struct bif_ctrl *ctrl) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return -ENODEV; |
| } |
| |
| rc = ctrl->bdev->desc->ops->get_bus_state(ctrl->bdev); |
| if (rc < 0) |
| pr_err("Error during bus state retrieval, rc=%d\n", rc); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_get_bus_state); |
| |
| /** |
| * bif_ctrl_set_bus_state() - set the state of the BIF bus |
| * @ctrl: BIF controller consumer handle |
| * @state: State for the BIF bus to enter |
| * |
| * Returns 0 on success or errno if an error occurred. |
| */ |
| int bif_ctrl_set_bus_state(struct bif_ctrl *ctrl, enum bif_bus_state state) |
| { |
| int rc; |
| |
| if (IS_ERR_OR_NULL(ctrl)) { |
| pr_err("Invalid controller handle.\n"); |
| return -ENODEV; |
| } |
| |
| bif_ctrl_lock(ctrl); |
| |
| rc = ctrl->bdev->desc->ops->set_bus_state(ctrl->bdev, state); |
| if (rc < 0) |
| pr_err("Error during bus state configuration, rc=%d\n", rc); |
| |
| /* |
| * Uncache the selected slave if the new bus state results in the slave |
| * becoming unselected. |
| */ |
| if (state == BIF_BUS_STATE_MASTER_DISABLED |
| || state == BIF_BUS_STATE_POWER_DOWN |
| || state == BIF_BUS_STATE_STANDBY) |
| ctrl->bdev->selected_sdev = NULL; |
| |
| bif_ctrl_unlock(ctrl); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(bif_ctrl_set_bus_state); |
| |
| /* |
| * Check if the specified function is a protocol function and if it is, then |
| * instantiate protocol function data for the slave. |
| */ |
| static int bif_initialize_protocol_function(struct bif_slave_dev *sdev, |
| struct bif_ddb_l2_data *func) |
| { |
| int rc = 0; |
| u8 buf[4]; |
| |
| /* Ensure that this is a protocol function. */ |
| if (func->function_type != BIF_FUNC_PROTOCOL) |
| return 0; |
| |
| if (sdev->protocol_function) { |
| pr_err("Duplicate protocol function found for BIF slave; DEV_ADR=0x%02X\n", |
| sdev->slave_addr); |
| return -EPERM; |
| } |
| |
| sdev->protocol_function = kzalloc(sizeof(struct bif_protocol_function), |
| GFP_KERNEL); |
| if (!sdev->protocol_function) { |
| pr_err("out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| rc = _bif_slave_read(sdev, func->function_pointer, buf, 4); |
| if (rc) { |
| pr_err("Protocol function data read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->protocol_function->protocol_pointer = buf[0] << 8 | buf[1]; |
| sdev->protocol_function->device_id_pointer = buf[2] << 8 | buf[3]; |
| sdev->protocol_function->l2_entry = func; |
| |
| rc = _bif_slave_read(sdev, sdev->protocol_function->device_id_pointer, |
| sdev->protocol_function->device_id, BIF_DEVICE_ID_BYTE_LENGTH); |
| if (rc) { |
| pr_err("Device ID read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Check if this slave does not have a UID value stored. */ |
| if (sdev->unique_id_bits_known == 0) { |
| sdev->unique_id_bits_known = BIF_UNIQUE_ID_BIT_LENGTH; |
| /* Fill in UID using manufacturer ID and device ID. */ |
| sdev->unique_id[0] = sdev->l1_data.manufacturer_id >> 8; |
| sdev->unique_id[1] = sdev->l1_data.manufacturer_id; |
| memcpy(&sdev->unique_id[2], |
| sdev->protocol_function->device_id, |
| BIF_DEVICE_ID_BYTE_LENGTH); |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Check if the specified function is a slave control function and if it is, |
| * then instantiate slave control function data for the slave. |
| */ |
| static int bif_initialize_slave_control_function(struct bif_slave_dev *sdev, |
| struct bif_ddb_l2_data *func) |
| { |
| int rc = 0; |
| int i; |
| u8 buf[3]; |
| |
| /* Ensure that this is a slave control function. */ |
| if (func->function_type != BIF_FUNC_SLAVE_CONTROL) |
| return 0; |
| |
| if (sdev->slave_ctrl_function) { |
| pr_err("Duplicate slave control function found for BIF slave; DEV_ADR=0x%02X\n", |
| sdev->slave_addr); |
| return -EPERM; |
| } |
| |
| sdev->slave_ctrl_function |
| = kzalloc(sizeof(struct bif_protocol_function), GFP_KERNEL); |
| if (!sdev->slave_ctrl_function) { |
| pr_err("out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| rc = _bif_slave_read(sdev, func->function_pointer, buf, 3); |
| if (rc) { |
| pr_err("Slave control function data read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->slave_ctrl_function->slave_ctrl_pointer = buf[0] << 8 | buf[1]; |
| sdev->slave_ctrl_function->task_count |
| = buf[2] * SLAVE_CTRL_TASKS_PER_SET; |
| sdev->slave_ctrl_function->l2_entry = func; |
| |
| if (sdev->slave_ctrl_function->task_count > 0) { |
| sdev->slave_ctrl_function->irq_notifier_list = |
| kzalloc(sizeof(struct blocking_notifier_head) |
| * sdev->slave_ctrl_function->task_count, |
| GFP_KERNEL); |
| if (!sdev->slave_ctrl_function->irq_notifier_list) { |
| pr_err("out of memory\n"); |
| kfree(sdev->slave_ctrl_function); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < sdev->slave_ctrl_function->task_count; i++) { |
| BLOCKING_INIT_NOTIFIER_HEAD( |
| &sdev->slave_ctrl_function->irq_notifier_list[i]); |
| } |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * 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. |
| */ |
| static int bif_initialize_nvm_function(struct bif_slave_dev *sdev, |
| struct bif_ddb_l2_data *func) |
| { |
| int rc = 0; |
| int data_len, read_size; |
| u8 buf[8], object_type; |
| struct bif_object *object; |
| struct bif_object *temp; |
| u16 addr; |
| u16 crc; |
| |
| /* Ensure that this is an NVM function. */ |
| if (func->function_type != BIF_FUNC_NVM) |
| return 0; |
| |
| if (sdev->nvm_function) { |
| pr_err("Duplicate NVM function found for BIF slave; DEV_ADR=0x%02X\n", |
| sdev->slave_addr); |
| return -EPERM; |
| } |
| |
| sdev->nvm_function |
| = kzalloc(sizeof(*sdev->nvm_function), GFP_KERNEL); |
| if (!sdev->nvm_function) { |
| pr_err("out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| rc = _bif_slave_read(sdev, func->function_pointer, buf, 8); |
| if (rc) { |
| pr_err("NVM function data read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->nvm_function->nvm_pointer = buf[0] << 8 | buf[1]; |
| sdev->nvm_function->slave_control_channel = buf[2]; |
| sdev->nvm_function->write_buffer_size = buf[3]; |
| sdev->nvm_function->nvm_base_address = buf[4] << 8 | buf[5]; |
| sdev->nvm_function->nvm_size = buf[6] << 8 | buf[7]; |
| |
| /* Read NVM lock offset */ |
| rc = _bif_slave_read(sdev, sdev->nvm_function->nvm_pointer, buf, 2); |
| if (rc) { |
| pr_err("Slave memory read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->nvm_function->nvm_lock_offset = buf[0] << 8 | buf[1]; |
| |
| INIT_LIST_HEAD(&sdev->nvm_function->object_list); |
| |
| /* Read object list */ |
| addr = sdev->nvm_function->nvm_base_address; |
| rc = _bif_slave_read(sdev, addr, &object_type, 1); |
| if (rc) { |
| pr_err("Slave memory read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| while (object_type != BIF_OBJ_END_OF_LIST) { |
| object = kzalloc(sizeof(*object), GFP_KERNEL); |
| if (!object) { |
| pr_err("out of memory\n"); |
| rc = -ENOMEM; |
| goto free_data; |
| } |
| list_add_tail(&object->list, &sdev->nvm_function->object_list); |
| |
| rc = _bif_slave_read(sdev, addr + 1, buf + 1, 5); |
| if (rc) { |
| pr_err("Slave memory read of object header failed; addr=0x%04X, len=%d, rc=%d\n", |
| addr + 1, 5, rc); |
| goto free_data; |
| } |
| |
| object->addr = addr; |
| object->type = object_type; |
| object->version = buf[1]; |
| object->manufacturer_id = buf[2] << 8 | buf[3]; |
| object->length = buf[4] << 8 | buf[5]; |
| |
| if ((object->addr + object->length) |
| > (sdev->nvm_function->nvm_base_address |
| + sdev->nvm_function->nvm_size)) { |
| pr_warn("warning: BIF slave object is not formatted correctly; NVM base=0x%04X, NVM len=%d, object addr=0x%04X, object len=%d\n", |
| sdev->nvm_function->nvm_base_address, |
| sdev->nvm_function->nvm_size, |
| object->addr, |
| object->length); |
| /* Limit object size to remaining NVM size. */ |
| object->length = sdev->nvm_function->nvm_size |
| + sdev->nvm_function->nvm_base_address |
| - object->addr; |
| } |
| |
| /* Object header + CRC takes up 8 bytes. */ |
| data_len = object->length - 8; |
| object->data = kmalloc(data_len, GFP_KERNEL); |
| if (!object->data) { |
| pr_err("out of memory\n"); |
| rc = -ENOMEM; |
| goto free_data; |
| } |
| |
| rc = _bif_slave_read(sdev, addr + 6, object->data, data_len); |
| if (rc) { |
| pr_err("Slave memory read of object data failed; addr=0x%04X, len=%d, rc=%d\n", |
| addr + 6, data_len, rc); |
| goto free_data; |
| } |
| |
| if ((object->length + addr) >= (sdev->nvm_function->nvm_size |
| + sdev->nvm_function->nvm_base_address)) |
| read_size = 2; |
| else |
| read_size = 3; |
| rc = _bif_slave_read(sdev, addr + 6 + data_len, buf, read_size); |
| if (rc) { |
| pr_err("Slave memory read of object CRC failed; addr=0x%04X, len=%d, rc=%d\n", |
| addr + 6 + data_len, read_size, rc); |
| goto free_data; |
| } |
| |
| object->crc = buf[0] << 8 | buf[1]; |
| object_type = (read_size == 3) ? buf[2] : BIF_OBJ_END_OF_LIST; |
| sdev->nvm_function->object_count++; |
| |
| crc = bif_object_crc_ccitt(object); |
| if (crc != object->crc) |
| pr_info("BIF object at addr=0x%04X has invalid CRC; crc calc=0x%04X, crc exp=0x%04X\n", |
| object->addr, crc, object->crc); |
| |
| addr += object->length; |
| } |
| |
| return rc; |
| |
| free_data: |
| list_for_each_entry_safe(object, temp, |
| &sdev->nvm_function->object_list, list) { |
| list_del(&object->list); |
| kfree(object->data); |
| kfree(object); |
| } |
| kfree(sdev->nvm_function); |
| sdev->nvm_function = NULL; |
| return rc; |
| } |
| |
| static int bif_parse_slave_data(struct bif_slave_dev *sdev) |
| { |
| int rc = 0; |
| u8 buf[10]; |
| u8 *func_buf; |
| struct bif_ddb_l2_data *func; |
| int function_count, i; |
| |
| rc = _bif_slave_read(sdev, BIF_DDB_L1_BASE_ADDR, buf, 10); |
| if (rc) { |
| pr_err("DDB L1 data read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->l1_data.revision = buf[0]; |
| sdev->l1_data.level = buf[1]; |
| sdev->l1_data.device_class = buf[2] << 8 | buf[3]; |
| sdev->l1_data.manufacturer_id = buf[4] << 8 | buf[5]; |
| sdev->l1_data.product_id = buf[6] << 8 | buf[7]; |
| sdev->l1_data.length = buf[8] << 8 | buf[9]; |
| |
| function_count = sdev->l1_data.length / 4; |
| if (sdev->l1_data.length % 4) { |
| pr_err("Function directory length=%d is invalid\n", |
| sdev->l1_data.length); |
| return -EPROTO; |
| } |
| |
| /* No DDB L2 function directory */ |
| if (function_count == 0) |
| return 0; |
| |
| func_buf = kmalloc(sdev->l1_data.length, GFP_KERNEL); |
| if (!func_buf) { |
| pr_err("out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| sdev->function_directory = kzalloc( |
| function_count * sizeof(struct bif_ddb_l2_data), GFP_KERNEL); |
| if (!sdev->function_directory) { |
| pr_err("out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| rc = _bif_slave_read(sdev, BIF_DDB_L2_BASE_ADDR, func_buf, |
| sdev->l1_data.length); |
| if (rc) { |
| pr_err("DDB L2 data read failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| for (i = 0; i < function_count; i++) { |
| func = &sdev->function_directory[i]; |
| func->function_type = func_buf[i * 4]; |
| func->function_version = func_buf[i * 4 + 1]; |
| func->function_pointer = func_buf[i * 4 + 2] << 8 |
| | func_buf[i * 4 + 3]; |
| rc = bif_initialize_protocol_function(sdev, func); |
| if (rc) |
| goto done; |
| rc = bif_initialize_slave_control_function(sdev, func); |
| if (rc) |
| goto done; |
| rc = bif_initialize_nvm_function(sdev, func); |
| if (rc) |
| goto done; |
| } |
| done: |
| kfree(func_buf); |
| return rc; |
| } |
| |
| static int bif_add_secondary_slaves(struct bif_slave_dev *primary_slave) |
| { |
| int rc = 0; |
| int data_len, i; |
| u16 crc; |
| struct bif_slave_dev *sdev; |
| struct bif_object *object; |
| |
| list_for_each_entry(object, &primary_slave->nvm_function->object_list, |
| list) { |
| if (object->type != BIF_OBJ_SEC_SLAVE) |
| continue; |
| |
| data_len = object->length - 8; |
| if (data_len % BIF_UNIQUE_ID_BYTE_LENGTH) { |
| pr_info("Invalid secondary slave object found, addr=0x%04X, data len=%d\n", |
| object->addr, data_len); |
| continue; |
| } |
| |
| crc = bif_object_crc_ccitt(object); |
| if (crc != object->crc) { |
| pr_info("BIF object at addr=0x%04X has invalid CRC; crc calc=0x%04X, crc exp=0x%04X\n", |
| object->addr, crc, object->crc); |
| continue; |
| } |
| |
| for (i = 0; i < data_len / BIF_UNIQUE_ID_BYTE_LENGTH; i++) { |
| sdev = bif_add_slave(primary_slave->bdev); |
| if (IS_ERR(sdev)) { |
| rc = PTR_ERR(sdev); |
| pr_err("bif_add_slave failed, rc=%d\n", rc); |
| return rc; |
| } |
| memcpy(sdev->unique_id, |
| &object->data[i * BIF_UNIQUE_ID_BYTE_LENGTH], |
| BIF_UNIQUE_ID_BYTE_LENGTH); |
| sdev->unique_id_bits_known = BIF_UNIQUE_ID_BIT_LENGTH; |
| |
| rc = bif_select_slave(sdev); |
| if (rc) { |
| pr_err("Could not select slave, rc=%d\n", rc); |
| goto free_slave; |
| } |
| |
| rc = bif_is_slave_selected(sdev->bdev); |
| if (rc < 0) { |
| pr_err("Transaction failed, rc=%d\n", rc); |
| goto free_slave; |
| } else if (rc == 1) { |
| sdev->present = true; |
| sdev->bdev->selected_sdev = sdev; |
| rc = bif_parse_slave_data(sdev); |
| if (rc) { |
| pr_err("Failed to parse secondary slave data, rc=%d\n", |
| rc); |
| goto free_slave; |
| } |
| } else { |
| sdev->present = false; |
| sdev->bdev->selected_sdev = NULL; |
| } |
| } |
| } |
| |
| return rc; |
| |
| free_slave: |
| bif_remove_slave(sdev); |
| return rc; |
| } |
| |
| /* |
| * Performs UID search to identify all slaves attached to the bus. Assumes that |
| * all necessary locks are held. |
| */ |
| static int bif_perform_uid_search(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| struct bif_slave_dev *new_slave; |
| bool resp[2], resp_dilc; |
| int i; |
| int rc = 0; |
| u8 cmd_probe[2] = {BIF_CMD_DIP0, BIF_CMD_DIP1}; |
| u8 cmd_enter[2] = {BIF_CMD_DIE0, BIF_CMD_DIE1}; |
| |
| /* |
| * Iterate over all partially known UIDs adding new ones as they are |
| * found. |
| */ |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| /* Skip slaves with fully known UIDs. */ |
| if (sdev->unique_id_bits_known == BIF_UNIQUE_ID_BIT_LENGTH |
| || sdev->bdev != bdev) |
| continue; |
| |
| /* Begin a new UID search. */ |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_BC, |
| BIF_CMD_DISS); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Step through all known UID bits (MSB to LSB). */ |
| for (i = 0; i < sdev->unique_id_bits_known; i++) { |
| rc = bdev->desc->ops->bus_transaction(bdev, |
| BIF_TRANS_BC, |
| cmd_enter[get_uid_bit(sdev->unique_id, i)]); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| /* Step through unknown UID bits. */ |
| for (i = sdev->unique_id_bits_known; |
| i < BIF_UNIQUE_ID_BIT_LENGTH; i++) { |
| rc = bdev->desc->ops->bus_transaction_query(bdev, |
| BIF_TRANS_BC, cmd_probe[0], &resp[0]); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = bdev->desc->ops->bus_transaction_query(bdev, |
| BIF_TRANS_BC, cmd_probe[1], &resp[1]); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| if (resp[0] && resp[1]) { |
| /* Create an entry for the new UID branch. */ |
| new_slave = bif_add_slave(bdev); |
| if (IS_ERR(new_slave)) { |
| rc = PTR_ERR(sdev); |
| pr_err("bif_add_slave failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| memcpy(new_slave->unique_id, sdev->unique_id, |
| BIF_UNIQUE_ID_BYTE_LENGTH); |
| new_slave->bdev = sdev->bdev; |
| |
| set_uid_bit(sdev->unique_id, i, 0); |
| sdev->unique_id_bits_known = i + 1; |
| |
| set_uid_bit(new_slave->unique_id, i, 1); |
| new_slave->unique_id_bits_known = i + 1; |
| } else if (resp[0]) { |
| set_uid_bit(sdev->unique_id, i, 0); |
| sdev->unique_id_bits_known = i + 1; |
| } else if (resp[1]) { |
| set_uid_bit(sdev->unique_id, i, 1); |
| sdev->unique_id_bits_known = i + 1; |
| } else { |
| pr_debug("no bus query response received\n"); |
| rc = -ENXIO; |
| return rc; |
| } |
| |
| rc = bdev->desc->ops->bus_transaction(bdev, |
| BIF_TRANS_BC, cmd_enter[resp[0] ? 0 : 1]); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| rc = bdev->desc->ops->bus_transaction_query(bdev, |
| BIF_TRANS_BC, BIF_CMD_DILC, &resp_dilc); |
| if (rc) { |
| pr_err("bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| if (resp_dilc) { |
| sdev->present = true; |
| sdev->bdev->selected_sdev = sdev; |
| rc = bif_parse_slave_data(sdev); |
| if (rc) { |
| pr_err("Failed to parse secondary slave data, rc=%d\n", |
| rc); |
| return rc; |
| } |
| } else { |
| pr_err("Slave failed to respond to DILC bus command; its UID is thus unverified.\n"); |
| sdev->unique_id_bits_known = 0; |
| rc = -ENXIO; |
| return rc; |
| } |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Removes slaves from the bif_sdev_list which have the same UID as previous |
| * slaves in the list. |
| */ |
| static int bif_remove_duplicate_slaves(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| struct bif_slave_dev *last_slave; |
| struct bif_slave_dev *temp; |
| |
| list_for_each_entry_safe(last_slave, temp, &bif_sdev_list, list) { |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (last_slave == sdev) { |
| break; |
| } else if (memcmp(last_slave->unique_id, |
| sdev->unique_id, |
| BIF_UNIQUE_ID_BYTE_LENGTH) == 0) { |
| bif_remove_slave(last_slave); |
| break; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int bif_add_all_slaves(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| int rc = 0; |
| int i; |
| bool has_slave = false, is_primary_slave = false; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| mutex_lock(&bdev->mutex); |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (sdev->bdev == bdev) { |
| has_slave = true; |
| break; |
| } |
| } |
| |
| if (!has_slave) { |
| /* Create a single empty slave to start the search algorithm. */ |
| sdev = bif_add_slave(bdev); |
| if (IS_ERR(sdev)) { |
| rc = PTR_ERR(sdev); |
| pr_err("bif_add_slave failed, rc=%d\n", rc); |
| goto out; |
| } |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| /* Attempt to select primary slave in battery pack. */ |
| rc = bdev->desc->ops->bus_transaction(bdev, |
| BIF_TRANS_SDA, BIF_PRIMARY_SLAVE_DEV_ADR); |
| if (rc == 0) |
| break; |
| } |
| if (rc) { |
| pr_err("BIF bus_transaction failed, rc=%d\n", rc); |
| goto out; |
| } |
| |
| /* Check if a slave is selected. */ |
| rc = bif_is_slave_selected(bdev); |
| if (rc < 0) { |
| pr_err("BIF bus_transaction failed, rc=%d\n", rc); |
| goto out; |
| } else { |
| is_primary_slave = rc; |
| } |
| } |
| |
| if (is_primary_slave) { |
| pr_debug("Using primary slave at DEV_ADR==0x%02X\n", |
| BIF_PRIMARY_SLAVE_DEV_ADR); |
| sdev->bdev->selected_sdev = sdev; |
| sdev->present = true; |
| sdev->slave_addr = BIF_PRIMARY_SLAVE_DEV_ADR; |
| rc = bif_parse_slave_data(sdev); |
| if (rc) { |
| pr_err("Failed to parse primary slave data, rc=%d\n", |
| rc); |
| goto out; |
| } |
| rc = bif_add_secondary_slaves(sdev); |
| if (rc) { |
| pr_err("Failed to add secondary slaves, rc=%d\n", rc); |
| goto out; |
| } |
| } else { |
| pr_debug("Falling back on full UID search.\n"); |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| rc = bif_perform_uid_search(bdev); |
| if (rc == 0) |
| break; |
| } |
| if (rc) { |
| pr_debug("BIF UID search failed, rc=%d\n", rc); |
| goto out; |
| } |
| } |
| |
| bif_remove_duplicate_slaves(bdev); |
| |
| mutex_unlock(&bdev->mutex); |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| return rc; |
| |
| out: |
| mutex_unlock(&bdev->mutex); |
| mutex_unlock(&bif_sdev_list_mutex); |
| pr_debug("BIF slave search failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| static int bif_add_known_slave(struct bif_ctrl_dev *bdev, u8 slave_addr) |
| { |
| struct bif_slave_dev *sdev; |
| int rc = 0; |
| int i; |
| |
| for (i = 0; i < BIF_TRANSACTION_RETRY_COUNT; i++) { |
| /* Attempt to select the slave. */ |
| rc = bdev->desc->ops->bus_transaction(bdev, BIF_TRANS_SDA, |
| slave_addr); |
| if (rc == 0) |
| break; |
| } |
| if (rc) { |
| pr_err("BIF bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* Check if a slave is selected. */ |
| rc = bif_is_slave_selected(bdev); |
| if (rc < 0) { |
| pr_err("BIF bus_transaction failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev = bif_add_slave(bdev); |
| if (IS_ERR(sdev)) { |
| rc = PTR_ERR(sdev); |
| pr_err("bif_add_slave failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| sdev->bdev->selected_sdev = sdev; |
| sdev->present = true; |
| sdev->slave_addr = slave_addr; |
| rc = bif_parse_slave_data(sdev); |
| if (rc) { |
| pr_err("Failed to parse slave data, addr=0x%02X, rc=%d\n", |
| slave_addr, rc); |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| static int bif_add_known_slaves_from_dt(struct bif_ctrl_dev *bdev, |
| struct device_node *of_node) |
| { |
| int len = 0; |
| int rc, i; |
| u32 addr; |
| const __be32 *val; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| mutex_lock(&bdev->mutex); |
| |
| val = of_get_property(of_node, "qcom,known-device-addresses", &len); |
| len /= sizeof(u32); |
| if (val && len == 0) { |
| pr_err("qcom,known-device-addresses property is invalid\n"); |
| rc = -EINVAL; |
| goto out; |
| } |
| |
| for (i = 0; i < len; i++) { |
| addr = be32_to_cpup(val++); |
| if (addr == 0x00 || addr > 0xFF) { |
| rc = -EINVAL; |
| pr_err("qcom,known-device-addresses property contains invalid address=0x%X\n", |
| addr); |
| goto out; |
| } |
| rc = bif_add_known_slave(bdev, addr); |
| if (rc) { |
| pr_err("bif_add_known_slave() failed, rc=%d\n", rc); |
| goto out; |
| } |
| } |
| |
| out: |
| if (len > 0) |
| bif_remove_duplicate_slaves(bdev); |
| |
| mutex_unlock(&bdev->mutex); |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| return rc; |
| } |
| |
| /* |
| * Programs a device address for the specified slave in order to simplify |
| * slave selection in the future. |
| */ |
| static int bif_assign_slave_dev_addr(struct bif_slave_dev *sdev, u8 dev_addr) |
| { |
| int rc; |
| u16 addr; |
| |
| if (!sdev->protocol_function) { |
| pr_err("Protocol function not present; cannot set device address.\n"); |
| return -ENODEV; |
| } |
| |
| addr = PROTOCOL_FUNC_DEV_ADR_ADDR( |
| sdev->protocol_function->protocol_pointer); |
| |
| rc = _bif_slave_write(sdev, addr, &dev_addr, 1); |
| if (rc) |
| pr_err("Failed to set slave device address.\n"); |
| else |
| sdev->slave_addr = dev_addr; |
| |
| return rc; |
| } |
| |
| /* Assigns a unique device address to all slaves which do not have one. */ |
| static int bif_assign_all_slaves_dev_addr(struct bif_ctrl_dev *bdev) |
| { |
| struct bif_slave_dev *sdev; |
| struct bif_slave_dev *sibling; |
| bool duplicate; |
| int rc = 0; |
| u8 dev_addr, first_dev_addr; |
| |
| mutex_lock(&bif_sdev_list_mutex); |
| mutex_lock(&bdev->mutex); |
| |
| first_dev_addr = next_dev_addr; |
| /* |
| * Iterate over all partially known UIDs adding new ones as they are |
| * found. |
| */ |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| /* |
| * Skip slaves without known UIDs, which already have a device |
| * address or which aren't present. |
| */ |
| if (sdev->unique_id_bits_known != BIF_UNIQUE_ID_BIT_LENGTH |
| || sdev->slave_addr != 0x00 || !sdev->present) |
| continue; |
| |
| do { |
| dev_addr = next_dev_addr; |
| duplicate = false; |
| list_for_each_entry(sibling, &bif_sdev_list, list) { |
| if (sibling->slave_addr == dev_addr) { |
| duplicate = true; |
| break; |
| } |
| } |
| |
| next_dev_addr = dev_addr + 1; |
| } while (duplicate && (next_dev_addr != first_dev_addr)); |
| |
| if (next_dev_addr == first_dev_addr) { |
| pr_err("No more BIF slave device addresses available.\n"); |
| rc = -ENODEV; |
| goto out; |
| } |
| |
| rc = bif_assign_slave_dev_addr(sdev, dev_addr); |
| if (rc) { |
| pr_err("Failed to set slave address.\n"); |
| goto out; |
| } |
| } |
| |
| mutex_unlock(&bdev->mutex); |
| mutex_unlock(&bif_sdev_list_mutex); |
| |
| return rc; |
| |
| out: |
| mutex_unlock(&bdev->mutex); |
| mutex_unlock(&bif_sdev_list_mutex); |
| pr_err("BIF slave device address setting failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /** |
| * bdev_get_drvdata() - get the private BIF controller driver data |
| * @bdev: BIF controller device pointer |
| */ |
| void *bdev_get_drvdata(struct bif_ctrl_dev *bdev) |
| { |
| return bdev->driver_data; |
| } |
| EXPORT_SYMBOL(bdev_get_drvdata); |
| |
| static const char * const battery_label[] = { |
| "unknown", |
| "none", |
| "special 1", |
| "special 2", |
| "special 3", |
| "low cost", |
| "smart", |
| }; |
| |
| static const char *bif_get_battery_pack_type(int rid_ohm) |
| { |
| const char *label = battery_label[0]; |
| |
| if (rid_ohm > BIF_BATT_RID_SMART_MAX) |
| label = battery_label[1]; |
| else if (rid_ohm >= BIF_BATT_RID_SMART_MIN) |
| label = battery_label[6]; |
| else if (rid_ohm >= BIF_BATT_RID_LOW_COST_MIN |
| && rid_ohm <= BIF_BATT_RID_LOW_COST_MAX) |
| label = battery_label[5]; |
| else if (rid_ohm >= BIF_BATT_RID_SPECIAL3_MIN |
| && rid_ohm <= BIF_BATT_RID_SPECIAL3_MAX) |
| label = battery_label[4]; |
| else if (rid_ohm >= BIF_BATT_RID_SPECIAL2_MIN |
| && rid_ohm <= BIF_BATT_RID_SPECIAL2_MAX) |
| label = battery_label[3]; |
| else if (rid_ohm >= BIF_BATT_RID_SPECIAL1_MIN |
| && rid_ohm <= BIF_BATT_RID_SPECIAL1_MAX) |
| label = battery_label[2]; |
| |
| return label; |
| } |
| |
| /** |
| * bif_ctrl_register() - register a BIF controller with the BIF framework |
| * @bif_desc: Pointer to BIF controller descriptor |
| * @dev: Device pointer of the BIF controller |
| * @driver_data: Private driver data to associate with the BIF controller |
| * @of_node Pointer to the device tree node of the BIF controller |
| * |
| * Returns a BIF controller device pointer for the controller if registration |
| * is successful or an ERR_PTR if an error occurred. |
| */ |
| struct bif_ctrl_dev *bif_ctrl_register(struct bif_ctrl_desc *bif_desc, |
| struct device *dev, void *driver_data, struct device_node *of_node) |
| { |
| struct bif_ctrl_dev *bdev = ERR_PTR(-EINVAL); |
| struct bif_slave_dev *sdev; |
| bool battery_present = false; |
| bool slaves_present = false; |
| int rc, rid_ohm; |
| |
| if (!bif_desc) { |
| pr_err("Invalid bif_desc specified\n"); |
| return bdev; |
| } else if (!bif_desc->name) { |
| pr_err("BIF name missing\n"); |
| return bdev; |
| } else if (!bif_desc->ops) { |
| pr_err("BIF operations missing\n"); |
| return bdev; |
| } else if (!bif_desc->ops->bus_transaction |
| || !bif_desc->ops->bus_transaction_query |
| || !bif_desc->ops->bus_transaction_read |
| || !bif_desc->ops->get_bus_state |
| || !bif_desc->ops->set_bus_state) { |
| pr_err("BIF operation callback function(s) missing\n"); |
| return bdev; |
| } |
| |
| bdev = kzalloc(sizeof(struct bif_ctrl_dev), GFP_KERNEL); |
| if (bdev == NULL) { |
| pr_err("Memory allocation failed for bif_ctrl_dev\n"); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| mutex_init(&bdev->mutex); |
| INIT_LIST_HEAD(&bdev->list); |
| INIT_DELAYED_WORK(&bdev->enter_irq_mode_work, bif_enter_irq_mode_work); |
| bdev->desc = bif_desc; |
| bdev->ctrl_dev = dev; |
| bdev->driver_data = driver_data; |
| bdev->irq_mode_delay_jiffies = 2; |
| |
| mutex_lock(&bif_ctrl_list_mutex); |
| list_add_tail(&bdev->list, &bif_ctrl_list); |
| mutex_unlock(&bif_ctrl_list_mutex); |
| |
| rc = bif_add_all_slaves(bdev); |
| if (rc) |
| pr_debug("Search for all slaves failed, rc=%d\n", rc); |
| rc = bif_add_known_slaves_from_dt(bdev, of_node); |
| if (rc) |
| pr_err("Adding slaves based on device tree addressed failed, rc=%d.\n", |
| rc); |
| rc = bif_assign_all_slaves_dev_addr(bdev); |
| if (rc) |
| pr_err("Failed to set slave device address, rc=%d\n", rc); |
| |
| bif_print_slaves(); |
| |
| if (bdev->desc->ops->get_battery_presence) { |
| rc = bdev->desc->ops->get_battery_presence(bdev); |
| if (rc < 0) { |
| pr_err("Could not determine battery presence, rc=%d\n", |
| rc); |
| } else { |
| battery_present = rc; |
| pr_info("Battery pack present = %c\n", rc ? 'Y' : 'N'); |
| } |
| } |
| |
| if (bdev->desc->ops->get_battery_rid) { |
| rid_ohm = bdev->desc->ops->get_battery_rid(bdev); |
| if (rid_ohm >= 0) |
| pr_info("Battery pack type = %s (Rid=%d ohm)\n", |
| bif_get_battery_pack_type(rid_ohm), rid_ohm); |
| else |
| pr_err("Could not read Rid, rc=%d\n", rid_ohm); |
| } |
| |
| list_for_each_entry(sdev, &bif_sdev_list, list) { |
| if (sdev->present) { |
| battery_present = true; |
| slaves_present = true; |
| break; |
| } |
| } |
| |
| BLOCKING_INIT_NOTIFIER_HEAD(&bdev->bus_change_notifier); |
| |
| /* Disable the BIF bus master if no slaves are found. */ |
| if (!slaves_present) { |
| rc = bdev->desc->ops->set_bus_state(bdev, |
| BIF_BUS_STATE_MASTER_DISABLED); |
| if (rc < 0) |
| pr_err("Could not disble BIF master, rc=%d\n", rc); |
| } |
| |
| if (battery_present) { |
| bdev->battery_present = true; |
| rc = blocking_notifier_call_chain(&bdev->bus_change_notifier, |
| BIF_BUS_EVENT_BATTERY_INSERTED, bdev); |
| if (rc) |
| pr_err("Call chain noification failed, rc=%d\n", rc); |
| } |
| |
| return bdev; |
| } |
| EXPORT_SYMBOL(bif_ctrl_register); |
| |
| /** |
| * bif_ctrl_unregister() - unregisters a BIF controller |
| * @bdev: BIF controller device pointer |
| */ |
| void bif_ctrl_unregister(struct bif_ctrl_dev *bdev) |
| { |
| if (bdev) { |
| mutex_lock(&bif_ctrl_list_mutex); |
| list_del(&bdev->list); |
| mutex_unlock(&bif_ctrl_list_mutex); |
| } |
| } |
| EXPORT_SYMBOL(bif_ctrl_unregister); |