/* * Copyright (C) 2012-2014 Motorola, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define M4SENSORHUB_NUM_GPIOS 6 /* --------------- Global Declarations -------------- */ char m4sensorhub_debug; EXPORT_SYMBOL_GPL(m4sensorhub_debug); /* ------------ Local Function Prototypes ----------- */ /* -------------- Local Data Structures ------------- */ static struct miscdevice m4sensorhub_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = M4SENSORHUB_DRIVER_NAME, }; struct init_call { int(*initcb)(struct init_calldata *); void *pdata; struct init_call *next; }; /* --------------- Local Declarations -------------- */ static struct m4sensorhub_data m4sensorhub_misc_data; static struct init_call *inithead; static struct init_call *preflash_head; static char tcmd_exec_status; unsigned short force_upgrade; module_param(force_upgrade, short, 0644); MODULE_PARM_DESC(force_upgrade, "Force FW download ignoring version check"); unsigned short debug_level; module_param(debug_level, short, 0644); MODULE_PARM_DESC(debug_level, "Set debug level 1 (CRITICAL) to 7 (VERBOSE_DEBUG)"); /* -------------- Global Functions ----------------- */ struct m4sensorhub_data *m4sensorhub_client_get_drvdata(void) { return &m4sensorhub_misc_data; } EXPORT_SYMBOL_GPL(m4sensorhub_client_get_drvdata); /* -------------- Local Functions ----------------- */ static ssize_t m4sensorhub_get_dbg(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", m4sensorhub_debug); } /* BEGIN BOARD FILE */ /* TODO: replace with request array */ int m4sensorhub_get_current_mode(void) { return m4sensorhub_misc_data.mode; } EXPORT_SYMBOL_GPL(m4sensorhub_get_current_mode); int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_bootmode bootmode) { if (m4sensorhub == NULL) { pr_err("%s: M4 data is NULL\n", __func__); return -EINVAL; } switch (bootmode) { case BOOTMODE00: gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0); gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0); break; case BOOTMODE01: gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1); gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0); break; case BOOTMODE10: gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0); gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1); break; case BOOTMODE11: gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1); gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1); default: break; } return 0; } EXPORT_SYMBOL_GPL(m4sensorhub_set_bootmode); void m4sensorhub_hw_reset(struct m4sensorhub_data *m4sensorhub) { int err = 0; if (m4sensorhub == NULL) { pr_err("%s: M4 data is NULL\n", __func__); err = -ENODATA; goto m4sensorhub_hw_reset_fail; } else if (m4sensorhub->i2c_client == NULL) { pr_err("%s: I2C client is missing\n", __func__); err = -ENODATA; goto m4sensorhub_hw_reset_fail; } if (m4sensorhub->i2c_client->addr == 0x39) { err = m4sensorhub_set_bootmode(m4sensorhub, BOOTMODE01); if (err < 0) { pr_err("%s: Failed to enter bootmode 01\n", __func__); goto m4sensorhub_hw_reset_fail; } usleep_range(5000, 10000); gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 0); usleep_range(10000, 12000); gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1); msleep(400); } else { err = m4sensorhub_set_bootmode(m4sensorhub, BOOTMODE00); if (err < 0) { pr_err("%s: Failed to enter bootmode 00\n", __func__); goto m4sensorhub_hw_reset_fail; } gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1); usleep_range(5000, 10000); gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 0); usleep_range(5000, 10000); gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1); } m4sensorhub_hw_reset_fail: if (err < 0) pr_err("%s: Failed with error code %d", __func__, err); } EXPORT_SYMBOL_GPL(m4sensorhub_hw_reset); /* callback from driver to initialize hardware on probe */ static int m4sensorhub_hw_init(struct m4sensorhub_data *m4sensorhub, struct device_node *node) { int gpio; int err = -EINVAL; const char *fp = NULL; if (m4sensorhub == NULL) { pr_err("%s: M4 data is NULL\n", __func__); err = -ENODATA; goto error; } else if (node == NULL) { pr_err("%s: Device node is missing\n", __func__); err = -ENODATA; goto error; } of_property_read_string(node, "mot,fw-filename", &fp); if (fp == NULL) { pr_err("%s: Missing M4 sensorhub firmware filename\n", __func__); err = -EINVAL; goto error; } m4sensorhub->filename = (char *)fp; gpio = of_get_named_gpio_flags(node, "mot,irq-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-intr"); if (err) { pr_err("Failed acquiring M4 Sensor Hub IRQ GPIO-%d (%d)\n", gpio, err); goto error; } gpio_direction_input(gpio); m4sensorhub->hwconfig.irq_gpio = gpio; gpio = of_get_named_gpio_flags(node, "mot,reset-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-reset"); if (err) { pr_err("Failed acquiring M4 Sensor Hub Reset GPIO-%d (%d)\n", gpio, err); goto error_reset; } /* hold M4 reset till M4 load firmware procduce starts * this is needed for snowflake touch determination */ gpio_direction_output(gpio, 0); m4sensorhub->hwconfig.reset_gpio = gpio; gpio = of_get_named_gpio_flags(node, "mot,wake-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-wake"); if (err) { pr_err("Failed acquiring M4 Sensor Hub Wake GPIO-%d (%d)\n", gpio, err); goto error_wake; } gpio_direction_output(gpio, 0); m4sensorhub->hwconfig.wake_gpio = gpio; gpio = of_get_named_gpio_flags(node, "mot,boot0-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot0"); if (err) { pr_err("Failed acquiring M4 Sensor Hub Boot0 GPIO-%d (%d)\n", gpio, err); goto error_boot0; } gpio_direction_output(gpio, 0); m4sensorhub->hwconfig.boot0_gpio = gpio; gpio = of_get_named_gpio_flags(node, "mot,boot1-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot1"); if (err) { pr_err("Failed acquiring M4 Sensor Hub Boot1 GPIO-%d (%d)\n", gpio, err); goto error_boot1; } gpio_direction_output(gpio, 0); m4sensorhub->hwconfig.boot1_gpio = gpio; gpio = of_get_named_gpio_flags(node, "mot,enable-gpio", 0, NULL); err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-enable"); if (err) { pr_err("Failed acquiring M4 Sensor Hub Enable GPIO-%d (%d)\n", gpio, err); goto error_enable; } gpio_direction_output(gpio, 0); m4sensorhub->hwconfig.mpu_9150_en_gpio = gpio; return 0; error_enable: gpio_free(m4sensorhub->hwconfig.boot1_gpio); m4sensorhub->hwconfig.boot1_gpio = -1; error_boot1: gpio_free(m4sensorhub->hwconfig.boot0_gpio); m4sensorhub->hwconfig.boot0_gpio = -1; error_boot0: gpio_free(m4sensorhub->hwconfig.wake_gpio); m4sensorhub->hwconfig.wake_gpio = -1; error_wake: gpio_free(m4sensorhub->hwconfig.reset_gpio); m4sensorhub->hwconfig.reset_gpio = -1; error_reset: gpio_free(m4sensorhub->hwconfig.irq_gpio); m4sensorhub->hwconfig.irq_gpio = -1; error: m4sensorhub->filename = NULL; return err; } /* callback from driver to free hardware on shutdown */ static void m4sensorhub_hw_free(struct m4sensorhub_data *m4sensorhub) { if (m4sensorhub == NULL) { pr_err("%s: M4 data is NULL\n", __func__); return; } if (m4sensorhub->hwconfig.irq_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.irq_gpio); m4sensorhub->hwconfig.irq_gpio = -1; } if (m4sensorhub->hwconfig.reset_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.reset_gpio); m4sensorhub->hwconfig.reset_gpio = -1; } if (m4sensorhub->hwconfig.wake_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.wake_gpio); m4sensorhub->hwconfig.wake_gpio = -1; } if (m4sensorhub->hwconfig.boot0_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.boot0_gpio); m4sensorhub->hwconfig.boot0_gpio = -1; } if (m4sensorhub->hwconfig.boot1_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.boot1_gpio); m4sensorhub->hwconfig.boot1_gpio = -1; } if (m4sensorhub->hwconfig.mpu_9150_en_gpio >= 0) { gpio_free(m4sensorhub->hwconfig.mpu_9150_en_gpio); m4sensorhub->hwconfig.mpu_9150_en_gpio = -1; } m4sensorhub->filename = NULL; } int m4sensorhub_register_initcall(int(*initfunc)(struct init_calldata *), void *pdata) { struct init_call *inc = NULL; inc = kzalloc(sizeof(struct init_call), GFP_KERNEL); if (inc == NULL) { KDEBUG(M4SH_ERROR, "%s: Unable to allocate init call mem\n", __func__); return -ENOMEM; } inc->initcb = initfunc; inc->pdata = pdata; /* add it to the list */ if (inithead == NULL) inc->next = NULL; else inc->next = inithead; inithead = inc; return 0; } EXPORT_SYMBOL_GPL(m4sensorhub_register_initcall); void m4sensorhub_unregister_initcall(int(*initfunc)(struct init_calldata *)) { struct init_call *node = inithead; struct init_call *prev; for (node = inithead, prev = NULL; node != NULL; prev = node, node = node->next) { if (node->initcb == initfunc) { /* remove this node */ if (node == inithead) inithead = node->next; else prev->next = node->next; kfree(node); } } } EXPORT_SYMBOL_GPL(m4sensorhub_unregister_initcall); int m4sensorhub_register_preflash_callback( int(*initfunc)(struct init_calldata *), void *pdata) { struct init_call *inc = NULL; inc = kzalloc(sizeof(struct init_call), GFP_KERNEL); if (inc == NULL) { KDEBUG(M4SH_ERROR, "%s: Unable to allocate init call mem\n", __func__); return -ENOMEM; } inc->initcb = initfunc; inc->pdata = pdata; /* add it to the list */ if (preflash_head == NULL) inc->next = NULL; else inc->next = preflash_head; preflash_head = inc; return 0; } EXPORT_SYMBOL_GPL(m4sensorhub_register_preflash_callback); void m4sensorhub_unregister_preflash_callback( int(*initfunc)(struct init_calldata *)) { struct init_call *node = preflash_head; struct init_call *prev; for (node = preflash_head, prev = NULL; node != NULL; prev = node, node = node->next) { if (node->initcb == initfunc) { /* remove this node */ if (node == preflash_head) preflash_head = node->next; else prev->next = node->next; kfree(node); } } } EXPORT_SYMBOL_GPL(m4sensorhub_unregister_preflash_callback); void m4sensorhub_call_preflash_callbacks(void) { struct init_calldata arg; struct init_call *inc = NULL; struct init_call *prev = NULL; int err = 0; /* Call any registered preflash callbacks */ inc = preflash_head; arg.p_m4sensorhub_data = &m4sensorhub_misc_data; prev = NULL; while (inc) { arg.p_data = inc->pdata; err = inc->initcb(&arg); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Callback failed with error code %d\n", __func__, err); } prev = inc; inc = inc->next; kfree(prev); } return; } EXPORT_SYMBOL_GPL(m4sensorhub_call_preflash_callbacks); bool m4sensorhub_preflash_callbacks_exist(void) { if (preflash_head != NULL) return true; else return false; } EXPORT_SYMBOL_GPL(m4sensorhub_preflash_callbacks_exist); /* END BOARD FILE FUNCTIONS */ /* Downloads m4 firmware and also initializes all m4 drivers */ static void m4sensorhub_initialize(const struct firmware *firmware, void *context) { int err = 0; struct init_call *inc, *prev; struct init_calldata arg; if (firmware == NULL) { KDEBUG(M4SH_ERROR, "%s: No firmware data recieved\n", __func__); return; } /* Initiate M4 firmware download */ KDEBUG(M4SH_CRITICAL, "%s: Starting M4 download %s = %d\n", __func__, "with force_upgrade", force_upgrade); if (m4sensorhub_misc_data.i2c_client->addr == 0x39) err = m4sensorhub_401_load_firmware(&m4sensorhub_misc_data, force_upgrade, firmware); else err = m4sensorhub_load_firmware(&m4sensorhub_misc_data, force_upgrade, firmware); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: %s = %d\n", __func__, "Failed to load M4 firmware", err); /* Since download failed, return M4 to boot mode */ m4sensorhub_hw_reset(&m4sensorhub_misc_data); return; } err = m4sensorhub_test_m4_reboot(&m4sensorhub_misc_data, false); if (err < 0) { /* Getting here is unlikely because failures normally panic */ KDEBUG(M4SH_ERROR, "%s: Testing M4 reboot failed.\n", __func__); return; } err = m4sensorhub_irq_init(&m4sensorhub_misc_data); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: m4sensorhub irq init failed (err=%d)\n", __func__, err); return; } err = m4sensorhub_extern_init(&m4sensorhub_misc_data); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Extern init failed.\n", __func__); return; } /* Initialize all the m4 drivers */ inc = inithead; arg.p_m4sensorhub_data = &m4sensorhub_misc_data; prev = NULL; while (inc) { arg.p_data = inc->pdata; err = inc->initcb(&arg); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Callback failed with error code %d\n", __func__, err); } prev = inc; inc = inc->next; kfree(prev); } /* Now that all drivers are kicked off, flag this as our normal mode of operation */ m4sensorhub_misc_data.mode = NORMALMODE; } static ssize_t m4sensorhub_set_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long debug; if ((kstrtol(buf, 10, &debug) < 0) || (debug < M4SH_NODEBUG) || (debug > M4SH_VERBOSE_DEBUG)) return -EINVAL; m4sensorhub_debug = debug; KDEBUG(M4SH_CRITICAL, "%s: M4 Sensor Hub debug level = %d\n", __func__, m4sensorhub_debug); return count; } static DEVICE_ATTR(debug_level, S_IRUSR|S_IWUSR, m4sensorhub_get_dbg, m4sensorhub_set_dbg); static ssize_t m4sensorhub_get_loglevel(struct device *dev, struct device_attribute *attr, char *buf) { uint32_t logenable[LOG_EN_SIZE], len, i; m4sensorhub_reg_read(&m4sensorhub_misc_data, M4SH_REG_LOG_LOGENABLE, (char *)&logenable); len = sprintf(buf, "M4 log levels = 0x"); for (i = 0; i < LOG_EN_SIZE; i++) len += sprintf(buf+len, "%08x", logenable[i]); KDEBUG(M4SH_INFO, "%s\n", buf); return snprintf(buf, PAGE_SIZE, "%s\n", buf); } void m4sensorhub_update_loglevels(int8_t *tag, int8_t *level, uint32_t *log_level) { uint32_t i; int32_t levelindex = -1; int32_t tagindex = -1; uint32_t mask; int32_t logenableindex; int32_t en = LOG_TAGS_PER_ENABLE; int32_t b = LOG_NO_OF_BITS_PER_TAG; for (i = 0; i < LOG_LEVELS_MAX; i++) { if (strcmp(acLogLevels[i], level) == 0) { levelindex = i; break; } } for (i = 0; i < LOG_MAX; i++) { if (strcmp(acLogTags[i], tag) == 0) { tagindex = i; break; } } if ((tagindex == -1) || (levelindex == -1)) return; logenableindex = tagindex/LOG_TAGS_PER_ENABLE; /*Clear the revelant bits*/ mask = LOG_TAG_MASK; *(log_level+logenableindex) &= ~(mask << ((tagindex % en) * b)); /*set debug level for the relevant bits*/ *(log_level+logenableindex) |= (levelindex << ((tagindex % en) * b)); KDEBUG(M4SH_DEBUG, "New M4 log levels = "); for (i = 0; i < LOG_EN_SIZE; i++) KDEBUG(M4SH_DEBUG, "enable %d = 0x%08x ", i, *(log_level+i)); KDEBUG(M4SH_DEBUG, "\n"); } /* Usage: adb shell into the directory of sysinterface log_level and echo LOG_ACCEL=LOG_DEGUB,LOG_POWER=LOG_ERROR > log_level */ static ssize_t m4sensorhub_set_loglevel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { uint32_t cur_loglevels[LOG_EN_SIZE]; char *tag, *level; char **logbuf = (char **) &buf; m4sensorhub_reg_read(&m4sensorhub_misc_data, M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels); while (1) { tag = strsep(logbuf, "=,\n "); if ((tag == NULL) || (logbuf == NULL)) break; level = strsep(logbuf, "=,\n "); if ((level == NULL) || (logbuf == NULL)) break; m4sensorhub_update_loglevels(tag, level, (uint32_t *)cur_loglevels); } m4sensorhub_reg_write(&m4sensorhub_misc_data, M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels, m4sh_no_mask); return count; } static DEVICE_ATTR(log_level, S_IRUSR|S_IWUSR, m4sensorhub_get_loglevel, m4sensorhub_set_loglevel); static ssize_t m4sensorhub_get_tcmd_response(struct device *dev, struct device_attribute *attr, char *buf) { if (tcmd_exec_status) return sprintf(buf, "TCMD execution passed\n"); else return sprintf(buf, "TCMD execution failed\n"); } static ssize_t m4sensorhub_execute_tcmd(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int opcode, subopcode; int ret, tcmd_resp_len, i; char tcmd_buf[20]; tcmd_exec_status = 0; sscanf(buf, "0x%x 0x%x 0x%x", &opcode, &subopcode, &tcmd_resp_len); tcmd_buf[0] = M4SH_TYPE_TCMD; tcmd_buf[1] = (opcode & 0xFF); tcmd_buf[2] = (subopcode & 0xFF); ret = m4sensorhub_i2c_write_read(&m4sensorhub_misc_data, tcmd_buf, 3, tcmd_resp_len); if (ret < 0) { KDEBUG(M4SH_ERROR, "m4sensorhub tcmd i2c failed\n"); return ret; } if (ret != tcmd_resp_len) { KDEBUG(M4SH_ERROR, "m4sensorhub tcmd wrong num bytes read\n"); return -EBADE; } for (i = 0; i < tcmd_resp_len; i++) KDEBUG(M4SH_INFO, "%#x ", (unsigned char)tcmd_buf[i]); KDEBUG(M4SH_INFO, "\n"); if (tcmd_buf[0] == 0x00) tcmd_exec_status = 1; return count; } static DEVICE_ATTR(tcmd, S_IRUSR|S_IWUSR, m4sensorhub_get_tcmd_response, m4sensorhub_execute_tcmd); static ssize_t m4sensorhub_get_download_status(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", m4sensorhub_misc_data.mode == NORMALMODE ? "1" : "0"); } static DEVICE_ATTR(download_status, S_IRUGO, m4sensorhub_get_download_status, NULL); static ssize_t m4sensorhub_get_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%#hx\n", m4sensorhub_misc_data.mode == NORMALMODE ? m4sensorhub_misc_data.fw_version : 0xFFFF); } static DEVICE_ATTR(firmware_version, S_IRUGO, m4sensorhub_get_firmware_version, NULL); static ssize_t m4sensorhub_disable_interrupts(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; ret = m4sensorhub_irq_disable_all(&m4sensorhub_misc_data); if (ret < 0) { KDEBUG(M4SH_ERROR, "%s: Unable to disable all m4 interrupts\n", __func__); return ret; } return count; } static DEVICE_ATTR(disable_interrupts, S_IWUSR, NULL, m4sensorhub_disable_interrupts); /* This sysfs is to be used only to disable power management. It cannot be used to enable power management */ static ssize_t m4sensorhub_disable_powermanagement(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { char enable = 0; return m4sensorhub_reg_write_1byte(&m4sensorhub_misc_data, M4SH_REG_POWER_ENABLE, enable, 0xFF); } static DEVICE_ATTR(disable_powermanagement, S_IWUSR, NULL, m4sensorhub_disable_powermanagement); /* This sysfs can be used to read/write m4 registers This is provided to help in debugging m4 issues Example to read an m4 register interrupt enable1( bank = 0x02, regaddr = 0x0B) echo R 0x02 0x0B 0x01 > raw_i2c R -- indicates read transaction 0x02 -- reg bank address 0x0B -- register address 0x01 -- number of bytes to be read The result of the read will be printed out in kernel logs To write to a m4 regiser interrupt enable1( bank = 0x02, regaddr = 0x0B) echo W 0x02 0x0B 0xFF > raw_i2c W -- indicates write transaction 0x02 -- reg bank address 0x0b -- register addr 0xFF -- data to be written to this address Note: Write trasactions have a limitation to be able to write only 1 byte */ static ssize_t m4sensorhub_raw_i2c(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned char buffer[20]; unsigned int bank, addr, len; unsigned char operation; int ret, i; sscanf(buf, "%c 0x%x 0x%x 0x%x", &operation, &bank, &addr, &len); if (operation == 'R') { KDEBUG(M4SH_INFO, "Reading %d bytes from bank 0x%x add 0x%x\n", len, bank, addr); buffer[0] = (bank & 0xFF); buffer[1] = (addr & 0xFF); ret = m4sensorhub_i2c_write_read(&m4sensorhub_misc_data, buffer, 2, len); if (ret != len) { KDEBUG(M4SH_ERROR, "Failed to read from bank 0x%x addr 0x%x", bank, addr); } else { for (i = 0; i < len; i++) KDEBUG(M4SH_INFO, "0x%x ", (unsigned char)buffer[i]); KDEBUG(M4SH_INFO, "\n"); } } else if (operation == 'W') { KDEBUG(M4SH_INFO, "Writing 0x%x to bank 0x%x addr 0x%x\n", len, bank, addr); buffer[0] = (bank & 0xFF); buffer[1] = (addr & 0xFF); buffer[2] = (len & 0xFF); ret = m4sensorhub_i2c_write_read(&m4sensorhub_misc_data, buffer, 3, 0); if (ret != 1) { KDEBUG(M4SH_ERROR, "Failed to write 0x%x from bank 0x%x addr 0x%x", len, bank, addr); } } else { KDEBUG(M4SH_ERROR, "Unknown operation = %c", operation); } return count; } static DEVICE_ATTR(raw_i2c, S_IWUSR, NULL, m4sensorhub_raw_i2c); static struct attribute *m4sensorhub_control_attributes[] = { &dev_attr_tcmd.attr, &dev_attr_log_level.attr, &dev_attr_debug_level.attr, &dev_attr_firmware_version.attr, &dev_attr_download_status.attr, &dev_attr_disable_interrupts.attr, &dev_attr_disable_powermanagement.attr, &dev_attr_raw_i2c.attr, NULL }; static const struct attribute_group m4sensorhub_control_group = { .attrs = m4sensorhub_control_attributes, }; static int m4sensorhub_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct m4sensorhub_data *m4sensorhub = &m4sensorhub_misc_data; struct device_node *node = client->dev.of_node; int err = -EINVAL; /* Set debug based on module argument if set, otherwise use default logging rate based on build type */ if (debug_level) m4sensorhub_debug = debug_level; else { #ifdef CONFIG_DEBUG_FS /* engineering build */ m4sensorhub_debug = M4SH_INFO; #else /* user/userdebug builds */ m4sensorhub_debug = M4SH_ERROR; #endif } /* Enabling detailed level M4 logs for all builds*/ m4sensorhub_debug = M4SH_INFO; KDEBUG(M4SH_ERROR, "%s: Initializing M4 Sensor Hub debug=%d\n", __func__, m4sensorhub_debug); m4sensorhub->mode = UNINITIALIZED; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { KDEBUG(M4SH_ERROR, "%s: client not i2c capable\n", __func__); err = -ENODEV; goto err_unload; } /* link m4sensorhub to i2c_client, hw_init uses i2c_client */ m4sensorhub->i2c_client = client; err = m4sensorhub_hw_init(m4sensorhub, node); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: hw_init failed!", __func__); goto done; } /* link i2c_client to m4sensorhub */ i2c_set_clientdata(client, m4sensorhub); err = misc_register(&m4sensorhub_misc_device); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: misc_register failed: %d\n", __func__, err); goto err_hw_free; } err = sysfs_create_group(&client->dev.kobj, &m4sensorhub_control_group); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Failed to create sysfs group\n", __func__); goto err_deregister; } if (m4sensorhub->hwconfig.irq_gpio >= 0) client->irq = gpio_to_irq(m4sensorhub->hwconfig.irq_gpio); else { KDEBUG(M4SH_ERROR, "%s: No IRQ configured\n", __func__); err = -ENODEV; goto err_unregister_control_group; } err = m4sensorhub_panic_init(m4sensorhub); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Panic init failed\n", __func__); goto err_reg_shutdown; } err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, m4sensorhub->filename, &(m4sensorhub->i2c_client->dev), GFP_KERNEL, m4sensorhub, m4sensorhub_initialize); if (err < 0) { KDEBUG(M4SH_ERROR, "%s: request_firmware_nowait failed: %d\n", __func__, err); goto err_panic_shutdown; } KDEBUG(M4SH_NOTICE, "Registered M4 Sensor Hub\n"); goto done; err_panic_shutdown: m4sensorhub_panic_shutdown(m4sensorhub); err_reg_shutdown: m4sensorhub_reg_shutdown(m4sensorhub); err_unregister_control_group: sysfs_remove_group(&client->dev.kobj, &m4sensorhub_control_group); err_deregister: misc_deregister(&m4sensorhub_misc_device); err_hw_free: m4sensorhub->i2c_client = NULL; i2c_set_clientdata(client, NULL); m4sensorhub_hw_free(m4sensorhub); m4sensorhub = NULL; err_unload: done: if (err < 0) { KDEBUG(M4SH_ERROR, "%s: Probe failed with error code %d\n", __func__, err); } return err; } static int __exit m4sensorhub_remove(struct i2c_client *client) { struct m4sensorhub_data *m4sensorhub = i2c_get_clientdata(client); KDEBUG(M4SH_INFO, "Removing M4 Sensor Hub Driver\n"); if (m4sensorhub == NULL) return 0; m4sensorhub_irq_shutdown(m4sensorhub); m4sensorhub_panic_shutdown(m4sensorhub); m4sensorhub_reg_shutdown(m4sensorhub); sysfs_remove_group(&client->dev.kobj, &m4sensorhub_control_group); m4sensorhub_hw_reset(m4sensorhub); misc_deregister(&m4sensorhub_misc_device); m4sensorhub->i2c_client = NULL; i2c_set_clientdata(client, NULL); m4sensorhub_hw_free(m4sensorhub); m4sensorhub = NULL; return 0; } #ifdef CONFIG_PM static int m4sensorhub_suspend(struct i2c_client *client, pm_message_t mesg) { KDEBUG(M4SH_DEBUG, "%s\n", __func__); m4sensorhub_misc_data.irq_dbg.suspend = 1; return 0; } static int m4sensorhub_resume(struct i2c_client *client) { KDEBUG(M4SH_DEBUG, "%s\n", __func__); m4sensorhub_misc_data.irq_dbg.suspend = 0; return 0; } #endif /* CONFIG_PM */ static const struct of_device_id of_m4sensorhub_match[] = { { .compatible = "mot,m4sensorhub", }, {}, }; static const struct i2c_device_id m4sensorhub_id[] = { {M4SENSORHUB_DRIVER_NAME, 0}, { }, }; MODULE_DEVICE_TABLE(i2c, m4sensorhub_id); static struct i2c_driver m4sensorhub_driver = { .driver = { .name = M4SENSORHUB_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_m4sensorhub_match), }, .probe = m4sensorhub_probe, .remove = __exit_p(m4sensorhub_remove), #ifdef CONFIG_PM .suspend = m4sensorhub_suspend, .resume = m4sensorhub_resume, #endif /* CONFIG_PM */ .id_table = m4sensorhub_id, }; static int __init m4sensorhub_init(void) { return i2c_add_driver(&m4sensorhub_driver); } static void __exit m4sensorhub_exit(void) { i2c_del_driver(&m4sensorhub_driver); return; } module_init(m4sensorhub_init); module_exit(m4sensorhub_exit); MODULE_ALIAS("platform:m4sensorhub"); MODULE_DESCRIPTION("M4 Sensor Hub driver"); MODULE_AUTHOR("Motorola"); MODULE_LICENSE("GPL");