diff options
| author | Doug Zobel <dzobel1@motorola.com> | 2013-11-15 14:29:07 -0600 |
|---|---|---|
| committer | James Wylder <jwylder@motorola.com> | 2014-03-05 17:46:52 -0600 |
| commit | d2a782003a6047da120a33e6f8ee6fd33bb825d6 (patch) | |
| tree | 8d20bd4ecda62a06e98993c4108456bc1acb0d0b /drivers/mfd/cpcap-uc.c | |
| parent | 32fd2d36d2464056d4522a9c02797b7c2b2e884f (diff) | |
| download | olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.tar.xz olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.zip | |
CW integration and minnow bringup
* create minnow machine type
* create Android makefile
* add pre-commit syntax check
* enable -Werror
* Add drivers: CPCAP, TPS65xxx, m4sensorhub, atmxt, lm3535,
usb gadget, minnow display, TI 12xx wireless
Change-Id: I7962f5e1256715f2452aed5a62a4f2f2383d5046
Diffstat (limited to 'drivers/mfd/cpcap-uc.c')
| -rw-r--r-- | drivers/mfd/cpcap-uc.c | 927 |
1 files changed, 927 insertions, 0 deletions
diff --git a/drivers/mfd/cpcap-uc.c b/drivers/mfd/cpcap-uc.c new file mode 100644 index 00000000000..2626bfcb5f7 --- /dev/null +++ b/drivers/mfd/cpcap-uc.c @@ -0,0 +1,927 @@ +/* + * Copyright (C) 2008-2010 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 <linux/completion.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/fs.h> +#include <linux/ihex.h> +#include <linux/miscdevice.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/module.h> + +#include <linux/spi/cpcap.h> +#include <linux/spi/cpcap-regbits.h> +#include <linux/spi/spi.h> + +#define ERROR_MACRO_TIMEOUT 0x81 +#define ERROR_MACRO_WRITE 0x82 +#define ERROR_MACRO_READ 0x83 + +#define RAM_START_TI 0x9000 +#define RAM_END_TI 0x9FA0 +#define RAM_START_ST 0x0000 +#define RAM_END_ST 0x0FFF + +#define HWCFG_ADDR_ST 0x0148 +#define HWCFG_ADDR_TI 0x90F4 /* Not yet implemented in the TI uC. */ + +enum { + READ_STATE_1, /* Send size and location of RAM read. */ + READ_STATE_2, /*!< Read MT registers. */ + READ_STATE_3, /*!< Read data from uC. */ + READ_STATE_4, /*!< Check for error. */ +}; + +enum { + WRITE_STATE_1, /* Send size and location of RAM write. */ + WRITE_STATE_2, /* Check for error. */ + WRITE_STATE_3, /* Write data to uC. */ + WRITE_STATE_4 /* Check for error. */ +}; + +struct cpcap_uc_data { + struct cpcap_device *cpcap; + unsigned char is_supported; + unsigned char is_ready; + struct completion completion; + int cb_status; + struct mutex lock; + unsigned char uc_reset; + unsigned char state; + unsigned short state_cntr; + struct { + unsigned short address; + unsigned short *data; + unsigned short num_words; + } req; +}; + +static struct cpcap_uc_data *cpcap_uc_info; + +static int fops_open(struct inode *inode, struct file *file); +static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static ssize_t fops_write(struct file *file, const char *buf, + size_t count, loff_t *ppos); +static ssize_t fops_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = fops_ioctl, + .open = fops_open, + .read = fops_read, + .write = fops_write, +}; + +static struct miscdevice uc_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cpcap_uc", + .fops = &fops, +}; + +static int is_valid_address(struct cpcap_device *cpcap, unsigned short address, + unsigned short num_words) +{ + int vld = 0; + + if (cpcap->vendor == CPCAP_VENDOR_TI) { + vld = (address >= RAM_START_TI) && + ((address + num_words) <= RAM_END_TI); + } else if (cpcap->vendor == CPCAP_VENDOR_ST) { + vld = ((address + num_words) <= RAM_END_ST); + } + + return vld; +} + +static void ram_read_state_machine(enum cpcap_irqs irq, void *data) +{ + struct cpcap_uc_data *uc_data = data; + unsigned short temp; + + if (irq != CPCAP_IRQ_UC_PRIRAMR) + return; + + switch (uc_data->state) { + case READ_STATE_1: + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1, + uc_data->req.address, 0xFFFF); + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, + uc_data->req.num_words, 0xFFFF); + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF); + + if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST) + uc_data->state = READ_STATE_2; + else + uc_data->state = READ_STATE_3; + + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + + break; + + case READ_STATE_2: + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp); + + if (temp == ERROR_MACRO_READ) { + uc_data->state = READ_STATE_1; + uc_data->state_cntr = 0; + + cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + + uc_data->cb_status = -EIO; + + complete(&uc_data->completion); + } else { + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp); + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp); + + uc_data->state = READ_STATE_3; + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + } + break; + + case READ_STATE_3: + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, + uc_data->req.data + uc_data->state_cntr); + + uc_data->state_cntr += 1; + + if (uc_data->state_cntr == uc_data->req.num_words) + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp); + else { + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, + uc_data->req.data + + uc_data->state_cntr); + + uc_data->state_cntr += 1; + } + + if (uc_data->state_cntr == uc_data->req.num_words) + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp); + else { + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, + uc_data->req.data + + uc_data->state_cntr); + + uc_data->state_cntr += 1; + } + + if (uc_data->state_cntr == uc_data->req.num_words) + uc_data->state = READ_STATE_4; + + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + break; + + case READ_STATE_4: + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp); + + if (temp != ERROR_MACRO_READ) + uc_data->cb_status = 0; + else + uc_data->cb_status = -EIO; + + complete(&uc_data->completion); + + uc_data->state = READ_STATE_1; + uc_data->state_cntr = 0; + break; + + default: + uc_data->state = READ_STATE_1; + uc_data->state_cntr = 0; + break; + } +} + +static void ram_write_state_machine(enum cpcap_irqs irq, void *data) +{ + struct cpcap_uc_data *uc_data = data; + unsigned short error_check; + + if (irq != CPCAP_IRQ_UC_PRIRAMW) + return; + + switch (uc_data->state) { + case WRITE_STATE_1: + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1, + uc_data->req.address, 0xFFFF); + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, + uc_data->req.num_words, 0xFFFF); + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF); + + uc_data->state = WRITE_STATE_2; + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW); + break; + + case WRITE_STATE_2: + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check); + + if (error_check == ERROR_MACRO_WRITE) { + uc_data->state = WRITE_STATE_1; + uc_data->state_cntr = 0; + + cpcap_irq_mask(uc_data->cpcap, + CPCAP_IRQ_UC_PRIRAMW); + + uc_data->cb_status = -EIO; + complete(&uc_data->completion); + break; + } else + uc_data->state = WRITE_STATE_3; + + /* No error has occured, fall through */ + + case WRITE_STATE_3: + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1, + *(uc_data->req.data + uc_data->state_cntr), + 0xFFFF); + uc_data->state_cntr += 1; + + if (uc_data->state_cntr == uc_data->req.num_words) + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, 0, + 0xFFFF); + else { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, + *(uc_data->req.data + + uc_data->state_cntr), 0xFFFF); + + uc_data->state_cntr += 1; + } + + if (uc_data->state_cntr == uc_data->req.num_words) + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, + 0xFFFF); + else { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, + *(uc_data->req.data + + uc_data->state_cntr), 0xFFFF); + + uc_data->state_cntr += 1; + } + + if (uc_data->state_cntr == uc_data->req.num_words) + uc_data->state = WRITE_STATE_4; + + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW); + break; + + case WRITE_STATE_4: + cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check); + + if (error_check != ERROR_MACRO_WRITE) + uc_data->cb_status = 0; + else + uc_data->cb_status = -EIO; + + complete(&uc_data->completion); + + uc_data->state = WRITE_STATE_1; + uc_data->state_cntr = 0; + break; + + default: + uc_data->state = WRITE_STATE_1; + uc_data->state_cntr = 0; + break; + } +} + +static void reset_handler(enum cpcap_irqs irq, void *data) +{ + int i; + unsigned short regval; + struct cpcap_uc_data *uc_data = data; + + if (irq != CPCAP_IRQ_UCRESET) + return; + + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCC1, + CPCAP_BIT_PRIHALT, CPCAP_BIT_PRIHALT); + + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_PGC, + CPCAP_BIT_PRI_UC_SUSPEND, CPCAP_BIT_PRI_UC_SUSPEND); + + uc_data->uc_reset = 1; + uc_data->cb_status = -EIO; + complete(&uc_data->completion); + + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2, 0, 0xFFFF); + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MIM1, 0xFFFF, 0xFFFF); + cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_PRIMAC); + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UCRESET); + + for (i = 0; i <= CPCAP_REG_END; i++) { + cpcap_regacc_read(uc_data->cpcap, i, ®val); + dev_err(&uc_data->cpcap->spi->dev, + "cpcap reg %d = 0x%04X\n", i, regval); + } + + BUG(); +} + +static void primac_handler(enum cpcap_irqs irq, void *data) +{ + struct cpcap_uc_data *uc_data = data; + + if (irq == CPCAP_IRQ_PRIMAC) + cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_PRIMAC); +} + +static int ram_write(struct cpcap_uc_data *uc_data, unsigned short address, + unsigned short num_words, unsigned short *data) +{ + int retval = -EFAULT; + + mutex_lock(&uc_data->lock); + + if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) && + (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM, + CPCAP_BIT_UCTM, CPCAP_BIT_UCTM); + } + + if (uc_data->is_supported && (num_words > 0) && + (data != NULL) && + is_valid_address(uc_data->cpcap, address, num_words) && + !uc_data->uc_reset) { + uc_data->req.address = address; + uc_data->req.data = data; + uc_data->req.num_words = num_words; + uc_data->state = WRITE_STATE_1; + uc_data->state_cntr = 0; + INIT_COMPLETION(uc_data->completion); + + retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2, + CPCAP_BIT_PRIRAMW, + CPCAP_BIT_PRIRAMW); + if (retval) + goto err; + + /* Cannot call cpcap_irq_register() here because unregister + * cannot be called from the state machine. Doing so causes + * a deadlock. */ + retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW); + if (retval) + goto err; + + wait_for_completion(&uc_data->completion); + retval = uc_data->cb_status; + } + +err: + if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) && + (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM, + 0, CPCAP_BIT_UCTM); + } + + mutex_unlock(&uc_data->lock); + + return retval; +} + +static int ram_read(struct cpcap_uc_data *uc_data, unsigned short address, + unsigned short num_words, unsigned short *data) +{ + int retval = -EFAULT; + + mutex_lock(&uc_data->lock); + + if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) && + (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM, + CPCAP_BIT_UCTM, CPCAP_BIT_UCTM); + } + + if (uc_data->is_supported && (num_words > 0) && + is_valid_address(uc_data->cpcap, address, num_words) && + !uc_data->uc_reset) { + uc_data->req.address = address; + uc_data->req.data = data; + uc_data->req.num_words = num_words; + uc_data->state = READ_STATE_1; + uc_data->state_cntr = 0; + INIT_COMPLETION(uc_data->completion); + + retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2, + CPCAP_BIT_PRIRAMR, + CPCAP_BIT_PRIRAMR); + if (retval) + goto err; + + /* Cannot call cpcap_irq_register() here because unregister + * cannot be called from the state machine. Doing so causes + * a deadlock. */ + retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + if (retval) + goto err; + + wait_for_completion(&uc_data->completion); + retval = uc_data->cb_status; + } + +err: + if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) && + (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) { + cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM, + 0, CPCAP_BIT_UCTM); + } + + mutex_unlock(&uc_data->lock); + + return retval; +} + +static int ram_load(struct cpcap_uc_data *uc_data, unsigned int num_words, + unsigned short *data) +{ + int retval = -EINVAL; + + if ((data != NULL) && (num_words > 0)) + retval = ram_write(uc_data, data[0], (num_words - 1), + (data + 1)); + + return retval; +} + +static ssize_t fops_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + ssize_t retval = -EINVAL; + unsigned short address; + unsigned short num_words; + unsigned short *data; + struct cpcap_uc_data *uc_data = file->private_data; + + if ((buf != NULL) && (ppos != NULL) && (count >= 2)) { + data = kzalloc(count, GFP_KERNEL); + + if (data != NULL) { + num_words = (unsigned short) (count >> 1); + + /* If the position (uC RAM address) is zero then the + * data contains the address */ + if (*ppos == 0) { + if (copy_from_user((void *) data, (void *) buf, + count) == 0) + retval = ram_load(uc_data, num_words, + data); + else + retval = -EFAULT; + } + /* If the position (uC RAM address) is not zero then the + * position holds the address to load the data */ + else { + address = (unsigned short) (*ppos); + + if (copy_from_user((void *) data, (void *) buf, + count) == 0) + retval = ram_write(uc_data, address, + num_words, data); + else + retval = -EFAULT; + } + + kfree(data); + } else { + retval = -ENOMEM; + } + } + + if (retval == 0) + retval = num_words; + + return retval; +} + +static ssize_t fops_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + ssize_t retval = -EFAULT; + unsigned short address; + unsigned short num_words; + unsigned short *data; + struct cpcap_uc_data *uc_data = file->private_data; + + if ((buf != NULL) && (ppos != NULL) && (count >= 2)) { + data = kzalloc(count, GFP_KERNEL); + + if (data != NULL) { + address = (unsigned short) (*ppos); + num_words = (unsigned short) (count >> 1); + + retval = ram_read(uc_data, address, num_words, data); + + if (retval) + goto err; + + if (copy_to_user((void *)buf, (void *)data, count) == 0) + retval = count; + else + retval = -EFAULT; + +err: + kfree(data); + } else { + retval = -ENOMEM; + } + } + + return retval; +} + +static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int retval = -ENOTTY; + struct cpcap_uc_data *data = file->private_data; + + switch (cmd) { + case CPCAP_IOCTL_UC_MACRO_START: + /* User space will only attempt to start the init macro if + * the ram load requests complete successfully. This is used + * as an indication that kernel requests to start macros can + * be allowed. + */ + data->is_ready = 1; + + retval = cpcap_uc_start(data->cpcap, (enum cpcap_macro)arg); + + break; + + case CPCAP_IOCTL_UC_MACRO_STOP: + retval = cpcap_uc_stop(data->cpcap, (enum cpcap_macro)arg); + break; + + case CPCAP_IOCTL_UC_GET_VENDOR: + retval = copy_to_user((enum cpcap_vendor *)arg, + &(data->cpcap->vendor), + sizeof(enum cpcap_vendor)); + break; + + case CPCAP_IOCTL_UC_SET_TURBO_MODE: + if (arg != 0) + arg = 1; + retval = cpcap_regacc_write(data->cpcap, CPCAP_REG_UCTM, + (unsigned short)arg, + CPCAP_BIT_UCTM); + break; + + default: + break; + } + + return retval; +} + +static int fops_open(struct inode *inode, struct file *file) +{ + int retval = -ENOTTY; + + if (cpcap_uc_info->is_supported) + retval = 0; + + file->private_data = cpcap_uc_info; + dev_info(&cpcap_uc_info->cpcap->spi->dev, "CPCAP uC: open status:%d\n", + retval); + + return retval; +} + +int cpcap_uc_start(struct cpcap_device *cpcap, enum cpcap_macro macro) +{ + int retval = -EFAULT; + struct cpcap_uc_data *data = cpcap->ucdata; + + if ((data->is_ready) && + (macro > CPCAP_MACRO_USEROFF) && (macro < CPCAP_MACRO__END) && + (data->uc_reset == 0)) { + if ((macro == CPCAP_MACRO_4) || + ((cpcap->vendor == CPCAP_VENDOR_ST) && + (macro == CPCAP_MACRO_12)) || + ((cpcap->vendor == CPCAP_VENDOR_ST) && + (macro == CPCAP_MACRO_15))) { + retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2, + (1 << macro), + (1 << macro)); + } else { + retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1, + 0, (1 << macro)); + } + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_uc_start); + +int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro) +{ + int retval = -EFAULT; + + if ((macro > CPCAP_MACRO_4) && + (macro < CPCAP_MACRO__END)) { + if ((cpcap->vendor == CPCAP_VENDOR_ST) && + (macro == CPCAP_MACRO_12)) { + retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2, + 0, (1 << macro)); + } else { + retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1, + (1 << macro), (1 << macro)); + } + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_uc_stop); + +unsigned char cpcap_uc_status(struct cpcap_device *cpcap, + enum cpcap_macro macro) +{ + unsigned char retval = 0; + unsigned short regval; + + if (macro < CPCAP_MACRO__END) { + if ((macro <= CPCAP_MACRO_4) || + ((cpcap->vendor == CPCAP_VENDOR_ST) && + (macro == CPCAP_MACRO_12))) { + cpcap_regacc_read(cpcap, CPCAP_REG_MI2, ®val); + + if (regval & (1 << macro)) + retval = 1; + } else { + cpcap_regacc_read(cpcap, CPCAP_REG_MIM1, ®val); + + if (!(regval & (1 << macro))) + retval = 1; + } + } + + return retval; +} +EXPORT_SYMBOL_GPL(cpcap_uc_status); + +#ifdef CONFIG_PM_DBG_DRV +int cpcap_uc_ram_write(struct cpcap_device *cpcap, unsigned short address, + unsigned short num_words, unsigned short *data) +{ + return ram_write(cpcap->ucdata, address, num_words, data); +} + +int cpcap_uc_ram_read(struct cpcap_device *cpcap, unsigned short address, + unsigned short num_words, unsigned short *data) +{ + return ram_read(cpcap->ucdata, address, num_words, data); +} +#endif /* CONFIG_PM_DBG_DRV */ + +static int fw_load(struct cpcap_uc_data *uc_data, struct device *dev) +{ + int err; + const struct ihex_binrec *rec; + const struct firmware *fw; + unsigned short *buf; + int i; + unsigned short num_bytes; + unsigned short num_words; + unsigned char odd_bytes; + struct cpcap_platform_data *data; + + data = uc_data->cpcap->spi->controller_data; + + if (!uc_data || !dev) + return -EINVAL; + + if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST) + err = request_ihex_firmware(&fw, "cpcap/firmware_0_2x.fw", dev); + else + err = request_ihex_firmware(&fw, "cpcap/firmware_1_2x.fw", dev); + + if (err) { + dev_err(dev, "Failed to load \"cpcap/firmware_%d_2x.fw\": %d\n", + uc_data->cpcap->vendor, err); + goto err; + } + + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + odd_bytes = 0; + num_bytes = be16_to_cpu(rec->len); + + /* Since loader requires words, need even number of bytes. */ + if (be16_to_cpu(rec->len) % 2) { + num_bytes++; + odd_bytes = 1; + } + + num_words = num_bytes >> 1; + dev_dbg(dev, "Loading %d word(s) at 0x%04x\n", + num_words, be32_to_cpu(rec->addr)); + + buf = kzalloc(num_bytes, GFP_KERNEL); + if (buf) { + for (i = 0; i < num_words; i++) { + if (odd_bytes && (i == (num_words - 1))) + buf[i] = rec->data[i * 2]; + else + buf[i] = ((uint16_t *)rec->data)[i]; + + buf[i] = be16_to_cpu(buf[i]); + } + + err = ram_write(uc_data, be32_to_cpu(rec->addr), + num_words, buf); + kfree(buf); + + if (err) { + dev_err(dev, "RAM write failed: %d\n", err); + break; + } + } else { + err = -ENOMEM; + dev_err(dev, "RAM write failed: %d\n", err); + break; + } + } + + release_firmware(fw); + + if (!err) { + uc_data->is_ready = 1; + + if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST) + err = ram_write(uc_data, 0x012C, 1, &(data->is_umts)); + else + err = ram_write(uc_data, 0x90F0, 1, &(data->is_umts)); + + dev_info(dev, "Loaded Sec SPI Init = %d: %d\n", + data->is_umts, err); + + if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST) + err = ram_write(uc_data, HWCFG_ADDR_ST, + CPCAP_HWCFG_NUM, data->hwcfg); + else + err = ram_write(uc_data, HWCFG_ADDR_TI, + CPCAP_HWCFG_NUM, data->hwcfg); + + dev_info(dev, "Loaded HWCFG data:"); + for (i = 0; i < CPCAP_HWCFG_NUM; i++) + dev_info(dev, " 0x%04x", data->hwcfg[i]); + dev_info(dev, "result: %d\n", err); + + err = cpcap_uc_start(uc_data->cpcap, CPCAP_MACRO_4); + dev_info(dev, "Started macro 4: %d\n", err); + + err = cpcap_uc_start(uc_data->cpcap, CPCAP_MACRO_15); + dev_info(dev, "Started macro 15: %d\n", err); + } + +err: + return err; +} + +static int cpcap_uc_probe(struct platform_device *pdev) +{ + int retval = 0; + struct cpcap_uc_data *data; + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->cpcap = pdev->dev.platform_data; + data->uc_reset = 0; + data->is_supported = 0; + data->req.address = 0; + data->req.data = NULL; + data->req.num_words = 0; + + init_completion(&data->completion); + mutex_init(&data->lock); + platform_set_drvdata(pdev, data); + cpcap_uc_info = data; + data->cpcap->ucdata = data; + + if (((data->cpcap->vendor == CPCAP_VENDOR_TI) && + (data->cpcap->revision >= CPCAP_REVISION_2_0)) || + (data->cpcap->vendor == CPCAP_VENDOR_ST)) { + retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_PRIMAC, + primac_handler, data); + if (retval) + goto err_free; + + cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UCRESET); + retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UCRESET, + reset_handler, data); + if (retval) + goto err_primac; + + retval = cpcap_irq_register(data->cpcap, + CPCAP_IRQ_UC_PRIRAMR, + ram_read_state_machine, data); + if (retval) + goto err_ucreset; + + retval = cpcap_irq_register(data->cpcap, + CPCAP_IRQ_UC_PRIRAMW, + ram_write_state_machine, data); + if (retval) + goto err_priramr; + + retval = misc_register(&uc_dev); + if (retval) + goto err_priramw; + + data->is_supported = 1; + + cpcap_regacc_write(data->cpcap, CPCAP_REG_MIM1, 0xFFFF, + 0xFFFF); + + retval = fw_load(data, &pdev->dev); + if (retval) + goto err_fw; + } else + retval = -ENODEV; + + return retval; + +err_fw: + misc_deregister(&uc_dev); +err_priramw: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW); +err_priramr: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR); +err_ucreset: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET); +err_primac: + cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC); +err_free: + kfree(data); + + return retval; +} + +static int __exit cpcap_uc_remove(struct platform_device *pdev) +{ + struct cpcap_uc_data *data = platform_get_drvdata(pdev); + + misc_deregister(&uc_dev); + + cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR); + cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET); + + kfree(data); + return 0; +} + + +static struct platform_driver cpcap_uc_driver = { + .probe = cpcap_uc_probe, + .remove = __exit_p(cpcap_uc_remove), + .driver = { + .name = "cpcap_uc", + .owner = THIS_MODULE, + }, +}; + +static int __init cpcap_uc_init(void) +{ + return platform_driver_register(&cpcap_uc_driver); +} +subsys_initcall(cpcap_uc_init); + +static void __exit cpcap_uc_exit(void) +{ + platform_driver_unregister(&cpcap_uc_driver); +} +module_exit(cpcap_uc_exit); + +MODULE_ALIAS("platform:cpcap_uc"); +MODULE_DESCRIPTION("CPCAP uC driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("cpcap/firmware_0_2x.fw"); +MODULE_FIRMWARE("cpcap/firmware_1_2x.fw"); |