/* drivers/i2c/chips/a1026.c - a1026 voice processor driver * * Copyright (C) 2009 HTC Corporation. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #define FIRMWARE_NAME_MAX_LENGTH 64 #define DEBUG 0 #define ENABLE_DIAG_IOCTLS 0 /* Max cmd length on es305 side is 252 */ #define ES305_I2C_CMD_FIFO_SIZE 128 /* delay (in us) in order to wait for stable power supplies & * for stable system clock. Audience recommends at least 1ms. */ #define ES305_HARD_RESET_PERIOD 1100 /* * This driver is based on the eS305-UG-APIGINTEL-V0 2.pdf spec * for the eS305 Voice Processor */ struct vp_ctxt { struct i2c_client *i2c_dev; struct a1026_platform_data *pdata; unsigned long open; int suspended; struct mutex mutex; } *es305; static int execute_cmdmsg(unsigned int msg, struct vp_ctxt *vp); static int suspend(struct vp_ctxt *vp); enum bool {gpio_l, gpio_h}; static int es305_i2c_read(u8 *rxData, int length, struct vp_ctxt *the_vp) { int rc; struct i2c_client *client = the_vp->i2c_dev; rc = i2c_master_recv(client, rxData, length); if (rc < 0) { pr_debug("%s: transfer error %d\n", __func__, rc); return rc; } #if DEBUG { int i; for (i = 0; i < length; i++) pr_debug("%s: rx[%d] = %2x\n", __func__, i, rxData[i]); } #endif return 0; } static int es305_i2c_write(const u8 *txData, int length, struct vp_ctxt *the_vp) { int rc; struct i2c_client *client = the_vp->i2c_dev; rc = i2c_master_send(client, txData, length); if (rc < 0) { pr_debug("%s: transfer error %d\n", __func__, rc); return rc; } #if DEBUG { int i; for (i = 0; i < length; i++) pr_debug("%s: tx[%d] = %2x\n", __func__, i, txData[i]); } #endif return 0; } static void es305_i2c_sw_reset(unsigned int reset_cmd, struct vp_ctxt *vp) { int rc; u8 msgbuf[4]; msgbuf[0] = (reset_cmd >> 24) & 0xFF; msgbuf[1] = (reset_cmd >> 16) & 0xFF; msgbuf[2] = (reset_cmd >> 8) & 0xFF; msgbuf[3] = reset_cmd & 0xFF; pr_debug("%s: %08x\n", __func__, reset_cmd); rc = es305_i2c_write(msgbuf, 4, vp); if (!rc) msleep(20); /* 20ms is recommended polling period -- p8 spec*/ } static int es305_open(struct inode *inode, struct file *file) { /* Check if device is already open */ if (test_and_set_bit(0, &es305->open)) return -EBUSY; file->private_data = es305; return 0; } static int es305_release(struct inode *inode, struct file *file) { clear_bit(0, &es305->open); return 0; } static int suspend(struct vp_ctxt *vp) { int rc; /* Put es305 into sleep mode */ rc = execute_cmdmsg(A100_msg_Sleep, vp); if (rc < 0) { pr_debug("%s: suspend error\n", __func__); goto set_suspend_err; } vp->suspended = 1; msleep(120); /* 120 defined by fig 2 of eS305 as the time to wait before clock gating */ pr_debug("A1026 suspend\n"); rc = intel_scu_ipc_set_osc_clk0(false, CLK0_AUDIENCE); if (rc) pr_err("ipc clk disable command failed: %d\n", rc); set_suspend_err: return rc; } static ssize_t es305_bootup_init(struct vp_ctxt *vp, const char *firmware_name) { int rc, pass = 0; int remaining; int retry = RETRY_CNT; int i; const u8 *index; u8 buf[2]; const struct firmware *fw_entry; if (firmware_name == NULL || firmware_name[0] == '\0') firmware_name = "vpimg.bin"; dev_dbg(&vp->i2c_dev->dev, "firmware file: %s\n", firmware_name); if (request_firmware(&fw_entry, firmware_name, &vp->i2c_dev->dev)) { dev_err(&vp->i2c_dev->dev, "Firmware not available\n"); return -EFAULT; } if (fw_entry->size > A1026_MAX_FW_SIZE) { pr_err("%s: invalid es305 image size %d\n", __func__, fw_entry->size); return -EINVAL; } while (retry--) { /* Reset es305 chip */ vp->pdata->reset(gpio_l); /* delay in order to wait for stable power supplies & for * stable system clock */ usleep_range(ES305_HARD_RESET_PERIOD, ES305_HARD_RESET_PERIOD); /* Take out of reset */ vp->pdata->reset(gpio_h); msleep(50); /* Delay defined in Figure 1 of eS305 spec */ /* Boot Cmd to es305 */ buf[0] = A1026_msg_BOOT >> 8; buf[1] = A1026_msg_BOOT & 0xff; rc = es305_i2c_write(buf, 2, vp); if (rc < 0) { pr_err("%s: set boot mode error (%d retries left)\n", __func__, retry); continue; } mdelay(1); /* eS305 internal delay */ rc = es305_i2c_read(buf, 1, vp); if (rc < 0) { pr_err("%s: boot mode ack error (%d retries left)\n", __func__, retry); continue; } if (buf[0] != A1026_msg_BOOT_ACK) { pr_err("%s: not a boot-mode ack (%d retries left)\n", __func__, retry); continue; } pr_debug("%s:ACK = %d\n", __func__, buf[0]); remaining = fw_entry->size / I2C_SMBUS_BLOCK_MAX; index = fw_entry->data; pr_debug("%s: starting to load image (%d passes)...\n", __func__, remaining); for (i = 0; i < remaining; i++) { rc = es305_i2c_write(index, I2C_SMBUS_BLOCK_MAX, vp); index += I2C_SMBUS_BLOCK_MAX; if (rc < 0) break; } if (rc >= 0 && fw_entry->size % I2C_SMBUS_BLOCK_MAX) rc = es305_i2c_write(index, fw_entry->size % I2C_SMBUS_BLOCK_MAX, vp); if (rc < 0) { pr_err("%s: fw load error %d (%d retries left)\n", __func__, rc, retry); continue; } msleep(100); /* Delay time before issue a Sync Cmd BUGBUG should be 10*/ pr_debug("%s: firmware loaded successfully\n", __func__); rc = execute_cmdmsg(A100_msg_Sync, vp); if (rc < 0) { pr_err("%s: sync command error %d (%d retries left)\n", __func__, rc, retry); continue; } pass = 1; break; } if (pass) pr_debug("%s: initialized!\n", __func__); else pr_err("%s: initialization failed\n", __func__); release_firmware(fw_entry); return rc; } static ssize_t chk_wakeup_es305(struct vp_ctxt *the_vp) { int rc = 0, retry = 3; if (the_vp->suspended == 1) { the_vp->pdata->wakeup(gpio_l); msleep(30); /* es305b spec: need to wait 30ms after wake-up */ do { rc = execute_cmdmsg(A100_msg_Sync, the_vp); } while ((rc < 0) && --retry); the_vp->pdata->wakeup(gpio_h); if (rc < 0) { pr_err("%s: failed (%d)\n", __func__, rc); goto wakeup_sync_err; } the_vp->suspended = 0; } wakeup_sync_err: return rc; } int execute_cmdmsg(unsigned int msg, struct vp_ctxt *vp) { int rc; int retries, pass = 0; u8 msgbuf[4]; u8 chkbuf[4]; unsigned int sw_reset; sw_reset = ((A100_msg_BootloadInitiate << 16) | RESET_IMMEDIATE); msgbuf[0] = (msg >> 24) & 0xFF; msgbuf[1] = (msg >> 16) & 0xFF; msgbuf[2] = (msg >> 8) & 0xFF; msgbuf[3] = msg & 0xFF; memcpy(chkbuf, msgbuf, 4); rc = es305_i2c_write(msgbuf, 4, vp); if (rc < 0) { pr_debug("%s: error %d\n", __func__, rc); es305_i2c_sw_reset(sw_reset, vp); return rc; } /* We don't need to get Ack after sending out a suspend command */ if (msg == A100_msg_Sleep) return rc; retries = POLLING_RETRY_CNT; msleep(1); /* BUGBUG should be 20 p8 spec */ while (retries--) { rc = 0; memset(msgbuf, 0, sizeof(msgbuf)); rc = es305_i2c_read(msgbuf, 4, vp); if (rc < 0) { pr_debug("%s: ack-read error %d (%d retries)\n" , __func__, rc, retries); continue; } if (msgbuf[0] == 0x80 && msgbuf[1] == chkbuf[1]) { pass = 1; pr_debug("%s: ACK OF SYNC CMD\n", __func__); break; } else if (msgbuf[0] == 0xff && msgbuf[1] == 0xff) { pr_debug("%s: illegal cmd %08x\n", __func__, msg); rc = -EINVAL; break; } else if (msgbuf[0] == 0x00 && msgbuf[1] == 0x00) { pr_debug("%s: not ready (%d retries)\n", __func__, retries); rc = -EBUSY; } else { pr_debug("%s: cmd/ack mismatch: (%d retries left)\n", __func__, retries); #if DEBUG pr_debug("%s: msgbuf[0] = %x\n", __func__, msgbuf[0]); pr_debug("%s: msgbuf[1] = %x\n", __func__, msgbuf[1]); pr_debug("%s: msgbuf[2] = %x\n", __func__, msgbuf[2]); pr_debug("%s: msgbuf[3] = %x\n", __func__, msgbuf[3]); #endif rc = -EBUSY; } msleep(20); /* eS305 spec p. 8 : use polling */ } if (!pass) { pr_err("%s: failed execute cmd %08x (%d)\n", __func__, msg, rc); es305_i2c_sw_reset(sw_reset, vp); } return rc; } static ssize_t es305_write(struct file *file, const char __user *buff, size_t count, loff_t *offp) { int rc; unsigned int i, j, sw_reset; unsigned int nb_block, nb_sub_block; unsigned int msg_buf_count, size_cmd_snd, remaining; unsigned char *kbuf; struct vp_ctxt *the_vp; #if DEBUG unsigned char msgbuf[4]; #endif sw_reset = ((A100_msg_BootloadInitiate << 16) | RESET_IMMEDIATE); mutex_lock(&es305->mutex); the_vp = file->private_data; if (!the_vp) { rc = -EINVAL; goto out_unlock; } rc = chk_wakeup_es305(the_vp); if (rc < 0) goto out_unlock; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) { rc = -ENOMEM; goto out_unlock; } if (copy_from_user(kbuf, buff, count)) { rc = -EFAULT; goto out_kfree; } #if DEBUG for (i = 0; i < count; i++) pr_debug("%s: kbuf %x\n", __func__, kbuf[i]); #endif size_cmd_snd = ES305_I2C_CMD_FIFO_SIZE; msg_buf_count = 0; nb_block = count / size_cmd_snd; nb_sub_block = size_cmd_snd / I2C_SMBUS_BLOCK_MAX; for (i = 0; i < nb_block; i++) { for (j = 0; j < nb_sub_block; j++) { rc = es305_i2c_write(&kbuf[msg_buf_count], I2C_SMBUS_BLOCK_MAX, the_vp); if (rc < 0) { pr_err("ES305 CMD block write error!\n"); es305_i2c_sw_reset(sw_reset, the_vp); goto out_kfree; } msg_buf_count += I2C_SMBUS_BLOCK_MAX; pr_debug("write OK block %d sub-block %d\n", i, j); } usleep_range(1*USEC_PER_MSEC, 2*USEC_PER_MSEC); } remaining = count - msg_buf_count; if (rc >= 0 && remaining) { nb_sub_block = remaining / I2C_SMBUS_BLOCK_MAX; for (i = 0; i < nb_sub_block; i++) { rc = es305_i2c_write(&kbuf[msg_buf_count], I2C_SMBUS_BLOCK_MAX, the_vp); if (rc < 0) { pr_err("ES305 CMD block write error!\n"); es305_i2c_sw_reset(sw_reset, the_vp); goto out_kfree; } msg_buf_count += I2C_SMBUS_BLOCK_MAX; remaining -= I2C_SMBUS_BLOCK_MAX; pr_debug("write OK last block sub-block %d\n", i); } } if (rc >= 0 && remaining) { rc = es305_i2c_write(&kbuf[msg_buf_count], remaining, the_vp); if (rc < 0) { pr_err("ES305 CMD block write error!\n"); es305_i2c_sw_reset(sw_reset, the_vp); goto out_kfree; } pr_debug("write OK remaining %d\n", remaining); } rc = count; out_kfree: kfree(kbuf); out_unlock: mutex_unlock(&es305->mutex); return rc; } static ssize_t es305_read(struct file *file, char __user *buff, size_t count, loff_t *offp) { u8 *kbuf; struct vp_ctxt *the_vp; int rc; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; mutex_lock(&es305->mutex); the_vp = file->private_data; if (!the_vp) { mutex_unlock(&es305->mutex); rc = -EINVAL; goto out_kfree; } es305_i2c_read(kbuf, count, the_vp); mutex_unlock(&es305->mutex); #if DEBUG { int i; for (i = 0; i < count; i++) pr_debug("%s: kbuf %x\n", __func__, kbuf[i]); } #endif if (copy_to_user(buff, kbuf, count)) { rc = -EFAULT; goto out_kfree; } rc = count; out_kfree: kfree(kbuf); return rc; } static long es305_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct vp_ctxt *the_vp; /* rc to u32 for 32bit->64bit safety as return value */ u32 rc; char firmware_name[FIRMWARE_NAME_MAX_LENGTH]; pr_debug("-> %s\n", __func__); if (file && file->private_data) the_vp = file->private_data; else return -EINVAL; pr_debug("%s: ioctl vp Ok\n", __func__); switch (cmd) { case A1026_BOOTUP_INIT: mutex_lock(&the_vp->mutex); rc = strncpy_from_user(firmware_name, (const char * __user) arg, FIRMWARE_NAME_MAX_LENGTH); if (rc == FIRMWARE_NAME_MAX_LENGTH) rc = -ERANGE; if (rc >= 0) rc = es305_bootup_init(the_vp, firmware_name); mutex_unlock(&the_vp->mutex); break; case A1026_SUSPEND: mutex_lock(&the_vp->mutex); rc = suspend(the_vp); mutex_unlock(&the_vp->mutex); if (rc < 0) pr_err("suspend error\n"); break; case A1026_ENABLE_CLOCK: pr_debug("%s:ipc clk enable command\n", __func__); mutex_lock(&the_vp->mutex); rc = intel_scu_ipc_set_osc_clk0(true, CLK0_AUDIENCE); mutex_unlock(&the_vp->mutex); if (rc) { pr_err("ipc clk enable command failed: %d\n", rc); return rc; } break; default: pr_debug("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); rc = -EINVAL; break; } return rc; } static const struct file_operations a1026_fops = { .owner = THIS_MODULE, .open = es305_open, .release = es305_release, .write = es305_write, .read = es305_read, .unlocked_ioctl = es305_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = es305_ioctl, #endif .llseek = no_llseek, }; static struct miscdevice a1026_device = { .minor = MISC_DYNAMIC_MINOR, .name = "audience_es305", .fops = &a1026_fops, }; static int a1026_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; struct vp_ctxt *the_vp; struct a1026_platform_data *pdata; dev_dbg(&client->dev, "probe\n"); the_vp = kzalloc(sizeof(struct vp_ctxt), GFP_KERNEL); if (!the_vp) { rc = -ENOMEM; dev_err(&client->dev, "platform data is out of memory\n"); goto err_exit; } the_vp->i2c_dev = client; i2c_set_clientdata(client, the_vp); pdata = client->dev.platform_data; if (!pdata) { rc = -EINVAL; dev_err(&client->dev, "platform data is invalid\n"); goto err_kfree; } rc = pdata->request_resources(client); if (rc) { dev_err(&client->dev, "Cannot get ressources\n"); goto err_kfree; } mutex_init(&the_vp->mutex); rc = misc_register(&a1026_device); if (rc) { dev_err(&client->dev, "es305_device register failed\n"); goto err_misc_register; } es305 = the_vp; es305->pdata = pdata; pdata->wakeup(gpio_h); pdata->reset(gpio_h); return 0; err_misc_register: mutex_destroy(&the_vp->mutex); err_kfree: kfree(the_vp); i2c_set_clientdata(client, NULL); err_exit: return rc; } static int a1026_remove(struct i2c_client *client) { struct a1026_platform_data *pdata; pdata = client->dev.platform_data; misc_deregister(&a1026_device); mutex_destroy(&es305->mutex); pdata->free_resources(client); kfree(i2c_get_clientdata(client)); i2c_set_clientdata(client, NULL); return 0; } static int a1026_suspend(struct i2c_client *client, pm_message_t mesg) { return 0; } static int a1026_resume(struct i2c_client *client) { return 0; } static const struct i2c_device_id a1026_id[] = { { "audience_es305", 0 }, { } }; static struct i2c_driver a1026_driver = { .probe = a1026_probe, .remove = a1026_remove, .suspend = a1026_suspend, .resume = a1026_resume, .id_table = a1026_id, .driver = { .name = "audience_es305", }, }; static int __init a1026_init(void) { pr_debug("AUDIENCE%s\n", __func__); return i2c_add_driver(&a1026_driver); } static void __exit a1026_exit(void) { i2c_del_driver(&a1026_driver); } module_init(a1026_init); module_exit(a1026_exit); MODULE_DESCRIPTION("A1026 voice processor driver"); MODULE_LICENSE("GPL");