diff options
Diffstat (limited to 'drivers')
345 files changed, 17663 insertions, 3577 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 7025593a58c8..d74926e0939e 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -603,6 +603,10 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) if (ret) goto err_out; + /* Hard-coded primecell ID instead of plug-n-play */ + if (dev->periphid != 0) + goto skip_probe; + /* * Dynamically calculate the size of the resource * and use this for iomap @@ -643,6 +647,7 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) if (ret) goto err_release; + skip_probe: ret = device_add(&dev->dev); if (ret) goto err_release; diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index ffd8797faf4f..471a04013fe0 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -6,6 +6,7 @@ */ #include "bcma_private.h" +#include <linux/slab.h> #include <linux/bcma/bcma.h> #include <linux/pci.h> diff --git a/drivers/block/brd.c b/drivers/block/brd.c index b7f51e4594f8..dba1c32e1ddf 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -35,10 +35,6 @@ */ struct brd_device { int brd_number; - int brd_refcnt; - loff_t brd_offset; - loff_t brd_sizelimit; - unsigned brd_blocksize; struct request_queue *brd_queue; struct gendisk *brd_disk; @@ -440,11 +436,11 @@ static int rd_nr; int rd_size = CONFIG_BLK_DEV_RAM_SIZE; static int max_part; static int part_shift; -module_param(rd_nr, int, 0); +module_param(rd_nr, int, S_IRUGO); MODULE_PARM_DESC(rd_nr, "Maximum number of brd devices"); -module_param(rd_size, int, 0); +module_param(rd_size, int, S_IRUGO); MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes."); -module_param(max_part, int, 0); +module_param(max_part, int, S_IRUGO); MODULE_PARM_DESC(max_part, "Maximum number of partitions per RAM disk"); MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR); @@ -552,7 +548,7 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data) struct kobject *kobj; mutex_lock(&brd_devices_mutex); - brd = brd_init_one(dev & MINORMASK); + brd = brd_init_one(MINOR(dev) >> part_shift); kobj = brd ? get_disk(brd->brd_disk) : ERR_PTR(-ENOMEM); mutex_unlock(&brd_devices_mutex); @@ -575,25 +571,39 @@ static int __init brd_init(void) * * (1) if rd_nr is specified, create that many upfront, and this * also becomes a hard limit. - * (2) if rd_nr is not specified, create 1 rd device on module - * load, user can further extend brd device by create dev node - * themselves and have kernel automatically instantiate actual - * device on-demand. + * (2) if rd_nr is not specified, create CONFIG_BLK_DEV_RAM_COUNT + * (default 16) rd device on module load, user can further + * extend brd device by create dev node themselves and have + * kernel automatically instantiate actual device on-demand. */ part_shift = 0; - if (max_part > 0) + if (max_part > 0) { part_shift = fls(max_part); + /* + * Adjust max_part according to part_shift as it is exported + * to user space so that user can decide correct minor number + * if [s]he want to create more devices. + * + * Note that -1 is required because partition 0 is reserved + * for the whole disk. + */ + max_part = (1UL << part_shift) - 1; + } + + if ((1UL << part_shift) > DISK_MAX_PARTS) + return -EINVAL; + if (rd_nr > 1UL << (MINORBITS - part_shift)) return -EINVAL; if (rd_nr) { nr = rd_nr; - range = rd_nr; + range = rd_nr << part_shift; } else { nr = CONFIG_BLK_DEV_RAM_COUNT; - range = 1UL << (MINORBITS - part_shift); + range = 1UL << MINORBITS; } if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) @@ -632,7 +642,7 @@ static void __exit brd_exit(void) unsigned long range; struct brd_device *brd, *next; - range = rd_nr ? rd_nr : 1UL << (MINORBITS - part_shift); + range = rd_nr ? rd_nr << part_shift : 1UL << MINORBITS; list_for_each_entry_safe(brd, next, &brd_devices, brd_list) brd_del_one(brd); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index c59a672a3de0..76c8da78212b 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1540,9 +1540,9 @@ static const struct block_device_operations lo_fops = { * And now the modules code and kernel interface. */ static int max_loop; -module_param(max_loop, int, 0); +module_param(max_loop, int, S_IRUGO); MODULE_PARM_DESC(max_loop, "Maximum number of loop devices"); -module_param(max_part, int, 0); +module_param(max_part, int, S_IRUGO); MODULE_PARM_DESC(max_part, "Maximum number of partitions per loop device"); MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(LOOP_MAJOR); @@ -1688,9 +1688,20 @@ static int __init loop_init(void) */ part_shift = 0; - if (max_part > 0) + if (max_part > 0) { part_shift = fls(max_part); + /* + * Adjust max_part according to part_shift as it is exported + * to user space so that user can decide correct minor number + * if [s]he want to create more devices. + * + * Note that -1 is required because partition 0 is reserved + * for the whole disk. + */ + max_part = (1UL << part_shift) - 1; + } + if ((1UL << part_shift) > DISK_MAX_PARTS) return -EINVAL; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 38223e93aa98..58c0e6387cf7 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -36,6 +36,7 @@ #include <asm/system.h> #include <linux/poll.h> #include <linux/sched.h> +#include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -1896,102 +1897,128 @@ int ipmi_request_supply_msgs(ipmi_user_t user, EXPORT_SYMBOL(ipmi_request_supply_msgs); #ifdef CONFIG_PROC_FS -static int ipmb_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_ipmb_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - ipmi_smi_t intf = data; + ipmi_smi_t intf = m->private; int i; - int rv = 0; - for (i = 0; i < IPMI_MAX_CHANNELS; i++) - rv += sprintf(out+rv, "%x ", intf->channels[i].address); - out[rv-1] = '\n'; /* Replace the final space with a newline */ - out[rv] = '\0'; - rv++; - return rv; + seq_printf(m, "%x", intf->channels[0].address); + for (i = 1; i < IPMI_MAX_CHANNELS; i++) + seq_printf(m, " %x", intf->channels[i].address); + return seq_putc(m, '\n'); } -static int version_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_ipmb_proc_open(struct inode *inode, struct file *file) { - char *out = (char *) page; - ipmi_smi_t intf = data; + return single_open(file, smi_ipmb_proc_show, PDE(inode)->data); +} - return sprintf(out, "%u.%u\n", +static const struct file_operations smi_ipmb_proc_ops = { + .open = smi_ipmb_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_version_proc_show(struct seq_file *m, void *v) +{ + ipmi_smi_t intf = m->private; + + return seq_printf(m, "%u.%u\n", ipmi_version_major(&intf->bmc->id), ipmi_version_minor(&intf->bmc->id)); } -static int stat_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_version_proc_open(struct inode *inode, struct file *file) { - char *out = (char *) page; - ipmi_smi_t intf = data; + return single_open(file, smi_version_proc_show, PDE(inode)->data); +} + +static const struct file_operations smi_version_proc_ops = { + .open = smi_version_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - out += sprintf(out, "sent_invalid_commands: %u\n", +static int smi_stats_proc_show(struct seq_file *m, void *v) +{ + ipmi_smi_t intf = m->private; + + seq_printf(m, "sent_invalid_commands: %u\n", ipmi_get_stat(intf, sent_invalid_commands)); - out += sprintf(out, "sent_local_commands: %u\n", + seq_printf(m, "sent_local_commands: %u\n", ipmi_get_stat(intf, sent_local_commands)); - out += sprintf(out, "handled_local_responses: %u\n", + seq_printf(m, "handled_local_responses: %u\n", ipmi_get_stat(intf, handled_local_responses)); - out += sprintf(out, "unhandled_local_responses: %u\n", + seq_printf(m, "unhandled_local_responses: %u\n", ipmi_get_stat(intf, unhandled_local_responses)); - out += sprintf(out, "sent_ipmb_commands: %u\n", + seq_printf(m, "sent_ipmb_commands: %u\n", ipmi_get_stat(intf, sent_ipmb_commands)); - out += sprintf(out, "sent_ipmb_command_errs: %u\n", + seq_printf(m, "sent_ipmb_command_errs: %u\n", ipmi_get_stat(intf, sent_ipmb_command_errs)); - out += sprintf(out, "retransmitted_ipmb_commands: %u\n", + seq_printf(m, "retransmitted_ipmb_commands: %u\n", ipmi_get_stat(intf, retransmitted_ipmb_commands)); - out += sprintf(out, "timed_out_ipmb_commands: %u\n", + seq_printf(m, "timed_out_ipmb_commands: %u\n", ipmi_get_stat(intf, timed_out_ipmb_commands)); - out += sprintf(out, "timed_out_ipmb_broadcasts: %u\n", + seq_printf(m, "timed_out_ipmb_broadcasts: %u\n", ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); - out += sprintf(out, "sent_ipmb_responses: %u\n", + seq_printf(m, "sent_ipmb_responses: %u\n", ipmi_get_stat(intf, sent_ipmb_responses)); - out += sprintf(out, "handled_ipmb_responses: %u\n", + seq_printf(m, "handled_ipmb_responses: %u\n", ipmi_get_stat(intf, handled_ipmb_responses)); - out += sprintf(out, "invalid_ipmb_responses: %u\n", + seq_printf(m, "invalid_ipmb_responses: %u\n", ipmi_get_stat(intf, invalid_ipmb_responses)); - out += sprintf(out, "unhandled_ipmb_responses: %u\n", + seq_printf(m, "unhandled_ipmb_responses: %u\n", ipmi_get_stat(intf, unhandled_ipmb_responses)); - out += sprintf(out, "sent_lan_commands: %u\n", + seq_printf(m, "sent_lan_commands: %u\n", ipmi_get_stat(intf, sent_lan_commands)); - out += sprintf(out, "sent_lan_command_errs: %u\n", + seq_printf(m, "sent_lan_command_errs: %u\n", ipmi_get_stat(intf, sent_lan_command_errs)); - out += sprintf(out, "retransmitted_lan_commands: %u\n", + seq_printf(m, "retransmitted_lan_commands: %u\n", ipmi_get_stat(intf, retransmitted_lan_commands)); - out += sprintf(out, "timed_out_lan_commands: %u\n", + seq_printf(m, "timed_out_lan_commands: %u\n", ipmi_get_stat(intf, timed_out_lan_commands)); - out += sprintf(out, "sent_lan_responses: %u\n", + seq_printf(m, "sent_lan_responses: %u\n", ipmi_get_stat(intf, sent_lan_responses)); - out += sprintf(out, "handled_lan_responses: %u\n", + seq_printf(m, "handled_lan_responses: %u\n", ipmi_get_stat(intf, handled_lan_responses)); - out += sprintf(out, "invalid_lan_responses: %u\n", + seq_printf(m, "invalid_lan_responses: %u\n", ipmi_get_stat(intf, invalid_lan_responses)); - out += sprintf(out, "unhandled_lan_responses: %u\n", + seq_printf(m, "unhandled_lan_responses: %u\n", ipmi_get_stat(intf, unhandled_lan_responses)); - out += sprintf(out, "handled_commands: %u\n", + seq_printf(m, "handled_commands: %u\n", ipmi_get_stat(intf, handled_commands)); - out += sprintf(out, "invalid_commands: %u\n", + seq_printf(m, "invalid_commands: %u\n", ipmi_get_stat(intf, invalid_commands)); - out += sprintf(out, "unhandled_commands: %u\n", + seq_printf(m, "unhandled_commands: %u\n", ipmi_get_stat(intf, unhandled_commands)); - out += sprintf(out, "invalid_events: %u\n", + seq_printf(m, "invalid_events: %u\n", ipmi_get_stat(intf, invalid_events)); - out += sprintf(out, "events: %u\n", + seq_printf(m, "events: %u\n", ipmi_get_stat(intf, events)); - out += sprintf(out, "failed rexmit LAN msgs: %u\n", + seq_printf(m, "failed rexmit LAN msgs: %u\n", ipmi_get_stat(intf, dropped_rexmit_lan_commands)); - out += sprintf(out, "failed rexmit IPMB msgs: %u\n", + seq_printf(m, "failed rexmit IPMB msgs: %u\n", ipmi_get_stat(intf, dropped_rexmit_ipmb_commands)); + return 0; +} - return (out - ((char *) page)); +static int smi_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_stats_proc_show, PDE(inode)->data); } + +static const struct file_operations smi_stats_proc_ops = { + .open = smi_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, - read_proc_t *read_proc, + const struct file_operations *proc_ops, void *data) { int rv = 0; @@ -2010,15 +2037,12 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, } strcpy(entry->name, name); - file = create_proc_entry(name, 0, smi->proc_dir); + file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data); if (!file) { kfree(entry->name); kfree(entry); rv = -ENOMEM; } else { - file->data = data; - file->read_proc = read_proc; - mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; @@ -2043,17 +2067,17 @@ static int add_proc_entries(ipmi_smi_t smi, int num) if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", - stat_file_read_proc, + &smi_stats_proc_ops, smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", - ipmb_file_read_proc, + &smi_ipmb_proc_ops, smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", - version_file_read_proc, + &smi_version_proc_ops, smi); #endif /* CONFIG_PROC_FS */ diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 64c6b8530615..9397ab49b72e 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -43,6 +43,7 @@ #include <linux/moduleparam.h> #include <asm/system.h> #include <linux/sched.h> +#include <linux/seq_file.h> #include <linux/timer.h> #include <linux/errno.h> #include <linux/spinlock.h> @@ -2805,54 +2806,73 @@ static int try_enable_event_buffer(struct smi_info *smi_info) return rv; } -static int type_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_type_proc_show(struct seq_file *m, void *v) { - struct smi_info *smi = data; + struct smi_info *smi = m->private; - return sprintf(page, "%s\n", si_to_str[smi->si_type]); + return seq_printf(m, "%s\n", si_to_str[smi->si_type]); } -static int stat_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_type_proc_open(struct inode *inode, struct file *file) { - char *out = (char *) page; - struct smi_info *smi = data; + return single_open(file, smi_type_proc_show, PDE(inode)->data); +} + +static const struct file_operations smi_type_proc_ops = { + .open = smi_type_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_si_stats_proc_show(struct seq_file *m, void *v) +{ + struct smi_info *smi = m->private; - out += sprintf(out, "interrupts_enabled: %d\n", + seq_printf(m, "interrupts_enabled: %d\n", smi->irq && !smi->interrupt_disabled); - out += sprintf(out, "short_timeouts: %u\n", + seq_printf(m, "short_timeouts: %u\n", smi_get_stat(smi, short_timeouts)); - out += sprintf(out, "long_timeouts: %u\n", + seq_printf(m, "long_timeouts: %u\n", smi_get_stat(smi, long_timeouts)); - out += sprintf(out, "idles: %u\n", + seq_printf(m, "idles: %u\n", smi_get_stat(smi, idles)); - out += sprintf(out, "interrupts: %u\n", + seq_printf(m, "interrupts: %u\n", smi_get_stat(smi, interrupts)); - out += sprintf(out, "attentions: %u\n", + seq_printf(m, "attentions: %u\n", smi_get_stat(smi, attentions)); - out += sprintf(out, "flag_fetches: %u\n", + seq_printf(m, "flag_fetches: %u\n", smi_get_stat(smi, flag_fetches)); - out += sprintf(out, "hosed_count: %u\n", + seq_printf(m, "hosed_count: %u\n", smi_get_stat(smi, hosed_count)); - out += sprintf(out, "complete_transactions: %u\n", + seq_printf(m, "complete_transactions: %u\n", smi_get_stat(smi, complete_transactions)); - out += sprintf(out, "events: %u\n", + seq_printf(m, "events: %u\n", smi_get_stat(smi, events)); - out += sprintf(out, "watchdog_pretimeouts: %u\n", + seq_printf(m, "watchdog_pretimeouts: %u\n", smi_get_stat(smi, watchdog_pretimeouts)); - out += sprintf(out, "incoming_messages: %u\n", + seq_printf(m, "incoming_messages: %u\n", smi_get_stat(smi, incoming_messages)); + return 0; +} - return out - page; +static int smi_si_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_si_stats_proc_show, PDE(inode)->data); } -static int param_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations smi_si_stats_proc_ops = { + .open = smi_si_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_params_proc_show(struct seq_file *m, void *v) { - struct smi_info *smi = data; + struct smi_info *smi = m->private; - return sprintf(page, + return seq_printf(m, "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", si_to_str[smi->si_type], addr_space_to_str[smi->io.addr_type], @@ -2864,6 +2884,18 @@ static int param_read_proc(char *page, char **start, off_t off, smi->slave_addr); } +static int smi_params_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_params_proc_show, PDE(inode)->data); +} + +static const struct file_operations smi_params_proc_ops = { + .open = smi_params_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * oem_data_avail_to_receive_msg_avail * @info - smi_info structure with msg_flags set @@ -3257,7 +3289,7 @@ static int try_smi_init(struct smi_info *new_smi) } rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", - type_file_read_proc, + &smi_type_proc_ops, new_smi); if (rv) { dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); @@ -3265,7 +3297,7 @@ static int try_smi_init(struct smi_info *new_smi) } rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", - stat_file_read_proc, + &smi_si_stats_proc_ops, new_smi); if (rv) { dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); @@ -3273,7 +3305,7 @@ static int try_smi_init(struct smi_info *new_smi) } rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", - param_read_proc, + &smi_params_proc_ops, new_smi); if (rv) { dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 966a95bc974b..25d139c9dbed 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -271,14 +271,13 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; vdata_size = sizeof(struct vma_data) + pages * sizeof(long); if (vdata_size <= PAGE_SIZE) - vdata = kmalloc(vdata_size, GFP_KERNEL); + vdata = kzalloc(vdata_size, GFP_KERNEL); else { - vdata = vmalloc(vdata_size); + vdata = vzalloc(vdata_size); flags = VMD_VMALLOCED; } if (!vdata) return -ENOMEM; - memset(vdata, 0, vdata_size); vdata->vm_start = vma->vm_start; vdata->vm_end = vma->vm_end; diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index f176dbaeb15a..3fcf80ff12f2 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -457,6 +457,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -ENODEV; modes = port->modes; + parport_put_port(port); if (copy_to_user (argp, &modes, sizeof (modes))) { return -EFAULT; } diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index cace0a7b707a..e47e73bbbcc5 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -19,7 +19,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define AMD76X_REVISION " Ver: 2.0.2 " __DATE__ +#define AMD76X_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "amd76x_edac" #define amd76x_printk(level, fmt, arg...) \ diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c index 35b78d04bbfa..ddd890052ce2 100644 --- a/drivers/edac/amd8111_edac.c +++ b/drivers/edac/amd8111_edac.c @@ -33,7 +33,7 @@ #include "edac_module.h" #include "amd8111_edac.h" -#define AMD8111_EDAC_REVISION " Ver: 1.0.0 " __DATE__ +#define AMD8111_EDAC_REVISION " Ver: 1.0.0" #define AMD8111_EDAC_MOD_STR "amd8111_edac" #define PCI_DEVICE_ID_AMD_8111_PCI 0x7460 diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c index b432d60c622a..a5c680561c73 100644 --- a/drivers/edac/amd8131_edac.c +++ b/drivers/edac/amd8131_edac.c @@ -33,7 +33,7 @@ #include "edac_module.h" #include "amd8131_edac.h" -#define AMD8131_EDAC_REVISION " Ver: 1.0.0 " __DATE__ +#define AMD8131_EDAC_REVISION " Ver: 1.0.0" #define AMD8131_EDAC_MOD_STR "amd8131_edac" /* Wrapper functions for accessing PCI configuration space */ diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index 837ad8f85b48..a687a0d16962 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -30,7 +30,7 @@ #include "edac_core.h" #include "edac_module.h" -#define CPC925_EDAC_REVISION " Ver: 1.0.0 " __DATE__ +#define CPC925_EDAC_REVISION " Ver: 1.0.0" #define CPC925_EDAC_MOD_STR "cpc925_edac" #define cpc925_printk(level, fmt, arg...) \ diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index ec302d426589..1af531a11d21 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -24,7 +24,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define E752X_REVISION " Ver: 2.0.2 " __DATE__ +#define E752X_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "e752x_edac" static int report_non_memory_errors; diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 1731d7245816..6ffb6d23281f 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -29,7 +29,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define E7XXX_REVISION " Ver: 2.0.2 " __DATE__ +#define E7XXX_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "e7xxx_edac" #define e7xxx_printk(level, fmt, arg...) \ diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index eefa3501916b..55b8278bb172 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -421,10 +421,6 @@ struct mem_ctl_info { u32 ce_count; /* Total Correctable Errors for this MC */ unsigned long start_time; /* mci load start time (in jiffies) */ - /* this stuff is for safe removal of mc devices from global list while - * NMI handlers may be traversing list - */ - struct rcu_head rcu; struct completion complete; /* edac sysfs device control */ @@ -620,10 +616,6 @@ struct edac_device_ctl_info { unsigned long start_time; /* edac_device load start time (jiffies) */ - /* these are for safe removal of mc devices from global list while - * NMI handlers may be traversing list - */ - struct rcu_head rcu; struct completion removal_complete; /* sysfs top name under 'edac' directory @@ -722,10 +714,6 @@ struct edac_pci_ctl_info { unsigned long start_time; /* edac_pci load start time (jiffies) */ - /* these are for safe removal of devices from global list while - * NMI handlers may be traversing list - */ - struct rcu_head rcu; struct completion complete; /* sysfs top name under 'edac' directory diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index a7408cf86f37..c3f67437afb6 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -346,30 +346,18 @@ fail1: } /* - * complete_edac_device_list_del - * - * callback function when reference count is zero - */ -static void complete_edac_device_list_del(struct rcu_head *head) -{ - struct edac_device_ctl_info *edac_dev; - - edac_dev = container_of(head, struct edac_device_ctl_info, rcu); - INIT_LIST_HEAD(&edac_dev->link); -} - -/* * del_edac_device_from_global_list - * - * remove the RCU, setup for a callback call, - * then wait for the callback to occur */ static void del_edac_device_from_global_list(struct edac_device_ctl_info *edac_device) { list_del_rcu(&edac_device->link); - call_rcu(&edac_device->rcu, complete_edac_device_list_del); - rcu_barrier(); + + /* these are for safe removal of devices from global list while + * NMI handlers may be traversing list + */ + synchronize_rcu(); + INIT_LIST_HEAD(&edac_device->link); } /* diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 1d8056049072..d69144a09043 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -447,20 +447,16 @@ fail1: return 1; } -static void complete_mc_list_del(struct rcu_head *head) -{ - struct mem_ctl_info *mci; - - mci = container_of(head, struct mem_ctl_info, rcu); - INIT_LIST_HEAD(&mci->link); -} - static void del_mc_from_global_list(struct mem_ctl_info *mci) { atomic_dec(&edac_handlers); list_del_rcu(&mci->link); - call_rcu(&mci->rcu, complete_mc_list_del); - rcu_barrier(); + + /* these are for safe removal of devices from global list while + * NMI handlers may be traversing list + */ + synchronize_rcu(); + INIT_LIST_HEAD(&mci->link); } /** diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index be4b075c3098..5ddaa86d6a6e 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -15,7 +15,7 @@ #include "edac_core.h" #include "edac_module.h" -#define EDAC_VERSION "Ver: 2.1.0 " __DATE__ +#define EDAC_VERSION "Ver: 2.1.0" #ifdef CONFIG_EDAC_DEBUG /* Values of 0 to 4 will generate output */ diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index efb5d5650783..2b378207d571 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -164,19 +164,6 @@ fail1: } /* - * complete_edac_pci_list_del - * - * RCU completion callback to indicate item is deleted - */ -static void complete_edac_pci_list_del(struct rcu_head *head) -{ - struct edac_pci_ctl_info *pci; - - pci = container_of(head, struct edac_pci_ctl_info, rcu); - INIT_LIST_HEAD(&pci->link); -} - -/* * del_edac_pci_from_global_list * * remove the PCI control struct from the global list @@ -184,8 +171,12 @@ static void complete_edac_pci_list_del(struct rcu_head *head) static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) { list_del_rcu(&pci->link); - call_rcu(&pci->rcu, complete_edac_pci_list_del); - rcu_barrier(); + + /* these are for safe removal of devices from global list while + * NMI handlers may be traversing list + */ + synchronize_rcu(); + INIT_LIST_HEAD(&pci->link); } #if 0 diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 87f427c2ce5c..4dc3ac25a422 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -27,7 +27,7 @@ /* * Alter this version for the I5000 module when modifications are made */ -#define I5000_REVISION " Ver: 2.0.12 " __DATE__ +#define I5000_REVISION " Ver: 2.0.12" #define EDAC_MOD_STR "i5000_edac" #define i5000_printk(level, fmt, arg...) \ diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 80a465efbae8..74d6ec342afb 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -33,7 +33,7 @@ /* * Alter this version for the I5400 module when modifications are made */ -#define I5400_REVISION " Ver: 1.0.0 " __DATE__ +#define I5400_REVISION " Ver: 1.0.0" #define EDAC_MOD_STR "i5400_edac" diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 363cc1602944..a76fe8366b68 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -31,7 +31,7 @@ /* * Alter this version for the I7300 module when modifications are made */ -#define I7300_REVISION " Ver: 1.0.0 " __DATE__ +#define I7300_REVISION " Ver: 1.0.0" #define EDAC_MOD_STR "i7300_edac" diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 465cbc25149f..04f1e7ce02b1 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -59,7 +59,7 @@ MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices"); /* * Alter this version for the module when modifications are made */ -#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__ +#define I7CORE_REVISION " Ver: 1.0.0" #define EDAC_MOD_STR "i7core_edac" /* diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index b8a95cf50718..931a05775049 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -16,7 +16,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define I82860_REVISION " Ver: 2.0.2 " __DATE__ +#define I82860_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "i82860_edac" #define i82860_printk(level, fmt, arg...) \ diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index b2fd1e899142..33864c63c684 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -20,7 +20,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define I82875P_REVISION " Ver: 2.0.2 " __DATE__ +#define I82875P_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "i82875p_edac" #define i82875p_printk(level, fmt, arg...) \ diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 92e65e7038e9..a5da732fe5b2 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -16,7 +16,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define I82975X_REVISION " Ver: 1.0.0 " __DATE__ +#define I82975X_REVISION " Ver: 1.0.0" #define EDAC_MOD_STR "i82975x_edac" #define i82975x_printk(level, fmt, arg...) \ diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index cb24df839460..932016f2cf06 100644 --- a/drivers/edac/mpc85xx_edac.h +++ b/drivers/edac/mpc85xx_edac.h @@ -11,7 +11,7 @@ #ifndef _MPC85XX_EDAC_H_ #define _MPC85XX_EDAC_H_ -#define MPC85XX_REVISION " Ver: 2.0.0 " __DATE__ +#define MPC85XX_REVISION " Ver: 2.0.0" #define EDAC_MOD_STR "MPC85xx_edac" #define mpc85xx_printk(level, fmt, arg...) \ diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h index e042e2daa8f4..c7f209c92a1a 100644 --- a/drivers/edac/mv64x60_edac.h +++ b/drivers/edac/mv64x60_edac.h @@ -12,7 +12,7 @@ #ifndef _MV64X60_EDAC_H_ #define _MV64X60_EDAC_H_ -#define MV64x60_REVISION " Ver: 2.0.0 " __DATE__ +#define MV64x60_REVISION " Ver: 2.0.0" #define EDAC_MOD_STR "MV64x60_edac" #define mv64x60_printk(level, fmt, arg...) \ diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index af8e7b1aa290..0de7d8770891 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -113,7 +113,7 @@ #define EDAC_OPSTATE_UNKNOWN_STR "unknown" #define PPC4XX_EDAC_MODULE_NAME "ppc4xx_edac" -#define PPC4XX_EDAC_MODULE_REVISION "v1.0.0 " __DATE__ +#define PPC4XX_EDAC_MODULE_REVISION "v1.0.0" #define PPC4XX_EDAC_MESSAGE_SIZE 256 diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index 678513738c33..b153674431f1 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -22,7 +22,7 @@ #include <linux/edac.h> #include "edac_core.h" -#define R82600_REVISION " Ver: 2.0.2 " __DATE__ +#define R82600_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "r82600_edac" #define r82600_printk(level, fmt, arg...) \ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 978852afc9dc..592397629ddc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -454,4 +454,11 @@ config AB8500_GPIO depends on AB8500_CORE && BROKEN help Select this to enable the AB8500 IC GPIO driver + +config GPIO_TPS65910 + bool "TPS65910 GPIO" + depends on MFD_TPS65910 + help + Select this option to enable GPIO driver for the TPS65910 + chip family. endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4182040a3522..b605f8ec6fbe 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_GPIO_SX150X) += sx150x.o obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o +obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o diff --git a/drivers/gpio/tps65910-gpio.c b/drivers/gpio/tps65910-gpio.c new file mode 100644 index 000000000000..8d1ddfdd63eb --- /dev/null +++ b/drivers/gpio/tps65910-gpio.c @@ -0,0 +1,100 @@ +/* + * tps65910-gpio.c -- TI TPS6591x + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * Author: Jorge Eduardo Candelaria jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/mfd/tps65910.h> + +static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + uint8_t val; + + tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val); + + if (val & GPIO_STS_MASK) + return 1; + + return 0; +} + +static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + if (value) + tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); + else + tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); +} + +static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + /* Set the initial value */ + tps65910_gpio_set(gc, 0, value); + + return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_CFG_MASK); +} + +static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); + + return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset, + GPIO_CFG_MASK); +} + +void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base) +{ + int ret; + + if (!gpio_base) + return; + + tps65910->gpio.owner = THIS_MODULE; + tps65910->gpio.label = tps65910->i2c_client->name; + tps65910->gpio.dev = tps65910->dev; + tps65910->gpio.base = gpio_base; + + switch(tps65910_chip_id(tps65910)) { + case TPS65910: + tps65910->gpio.ngpio = 6; + case TPS65911: + tps65910->gpio.ngpio = 9; + default: + return; + } + tps65910->gpio.can_sleep = 1; + + tps65910->gpio.direction_input = tps65910_gpio_input; + tps65910->gpio.direction_output = tps65910_gpio_output; + tps65910->gpio.set = tps65910_gpio_set; + tps65910->gpio.get = tps65910_gpio_get; + + ret = gpiochip_add(&tps65910->gpio); + + if (ret) + dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret); +} diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 9577c432e77f..de3d2465fe24 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -350,6 +350,7 @@ static int get_pkg_tjmax(unsigned int cpu, struct device *dev) static int create_name_attr(struct platform_data *pdata, struct device *dev) { + sysfs_attr_init(&pdata->name_attr.attr); pdata->name_attr.attr.name = "name"; pdata->name_attr.attr.mode = S_IRUGO; pdata->name_attr.show = show_name; @@ -372,6 +373,7 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, for (i = 0; i < MAX_ATTRS; i++) { snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], attr_no); + sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; @@ -422,7 +424,7 @@ static void update_ttarget(__u8 cpu_model, struct temp_data *tdata, } } -static int chk_ucode_version(struct platform_device *pdev) +static int __devinit chk_ucode_version(struct platform_device *pdev) { struct cpuinfo_x86 *c = &cpu_data(pdev->id); int err; @@ -509,8 +511,8 @@ static int create_core_data(struct platform_data *pdata, /* * Provide a single set of attributes for all HT siblings of a core * to avoid duplicate sensors (the processor ID and core ID of all - * HT siblings of a core is the same). - * Skip if a HT sibling of this core is already online. + * HT siblings of a core are the same). + * Skip if a HT sibling of this core is already registered. * This is not an error. */ if (pdata->core_data[attr_no] != NULL) @@ -770,10 +772,10 @@ static void __cpuinit put_core_offline(unsigned int cpu) coretemp_remove_core(pdata, &pdev->dev, indx); /* - * If a core is taken offline, but a HT sibling of the same core is - * still online, register the alternate sibling. This ensures that - * exactly one set of attributes is provided as long as at least one - * HT sibling of a core is online. + * If a HT sibling of a core is taken offline, but another HT sibling + * of the same core is still online, register the alternate sibling. + * This ensures that exactly one set of attributes is provided as long + * as at least one HT sibling of a core is online. */ for_each_sibling(i, cpu) { if (i != cpu) { diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c index 98799bab69ce..354770ed3186 100644 --- a/drivers/hwmon/pmbus_core.c +++ b/drivers/hwmon/pmbus_core.c @@ -707,6 +707,7 @@ do { \ struct sensor_device_attribute *a \ = &data->_type##s[data->num_##_type##s].attribute; \ BUG_ON(data->num_attributes >= data->max_attributes); \ + sysfs_attr_init(&a->dev_attr.attr); \ a->dev_attr.attr.name = _name; \ a->dev_attr.attr.mode = _mode; \ a->dev_attr.show = _show; \ diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c index d36a4c09e25d..0bbee7824d78 100644 --- a/drivers/isdn/hardware/eicon/divasfunc.c +++ b/drivers/isdn/hardware/eicon/divasfunc.c @@ -113,9 +113,8 @@ void diva_xdi_didd_remove_adapter(int card) static void start_dbg(void) { DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT); - DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s]-%s-%s)", - DIVA_BUILD, diva_xdi_common_code_build, __DATE__, - __TIME__)) + DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s])", + DIVA_BUILD, diva_xdi_common_code_build)) } /* diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 2d8b4044be36..b2b0c45f32a9 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -20,6 +20,7 @@ */ #include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -49,11 +50,12 @@ #define UNSET (-1U) -#define DM1105_BOARD_NOAUTO UNSET -#define DM1105_BOARD_UNKNOWN 0 -#define DM1105_BOARD_DVBWORLD_2002 1 -#define DM1105_BOARD_DVBWORLD_2004 2 -#define DM1105_BOARD_AXESS_DM05 3 +#define DM1105_BOARD_NOAUTO UNSET +#define DM1105_BOARD_UNKNOWN 0 +#define DM1105_BOARD_DVBWORLD_2002 1 +#define DM1105_BOARD_DVBWORLD_2004 2 +#define DM1105_BOARD_AXESS_DM05 3 +#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 /* ----------------------------------------------- */ /* @@ -157,22 +159,38 @@ #define DM1105_MAX 0x04 #define DRIVER_NAME "dm1105" +#define DM1105_I2C_GPIO_NAME "dm1105-gpio" #define DM1105_DMA_PACKETS 47 #define DM1105_DMA_PACKET_LENGTH (128*4) #define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) +/* */ +#define GPIO08 (1 << 8) +#define GPIO13 (1 << 13) +#define GPIO14 (1 << 14) +#define GPIO15 (1 << 15) +#define GPIO16 (1 << 16) +#define GPIO17 (1 << 17) +#define GPIO_ALL 0x03ffff + /* GPIO's for LNB power control */ -#define DM1105_LNB_MASK 0x00000000 -#define DM1105_LNB_OFF 0x00020000 -#define DM1105_LNB_13V 0x00010100 -#define DM1105_LNB_18V 0x00000100 +#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +#define DM1105_LNB_OFF GPIO17 +#define DM1105_LNB_13V (GPIO16 | GPIO08) +#define DM1105_LNB_18V GPIO08 /* GPIO's for LNB power control for Axess DM05 */ -#define DM05_LNB_MASK 0x00000000 -#define DM05_LNB_OFF 0x00020000/* actually 13v */ -#define DM05_LNB_13V 0x00020000 -#define DM05_LNB_18V 0x00030000 +#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +#define DM05_LNB_OFF GPIO17/* actually 13v */ +#define DM05_LNB_13V GPIO17 +#define DM05_LNB_18V (GPIO17 | GPIO16) + +/* GPIO's for LNB power control for unbranded with I2C on GPIO */ +#define UNBR_LNB_MASK (GPIO17 | GPIO16) +#define UNBR_LNB_OFF 0 +#define UNBR_LNB_13V GPIO17 +#define UNBR_LNB_18V (GPIO17 | GPIO16) static unsigned int card[] = {[0 ... 3] = UNSET }; module_param_array(card, int, NULL, 0444); @@ -187,7 +205,11 @@ static unsigned int dm1105_devcount; DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct dm1105_board { - char *name; + char *name; + struct { + u32 mask, off, v13, v18; + } lnb; + u32 gpio_scl, gpio_sda; }; struct dm1105_subid { @@ -199,15 +221,50 @@ struct dm1105_subid { static const struct dm1105_board dm1105_boards[] = { [DM1105_BOARD_UNKNOWN] = { .name = "UNKNOWN/GENERIC", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, }, [DM1105_BOARD_DVBWORLD_2002] = { .name = "DVBWorld PCI 2002", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, }, [DM1105_BOARD_DVBWORLD_2004] = { .name = "DVBWorld PCI 2004", + .lnb = { + .mask = DM1105_LNB_MASK, + .off = DM1105_LNB_OFF, + .v13 = DM1105_LNB_13V, + .v18 = DM1105_LNB_18V, + }, }, [DM1105_BOARD_AXESS_DM05] = { .name = "Axess/EasyTv DM05", + .lnb = { + .mask = DM05_LNB_MASK, + .off = DM05_LNB_OFF, + .v13 = DM05_LNB_13V, + .v18 = DM05_LNB_18V, + }, + }, + [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { + .name = "Unbranded DM1105 with i2c on GPIOs", + .lnb = { + .mask = UNBR_LNB_MASK, + .off = UNBR_LNB_OFF, + .v13 = UNBR_LNB_13V, + .v18 = UNBR_LNB_18V, + }, + .gpio_scl = GPIO14, + .gpio_sda = GPIO13, }, }; @@ -293,6 +350,8 @@ struct dm1105_dev { /* i2c */ struct i2c_adapter i2c_adap; + struct i2c_adapter i2c_bb_adap; + struct i2c_algo_bit_data i2c_bit; /* irq */ struct work_struct work; @@ -328,6 +387,103 @@ struct dm1105_dev { #define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) #define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) +/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, + so we can use only 3 GPIO's from GPIO15 to GPIO17. + Here I don't check whether HOST is enebled as it is not implemented yet. + */ +static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); + +} + +static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); + +} + +static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); + +} + +static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if (mask & 0x0003ffff) + return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; + + return 0; +} + +static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) +{ + if (mask & 0xfffc0000) + printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); + + if ((mask & 0x0003ffff) && asoutput) + dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); + else if ((mask & 0x0003ffff) && !asoutput) + dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); + +} + +static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) +{ + if (state) + dm1105_gpio_enable(dev, line, 0); + else { + dm1105_gpio_enable(dev, line, 1); + dm1105_gpio_clear(dev, line); + } +} + +static void dm1105_setsda(void *data, int state) +{ + struct dm1105_dev *dev = data; + + dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); +} + +static void dm1105_setscl(void *data, int state) +{ + struct dm1105_dev *dev = data; + + dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); +} + +static int dm1105_getsda(void *data) +{ + struct dm1105_dev *dev = data; + + return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) + ? 1 : 0; +} + +static int dm1105_getscl(void *data) +{ + struct dm1105_dev *dev = data; + + return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) + ? 1 : 0; +} + static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { @@ -436,31 +592,20 @@ static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); - u32 lnb_mask, lnb_13v, lnb_18v, lnb_off; - switch (dev->boardnr) { - case DM1105_BOARD_AXESS_DM05: - lnb_mask = DM05_LNB_MASK; - lnb_off = DM05_LNB_OFF; - lnb_13v = DM05_LNB_13V; - lnb_18v = DM05_LNB_18V; - break; - case DM1105_BOARD_DVBWORLD_2002: - case DM1105_BOARD_DVBWORLD_2004: - default: - lnb_mask = DM1105_LNB_MASK; - lnb_off = DM1105_LNB_OFF; - lnb_13v = DM1105_LNB_13V; - lnb_18v = DM1105_LNB_18V; - } - - dm_writel(DM1105_GPIOCTR, lnb_mask); + dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); if (voltage == SEC_VOLTAGE_18) - dm_writel(DM1105_GPIOVAL, lnb_18v); + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.v18); else if (voltage == SEC_VOLTAGE_13) - dm_writel(DM1105_GPIOVAL, lnb_13v); + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.v13); else - dm_writel(DM1105_GPIOVAL, lnb_off); + dm1105_gpio_andor(dev, + dm1105_boards[dev->boardnr].lnb.mask, + dm1105_boards[dev->boardnr].lnb.off); return 0; } @@ -708,6 +853,38 @@ static int __devinit frontend_init(struct dm1105_dev *dev) int ret; switch (dev->boardnr) { + case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: + dm1105_gpio_enable(dev, GPIO15, 1); + dm1105_gpio_clear(dev, GPIO15); + msleep(100); + dm1105_gpio_set(dev, GPIO15); + msleep(200); + dev->fe = dvb_attach( + stv0299_attach, &sharp_z0194a_config, + &dev->i2c_bb_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(dvb_pll_attach, dev->fe, 0x60, + &dev->i2c_bb_adap, DVB_PLL_OPERA1); + break; + } + + dev->fe = dvb_attach( + stv0288_attach, &earda_config, + &dev->i2c_bb_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(stb6000_attach, dev->fe, 0x61, + &dev->i2c_bb_adap); + break; + } + + dev->fe = dvb_attach( + si21xx_attach, &serit_config, + &dev->i2c_bb_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; + break; case DM1105_BOARD_DVBWORLD_2004: dev->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, @@ -870,11 +1047,32 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, if (ret < 0) goto err_dm1105_hw_exit; + i2c_set_adapdata(&dev->i2c_bb_adap, dev); + strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); + dev->i2c_bb_adap.owner = THIS_MODULE; + dev->i2c_bb_adap.dev.parent = &pdev->dev; + dev->i2c_bb_adap.algo_data = &dev->i2c_bit; + dev->i2c_bit.data = dev; + dev->i2c_bit.setsda = dm1105_setsda; + dev->i2c_bit.setscl = dm1105_setscl; + dev->i2c_bit.getsda = dm1105_getsda; + dev->i2c_bit.getscl = dm1105_getscl; + dev->i2c_bit.udelay = 10; + dev->i2c_bit.timeout = 10; + + /* Raise SCL and SDA */ + dm1105_setsda(dev, 1); + dm1105_setscl(dev, 1); + + ret = i2c_bit_add_bus(&dev->i2c_bb_adap); + if (ret < 0) + goto err_i2c_del_adapter; + /* dvb */ ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, THIS_MODULE, &pdev->dev, adapter_nr); if (ret < 0) - goto err_i2c_del_adapter; + goto err_i2c_del_adapters; dvb_adapter = &dev->dvb_adapter; @@ -952,6 +1150,8 @@ err_dvb_dmx_release: dvb_dmx_release(dvbdemux); err_dvb_unregister_adapter: dvb_unregister_adapter(dvb_adapter); +err_i2c_del_adapters: + i2c_del_adapter(&dev->i2c_bb_adap); err_i2c_del_adapter: i2c_del_adapter(&dev->i2c_adap); err_dm1105_hw_exit: diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index f36f471deae2..37b146961ae2 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -207,17 +207,6 @@ static int lme2510_stream_restart(struct dvb_usb_device *d) rbuff, sizeof(rbuff)); return ret; } -static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u32 keypress) -{ - struct dvb_usb_device *d = adap->dev; - - deb_info(1, "INT Key Keypress =%04x", keypress); - - if (keypress > 0) - rc_keydown(d->rc_dev, keypress, 0); - - return 0; -} static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) { @@ -256,6 +245,7 @@ static void lme2510_int_response(struct urb *lme_urb) struct lme2510_state *st = adap->dev->priv; static u8 *ibuf, *rbuf; int i = 0, offset; + u32 key; switch (lme_urb->status) { case 0: @@ -282,10 +272,16 @@ static void lme2510_int_response(struct urb *lme_urb) switch (ibuf[0]) { case 0xaa: - debug_data_snipet(1, "INT Remote data snipet in", ibuf); - lme2510_remote_keypress(adap, - (u32)(ibuf[2] << 24) + (ibuf[3] << 16) + - (ibuf[4] << 8) + ibuf[5]); + debug_data_snipet(1, "INT Remote data snipet", ibuf); + if ((ibuf[4] + ibuf[5]) == 0xff) { + key = ibuf[5]; + key += (ibuf[3] > 0) + ? (ibuf[3] ^ 0xff) << 8 : 0; + key += (ibuf[2] ^ 0xff) << 16; + deb_info(1, "INT Key =%08x", key); + if (adap->dev->rc_dev != NULL) + rc_keydown(adap->dev->rc_dev, key, 0); + } break; case 0xbb: switch (st->tuner_config) { @@ -691,45 +687,6 @@ static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return (ret < 0) ? -ENODEV : 0; } -static int lme2510_int_service(struct dvb_usb_adapter *adap) -{ - struct dvb_usb_device *d = adap->dev; - struct rc_dev *rc; - int ret; - - info("STA Configuring Remote"); - - rc = rc_allocate_device(); - if (!rc) - return -ENOMEM; - - usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); - strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); - - rc->input_name = "LME2510 Remote Control"; - rc->input_phys = d->rc_phys; - rc->map_name = RC_MAP_LME2510; - rc->driver_name = "LME 2510"; - usb_to_input_id(d->udev, &rc->input_id); - - ret = rc_register_device(rc); - if (ret) { - rc_free_device(rc); - return ret; - } - d->rc_dev = rc; - - /* Start the Interrupt */ - ret = lme2510_int_read(adap); - if (ret < 0) { - rc_unregister_device(rc); - info("INT Unable to start Interrupt Service"); - return -ENODEV; - } - - return 0; -} - static u8 check_sum(u8 *p, u8 len) { u8 sum = 0; @@ -831,7 +788,7 @@ static int lme_firmware_switch(struct usb_device *udev, int cold) cold_fw = !cold; - if (udev->descriptor.idProduct == 0x1122) { + if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) { switch (dvb_usb_lme2510_firmware) { default: dvb_usb_lme2510_firmware = TUNER_S0194; @@ -1053,8 +1010,11 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) end: if (ret) { - kfree(adap->fe); - adap->fe = NULL; + if (adap->fe) { + dvb_frontend_detach(adap->fe); + adap->fe = NULL; + } + adap->dev->props.rc.core.rc_codes = NULL; return -ENODEV; } @@ -1097,8 +1057,12 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) return -ENODEV; } - /* Start the Interrupt & Remote*/ - ret = lme2510_int_service(adap); + /* Start the Interrupt*/ + ret = lme2510_int_read(adap); + if (ret < 0) { + info("INT Unable to start Interrupt Service"); + return -ENODEV; + } return ret; } @@ -1204,6 +1168,12 @@ static struct dvb_usb_device_properties lme2510_properties = { } } }, + .rc.core = { + .protocol = RC_TYPE_NEC, + .module_name = "LME2510 Remote Control", + .allowed_protos = RC_TYPE_NEC, + .rc_codes = RC_MAP_LME2510, + }, .power_ctrl = lme2510_powerup, .identify_state = lme2510_identify_state, .i2c_algo = &lme2510_i2c_algo, @@ -1246,6 +1216,12 @@ static struct dvb_usb_device_properties lme2510c_properties = { } } }, + .rc.core = { + .protocol = RC_TYPE_NEC, + .module_name = "LME2510 Remote Control", + .allowed_protos = RC_TYPE_NEC, + .rc_codes = RC_MAP_LME2510, + }, .power_ctrl = lme2510_powerup, .identify_state = lme2510_identify_state, .i2c_algo = &lme2510_i2c_algo, @@ -1269,19 +1245,21 @@ static void *lme2510_exit_int(struct dvb_usb_device *d) adap->feedcount = 0; } - if (st->lme_urb != NULL) { + if (st->usb_buffer != NULL) { st->i2c_talk_onoff = 1; st->signal_lock = 0; st->signal_level = 0; st->signal_sn = 0; buffer = st->usb_buffer; + } + + if (st->lme_urb != NULL) { usb_kill_urb(st->lme_urb); usb_free_coherent(d->udev, 5000, st->buffer, st->lme_urb->transfer_dma); info("Interrupt Service Stopped"); - rc_unregister_device(d->rc_dev); - info("Remote Stopped"); } + return buffer; } @@ -1293,7 +1271,8 @@ static void lme2510_exit(struct usb_interface *intf) if (d != NULL) { usb_buffer = lme2510_exit_int(d); dvb_usb_device_exit(intf); - kfree(usb_buffer); + if (usb_buffer != NULL) + kfree(usb_buffer); } } @@ -1327,5 +1306,5 @@ module_exit(lme2510_module_exit); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.86"); +MODULE_VERSION("1.88"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c index 2da55ec20392..d70eee00f33a 100644 --- a/drivers/media/dvb/frontends/stb0899_algo.c +++ b/drivers/media/dvb/frontends/stb0899_algo.c @@ -23,7 +23,7 @@ #include "stb0899_priv.h" #include "stb0899_reg.h" -inline u32 stb0899_do_div(u64 n, u32 d) +static inline u32 stb0899_do_div(u64 n, u32 d) { /* wrap do_div() for ease of use */ diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c index 1742056a34e8..53c7d8f1df28 100644 --- a/drivers/media/dvb/frontends/tda8261.c +++ b/drivers/media/dvb/frontends/tda8261.c @@ -224,7 +224,6 @@ exit: } EXPORT_SYMBOL(tda8261_attach); -MODULE_PARM_DESC(verbose, "Set verbosity level"); MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index 5c2a9058c09f..e83e84003025 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -412,8 +412,7 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d goto err_out_free_region; } - v4l2_info(v4l2_dev, "version " DRIVER_VERSION - " time " __TIME__ " " __DATE__ "\n"); + v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n"); v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n", dev->io); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 46cacf845049..459f7272d326 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1382,7 +1382,7 @@ static int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - ctrl->val = wl1273_fm_get_tx_ctune(radio); + ctrl->cur.val = wl1273_fm_get_tx_ctune(radio); break; default: diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index d50e5ac75ab6..87010724f914 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -191,7 +191,7 @@ static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - ctrl->val = fm_tx_get_tune_cap_val(fmdev); + ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev); break; default: fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 154c337f00fd..7d4bbc226d06 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -148,6 +148,18 @@ config IR_ITE_CIR To compile this driver as a module, choose M here: the module will be called ite-cir. +config IR_FINTEK + tristate "Fintek Consumer Infrared Transceiver" + depends on PNP + depends on RC_CORE + ---help--- + Say Y here to enable support for integrated infrared receiver + /transciever made by Fintek. This chip is found on assorted + Jetway motherboards (and of course, possibly others). + + To compile this driver as a module, choose M here: the + module will be called fintek-cir. + config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" depends on PNP diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 1f90a219a162..52830e5f4eaa 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o obj-$(CONFIG_IR_IMON) += imon.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o +obj-$(CONFIG_IR_FINTEK) += fintek-cir.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o obj-$(CONFIG_IR_REDRAT3) += redrat3.o diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c new file mode 100644 index 000000000000..8fa539dde1b4 --- /dev/null +++ b/drivers/media/rc/fintek-cir.c @@ -0,0 +1,684 @@ +/* + * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR + * + * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> + * + * Special thanks to Fintek for providing hardware and spec sheets. + * This driver is based upon the nuvoton, ite and ene drivers for + * similar hardware. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/pnp.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <media/rc-core.h> +#include <linux/pci_ids.h> + +#include "fintek-cir.h" + +/* write val to config reg */ +static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg) +{ + fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", + __func__, reg, val, fintek->cr_ip, fintek->cr_dp); + outb(reg, fintek->cr_ip); + outb(val, fintek->cr_dp); +} + +/* read val from config reg */ +static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg) +{ + u8 val; + + outb(reg, fintek->cr_ip); + val = inb(fintek->cr_dp); + + fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)", + __func__, reg, val, fintek->cr_ip, fintek->cr_dp); + return val; +} + +/* update config register bit without changing other bits */ +static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) +{ + u8 tmp = fintek_cr_read(fintek, reg) | val; + fintek_cr_write(fintek, tmp, reg); +} + +/* clear config register bit without changing other bits */ +static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) +{ + u8 tmp = fintek_cr_read(fintek, reg) & ~val; + fintek_cr_write(fintek, tmp, reg); +} + +/* enter config mode */ +static inline void fintek_config_mode_enable(struct fintek_dev *fintek) +{ + /* Enabling Config Mode explicitly requires writing 2x */ + outb(CONFIG_REG_ENABLE, fintek->cr_ip); + outb(CONFIG_REG_ENABLE, fintek->cr_ip); +} + +/* exit config mode */ +static inline void fintek_config_mode_disable(struct fintek_dev *fintek) +{ + outb(CONFIG_REG_DISABLE, fintek->cr_ip); +} + +/* + * When you want to address a specific logical device, write its logical + * device number to GCR_LOGICAL_DEV_NO + */ +static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev) +{ + fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO); +} + +/* write val to cir config register */ +static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset) +{ + outb(val, fintek->cir_addr + offset); +} + +/* read val from cir config register */ +static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset) +{ + u8 val; + + val = inb(fintek->cir_addr + offset); + + return val; +} + +#define pr_reg(text, ...) \ + printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__) + +/* dump current cir register contents */ +static void cir_dump_regs(struct fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + + pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME); + pr_reg(" * CR CIR BASE ADDR: 0x%x\n", + (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) | + fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO)); + pr_reg(" * CR CIR IRQ NUM: 0x%x\n", + fintek_cr_read(fintek, CIR_CR_IRQ_SEL)); + + fintek_config_mode_disable(fintek); + + pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME); + pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS)); + pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL)); + pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA)); + pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL)); + pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA)); +} + +/* detect hardware features */ +static int fintek_hw_detect(struct fintek_dev *fintek) +{ + unsigned long flags; + u8 chip_major, chip_minor; + u8 vendor_major, vendor_minor; + u8 portsel, ir_class; + u16 vendor; + int ret = 0; + + fintek_config_mode_enable(fintek); + + /* Check if we're using config port 0x4e or 0x2e */ + portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); + if (portsel == 0xff) { + fit_pr(KERN_INFO, "first portsel read was bunk, trying alt"); + fintek_config_mode_disable(fintek); + fintek->cr_ip = CR_INDEX_PORT2; + fintek->cr_dp = CR_DATA_PORT2; + fintek_config_mode_enable(fintek); + portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL); + } + fit_dbg("portsel reg: 0x%02x", portsel); + + ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS); + fit_dbg("ir_class reg: 0x%02x", ir_class); + + switch (ir_class) { + case CLASS_RX_2TX: + case CLASS_RX_1TX: + fintek->hw_tx_capable = true; + break; + case CLASS_RX_ONLY: + default: + fintek->hw_tx_capable = false; + break; + } + + chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI); + chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO); + + vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI); + vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO); + vendor = vendor_major << 8 | vendor_minor; + + if (vendor != VENDOR_ID_FINTEK) + fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor); + else + fit_dbg("Read Fintek vendor ID from chip"); + + fintek_config_mode_disable(fintek); + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek->chip_major = chip_major; + fintek->chip_minor = chip_minor; + fintek->chip_vendor = vendor; + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + return ret; +} + +static void fintek_cir_ldev_init(struct fintek_dev *fintek) +{ + /* Select CIR logical device and enable */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + /* Write allocated CIR address and IRQ information to hardware */ + fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI); + fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO); + + fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL); + + fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)", + fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len); +} + +/* enable CIR interrupts */ +static void fintek_enable_cir_irq(struct fintek_dev *fintek) +{ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); +} + +static void fintek_cir_regs_init(struct fintek_dev *fintek) +{ + /* clear any and all stray interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + /* and finally, enable interrupts */ + fintek_enable_cir_irq(fintek); +} + +static void fintek_enable_wake(struct fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI); + + /* Allow CIR PME's to wake system */ + fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG); + /* Enable CIR PME's */ + fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG); + /* Clear CIR PME status register */ + fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG); + /* Save state */ + fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG); + + fintek_config_mode_disable(fintek); +} + +static int fintek_cmdsize(u8 cmd, u8 subcmd) +{ + int datasize = 0; + + switch (cmd) { + case BUF_COMMAND_NULL: + if (subcmd == BUF_HW_CMD_HEADER) + datasize = 1; + break; + case BUF_HW_CMD_HEADER: + if (subcmd == BUF_CMD_G_REVISION) + datasize = 2; + break; + case BUF_COMMAND_HEADER: + switch (subcmd) { + case BUF_CMD_S_CARRIER: + case BUF_CMD_S_TIMEOUT: + case BUF_RSP_PULSE_COUNT: + datasize = 2; + break; + case BUF_CMD_SIG_END: + case BUF_CMD_S_TXMASK: + case BUF_CMD_S_RXSENSOR: + datasize = 1; + break; + } + } + + return datasize; +} + +/* process ir data stored in driver buffer */ +static void fintek_process_rx_ir_data(struct fintek_dev *fintek) +{ + DEFINE_IR_RAW_EVENT(rawir); + u8 sample; + int i; + + for (i = 0; i < fintek->pkts; i++) { + sample = fintek->buf[i]; + switch (fintek->parser_state) { + case CMD_HEADER: + fintek->cmd = sample; + if ((fintek->cmd == BUF_COMMAND_HEADER) || + ((fintek->cmd & BUF_COMMAND_MASK) != + BUF_PULSE_BIT)) { + fintek->parser_state = SUBCMD; + continue; + } + fintek->rem = (fintek->cmd & BUF_LEN_MASK); + fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem); + if (fintek->rem) + fintek->parser_state = PARSE_IRDATA; + else + ir_raw_event_reset(fintek->rdev); + break; + case SUBCMD: + fintek->rem = fintek_cmdsize(fintek->cmd, sample); + fintek->parser_state = CMD_DATA; + break; + case CMD_DATA: + fintek->rem--; + break; + case PARSE_IRDATA: + fintek->rem--; + init_ir_raw_event(&rawir); + rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); + rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK) + * CIR_SAMPLE_PERIOD); + + fit_dbg("Storing %s with duration %d", + rawir.pulse ? "pulse" : "space", + rawir.duration); + ir_raw_event_store_with_filter(fintek->rdev, &rawir); + break; + } + + if ((fintek->parser_state != CMD_HEADER) && !fintek->rem) + fintek->parser_state = CMD_HEADER; + } + + fintek->pkts = 0; + + fit_dbg("Calling ir_raw_event_handle"); + ir_raw_event_handle(fintek->rdev); +} + +/* copy data from hardware rx register into driver buffer */ +static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs) +{ + unsigned long flags; + u8 sample, status; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + + /* + * We must read data from CIR_RX_DATA until the hardware IR buffer + * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in + * the CIR_STATUS register + */ + do { + sample = fintek_cir_reg_read(fintek, CIR_RX_DATA); + fit_dbg("%s: sample: 0x%02x", __func__, sample); + + fintek->buf[fintek->pkts] = sample; + fintek->pkts++; + + status = fintek_cir_reg_read(fintek, CIR_STATUS); + if (!(status & CIR_STATUS_IRQ_EN)) + break; + } while (status & rx_irqs); + + fintek_process_rx_ir_data(fintek); + + spin_unlock_irqrestore(&fintek->fintek_lock, flags); +} + +static void fintek_cir_log_irqs(u8 status) +{ + fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status, + status & CIR_STATUS_IRQ_EN ? " IRQEN" : "", + status & CIR_STATUS_TX_FINISH ? " TXF" : "", + status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "", + status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "", + status & CIR_STATUS_RX_RECEIVE ? " RXOK" : ""); +} + +/* interrupt service routine for incoming and outgoing CIR data */ +static irqreturn_t fintek_cir_isr(int irq, void *data) +{ + struct fintek_dev *fintek = data; + u8 status, rx_irqs; + + fit_dbg_verbose("%s firing", __func__); + + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_config_mode_disable(fintek); + + /* + * Get IR Status register contents. Write 1 to ack/clear + * + * bit: reg name - description + * 3: TX_FINISH - TX is finished + * 2: TX_UNDERRUN - TX underrun + * 1: RX_TIMEOUT - RX data timeout + * 0: RX_RECEIVE - RX data received + */ + status = fintek_cir_reg_read(fintek, CIR_STATUS); + if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) { + fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status); + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + return IRQ_RETVAL(IRQ_NONE); + } + + if (debug) + fintek_cir_log_irqs(status); + + rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT); + if (rx_irqs) + fintek_get_rx_ir_data(fintek, rx_irqs); + + /* ack/clear all irq flags we've got */ + fintek_cir_reg_write(fintek, status, CIR_STATUS); + + fit_dbg_verbose("%s done", __func__); + return IRQ_RETVAL(IRQ_HANDLED); +} + +static void fintek_enable_cir(struct fintek_dev *fintek) +{ + /* set IRQ enabled */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS); + + fintek_config_mode_enable(fintek); + + /* enable the CIR logical device */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + /* clear all pending interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + /* enable interrupts */ + fintek_enable_cir_irq(fintek); +} + +static void fintek_disable_cir(struct fintek_dev *fintek) +{ + fintek_config_mode_enable(fintek); + + /* disable the CIR logical device */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); +} + +static int fintek_open(struct rc_dev *dev) +{ + struct fintek_dev *fintek = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek_enable_cir(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + return 0; +} + +static void fintek_close(struct rc_dev *dev) +{ + struct fintek_dev *fintek = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + fintek_disable_cir(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); +} + +/* Allocate memory, probe hardware, and initialize everything */ +static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) +{ + struct fintek_dev *fintek; + struct rc_dev *rdev; + int ret = -ENOMEM; + + fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL); + if (!fintek) + return ret; + + /* input device for IR remote (and tx) */ + rdev = rc_allocate_device(); + if (!rdev) + goto failure; + + ret = -ENODEV; + /* validate pnp resources */ + if (!pnp_port_valid(pdev, 0)) { + dev_err(&pdev->dev, "IR PNP Port not valid!\n"); + goto failure; + } + + if (!pnp_irq_valid(pdev, 0)) { + dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); + goto failure; + } + + fintek->cir_addr = pnp_port_start(pdev, 0); + fintek->cir_irq = pnp_irq(pdev, 0); + fintek->cir_port_len = pnp_port_len(pdev, 0); + + fintek->cr_ip = CR_INDEX_PORT; + fintek->cr_dp = CR_DATA_PORT; + + spin_lock_init(&fintek->fintek_lock); + + ret = -EBUSY; + /* now claim resources */ + if (!request_region(fintek->cir_addr, + fintek->cir_port_len, FINTEK_DRIVER_NAME)) + goto failure; + + if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, + FINTEK_DRIVER_NAME, (void *)fintek)) + goto failure; + + pnp_set_drvdata(pdev, fintek); + fintek->pdev = pdev; + + ret = fintek_hw_detect(fintek); + if (ret) + goto failure; + + /* Initialize CIR & CIR Wake Logical Devices */ + fintek_config_mode_enable(fintek); + fintek_cir_ldev_init(fintek); + fintek_config_mode_disable(fintek); + + /* Initialize CIR & CIR Wake Config Registers */ + fintek_cir_regs_init(fintek); + + /* Set up the rc device */ + rdev->priv = fintek; + rdev->driver_type = RC_DRIVER_IR_RAW; + rdev->allowed_protos = RC_TYPE_ALL; + rdev->open = fintek_open; + rdev->close = fintek_close; + rdev->input_name = FINTEK_DESCRIPTION; + rdev->input_phys = "fintek/cir0"; + rdev->input_id.bustype = BUS_HOST; + rdev->input_id.vendor = VENDOR_ID_FINTEK; + rdev->input_id.product = fintek->chip_major; + rdev->input_id.version = fintek->chip_minor; + rdev->dev.parent = &pdev->dev; + rdev->driver_name = FINTEK_DRIVER_NAME; + rdev->map_name = RC_MAP_RC6_MCE; + rdev->timeout = US_TO_NS(1000); + /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ + rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + + ret = rc_register_device(rdev); + if (ret) + goto failure; + + device_init_wakeup(&pdev->dev, true); + fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); + if (debug) + cir_dump_regs(fintek); + + return 0; + +failure: + if (fintek->cir_irq) + free_irq(fintek->cir_irq, fintek); + if (fintek->cir_addr) + release_region(fintek->cir_addr, fintek->cir_port_len); + + rc_free_device(rdev); + kfree(fintek); + + return ret; +} + +static void __devexit fintek_remove(struct pnp_dev *pdev) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&fintek->fintek_lock, flags); + /* disable CIR */ + fintek_disable_cir(fintek); + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + /* enable CIR Wake (for IR power-on) */ + fintek_enable_wake(fintek); + spin_unlock_irqrestore(&fintek->fintek_lock, flags); + + /* free resources */ + free_irq(fintek->cir_irq, fintek); + release_region(fintek->cir_addr, fintek->cir_port_len); + + rc_unregister_device(fintek->rdev); + + kfree(fintek); +} + +static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + + fit_dbg("%s called", __func__); + + /* disable all CIR interrupts */ + fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS); + + fintek_config_mode_enable(fintek); + + /* disable cir logical dev */ + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + /* make sure wake is enabled */ + fintek_enable_wake(fintek); + + return 0; +} + +static int fintek_resume(struct pnp_dev *pdev) +{ + int ret = 0; + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + + fit_dbg("%s called", __func__); + + /* open interrupt */ + fintek_enable_cir_irq(fintek); + + /* Enable CIR logical device */ + fintek_config_mode_enable(fintek); + fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR); + fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN); + + fintek_config_mode_disable(fintek); + + fintek_cir_regs_init(fintek); + + return ret; +} + +static void fintek_shutdown(struct pnp_dev *pdev) +{ + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + fintek_enable_wake(fintek); +} + +static const struct pnp_device_id fintek_ids[] = { + { "FIT0002", 0 }, /* CIR */ + { "", 0 }, +}; + +static struct pnp_driver fintek_driver = { + .name = FINTEK_DRIVER_NAME, + .id_table = fintek_ids, + .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, + .probe = fintek_probe, + .remove = __devexit_p(fintek_remove), + .suspend = fintek_suspend, + .resume = fintek_resume, + .shutdown = fintek_shutdown, +}; + +int fintek_init(void) +{ + return pnp_register_driver(&fintek_driver); +} + +void fintek_exit(void) +{ + pnp_unregister_driver(&fintek_driver); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging output"); + +MODULE_DEVICE_TABLE(pnp, fintek_ids); +MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver"); + +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(fintek_init); +module_exit(fintek_exit); diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h new file mode 100644 index 000000000000..1b10b2011f5e --- /dev/null +++ b/drivers/media/rc/fintek-cir.h @@ -0,0 +1,243 @@ +/* + * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR + * + * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com> + * + * Special thanks to Fintek for providing hardware and spec sheets. + * This driver is based upon the nuvoton, ite and ene drivers for + * similar hardware. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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/spinlock.h> +#include <linux/ioctl.h> + +/* platform driver name to register */ +#define FINTEK_DRIVER_NAME "fintek-cir" +#define FINTEK_DESCRIPTION "Fintek LPC SuperIO Consumer IR Transceiver" +#define VENDOR_ID_FINTEK 0x1934 + + +/* debugging module parameter */ +static int debug; + +#define fit_pr(level, text, ...) \ + printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) + +#define fit_dbg(text, ...) \ + if (debug) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define fit_dbg_verbose(text, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + +#define fit_dbg_wake(text, ...) \ + if (debug > 2) \ + printk(KERN_DEBUG \ + KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) + + +#define TX_BUF_LEN 256 +#define RX_BUF_LEN 32 + +struct fintek_dev { + struct pnp_dev *pdev; + struct rc_dev *rdev; + + spinlock_t fintek_lock; + + /* for rx */ + u8 buf[RX_BUF_LEN]; + unsigned int pkts; + + struct { + spinlock_t lock; + u8 buf[TX_BUF_LEN]; + unsigned int buf_count; + unsigned int cur_buf_num; + wait_queue_head_t queue; + } tx; + + /* Config register index/data port pair */ + u8 cr_ip; + u8 cr_dp; + + /* hardware I/O settings */ + unsigned long cir_addr; + int cir_irq; + int cir_port_len; + + /* hardware id */ + u8 chip_major; + u8 chip_minor; + u16 chip_vendor; + + /* hardware features */ + bool hw_learning_capable; + bool hw_tx_capable; + + /* rx settings */ + bool learning_enabled; + bool carrier_detect_enabled; + + enum { + CMD_HEADER = 0, + SUBCMD, + CMD_DATA, + PARSE_IRDATA, + } parser_state; + + u8 cmd, rem; + + /* carrier period = 1 / frequency */ + u32 carrier; +}; + +/* buffer packet constants, largely identical to mceusb.c */ +#define BUF_PULSE_BIT 0x80 +#define BUF_LEN_MASK 0x1f +#define BUF_SAMPLE_MASK 0x7f + +#define BUF_COMMAND_HEADER 0x9f +#define BUF_COMMAND_MASK 0xe0 +#define BUF_COMMAND_NULL 0x00 +#define BUF_HW_CMD_HEADER 0xff +#define BUF_CMD_G_REVISION 0x0b +#define BUF_CMD_S_CARRIER 0x06 +#define BUF_CMD_S_TIMEOUT 0x0c +#define BUF_CMD_SIG_END 0x01 +#define BUF_CMD_S_TXMASK 0x08 +#define BUF_CMD_S_RXSENSOR 0x14 +#define BUF_RSP_PULSE_COUNT 0x15 + +#define CIR_SAMPLE_PERIOD 50 + +/* + * Configuration Register: + * Index Port + * Data Port + */ +#define CR_INDEX_PORT 0x2e +#define CR_DATA_PORT 0x2f + +/* Possible alternate values, depends on how the chip is wired */ +#define CR_INDEX_PORT2 0x4e +#define CR_DATA_PORT2 0x4f + +/* + * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is + * active. 1 = 0x4e, 0 = 0x2e + */ +#define PORT_SEL_PORT_4E_EN 0x10 + +/* Extended Function Mode enable/disable magic values */ +#define CONFIG_REG_ENABLE 0x87 +#define CONFIG_REG_DISABLE 0xaa + +/* Chip IDs found in CR_CHIP_ID_{HI,LO} */ +#define CHIP_ID_HIGH_F71809U 0x04 +#define CHIP_ID_LOW_F71809U 0x08 + +/* + * Global control regs we need to care about: + * Global Control def. + * Register name addr val. */ +#define GCR_SOFTWARE_RESET 0x02 /* 0x00 */ +#define GCR_LOGICAL_DEV_NO 0x07 /* 0x00 */ +#define GCR_CHIP_ID_HI 0x20 /* 0x04 */ +#define GCR_CHIP_ID_LO 0x21 /* 0x08 */ +#define GCR_VENDOR_ID_HI 0x23 /* 0x19 */ +#define GCR_VENDOR_ID_LO 0x24 /* 0x34 */ +#define GCR_CONFIG_PORT_SEL 0x25 /* 0x01 */ +#define GCR_KBMOUSE_WAKEUP 0x27 + +#define LOGICAL_DEV_DISABLE 0x00 +#define LOGICAL_DEV_ENABLE 0x01 + +/* Logical device number of the CIR function */ +#define LOGICAL_DEV_CIR 0x05 + +/* CIR Logical Device (LDN 0x08) config registers */ +#define CIR_CR_COMMAND_INDEX 0x04 +#define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host + must set to 1. When host finshes write + command to IR, host must clear to 0. */ +#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */ +#define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx, + 0x33 = rx + 1 tx */ +#define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */ +#define CIR_CR_BASE_ADDR_HI 0x60 /* MSB of CIR IO base addr */ +#define CIR_CR_BASE_ADDR_LO 0x61 /* LSB of CIR IO base addr */ +#define CIR_CR_IRQ_SEL 0x70 /* bits3-0 store CIR IRQ */ +#define CIR_CR_PSOUT_STATUS 0xf1 +#define CIR_CR_WAKE_KEY3_ADDR 0xf8 +#define CIR_CR_WAKE_KEY3_CODE 0xf9 +#define CIR_CR_WAKE_KEY3_DC 0xfa +#define CIR_CR_WAKE_CONTROL 0xfb +#define CIR_CR_WAKE_KEY12_ADDR 0xfc +#define CIR_CR_WAKE_KEY4_ADDR 0xfd +#define CIR_CR_WAKE_KEY5_ADDR 0xfe + +#define CLASS_RX_ONLY 0xff +#define CLASS_RX_2TX 0x66 +#define CLASS_RX_1TX 0x33 + +/* CIR device registers */ +#define CIR_STATUS 0x00 +#define CIR_RX_DATA 0x01 +#define CIR_TX_CONTROL 0x02 +#define CIR_TX_DATA 0x03 +#define CIR_CONTROL 0x04 + +/* Bits to enable CIR wake */ +#define LOGICAL_DEV_ACPI 0x01 +#define LDEV_ACPI_WAKE_EN_REG 0xe8 +#define ACPI_WAKE_EN_CIR_BIT 0x04 + +#define LDEV_ACPI_PME_EN_REG 0xf0 +#define LDEV_ACPI_PME_CLR_REG 0xf1 +#define ACPI_PME_CIR_BIT 0x02 + +#define LDEV_ACPI_STATE_REG 0xf4 +#define ACPI_STATE_CIR_BIT 0x20 + +/* + * CIR status register (0x00): + * 7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable) + * 3 - TX_FINISH (1 when TX finished, write 1 to clear) + * 2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear) + * 1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear) + * 0 - RX_RECEIVE (1 on RX receive, write 1 to clear) + */ +#define CIR_STATUS_IRQ_EN 0x80 +#define CIR_STATUS_TX_FINISH 0x08 +#define CIR_STATUS_TX_UNDERRUN 0x04 +#define CIR_STATUS_RX_TIMEOUT 0x02 +#define CIR_STATUS_RX_RECEIVE 0x01 +#define CIR_STATUS_IRQ_MASK 0x0f + +/* + * CIR TX control register (0x02): + * 7 - TX_START (1 to indicate TX start, auto-cleared when done) + * 6 - TX_END (1 to indicate TX data written to TX fifo) + */ +#define CIR_TX_CONTROL_TX_START 0x80 +#define CIR_TX_CONTROL_TX_END 0x40 + diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c index afae14fd152e..129d3f9a461d 100644 --- a/drivers/media/rc/keymaps/rc-lme2510.c +++ b/drivers/media/rc/keymaps/rc-lme2510.c @@ -14,81 +14,81 @@ static struct rc_map_table lme2510_rc[] = { /* Type 1 - 26 buttons */ - { 0xef12ba45, KEY_0 }, - { 0xef12a05f, KEY_1 }, - { 0xef12af50, KEY_2 }, - { 0xef12a25d, KEY_3 }, - { 0xef12be41, KEY_4 }, - { 0xef12f50a, KEY_5 }, - { 0xef12bd42, KEY_6 }, - { 0xef12b847, KEY_7 }, - { 0xef12b649, KEY_8 }, - { 0xef12fa05, KEY_9 }, - { 0xef12bc43, KEY_POWER }, - { 0xef12b946, KEY_SUBTITLE }, - { 0xef12f906, KEY_PAUSE }, - { 0xef12fc03, KEY_MEDIA_REPEAT}, - { 0xef12fd02, KEY_PAUSE }, - { 0xef12a15e, KEY_VOLUMEUP }, - { 0xef12a35c, KEY_VOLUMEDOWN }, - { 0xef12f609, KEY_CHANNELUP }, - { 0xef12e51a, KEY_CHANNELDOWN }, - { 0xef12e11e, KEY_PLAY }, - { 0xef12e41b, KEY_ZOOM }, - { 0xef12a659, KEY_MUTE }, - { 0xef12a55a, KEY_TV }, - { 0xef12e718, KEY_RECORD }, - { 0xef12f807, KEY_EPG }, - { 0xef12fe01, KEY_STOP }, + { 0x10ed45, KEY_0 }, + { 0x10ed5f, KEY_1 }, + { 0x10ed50, KEY_2 }, + { 0x10ed5d, KEY_3 }, + { 0x10ed41, KEY_4 }, + { 0x10ed0a, KEY_5 }, + { 0x10ed42, KEY_6 }, + { 0x10ed47, KEY_7 }, + { 0x10ed49, KEY_8 }, + { 0x10ed05, KEY_9 }, + { 0x10ed43, KEY_POWER }, + { 0x10ed46, KEY_SUBTITLE }, + { 0x10ed06, KEY_PAUSE }, + { 0x10ed03, KEY_MEDIA_REPEAT}, + { 0x10ed02, KEY_PAUSE }, + { 0x10ed5e, KEY_VOLUMEUP }, + { 0x10ed5c, KEY_VOLUMEDOWN }, + { 0x10ed09, KEY_CHANNELUP }, + { 0x10ed1a, KEY_CHANNELDOWN }, + { 0x10ed1e, KEY_PLAY }, + { 0x10ed1b, KEY_ZOOM }, + { 0x10ed59, KEY_MUTE }, + { 0x10ed5a, KEY_TV }, + { 0x10ed18, KEY_RECORD }, + { 0x10ed07, KEY_EPG }, + { 0x10ed01, KEY_STOP }, /* Type 2 - 20 buttons */ - { 0xff40ea15, KEY_0 }, - { 0xff40f708, KEY_1 }, - { 0xff40f609, KEY_2 }, - { 0xff40f50a, KEY_3 }, - { 0xff40f30c, KEY_4 }, - { 0xff40f20d, KEY_5 }, - { 0xff40f10e, KEY_6 }, - { 0xff40ef10, KEY_7 }, - { 0xff40ee11, KEY_8 }, - { 0xff40ed12, KEY_9 }, - { 0xff40ff00, KEY_POWER }, - { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xff40e51a, KEY_PAUSE }, /* Timeshift */ - { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xff40fe01, KEY_CHANNELUP }, - { 0xff40fa05, KEY_CHANNELDOWN }, - { 0xff40eb14, KEY_ZOOM }, - { 0xff40e718, KEY_RECORD }, - { 0xff40e916, KEY_STOP }, + { 0xbf15, KEY_0 }, + { 0xbf08, KEY_1 }, + { 0xbf09, KEY_2 }, + { 0xbf0a, KEY_3 }, + { 0xbf0c, KEY_4 }, + { 0xbf0d, KEY_5 }, + { 0xbf0e, KEY_6 }, + { 0xbf10, KEY_7 }, + { 0xbf11, KEY_8 }, + { 0xbf12, KEY_9 }, + { 0xbf00, KEY_POWER }, + { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */ + { 0xbf1a, KEY_PAUSE }, /* Timeshift */ + { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0xbf01, KEY_CHANNELUP }, + { 0xbf05, KEY_CHANNELDOWN }, + { 0xbf14, KEY_ZOOM }, + { 0xbf18, KEY_RECORD }, + { 0xbf16, KEY_STOP }, /* Type 3 - 20 buttons */ - { 0xff00e31c, KEY_0 }, - { 0xff00f807, KEY_1 }, - { 0xff00ea15, KEY_2 }, - { 0xff00f609, KEY_3 }, - { 0xff00e916, KEY_4 }, - { 0xff00e619, KEY_5 }, - { 0xff00f20d, KEY_6 }, - { 0xff00f30c, KEY_7 }, - { 0xff00e718, KEY_8 }, - { 0xff00a15e, KEY_9 }, - { 0xff00ba45, KEY_POWER }, - { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */ - { 0xff00b54a, KEY_PAUSE }, /* Timeshift */ - { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ - { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ - { 0xff00b946, KEY_CHANNELUP }, - { 0xff00bf40, KEY_CHANNELDOWN }, - { 0xff00f708, KEY_ZOOM }, - { 0xff00bd42, KEY_RECORD }, - { 0xff00a55a, KEY_STOP }, + { 0x1c, KEY_0 }, + { 0x07, KEY_1 }, + { 0x15, KEY_2 }, + { 0x09, KEY_3 }, + { 0x16, KEY_4 }, + { 0x19, KEY_5 }, + { 0x0d, KEY_6 }, + { 0x0c, KEY_7 }, + { 0x18, KEY_8 }, + { 0x5e, KEY_9 }, + { 0x45, KEY_POWER }, + { 0x44, KEY_MEDIA_REPEAT}, /* Recall */ + { 0x4a, KEY_PAUSE }, /* Timeshift */ + { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */ + { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/ + { 0x46, KEY_CHANNELUP }, + { 0x40, KEY_CHANNELDOWN }, + { 0x08, KEY_ZOOM }, + { 0x42, KEY_RECORD }, + { 0x5a, KEY_STOP }, }; static struct rc_map_list lme2510_map = { .map = { .scan = lme2510_rc, .size = ARRAY_SIZE(lme2510_rc), - .rc_type = RC_TYPE_UNKNOWN, + .rc_type = RC_TYPE_NEC, .name = RC_MAP_LME2510, } }; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3be180b3ba27..bb53de7fe408 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -687,7 +687,7 @@ config VIDEO_HEXIUM_GEMINI config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_V4L2 && I2C && DMADEVICES select DMA_ENGINE select TIMB_DMA select VIDEO_ADV7180 @@ -757,6 +757,8 @@ config VIDEO_NOON010PC30 ---help--- This driver supports NOON010PC30 CIF camera from Siliconfile +source "drivers/media/video/m5mols/Kconfig" + config VIDEO_OMAP3 tristate "OMAP 3 Camera support (EXPERIMENTAL)" select OMAP_IOMMU @@ -952,7 +954,7 @@ config VIDEO_SAMSUNG_S5P_FIMC config VIDEO_S5P_MIPI_CSIS tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" - depends on VIDEO_V4L2 && PM_RUNTIME && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API ---help--- This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9519160c2e01..f0fecd6f6a33 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 0073a8c55336..40eb6326e48a 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -438,7 +438,7 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v strcat(vc->card, " (676/"); break; default: - strcat(vc->card, " (???/"); + strcat(vc->card, " (XXX/"); break; } switch (cam->params.version.sensor_flags) { @@ -458,7 +458,7 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v strcat(vc->card, "500)"); break; default: - strcat(vc->card, "???)"); + strcat(vc->card, "XXX)"); break; } diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 280df43ca446..8d7813415760 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -1354,7 +1354,7 @@ void cx231xx_dump_SC_reg(struct cx231xx *dev) { u8 value[4] = { 0, 0, 0, 0 }; int status = 0; - cx231xx_info("cx231xx_dump_SC_reg %s!\n", __TIME__); + cx231xx_info("cx231xx_dump_SC_reg!\n"); status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, value, 4); diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c index 66671a4092e4..26fc206f095e 100644 --- a/drivers/media/video/gspca/kinect.c +++ b/drivers/media/video/gspca/kinect.c @@ -34,7 +34,7 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); MODULE_LICENSE("GPL"); -#ifdef DEBUG +#ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK | D_USBI | D_USBO | D_V4L2; #endif diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig new file mode 100644 index 000000000000..302dc3d70193 --- /dev/null +++ b/drivers/media/video/m5mols/Kconfig @@ -0,0 +1,5 @@ +config VIDEO_M5MOLS + tristate "Fujitsu M-5MOLS 8MP sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile new file mode 100644 index 000000000000..0a44e028edc7 --- /dev/null +++ b/drivers/media/video/m5mols/Makefile @@ -0,0 +1,3 @@ +m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o + +obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h new file mode 100644 index 000000000000..10b55c854487 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols.h @@ -0,0 +1,296 @@ +/* + * Header for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim, riverful.kim@samsung.com + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef M5MOLS_H +#define M5MOLS_H + +#include <media/v4l2-subdev.h> +#include "m5mols_reg.h" + +extern int m5mols_debug; + +#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd) + +#define to_sd(__ctrl) \ + (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd) + +enum m5mols_restype { + M5MOLS_RESTYPE_MONITOR, + M5MOLS_RESTYPE_CAPTURE, + M5MOLS_RESTYPE_MAX, +}; + +/** + * struct m5mols_resolution - structure for the resolution + * @type: resolution type according to the pixel code + * @width: width of the resolution + * @height: height of the resolution + * @reg: resolution preset register value + */ +struct m5mols_resolution { + u8 reg; + enum m5mols_restype type; + u16 width; + u16 height; +}; + +/** + * struct m5mols_exif - structure for the EXIF information of M-5MOLS + * @exposure_time: exposure time register value + * @shutter_speed: speed of the shutter register value + * @aperture: aperture register value + * @exposure_bias: it calls also EV bias + * @iso_speed: ISO register value + * @flash: status register value of the flash + * @sdr: status register value of the Subject Distance Range + * @qval: not written exact meaning in document + */ +struct m5mols_exif { + u32 exposure_time; + u32 shutter_speed; + u32 aperture; + u32 brightness; + u32 exposure_bias; + u16 iso_speed; + u16 flash; + u16 sdr; + u16 qval; +}; + +/** + * struct m5mols_capture - Structure for the capture capability + * @exif: EXIF information + * @main: size in bytes of the main image + * @thumb: size in bytes of the thumb image, if it was accompanied + * @total: total size in bytes of the produced image + */ +struct m5mols_capture { + struct m5mols_exif exif; + u32 main; + u32 thumb; + u32 total; +}; + +/** + * struct m5mols_scenemode - structure for the scenemode capability + * @metering: metering light register value + * @ev_bias: EV bias register value + * @wb_mode: mode which means the WhiteBalance is Auto or Manual + * @wb_preset: whitebalance preset register value in the Manual mode + * @chroma_en: register value whether the Chroma capability is enabled or not + * @chroma_lvl: chroma's level register value + * @edge_en: register value Whether the Edge capability is enabled or not + * @edge_lvl: edge's level register value + * @af_range: Auto Focus's range + * @fd_mode: Face Detection mode + * @mcc: Multi-axis Color Conversion which means emotion color + * @light: status of the Light + * @flash: status of the Flash + * @tone: Tone color which means Contrast + * @iso: ISO register value + * @capt_mode: Mode of the Image Stabilization while the camera capturing + * @wdr: Wide Dynamic Range register value + * + * The each value according to each scenemode is recommended in the documents. + */ +struct m5mols_scenemode { + u32 metering; + u32 ev_bias; + u32 wb_mode; + u32 wb_preset; + u32 chroma_en; + u32 chroma_lvl; + u32 edge_en; + u32 edge_lvl; + u32 af_range; + u32 fd_mode; + u32 mcc; + u32 light; + u32 flash; + u32 tone; + u32 iso; + u32 capt_mode; + u32 wdr; +}; + +/** + * struct m5mols_version - firmware version information + * @customer: customer information + * @project: version of project information according to customer + * @fw: firmware revision + * @hw: hardware revision + * @param: version of the parameter + * @awb: Auto WhiteBalance algorithm version + * @str: information about manufacturer and packaging vendor + * @af: Auto Focus version + * + * The register offset starts the customer version at 0x0, and it ends + * the awb version at 0x09. The customer, project information occupies 1 bytes + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is + * unique string associated with firmware's version. It includes information + * about manufacturer and the vendor of the sensor's packaging. The least + * significant 2 bytes of the string indicate packaging manufacturer. + */ +#define VERSION_STRING_SIZE 22 +struct m5mols_version { + u8 customer; + u8 project; + u16 fw; + u16 hw; + u16 param; + u16 awb; + u8 str[VERSION_STRING_SIZE]; + u8 af; +}; +#define VERSION_SIZE sizeof(struct m5mols_version) + +/** + * struct m5mols_info - M-5MOLS driver data structure + * @pdata: platform data + * @sd: v4l-subdev instance + * @pad: media pad + * @ffmt: current fmt according to resolution type + * @res_type: current resolution type + * @code: current code + * @irq_waitq: waitqueue for the capture + * @work_irq: workqueue for the IRQ + * @flags: state variable for the interrupt handler + * @handle: control handler + * @autoexposure: Auto Exposure control + * @exposure: Exposure control + * @autowb: Auto White Balance control + * @colorfx: Color effect control + * @saturation: Saturation control + * @zoom: Zoom control + * @ver: information of the version + * @cap: the capture mode attributes + * @power: current sensor's power status + * @ctrl_sync: true means all controls of the sensor are initialized + * @int_capture: true means the capture interrupt is issued once + * @lock_ae: true means the Auto Exposure is locked + * @lock_awb: true means the Aut WhiteBalance is locked + * @resolution: register value for current resolution + * @interrupt: register value for current interrupt status + * @mode: register value for current operation mode + * @mode_save: register value for current operation mode for saving + * @set_power: optional power callback to the board code + */ +struct m5mols_info { + const struct m5mols_platform_data *pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; + int res_type; + enum v4l2_mbus_pixelcode code; + wait_queue_head_t irq_waitq; + struct work_struct work_irq; + unsigned long flags; + + struct v4l2_ctrl_handler handle; + /* Autoexposure/exposure control cluster */ + struct { + struct v4l2_ctrl *autoexposure; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *autowb; + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *zoom; + + struct m5mols_version ver; + struct m5mols_capture cap; + bool power; + bool ctrl_sync; + bool lock_ae; + bool lock_awb; + u8 resolution; + u32 interrupt; + u32 mode; + u32 mode_save; + int (*set_power)(struct device *dev, int on); +}; + +#define ST_CAPT_IRQ 0 + +#define is_powered(__info) (__info->power) +#define is_ctrl_synced(__info) (__info->ctrl_sync) +#define is_available_af(__info) (__info->ver.af) +#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) +#define is_manufacturer(__info, __manufacturer) \ + (__info->ver.str[0] == __manufacturer[0] && \ + __info->ver.str[1] == __manufacturer[1]) +/* + * I2C operation of the M-5MOLS + * + * The I2C read operation of the M-5MOLS requires 2 messages. The first + * message sends the information about the command, command category, and total + * message size. The second message is used to retrieve the data specifed in + * the first message + * + * 1st message 2nd message + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+-------+ +------+------+------+------+ + * - size1: message data size(5 in this case) + * - size2: desired buffer size of the 2nd message + * - d[0..3]: according to size2 + * + * The I2C write operation needs just one message. The message includes + * category, command, total size, and desired data. + * + * 1st message + * +-------+---+----------+-----+------+------+------+------+ + * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | + * +-------+---+----------+-----+------+------+------+------+ + * - d[0..3]: according to size1 + */ +int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); +int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value); + +/* + * Mode operation of the M-5MOLS + * + * Changing the mode of the M-5MOLS is needed right executing order. + * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed + * by user. There are various categories associated with each mode. + * + * +============================================================+ + * | mode | category | + * +============================================================+ + * | FLASH | FLASH(only after Stand-by or Power-on) | + * | SYSTEM | SYSTEM(only after sensor arm-booting) | + * | PARAMETER | PARAMETER | + * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | + * | CAPTURE | Single CAPTURE, Preview(recording) | + * +============================================================+ + * + * The available executing order between each modes are as follows: + * PARAMETER <---> MONITOR <---> CAPTURE + */ +int m5mols_mode(struct m5mols_info *info, u32 mode); + +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg); +int m5mols_sync_controls(struct m5mols_info *info); +int m5mols_start_capture(struct m5mols_info *info); +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode); +int m5mols_lock_3a(struct m5mols_info *info, bool lock); +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); + +/* The firmware function */ +int m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)); + +#endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c new file mode 100644 index 000000000000..d71a3903b60f --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -0,0 +1,191 @@ +/* + * The Capture code for Fujitsu M-5MOLS ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim, riverful.kim@samsung.com + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/m5mols.h> + +#include "m5mols.h" +#include "m5mols_reg.h" + +static int m5mols_capture_error_handler(struct m5mols_info *info, + int timeout) +{ + int ret; + + /* Disable all interrupts and clear relevant interrupt staus bits */ + ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE, + info->interrupt & ~(REG_INT_CAPTURE)); + if (ret) + return ret; + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} +/** + * m5mols_read_rational - I2C read of a rational number + * + * Read numerator and denominator from registers @addr_num and @addr_den + * respectively and return the division result in @val. + */ +static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, + u32 addr_den, u32 *val) +{ + u32 num, den; + + int ret = m5mols_read(sd, addr_num, &num); + if (!ret) + ret = m5mols_read(sd, addr_den, &den); + if (ret) + return ret; + *val = den == 0 ? 0 : num / den; + return ret; +} + +/** + * m5mols_capture_info - Gather captured image information + * + * For now it gathers only EXIF information and file size. + */ +static int m5mols_capture_info(struct m5mols_info *info) +{ + struct m5mols_exif *exif = &info->cap.exif; + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, + EXIF_INFO_EXPTIME_DE, &exif->exposure_time); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, + &exif->shutter_speed); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, + &exif->aperture); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, + &exif->brightness); + if (ret) + return ret; + ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, + &exif->exposure_bias); + if (ret) + return ret; + + ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed); + if (!ret) + ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash); + if (!ret) + ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr); + if (!ret) + ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval); + if (ret) + return ret; + + if (!ret) + ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main); + if (!ret) + ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb); + if (!ret) + info->cap.total = info->cap.main + info->cap.thumb; + + return ret; +} + +int m5mols_start_capture(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + u32 resolution = info->resolution; + int timeout; + int ret; + + /* + * Preparing capture. Setting control & interrupt before entering + * capture mode + * + * 1) change to MONITOR mode for operating control & interrupt + * 2) set controls (considering v4l2_control value & lock 3A) + * 3) set interrupt + * 4) change to CAPTURE mode + */ + ret = m5mols_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_sync_controls(info); + if (!ret) + ret = m5mols_lock_3a(info, true); + if (!ret) + ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); + if (!ret) + ret = m5mols_mode(info, REG_CAPTURE); + if (!ret) { + /* Wait for capture interrupt, after changing capture mode */ + timeout = wait_event_interruptible_timeout(info->irq_waitq, + test_bit(ST_CAPT_IRQ, &info->flags), + msecs_to_jiffies(2000)); + if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) + ret = m5mols_capture_error_handler(info, timeout); + } + if (!ret) + ret = m5mols_lock_3a(info, false); + if (ret) + return ret; + /* + * Starting capture. Setting capture frame count and resolution and + * the format(available format: JPEG, Bayer RAW, YUV). + * + * 1) select single or multi(enable to 25), format, size + * 2) set interrupt + * 3) start capture(for main image, now) + * 4) get information + * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device) + */ + ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); + if (!ret) + ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); + if (!ret) + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); + if (!ret) + ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); + if (!ret) + ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); + if (!ret) { + /* Wait for the capture completion interrupt */ + timeout = wait_event_interruptible_timeout(info->irq_waitq, + test_bit(ST_CAPT_IRQ, &info->flags), + msecs_to_jiffies(2000)); + if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) { + ret = m5mols_capture_info(info); + if (!ret) + v4l2_subdev_notify(sd, 0, &info->cap.total); + } + } + + return m5mols_capture_error_handler(info, timeout); +} diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c new file mode 100644 index 000000000000..817c16fec368 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -0,0 +1,299 @@ +/* + * Controls for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim, riverful.kim@samsung.com + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-ctrls.h> + +#include "m5mols.h" +#include "m5mols_reg.h" + +static struct m5mols_scenemode m5mols_default_scenemode[] = { + [REG_SCENE_NORMAL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, + 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PORTRAIT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 4, + REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_LANDSCAPE] = { + REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 6, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SPORTS] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PARTY_INDOOR] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_BEACH_SNOW] = { + REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SUNSET] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_DAYLIGHT, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_DAWN_DUSK] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_FLUORESCENT_1, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FALL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 5, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_NIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_AGAINST_LIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FIRE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_TEXT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 7, + REG_AF_MACRO, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, + }, + [REG_SCENE_CANDLE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, +}; + +/** + * m5mols_do_scenemode() - Change current scenemode + * @mode: Desired mode of the scenemode + * + * WARNING: The execution order is important. Do not change the order. + */ +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode) +{ + struct v4l2_subdev *sd = &info->sd; + struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; + int ret; + + if (mode > REG_SCENE_CANDLE) + return -EINVAL; + + ret = m5mols_lock_3a(info, false); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); + if (!ret) + ret = m5mols_write(sd, AE_MODE, scenemode.metering); + if (!ret) + ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); + if (!ret) + ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, AF_MODE, scenemode.af_range); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); + if (!ret) + ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); + if (!ret) + ret = m5mols_write(sd, AE_ISO, scenemode.iso); + if (!ret) + ret = m5mols_mode(info, REG_CAPTURE); + if (!ret) + ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); + if (!ret) + ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); + if (!ret) + ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); + if (!ret) + ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); + if (!ret) + ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); + if (!ret) + ret = m5mols_mode(info, REG_MONITOR); + + return ret; +} + +static int m5mols_lock_ae(struct m5mols_info *info, bool lock) +{ + int ret = 0; + + if (info->lock_ae != lock) + ret = m5mols_write(&info->sd, AE_LOCK, + lock ? REG_AE_LOCK : REG_AE_UNLOCK); + if (!ret) + info->lock_ae = lock; + + return ret; +} + +static int m5mols_lock_awb(struct m5mols_info *info, bool lock) +{ + int ret = 0; + + if (info->lock_awb != lock) + ret = m5mols_write(&info->sd, AWB_LOCK, + lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); + if (!ret) + info->lock_awb = lock; + + return ret; +} + +/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ +int m5mols_lock_3a(struct m5mols_info *info, bool lock) +{ + int ret; + + ret = m5mols_lock_ae(info, lock); + if (!ret) + ret = m5mols_lock_awb(info, lock); + /* Don't need to handle unlocking AF */ + if (!ret && is_available_af(info) && lock) + ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); + + return ret; +} + +/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret; + + switch (ctrl->id) { + case V4L2_CID_ZOOM_ABSOLUTE: + return m5mols_write(sd, MON_ZOOM, ctrl->val); + + case V4L2_CID_EXPOSURE_AUTO: + ret = m5mols_lock_ae(info, + ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); + if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) + ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); + if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { + int val = info->exposure->val; + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (!ret) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); + if (!ret) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); + } + return ret; + + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = m5mols_lock_awb(info, ctrl->val ? false : true); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, ctrl->val ? + REG_AWB_AUTO : REG_AWB_PRESET); + return ret; + + case V4L2_CID_SATURATION: + ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); + return ret; + + case V4L2_CID_COLORFX: + /* + * This control uses two kinds of registers: normal & color. + * The normal effect belongs to category 1, while the color + * one belongs to category 2. + * + * The normal effect uses one register: CAT1_EFFECT. + * The color effect uses three registers: + * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. + */ + ret = m5mols_write(sd, PARM_EFFECT, + ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : + ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : + REG_EFFECT_OFF); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); + if (!ret) + ret = m5mols_write(sd, MON_CFIXR, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_CFIXR_SEPIA : 0); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_CFIXB_SEPIA : 0); + return ret; + } + + return -EINVAL; +} diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c new file mode 100644 index 000000000000..76eac26e84ae --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -0,0 +1,1004 @@ +/* + * Driver for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim, riverful.kim@samsung.com + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/videodev2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/m5mols.h> + +#include "m5mols.h" +#include "m5mols_reg.h" + +int m5mols_debug; +module_param(m5mols_debug, int, 0644); + +#define MODULE_NAME "M5MOLS" +#define M5MOLS_I2C_CHECK_RETRY 500 + +/* The regulator consumer names for external voltage regulators */ +static struct regulator_bulk_data supplies[] = { + { + .supply = "core", /* ARM core power, 1.2V */ + }, { + .supply = "dig_18", /* digital power 1, 1.8V */ + }, { + .supply = "d_sensor", /* sensor power 1, 1.8V */ + }, { + .supply = "dig_28", /* digital power 2, 2.8V */ + }, { + .supply = "a_sensor", /* analog power */ + }, { + .supply = "dig_12", /* digital power 3, 1.2V */ + }, +}; + +static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { + [M5MOLS_RESTYPE_MONITOR] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_VYUY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, + [M5MOLS_RESTYPE_CAPTURE] = { + .width = 1920, + .height = 1080, + .code = V4L2_MBUS_FMT_JPEG_1X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; +#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) + +static const struct m5mols_resolution m5mols_reg_res[] = { + { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ + { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ + { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ + { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, + { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ + { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ + { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ + { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ + { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ + { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, + { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ + { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ + { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, + { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ + { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ + { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ + { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ + { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ + { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ + + { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ + { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ + { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, + { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ + { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ + { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ + { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ + { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ + { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ + { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ + { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ + { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, + { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ + { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, + { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ + { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ + { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, + { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ +}; + +/** + * m5mols_swap_byte - an byte array to integer conversion function + * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet + * + * Convert I2C data byte array with performing any required byte + * reordering to assure proper values for each data type, regardless + * of the architecture endianness. + */ +static u32 m5mols_swap_byte(u8 *data, u8 length) +{ + if (length == 1) + return *data; + else if (length == 2) + return be16_to_cpu(*((u16 *)data)); + else + return be32_to_cpu(*((u32 *)data)); +} + +/** + * m5mols_read - I2C read function + * @reg: combination of size, category and command for the I2C packet + * @val: read value + */ +int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; + u8 size = I2C_SIZE(reg); + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + struct i2c_msg msg[2]; + u8 wbuf[5]; + int ret; + + if (!client->adapter) + return -ENODEV; + + if (size != 1 && size != 2 && size != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 5; + msg[0].buf = wbuf; + wbuf[0] = 5; + wbuf[1] = M5MOLS_BYTE_READ; + wbuf[2] = category; + wbuf[3] = cmd; + wbuf[4] = size; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = size + 1; + msg[1].buf = rbuf; + + /* minimum stabilization time */ + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", + size, category, cmd, ret); + return ret; + } + + *val = m5mols_swap_byte(&rbuf[1], size); + + return 0; +} + +/** + * m5mols_write - I2C command write function + * @reg: combination of size, category and command for the I2C packet + * @val: value to write + */ +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; + u8 category = I2C_CATEGORY(reg); + u8 cmd = I2C_COMMAND(reg); + u8 size = I2C_SIZE(reg); + u32 *buf = (u32 *)&wbuf[4]; + struct i2c_msg msg[1]; + int ret; + + if (!client->adapter) + return -ENODEV; + + if (size != 1 && size != 2 && size != 4) { + v4l2_err(sd, "Wrong data size\n"); + return -EINVAL; + } + + msg->addr = client->addr; + msg->flags = 0; + msg->len = (u16)size + 4; + msg->buf = wbuf; + wbuf[0] = size + 4; + wbuf[1] = M5MOLS_BYTE_WRITE; + wbuf[2] = category; + wbuf[3] = cmd; + + *buf = m5mols_swap_byte((u8 *)&val, size); + + usleep_range(200, 200); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) { + v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n", + size, category, cmd, ret); + return ret; + } + + return 0; +} + +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask) +{ + u32 busy, i; + int ret; + + for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) { + ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy); + if (ret < 0) + return ret; + if ((busy & mask) == mask) + return 0; + } + return -EBUSY; +} + +/** + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts + * + * Before writing desired interrupt value the INT_FACTOR register should + * be read to clear pending interrupts. + */ +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg) +{ + struct m5mols_info *info = to_m5mols(sd); + u32 mask = is_available_af(info) ? REG_INT_AF : 0; + u32 dummy; + int ret; + + ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy); + if (!ret) + ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); + return ret; +} + +/** + * m5mols_reg_mode - Write the mode and check busy status + * + * It always accompanies a little delay changing the M-5MOLS mode, so it is + * needed checking current busy status to guarantee right mode. + */ +static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode) +{ + int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); + + return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode); +} + +/** + * m5mols_mode - manage the M-5MOLS's mode + * @mode: the required operation mode + * + * The commands of M-5MOLS are grouped into specific modes. Each functionality + * can be guaranteed only when the sensor is operating in mode which which + * a command belongs to. + */ +int m5mols_mode(struct m5mols_info *info, u32 mode) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + u32 reg; + + if (mode < REG_PARAMETER && mode > REG_CAPTURE) + return ret; + + ret = m5mols_read(sd, SYSTEM_SYSMODE, ®); + if ((!ret && reg == mode) || ret) + return ret; + + switch (reg) { + case REG_PARAMETER: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (!ret && mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_MONITOR: + if (mode == REG_PARAMETER) { + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + } + + ret = m5mols_reg_mode(sd, REG_CAPTURE); + break; + + case REG_CAPTURE: + ret = m5mols_reg_mode(sd, REG_MONITOR); + if (!ret && mode == REG_MONITOR) + break; + if (!ret) + ret = m5mols_reg_mode(sd, REG_PARAMETER); + break; + + default: + v4l2_warn(sd, "Wrong mode: %d\n", mode); + } + + if (!ret) + info->mode = mode; + + return ret; +} + +/** + * m5mols_get_version - retrieve full revisions information of M-5MOLS + * + * The version information includes revisions of hardware and firmware, + * AutoFocus alghorithm version and the version string. + */ +static int m5mols_get_version(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + union { + struct m5mols_version ver; + u8 bytes[VERSION_SIZE]; + } version; + u32 *value; + u8 cmd = CAT0_VER_CUSTOMER; + int ret; + + do { + value = (u32 *)&version.bytes[cmd]; + ret = m5mols_read(sd, SYSTEM_CMD(cmd), value); + if (ret) + return ret; + } while (cmd++ != CAT0_VER_AWB); + + do { + value = (u32 *)&version.bytes[cmd]; + ret = m5mols_read(sd, SYSTEM_VER_STRING, value); + if (ret) + return ret; + if (cmd >= VERSION_SIZE - 1) + return -EINVAL; + } while (version.bytes[cmd++]); + + value = (u32 *)&version.bytes[cmd]; + ret = m5mols_read(sd, AF_VERSION, value); + if (ret) + return ret; + + /* store version information swapped for being readable */ + info->ver = version.ver; + info->ver.fw = be16_to_cpu(info->ver.fw); + info->ver.hw = be16_to_cpu(info->ver.hw); + info->ver.param = be16_to_cpu(info->ver.param); + info->ver.awb = be16_to_cpu(info->ver.awb); + + v4l2_info(sd, "Manufacturer\t[%s]\n", + is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? + "Samsung Electro-Machanics" : + is_manufacturer(info, REG_SAMSUNG_OPTICS) ? + "Samsung Fiber-Optics" : + is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? + "Samsung Techwin" : "None"); + v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", + info->ver.customer, info->ver.project); + + if (!is_available_af(info)) + v4l2_info(sd, "No support Auto Focus on this firmware\n"); + + return ret; +} + +/** + * __find_restype - Lookup M-5MOLS resolution type according to pixel code + * @code: pixel code + */ +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) +{ + enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; + + do { + if (code == m5mols_default_ffmt[type].code) + return type; + } while (type++ != SIZE_DEFAULT_FFMT); + + return 0; +} + +/** + * __find_resolution - Lookup preset and type of M-5MOLS's resolution + * @mf: pixel format to find/negotiate the resolution preset for + * @type: M-5MOLS resolution type + * @resolution: M-5MOLS resolution preset register value + * + * Find nearest resolution matching resolution preset and adjust mf + * to supported values. + */ +static int __find_resolution(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf, + enum m5mols_restype *type, + u32 *resolution) +{ + const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; + const struct m5mols_resolution *match = NULL; + enum m5mols_restype stype = __find_restype(mf->code); + int i = ARRAY_SIZE(m5mols_reg_res); + unsigned int min_err = ~0; + + while (i--) { + int err; + if (stype == fsize->type) { + err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + + if (err < min_err) { + min_err = err; + match = fsize; + } + } + fsize++; + } + if (match) { + mf->width = match->width; + mf->height = match->height; + *resolution = match->reg; + *type = stype; + return 0; + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, + struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which, + enum m5mols_restype type) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + + return &info->ffmt[type]; +} + +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format; + + if (fmt->pad != 0) + return -EINVAL; + + format = __find_format(info, fh, fmt->which, info->res_type); + if (!format) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct m5mols_info *info = to_m5mols(sd); + struct v4l2_mbus_framefmt *format = &fmt->format; + struct v4l2_mbus_framefmt *sfmt; + enum m5mols_restype type; + u32 resolution = 0; + int ret; + + if (fmt->pad != 0) + return -EINVAL; + + ret = __find_resolution(sd, format, &type, &resolution); + if (ret < 0) + return ret; + + sfmt = __find_format(info, fh, fmt->which, type); + if (!sfmt) + return 0; + + *sfmt = m5mols_default_ffmt[type]; + sfmt->width = format->width; + sfmt->height = format->height; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + info->resolution = resolution; + info->code = format->code; + info->res_type = type; + } + + return 0; +} + +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (!code || code->index >= SIZE_DEFAULT_FFMT) + return -EINVAL; + + code->code = m5mols_default_ffmt[code->index].code; + + return 0; +} + +static struct v4l2_subdev_pad_ops m5mols_pad_ops = { + .enum_mbus_code = m5mols_enum_mbus_code, + .get_fmt = m5mols_get_fmt, + .set_fmt = m5mols_set_fmt, +}; + +/** + * m5mols_sync_controls - Apply default scene mode and the current controls + * + * This is used only streaming for syncing between v4l2_ctrl framework and + * m5mols's controls. First, do the scenemode to the sensor, then call + * v4l2_ctrl_handler_setup. It can be same between some commands and + * the scenemode's in the default v4l2_ctrls. But, such commands of control + * should be prior to the scenemode's one. + */ +int m5mols_sync_controls(struct m5mols_info *info) +{ + int ret = -EINVAL; + + if (!is_ctrl_synced(info)) { + ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); + if (ret) + return ret; + + v4l2_ctrl_handler_setup(&info->handle); + info->ctrl_sync = true; + } + + return ret; +} + +/** + * m5mols_start_monitor - Start the monitor mode + * + * Before applying the controls setup the resolution and frame rate + * in PARAMETER mode, and then switch over to MONITOR mode. + */ +static int m5mols_start_monitor(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + int ret; + + ret = m5mols_mode(info, REG_PARAMETER); + if (!ret) + ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); + if (!ret) + ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); + if (!ret) + ret = m5mols_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_sync_controls(info); + + return ret; +} + +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct m5mols_info *info = to_m5mols(sd); + + if (enable) { + int ret = -EINVAL; + + if (is_code(info->code, M5MOLS_RESTYPE_MONITOR)) + ret = m5mols_start_monitor(info); + if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE)) + ret = m5mols_start_capture(info); + + return ret; + } + + return m5mols_mode(info, REG_PARAMETER); +} + +static const struct v4l2_subdev_video_ops m5mols_video_ops = { + .s_stream = m5mols_s_stream, +}; + +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int ret; + + info->mode_save = info->mode; + + ret = m5mols_mode(info, REG_PARAMETER); + if (!ret) + ret = m5mols_set_ctrl(ctrl); + if (!ret) + ret = m5mols_mode(info, info->mode_save); + + return ret; +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .s_ctrl = m5mols_s_ctrl, +}; + +static int m5mols_sensor_power(struct m5mols_info *info, bool enable) +{ + struct v4l2_subdev *sd = &info->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + const struct m5mols_platform_data *pdata = info->pdata; + int ret; + + if (enable) { + if (is_powered(info)) + return 0; + + if (info->set_power) { + ret = info->set_power(&client->dev, 1); + if (ret) + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + if (ret) { + info->set_power(&client->dev, 0); + return ret; + } + + gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); + usleep_range(1000, 1000); + info->power = true; + + return ret; + } + + if (!is_powered(info)) + return 0; + + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + if (ret) + return ret; + + if (info->set_power) + info->set_power(&client->dev, 0); + + gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); + usleep_range(1000, 1000); + info->power = false; + + return ret; +} + +/* m5mols_update_fw - optional firmware update routine */ +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, + int (*set_power)(struct m5mols_info *, bool)) +{ + return 0; +} + +/** + * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core. + * + * Booting internal ARM core makes the M-5MOLS is ready for getting commands + * with I2C. It's the first thing to be done after it powered up. It must wait + * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting. + */ +static int m5mols_sensor_armboot(struct v4l2_subdev *sd) +{ + int ret; + + ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); + if (ret < 0) + return ret; + + msleep(520); + + ret = m5mols_get_version(sd); + if (!ret) + ret = m5mols_update_fw(sd, m5mols_sensor_power); + if (ret) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); + + ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); + if (!ret) + ret = m5mols_enable_interrupt(sd, REG_INT_AF); + + return ret; +} + +static int m5mols_init_controls(struct m5mols_info *info) +{ + struct v4l2_subdev *sd = &info->sd; + u16 max_exposure; + u16 step_zoom; + int ret; + + /* Determine value's range & step of controls for various FW version */ + ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure); + if (!ret) + step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + if (ret) + return ret; + + v4l2_ctrl_handler_init(&info->handle, 6); + info->autowb = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 0); + info->saturation = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_SATURATION, + 1, 5, 1, 3); + info->zoom = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, + 1, 70, step_zoom, 1); + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, max_exposure, 1, (int)max_exposure/2); + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_COLORFX, + 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); + info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, 0, V4L2_EXPOSURE_MANUAL); + + sd->ctrl_handler = &info->handle; + if (info->handle.error) { + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); + return info->handle.error; + } + + v4l2_ctrl_cluster(2, &info->autoexposure); + + return 0; +} + +/** + * m5mols_s_power - Main sensor power control function + * + * To prevent breaking the lens when the sensor is powered off the Soft-Landing + * algorithm is called where available. The Soft-Landing algorithm availability + * dependends on the firmware provider. + */ +static int m5mols_s_power(struct v4l2_subdev *sd, int on) +{ + struct m5mols_info *info = to_m5mols(sd); + int ret; + + if (on) { + ret = m5mols_sensor_power(info, true); + if (!ret) + ret = m5mols_sensor_armboot(sd); + if (!ret) + ret = m5mols_init_controls(info); + if (ret) + return ret; + + info->ffmt[M5MOLS_RESTYPE_MONITOR] = + m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR]; + info->ffmt[M5MOLS_RESTYPE_CAPTURE] = + m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE]; + return ret; + } + + if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { + ret = m5mols_mode(info, REG_MONITOR); + if (!ret) + ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); + if (!ret) + ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); + if (!ret) + ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS, + REG_AF_IDLE); + if (!ret) + v4l2_info(sd, "Success soft-landing lens\n"); + } + + ret = m5mols_sensor_power(info, false); + if (!ret) { + v4l2_ctrl_handler_free(&info->handle); + info->ctrl_sync = false; + } + + return ret; +} + +static int m5mols_log_status(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + + v4l2_ctrl_handler_log_status(&info->handle, sd->name); + + return 0; +} + +static const struct v4l2_subdev_core_ops m5mols_core_ops = { + .s_power = m5mols_s_power, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .log_status = m5mols_log_status, +}; + +static const struct v4l2_subdev_ops m5mols_ops = { + .core = &m5mols_core_ops, + .pad = &m5mols_pad_ops, + .video = &m5mols_video_ops, +}; + +static void m5mols_irq_work(struct work_struct *work) +{ + struct m5mols_info *info = + container_of(work, struct m5mols_info, work_irq); + struct v4l2_subdev *sd = &info->sd; + u32 reg; + int ret; + + if (!is_powered(info) || + m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt)) + return; + + switch (info->interrupt & REG_INT_MASK) { + case REG_INT_AF: + if (!is_available_af(info)) + break; + ret = m5mols_read(sd, AF_STATUS, ®); + v4l2_dbg(2, m5mols_debug, sd, "AF %s\n", + reg == REG_AF_FAIL ? "Failed" : + reg == REG_AF_SUCCESS ? "Success" : + reg == REG_AF_IDLE ? "Idle" : "Busy"); + break; + case REG_INT_CAPTURE: + if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags)) + wake_up_interruptible(&info->irq_waitq); + + v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n"); + break; + default: + v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg); + break; + }; +} + +static irqreturn_t m5mols_irq_handler(int irq, void *data) +{ + struct v4l2_subdev *sd = data; + struct m5mols_info *info = to_m5mols(sd); + + schedule_work(&info->work_irq); + + return IRQ_HANDLED; +} + +static int __devinit m5mols_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct m5mols_platform_data *pdata = client->dev.platform_data; + struct m5mols_info *info; + struct v4l2_subdev *sd; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + if (!gpio_is_valid(pdata->gpio_reset)) { + dev_err(&client->dev, "No valid RESET GPIO specified\n"); + return -EINVAL; + } + + if (!pdata->irq) { + dev_err(&client->dev, "Interrupt not assigned\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdata = pdata; + info->set_power = pdata->set_power; + + ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); + if (ret) { + dev_err(&client->dev, "Failed to request gpio: %d\n", ret); + goto out_free; + } + gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); + + ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators: %d\n", ret); + goto out_gpio; + } + + sd = &info->sd; + strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + v4l2_i2c_subdev_init(sd, client, &m5mols_ops); + + info->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + if (ret < 0) + goto out_reg; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + init_waitqueue_head(&info->irq_waitq); + INIT_WORK(&info->work_irq, m5mols_irq_work); + ret = request_irq(pdata->irq, m5mols_irq_handler, + IRQF_TRIGGER_RISING, MODULE_NAME, sd); + if (ret) { + dev_err(&client->dev, "Interrupt request failed: %d\n", ret); + goto out_me; + } + info->res_type = M5MOLS_RESTYPE_MONITOR; + return 0; +out_me: + media_entity_cleanup(&sd->entity); +out_reg: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); +out_gpio: + gpio_free(pdata->gpio_reset); +out_free: + kfree(info); + return ret; +} + +static int __devexit m5mols_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct m5mols_info *info = to_m5mols(sd); + + v4l2_device_unregister_subdev(sd); + free_irq(info->pdata->irq, sd); + + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + gpio_free(info->pdata->gpio_reset); + media_entity_cleanup(&sd->entity); + kfree(info); + return 0; +} + +static const struct i2c_device_id m5mols_id[] = { + { MODULE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, m5mols_id); + +static struct i2c_driver m5mols_i2c_driver = { + .driver = { + .name = MODULE_NAME, + }, + .probe = m5mols_probe, + .remove = __devexit_p(m5mols_remove), + .id_table = m5mols_id, +}; + +static int __init m5mols_mod_init(void) +{ + return i2c_add_driver(&m5mols_i2c_driver); +} + +static void __exit m5mols_mod_exit(void) +{ + i2c_del_driver(&m5mols_i2c_driver); +} + +module_init(m5mols_mod_init); +module_exit(m5mols_mod_exit); + +MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); +MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h new file mode 100644 index 000000000000..b83e36fc6ac6 --- /dev/null +++ b/drivers/media/video/m5mols/m5mols_reg.h @@ -0,0 +1,399 @@ +/* + * Register map for M-5MOLS 8M Pixel camera sensor with ISP + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Author: HeungJun Kim, riverful.kim@samsung.com + * + * Copyright (C) 2009 Samsung Electronics Co., Ltd. + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef M5MOLS_REG_H +#define M5MOLS_REG_H + +#define M5MOLS_I2C_MAX_SIZE 4 +#define M5MOLS_BYTE_READ 0x01 +#define M5MOLS_BYTE_WRITE 0x02 + +#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) +#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) +#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) +#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) + +/* + * Category section register + * + * The category means set including relevant command of M-5MOLS. + */ +#define CAT_SYSTEM 0x00 +#define CAT_PARAM 0x01 +#define CAT_MONITOR 0x02 +#define CAT_AE 0x03 +#define CAT_WB 0x06 +#define CAT_EXIF 0x07 +#define CAT_FD 0x09 +#define CAT_LENS 0x0a +#define CAT_CAPT_PARM 0x0b +#define CAT_CAPT_CTRL 0x0c +#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ + +/* + * Category 0 - SYSTEM mode + * + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole + * & all-round system of sensor. It deals with version/interrupt/setting mode & + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by + * packaging & manufacturer, even the customer and project code. And the + * function details may vary among them. The version information helps to + * determine what methods shall be used in the driver. + * + * There is many registers between customer version address and awb one. For + * more specific contents, see definition if file m5mols.h. + */ +#define CAT0_VER_CUSTOMER 0x00 /* customer version */ +#define CAT0_VER_AWB 0x09 /* Auto WB version */ +#define CAT0_VER_STRING 0x0a /* string including M-5MOLS */ +#define CAT0_SYSMODE 0x0b /* SYSTEM mode register */ +#define CAT0_STATUS 0x0c /* SYSTEM mode status register */ +#define CAT0_INT_FACTOR 0x10 /* interrupt pending register */ +#define CAT0_INT_ENABLE 0x11 /* interrupt enable register */ + +#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1) +#define REG_SYSINIT 0x00 /* SYSTEM mode */ +#define REG_PARAMETER 0x01 /* PARAMETER mode */ +#define REG_MONITOR 0x02 /* MONITOR mode */ +#define REG_CAPTURE 0x03 /* CAPTURE mode */ + +#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) +#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1) +#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ +#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ +#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ + +#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1) +#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1) +#define REG_INT_MODE (1 << 0) +#define REG_INT_AF (1 << 1) +#define REG_INT_ZOOM (1 << 2) +#define REG_INT_CAPTURE (1 << 3) +#define REG_INT_FRAMESYNC (1 << 4) +#define REG_INT_FD (1 << 5) +#define REG_INT_LENS_INIT (1 << 6) +#define REG_INT_SOUND (1 << 7) +#define REG_INT_MASK 0x0f + +/* + * category 1 - PARAMETER mode + * + * This category supports function of camera features of M-5MOLS. It means we + * can handle with preview(MONITOR) resolution size/frame per second/interface + * between the sensor and the Application Processor/even the image effect. + */ +#define CAT1_DATA_INTERFACE 0x00 /* interface between sensor and AP */ +#define CAT1_MONITOR_SIZE 0x01 /* resolution at the MONITOR mode */ +#define CAT1_MONITOR_FPS 0x02 /* frame per second at this mode */ +#define CAT1_EFFECT 0x0b /* image effects */ + +#define PARM_MON_SIZE I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1) + +#define PARM_MON_FPS I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1) +#define REG_FPS_30 0x02 + +#define PARM_INTERFACE I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1) +#define REG_INTERFACE_MIPI 0x02 + +#define PARM_EFFECT I2C_REG(CAT_PARAM, CAT1_EFFECT, 1) +#define REG_EFFECT_OFF 0x00 +#define REG_EFFECT_NEGA 0x01 +#define REG_EFFECT_EMBOSS 0x06 +#define REG_EFFECT_OUTLINE 0x07 +#define REG_EFFECT_WATERCOLOR 0x08 + +/* + * Category 2 - MONITOR mode + * + * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another + * mode named "Preview", but this preview mode is used at the case specific + * vider-recording mode. This mmode supports only YUYV format. On the other + * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are + * another options like zoom/color effect(different with effect in PARAMETER + * mode)/anti hand shaking algorithm. + */ +#define CAT2_ZOOM 0x01 /* set the zoom position & execute */ +#define CAT2_ZOOM_STEP 0x03 /* set the zoom step */ +#define CAT2_CFIXB 0x09 /* CB value for color effect */ +#define CAT2_CFIXR 0x0a /* CR value for color effect */ +#define CAT2_COLOR_EFFECT 0x0b /* set on/off of color effect */ +#define CAT2_CHROMA_LVL 0x0f /* set chroma level */ +#define CAT2_CHROMA_EN 0x10 /* set on/off of choroma */ +#define CAT2_EDGE_LVL 0x11 /* set sharpness level */ +#define CAT2_EDGE_EN 0x12 /* set on/off sharpness */ +#define CAT2_TONE_CTL 0x25 /* set tone color(contrast) */ + +#define MON_ZOOM I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1) + +#define MON_CFIXR I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1) +#define MON_CFIXB I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1) +#define REG_CFIXB_SEPIA 0xd8 +#define REG_CFIXR_SEPIA 0x18 + +#define MON_EFFECT I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1) +#define REG_COLOR_EFFECT_OFF 0x00 +#define REG_COLOR_EFFECT_ON 0x01 + +#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1) +#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1) +#define REG_CHROMA_OFF 0x00 +#define REG_CHROMA_ON 0x01 + +#define MON_EDGE_EN I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1) +#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1) +#define REG_EDGE_OFF 0x00 +#define REG_EDGE_ON 0x01 + +#define MON_TONE_CTL I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1) + +/* + * Category 3 - Auto Exposure + * + * The M-5MOLS exposure capbility is detailed as which is similar to digital + * camera. This category supports AE locking/various AE mode(range of exposure) + * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the + * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be + * different. So, this category also provide getting the max/min values. And, + * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. + */ +#define CAT3_AE_LOCK 0x00 /* locking Auto exposure */ +#define CAT3_AE_MODE 0x01 /* set AE mode, mode means range */ +#define CAT3_ISO 0x05 /* set ISO */ +#define CAT3_EV_PRESET_MONITOR 0x0a /* EV(scenemode) preset for MONITOR */ +#define CAT3_EV_PRESET_CAPTURE 0x0b /* EV(scenemode) preset for CAPTURE */ +#define CAT3_MANUAL_GAIN_MON 0x12 /* meteoring value for the MONITOR */ +#define CAT3_MAX_GAIN_MON 0x1a /* max gain value for the MONITOR */ +#define CAT3_MANUAL_GAIN_CAP 0x26 /* meteoring value for the CAPTURE */ +#define CAT3_AE_INDEX 0x38 /* AE index */ + +#define AE_LOCK I2C_REG(CAT_AE, CAT3_AE_LOCK, 1) +#define REG_AE_UNLOCK 0x00 +#define REG_AE_LOCK 0x01 + +#define AE_MODE I2C_REG(CAT_AE, CAT3_AE_MODE, 1) +#define REG_AE_OFF 0x00 /* AE off */ +#define REG_AE_ALL 0x01 /* calc AE in all block integral */ +#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ +#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ + +#define AE_ISO I2C_REG(CAT_AE, CAT3_ISO, 1) +#define REG_ISO_AUTO 0x00 +#define REG_ISO_50 0x01 +#define REG_ISO_100 0x02 +#define REG_ISO_200 0x03 +#define REG_ISO_400 0x04 +#define REG_ISO_800 0x05 + +#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1) +#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1) +#define REG_SCENE_NORMAL 0x00 +#define REG_SCENE_PORTRAIT 0x01 +#define REG_SCENE_LANDSCAPE 0x02 +#define REG_SCENE_SPORTS 0x03 +#define REG_SCENE_PARTY_INDOOR 0x04 +#define REG_SCENE_BEACH_SNOW 0x05 +#define REG_SCENE_SUNSET 0x06 +#define REG_SCENE_DAWN_DUSK 0x07 +#define REG_SCENE_FALL 0x08 +#define REG_SCENE_NIGHT 0x09 +#define REG_SCENE_AGAINST_LIGHT 0x0a +#define REG_SCENE_FIRE 0x0b +#define REG_SCENE_TEXT 0x0c +#define REG_SCENE_CANDLE 0x0d + +#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2) +#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2) +#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2) + +#define AE_INDEX I2C_REG(CAT_AE, CAT3_AE_INDEX, 1) +#define REG_AE_INDEX_20_NEG 0x00 +#define REG_AE_INDEX_15_NEG 0x01 +#define REG_AE_INDEX_10_NEG 0x02 +#define REG_AE_INDEX_05_NEG 0x03 +#define REG_AE_INDEX_00 0x04 +#define REG_AE_INDEX_05_POS 0x05 +#define REG_AE_INDEX_10_POS 0x06 +#define REG_AE_INDEX_15_POS 0x07 +#define REG_AE_INDEX_20_POS 0x08 + +/* + * Category 6 - White Balance + * + * This category provide AWB locking/mode/preset/speed/gain bias, etc. + */ +#define CAT6_AWB_LOCK 0x00 /* locking Auto Whitebalance */ +#define CAT6_AWB_MODE 0x02 /* set Auto or Manual */ +#define CAT6_AWB_MANUAL 0x03 /* set Manual(preset) value */ + +#define AWB_LOCK I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1) +#define REG_AWB_UNLOCK 0x00 +#define REG_AWB_LOCK 0x01 + +#define AWB_MODE I2C_REG(CAT_WB, CAT6_AWB_MODE, 1) +#define REG_AWB_AUTO 0x01 /* AWB off */ +#define REG_AWB_PRESET 0x02 /* AWB preset */ + +#define AWB_MANUAL I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1) +#define REG_AWB_INCANDESCENT 0x01 +#define REG_AWB_FLUORESCENT_1 0x02 +#define REG_AWB_FLUORESCENT_2 0x03 +#define REG_AWB_DAYLIGHT 0x04 +#define REG_AWB_CLOUDY 0x05 +#define REG_AWB_SHADE 0x06 +#define REG_AWB_HORIZON 0x07 +#define REG_AWB_LEDLIGHT 0x09 + +/* + * Category 7 - EXIF information + */ +#define CAT7_INFO_EXPTIME_NU 0x00 +#define CAT7_INFO_EXPTIME_DE 0x04 +#define CAT7_INFO_TV_NU 0x08 +#define CAT7_INFO_TV_DE 0x0c +#define CAT7_INFO_AV_NU 0x10 +#define CAT7_INFO_AV_DE 0x14 +#define CAT7_INFO_BV_NU 0x18 +#define CAT7_INFO_BV_DE 0x1c +#define CAT7_INFO_EBV_NU 0x20 +#define CAT7_INFO_EBV_DE 0x24 +#define CAT7_INFO_ISO 0x28 +#define CAT7_INFO_FLASH 0x2a +#define CAT7_INFO_SDR 0x2c +#define CAT7_INFO_QVAL 0x2e + +#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4) +#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4) +#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4) +#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4) +#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4) +#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4) +#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4) +#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4) +#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4) +#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4) +#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2) +#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2) +#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2) +#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2) + +/* + * Category 9 - Face Detection + */ +#define CAT9_FD_CTL 0x00 + +#define FD_CTL I2C_REG(CAT_FD, CAT9_FD_CTL, 1) +#define BIT_FD_EN 0 +#define BIT_FD_DRAW_FACE_FRAME 4 +#define BIT_FD_DRAW_SMILE_LVL 6 +#define REG_FD(shift) (1 << shift) +#define REG_FD_OFF 0x0 + +/* + * Category A - Lens Parameter + */ +#define CATA_AF_MODE 0x01 +#define CATA_AF_EXECUTE 0x02 +#define CATA_AF_STATUS 0x03 +#define CATA_AF_VERSION 0x0a + +#define AF_MODE I2C_REG(CAT_LENS, CATA_AF_MODE, 1) +#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ +#define REG_AF_MACRO 0x01 /* Macro AF, one time */ +#define REG_AF_POWEROFF 0x07 + +#define AF_EXECUTE I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1) +#define REG_AF_STOP 0x00 +#define REG_AF_EXE_AUTO 0x01 +#define REG_AF_EXE_CAF 0x02 + +#define AF_STATUS I2C_REG(CAT_LENS, CATA_AF_STATUS, 1) +#define REG_AF_FAIL 0x00 +#define REG_AF_SUCCESS 0x02 +#define REG_AF_IDLE 0x04 +#define REG_AF_BUSY 0x05 + +#define AF_VERSION I2C_REG(CAT_LENS, CATA_AF_VERSION, 1) + +/* + * Category B - CAPTURE Parameter + */ +#define CATB_YUVOUT_MAIN 0x00 +#define CATB_MAIN_IMAGE_SIZE 0x01 +#define CATB_MCC_MODE 0x1d +#define CATB_WDR_EN 0x2c +#define CATB_LIGHT_CTRL 0x40 +#define CATB_FLASH_CTRL 0x41 + +#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1) +#define REG_YUV422 0x00 +#define REG_BAYER10 0x05 +#define REG_BAYER8 0x06 +#define REG_JPEG 0x10 + +#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1) + +#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1) +#define REG_MCC_OFF 0x00 +#define REG_MCC_NORMAL 0x01 + +#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1) +#define REG_WDR_OFF 0x00 +#define REG_WDR_ON 0x01 +#define REG_WDR_AUTO 0x02 + +#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1) +#define REG_LIGHT_OFF 0x00 +#define REG_LIGHT_ON 0x01 +#define REG_LIGHT_AUTO 0x02 + +#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1) +#define REG_FLASH_OFF 0x00 +#define REG_FLASH_ON 0x01 +#define REG_FLASH_AUTO 0x02 + +/* + * Category C - CAPTURE Control + */ +#define CATC_CAP_MODE 0x00 +#define CATC_CAP_SEL_FRAME 0x06 /* It determines Single or Multi */ +#define CATC_CAP_START 0x09 +#define CATC_CAP_IMAGE_SIZE 0x0d +#define CATC_CAP_THUMB_SIZE 0x11 + +#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1) +#define REG_CAP_NONE 0x00 +#define REG_CAP_ANTI_SHAKE 0x02 + +#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1) + +#define CAPC_START I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1) +#define REG_CAP_START_MAIN 0x01 +#define REG_CAP_START_THUMB 0x03 + +#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1) +#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1) + +/* + * Category F - Flash + * + * This mode provides functions about internal flash stuff and system startup. + */ +#define CATF_CAM_START 0x12 /* It starts internal ARM core booting + * after power-up */ + +#define FLASH_CAM_START I2C_REG(CAT_FLASH, CATF_CAM_START, 1) +#define REG_START_ARM_BOOT 0x01 + +#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile index 968c1994eda0..2071ca8a2f03 100644 --- a/drivers/media/video/uvc/Makefile +++ b/drivers/media/video/uvc/Makefile @@ -1,3 +1,6 @@ uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ uvc_status.o uvc_isight.o +ifeq ($(CONFIG_MEDIA_CONTROLLER),y) +uvcvideo-objs += uvc_entity.o +endif obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 823f4b389745..b6eae48d7fb8 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -248,7 +248,7 @@ uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) * Terminal and unit management */ -static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) +struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) { struct uvc_entity *entity; @@ -795,9 +795,12 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, struct uvc_entity *entity; unsigned int num_inputs; unsigned int size; + unsigned int i; + extra_size = ALIGN(extra_size, sizeof(*entity->pads)); num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; - size = sizeof(*entity) + extra_size + num_inputs; + size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads + + num_inputs; entity = kzalloc(size, GFP_KERNEL); if (entity == NULL) return NULL; @@ -805,8 +808,17 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, entity->id = id; entity->type = type; + entity->num_links = 0; + entity->num_pads = num_pads; + entity->pads = ((void *)(entity + 1)) + extra_size; + + for (i = 0; i < num_inputs; ++i) + entity->pads[i].flags = MEDIA_PAD_FL_SINK; + if (!UVC_ENTITY_IS_OTERM(entity)) + entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; + entity->bNrInPins = num_inputs; - entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; + entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); return entity; } @@ -1585,6 +1597,13 @@ static void uvc_delete(struct uvc_device *dev) uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); + if (dev->vdev.dev) + v4l2_device_unregister(&dev->vdev); +#ifdef CONFIG_MEDIA_CONTROLLER + if (media_devnode_is_registered(&dev->mdev.devnode)) + media_device_unregister(&dev->mdev); +#endif + list_for_each_safe(p, n, &dev->chains) { struct uvc_video_chain *chain; chain = list_entry(p, struct uvc_video_chain, list); @@ -1594,6 +1613,13 @@ static void uvc_delete(struct uvc_device *dev) list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; entity = list_entry(p, struct uvc_entity, list); +#ifdef CONFIG_MEDIA_CONTROLLER + uvc_mc_cleanup_entity(entity); +#endif + if (entity->vdev) { + video_device_release(entity->vdev); + entity->vdev = NULL; + } kfree(entity); } @@ -1616,8 +1642,6 @@ static void uvc_release(struct video_device *vdev) struct uvc_streaming *stream = video_get_drvdata(vdev); struct uvc_device *dev = stream->dev; - video_device_release(vdev); - /* Decrement the registered streams count and delete the device when it * reaches zero. */ @@ -1682,7 +1706,7 @@ static int uvc_register_video(struct uvc_device *dev, * unregistered before the reference is released, so we don't need to * get another one. */ - vdev->parent = &dev->intf->dev; + vdev->v4l2_dev = &dev->vdev; vdev->fops = &uvc_fops; vdev->release = uvc_release; strlcpy(vdev->name, dev->name, sizeof vdev->name); @@ -1731,6 +1755,8 @@ static int uvc_register_terms(struct uvc_device *dev, ret = uvc_register_video(dev, stream); if (ret < 0) return ret; + + term->vdev = stream->vdev; } return 0; @@ -1745,6 +1771,14 @@ static int uvc_register_chains(struct uvc_device *dev) ret = uvc_register_terms(dev, chain); if (ret < 0) return ret; + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = uvc_mc_register_entities(chain); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to register entites " + "(%d).\n", ret); + } +#endif } return 0; @@ -1814,6 +1848,24 @@ static int uvc_probe(struct usb_interface *intf, "linux-uvc-devel mailing list.\n"); } + /* Register the media and V4L2 devices. */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &intf->dev; + strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); + if (udev->serial) + strlcpy(dev->mdev.serial, udev->serial, + sizeof(dev->mdev.serial)); + strcpy(dev->mdev.bus_info, udev->devpath); + dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + dev->mdev.driver_version = DRIVER_VERSION_NUMBER; + if (media_device_register(&dev->mdev) < 0) + goto error; + + dev->vdev.mdev = &dev->mdev; +#endif + if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) + goto error; + /* Initialize controls. */ if (uvc_ctrl_init_device(dev) < 0) goto error; @@ -1822,7 +1874,7 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_scan_device(dev) < 0) goto error; - /* Register video devices. */ + /* Register video device nodes. */ if (uvc_register_chains(dev) < 0) goto error; diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c new file mode 100644 index 000000000000..ede7852bb1df --- /dev/null +++ b/drivers/media/video/uvc/uvc_entity.c @@ -0,0 +1,118 @@ +/* + * uvc_entity.c -- USB Video Class driver + * + * Copyright (C) 2005-2011 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video subdevices registration and unregistration + */ + +static int uvc_mc_register_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) +{ + const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; + struct uvc_entity *remote; + unsigned int i; + u8 remote_pad; + int ret; + + for (i = 0; i < entity->num_pads; ++i) { + struct media_entity *source; + struct media_entity *sink; + + if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) + continue; + + remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); + if (remote == NULL) + return -EINVAL; + + source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) + ? &remote->vdev->entity : &remote->subdev.entity; + sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) + ? &entity->vdev->entity : &entity->subdev.entity; + + remote_pad = remote->num_pads - 1; + ret = media_entity_create_link(source, remote_pad, + sink, i, flags); + if (ret < 0) + return ret; + } + + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) + ret = v4l2_device_register_subdev(&chain->dev->vdev, + &entity->subdev); + + return ret; +} + +static struct v4l2_subdev_ops uvc_subdev_ops = { +}; + +void uvc_mc_cleanup_entity(struct uvc_entity *entity) +{ + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) + media_entity_cleanup(&entity->subdev.entity); + else if (entity->vdev != NULL) + media_entity_cleanup(&entity->vdev->entity); +} + +static int uvc_mc_init_entity(struct uvc_entity *entity) +{ + int ret; + + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { + v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); + strlcpy(entity->subdev.name, entity->name, + sizeof(entity->subdev.name)); + + ret = media_entity_init(&entity->subdev.entity, + entity->num_pads, entity->pads, 0); + } else + ret = media_entity_init(&entity->vdev->entity, + entity->num_pads, entity->pads, 0); + + return ret; +} + +int uvc_mc_register_entities(struct uvc_video_chain *chain) +{ + struct uvc_entity *entity; + int ret; + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_init_entity(entity); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to initialize entity for " + "entity %u\n", entity->id); + return ret; + } + } + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_register_entity(chain, entity); + if (ret < 0) { + uvc_printk(KERN_INFO, "Failed to register entity for " + "entity %u\n", entity->id); + return ret; + } + } + + return 0; +} diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 7cf224bae2e5..20107fd3574d 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -98,8 +98,11 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include <linux/poll.h> +#include <linux/usb.h> #include <linux/usb/video.h> #include <linux/uvcvideo.h> +#include <media/media-device.h> +#include <media/v4l2-device.h> /* -------------------------------------------------------------------------- * UVC constants @@ -301,6 +304,13 @@ struct uvc_entity { __u16 type; char name[64]; + /* Media controller-related fields. */ + struct video_device *vdev; + struct v4l2_subdev subdev; + unsigned int num_pads; + unsigned int num_links; + struct media_pad *pads; + union { struct { __u16 wObjectiveFocalLengthMin; @@ -504,6 +514,10 @@ struct uvc_device { atomic_t nmappings; /* Video control interface */ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif + struct v4l2_device vdev; __u16 uvc_version; __u32 clock_frequency; @@ -583,6 +597,8 @@ extern unsigned int uvc_timeout_param; /* Core driver */ extern struct uvc_driver uvc_driver; +extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); + /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, int drop_corrupted); @@ -616,6 +632,10 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) /* V4L2 interface */ extern const struct v4l2_file_operations uvc_fops; +/* Media controller */ +extern int uvc_mc_register_entities(struct uvc_video_chain *chain); +extern void uvc_mc_cleanup_entity(struct uvc_entity *entity); + /* Video */ extern int uvc_video_init(struct uvc_streaming *stream); extern int uvc_video_suspend(struct uvc_streaming *stream); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8344fc0ab858..b6c267724e14 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -719,6 +719,15 @@ config MFD_PM8XXX_IRQ This is required to use certain other PM 8xxx features, such as GPIO and MPP. +config MFD_TPS65910 + bool "TPS65910 Power Management chip" + depends on I2C=y + select MFD_CORE + select GPIO_TPS65910 + help + if you say yes here you get support for the TPS65910 series of + Power Management chips. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 1acb8f29a96c..efe3cc33ed92 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -93,3 +93,4 @@ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o +obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c new file mode 100644 index 000000000000..2bfad5c86cc7 --- /dev/null +++ b/drivers/mfd/tps65910-irq.c @@ -0,0 +1,218 @@ +/* + * tps65910-irq.c -- TI TPS6591x + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/mfd/tps65910.h> + +static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, + int irq) +{ + return (irq - tps65910->irq_base); +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. + */ +static irqreturn_t tps65910_irq(int irq, void *irq_data) +{ + struct tps65910 *tps65910 = irq_data; + u32 irq_sts; + u32 irq_mask; + u8 reg; + int i; + + tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); + irq_sts = reg; + tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); + irq_sts |= reg << 8; + switch (tps65910_chip_id(tps65910)) { + case TPS65911: + tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); + irq_sts |= reg << 16; + } + + tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + irq_mask = reg; + tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + irq_mask |= reg << 8; + switch (tps65910_chip_id(tps65910)) { + case TPS65911: + tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + irq_mask |= reg << 16; + } + + irq_sts &= ~irq_mask; + + if (!irq_sts) + return IRQ_NONE; + + for (i = 0; i < tps65910->irq_num; i++) { + + if (!(irq_sts & (1 << i))) + continue; + + handle_nested_irq(tps65910->irq_base + i); + } + + /* Write the STS register back to clear IRQs we handled */ + reg = irq_sts & 0xFF; + irq_sts >>= 8; + tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); + reg = irq_sts & 0xFF; + tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); + switch (tps65910_chip_id(tps65910)) { + case TPS65911: + reg = irq_sts >> 8; + tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); + } + + return IRQ_HANDLED; +} + +static void tps65910_irq_lock(struct irq_data *data) +{ + struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); + + mutex_lock(&tps65910->irq_lock); +} + +static void tps65910_irq_sync_unlock(struct irq_data *data) +{ + struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); + u32 reg_mask; + u8 reg; + + tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + reg_mask = reg; + tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + reg_mask |= reg << 8; + switch (tps65910_chip_id(tps65910)) { + case TPS65911: + tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + reg_mask |= reg << 16; + } + + if (tps65910->irq_mask != reg_mask) { + reg = tps65910->irq_mask & 0xFF; + tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); + reg = tps65910->irq_mask >> 8 & 0xFF; + tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); + switch (tps65910_chip_id(tps65910)) { + case TPS65911: + reg = tps65910->irq_mask >> 16; + tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); + } + } + mutex_unlock(&tps65910->irq_lock); +} + +static void tps65910_irq_enable(struct irq_data *data) +{ + struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); + + tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq)); +} + +static void tps65910_irq_disable(struct irq_data *data) +{ + struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); + + tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq)); +} + +static struct irq_chip tps65910_irq_chip = { + .name = "tps65910", + .irq_bus_lock = tps65910_irq_lock, + .irq_bus_sync_unlock = tps65910_irq_sync_unlock, + .irq_disable = tps65910_irq_disable, + .irq_enable = tps65910_irq_enable, +}; + +int tps65910_irq_init(struct tps65910 *tps65910, int irq, + struct tps65910_platform_data *pdata) +{ + int ret, cur_irq; + int flags = IRQF_ONESHOT; + + if (!irq) { + dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; + } + + if (!pdata || !pdata->irq_base) { + dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n"); + return -EINVAL; + } + + tps65910->irq_mask = 0xFFFFFF; + + mutex_init(&tps65910->irq_lock); + tps65910->chip_irq = irq; + tps65910->irq_base = pdata->irq_base; + + switch (tps65910_chip_id(tps65910)) { + case TPS65910: + tps65910->irq_num = TPS65910_NUM_IRQ; + case TPS65911: + tps65910->irq_num = TPS65911_NUM_IRQ; + } + + /* Register with genirq */ + for (cur_irq = tps65910->irq_base; + cur_irq < tps65910->irq_num + tps65910->irq_base; + cur_irq++) { + irq_set_chip_data(cur_irq, tps65910); + irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(irq, NULL, tps65910_irq, flags, + "tps65910", tps65910); + + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); + + if (ret != 0) + dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret); + + return ret; +} + +int tps65910_irq_exit(struct tps65910 *tps65910) +{ + free_irq(tps65910->chip_irq, tps65910); + return 0; +} diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c new file mode 100644 index 000000000000..2229e66d80db --- /dev/null +++ b/drivers/mfd/tps65910.c @@ -0,0 +1,229 @@ +/* + * tps65910.c -- TI TPS6591x + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps65910.h> + +static struct mfd_cell tps65910s[] = { + { + .name = "tps65910-pmic", + }, + { + .name = "tps65910-rtc", + }, + { + .name = "tps65910-power", + }, +}; + + +static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = tps65910->i2c_client; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = bytes; + xfer[1].buf = dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, + int bytes, void *src) +{ + struct i2c_client *i2c = tps65910->i2c_client; + /* we add 1 byte for device register */ + u8 msg[TPS65910_MAX_REGISTER + 1]; + int ret; + + if (bytes > TPS65910_MAX_REGISTER) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 1); + if (ret < 0) + return ret; + if (ret != bytes + 1) + return -EIO; + return 0; +} + +int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&tps65910->io_mutex); + err = tps65910_i2c_read(tps65910, reg, 1, &data); + if (err) { + dev_err(tps65910->dev, "read from reg %x failed\n", reg); + goto out; + } + + data |= mask; + err = tps65910_i2c_write(tps65910, reg, 1, &data); + if (err) + dev_err(tps65910->dev, "write to reg %x failed\n", reg); + +out: + mutex_unlock(&tps65910->io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(tps65910_set_bits); + +int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&tps65910->io_mutex); + err = tps65910_i2c_read(tps65910, reg, 1, &data); + if (err) { + dev_err(tps65910->dev, "read from reg %x failed\n", reg); + goto out; + } + + data &= mask; + err = tps65910_i2c_write(tps65910, reg, 1, &data); + if (err) + dev_err(tps65910->dev, "write to reg %x failed\n", reg); + +out: + mutex_unlock(&tps65910->io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(tps65910_clear_bits); + +static int tps65910_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tps65910 *tps65910; + struct tps65910_board *pmic_plat_data; + struct tps65910_platform_data *init_data; + int ret = 0; + + pmic_plat_data = dev_get_platdata(&i2c->dev); + if (!pmic_plat_data) + return -EINVAL; + + init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL); + if (init_data == NULL) + return -ENOMEM; + + init_data->irq = pmic_plat_data->irq; + init_data->irq_base = pmic_plat_data->irq; + + tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL); + if (tps65910 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, tps65910); + tps65910->dev = &i2c->dev; + tps65910->i2c_client = i2c; + tps65910->id = id->driver_data; + tps65910->read = tps65910_i2c_read; + tps65910->write = tps65910_i2c_write; + mutex_init(&tps65910->io_mutex); + + ret = mfd_add_devices(tps65910->dev, -1, + tps65910s, ARRAY_SIZE(tps65910s), + NULL, 0); + if (ret < 0) + goto err; + + tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); + + ret = tps65910_irq_init(tps65910, init_data->irq, init_data); + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(tps65910->dev); + kfree(tps65910); + return ret; +} + +static int tps65910_i2c_remove(struct i2c_client *i2c) +{ + struct tps65910 *tps65910 = i2c_get_clientdata(i2c); + + mfd_remove_devices(tps65910->dev); + kfree(tps65910); + + return 0; +} + +static const struct i2c_device_id tps65910_i2c_id[] = { + { "tps65910", TPS65910 }, + { "tps65911", TPS65911 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id); + + +static struct i2c_driver tps65910_i2c_driver = { + .driver = { + .name = "tps65910", + .owner = THIS_MODULE, + }, + .probe = tps65910_i2c_probe, + .remove = tps65910_i2c_remove, + .id_table = tps65910_i2c_id, +}; + +static int __init tps65910_i2c_init(void) +{ + return i2c_add_driver(&tps65910_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(tps65910_i2c_init); + +static void __exit tps65910_i2c_exit(void) +{ + i2c_del_driver(&tps65910_i2c_driver); +} +module_exit(tps65910_i2c_exit); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); +MODULE_DESCRIPTION("TPS6591x chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c new file mode 100644 index 000000000000..3d2dc56a3d40 --- /dev/null +++ b/drivers/mfd/tps65911-comparator.c @@ -0,0 +1,188 @@ +/* + * tps65910.c -- TI TPS6591x + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/gpio.h> +#include <linux/mfd/tps65910.h> + +#define COMP 0 +#define COMP1 1 +#define COMP2 2 + +/* Comparator 1 voltage selection table in milivolts */ +static const u16 COMP_VSEL_TABLE[] = { + 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650, + 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050, + 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450, + 3500, +}; + +struct comparator { + const char *name; + int reg; + int uV_max; + const u16 *vsel_table; +}; + +static struct comparator tps_comparators[] = { + { + .name = "COMP1", + .reg = TPS65911_VMBCH, + .uV_max = 3500, + .vsel_table = COMP_VSEL_TABLE, + }, + { + .name = "COMP2", + .reg = TPS65911_VMBCH2, + .uV_max = 3500, + .vsel_table = COMP_VSEL_TABLE, + }, +}; + +static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage) +{ + struct comparator tps_comp = tps_comparators[id]; + int curr_voltage = 0; + int ret; + u8 index = 0, val; + + if (id == COMP) + return 0; + + while (curr_voltage < tps_comp.uV_max) { + curr_voltage = tps_comp.vsel_table[index]; + if (curr_voltage >= voltage) + break; + else if (curr_voltage < voltage) + index ++; + } + + if (curr_voltage > tps_comp.uV_max) + return -EINVAL; + + val = index << 1; + ret = tps65910->write(tps65910, tps_comp.reg, 1, &val); + + return ret; +} + +static int comp_threshold_get(struct tps65910 *tps65910, int id) +{ + struct comparator tps_comp = tps_comparators[id]; + int ret; + u8 val; + + if (id == COMP) + return 0; + + ret = tps65910->read(tps65910, tps_comp.reg, 1, &val); + if (ret < 0) + return ret; + + val >>= 1; + return tps_comp.vsel_table[val]; +} + +static ssize_t comp_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tps65910 *tps65910 = dev_get_drvdata(dev->parent); + struct attribute comp_attr = attr->attr; + int id, uVolt; + + if (!strcmp(comp_attr.name, "comp1_threshold")) + id = COMP1; + else if (!strcmp(comp_attr.name, "comp2_threshold")) + id = COMP2; + else + return -EINVAL; + + uVolt = comp_threshold_get(tps65910, id); + + return sprintf(buf, "%d\n", uVolt); +} + +static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL); +static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL); + +static __devinit int tps65911_comparator_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct tps65910_platform_data *pdata = dev_get_platdata(tps65910->dev); + int ret; + + ret = comp_threshold_set(tps65910, COMP1, pdata->vmbch_threshold); + if (ret < 0) { + dev_err(&pdev->dev, "cannot set COMP1 threshold\n"); + return ret; + } + + ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold); + if (ret < 0) { + dev_err(&pdev->dev, "cannot set COMP2 theshold\n"); + return ret; + } + + /* Create sysfs entry */ + ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold); + if (ret < 0) + dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n"); + + ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold); + if (ret < 0) + dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n"); + + return ret; +} + +static __devexit int tps65911_comparator_remove(struct platform_device *pdev) +{ + struct tps65910 *tps65910; + + tps65910 = dev_get_drvdata(pdev->dev.parent); + + return 0; +} + +static struct platform_driver tps65911_comparator_driver = { + .driver = { + .name = "tps65911-comparator", + .owner = THIS_MODULE, + }, + .probe = tps65911_comparator_probe, + .remove = __devexit_p(tps65911_comparator_remove), +}; + +static int __init tps65911_comparator_init(void) +{ + return platform_driver_register(&tps65911_comparator_driver); +} +subsys_initcall(tps65911_comparator_init); + +static void __exit tps65911_comparator_exit(void) +{ + platform_driver_unregister(&tps65911_comparator_driver); +} +module_exit(tps65911_comparator_exit); + +MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); +MODULE_DESCRIPTION("TPS65911 comparator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65911-comparator"); diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 74f16f167b8e..b0c56313dbbb 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -285,33 +285,26 @@ static void hw_break_val_write(void) static int check_and_rewind_pc(char *put_str, char *arg) { unsigned long addr = lookup_addr(arg); + unsigned long ip; int offset = 0; kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); - v2printk("Stopped at IP: %lx\n", instruction_pointer(&kgdbts_regs)); -#ifdef CONFIG_X86 - /* On x86 a breakpoint stop requires it to be decremented */ - if (addr + 1 == kgdbts_regs.ip) - offset = -1; -#elif defined(CONFIG_SUPERH) - /* On SUPERH a breakpoint stop requires it to be decremented */ - if (addr + 2 == kgdbts_regs.pc) - offset = -2; + ip = instruction_pointer(&kgdbts_regs); + v2printk("Stopped at IP: %lx\n", ip); +#ifdef GDB_ADJUSTS_BREAK_OFFSET + /* On some arches, a breakpoint stop requires it to be decremented */ + if (addr + BREAK_INSTR_SIZE == ip) + offset = -BREAK_INSTR_SIZE; #endif - if (strcmp(arg, "silent") && - instruction_pointer(&kgdbts_regs) + offset != addr) { + if (strcmp(arg, "silent") && ip + offset != addr) { eprintk("kgdbts: BP mismatch %lx expected %lx\n", - instruction_pointer(&kgdbts_regs) + offset, addr); + ip + offset, addr); return 1; } -#ifdef CONFIG_X86 - /* On x86 adjust the instruction pointer if needed */ - kgdbts_regs.ip += offset; -#elif defined(CONFIG_SUPERH) - kgdbts_regs.pc += offset; -#endif + /* Readjust the instruction pointer if needed */ + instruction_pointer_set(&kgdbts_regs, ip + offset); return 0; } diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 4941e06fe2e1..5da5bea0f9f0 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -51,6 +51,7 @@ static unsigned int fmax = 515633; * is asserted (likewise for RX) * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register */ struct variant_data { unsigned int clkreg; @@ -60,6 +61,7 @@ struct variant_data { unsigned int fifohalfsize; bool sdio; bool st_clkdiv; + bool blksz_datactrl16; }; static struct variant_data variant_arm = { @@ -92,6 +94,17 @@ static struct variant_data variant_ux500 = { .st_clkdiv = true, }; +static struct variant_data variant_ux500v2 = { + .fifosize = 30 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .datalength_bits = 24, + .sdio = true, + .st_clkdiv = true, + .blksz_datactrl16 = true, +}; + /* * This must be called with host->lock held */ @@ -465,7 +478,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) blksz_bits = ffs(data->blksz) - 1; BUG_ON(1 << blksz_bits != data->blksz); - datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; + if (variant->blksz_datactrl16) + datactrl = MCI_DPSM_ENABLE | (data->blksz << 16); + else + datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; @@ -1311,9 +1327,14 @@ static struct amba_id mmci_ids[] = { }, { .id = 0x00480180, - .mask = 0x00ffffff, + .mask = 0xf0ffffff, .data = &variant_ux500, }, + { + .id = 0x10480180, + .mask = 0xf0ffffff, + .data = &variant_ux500v2, + }, { 0, 0 }, }; diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index bc50d5ea5534..4be8373d43e5 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -33,20 +33,6 @@ config MTD_TESTS should normally be compiled as kernel modules. The modules perform various checks and verifications when loaded. -config MTD_PARTITIONS - bool "MTD partitioning support" - help - If you have a device which needs to divide its flash chip(s) up - into multiple 'partitions', each of which appears to the user as - a separate MTD device, you require this option to be enabled. If - unsure, say 'Y'. - - Note, however, that you don't need this option for the DiskOnChip - devices. Partitioning on NFTL 'devices' is a different - that's the - 'normal' form of partitioning used on a block device. - -if MTD_PARTITIONS - config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" ---help--- @@ -99,7 +85,7 @@ endif # MTD_REDBOOT_PARTS config MTD_CMDLINE_PARTS bool "Command line partition table parsing" - depends on MTD_PARTITIONS = "y" && MTD = "y" + depends on MTD = "y" ---help--- Allow generic configuration of the MTD partition tables via the kernel command line. Multiple flash resources are supported for hardware where @@ -163,8 +149,6 @@ config MTD_AR7_PARTS ---help--- TI AR7 partitioning support -endif # MTD_PARTITIONS - comment "User Modules And Translation Layers" config MTD_CHAR diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index d578095fb255..39664c4229ff 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -4,8 +4,7 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o -mtd-y := mtdcore.o mtdsuper.o mtdconcat.o -mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtd-$(CONFIG_MTD_OF_PARTS) += ofpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 09cb7c8d93b4..e1e122f2f929 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -812,12 +812,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long break; if (time_after(jiffies, timeo)) { - /* Urgh. Resume and pretend we weren't here. */ - map_write(map, CMD(0xd0), adr); - /* Make sure we're in 'read status' mode if it had finished */ - map_write(map, CMD(0x70), adr); - chip->state = FL_ERASING; - chip->oldstate = FL_READY; + /* Urgh. Resume and pretend we weren't here. + * Make sure we're in 'read status' mode if it had finished */ + put_chip(map, chip, adr); printk(KERN_ERR "%s: Chip not ready after erase " "suspended: status = 0x%lx\n", map->name, status.x[0]); return -EIO; @@ -997,7 +994,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad switch(chip->oldstate) { case FL_ERASING: - chip->state = chip->oldstate; /* What if one interleaved chip has finished and the other hasn't? The old code would leave the finished one in READY mode. That's bad, and caused -EROFS diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 0b49266840b9..23175edd5634 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -462,13 +462,14 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) cfi_fixup_major_minor(cfi, extp); /* - * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4 + * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19 * http://www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf * http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf + * http://www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf */ if (extp->MajorVersion != '1' || - (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '4'))) { + (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '5'))) { printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " "version %c.%c (%#02x/%#02x).\n", extp->MajorVersion, extp->MinorVersion, @@ -710,9 +711,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * there was an error (so leave the erase * routine to recover from it) or we trying to * use the erase-in-progress sector. */ - map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr); - chip->state = FL_ERASING; - chip->oldstate = FL_READY; + put_chip(map, chip, adr); printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); return -EIO; } @@ -762,7 +761,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad switch(chip->oldstate) { case FL_ERASING: - chip->state = chip->oldstate; map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr); chip->oldstate = FL_READY; chip->state = FL_ERASING; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index ed56ad3884fb..179814a95f3a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -296,6 +296,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* make sure we're in 'read status' mode */ map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; + wake_up(&chip->wq); mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%lx\n", status.x[0]); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 97183c8c9e33..b78f23169d4e 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -294,7 +294,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.priv = dev; dev->mtd.owner = THIS_MODULE; - if (add_mtd_device(&dev->mtd)) { + if (mtd_device_register(&dev->mtd, NULL, 0)) { /* Device didn't get added, so free the entry */ goto devinit_err; } @@ -465,7 +465,7 @@ static void __devexit block2mtd_exit(void) list_for_each_safe(pos, next, &blkmtd_device_list) { struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); - del_mtd_device(&dev->mtd); + mtd_device_unregister(&dev->mtd); INFO("mtd%d: [%s] removed", dev->mtd.index, dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 5bf5f460e132..f7fbf6025ef2 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -597,7 +597,7 @@ void DoC2k_init(struct mtd_info *mtd) doc2klist = mtd; mtd->size = this->totlen; mtd->erasesize = this->erasesize; - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); return; } } @@ -1185,7 +1185,7 @@ static void __exit cleanup_doc2000(void) this = mtd->priv; doc2klist = this->nextdoc; - del_mtd_device(mtd); + mtd_device_unregister(mtd); iounmap(this->virtadr); kfree(this->chips); diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 0990f7803628..241192f05bc8 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -376,7 +376,7 @@ void DoCMil_init(struct mtd_info *mtd) this->nextdoc = docmillist; docmillist = mtd; mtd->size = this->totlen; - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); return; } } @@ -826,7 +826,7 @@ static void __exit cleanup_doc2001(void) this = mtd->priv; docmillist = this->nextdoc; - del_mtd_device(mtd); + mtd_device_unregister(mtd); iounmap(this->virtadr); kfree(this->chips); diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 8b36fa77a195..09ae0adc3ad0 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -499,7 +499,7 @@ void DoCMilPlus_init(struct mtd_info *mtd) docmilpluslist = mtd; mtd->size = this->totlen; mtd->erasesize = this->erasesize; - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); return; } } @@ -1091,7 +1091,7 @@ static void __exit cleanup_doc2001plus(void) this = mtd->priv; docmilpluslist = this->nextdoc; - del_mtd_device(mtd); + mtd_device_unregister(mtd); iounmap(this->virtadr); kfree(this->chips); diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 4b829f97d56c..772a0ff89e0f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -684,9 +684,10 @@ static int __init lart_flash_init (void) #endif #ifndef HAVE_PARTITIONS - result = add_mtd_device (&mtd); + result = mtd_device_register(&mtd, NULL, 0); #else - result = add_mtd_partitions (&mtd,lart_partitions, ARRAY_SIZE(lart_partitions)); + result = mtd_device_register(&mtd, lart_partitions, + ARRAY_SIZE(lart_partitions)); #endif return (result); @@ -695,9 +696,9 @@ static int __init lart_flash_init (void) static void __exit lart_flash_exit (void) { #ifndef HAVE_PARTITIONS - del_mtd_device (&mtd); + mtd_device_unregister(&mtd); #else - del_mtd_partitions (&mtd); + mtd_device_unregister(&mtd); #endif } diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 3fb981d4bb51..35180e475c4c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/mod_devicetable.h> +#include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -55,6 +56,9 @@ #define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ #define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ +/* Used for Spansion flashes only. */ +#define OPCODE_BRWR 0x17 /* Bank register write */ + /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ @@ -76,6 +80,8 @@ #define FAST_READ_DUMMY_BYTE 0 #endif +#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + /****************************************************************************/ struct m25p { @@ -158,11 +164,18 @@ static inline int write_disable(struct m25p *flash) /* * Enable/disable 4-byte addressing mode. */ -static inline int set_4byte(struct m25p *flash, int enable) +static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) { - u8 code = enable ? OPCODE_EN4B : OPCODE_EX4B; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; + return spi_write(flash->spi, flash->command, 1); + default: + /* Spansion style */ + flash->command[0] = OPCODE_BRWR; + flash->command[1] = enable << 7; + return spi_write(flash->spi, flash->command, 2); + } } /* @@ -668,6 +681,7 @@ static const struct spi_device_id m25p_ids[] = { /* Macronix */ { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, @@ -684,6 +698,10 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, @@ -729,7 +747,10 @@ static const struct spi_device_id m25p_ids[] = { { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, - { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, @@ -804,6 +825,8 @@ static int __devinit m25p_probe(struct spi_device *spi) struct m25p *flash; struct flash_info *info; unsigned i; + struct mtd_partition *parts = NULL; + int nr_parts = 0; /* Platform data helps sort out which chip type we have, as * well as how this board partitions it. If we don't have @@ -868,9 +891,9 @@ static int __devinit m25p_probe(struct spi_device *spi) * up with the software protection bits set */ - if (info->jedec_id >> 16 == 0x1f || - info->jedec_id >> 16 == 0x89 || - info->jedec_id >> 16 == 0xbf) { + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { write_enable(flash); write_sr(flash, 0); } @@ -888,7 +911,7 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.read = m25p80_read; /* sst flash chips use AAI word program */ - if (info->jedec_id >> 16 == 0xbf) + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) flash->mtd.write = sst_write; else flash->mtd.write = m25p80_write; @@ -914,7 +937,7 @@ static int __devinit m25p_probe(struct spi_device *spi) /* enable 4-byte addressing if the device exceeds 16MiB */ if (flash->mtd.size > 0x1000000) { flash->addr_width = 4; - set_4byte(flash, 1); + set_4byte(flash, info->jedec_id, 1); } else flash->addr_width = 3; } @@ -945,48 +968,41 @@ static int __devinit m25p_probe(struct spi_device *spi) /* partitions should match sector boundaries; and it may be good to * use readonly partitions for writeprotected sectors (BP2..BP0). */ - if (mtd_has_partitions()) { - struct mtd_partition *parts = NULL; - int nr_parts = 0; - - if (mtd_has_cmdlinepart()) { - static const char *part_probes[] - = { "cmdlinepart", NULL, }; + if (mtd_has_cmdlinepart()) { + static const char *part_probes[] + = { "cmdlinepart", NULL, }; - nr_parts = parse_mtd_partitions(&flash->mtd, - part_probes, &parts, 0); - } + nr_parts = parse_mtd_partitions(&flash->mtd, + part_probes, &parts, 0); + } - if (nr_parts <= 0 && data && data->parts) { - parts = data->parts; - nr_parts = data->nr_parts; - } + if (nr_parts <= 0 && data && data->parts) { + parts = data->parts; + nr_parts = data->nr_parts; + } #ifdef CONFIG_MTD_OF_PARTS - if (nr_parts <= 0 && spi->dev.of_node) { - nr_parts = of_mtd_parse_partitions(&spi->dev, - spi->dev.of_node, &parts); - } + if (nr_parts <= 0 && spi->dev.of_node) { + nr_parts = of_mtd_parse_partitions(&spi->dev, + spi->dev.of_node, &parts); + } #endif - if (nr_parts > 0) { - for (i = 0; i < nr_parts; i++) { - DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " - "{.name = %s, .offset = 0x%llx, " - ".size = 0x%llx (%lldKiB) }\n", - i, parts[i].name, - (long long)parts[i].offset, - (long long)parts[i].size, - (long long)(parts[i].size >> 10)); - } - flash->partitioned = 1; - return add_mtd_partitions(&flash->mtd, parts, nr_parts); + if (nr_parts > 0) { + for (i = 0; i < nr_parts; i++) { + DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " + "{.name = %s, .offset = 0x%llx, " + ".size = 0x%llx (%lldKiB) }\n", + i, parts[i].name, + (long long)parts[i].offset, + (long long)parts[i].size, + (long long)(parts[i].size >> 10)); } - } else if (data && data->nr_parts) - dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", - data->nr_parts, data->name); + flash->partitioned = 1; + } - return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0; + return mtd_device_register(&flash->mtd, parts, nr_parts) == 1 ? + -ENODEV : 0; } @@ -996,10 +1012,7 @@ static int __devexit m25p_remove(struct spi_device *spi) int status; /* Clean up MTD stuff. */ - if (mtd_has_partitions() && flash->partitioned) - status = del_mtd_partitions(&flash->mtd); - else - status = del_mtd_device(&flash->mtd); + status = mtd_device_unregister(&flash->mtd); if (status == 0) { kfree(flash->command); kfree(flash); diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 6a9a24a80a6d..8423fb6d4f26 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -220,7 +220,7 @@ static int __init ms02nv_init_one(ulong addr) mtd->writesize = 1; ret = -EIO; - if (add_mtd_device(mtd)) { + if (mtd_device_register(mtd, NULL, 0)) { printk(KERN_ERR "ms02-nv: Unable to register MTD device, aborting!\n"); goto err_out_csr_res; @@ -262,7 +262,7 @@ static void __exit ms02nv_remove_one(void) root_ms02nv_mtd = mp->next; - del_mtd_device(mtd); + mtd_device_unregister(mtd); release_resource(mp->resource.csr); kfree(mp->resource.csr); diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index c5015cc721d5..13749d458a31 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -637,6 +637,8 @@ add_dataflash_otp(struct spi_device *spi, char *name, struct flash_platform_data *pdata = spi->dev.platform_data; char *otp_tag = ""; int err = 0; + struct mtd_partition *parts; + int nr_parts = 0; priv = kzalloc(sizeof *priv, GFP_KERNEL); if (!priv) @@ -675,33 +677,25 @@ add_dataflash_otp(struct spi_device *spi, char *name, pagesize, otp_tag); dev_set_drvdata(&spi->dev, priv); - if (mtd_has_partitions()) { - struct mtd_partition *parts; - int nr_parts = 0; + if (mtd_has_cmdlinepart()) { + static const char *part_probes[] = { "cmdlinepart", NULL, }; - if (mtd_has_cmdlinepart()) { - static const char *part_probes[] - = { "cmdlinepart", NULL, }; - - nr_parts = parse_mtd_partitions(device, - part_probes, &parts, 0); - } + nr_parts = parse_mtd_partitions(device, part_probes, &parts, + 0); + } - if (nr_parts <= 0 && pdata && pdata->parts) { - parts = pdata->parts; - nr_parts = pdata->nr_parts; - } + if (nr_parts <= 0 && pdata && pdata->parts) { + parts = pdata->parts; + nr_parts = pdata->nr_parts; + } - if (nr_parts > 0) { - priv->partitioned = 1; - err = add_mtd_partitions(device, parts, nr_parts); - goto out; - } - } else if (pdata && pdata->nr_parts) - dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", - pdata->nr_parts, device->name); + if (nr_parts > 0) { + priv->partitioned = 1; + err = mtd_device_register(device, parts, nr_parts); + goto out; + } - if (add_mtd_device(device) == 1) + if (mtd_device_register(device, NULL, 0) == 1) err = -ENODEV; out: @@ -939,10 +933,7 @@ static int __devexit dataflash_remove(struct spi_device *spi) DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev)); - if (mtd_has_partitions() && flash->partitioned) - status = del_mtd_partitions(&flash->mtd); - else - status = del_mtd_device(&flash->mtd); + status = mtd_device_unregister(&flash->mtd); if (status == 0) { dev_set_drvdata(&spi->dev, NULL); kfree(flash); diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 1483e18971ce..2562689ba6b4 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -104,7 +104,7 @@ static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, static void __exit cleanup_mtdram(void) { if (mtd_info) { - del_mtd_device(mtd_info); + mtd_device_unregister(mtd_info); vfree(mtd_info->priv); kfree(mtd_info); } @@ -133,9 +133,8 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, mtd->read = ram_read; mtd->write = ram_write; - if (add_mtd_device(mtd)) { + if (mtd_device_register(mtd, NULL, 0)) return -EIO; - } return 0; } diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 8d28fa02a5a2..23423bd00b06 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -115,7 +115,7 @@ static void unregister_devices(void) struct phram_mtd_list *this, *safe; list_for_each_entry_safe(this, safe, &phram_list, list) { - del_mtd_device(&this->mtd); + mtd_device_unregister(&this->mtd); iounmap(this->mtd.priv); kfree(this->mtd.name); kfree(this); @@ -153,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) new->mtd.writesize = 1; ret = -EAGAIN; - if (add_mtd_device(&new->mtd)) { + if (mtd_device_register(&new->mtd, NULL, 0)) { pr_err("Failed to register new device\n"); goto out2; } diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 41b8cdcc64cb..ecff765579dd 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -798,7 +798,7 @@ static int __init init_pmc551(void) mtd->writesize = 1; mtd->owner = THIS_MODULE; - if (add_mtd_device(mtd)) { + if (mtd_device_register(mtd, NULL, 0)) { printk(KERN_NOTICE "pmc551: Failed to register new device\n"); pci_iounmap(PCI_Device, priv->start); kfree(mtd->priv); @@ -806,7 +806,7 @@ static int __init init_pmc551(void) break; } - /* Keep a reference as the add_mtd_device worked */ + /* Keep a reference as the mtd_device_register worked */ pci_dev_get(PCI_Device); printk(KERN_NOTICE "Registered pmc551 memory device.\n"); @@ -856,7 +856,7 @@ static void __exit cleanup_pmc551(void) pci_dev_put(priv->dev); kfree(mtd->priv); - del_mtd_device(mtd); + mtd_device_unregister(mtd); kfree(mtd); found++; } diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 592016a0668f..e585263161b9 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -210,7 +210,7 @@ static int register_device(char *name, unsigned long start, unsigned long length (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; (*curmtd)->mtdinfo->writesize = 1; - if (add_mtd_device((*curmtd)->mtdinfo)) { + if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); kfree((*curmtd)->mtdinfo->priv); @@ -231,7 +231,7 @@ static void unregister_devices(void) while (slram_mtdlist) { nextitem = slram_mtdlist->next; - del_mtd_device(slram_mtdlist->mtdinfo); + mtd_device_unregister(slram_mtdlist->mtdinfo); iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); kfree(slram_mtdlist->mtdinfo->priv); kfree(slram_mtdlist->mtdinfo); diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index c163e619abc9..1e2c430aaad2 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -66,7 +66,7 @@ struct flash_info { #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) -static struct flash_info __initdata sst25l_flash_info[] = { +static struct flash_info __devinitdata sst25l_flash_info[] = { {"sst25lf020a", 0xbf43, 256, 1024, 4096}, {"sst25lf040a", 0xbf44, 256, 2048, 4096}, }; @@ -381,6 +381,8 @@ static int __devinit sst25l_probe(struct spi_device *spi) struct sst25l_flash *flash; struct flash_platform_data *data; int ret, i; + struct mtd_partition *parts = NULL; + int nr_parts = 0; flash_info = sst25l_match_device(spi); if (!flash_info) @@ -420,46 +422,37 @@ static int __devinit sst25l_probe(struct spi_device *spi) flash->mtd.erasesize, flash->mtd.erasesize / 1024, flash->mtd.numeraseregions); - if (mtd_has_partitions()) { - struct mtd_partition *parts = NULL; - int nr_parts = 0; - if (mtd_has_cmdlinepart()) { - static const char *part_probes[] = - {"cmdlinepart", NULL}; + if (mtd_has_cmdlinepart()) { + static const char *part_probes[] = {"cmdlinepart", NULL}; - nr_parts = parse_mtd_partitions(&flash->mtd, - part_probes, - &parts, 0); - } + nr_parts = parse_mtd_partitions(&flash->mtd, + part_probes, + &parts, 0); + } - if (nr_parts <= 0 && data && data->parts) { - parts = data->parts; - nr_parts = data->nr_parts; - } + if (nr_parts <= 0 && data && data->parts) { + parts = data->parts; + nr_parts = data->nr_parts; + } - if (nr_parts > 0) { - for (i = 0; i < nr_parts; i++) { - DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " - "{.name = %s, .offset = 0x%llx, " - ".size = 0x%llx (%lldKiB) }\n", - i, parts[i].name, - (long long)parts[i].offset, - (long long)parts[i].size, - (long long)(parts[i].size >> 10)); - } - - flash->partitioned = 1; - return add_mtd_partitions(&flash->mtd, - parts, nr_parts); + if (nr_parts > 0) { + for (i = 0; i < nr_parts; i++) { + DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " + "{.name = %s, .offset = 0x%llx, " + ".size = 0x%llx (%lldKiB) }\n", + i, parts[i].name, + (long long)parts[i].offset, + (long long)parts[i].size, + (long long)(parts[i].size >> 10)); } - } else if (data && data->nr_parts) { - dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", - data->nr_parts, data->name); + flash->partitioned = 1; + return mtd_device_register(&flash->mtd, parts, + nr_parts); } - ret = add_mtd_device(&flash->mtd); + ret = mtd_device_register(&flash->mtd, NULL, 0); if (ret == 1) { kfree(flash); dev_set_drvdata(&spi->dev, NULL); @@ -469,15 +462,12 @@ static int __devinit sst25l_probe(struct spi_device *spi) return 0; } -static int __exit sst25l_remove(struct spi_device *spi) +static int __devexit sst25l_remove(struct spi_device *spi) { struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); int ret; - if (mtd_has_partitions() && flash->partitioned) - ret = del_mtd_partitions(&flash->mtd); - else - ret = del_mtd_device(&flash->mtd); + ret = mtd_device_unregister(&flash->mtd); if (ret == 0) kfree(flash); return ret; @@ -490,7 +480,7 @@ static struct spi_driver sst25l_driver = { .owner = THIS_MODULE, }, .probe = sst25l_probe, - .remove = __exit_p(sst25l_remove), + .remove = __devexit_p(sst25l_remove), }; static int __init sst25l_init(void) diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index 12679925b420..65655dd59e1f 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -313,12 +313,7 @@ static int chip_ready(struct map_info *map, struct flchip *chip, int mode) if (ret) { /* Oops. something got wrong. */ /* Resume and pretend we weren't here. */ - map_write(map, CMD(LPDDR_RESUME), - map->pfow_base + PFOW_COMMAND_CODE); - map_write(map, CMD(LPDDR_START_EXECUTION), - map->pfow_base + PFOW_COMMAND_EXECUTE); - chip->state = FL_ERASING; - chip->oldstate = FL_READY; + put_chip(map, chip); printk(KERN_ERR "%s: suspend operation failed." "State may be wrong \n", map->name); return -EIO; @@ -383,7 +378,6 @@ static void put_chip(struct map_info *map, struct flchip *chip) switch (chip->oldstate) { case FL_ERASING: - chip->state = chip->oldstate; map_write(map, CMD(LPDDR_RESUME), map->pfow_base + PFOW_COMMAND_CODE); map_write(map, CMD(LPDDR_START_EXECUTION), diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 5069111c81cc..c0c328c5b133 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -82,7 +82,6 @@ config MTD_PHYSMAP_OF config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI - select MTD_PARTITIONS help This provides a 'mapping' driver which supports the way in which user-programmable flash chips are connected on the @@ -122,7 +121,7 @@ config MTD_SC520CDP config MTD_NETSC520 tristate "CFI Flash device mapped on AMD NetSc520" - depends on X86 && MTD_CFI && MTD_PARTITIONS + depends on X86 && MTD_CFI help This enables access routines for the flash chips on the AMD NetSc520 demonstration board. If you have one of these boards and would like @@ -131,7 +130,6 @@ config MTD_NETSC520 config MTD_TS5500 tristate "JEDEC Flash device mapped on Technologic Systems TS-5500" depends on X86 - select MTD_PARTITIONS select MTD_JEDECPROBE select MTD_CFI_AMDSTD help @@ -149,7 +147,7 @@ config MTD_TS5500 config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" - depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS + depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS help This provides a driver for the on-board flash of Arcom Control Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX. @@ -161,7 +159,6 @@ config MTD_SBC_GXX config MTD_PXA2XX tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards" depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT - select MTD_PARTITIONS help This provides a driver for the NOR flash attached to a PXA2xx chip. @@ -185,7 +182,7 @@ config MTD_VMAX config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" - depends on SCx200 && MTD_CFI && MTD_PARTITIONS + depends on SCx200 && MTD_CFI help Enable support for a flash chip mapped using the DOCCS signal on a National Semiconductor SCx200 processor. @@ -247,7 +244,7 @@ config MTD_TSUNAMI config MTD_NETtel tristate "CFI flash device on SnapGear/SecureEdge" - depends on X86 && MTD_PARTITIONS && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE help Support for flash chips on NETtel/SecureEdge/SnapGear boards. @@ -269,7 +266,7 @@ config MTD_LANTIQ config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" - depends on X86 && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN + depends on X86 && MTD_CFI_INTELEXT && BROKEN help MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm> @@ -355,7 +352,7 @@ config MTD_CDB89712 config MTD_SA1100 tristate "CFI Flash device mapped on StrongARM SA11x0" - depends on MTD_CFI && ARCH_SA1100 && MTD_PARTITIONS + depends on MTD_CFI && ARCH_SA1100 help This enables access to the flash chips on most platforms based on the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. @@ -389,7 +386,7 @@ config MTD_IXP2000 config MTD_FORTUNET tristate "CFI Flash device mapped on the FortuNet board" - depends on MTD_CFI && MTD_PARTITIONS && SA1100_FORTUNET + depends on MTD_CFI && SA1100_FORTUNET help This enables access to the Flash on the FortuNet board. If you have such a board, say 'Y'. @@ -461,7 +458,6 @@ config MTD_PCMCIA_ANONYMOUS config MTD_BFIN_ASYNC tristate "Blackfin BF533-STAMP Flash Chip Support" depends on BFIN533_STAMP && MTD_CFI && MTD_COMPLEX_MAPPINGS - select MTD_PARTITIONS default y help Map driver which allows for simultaneous utilization of @@ -473,7 +469,6 @@ config MTD_GPIO_ADDR tristate "GPIO-assisted Flash Chip Support" depends on GENERIC_GPIO || GPIOLIB depends on MTD_COMPLEX_MAPPINGS - select MTD_PARTITIONS help Map driver which allows flashes to be partially physically addressed and assisted by GPIOs. @@ -482,14 +477,13 @@ config MTD_GPIO_ADDR config MTD_UCLINUX bool "Generic uClinux RAM/ROM filesystem support" - depends on MTD_PARTITIONS && MTD_RAM=y && !MMU + depends on MTD_RAM=y && !MMU help Map driver to support image based filesystems for uClinux. config MTD_WRSBC8260 tristate "Map driver for WindRiver PowerQUICC II MPC82xx board" depends on (SBC82xx || SBC8560) - select MTD_PARTITIONS select MTD_MAP_BANK_WIDTH_4 select MTD_MAP_BANK_WIDTH_1 select MTD_CFI_I1 @@ -502,7 +496,6 @@ config MTD_WRSBC8260 config MTD_DMV182 tristate "Map driver for Dy-4 SVME/DMV-182 board." depends on DMV182 - select MTD_PARTITIONS select MTD_MAP_BANK_WIDTH_32 select MTD_CFI_I8 select MTD_CFI_AMDSTD diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 92de7e3a49a5..e2875d6fe129 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -82,7 +82,7 @@ static void amd76xrom_cleanup(struct amd76xrom_window *window) if (map->rsrc.parent) { release_resource(&map->rsrc); } - del_mtd_device(map->mtd); + mtd_device_unregister(map->mtd); map_destroy(map->mtd); list_del(&map->list); kfree(map); @@ -262,7 +262,7 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, /* Now that the mtd devices is complete claim and export it */ map->mtd->owner = THIS_MODULE; - if (add_mtd_device(map->mtd)) { + if (mtd_device_register(map->mtd, NULL, 0)) { map_destroy(map->mtd); map->mtd = NULL; goto out; diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 53664188fc47..e5bfd0e093bb 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -88,7 +88,7 @@ map: sram_mtd->owner = THIS_MODULE; sram_mtd->erasesize = 16; - if (add_mtd_device(sram_mtd)) { + if (mtd_device_register(sram_mtd, NULL, 0)) { printk("NV-RAM device addition failed\n"); err = -ENOMEM; goto out_probe; @@ -111,7 +111,7 @@ out: static void __exit cleanup_autcpu12_maps(void) { if (sram_mtd) { - del_mtd_device(sram_mtd); + mtd_device_unregister(sram_mtd); map_destroy(sram_mtd); iounmap((void *)autcpu12_sram_map.virt); } diff --git a/drivers/mtd/maps/bcm963xx-flash.c b/drivers/mtd/maps/bcm963xx-flash.c index 1f3049590d9e..608967fe74c6 100644 --- a/drivers/mtd/maps/bcm963xx-flash.c +++ b/drivers/mtd/maps/bcm963xx-flash.c @@ -224,8 +224,8 @@ probe_ok: goto err_probe; } - return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts, - parsed_nr_parts); + return mtd_device_register(bcm963xx_mtd_info, parsed_parts, + parsed_nr_parts); err_probe: iounmap(bcm963xx_map.virt); @@ -235,7 +235,7 @@ err_probe: static int bcm963xx_remove(struct platform_device *pdev) { if (bcm963xx_mtd_info) { - del_mtd_partitions(bcm963xx_mtd_info); + mtd_device_unregister(bcm963xx_mtd_info); map_destroy(bcm963xx_mtd_info); } diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 85dd18193cf2..d4297a97e100 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -41,9 +41,7 @@ struct async_state { uint32_t flash_ambctl0, flash_ambctl1; uint32_t save_ambctl0, save_ambctl1; unsigned long irq_flags; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif }; static void switch_to_flash(struct async_state *state) @@ -124,9 +122,7 @@ static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const voi switch_back(state); } -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; -#endif static int __devinit bfin_flash_probe(struct platform_device *pdev) { @@ -169,22 +165,17 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) return -ENXIO; } -#ifdef CONFIG_MTD_PARTITIONS ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); if (ret > 0) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); - add_mtd_partitions(state->mtd, pdata->parts, ret); + mtd_device_register(state->mtd, pdata->parts, ret); state->parts = pdata->parts; - } else if (pdata->nr_parts) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); - add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); - - } else -#endif - { + mtd_device_register(state->mtd, pdata->parts, pdata->nr_parts); + } else { pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n"); - add_mtd_device(state->mtd); + mtd_device_register(state->mtd, NULL, 0); } platform_set_drvdata(pdev, state); @@ -196,10 +187,8 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev) { struct async_state *state = platform_get_drvdata(pdev); gpio_free(state->enet_flash_pin); -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(state->mtd); + mtd_device_unregister(state->mtd); kfree(state->parts); -#endif map_destroy(state->mtd); kfree(state); return 0; diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 8d92d8db9a98..c29cbf87ea0c 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -75,7 +75,7 @@ static int __init init_cdb89712_flash (void) flash_mtd->owner = THIS_MODULE; - if (add_mtd_device(flash_mtd)) { + if (mtd_device_register(flash_mtd, NULL, 0)) { printk("FLASH device addition failed\n"); err = -ENOMEM; goto out_probe; @@ -141,7 +141,7 @@ static int __init init_cdb89712_sram (void) sram_mtd->owner = THIS_MODULE; sram_mtd->erasesize = 16; - if (add_mtd_device(sram_mtd)) { + if (mtd_device_register(sram_mtd, NULL, 0)) { printk("SRAM device addition failed\n"); err = -ENOMEM; goto out_probe; @@ -209,7 +209,7 @@ static int __init init_cdb89712_bootrom (void) bootrom_mtd->owner = THIS_MODULE; bootrom_mtd->erasesize = 0x10000; - if (add_mtd_device(bootrom_mtd)) { + if (mtd_device_register(bootrom_mtd, NULL, 0)) { printk("BootROM device addition failed\n"); err = -ENOMEM; goto out_probe; @@ -249,21 +249,21 @@ static int __init init_cdb89712_maps(void) static void __exit cleanup_cdb89712_maps(void) { if (sram_mtd) { - del_mtd_device(sram_mtd); + mtd_device_unregister(sram_mtd); map_destroy(sram_mtd); iounmap((void *)cdb89712_sram_map.virt); release_resource (&cdb89712_sram_resource); } if (flash_mtd) { - del_mtd_device(flash_mtd); + mtd_device_unregister(flash_mtd); map_destroy(flash_mtd); iounmap((void *)cdb89712_flash_map.virt); release_resource (&cdb89712_flash_resource); } if (bootrom_mtd) { - del_mtd_device(bootrom_mtd); + mtd_device_unregister(bootrom_mtd); map_destroy(bootrom_mtd); iounmap((void *)cdb89712_bootrom_map.virt); release_resource (&cdb89712_bootrom_resource); diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 23f551dc8ca8..06f9c9815720 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -224,7 +224,7 @@ static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd { int i; - del_mtd_partitions(mtd); + mtd_device_unregister(mtd); if (mtd != clps[0].mtd) mtd_concat_destroy(mtd); @@ -292,11 +292,11 @@ static void __init clps_locate_partitions(struct mtd_info *mtd) if (nr_parts == 0) { printk(KERN_NOTICE "clps flash: no partition info " "available, registering whole flash\n"); - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); } else { printk(KERN_NOTICE "clps flash: using %s partition " "definition\n", part_type); - add_mtd_partitions(mtd, parsed_parts, nr_parts); + mtd_device_register(mtd, parsed_parts, nr_parts); } /* Always succeeds. */ diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index f71343cd77cc..d16fc9d3b8cd 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -107,7 +107,7 @@ static int __init init_flagadm(void) mymtd = do_map_probe("cfi_probe", &flagadm_map); if (mymtd) { mymtd->owner = THIS_MODULE; - add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT); + mtd_device_register(mymtd, flagadm_parts, PARTITION_COUNT); printk(KERN_NOTICE "FlagaDM flash device initialized\n"); return 0; } @@ -119,7 +119,7 @@ static int __init init_flagadm(void) static void __exit cleanup_flagadm(void) { if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (flagadm_map.virt) { diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 5fdb7b26cea3..3d0e762fa5f2 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -94,7 +94,7 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window) if (map->rsrc.parent) release_resource(&map->rsrc); - del_mtd_device(map->mtd); + mtd_device_unregister(map->mtd); map_destroy(map->mtd); list_del(&map->list); kfree(map); @@ -291,7 +291,7 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, /* Now that the mtd devices is complete claim and export it */ map->mtd->owner = THIS_MODULE; - if (add_mtd_device(map->mtd)) { + if (mtd_device_register(map->mtd, NULL, 0)) { map_destroy(map->mtd); map->mtd = NULL; goto out; diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index cfacfa6f45dd..85bdece6ab3f 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -93,7 +93,7 @@ static int __init init_dbox2_flash(void) mymtd->owner = THIS_MODULE; /* Create MTD devices for each partition. */ - add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); return 0; } @@ -105,7 +105,7 @@ static int __init init_dbox2_flash(void) static void __exit cleanup_dbox2_flash(void) { if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (dbox2_flash_map.virt) { diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index b3cb3a183809..7a9e1989c977 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -145,17 +145,13 @@ static struct map_info dc21285_map = { /* Partition stuff */ -#ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition *dc21285_parts; static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; -#endif static int __init init_dc21285(void) { -#ifdef CONFIG_MTD_PARTITIONS int nrparts; -#endif /* Determine bankwidth */ switch (*CSR_SA110_CNTL & (3<<14)) { @@ -204,13 +200,8 @@ static int __init init_dc21285(void) dc21285_mtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_PARTITIONS nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0); - if (nrparts > 0) - add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts); - else -#endif - add_mtd_device(dc21285_mtd); + mtd_device_register(dc21285_mtd, dc21285_parts, nrparts); if(machine_is_ebsa285()) { /* @@ -232,14 +223,9 @@ static int __init init_dc21285(void) static void __exit cleanup_dc21285(void) { -#ifdef CONFIG_MTD_PARTITIONS - if (dc21285_parts) { - del_mtd_partitions(dc21285_mtd); + mtd_device_unregister(dc21285_mtd); + if (dc21285_parts) kfree(dc21285_parts); - } else -#endif - del_mtd_device(dc21285_mtd); - map_destroy(dc21285_mtd); iounmap(dc21285_map.virt); } diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index 0713e3a5a22c..3e393f0da823 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -450,7 +450,7 @@ static int __init init_dnpc(void) partition_info[2].mtdp = &lowlvl_parts[1]; partition_info[3].mtdp = &lowlvl_parts[3]; - add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); /* ** now create a virtual MTD device by concatenating the for partitions @@ -463,7 +463,8 @@ static int __init init_dnpc(void) ** we do not supply mtd pointers in higlvl_partition_info, so ** add_mtd_partitions() will register the devices. */ - add_mtd_partitions(merged_mtd, higlvl_partition_info, NUM_HIGHLVL_PARTITIONS); + mtd_device_register(merged_mtd, higlvl_partition_info, + NUM_HIGHLVL_PARTITIONS); } return 0; @@ -472,12 +473,12 @@ static int __init init_dnpc(void) static void __exit cleanup_dnpc(void) { if(merged_mtd) { - del_mtd_partitions(merged_mtd); + mtd_device_unregister(merged_mtd); mtd_concat_destroy(merged_mtd); } if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (dnpc_map.virt) { diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c index d171674eb2ed..6538ac675e00 100644 --- a/drivers/mtd/maps/dmv182.c +++ b/drivers/mtd/maps/dmv182.c @@ -120,7 +120,7 @@ static int __init init_svme182(void) this_mtd->size >> 20, FLASH_BASE_ADDR); this_mtd->owner = THIS_MODULE; - add_mtd_partitions(this_mtd, partitions, num_parts); + mtd_device_register(this_mtd, partitions, num_parts); return 0; } @@ -129,7 +129,7 @@ static void __exit cleanup_svme182(void) { if (this_mtd) { - del_mtd_partitions(this_mtd); + mtd_device_unregister(this_mtd); map_destroy(this_mtd); } diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index be9e90b44587..fe42a212bb3e 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -15,10 +15,7 @@ #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> - -#ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> -#endif #define WINDOW_ADDR 0x00000000 /* physical properties of flash */ #define WINDOW_SIZE 0x01000000 @@ -40,8 +37,6 @@ struct map_info edb7312nor_map = { .phys = WINDOW_ADDR, }; -#ifdef CONFIG_MTD_PARTITIONS - /* * MTD partitioning stuff */ @@ -66,8 +61,6 @@ static struct mtd_partition static_partitions[3] = static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; -#endif - static int mtd_parts_nb = 0; static struct mtd_partition *mtd_parts = 0; @@ -96,27 +89,24 @@ static int __init init_edb7312nor(void) if (mymtd) { mymtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_PARTITIONS mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); if (mtd_parts_nb > 0) - part_type = "detected"; + part_type = "detected"; - if (mtd_parts_nb == 0) - { + if (mtd_parts_nb == 0) { mtd_parts = static_partitions; mtd_parts_nb = ARRAY_SIZE(static_partitions); part_type = "static"; } -#endif - add_mtd_device(mymtd); + if (mtd_parts_nb == 0) - printk(KERN_NOTICE MSG_PREFIX "no partition info available\n"); + printk(KERN_NOTICE MSG_PREFIX "no partition info available\n"); else - { printk(KERN_NOTICE MSG_PREFIX "using %s partition definition\n", part_type); - add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb); - } + /* Register the whole device first. */ + mtd_device_register(mymtd, NULL, 0); + mtd_device_register(mymtd, mtd_parts, mtd_parts_nb); return 0; } @@ -127,7 +117,7 @@ static int __init init_edb7312nor(void) static void __exit cleanup_edb7312nor(void) { if (mymtd) { - del_mtd_device(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (edb7312nor_map.virt) { diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index 4feb7507ab7c..08322b1c3e81 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -128,7 +128,7 @@ static void esb2rom_cleanup(struct esb2rom_window *window) list_for_each_entry_safe(map, scratch, &window->maps, list) { if (map->rsrc.parent) release_resource(&map->rsrc); - del_mtd_device(map->mtd); + mtd_device_unregister(map->mtd); map_destroy(map->mtd); list_del(&map->list); kfree(map); @@ -352,7 +352,7 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, /* Now that the mtd devices is complete claim and export it */ map->mtd->owner = THIS_MODULE; - if (add_mtd_device(map->mtd)) { + if (mtd_device_register(map->mtd, NULL, 0)) { map_destroy(map->mtd); map->mtd = NULL; goto out; diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 1e43124d498b..956e2e4f30ea 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -243,8 +243,9 @@ static int __init init_fortunet(void) &map_regions[ix].map_info); } map_regions[ix].mymtd->owner = THIS_MODULE; - add_mtd_partitions(map_regions[ix].mymtd, - map_regions[ix].parts,map_regions_parts[ix]); + mtd_device_register(map_regions[ix].mymtd, + map_regions[ix].parts, + map_regions_parts[ix]); } } if(iy) @@ -261,7 +262,7 @@ static void __exit cleanup_fortunet(void) { if( map_regions[ix].mymtd ) { - del_mtd_partitions( map_regions[ix].mymtd ); + mtd_device_unregister(map_regions[ix].mymtd); map_destroy( map_regions[ix].mymtd ); } iounmap((void *)map_regions[ix].map_info.virt); diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index af5707a80205..7568c5f8b8ae 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -155,9 +155,7 @@ static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, memcpy_toio(map->virt + (to % state->win_size), from, len); } -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; -#endif /** * gpio_flash_probe() - setup a mapping for a GPIO assisted flash @@ -189,7 +187,7 @@ static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; */ static int __devinit gpio_flash_probe(struct platform_device *pdev) { - int ret; + int nr_parts; size_t i, arr_size; struct physmap_flash_data *pdata; struct resource *memory; @@ -254,24 +252,21 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev) return -ENXIO; } -#ifdef CONFIG_MTD_PARTITIONS - ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); - if (ret > 0) { + nr_parts = parse_mtd_partitions(state->mtd, part_probe_types, + &pdata->parts, 0); + if (nr_parts > 0) { pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n"); - add_mtd_partitions(state->mtd, pdata->parts, ret); kfree(pdata->parts); - } else if (pdata->nr_parts) { pr_devinit(KERN_NOTICE PFX "Using board partition definition\n"); - add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); - - } else -#endif - { + nr_parts = pdata->nr_parts; + } else { pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n"); - add_mtd_device(state->mtd); + nr_parts = 0; } + mtd_device_register(state->mtd, pdata->parts, nr_parts); + return 0; } @@ -282,9 +277,7 @@ static int __devexit gpio_flash_remove(struct platform_device *pdev) do { gpio_free(state->gpio_addrs[i]); } while (++i < state->gpio_count); -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(state->mtd); -#endif + mtd_device_unregister(state->mtd); map_destroy(state->mtd); kfree(state); return 0; diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 72c724fa8c27..7f035860a36b 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -92,18 +92,16 @@ static int __init h720x_mtd_init(void) if (mymtd) { mymtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_PARTITIONS nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); if (nr_mtd_parts > 0) part_type = "command line"; -#endif if (nr_mtd_parts <= 0) { mtd_parts = h720x_partitions; nr_mtd_parts = NUM_PARTITIONS; part_type = "builtin"; } printk(KERN_INFO "Using %s partition table\n", part_type); - add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); + mtd_device_register(mymtd, mtd_parts, nr_mtd_parts); return 0; } @@ -118,7 +116,7 @@ static void __exit h720x_mtd_cleanup(void) { if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 1337a4191a0c..6689dcb3124d 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -67,7 +67,7 @@ static void ichxrom_cleanup(struct ichxrom_window *window) list_for_each_entry_safe(map, scratch, &window->maps, list) { if (map->rsrc.parent) release_resource(&map->rsrc); - del_mtd_device(map->mtd); + mtd_device_unregister(map->mtd); map_destroy(map->mtd); list_del(&map->list); kfree(map); @@ -287,7 +287,7 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, /* Now that the mtd devices is complete claim and export it */ map->mtd->owner = THIS_MODULE; - if (add_mtd_device(map->mtd)) { + if (mtd_device_register(map->mtd, NULL, 0)) { map_destroy(map->mtd); map->mtd = NULL; goto out; diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 998a27da97f3..404a50cbafa0 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -15,10 +15,7 @@ #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> - -#ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> -#endif #define WINDOW_ADDR0 0x00000000 /* physical properties of flash */ #define WINDOW_SIZE0 0x00800000 @@ -49,8 +46,6 @@ static struct map_info impa7_map[NUM_FLASHBANKS] = { }, }; -#ifdef CONFIG_MTD_PARTITIONS - /* * MTD partitioning stuff */ @@ -66,8 +61,6 @@ static struct mtd_partition static_partitions[] = static int mtd_parts_nb[NUM_FLASHBANKS]; static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; -#endif - static const char *probes[] = { "cmdlinepart", NULL }; static int __init init_impa7(void) @@ -104,7 +97,6 @@ static int __init init_impa7(void) if (impa7_mtd[i]) { impa7_mtd[i]->owner = THIS_MODULE; devicesfound++; -#ifdef CONFIG_MTD_PARTITIONS mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], probes, &mtd_parts[i], @@ -120,12 +112,8 @@ static int __init init_impa7(void) printk(KERN_NOTICE MSG_PREFIX "using %s partition definition\n", part_type); - add_mtd_partitions(impa7_mtd[i], - mtd_parts[i], mtd_parts_nb[i]); -#else - add_mtd_device(impa7_mtd[i]); - -#endif + mtd_device_register(impa7_mtd[i], + mtd_parts[i], mtd_parts_nb[i]); } else iounmap((void *)impa7_map[i].virt); @@ -138,11 +126,7 @@ static void __exit cleanup_impa7(void) int i; for (i=0; i<NUM_FLASHBANKS; i++) { if (impa7_mtd[i]) { -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(impa7_mtd[i]); -#else - del_mtd_device(impa7_mtd[i]); -#endif + mtd_device_unregister(impa7_mtd[i]); map_destroy(impa7_mtd[i]); iounmap((void *)impa7_map[i].virt); impa7_map[i].virt = 0; diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index fc1998512eb4..d2f47be8754b 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -66,33 +66,18 @@ struct vr_nor_mtd { static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p) { - if (p->nr_parts > 0) { -#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) - del_mtd_partitions(p->info); -#endif - } else - del_mtd_device(p->info); + mtd_device_unregister(p->info); } static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p) { - int err = 0; -#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) struct mtd_partition *parts; static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /* register the flash bank */ -#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) /* partition the flash bank */ p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0); - if (p->nr_parts > 0) - err = add_mtd_partitions(p->info, parts, p->nr_parts); -#endif - if (p->nr_parts <= 0) - err = add_mtd_device(p->info); - - return err; + return mtd_device_register(p->info, parts, p->nr_parts); } static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p) diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index 9639d83a9d6c..c00b9175ba9e 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -119,7 +119,7 @@ static int ixp2000_flash_remove(struct platform_device *dev) return 0; if (info->mtd) { - del_mtd_partitions(info->mtd); + mtd_device_unregister(info->mtd); map_destroy(info->mtd); } if (info->map.map_priv_1) @@ -230,7 +230,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); if (err > 0) { - err = add_mtd_partitions(info->mtd, info->partitions, err); + err = mtd_device_register(info->mtd, info->partitions, err); if(err) dev_err(&dev->dev, "Could not parse partitions\n"); } diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 1f9fde0dad35..155b21942f47 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -162,7 +162,7 @@ static int ixp4xx_flash_remove(struct platform_device *dev) return 0; if (info->mtd) { - del_mtd_partitions(info->mtd); + mtd_device_unregister(info->mtd); map_destroy(info->mtd); } if (info->map.virt) @@ -252,10 +252,8 @@ static int ixp4xx_flash_probe(struct platform_device *dev) /* Use the fast version */ info->map.write = ixp4xx_write16; -#ifdef CONFIG_MTD_PARTITIONS nr_parts = parse_mtd_partitions(info->mtd, probes, &info->partitions, dev->resource->start); -#endif if (nr_parts > 0) { part_type = "dynamic"; } else { @@ -263,18 +261,16 @@ static int ixp4xx_flash_probe(struct platform_device *dev) nr_parts = plat->nr_parts; part_type = "static"; } - if (nr_parts == 0) { + if (nr_parts == 0) printk(KERN_NOTICE "IXP4xx flash: no partition info " "available, registering whole flash\n"); - err = add_mtd_device(info->mtd); - } else { + else printk(KERN_NOTICE "IXP4xx flash: using %s partition " "definition\n", part_type); - err = add_mtd_partitions(info->mtd, info->partitions, nr_parts); - if(err) - printk(KERN_ERR "Could not parse partitions\n"); - } + err = mtd_device_register(info->mtd, info->partitions, nr_parts); + if (err) + printk(KERN_ERR "Could not parse partitions\n"); if (err) goto Error; diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 9e054503c4cf..dd0360ba2412 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -138,7 +138,7 @@ static int __init init_l440gx(void) if (mymtd) { mymtd->owner = THIS_MODULE; - add_mtd_device(mymtd); + mtd_device_register(mymtd, NULL, 0); return 0; } @@ -148,7 +148,7 @@ static int __init init_l440gx(void) static void __exit cleanup_l440gx(void) { - del_mtd_device(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); iounmap(l440gx_map.virt); diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index ee2548085334..5936c466e901 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -112,18 +112,9 @@ static int latch_addr_flash_remove(struct platform_device *dev) latch_addr_data = dev->dev.platform_data; if (info->mtd != NULL) { - if (mtd_has_partitions()) { - if (info->nr_parts) { - del_mtd_partitions(info->mtd); - kfree(info->parts); - } else if (latch_addr_data->nr_parts) { - del_mtd_partitions(info->mtd); - } else { - del_mtd_device(info->mtd); - } - } else { - del_mtd_device(info->mtd); - } + if (info->nr_parts) + kfree(info->parts); + mtd_device_unregister(info->mtd); map_destroy(info->mtd); } @@ -215,23 +206,21 @@ static int __devinit latch_addr_flash_probe(struct platform_device *dev) } info->mtd->owner = THIS_MODULE; - if (mtd_has_partitions()) { - - err = parse_mtd_partitions(info->mtd, - (const char **)part_probe_types, - &info->parts, 0); - if (err > 0) { - add_mtd_partitions(info->mtd, info->parts, err); - return 0; - } - if (latch_addr_data->nr_parts) { - pr_notice("Using latch-addr-flash partition information\n"); - add_mtd_partitions(info->mtd, latch_addr_data->parts, - latch_addr_data->nr_parts); - return 0; - } + err = parse_mtd_partitions(info->mtd, (const char **)part_probe_types, + &info->parts, 0); + if (err > 0) { + mtd_device_register(info->mtd, info->parts, err); + return 0; + } + if (latch_addr_data->nr_parts) { + pr_notice("Using latch-addr-flash partition information\n"); + mtd_device_register(info->mtd, + latch_addr_data->parts, + latch_addr_data->nr_parts); + return 0; } - add_mtd_device(info->mtd); + + mtd_device_register(info->mtd, NULL, 0); return 0; iounmap: diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 0eb5a7c85380..93fa56c33003 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -69,8 +69,8 @@ static int __init init_mbx(void) mymtd = do_map_probe("jedec_probe", &mbx_map); if (mymtd) { mymtd->owner = THIS_MODULE; - add_mtd_device(mymtd); - add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + mtd_device_register(mymtd, NULL, 0); + mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); return 0; } @@ -81,7 +81,7 @@ static int __init init_mbx(void) static void __exit cleanup_mbx(void) { if (mymtd) { - del_mtd_device(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (mbx_map.virt) { diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index c0cb319b2b70..81dc2598bc0a 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -116,14 +116,14 @@ static int __init init_netsc520(void) } mymtd->owner = THIS_MODULE; - add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); + mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); return 0; } static void __exit cleanup_netsc520(void) { if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (netsc520_map.virt) { diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index a97133eb9d70..eadcfffc4f9c 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -383,13 +383,13 @@ static int __init nettel_init(void) /* No BIOS regions when AMD boot */ num_intel_partitions -= 2; } - rc = add_mtd_partitions(intel_mtd, nettel_intel_partitions, - num_intel_partitions); + rc = mtd_device_register(intel_mtd, nettel_intel_partitions, + num_intel_partitions); #endif if (amd_mtd) { - rc = add_mtd_partitions(amd_mtd, nettel_amd_partitions, - num_amd_partitions); + rc = mtd_device_register(amd_mtd, nettel_amd_partitions, + num_amd_partitions); } #ifdef CONFIG_MTD_CFI_INTELEXT @@ -419,7 +419,7 @@ static void __exit nettel_cleanup(void) unregister_reboot_notifier(&nettel_notifier_block); #endif if (amd_mtd) { - del_mtd_partitions(amd_mtd); + mtd_device_unregister(amd_mtd); map_destroy(amd_mtd); } if (nettel_mmcrp) { @@ -432,7 +432,7 @@ static void __exit nettel_cleanup(void) } #ifdef CONFIG_MTD_CFI_INTELEXT if (intel_mtd) { - del_mtd_partitions(intel_mtd); + mtd_device_unregister(intel_mtd); map_destroy(intel_mtd); } if (nettel_intel_map.virt) { diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index 23fe1786770f..807ac2a2e686 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -175,7 +175,7 @@ void cleanup_oct5066(void) int i; for (i=0; i<2; i++) { if (oct5066_mtd[i]) { - del_mtd_device(oct5066_mtd[i]); + mtd_device_unregister(oct5066_mtd[i]); map_destroy(oct5066_mtd[i]); } } @@ -220,7 +220,7 @@ static int __init init_oct5066(void) oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); if (oct5066_mtd[i]) { oct5066_mtd[i]->owner = THIS_MODULE; - add_mtd_device(oct5066_mtd[i]); + mtd_device_register(oct5066_mtd[i], NULL, 0); } } diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 48f4cf5cb9d1..1d005a3e9b41 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -313,7 +313,7 @@ mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto release; mtd->owner = THIS_MODULE; - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); pci_set_drvdata(dev, mtd); @@ -336,7 +336,7 @@ mtd_pci_remove(struct pci_dev *dev) struct mtd_info *mtd = pci_get_drvdata(dev); struct map_pci_info *map = mtd->priv; - del_mtd_device(mtd); + mtd_device_unregister(mtd); map_destroy(mtd); map->exit(dev, map); kfree(map); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 33dc2829b01b..bbe168b65c26 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -630,7 +630,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) dev->pcmcia_map.copy_to = pcmcia_copy_to; } - if(add_mtd_device(mtd)) { + if (mtd_device_register(mtd, NULL, 0)) { map_destroy(mtd); dev->mtd_info = NULL; dev_err(&dev->p_dev->dev, @@ -669,7 +669,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link) DEBUG(3, "link=0x%p", link); if(dev->mtd_info) { - del_mtd_device(dev->mtd_info); + mtd_device_unregister(dev->mtd_info); dev_info(&dev->p_dev->dev, "mtd%d: Removing\n", dev->mtd_info->index); map_destroy(dev->mtd_info); diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 1a9b94f0ee54..f64cee4a3bfb 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -27,10 +27,8 @@ struct physmap_flash_info { struct mtd_info *mtd[MAX_RESOURCES]; struct mtd_info *cmtd; struct map_info map[MAX_RESOURCES]; -#ifdef CONFIG_MTD_PARTITIONS int nr_parts; struct mtd_partition *parts; -#endif }; static int physmap_flash_remove(struct platform_device *dev) @@ -47,18 +45,9 @@ static int physmap_flash_remove(struct platform_device *dev) physmap_data = dev->dev.platform_data; if (info->cmtd) { -#ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts || physmap_data->nr_parts) { - del_mtd_partitions(info->cmtd); - - if (info->nr_parts) - kfree(info->parts); - } else { - del_mtd_device(info->cmtd); - } -#else - del_mtd_device(info->cmtd); -#endif + mtd_device_unregister(info->cmtd); + if (info->nr_parts) + kfree(info->parts); if (info->cmtd != info->mtd[0]) mtd_concat_destroy(info->cmtd); } @@ -92,10 +81,8 @@ static const char *rom_probe_types[] = { "qinfo_probe", "map_rom", NULL }; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -#endif static int physmap_flash_probe(struct platform_device *dev) { @@ -188,24 +175,23 @@ static int physmap_flash_probe(struct platform_device *dev) if (err) goto err_out; -#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(info->cmtd, part_probe_types, - &info->parts, 0); + &info->parts, 0); if (err > 0) { - add_mtd_partitions(info->cmtd, info->parts, err); + mtd_device_register(info->cmtd, info->parts, err); info->nr_parts = err; return 0; } if (physmap_data->nr_parts) { printk(KERN_NOTICE "Using physmap partition information\n"); - add_mtd_partitions(info->cmtd, physmap_data->parts, - physmap_data->nr_parts); + mtd_device_register(info->cmtd, physmap_data->parts, + physmap_data->nr_parts); return 0; } -#endif - add_mtd_device(info->cmtd); + mtd_device_register(info->cmtd, NULL, 0); + return 0; err_out: @@ -269,14 +255,12 @@ void physmap_configure(unsigned long addr, unsigned long size, physmap_flash_data.set_vpp = set_vpp; } -#ifdef CONFIG_MTD_PARTITIONS void physmap_set_partitions(struct mtd_partition *parts, int num_parts) { physmap_flash_data.nr_parts = num_parts; physmap_flash_data.parts = parts; } #endif -#endif static int __init physmap_init(void) { diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index c1d33464aee8..d251d1db129b 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -34,16 +34,12 @@ struct of_flash_list { struct of_flash { struct mtd_info *cmtd; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif int list_size; /* number of elements in of_flash_list */ struct of_flash_list list[0]; }; -#ifdef CONFIG_MTD_PARTITIONS #define OF_FLASH_PARTS(info) ((info)->parts) - static int parse_obsolete_partitions(struct platform_device *dev, struct of_flash *info, struct device_node *dp) @@ -89,10 +85,6 @@ static int parse_obsolete_partitions(struct platform_device *dev, return nr_parts; } -#else /* MTD_PARTITIONS */ -#define OF_FLASH_PARTS(info) (0) -#define parse_partitions(info, dev) (0) -#endif /* MTD_PARTITIONS */ static int of_flash_remove(struct platform_device *dev) { @@ -105,17 +97,14 @@ static int of_flash_remove(struct platform_device *dev) dev_set_drvdata(&dev->dev, NULL); if (info->cmtd != info->list[0].mtd) { - del_mtd_device(info->cmtd); + mtd_device_unregister(info->cmtd); mtd_concat_destroy(info->cmtd); } if (info->cmtd) { - if (OF_FLASH_PARTS(info)) { - del_mtd_partitions(info->cmtd); + if (OF_FLASH_PARTS(info)) kfree(OF_FLASH_PARTS(info)); - } else { - del_mtd_device(info->cmtd); - } + mtd_device_unregister(info->cmtd); } for (i = 0; i < info->list_size; i++) { @@ -172,7 +161,6 @@ static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev, } } -#ifdef CONFIG_MTD_PARTITIONS /* When partitions are set we look for a linux,part-probe property which specifies the list of partition probers to use. If none is given then the default is use. These take precedence over other device tree @@ -212,14 +200,11 @@ static void __devinit of_free_probes(const char **probes) if (probes != part_probe_types_def) kfree(probes); } -#endif static struct of_device_id of_flash_match[]; static int __devinit of_flash_probe(struct platform_device *dev) { -#ifdef CONFIG_MTD_PARTITIONS const char **part_probe_types; -#endif const struct of_device_id *match; struct device_node *dp = dev->dev.of_node; struct resource res; @@ -346,7 +331,6 @@ static int __devinit of_flash_probe(struct platform_device *dev) if (err) goto err_out; -#ifdef CONFIG_MTD_PARTITIONS part_probe_types = of_get_probes(dp); err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); @@ -356,13 +340,11 @@ static int __devinit of_flash_probe(struct platform_device *dev) } of_free_probes(part_probe_types); -#ifdef CONFIG_MTD_OF_PARTS if (err == 0) { err = of_mtd_parse_partitions(&dev->dev, dp, &info->parts); if (err < 0) goto err_out; } -#endif if (err == 0) { err = parse_obsolete_partitions(dev, info, dp); @@ -370,11 +352,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) goto err_out; } - if (err > 0) - add_mtd_partitions(info->cmtd, info->parts, err); - else -#endif - add_mtd_device(info->cmtd); + mtd_device_register(info->cmtd, info->parts, err); kfree(mtd_list); diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 76a76be5a7bd..9ca1eccba4bc 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -94,14 +94,11 @@ static int platram_remove(struct platform_device *pdev) return 0; if (info->mtd) { -#ifdef CONFIG_MTD_PARTITIONS + mtd_device_unregister(info->mtd); if (info->partitions) { - del_mtd_partitions(info->mtd); if (info->free_partitions) kfree(info->partitions); } -#endif - del_mtd_device(info->mtd); map_destroy(info->mtd); } @@ -231,7 +228,6 @@ static int platram_probe(struct platform_device *pdev) /* check to see if there are any available partitions, or wether * to add this device whole */ -#ifdef CONFIG_MTD_PARTITIONS if (!pdata->nr_partitions) { /* try to probe using the supplied probe type */ if (pdata->probes) { @@ -239,24 +235,22 @@ static int platram_probe(struct platform_device *pdev) &info->partitions, 0); info->free_partitions = 1; if (err > 0) - err = add_mtd_partitions(info->mtd, + err = mtd_device_register(info->mtd, info->partitions, err); } } /* use the static mapping */ else - err = add_mtd_partitions(info->mtd, pdata->partitions, - pdata->nr_partitions); -#endif /* CONFIG_MTD_PARTITIONS */ - - if (add_mtd_device(info->mtd)) { - dev_err(&pdev->dev, "add_mtd_device() failed\n"); - err = -ENOMEM; - } - + err = mtd_device_register(info->mtd, pdata->partitions, + pdata->nr_partitions); if (!err) dev_info(&pdev->dev, "registered mtd device\n"); + /* add the whole device. */ + err = mtd_device_register(info->mtd, NULL, 0); + if (err) + dev_err(&pdev->dev, "failed to register the entire device\n"); + return err; exit_free: diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 64aea6acd48e..744ca5cacc9b 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -173,7 +173,7 @@ static int __init init_msp_flash(void) msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]); if (msp_flash[i]) { msp_flash[i]->owner = THIS_MODULE; - add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); + mtd_device_register(msp_flash[i], msp_parts[i], pcnt); } else { printk(KERN_ERR "map probe failed for flash\n"); ret = -ENXIO; @@ -188,7 +188,7 @@ static int __init init_msp_flash(void) cleanup_loop: while (i--) { - del_mtd_partitions(msp_flash[i]); + mtd_device_unregister(msp_flash[i]); map_destroy(msp_flash[i]); kfree(msp_maps[i].name); iounmap(msp_maps[i].virt); @@ -207,7 +207,7 @@ static void __exit cleanup_msp_flash(void) int i; for (i = 0; i < fcnt; i++) { - del_mtd_partitions(msp_flash[i]); + mtd_device_unregister(msp_flash[i]); map_destroy(msp_flash[i]); iounmap((void *)msp_maps[i].virt); diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index d8ae634d347e..f59d62f74d44 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -104,23 +104,18 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev) } info->mtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_PARTITIONS ret = parse_mtd_partitions(info->mtd, probes, &parts, 0); if (ret > 0) { info->nr_parts = ret; info->parts = parts; } -#endif - if (info->nr_parts) { - add_mtd_partitions(info->mtd, info->parts, - info->nr_parts); - } else { + if (!info->nr_parts) printk("Registering %s as whole device\n", info->map.name); - add_mtd_device(info->mtd); - } + + mtd_device_register(info->mtd, info->parts, info->nr_parts); platform_set_drvdata(pdev, info); return 0; @@ -132,12 +127,7 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); -#ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts) - del_mtd_partitions(info->mtd); - else -#endif - del_mtd_device(info->mtd); + mtd_device_unregister(info->mtd); map_destroy(info->mtd); iounmap(info->map.virt); diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index 83ed64512c5e..761fb459d2c7 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -25,10 +25,8 @@ struct rbtx4939_flash_info { struct mtd_info *mtd; struct map_info map; -#ifdef CONFIG_MTD_PARTITIONS int nr_parts; struct mtd_partition *parts; -#endif }; static int rbtx4939_flash_remove(struct platform_device *dev) @@ -41,28 +39,18 @@ static int rbtx4939_flash_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); if (info->mtd) { -#ifdef CONFIG_MTD_PARTITIONS struct rbtx4939_flash_data *pdata = dev->dev.platform_data; - if (info->nr_parts) { - del_mtd_partitions(info->mtd); + if (info->nr_parts) kfree(info->parts); - } else if (pdata->nr_parts) - del_mtd_partitions(info->mtd); - else - del_mtd_device(info->mtd); -#else - del_mtd_device(info->mtd); -#endif + mtd_device_unregister(info->mtd); map_destroy(info->mtd); } return 0; } static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", NULL }; -#endif static int rbtx4939_flash_probe(struct platform_device *dev) { @@ -120,23 +108,21 @@ static int rbtx4939_flash_probe(struct platform_device *dev) if (err) goto err_out; -#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); if (err > 0) { - add_mtd_partitions(info->mtd, info->parts, err); + mtd_device_register(info->mtd, info->parts, err); info->nr_parts = err; return 0; } if (pdata->nr_parts) { pr_notice("Using rbtx4939 partition information\n"); - add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); + mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts); return 0; } -#endif - add_mtd_device(info->mtd); + mtd_device_register(info->mtd, NULL, 0); return 0; err_out: diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 3e3ef53d4fd4..ed88225bf667 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -36,7 +36,7 @@ static int __init init_rpxlite(void) mymtd = do_map_probe("cfi_probe", &rpxlite_map); if (mymtd) { mymtd->owner = THIS_MODULE; - add_mtd_device(mymtd); + mtd_device_register(mymtd, NULL, 0); return 0; } @@ -47,7 +47,7 @@ static int __init init_rpxlite(void) static void __exit cleanup_rpxlite(void) { if (mymtd) { - del_mtd_device(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (rpxlite_map.virt) { diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index da875908ea8e..a9b5e0e5c4c5 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -226,12 +226,7 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla int i; if (info->mtd) { - if (info->nr_parts == 0) - del_mtd_device(info->mtd); -#ifdef CONFIG_MTD_PARTITIONS - else - del_mtd_partitions(info->mtd); -#endif + mtd_device_unregister(info->mtd); if (info->mtd != info->subdev[0].mtd) mtd_concat_destroy(info->mtd); } @@ -363,28 +358,24 @@ static int __devinit sa1100_mtd_probe(struct platform_device *pdev) /* * Partition selection stuff. */ -#ifdef CONFIG_MTD_PARTITIONS nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0); if (nr_parts > 0) { info->parts = parts; part_type = "dynamic"; - } else -#endif - { + } else { parts = plat->parts; nr_parts = plat->nr_parts; part_type = "static"; } - if (nr_parts == 0) { + if (nr_parts == 0) printk(KERN_NOTICE "SA1100 flash: no partition info " "available, registering whole flash\n"); - add_mtd_device(info->mtd); - } else { + else printk(KERN_NOTICE "SA1100 flash: using %s partition " "definition\n", part_type); - add_mtd_partitions(info->mtd, parts, nr_parts); - } + + mtd_device_register(info->mtd, parts, nr_parts); info->nr_parts = nr_parts; diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 04b2781fc627..556a2dfe94c5 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -182,7 +182,7 @@ static struct mtd_info *all_mtd; static void cleanup_sbc_gxx(void) { if( all_mtd ) { - del_mtd_partitions( all_mtd ); + mtd_device_unregister(all_mtd); map_destroy( all_mtd ); } @@ -223,7 +223,7 @@ static int __init init_sbc_gxx(void) all_mtd->owner = THIS_MODULE; /* Create MTD devices for each partition. */ - add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); + mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS); return 0; } diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 4d8aaaf4bb76..8fead8e46bce 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -266,10 +266,10 @@ static int __init init_sc520cdp(void) /* Combine the two flash banks into a single MTD device & register it: */ merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1"); if(merged_mtd) - add_mtd_device(merged_mtd); + mtd_device_register(merged_mtd, NULL, 0); } if(devices_found == 3) /* register the third (DIL-Flash) device */ - add_mtd_device(mymtd[2]); + mtd_device_register(mymtd[2], NULL, 0); return(devices_found ? 0 : -ENXIO); } @@ -278,11 +278,11 @@ static void __exit cleanup_sc520cdp(void) int i; if (merged_mtd) { - del_mtd_device(merged_mtd); + mtd_device_unregister(merged_mtd); mtd_concat_destroy(merged_mtd); } if (mymtd[2]) - del_mtd_device(mymtd[2]); + mtd_device_unregister(mymtd[2]); for (i = 0; i < NUM_FLASH_BANKS; i++) { if (mymtd[i]) diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 7e329f09a548..d88c8426bb0f 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -180,7 +180,7 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) scb2_mtd->owner = THIS_MODULE; if (scb2_fixup_mtd(scb2_mtd) < 0) { - del_mtd_device(scb2_mtd); + mtd_device_unregister(scb2_mtd); map_destroy(scb2_mtd); iounmap(scb2_ioaddr); if (!region_fail) @@ -192,7 +192,7 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) (unsigned long long)scb2_mtd->size, (unsigned long long)(SCB2_WINDOW - scb2_mtd->size)); - add_mtd_device(scb2_mtd); + mtd_device_register(scb2_mtd, NULL, 0); return 0; } @@ -207,7 +207,7 @@ scb2_flash_remove(struct pci_dev *dev) if (scb2_mtd->lock) scb2_mtd->lock(scb2_mtd, 0, scb2_mtd->size); - del_mtd_device(scb2_mtd); + mtd_device_unregister(scb2_mtd); map_destroy(scb2_mtd); iounmap(scb2_ioaddr); diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 027e628a4f1d..f1c1f737d0d7 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -44,7 +44,6 @@ static struct resource docmem = { static struct mtd_info *mymtd; -#ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition partition_info[] = { { .name = "DOCCS Boot kernel", @@ -68,8 +67,6 @@ static struct mtd_partition partition_info[] = { }, }; #define NUM_PARTITIONS ARRAY_SIZE(partition_info) -#endif - static struct map_info scx200_docflash_map = { .name = "NatSemi SCx200 DOCCS Flash", @@ -198,24 +195,17 @@ static int __init init_scx200_docflash(void) mymtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_PARTITIONS partition_info[3].offset = mymtd->size-partition_info[3].size; partition_info[2].size = partition_info[3].offset-partition_info[2].offset; - add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); -#else - add_mtd_device(mymtd); -#endif + mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); + return 0; } static void __exit cleanup_scx200_docflash(void) { if (mymtd) { -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(mymtd); -#else - del_mtd_device(mymtd); -#endif + mtd_device_unregister(mymtd); map_destroy(mymtd); } if (scx200_docflash_map.virt) { diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index 0eb41d9c6786..cbf6bade9354 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -89,7 +89,7 @@ static int __init init_soleng_maps(void) eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map); if (eprom_mtd) { eprom_mtd->owner = THIS_MODULE; - add_mtd_device(eprom_mtd); + mtd_device_register(eprom_mtd, NULL, 0); } nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); @@ -104,9 +104,9 @@ static int __init init_soleng_maps(void) #endif /* CONFIG_MTD_SUPERH_RESERVE */ if (nr_parts > 0) - add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + mtd_device_register(flash_mtd, parsed_parts, nr_parts); else - add_mtd_device(flash_mtd); + mtd_device_register(flash_mtd, NULL, 0); return 0; } @@ -114,14 +114,14 @@ static int __init init_soleng_maps(void) static void __exit cleanup_soleng_maps(void) { if (eprom_mtd) { - del_mtd_device(eprom_mtd); + mtd_device_unregister(eprom_mtd); map_destroy(eprom_mtd); } if (parsed_parts) - del_mtd_partitions(flash_mtd); + mtd_device_unregister(flash_mtd); else - del_mtd_device(flash_mtd); + mtd_device_unregister(flash_mtd); map_destroy(flash_mtd); } diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 3f1cb328a574..2d66234f57cb 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -101,7 +101,7 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp) up->mtd->owner = THIS_MODULE; - add_mtd_device(up->mtd); + mtd_device_register(up->mtd, NULL, 0); dev_set_drvdata(&op->dev, up); @@ -126,7 +126,7 @@ static int __devexit uflash_remove(struct platform_device *op) struct uflash_dev *up = dev_get_drvdata(&op->dev); if (up->mtd) { - del_mtd_device(up->mtd); + mtd_device_unregister(up->mtd); map_destroy(up->mtd); } if (up->map.virt) { diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 0718dfb3ee64..d78587990e7e 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -62,7 +62,6 @@ static void __iomem *start_scan_addr; * "struct map_desc *_io_desc" for the corresponding machine. */ -#ifdef CONFIG_MTD_PARTITIONS /* Currently, TQM8xxL has up to 8MiB flash */ static unsigned long tqm8xxl_max_flash_size = 0x00800000; @@ -107,7 +106,6 @@ static struct mtd_partition tqm8xxl_fs_partitions[] = { //.size = MTDPART_SIZ_FULL, } }; -#endif static int __init init_tqm_mtd(void) { @@ -188,7 +186,6 @@ static int __init init_tqm_mtd(void) goto error_mem; } -#ifdef CONFIG_MTD_PARTITIONS /* * Select Static partition definitions */ @@ -201,21 +198,14 @@ static int __init init_tqm_mtd(void) part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions); for(idx = 0; idx < num_banks ; idx++) { - if (part_banks[idx].nums == 0) { + if (part_banks[idx].nums == 0) printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx); - add_mtd_device(mtd_banks[idx]); - } else { + else printk(KERN_NOTICE "TQM flash%d: Using %s partition definition\n", idx, part_banks[idx].type); - add_mtd_partitions(mtd_banks[idx], part_banks[idx].mtd_part, - part_banks[idx].nums); - } + mtd_device_register(mtd_banks[idx], part_banks[idx].mtd_part, + part_banks[idx].nums); } -#else - printk(KERN_NOTICE "TQM flash: registering %d whole flash banks at once\n", num_banks); - for(idx = 0 ; idx < num_banks ; idx++) - add_mtd_device(mtd_banks[idx]); -#endif return 0; error_mem: for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { @@ -237,7 +227,7 @@ static void __exit cleanup_tqm_mtd(void) for(idx = 0 ; idx < num_banks ; idx++) { /* destroy mtd_info previously allocated */ if (mtd_banks[idx]) { - del_mtd_partitions(mtd_banks[idx]); + mtd_device_unregister(mtd_banks[idx]); map_destroy(mtd_banks[idx]); } /* release map_info not used anymore */ diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index e02dfa9d4ddd..d1d671daf235 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -89,7 +89,7 @@ static int __init init_ts5500_map(void) } mymtd->owner = THIS_MODULE; - add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS); + mtd_device_register(mymtd, ts5500_partitions, NUM_PARTITIONS); return 0; @@ -102,7 +102,7 @@ err2: static void __exit cleanup_ts5500_map(void) { if (mymtd) { - del_mtd_partitions(mymtd); + mtd_device_unregister(mymtd); map_destroy(mymtd); } diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c index 77a8bfc02577..1de390e1c2fb 100644 --- a/drivers/mtd/maps/tsunami_flash.c +++ b/drivers/mtd/maps/tsunami_flash.c @@ -76,7 +76,7 @@ static void __exit cleanup_tsunami_flash(void) struct mtd_info *mtd; mtd = tsunami_flash_mtd; if (mtd) { - del_mtd_device(mtd); + mtd_device_unregister(mtd); map_destroy(mtd); } tsunami_flash_mtd = 0; @@ -97,7 +97,7 @@ static int __init init_tsunami_flash(void) } if (tsunami_flash_mtd) { tsunami_flash_mtd->owner = THIS_MODULE; - add_mtd_device(tsunami_flash_mtd); + mtd_device_register(tsunami_flash_mtd, NULL, 0); return 0; } return -ENXIO; diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 35009294b435..6793074f3f40 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -89,11 +89,7 @@ static int __init uclinux_mtd_init(void) mtd->priv = mapp; uclinux_ram_mtdinfo = mtd; -#ifdef CONFIG_MTD_PARTITIONS - add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); -#else - add_mtd_device(mtd); -#endif + mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS); return(0); } @@ -103,11 +99,7 @@ static int __init uclinux_mtd_init(void) static void __exit uclinux_mtd_cleanup(void) { if (uclinux_ram_mtdinfo) { -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(uclinux_ram_mtdinfo); -#else - del_mtd_device(uclinux_ram_mtdinfo); -#endif + mtd_device_unregister(uclinux_ram_mtdinfo); map_destroy(uclinux_ram_mtdinfo); uclinux_ram_mtdinfo = NULL; } diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index 6adaa6acc193..5e68de73eabc 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -138,7 +138,7 @@ static void __exit cleanup_vmax301(void) for (i=0; i<2; i++) { if (vmax_mtd[i]) { - del_mtd_device(vmax_mtd[i]); + mtd_device_unregister(vmax_mtd[i]); map_destroy(vmax_mtd[i]); } } @@ -176,7 +176,7 @@ static int __init init_vmax301(void) vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); if (vmax_mtd[i]) { vmax_mtd[i]->owner = THIS_MODULE; - add_mtd_device(vmax_mtd[i]); + mtd_device_register(vmax_mtd[i], NULL, 0); } } diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 4afc167731ef..3a04b078576a 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -563,7 +563,7 @@ static void vmu_queryblocks(struct mapleq *mq) goto fail_cache_create; part_cur->pcache = pcache; - error = add_mtd_device(mtd_cur); + error = mtd_device_register(mtd_cur, NULL, 0); if (error) goto fail_mtd_register; @@ -709,7 +709,7 @@ static void __devexit vmu_disconnect(struct maple_device *mdev) for (x = 0; x < card->partitions; x++) { mpart = ((card->mtd)[x]).priv; mpart->mdev = NULL; - del_mtd_device(&((card->mtd)[x])); + mtd_device_unregister(&((card->mtd)[x])); kfree(((card->parts)[x]).name); } kfree(card->parts); diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 933a2b6598b4..901ce968efae 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -132,17 +132,20 @@ static int __init init_sbc82xx_flash(void) nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes, &sbcmtd_parts[i], 0); if (nr_parts > 0) { - add_mtd_partitions (sbcmtd[i], sbcmtd_parts[i], nr_parts); + mtd_device_register(sbcmtd[i], sbcmtd_parts[i], + nr_parts); continue; } /* No partitioning detected. Use default */ if (i == 2) { - add_mtd_device(sbcmtd[i]); + mtd_device_register(sbcmtd[i], NULL, 0); } else if (i == bigflash) { - add_mtd_partitions (sbcmtd[i], bigflash_parts, ARRAY_SIZE(bigflash_parts)); + mtd_device_register(sbcmtd[i], bigflash_parts, + ARRAY_SIZE(bigflash_parts)); } else { - add_mtd_partitions (sbcmtd[i], smallflash_parts, ARRAY_SIZE(smallflash_parts)); + mtd_device_register(sbcmtd[i], smallflash_parts, + ARRAY_SIZE(smallflash_parts)); } } return 0; @@ -157,9 +160,9 @@ static void __exit cleanup_sbc82xx_flash(void) continue; if (i<2 || sbcmtd_parts[i]) - del_mtd_partitions(sbcmtd[i]); + mtd_device_unregister(sbcmtd[i]); else - del_mtd_device(sbcmtd[i]); + mtd_device_unregister(sbcmtd[i]); kfree(sbcmtd_parts[i]); map_destroy(sbcmtd[i]); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index a534e1f0c348..ca385697446e 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -221,15 +221,33 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) kref_get(&dev->ref); __module_get(dev->tr->owner); - if (dev->mtd) { - ret = dev->tr->open ? dev->tr->open(dev) : 0; - __get_mtd_device(dev->mtd); + if (!dev->mtd) + goto unlock; + + if (dev->tr->open) { + ret = dev->tr->open(dev); + if (ret) + goto error_put; } + ret = __get_mtd_device(dev->mtd); + if (ret) + goto error_release; + unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); return ret; + +error_release: + if (dev->tr->release) + dev->tr->release(dev); +error_put: + module_put(dev->tr->owner); + kref_put(&dev->ref, blktrans_dev_release); + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static int blktrans_release(struct gendisk *disk, fmode_t mode) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 4c36ef66a46b..3f92731a5b9e 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -166,10 +166,23 @@ static int mtd_close(struct inode *inode, struct file *file) return 0; } /* mtd_close */ -/* FIXME: This _really_ needs to die. In 2.5, we should lock the - userspace buffer down and use it directly with readv/writev. -*/ -#define MAX_KMALLOC_SIZE 0x20000 +/* Back in June 2001, dwmw2 wrote: + * + * FIXME: This _really_ needs to die. In 2.5, we should lock the + * userspace buffer down and use it directly with readv/writev. + * + * The implementation below, using mtd_kmalloc_up_to, mitigates + * allocation failures when the system is under low-memory situations + * or if memory is highly fragmented at the cost of reducing the + * performance of the requested transfer due to a smaller buffer size. + * + * A more complex but more memory-efficient implementation based on + * get_user_pages and iovecs to cover extents of those pages is a + * longer-term goal, as intimated by dwmw2 above. However, for the + * write case, this requires yet more complex head and tail transfer + * handling when those head and tail offsets and sizes are such that + * alignment requirements are not met in the NAND subdriver. + */ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { @@ -179,6 +192,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t size_t total_retlen=0; int ret=0; int len; + size_t size = count; char *kbuf; DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); @@ -189,23 +203,12 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t if (!count) return 0; - /* FIXME: Use kiovec in 2.5 to lock down the user's buffers - and pass them directly to the MTD functions */ - - if (count > MAX_KMALLOC_SIZE) - kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); - else - kbuf=kmalloc(count, GFP_KERNEL); - + kbuf = mtd_kmalloc_up_to(mtd, &size); if (!kbuf) return -ENOMEM; while (count) { - - if (count > MAX_KMALLOC_SIZE) - len = MAX_KMALLOC_SIZE; - else - len = count; + len = min_t(size_t, count, size); switch (mfi->mode) { case MTD_MODE_OTP_FACTORY: @@ -268,6 +271,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; + size_t size = count; char *kbuf; size_t retlen; size_t total_retlen=0; @@ -285,20 +289,12 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count if (!count) return 0; - if (count > MAX_KMALLOC_SIZE) - kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); - else - kbuf=kmalloc(count, GFP_KERNEL); - + kbuf = mtd_kmalloc_up_to(mtd, &size); if (!kbuf) return -ENOMEM; while (count) { - - if (count > MAX_KMALLOC_SIZE) - len = MAX_KMALLOC_SIZE; - else - len = count; + len = min_t(size_t, count, size); if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); @@ -512,7 +508,6 @@ static int shrink_ecclayout(const struct nand_ecclayout *from, return 0; } -#ifdef CONFIG_MTD_PARTITIONS static int mtd_blkpg_ioctl(struct mtd_info *mtd, struct blkpg_ioctl_arg __user *arg) { @@ -548,8 +543,6 @@ static int mtd_blkpg_ioctl(struct mtd_info *mtd, return -EINVAL; } } -#endif - static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) { @@ -941,7 +934,6 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) break; } -#ifdef CONFIG_MTD_PARTITIONS case BLKPG: { ret = mtd_blkpg_ioctl(mtd, @@ -955,7 +947,6 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) ret = 0; break; } -#endif default: ret = -ENOTTY; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 5060e608ea5d..e601672a5305 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -319,7 +319,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - ops->retlen = 0; + ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -334,7 +334,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) devops.len = subdev->size - to; err = subdev->write_oob(subdev, to, &devops); - ops->retlen += devops.retlen; + ops->retlen += devops.oobretlen; if (err) return err; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index da69bc8a5a7d..c510aff289a8 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/ptrace.h> +#include <linux/seq_file.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/major.h> @@ -37,6 +38,7 @@ #include <linux/gfp.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> #include "mtdcore.h" /* @@ -391,7 +393,7 @@ fail_locked: * if the requested device does not appear to be present in the list. */ -int del_mtd_device (struct mtd_info *mtd) +int del_mtd_device(struct mtd_info *mtd) { int ret; struct mtd_notifier *not; @@ -427,6 +429,50 @@ out_error: } /** + * mtd_device_register - register an MTD device. + * + * @master: the MTD device to register + * @parts: the partitions to register - only valid if nr_parts > 0 + * @nr_parts: the number of partitions in parts. If zero then the full MTD + * device is registered + * + * Register an MTD device with the system and optionally, a number of + * partitions. If nr_parts is 0 then the whole device is registered, otherwise + * only the partitions are registered. To register both the full device *and* + * the partitions, call mtd_device_register() twice, once with nr_parts == 0 + * and once equal to the number of partitions. + */ +int mtd_device_register(struct mtd_info *master, + const struct mtd_partition *parts, + int nr_parts) +{ + return parts ? add_mtd_partitions(master, parts, nr_parts) : + add_mtd_device(master); +} +EXPORT_SYMBOL_GPL(mtd_device_register); + +/** + * mtd_device_unregister - unregister an existing MTD device. + * + * @master: the MTD device to unregister. This will unregister both the master + * and any partitions if registered. + */ +int mtd_device_unregister(struct mtd_info *master) +{ + int err; + + err = del_mtd_partitions(master); + if (err) + return err; + + if (!device_is_registered(&master->dev)) + return 0; + + return del_mtd_device(master); +} +EXPORT_SYMBOL_GPL(mtd_device_unregister); + +/** * register_mtd_user - register a 'user' of MTD devices. * @new: pointer to notifier info structure * @@ -443,7 +489,7 @@ void register_mtd_user (struct mtd_notifier *new) list_add(&new->list, &mtd_notifiers); - __module_get(THIS_MODULE); + __module_get(THIS_MODULE); mtd_for_each_device(mtd) new->add(mtd); @@ -532,7 +578,6 @@ int __get_mtd_device(struct mtd_info *mtd) return -ENODEV; if (mtd->get_device) { - err = mtd->get_device(mtd); if (err) { @@ -570,21 +615,13 @@ struct mtd_info *get_mtd_device_nm(const char *name) if (!mtd) goto out_unlock; - if (!try_module_get(mtd->owner)) + err = __get_mtd_device(mtd); + if (err) goto out_unlock; - if (mtd->get_device) { - err = mtd->get_device(mtd); - if (err) - goto out_put; - } - - mtd->usecount++; mutex_unlock(&mtd_table_mutex); return mtd; -out_put: - module_put(mtd->owner); out_unlock: mutex_unlock(&mtd_table_mutex); return ERR_PTR(err); @@ -638,8 +675,54 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, return ret; } -EXPORT_SYMBOL_GPL(add_mtd_device); -EXPORT_SYMBOL_GPL(del_mtd_device); +/** + * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size + * @size: A pointer to the ideal or maximum size of the allocation. Points + * to the actual allocation size on success. + * + * This routine attempts to allocate a contiguous kernel buffer up to + * the specified size, backing off the size of the request exponentially + * until the request succeeds or until the allocation size falls below + * the system page size. This attempts to make sure it does not adversely + * impact system performance, so when allocating more than one page, we + * ask the memory allocator to avoid re-trying, swapping, writing back + * or performing I/O. + * + * Note, this function also makes sure that the allocated buffer is aligned to + * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value. + * + * This is called, for example by mtd_{read,write} and jffs2_scan_medium, + * to handle smaller (i.e. degraded) buffer allocations under low- or + * fragmented-memory situations where such reduced allocations, from a + * requested ideal, are allowed. + * + * Returns a pointer to the allocated buffer on success; otherwise, NULL. + */ +void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size) +{ + gfp_t flags = __GFP_NOWARN | __GFP_WAIT | + __GFP_NORETRY | __GFP_NO_KSWAPD; + size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE); + void *kbuf; + + *size = min_t(size_t, *size, KMALLOC_MAX_SIZE); + + while (*size > min_alloc) { + kbuf = kmalloc(*size, flags); + if (kbuf) + return kbuf; + + *size >>= 1; + *size = ALIGN(*size, mtd->writesize); + } + + /* + * For the last resort allocation allow 'kmalloc()' to do all sorts of + * things (write-back, dropping caches, etc) by using GFP_KERNEL. + */ + return kmalloc(*size, GFP_KERNEL); +} + EXPORT_SYMBOL_GPL(get_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device_nm); EXPORT_SYMBOL_GPL(__get_mtd_device); @@ -648,6 +731,7 @@ EXPORT_SYMBOL_GPL(__put_mtd_device); EXPORT_SYMBOL_GPL(register_mtd_user); EXPORT_SYMBOL_GPL(unregister_mtd_user); EXPORT_SYMBOL_GPL(default_mtd_writev); +EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to); #ifdef CONFIG_PROC_FS @@ -656,44 +740,32 @@ EXPORT_SYMBOL_GPL(default_mtd_writev); static struct proc_dir_entry *proc_mtd; -static inline int mtd_proc_info(char *buf, struct mtd_info *this) -{ - return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index, - (unsigned long long)this->size, - this->erasesize, this->name); -} - -static int mtd_read_proc (char *page, char **start, off_t off, int count, - int *eof, void *data_unused) +static int mtd_proc_show(struct seq_file *m, void *v) { struct mtd_info *mtd; - int len, l; - off_t begin = 0; + seq_puts(m, "dev: size erasesize name\n"); mutex_lock(&mtd_table_mutex); - - len = sprintf(page, "dev: size erasesize name\n"); mtd_for_each_device(mtd) { - l = mtd_proc_info(page + len, mtd); - len += l; - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } - } - - *eof = 1; - -done: + seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"\n", + mtd->index, (unsigned long long)mtd->size, + mtd->erasesize, mtd->name); + } mutex_unlock(&mtd_table_mutex); - if (off >= len+begin) - return 0; - *start = page + (off-begin); - return ((count < begin+len-off) ? count : begin+len-off); + return 0; +} + +static int mtd_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtd_proc_show, NULL); } +static const struct file_operations mtd_proc_ops = { + .open = mtd_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_PROC_FS */ /*====================================================================*/ @@ -734,8 +806,7 @@ static int __init init_mtd(void) goto err_bdi3; #ifdef CONFIG_PROC_FS - if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) - proc_mtd->read_proc = mtd_read_proc; + proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops); #endif /* CONFIG_PROC_FS */ return 0; @@ -753,7 +824,7 @@ err_reg: static void __exit cleanup_mtd(void) { #ifdef CONFIG_PROC_FS - if (proc_mtd) + if (proc_mtd) remove_proc_entry( "mtd", NULL); #endif /* CONFIG_PROC_FS */ class_unregister(&mtd_class); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index 6a64fdebc898..0ed6126b4c1f 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -10,6 +10,12 @@ extern struct mutex mtd_table_mutex; extern struct mtd_info *__mtd_next_device(int i); +extern int add_mtd_device(struct mtd_info *mtd); +extern int del_mtd_device(struct mtd_info *mtd); +extern int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, + int); +extern int del_mtd_partitions(struct mtd_info *); + #define mtd_for_each_device(mtd) \ for ((mtd) = __mtd_next_device(0); \ (mtd) != NULL; \ diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 0a4760174782..630be3e7da04 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -31,6 +31,8 @@ #include <linux/mtd/partitions.h> #include <linux/err.h> +#include "mtdcore.h" + /* Our partition linked list */ static LIST_HEAD(mtd_partitions); static DEFINE_MUTEX(mtd_partitions_mutex); @@ -376,7 +378,6 @@ int del_mtd_partitions(struct mtd_info *master) return err; } -EXPORT_SYMBOL(del_mtd_partitions); static struct mtd_part *allocate_partition(struct mtd_info *master, const struct mtd_partition *part, int partno, @@ -671,7 +672,6 @@ int add_mtd_partitions(struct mtd_info *master, return 0; } -EXPORT_SYMBOL(add_mtd_partitions); static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); @@ -722,11 +722,8 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types, parser = get_partition_parser(*types); if (!parser && !request_module("%s", *types)) parser = get_partition_parser(*types); - if (!parser) { - printk(KERN_NOTICE "%s partition parsing not available\n", - *types); + if (!parser) continue; - } ret = (*parser->parse_fn)(master, pparts, origin); if (ret > 0) { printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index fed215c4cfa1..fd7885327611 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1450,7 +1450,13 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) } oinfo = mtd->ecclayout; - if (!mtd->oobsize || !oinfo || oinfo->oobavail < MTDSWAP_OOBSIZE) { + if (!oinfo) { + printk(KERN_ERR "%s: mtd%d does not have OOB\n", + MTDSWAP_PREFIX, mtd->index); + return; + } + + if (!mtd->oobsize || oinfo->oobavail < MTDSWAP_OOBSIZE) { printk(KERN_ERR "%s: Not enough free bytes in OOB, " "%d available, %zu needed.\n", MTDSWAP_PREFIX, oinfo->oobavail, MTDSWAP_OOBSIZE); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index edec457d361d..4c3425235adc 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -92,7 +92,7 @@ config MTD_NAND_EDB7312 config MTD_NAND_H1900 tristate "iPAQ H1900 flash" - depends on ARCH_PXA && MTD_PARTITIONS + depends on ARCH_PXA help This enables the driver for the iPAQ h1900 flash. @@ -419,7 +419,6 @@ config MTD_NAND_TMIO config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" - depends on MTD_PARTITIONS help The simulator may simulate various NAND flash chips for the MTD nand layer. @@ -513,7 +512,7 @@ config MTD_NAND_SOCRATES config MTD_NAND_NUC900 tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." - depends on ARCH_W90X900 && MTD_PARTITIONS + depends on ARCH_W90X900 help This enables the driver for the NAND Flash on evaluation board based on w90p910 / NUC9xx. diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 8691e0482ed2..eb40ea829ab2 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -120,7 +120,7 @@ static void alauda_delete(struct kref *kref) struct alauda *al = container_of(kref, struct alauda, kref); if (al->mtd) { - del_mtd_device(al->mtd); + mtd_device_unregister(al->mtd); kfree(al->mtd); } usb_put_dev(al->dev); @@ -592,7 +592,7 @@ static int alauda_init_media(struct alauda *al) mtd->priv = al; mtd->owner = THIS_MODULE; - err = add_mtd_device(mtd); + err = mtd_device_register(mtd, NULL, 0); if (err) { err = -ENFILE; goto error; diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index bc65bf71e1a2..78017eb9318e 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -235,8 +235,8 @@ static int __devinit ams_delta_init(struct platform_device *pdev) } /* Register the partitions */ - add_mtd_partitions(ams_delta_mtd, partition_info, - ARRAY_SIZE(partition_info)); + mtd_device_register(ams_delta_mtd, partition_info, + ARRAY_SIZE(partition_info)); goto out; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 950646aa4c4b..b300705d41cb 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -30,6 +30,7 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/dmaengine.h> #include <linux/gpio.h> #include <linux/io.h> @@ -494,11 +495,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct resource *regs; struct resource *mem; int res; - -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *partitions = NULL; int num_partitions = 0; -#endif mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { @@ -656,7 +654,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_scan_tail; } -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "atmel_nand"; num_partitions = parse_mtd_partitions(mtd, part_probes, @@ -672,17 +669,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_no_partitions; } - res = add_mtd_partitions(mtd, partitions, num_partitions); -#else - res = add_mtd_device(mtd); -#endif - + res = mtd_device_register(mtd, partitions, num_partitions); if (!res) return res; -#ifdef CONFIG_MTD_PARTITIONS err_no_partitions: -#endif nand_release(mtd); err_scan_tail: err_scan_ident: diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 5d513b54a7d7..e7767eef4505 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -581,7 +581,8 @@ static int __init au1xxx_nand_init(void) } /* Register the partitions */ - add_mtd_partitions(au1550_mtd, partition_info, ARRAY_SIZE(partition_info)); + mtd_device_register(au1550_mtd, partition_info, + ARRAY_SIZE(partition_info)); return 0; diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index 0911cf03db80..eddc9a224985 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -185,20 +185,20 @@ static int __init autcpu12_init(void) /* Register the partitions */ switch (autcpu12_mtd->size) { case SZ_16M: - add_mtd_partitions(autcpu12_mtd, partition_info16k, - NUM_PARTITIONS16K); + mtd_device_register(autcpu12_mtd, partition_info16k, + NUM_PARTITIONS16K); break; case SZ_32M: - add_mtd_partitions(autcpu12_mtd, partition_info32k, - NUM_PARTITIONS32K); + mtd_device_register(autcpu12_mtd, partition_info32k, + NUM_PARTITIONS32K); break; case SZ_64M: - add_mtd_partitions(autcpu12_mtd, partition_info64k, - NUM_PARTITIONS64K); + mtd_device_register(autcpu12_mtd, partition_info64k, + NUM_PARTITIONS64K); break; case SZ_128M: - add_mtd_partitions(autcpu12_mtd, partition_info128k, - NUM_PARTITIONS128K); + mtd_device_register(autcpu12_mtd, partition_info128k, + NUM_PARTITIONS128K); break; default: printk("Unsupported SmartMedia device\n"); diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index dfe262c726fb..9ec280738a9a 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -52,9 +52,7 @@ static const __devinitconst char gBanner[] = KERN_INFO \ "BCM UMI MTD NAND Driver: 1.00\n"; -#ifdef CONFIG_MTD_PARTITIONS const char *part_probes[] = { "cmdlinepart", NULL }; -#endif #if NAND_ECC_BCH static uint8_t scan_ff_pattern[] = { 0xff }; @@ -509,7 +507,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) kfree(board_mtd); return -EIO; } - add_mtd_partitions(board_mtd, partition_info, nr_partitions); + mtd_device_register(board_mtd, partition_info, nr_partitions); } /* Return happy */ diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 79947bea4d57..dd899cb5d366 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -659,15 +659,10 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) { struct mtd_info *mtd = &info->mtd; - -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts = info->platform->partitions; int nr = info->platform->nr_partitions; - return add_mtd_partitions(mtd, parts, nr); -#else - return add_mtd_device(mtd); -#endif + return mtd_device_register(mtd, parts, nr); } static int __devexit bf5xx_nand_remove(struct platform_device *pdev) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index e06c8983978e..87ebb4e5b0c3 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -90,9 +90,7 @@ static unsigned int numtimings; static int timing[3]; module_param_array(timing, int, &numtimings, 0644); -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; -#endif /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -632,10 +630,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, struct cafe_priv *cafe; uint32_t ctrl; int err = 0; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; int nr_parts; -#endif /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ @@ -804,9 +800,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mtd); /* We register the whole device first, separate from the partitions */ - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "cafe_nand"; #endif @@ -814,9 +809,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, if (nr_parts > 0) { cafe->parts = parts; dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts); - add_mtd_partitions(mtd, parts, nr_parts); + mtd_device_register(mtd, parts, nr_parts); } -#endif goto out; out_irq: @@ -838,7 +832,6 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) struct mtd_info *mtd = pci_get_drvdata(pdev); struct cafe_priv *cafe = mtd->priv; - del_mtd_device(mtd); /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 6e6495278258..6fc043a30d1e 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -238,7 +238,7 @@ static int __init cmx270_init(void) /* Register the partitions */ pr_notice("Using %s partition definition\n", part_type); - ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); + ret = mtd_device_register(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); if (ret) goto err_scan; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 71c35a0b9826..f59ad1f2d5db 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -277,22 +277,15 @@ static int is_geode(void) return 0; } - -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif - static int __init cs553x_init(void) { int err = -ENXIO; int i; uint64_t val; - -#ifdef CONFIG_MTD_PARTITIONS int mtd_parts_nb = 0; struct mtd_partition *mtd_parts = NULL; -#endif /* If the CPU isn't a Geode GX or LX, abort */ if (!is_geode()) @@ -324,17 +317,11 @@ static int __init cs553x_init(void) if (cs553x_mtd[i]) { /* If any devices registered, return success. Else the last error. */ -#ifdef CONFIG_MTD_PARTITIONS mtd_parts_nb = parse_mtd_partitions(cs553x_mtd[i], part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) { + if (mtd_parts_nb > 0) printk(KERN_NOTICE "Using command line partition definition\n"); - add_mtd_partitions(cs553x_mtd[i], mtd_parts, mtd_parts_nb); - } else { - add_mtd_device(cs553x_mtd[i]); - } -#else - add_mtd_device(cs553x_mtd[i]); -#endif + mtd_device_register(cs553x_mtd[i], mtd_parts, + mtd_parts_nb); err = 0; } } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index aff3468867ac..1f34951ae1a7 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -530,6 +530,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) int ret; uint32_t val; nand_ecc_modes_t ecc_mode; + struct mtd_partition *mtd_parts = NULL; + int mtd_parts_nb = 0; /* insist on board-specific configuration */ if (!pdata) @@ -749,41 +751,33 @@ syndrome_done: if (ret < 0) goto err_scan; - if (mtd_has_partitions()) { - struct mtd_partition *mtd_parts = NULL; - int mtd_parts_nb = 0; + if (mtd_has_cmdlinepart()) { + static const char *probes[] __initconst = { + "cmdlinepart", NULL + }; - if (mtd_has_cmdlinepart()) { - static const char *probes[] __initconst = - { "cmdlinepart", NULL }; - - mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, - &mtd_parts, 0); - } - - if (mtd_parts_nb <= 0) { - mtd_parts = pdata->parts; - mtd_parts_nb = pdata->nr_parts; - } + mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, + &mtd_parts, 0); + } - /* Register any partitions */ - if (mtd_parts_nb > 0) { - ret = add_mtd_partitions(&info->mtd, - mtd_parts, mtd_parts_nb); - if (ret == 0) - info->partitioned = true; - } + if (mtd_parts_nb <= 0) { + mtd_parts = pdata->parts; + mtd_parts_nb = pdata->nr_parts; + } - } else if (pdata->nr_parts) { - dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", - pdata->nr_parts, info->mtd.name); + /* Register any partitions */ + if (mtd_parts_nb > 0) { + ret = mtd_device_register(&info->mtd, mtd_parts, + mtd_parts_nb); + if (ret == 0) + info->partitioned = true; } /* If there's no partition info, just package the whole chip * as a single MTD device. */ if (!info->partitioned) - ret = add_mtd_device(&info->mtd) ? -ENODEV : 0; + ret = mtd_device_register(&info->mtd, NULL, 0) ? -ENODEV : 0; if (ret < 0) goto err_scan; @@ -824,10 +818,7 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) struct davinci_nand_info *info = platform_get_drvdata(pdev); int status; - if (mtd_has_partitions() && info->partitioned) - status = del_mtd_partitions(&info->mtd); - else - status = del_mtd_device(&info->mtd); + status = mtd_device_unregister(&info->mtd); spin_lock_irq(&davinci_nand_lock); if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 4633f094c510..d5276218945f 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/wait.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -44,16 +45,16 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." /* We define a macro here that combines all interrupts this driver uses into * a single constant value, for convenience. */ -#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ - INTR_STATUS0__ECC_TRANSACTION_DONE | \ - INTR_STATUS0__ECC_ERR | \ - INTR_STATUS0__PROGRAM_FAIL | \ - INTR_STATUS0__LOAD_COMP | \ - INTR_STATUS0__PROGRAM_COMP | \ - INTR_STATUS0__TIME_OUT | \ - INTR_STATUS0__ERASE_FAIL | \ - INTR_STATUS0__RST_COMP | \ - INTR_STATUS0__ERASE_COMP) +#define DENALI_IRQ_ALL (INTR_STATUS__DMA_CMD_COMP | \ + INTR_STATUS__ECC_TRANSACTION_DONE | \ + INTR_STATUS__ECC_ERR | \ + INTR_STATUS__PROGRAM_FAIL | \ + INTR_STATUS__LOAD_COMP | \ + INTR_STATUS__PROGRAM_COMP | \ + INTR_STATUS__TIME_OUT | \ + INTR_STATUS__ERASE_FAIL | \ + INTR_STATUS__RST_COMP | \ + INTR_STATUS__ERASE_COMP) /* indicates whether or not the internal value for the flash bank is * valid or not */ @@ -95,30 +96,6 @@ static const struct pci_device_id denali_pci_ids[] = { { /* end: all zeroes */ } }; - -/* these are static lookup tables that give us easy access to - * registers in the NAND controller. - */ -static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, - INTR_STATUS1, - INTR_STATUS2, - INTR_STATUS3}; - -static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, - DEVICE_RESET__BANK1, - DEVICE_RESET__BANK2, - DEVICE_RESET__BANK3}; - -static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, - INTR_STATUS1__TIME_OUT, - INTR_STATUS2__TIME_OUT, - INTR_STATUS3__TIME_OUT}; - -static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, - INTR_STATUS1__RST_COMP, - INTR_STATUS2__RST_COMP, - INTR_STATUS3__RST_COMP}; - /* forward declarations */ static void clear_interrupts(struct denali_nand_info *denali); static uint32_t wait_for_irq(struct denali_nand_info *denali, @@ -180,19 +157,17 @@ static void read_status(struct denali_nand_info *denali) static void reset_bank(struct denali_nand_info *denali) { uint32_t irq_status = 0; - uint32_t irq_mask = reset_complete[denali->flash_bank] | - operation_timeout[denali->flash_bank]; - int bank = 0; + uint32_t irq_mask = INTR_STATUS__RST_COMP | + INTR_STATUS__TIME_OUT; clear_interrupts(denali); - bank = device_reset_banks[denali->flash_bank]; - iowrite32(bank, denali->flash_reg + DEVICE_RESET); + iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET); irq_status = wait_for_irq(denali, irq_mask); - if (irq_status & operation_timeout[denali->flash_bank]) - dev_err(&denali->dev->dev, "reset bank failed.\n"); + if (irq_status & INTR_STATUS__TIME_OUT) + dev_err(denali->dev, "reset bank failed.\n"); } /* Reset the flash controller */ @@ -200,29 +175,28 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali) { uint32_t i; - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); - for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) - iowrite32(reset_complete[i] | operation_timeout[i], - denali->flash_reg + intr_status_addresses[i]); + for (i = 0 ; i < denali->max_banks; i++) + iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT, + denali->flash_reg + INTR_STATUS(i)); - for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { - iowrite32(device_reset_banks[i], - denali->flash_reg + DEVICE_RESET); + for (i = 0 ; i < denali->max_banks; i++) { + iowrite32(1 << i, denali->flash_reg + DEVICE_RESET); while (!(ioread32(denali->flash_reg + - intr_status_addresses[i]) & - (reset_complete[i] | operation_timeout[i]))) + INTR_STATUS(i)) & + (INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT))) cpu_relax(); - if (ioread32(denali->flash_reg + intr_status_addresses[i]) & - operation_timeout[i]) - dev_dbg(&denali->dev->dev, + if (ioread32(denali->flash_reg + INTR_STATUS(i)) & + INTR_STATUS__TIME_OUT) + dev_dbg(denali->dev, "NAND Reset operation timed out on bank %d\n", i); } - for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) - iowrite32(reset_complete[i] | operation_timeout[i], - denali->flash_reg + intr_status_addresses[i]); + for (i = 0; i < denali->max_banks; i++) + iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT, + denali->flash_reg + INTR_STATUS(i)); return PASS; } @@ -254,7 +228,7 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali, uint16_t acc_clks; uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); en_lo = CEIL_DIV(Trp[mode], CLK_X); @@ -291,7 +265,7 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali, acc_clks++; if ((data_invalid - acc_clks * CLK_X) < 2) - dev_warn(&denali->dev->dev, "%s, Line %d: Warning!\n", + dev_warn(denali->dev, "%s, Line %d: Warning!\n", __FILE__, __LINE__); addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); @@ -419,7 +393,7 @@ static void get_hynix_nand_para(struct denali_nand_info *denali, #endif break; default: - dev_warn(&denali->dev->dev, + dev_warn(denali->dev, "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." "Will use default parameter values instead.\n", device_id); @@ -431,17 +405,17 @@ static void get_hynix_nand_para(struct denali_nand_info *denali, */ static void find_valid_banks(struct denali_nand_info *denali) { - uint32_t id[LLD_MAX_FLASH_BANKS]; + uint32_t id[denali->max_banks]; int i; denali->total_used_banks = 1; - for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + for (i = 0; i < denali->max_banks; i++) { index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "Return 1st ID for bank[%d]: %x\n", i, id[i]); if (i == 0) { @@ -461,16 +435,27 @@ static void find_valid_banks(struct denali_nand_info *denali) * Multichip support is not enabled. */ if (denali->total_used_banks != 1) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "Sorry, Intel CE4100 only supports " "a single NAND device.\n"); BUG(); } } - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "denali->total_used_banks: %d\n", denali->total_used_banks); } +/* + * Use the configuration feature register to determine the maximum number of + * banks that the hardware supports. + */ +static void detect_max_banks(struct denali_nand_info *denali) +{ + uint32_t features = ioread32(denali->flash_reg + FEATURES); + + denali->max_banks = 2 << (features & FEATURES__N_BANKS); +} + static void detect_partition_feature(struct denali_nand_info *denali) { /* For MRST platform, denali->fwblks represent the @@ -480,15 +465,15 @@ static void detect_partition_feature(struct denali_nand_info *denali) * blocks it can't touch. * */ if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { - if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & - PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID(1)) & + PERM_SRC_ID__SRCID) == SPECTRA_PARTITION_ID) { denali->fwblks = - ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & - MIN_MAX_BANK_1__MIN_VALUE) * + ((ioread32(denali->flash_reg + MIN_MAX_BANK(1)) & + MIN_MAX_BANK__MIN_VALUE) * denali->blksperchip) + - (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & - MIN_BLK_ADDR_1__VALUE); + (ioread32(denali->flash_reg + MIN_BLK_ADDR(1)) & + MIN_BLK_ADDR__VALUE); } else denali->fwblks = SPECTRA_START_BLOCK; } else @@ -501,7 +486,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) uint32_t id_bytes[5], addr; uint8_t i, maf_id, device_id; - dev_dbg(&denali->dev->dev, + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); @@ -530,7 +515,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) get_hynix_nand_para(denali, device_id); } - dev_info(&denali->dev->dev, + dev_info(denali->dev, "Dump timing register values:" "acc_clks: %d, re_2_we: %d, re_2_re: %d\n" "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n" @@ -560,7 +545,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) static void denali_set_intr_modes(struct denali_nand_info *denali, uint16_t INT_ENABLE) { - dev_dbg(&denali->dev->dev, "%s, Line %d, Function: %s\n", + dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (INT_ENABLE) @@ -580,6 +565,7 @@ static inline bool is_flash_bank_valid(int flash_bank) static void denali_irq_init(struct denali_nand_info *denali) { uint32_t int_mask = 0; + int i; /* Disable global interrupts */ denali_set_intr_modes(denali, false); @@ -587,10 +573,8 @@ static void denali_irq_init(struct denali_nand_info *denali) int_mask = DENALI_IRQ_ALL; /* Clear all status bits */ - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS0); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS1); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS2); - iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS3); + for (i = 0; i < denali->max_banks; ++i) + iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i)); denali_irq_enable(denali, int_mask); } @@ -604,10 +588,10 @@ static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) { - iowrite32(int_mask, denali->flash_reg + INTR_EN0); - iowrite32(int_mask, denali->flash_reg + INTR_EN1); - iowrite32(int_mask, denali->flash_reg + INTR_EN2); - iowrite32(int_mask, denali->flash_reg + INTR_EN3); + int i; + + for (i = 0; i < denali->max_banks; ++i) + iowrite32(int_mask, denali->flash_reg + INTR_EN(i)); } /* This function only returns when an interrupt that this driver cares about @@ -624,7 +608,7 @@ static inline void clear_interrupt(struct denali_nand_info *denali, { uint32_t intr_status_reg = 0; - intr_status_reg = intr_status_addresses[denali->flash_bank]; + intr_status_reg = INTR_STATUS(denali->flash_bank); iowrite32(irq_mask, denali->flash_reg + intr_status_reg); } @@ -645,7 +629,7 @@ static uint32_t read_interrupt_status(struct denali_nand_info *denali) { uint32_t intr_status_reg = 0; - intr_status_reg = intr_status_addresses[denali->flash_bank]; + intr_status_reg = INTR_STATUS(denali->flash_bank); return ioread32(denali->flash_reg + intr_status_reg); } @@ -754,7 +738,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, irq_mask = 0; if (op == DENALI_READ) - irq_mask = INTR_STATUS0__LOAD_COMP; + irq_mask = INTR_STATUS__LOAD_COMP; else if (op == DENALI_WRITE) irq_mask = 0; else @@ -800,7 +784,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "cmd, page, addr on timeout " "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); @@ -861,8 +845,8 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | - INTR_STATUS0__PROGRAM_FAIL; + uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP | + INTR_STATUS__PROGRAM_FAIL; int status = 0; denali->page = page; @@ -875,11 +859,11 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, "OOB write failed\n"); + dev_err(denali->dev, "OOB write failed\n"); status = -EIO; } } else { - dev_err(&denali->dev->dev, "unable to send pipeline command\n"); + dev_err(denali->dev, "unable to send pipeline command\n"); status = -EIO; } return status; @@ -889,7 +873,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, + uint32_t irq_mask = INTR_STATUS__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; denali->page = page; @@ -904,7 +888,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) - dev_err(&denali->dev->dev, "page on OOB timeout %d\n", + dev_err(denali->dev, "page on OOB timeout %d\n", denali->page); /* We set the device back to MAIN_ACCESS here as I observed @@ -944,7 +928,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, { bool check_erased_page = false; - if (irq_status & INTR_STATUS0__ECC_ERR) { + if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ uint32_t err_address = 0, err_correction_info = 0; uint32_t err_byte = 0, err_sector = 0, err_device = 0; @@ -995,7 +979,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, * for a while for this interrupt * */ while (!(read_interrupt_status(denali) & - INTR_STATUS0__ECC_TRANSACTION_DONE)) + INTR_STATUS__ECC_TRANSACTION_DONE)) cpu_relax(); clear_interrupts(denali); denali_set_intr_modes(denali, true); @@ -1045,14 +1029,13 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, bool raw_xfer) { struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | - INTR_STATUS0__PROGRAM_FAIL; + uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP | + INTR_STATUS__PROGRAM_FAIL; /* if it is a raw xfer, we want to disable ecc, and send * the spare area. @@ -1071,7 +1054,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize); } - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE); clear_interrupts(denali); denali_enable_dma(denali, true); @@ -1082,16 +1065,16 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) { - dev_err(&denali->dev->dev, + dev_err(denali->dev, "timeout on write_page (type = %d)\n", raw_xfer); denali->status = - (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? + (irq_status & INTR_STATUS__PROGRAM_FAIL) ? NAND_STATUS_FAIL : PASS; } denali_enable_dma(denali, false); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE); } /* NAND core entry points */ @@ -1139,18 +1122,17 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | - INTR_STATUS0__ECC_ERR; + uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE | + INTR_STATUS__ECC_ERR; bool check_erased_page = false; if (page != denali->page) { - dev_err(&denali->dev->dev, "IN %s: page %d is not" + dev_err(denali->dev, "IN %s: page %d is not" " equal to denali->page %d, investigate!!", __func__, page, denali->page); BUG(); @@ -1159,7 +1141,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, setup_ecc_for_xfer(denali, true, false); denali_enable_dma(denali, true); - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); clear_interrupts(denali); denali_setup_dma(denali, DENALI_READ); @@ -1167,7 +1149,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); memcpy(buf, denali->buf.buf, mtd->writesize); @@ -1192,16 +1174,15 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - struct pci_dev *pci_dev = denali->dev; dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; + uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP; if (page != denali->page) { - dev_err(&denali->dev->dev, "IN %s: page %d is not" + dev_err(denali->dev, "IN %s: page %d is not" " equal to denali->page %d, investigate!!", __func__, page, denali->page); BUG(); @@ -1210,7 +1191,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, setup_ecc_for_xfer(denali, false, true); denali_enable_dma(denali, true); - pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); clear_interrupts(denali); denali_setup_dma(denali, DENALI_READ); @@ -1218,7 +1199,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); denali_enable_dma(denali, false); @@ -1271,10 +1252,10 @@ static void denali_erase(struct mtd_info *mtd, int page) index_addr(denali, (uint32_t)cmd, 0x1); /* wait for erase to complete or failure to occur */ - irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | - INTR_STATUS0__ERASE_FAIL); + irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP | + INTR_STATUS__ERASE_FAIL); - denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? + denali->status = (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS; } @@ -1330,7 +1311,7 @@ static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc_code) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_calculate called unexpectedly\n"); BUG(); return -EIO; @@ -1340,7 +1321,7 @@ static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, uint8_t *read_ecc, uint8_t *calc_ecc) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_correct called unexpectedly\n"); BUG(); return -EIO; @@ -1349,7 +1330,7 @@ static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) { struct denali_nand_info *denali = mtd_to_denali(mtd); - dev_err(&denali->dev->dev, + dev_err(denali->dev, "denali_ecc_hwctl called unexpectedly\n"); BUG(); } @@ -1375,6 +1356,7 @@ static void denali_hw_init(struct denali_nand_info *denali) /* Should set value for these registers when init */ iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES); iowrite32(1, denali->flash_reg + ECC_ENABLE); + detect_max_banks(denali); denali_nand_timing_set(denali); denali_irq_init(denali); } @@ -1484,24 +1466,22 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } /* Is 32-bit DMA supported? */ - ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); - + ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(32)); if (ret) { printk(KERN_ERR "Spectra: no usable DMA configuration\n"); goto failed_enable_dev; } - denali->buf.dma_buf = - pci_map_single(dev, denali->buf.buf, - DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); + denali->buf.dma_buf = dma_map_single(&dev->dev, denali->buf.buf, + DENALI_BUF_SIZE, + DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) { + if (dma_mapping_error(&dev->dev, denali->buf.dma_buf)) { dev_err(&dev->dev, "Spectra: failed to map DMA buffer\n"); goto failed_enable_dev; } pci_set_master(dev); - denali->dev = dev; + denali->dev = &dev->dev; denali->mtd.dev.parent = &dev->dev; ret = pci_request_regions(dev, DENALI_NAND_NAME); @@ -1554,7 +1534,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) /* scan for NAND devices attached to the controller * this is the first stage in a two step process to register * with the nand subsystem */ - if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) { + if (nand_scan_ident(&denali->mtd, denali->max_banks, NULL)) { ret = -ENXIO; goto failed_req_irq; } @@ -1664,7 +1644,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto failed_req_irq; } - ret = add_mtd_device(&denali->mtd); + ret = mtd_device_register(&denali->mtd, NULL, 0); if (ret) { dev_err(&dev->dev, "Spectra: Failed to register MTD: %d\n", ret); @@ -1681,8 +1661,8 @@ failed_remap_reg: failed_req_regions: pci_release_regions(dev); failed_dma_map: - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); + dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + DMA_BIDIRECTIONAL); failed_enable_dev: pci_disable_device(dev); failed_alloc_memery: @@ -1696,7 +1676,7 @@ static void denali_pci_remove(struct pci_dev *dev) struct denali_nand_info *denali = pci_get_drvdata(dev); nand_release(&denali->mtd); - del_mtd_device(&denali->mtd); + mtd_device_unregister(&denali->mtd); denali_irq_cleanup(dev->irq, denali); @@ -1704,8 +1684,8 @@ static void denali_pci_remove(struct pci_dev *dev) iounmap(denali->flash_mem); pci_release_regions(dev); pci_disable_device(dev); - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); + dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + DMA_BIDIRECTIONAL); pci_set_drvdata(dev, NULL); kfree(denali); } @@ -1721,8 +1701,7 @@ static struct pci_driver denali_pci_driver = { static int __devinit denali_init(void) { - printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", - __DATE__, __TIME__); + printk(KERN_INFO "Spectra MTD driver\n"); return pci_register_driver(&denali_pci_driver); } diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 3918bcb1561e..fabb9d56b39e 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -211,185 +211,46 @@ #define TRANSFER_MODE 0x400 #define TRANSFER_MODE__VALUE 0x0003 -#define INTR_STATUS0 0x410 -#define INTR_STATUS0__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS0__ECC_ERR 0x0002 -#define INTR_STATUS0__DMA_CMD_COMP 0x0004 -#define INTR_STATUS0__TIME_OUT 0x0008 -#define INTR_STATUS0__PROGRAM_FAIL 0x0010 -#define INTR_STATUS0__ERASE_FAIL 0x0020 -#define INTR_STATUS0__LOAD_COMP 0x0040 -#define INTR_STATUS0__PROGRAM_COMP 0x0080 -#define INTR_STATUS0__ERASE_COMP 0x0100 -#define INTR_STATUS0__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS0__LOCKED_BLK 0x0400 -#define INTR_STATUS0__UNSUP_CMD 0x0800 -#define INTR_STATUS0__INT_ACT 0x1000 -#define INTR_STATUS0__RST_COMP 0x2000 -#define INTR_STATUS0__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS0__PAGE_XFER_INC 0x8000 - -#define INTR_EN0 0x420 -#define INTR_EN0__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN0__ECC_ERR 0x0002 -#define INTR_EN0__DMA_CMD_COMP 0x0004 -#define INTR_EN0__TIME_OUT 0x0008 -#define INTR_EN0__PROGRAM_FAIL 0x0010 -#define INTR_EN0__ERASE_FAIL 0x0020 -#define INTR_EN0__LOAD_COMP 0x0040 -#define INTR_EN0__PROGRAM_COMP 0x0080 -#define INTR_EN0__ERASE_COMP 0x0100 -#define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN0__LOCKED_BLK 0x0400 -#define INTR_EN0__UNSUP_CMD 0x0800 -#define INTR_EN0__INT_ACT 0x1000 -#define INTR_EN0__RST_COMP 0x2000 -#define INTR_EN0__PIPE_CMD_ERR 0x4000 -#define INTR_EN0__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT0 0x430 -#define PAGE_CNT0__VALUE 0x00ff - -#define ERR_PAGE_ADDR0 0x440 -#define ERR_PAGE_ADDR0__VALUE 0xffff - -#define ERR_BLOCK_ADDR0 0x450 -#define ERR_BLOCK_ADDR0__VALUE 0xffff - -#define INTR_STATUS1 0x460 -#define INTR_STATUS1__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS1__ECC_ERR 0x0002 -#define INTR_STATUS1__DMA_CMD_COMP 0x0004 -#define INTR_STATUS1__TIME_OUT 0x0008 -#define INTR_STATUS1__PROGRAM_FAIL 0x0010 -#define INTR_STATUS1__ERASE_FAIL 0x0020 -#define INTR_STATUS1__LOAD_COMP 0x0040 -#define INTR_STATUS1__PROGRAM_COMP 0x0080 -#define INTR_STATUS1__ERASE_COMP 0x0100 -#define INTR_STATUS1__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS1__LOCKED_BLK 0x0400 -#define INTR_STATUS1__UNSUP_CMD 0x0800 -#define INTR_STATUS1__INT_ACT 0x1000 -#define INTR_STATUS1__RST_COMP 0x2000 -#define INTR_STATUS1__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS1__PAGE_XFER_INC 0x8000 - -#define INTR_EN1 0x470 -#define INTR_EN1__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN1__ECC_ERR 0x0002 -#define INTR_EN1__DMA_CMD_COMP 0x0004 -#define INTR_EN1__TIME_OUT 0x0008 -#define INTR_EN1__PROGRAM_FAIL 0x0010 -#define INTR_EN1__ERASE_FAIL 0x0020 -#define INTR_EN1__LOAD_COMP 0x0040 -#define INTR_EN1__PROGRAM_COMP 0x0080 -#define INTR_EN1__ERASE_COMP 0x0100 -#define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN1__LOCKED_BLK 0x0400 -#define INTR_EN1__UNSUP_CMD 0x0800 -#define INTR_EN1__INT_ACT 0x1000 -#define INTR_EN1__RST_COMP 0x2000 -#define INTR_EN1__PIPE_CMD_ERR 0x4000 -#define INTR_EN1__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT1 0x480 -#define PAGE_CNT1__VALUE 0x00ff - -#define ERR_PAGE_ADDR1 0x490 -#define ERR_PAGE_ADDR1__VALUE 0xffff - -#define ERR_BLOCK_ADDR1 0x4a0 -#define ERR_BLOCK_ADDR1__VALUE 0xffff - -#define INTR_STATUS2 0x4b0 -#define INTR_STATUS2__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS2__ECC_ERR 0x0002 -#define INTR_STATUS2__DMA_CMD_COMP 0x0004 -#define INTR_STATUS2__TIME_OUT 0x0008 -#define INTR_STATUS2__PROGRAM_FAIL 0x0010 -#define INTR_STATUS2__ERASE_FAIL 0x0020 -#define INTR_STATUS2__LOAD_COMP 0x0040 -#define INTR_STATUS2__PROGRAM_COMP 0x0080 -#define INTR_STATUS2__ERASE_COMP 0x0100 -#define INTR_STATUS2__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS2__LOCKED_BLK 0x0400 -#define INTR_STATUS2__UNSUP_CMD 0x0800 -#define INTR_STATUS2__INT_ACT 0x1000 -#define INTR_STATUS2__RST_COMP 0x2000 -#define INTR_STATUS2__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS2__PAGE_XFER_INC 0x8000 - -#define INTR_EN2 0x4c0 -#define INTR_EN2__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN2__ECC_ERR 0x0002 -#define INTR_EN2__DMA_CMD_COMP 0x0004 -#define INTR_EN2__TIME_OUT 0x0008 -#define INTR_EN2__PROGRAM_FAIL 0x0010 -#define INTR_EN2__ERASE_FAIL 0x0020 -#define INTR_EN2__LOAD_COMP 0x0040 -#define INTR_EN2__PROGRAM_COMP 0x0080 -#define INTR_EN2__ERASE_COMP 0x0100 -#define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN2__LOCKED_BLK 0x0400 -#define INTR_EN2__UNSUP_CMD 0x0800 -#define INTR_EN2__INT_ACT 0x1000 -#define INTR_EN2__RST_COMP 0x2000 -#define INTR_EN2__PIPE_CMD_ERR 0x4000 -#define INTR_EN2__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT2 0x4d0 -#define PAGE_CNT2__VALUE 0x00ff - -#define ERR_PAGE_ADDR2 0x4e0 -#define ERR_PAGE_ADDR2__VALUE 0xffff - -#define ERR_BLOCK_ADDR2 0x4f0 -#define ERR_BLOCK_ADDR2__VALUE 0xffff - -#define INTR_STATUS3 0x500 -#define INTR_STATUS3__ECC_TRANSACTION_DONE 0x0001 -#define INTR_STATUS3__ECC_ERR 0x0002 -#define INTR_STATUS3__DMA_CMD_COMP 0x0004 -#define INTR_STATUS3__TIME_OUT 0x0008 -#define INTR_STATUS3__PROGRAM_FAIL 0x0010 -#define INTR_STATUS3__ERASE_FAIL 0x0020 -#define INTR_STATUS3__LOAD_COMP 0x0040 -#define INTR_STATUS3__PROGRAM_COMP 0x0080 -#define INTR_STATUS3__ERASE_COMP 0x0100 -#define INTR_STATUS3__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_STATUS3__LOCKED_BLK 0x0400 -#define INTR_STATUS3__UNSUP_CMD 0x0800 -#define INTR_STATUS3__INT_ACT 0x1000 -#define INTR_STATUS3__RST_COMP 0x2000 -#define INTR_STATUS3__PIPE_CMD_ERR 0x4000 -#define INTR_STATUS3__PAGE_XFER_INC 0x8000 - -#define INTR_EN3 0x510 -#define INTR_EN3__ECC_TRANSACTION_DONE 0x0001 -#define INTR_EN3__ECC_ERR 0x0002 -#define INTR_EN3__DMA_CMD_COMP 0x0004 -#define INTR_EN3__TIME_OUT 0x0008 -#define INTR_EN3__PROGRAM_FAIL 0x0010 -#define INTR_EN3__ERASE_FAIL 0x0020 -#define INTR_EN3__LOAD_COMP 0x0040 -#define INTR_EN3__PROGRAM_COMP 0x0080 -#define INTR_EN3__ERASE_COMP 0x0100 -#define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN3__LOCKED_BLK 0x0400 -#define INTR_EN3__UNSUP_CMD 0x0800 -#define INTR_EN3__INT_ACT 0x1000 -#define INTR_EN3__RST_COMP 0x2000 -#define INTR_EN3__PIPE_CMD_ERR 0x4000 -#define INTR_EN3__PAGE_XFER_INC 0x8000 - -#define PAGE_CNT3 0x520 -#define PAGE_CNT3__VALUE 0x00ff - -#define ERR_PAGE_ADDR3 0x530 -#define ERR_PAGE_ADDR3__VALUE 0xffff - -#define ERR_BLOCK_ADDR3 0x540 -#define ERR_BLOCK_ADDR3__VALUE 0xffff +#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50)) +#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50)) + +#define INTR_STATUS__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS__ECC_ERR 0x0002 +#define INTR_STATUS__DMA_CMD_COMP 0x0004 +#define INTR_STATUS__TIME_OUT 0x0008 +#define INTR_STATUS__PROGRAM_FAIL 0x0010 +#define INTR_STATUS__ERASE_FAIL 0x0020 +#define INTR_STATUS__LOAD_COMP 0x0040 +#define INTR_STATUS__PROGRAM_COMP 0x0080 +#define INTR_STATUS__ERASE_COMP 0x0100 +#define INTR_STATUS__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS__LOCKED_BLK 0x0400 +#define INTR_STATUS__UNSUP_CMD 0x0800 +#define INTR_STATUS__INT_ACT 0x1000 +#define INTR_STATUS__RST_COMP 0x2000 +#define INTR_STATUS__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS__PAGE_XFER_INC 0x8000 + +#define INTR_EN__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN__ECC_ERR 0x0002 +#define INTR_EN__DMA_CMD_COMP 0x0004 +#define INTR_EN__TIME_OUT 0x0008 +#define INTR_EN__PROGRAM_FAIL 0x0010 +#define INTR_EN__ERASE_FAIL 0x0020 +#define INTR_EN__LOAD_COMP 0x0040 +#define INTR_EN__PROGRAM_COMP 0x0080 +#define INTR_EN__ERASE_COMP 0x0100 +#define INTR_EN__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN__LOCKED_BLK 0x0400 +#define INTR_EN__UNSUP_CMD 0x0800 +#define INTR_EN__INT_ACT 0x1000 +#define INTR_EN__RST_COMP 0x2000 +#define INTR_EN__PIPE_CMD_ERR 0x4000 +#define INTR_EN__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50)) +#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50)) +#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50)) #define DATA_INTR 0x550 #define DATA_INTR__WRITE_SPACE_AV 0x0001 @@ -484,141 +345,23 @@ #define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010 #define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 -#define PERM_SRC_ID_0 0x830 -#define PERM_SRC_ID_0__SRCID 0x00ff -#define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_0__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_0__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_0 0x840 -#define MIN_BLK_ADDR_0__VALUE 0xffff - -#define MAX_BLK_ADDR_0 0x850 -#define MAX_BLK_ADDR_0__VALUE 0xffff - -#define MIN_MAX_BANK_0 0x860 -#define MIN_MAX_BANK_0__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_0__MAX_VALUE 0x000c - -#define PERM_SRC_ID_1 0x870 -#define PERM_SRC_ID_1__SRCID 0x00ff -#define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_1__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_1__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_1 0x880 -#define MIN_BLK_ADDR_1__VALUE 0xffff - -#define MAX_BLK_ADDR_1 0x890 -#define MAX_BLK_ADDR_1__VALUE 0xffff - -#define MIN_MAX_BANK_1 0x8a0 -#define MIN_MAX_BANK_1__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_1__MAX_VALUE 0x000c - -#define PERM_SRC_ID_2 0x8b0 -#define PERM_SRC_ID_2__SRCID 0x00ff -#define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_2__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_2__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_2 0x8c0 -#define MIN_BLK_ADDR_2__VALUE 0xffff - -#define MAX_BLK_ADDR_2 0x8d0 -#define MAX_BLK_ADDR_2__VALUE 0xffff - -#define MIN_MAX_BANK_2 0x8e0 -#define MIN_MAX_BANK_2__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_2__MAX_VALUE 0x000c - -#define PERM_SRC_ID_3 0x8f0 -#define PERM_SRC_ID_3__SRCID 0x00ff -#define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_3__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_3__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_3 0x900 -#define MIN_BLK_ADDR_3__VALUE 0xffff - -#define MAX_BLK_ADDR_3 0x910 -#define MAX_BLK_ADDR_3__VALUE 0xffff - -#define MIN_MAX_BANK_3 0x920 -#define MIN_MAX_BANK_3__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_3__MAX_VALUE 0x000c - -#define PERM_SRC_ID_4 0x930 -#define PERM_SRC_ID_4__SRCID 0x00ff -#define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_4__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_4__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_4 0x940 -#define MIN_BLK_ADDR_4__VALUE 0xffff - -#define MAX_BLK_ADDR_4 0x950 -#define MAX_BLK_ADDR_4__VALUE 0xffff - -#define MIN_MAX_BANK_4 0x960 -#define MIN_MAX_BANK_4__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_4__MAX_VALUE 0x000c - -#define PERM_SRC_ID_5 0x970 -#define PERM_SRC_ID_5__SRCID 0x00ff -#define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_5__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_5__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_5 0x980 -#define MIN_BLK_ADDR_5__VALUE 0xffff - -#define MAX_BLK_ADDR_5 0x990 -#define MAX_BLK_ADDR_5__VALUE 0xffff - -#define MIN_MAX_BANK_5 0x9a0 -#define MIN_MAX_BANK_5__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_5__MAX_VALUE 0x000c - -#define PERM_SRC_ID_6 0x9b0 -#define PERM_SRC_ID_6__SRCID 0x00ff -#define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_6__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_6__PARTITION_VALID 0x8000 - -#define MIN_BLK_ADDR_6 0x9c0 -#define MIN_BLK_ADDR_6__VALUE 0xffff - -#define MAX_BLK_ADDR_6 0x9d0 -#define MAX_BLK_ADDR_6__VALUE 0xffff - -#define MIN_MAX_BANK_6 0x9e0 -#define MIN_MAX_BANK_6__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_6__MAX_VALUE 0x000c - -#define PERM_SRC_ID_7 0x9f0 -#define PERM_SRC_ID_7__SRCID 0x00ff -#define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 -#define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 -#define PERM_SRC_ID_7__READ_ACTIVE 0x4000 -#define PERM_SRC_ID_7__PARTITION_VALID 0x8000 +#define PERM_SRC_ID(__bank) (0x830 + ((__bank) * 0x40)) +#define PERM_SRC_ID__SRCID 0x00ff +#define PERM_SRC_ID__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID__READ_ACTIVE 0x4000 +#define PERM_SRC_ID__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR(__bank) (0x840 + ((__bank) * 0x40)) +#define MIN_BLK_ADDR__VALUE 0xffff + +#define MAX_BLK_ADDR(__bank) (0x850 + ((__bank) * 0x40)) +#define MAX_BLK_ADDR__VALUE 0xffff + +#define MIN_MAX_BANK(__bank) (0x860 + ((__bank) * 0x40)) +#define MIN_MAX_BANK__MIN_VALUE 0x0003 +#define MIN_MAX_BANK__MAX_VALUE 0x000c -#define MIN_BLK_ADDR_7 0xa00 -#define MIN_BLK_ADDR_7__VALUE 0xffff - -#define MAX_BLK_ADDR_7 0xa10 -#define MAX_BLK_ADDR_7__VALUE 0xffff - -#define MIN_MAX_BANK_7 0xa20 -#define MIN_MAX_BANK_7__MIN_VALUE 0x0003 -#define MIN_MAX_BANK_7__MAX_VALUE 0x000c /* ffsdefs.h */ #define CLEAR 0 /*use this to clear a field instead of "fail"*/ @@ -711,7 +454,6 @@ #define READ_WRITE_ENABLE_HIGH_COUNT 22 #define ECC_SECTOR_SIZE 512 -#define LLD_MAX_FLASH_BANKS 4 #define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) @@ -732,7 +474,7 @@ struct denali_nand_info { int status; int platform; struct nand_buf buf; - struct pci_dev *dev; + struct device *dev; int total_used_banks; uint32_t block; /* stored for future use */ uint16_t page; @@ -751,6 +493,7 @@ struct denali_nand_info { uint32_t totalblks; uint32_t blksperchip; uint32_t bbtskipbytes; + uint32_t max_banks; }; #endif /*_LLD_NAND_*/ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 657b9f4b6f9b..7837728d02ff 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1360,11 +1360,9 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd) At least as nand_bbt.c is currently written. */ if ((ret = nand_scan_bbt(mtd, NULL))) return ret; - add_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS + mtd_device_register(mtd, NULL, 0); if (!no_autopart) - add_mtd_partitions(mtd, parts, numparts); -#endif + mtd_device_register(mtd, parts, numparts); return 0; } @@ -1419,11 +1417,9 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) autopartitioning, but I want to give it more thought. */ if (!numparts) return -EIO; - add_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS + mtd_device_register(mtd, NULL, 0); if (!no_autopart) - add_mtd_partitions(mtd, parts, numparts); -#endif + mtd_device_register(mtd, parts, numparts); return 0; } @@ -1678,9 +1674,9 @@ static int __init doc_probe(unsigned long physadr) /* DBB note: i believe nand_release is necessary here, as buffers may have been allocated in nand_base. Check with Thomas. FIX ME! */ - /* nand_release will call del_mtd_device, but we haven't yet - added it. This is handled without incident by - del_mtd_device, as far as I can tell. */ + /* nand_release will call mtd_device_unregister, but we + haven't yet added it. This is handled without incident by + mtd_device_unregister, as far as I can tell. */ nand_release(mtd); kfree(mtd); goto fail; diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index 86366bfba9f8..8400d0f6dada 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -55,7 +55,6 @@ static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE; static void __iomem *ep7312_pxdr = (void __iomem *)EP7312_PXDR; static void __iomem *ep7312_pxddr = (void __iomem *)EP7312_PXDDR; -#ifdef CONFIG_MTD_PARTITIONS /* * Define static partitions for flash device */ @@ -67,8 +66,6 @@ static struct mtd_partition partition_info[] = { #define NUM_PARTITIONS 1 -#endif - /* * hardware specific access to control-lines * @@ -101,9 +98,7 @@ static int ep7312_device_ready(struct mtd_info *mtd) return 1; } -#ifdef CONFIG_MTD_PARTITIONS const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /* * Main initialization routine @@ -162,14 +157,12 @@ static int __init ep7312_init(void) kfree(ep7312_mtd); return -ENXIO; } -#ifdef CONFIG_MTD_PARTITIONS ep7312_mtd->name = "edb7312-nand"; mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0); if (mtd_parts_nb > 0) part_type = "command line"; else mtd_parts_nb = 0; -#endif if (mtd_parts_nb == 0) { mtd_parts = partition_info; mtd_parts_nb = NUM_PARTITIONS; @@ -178,7 +171,7 @@ static int __init ep7312_init(void) /* Register the partitions */ printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ep7312_mtd, mtd_parts, mtd_parts_nb); + mtd_device_register(ep7312_mtd, mtd_parts, mtd_parts_nb); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 537e380b8dcb..0bb254c7d2b1 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -841,12 +841,9 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) struct fsl_elbc_mtd *priv; struct resource res; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; - -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; struct mtd_partition *parts; -#endif int ret; int bank; struct device *dev; @@ -935,26 +932,19 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev) if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0); if (ret < 0) goto err; -#ifdef CONFIG_MTD_OF_PARTS if (ret == 0) { ret = of_mtd_parse_partitions(priv->dev, node, &parts); if (ret < 0) goto err; } -#endif - if (ret > 0) - add_mtd_partitions(&priv->mtd, parts, ret); - else -#endif - add_mtd_device(&priv->mtd); + mtd_device_register(&priv->mtd, parts, ret); printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 073ee026a17c..23752fd5bc59 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -33,10 +33,7 @@ struct fsl_upm_nand { struct mtd_info mtd; struct nand_chip chip; int last_ctrl; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif - struct fsl_upm upm; uint8_t upm_addr_offset; uint8_t upm_cmd_offset; @@ -161,9 +158,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, { int ret; struct device_node *flash_np; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_types[] = { "cmdlinepart", NULL, }; -#endif fun->chip.IO_ADDR_R = fun->io_base; fun->chip.IO_ADDR_W = fun->io_base; @@ -197,7 +192,6 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0); #ifdef CONFIG_MTD_OF_PARTS @@ -207,11 +201,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, goto err; } #endif - if (ret > 0) - ret = add_mtd_partitions(&fun->mtd, fun->parts, ret); - else -#endif - ret = add_mtd_device(&fun->mtd); + ret = mtd_device_register(&fun->mtd, fun->parts, ret); err: of_node_put(flash_np); return ret; diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 0d45ef3883e8..e9b275ac381c 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -120,8 +120,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { } }; - -#ifdef CONFIG_MTD_PARTITIONS /* * Default partition tables to be used if the partition information not * provided through platform data. @@ -182,7 +180,6 @@ static struct mtd_partition partition_info_128KB_blk[] = { #ifdef CONFIG_MTD_CMDLINE_PARTS const char *part_probes[] = { "cmdlinepart", NULL }; #endif -#endif /** * struct fsmc_nand_data - structure for FSMC NAND device state @@ -719,7 +716,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * platform data, * default partition information present in driver. */ -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS /* * Check if partition info passed via command line @@ -777,19 +773,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } #endif - if (host->partitions) { - ret = add_mtd_partitions(&host->mtd, host->partitions, - host->nr_partitions); - if (ret) - goto err_probe; - } -#else - dev_info(&pdev->dev, "Registering %s as whole device\n", mtd->name); - if (!add_mtd_device(mtd)) { - ret = -ENXIO; + ret = mtd_device_register(&host->mtd, host->partitions, + host->nr_partitions); + if (ret) goto err_probe; - } -#endif platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); @@ -835,11 +822,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (host) { -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(&host->mtd); -#else - del_mtd_device(&host->mtd); -#endif + mtd_device_unregister(&host->mtd); clk_disable(host->clk); clk_put(host->clk); diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 0cde618bcc1e..2c2060b2800e 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -316,8 +316,8 @@ static int __devinit gpio_nand_probe(struct platform_device *dev) gpiomtd->plat.adjust_parts(&gpiomtd->plat, gpiomtd->mtd_info.size); - add_mtd_partitions(&gpiomtd->mtd_info, gpiomtd->plat.parts, - gpiomtd->plat.num_parts); + mtd_device_register(&gpiomtd->mtd_info, gpiomtd->plat.parts, + gpiomtd->plat.num_parts); platform_set_drvdata(dev, gpiomtd); return 0; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index f8ce79b446ed..02a03e67109c 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -38,7 +38,6 @@ static struct mtd_info *h1910_nand_mtd = NULL; * Module stuff */ -#ifdef CONFIG_MTD_PARTITIONS /* * Define static partitions for flash device */ @@ -50,8 +49,6 @@ static struct mtd_partition partition_info[] = { #define NUM_PARTITIONS 1 -#endif - /* * hardware specific access to control-lines * @@ -154,7 +151,7 @@ static int __init h1910_init(void) /* Register the partitions */ printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); + mtd_device_register(h1910_nand_mtd, mtd_parts, mtd_parts_nb); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index cea38a5d4ac5..6e813daed068 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -299,10 +299,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) struct nand_chip *chip; struct mtd_info *mtd; struct jz_nand_platform_data *pdata = pdev->dev.platform_data; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *partition_info; int num_partitions = 0; -#endif nand = kzalloc(sizeof(*nand), GFP_KERNEL); if (!nand) { @@ -375,7 +373,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) goto err_gpio_free; } -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS num_partitions = parse_mtd_partitions(mtd, part_probes, &partition_info, 0); @@ -384,12 +381,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) num_partitions = pdata->num_partitions; partition_info = pdata->partitions; } - - if (num_partitions > 0) - ret = add_mtd_partitions(mtd, partition_info, num_partitions); - else -#endif - ret = add_mtd_device(mtd); + ret = mtd_device_register(mtd, partition_info, num_partitions); if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 0b81b5b499d1..2f7c930872f9 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -131,9 +131,7 @@ struct mpc5121_nfc_prv { static void mpc5121_nfc_done(struct mtd_info *mtd); -#ifdef CONFIG_MTD_PARTITIONS static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL }; -#endif /* Read NFC register */ static inline u16 nfc_read(struct mtd_info *mtd, uint reg) @@ -658,9 +656,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) struct mpc5121_nfc_prv *prv; struct resource res; struct mtd_info *mtd; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif struct nand_chip *chip; unsigned long regs_paddr, regs_size; const __be32 *chips_no; @@ -841,7 +837,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) dev_set_drvdata(dev, mtd); /* Register device in MTD */ -#ifdef CONFIG_MTD_PARTITIONS retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0); #ifdef CONFIG_MTD_OF_PARTS if (retval == 0) @@ -854,12 +849,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) goto error; } - if (retval > 0) - retval = add_mtd_partitions(mtd, parts, retval); - else -#endif - retval = add_mtd_device(mtd); - + retval = mtd_device_register(mtd, parts, retval); if (retval) { dev_err(dev, "Error adding MTD device!\n"); devm_free_irq(dev, prv->irq, mtd); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 42a95fb41504..90df34c4d26c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -56,8 +56,14 @@ #define NFC_V1_V2_WRPROT (host->regs + 0x12) #define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) #define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) -#define NFC_V21_UNLOCKSTART_BLKADDR (host->regs + 0x20) -#define NFC_V21_UNLOCKEND_BLKADDR (host->regs + 0x22) +#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) +#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) +#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) +#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) +#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) +#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) +#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) +#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) #define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) #define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) #define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) @@ -152,6 +158,7 @@ struct mxc_nand_host { int clk_act; int irq; int eccsize; + int active_cs; struct completion op_completion; @@ -236,9 +243,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; -#endif static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { @@ -445,7 +450,7 @@ static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) for (i = 0; i < bufs; i++) { /* NANDFC buffer 0 is used for page read/write */ - writew(i, NFC_V1_V2_BUF_ADDR); + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); writew(ops, NFC_V1_V2_CONFIG2); @@ -470,7 +475,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) struct nand_chip *this = &host->nand; /* NANDFC buffer 0 is used for device ID output */ - writew(0x0, NFC_V1_V2_BUF_ADDR); + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); writew(NFC_ID, NFC_V1_V2_CONFIG2); @@ -505,7 +510,7 @@ static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) uint32_t store; uint16_t ret; - writew(0x0, NFC_V1_V2_BUF_ADDR); + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); /* * The device status is stored in main_area0. To @@ -686,24 +691,24 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - switch (chip) { - case -1: + if (chip == -1) { /* Disable the NFC clock */ if (host->clk_act) { clk_disable(host->clk); host->clk_act = 0; } - break; - case 0: + return; + } + + if (!host->clk_act) { /* Enable the NFC clock */ - if (!host->clk_act) { - clk_enable(host->clk); - host->clk_act = 1; - } - break; + clk_enable(host->clk); + host->clk_act = 1; + } - default: - break; + if (nfc_is_v21()) { + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } } @@ -834,8 +839,14 @@ static void preset_v1_v2(struct mtd_info *mtd) /* Blocks to be unlocked */ if (nfc_is_v21()) { - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); } else if (nfc_is_v1()) { writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); writew(0x4000, NFC_V1_UNLOCKEND_BLKADDR); @@ -1200,7 +1211,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) irq_control_v1_v2(host, 1); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { + if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) { err = -ENXIO; goto escan; } @@ -1220,18 +1231,15 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Register the partitions */ -#ifdef CONFIG_MTD_PARTITIONS nr_parts = parse_mtd_partitions(mtd, part_probes, &host->parts, 0); if (nr_parts > 0) - add_mtd_partitions(mtd, host->parts, nr_parts); + mtd_device_register(mtd, host->parts, nr_parts); else if (pdata->parts) - add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); - else -#endif - { + mtd_device_register(mtd, pdata->parts, pdata->nr_parts); + else { pr_info("Registering %s as whole device\n", mtd->name); - add_mtd_device(mtd); + mtd_device_register(mtd, NULL, 0); } platform_set_drvdata(pdev, host); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c54a4cbac6bc..a46e9bb847bd 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -47,10 +47,7 @@ #include <linux/bitops.h> #include <linux/leds.h> #include <linux/io.h> - -#ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> -#endif /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -976,9 +973,6 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0); out: - /* de-select the NAND device */ - chip->select_chip(mtd, -1); - nand_release_device(mtd); return ret; @@ -1046,9 +1040,6 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ret = __nand_unlock(mtd, ofs, len, 0x1); out: - /* de-select the NAND device */ - chip->select_chip(mtd, -1); - nand_release_device(mtd); return ret; @@ -3112,6 +3103,8 @@ ident_done: chip->chip_shift += 32 - 1; } + chip->badblockbits = 8; + /* Set the bad block position */ if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) chip->badblockpos = NAND_LARGE_BADBLOCK_POS; @@ -3539,12 +3532,7 @@ void nand_release(struct mtd_info *mtd) if (chip->ecc.mode == NAND_ECC_SOFT_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); -#ifdef CONFIG_MTD_PARTITIONS - /* Deregister partitions */ - del_mtd_partitions(mtd); -#endif - /* Deregister the device */ - del_mtd_device(mtd); + mtd_device_unregister(mtd); /* Free bad block table memory */ kfree(chip->bbt); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index af46428286fe..ccbeaa1e4a8e 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1276,20 +1276,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; -static struct nand_bbt_descr smallpage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = NAND_SMALL_BADBLOCK_POS, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr largepage_flashbased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = NAND_LARGE_BADBLOCK_POS, - .len = 2, - .pattern = scan_ff_pattern -}; - static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; static struct nand_bbt_descr agand_flashbased = { @@ -1355,10 +1341,6 @@ static struct nand_bbt_descr bbt_mirror_no_bbt_descr = { * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when * passed to this function. * - * TODO: Handle other flags, replace other static structs - * (e.g. handle NAND_BBT_FLASH for flash-based BBT, - * replace smallpage_flashbased) - * */ static int nand_create_default_bbt_descr(struct nand_chip *this) { @@ -1422,15 +1404,14 @@ int nand_default_bbt(struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; } } - if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; - } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (!this->badblock_pattern) - nand_create_default_bbt_descr(this); } + + if (!this->badblock_pattern) + nand_create_default_bbt_descr(this); + return nand_scan_bbt(mtd, this->badblock_pattern); } diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 893d95bfea48..357e8c5252a8 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2383,7 +2383,9 @@ static int __init ns_init_module(void) goto err_exit; /* Register NAND partitions */ - if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0) + retval = mtd_device_register(nsmtd, &nand->partitions[0], + nand->nbparts); + if (retval != 0) goto err_exit; return 0; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index bbe6d451290d..ea2dea8a9c88 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -33,6 +33,7 @@ #include <linux/of_platform.h> #include <asm/io.h> +#define NDFC_MAX_CS 4 struct ndfc_controller { struct platform_device *ofdev; @@ -41,17 +42,16 @@ struct ndfc_controller { struct nand_chip chip; int chip_select; struct nand_hw_control ndfc_control; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif }; -static struct ndfc_controller ndfc_ctrl; +static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; static void ndfc_select_chip(struct mtd_info *mtd, int chip) { uint32_t ccr; - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *nchip = mtd->priv; + struct ndfc_controller *ndfc = nchip->priv; ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); if (chip >= 0) { @@ -64,7 +64,8 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; if (cmd == NAND_CMD_NONE) return; @@ -77,7 +78,8 @@ static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) static int ndfc_ready(struct mtd_info *mtd) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; } @@ -85,7 +87,8 @@ static int ndfc_ready(struct mtd_info *mtd) static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) { uint32_t ccr; - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); ccr |= NDFC_CCR_RESET_ECC; @@ -96,7 +99,8 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) static int ndfc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t ecc; uint8_t *p = (uint8_t *)&ecc; @@ -119,7 +123,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, */ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) @@ -128,7 +133,8 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) @@ -137,7 +143,8 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct nand_chip *chip = mtd->priv; + struct ndfc_controller *ndfc = chip->priv; uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) @@ -152,13 +159,11 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) static int ndfc_chip_init(struct ndfc_controller *ndfc, struct device_node *node) { -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS static const char *part_types[] = { "cmdlinepart", NULL }; #else static const char *part_types[] = { NULL }; #endif -#endif struct device_node *flash_np; struct nand_chip *chip = &ndfc->chip; int ret; @@ -179,6 +184,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->priv = ndfc; ndfc->mtd.priv = chip; ndfc->mtd.owner = THIS_MODULE; @@ -198,25 +204,18 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, if (ret) goto err; -#ifdef CONFIG_MTD_PARTITIONS ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); if (ret < 0) goto err; -#ifdef CONFIG_MTD_OF_PARTS if (ret == 0) { ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, &ndfc->parts); if (ret < 0) goto err; } -#endif - if (ret > 0) - ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); - else -#endif - ret = add_mtd_device(&ndfc->mtd); + ret = mtd_device_register(&ndfc->mtd, ndfc->parts, ret); err: of_node_put(flash_np); @@ -227,15 +226,10 @@ err: static int __devinit ndfc_probe(struct platform_device *ofdev) { - struct ndfc_controller *ndfc = &ndfc_ctrl; + struct ndfc_controller *ndfc; const __be32 *reg; u32 ccr; - int err, len; - - spin_lock_init(&ndfc->ndfc_control.lock); - init_waitqueue_head(&ndfc->ndfc_control.wq); - ndfc->ofdev = ofdev; - dev_set_drvdata(&ofdev->dev, ndfc); + int err, len, cs; /* Read the reg property to get the chip select */ reg = of_get_property(ofdev->dev.of_node, "reg", &len); @@ -243,7 +237,20 @@ static int __devinit ndfc_probe(struct platform_device *ofdev) dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); return -ENOENT; } - ndfc->chip_select = be32_to_cpu(reg[0]); + + cs = be32_to_cpu(reg[0]); + if (cs >= NDFC_MAX_CS) { + dev_err(&ofdev->dev, "invalid CS number (%d)\n", cs); + return -EINVAL; + } + + ndfc = &ndfc_ctrl[cs]; + ndfc->chip_select = cs; + + spin_lock_init(&ndfc->ndfc_control.lock); + init_waitqueue_head(&ndfc->ndfc_control.wq); + ndfc->ofdev = ofdev; + dev_set_drvdata(&ofdev->dev, ndfc); ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); if (!ndfc->ndfcbase) { diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c index a045a4a581b6..b6a5c86ab31e 100644 --- a/drivers/mtd/nand/nomadik_nand.c +++ b/drivers/mtd/nand/nomadik_nand.c @@ -158,12 +158,7 @@ static int nomadik_nand_probe(struct platform_device *pdev) goto err_unmap; } -#ifdef CONFIG_MTD_PARTITIONS - add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts); -#else - pr_info("Registering %s as whole device\n", mtd->name); - add_mtd_device(mtd); -#endif + mtd_device_register(&host->mtd, pdata->parts, pdata->nparts); platform_set_drvdata(pdev, host); return 0; diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 6eddf7361ed7..9c30a0b03171 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -321,8 +321,8 @@ static int __devinit nuc900_nand_probe(struct platform_device *pdev) goto fail3; } - add_mtd_partitions(&(nuc900_nand->mtd), partitions, - ARRAY_SIZE(partitions)); + mtd_device_register(&(nuc900_nand->mtd), partitions, + ARRAY_SIZE(partitions)); platform_set_drvdata(pdev, nuc900_nand); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index da9a351c9d79..0db2c0e7656a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -94,9 +94,7 @@ #define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) #define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /* oob info generated runtime depending on ecc algorithm and layout selected */ static struct nand_ecclayout omap_oobinfo; @@ -263,11 +261,10 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) - omap_read_buf16(mtd, buf, len); + omap_read_buf16(mtd, (u_char *)p, len); else - omap_read_buf8(mtd, buf, len); + omap_read_buf8(mtd, (u_char *)p, len); } else { - p = (u32 *) buf; do { r_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); r_count = r_count >> 2; @@ -293,7 +290,7 @@ static void omap_write_buf_pref(struct mtd_info *mtd, struct omap_nand_info, mtd); uint32_t w_count = 0; int i = 0, ret = 0; - u16 *p; + u16 *p = (u16 *)buf; unsigned long tim, limit; /* take care of subpage writes */ @@ -309,11 +306,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd, if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) - omap_write_buf16(mtd, buf, len); + omap_write_buf16(mtd, (u_char *)p, len); else - omap_write_buf8(mtd, buf, len); + omap_write_buf8(mtd, (u_char *)p, len); } else { - p = (u16 *) buf; while (len) { w_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); w_count = w_count >> 1; @@ -1073,9 +1069,9 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) /* DIP switches on some boards change between 8 and 16 bit * bus widths for flash. Try the other width if the first try fails. */ - if (nand_scan(&info->mtd, 1)) { + if (nand_scan_ident(&info->mtd, 1, NULL)) { info->nand.options ^= NAND_BUSWIDTH_16; - if (nand_scan(&info->mtd, 1)) { + if (nand_scan_ident(&info->mtd, 1, NULL)) { err = -ENXIO; goto out_release_mem_region; } @@ -1101,15 +1097,19 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.ecc.layout = &omap_oobinfo; } -#ifdef CONFIG_MTD_PARTITIONS + /* second phase scan */ + if (nand_scan_tail(&info->mtd)) { + err = -ENXIO; + goto out_release_mem_region; + } + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) - add_mtd_partitions(&info->mtd, info->parts, err); + mtd_device_register(&info->mtd, info->parts, err); else if (pdata->parts) - add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + mtd_device_register(&info->mtd, pdata->parts, pdata->nr_parts); else -#endif - add_mtd_device(&info->mtd); + mtd_device_register(&info->mtd, NULL, 0); platform_set_drvdata(pdev, &info->mtd); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index da6e75343052..7794d0680f91 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -21,9 +21,7 @@ #include <mach/hardware.h> #include <plat/orion_nand.h> -#ifdef CONFIG_MTD_CMDLINE_PARTS static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { @@ -83,10 +81,8 @@ static int __init orion_nand_probe(struct platform_device *pdev) struct resource *res; void __iomem *io_base; int ret = 0; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *partitions = NULL; int num_part = 0; -#endif nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL); if (!nc) { @@ -136,7 +132,6 @@ static int __init orion_nand_probe(struct platform_device *pdev) goto no_dev; } -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS mtd->name = "orion_nand"; num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0); @@ -147,14 +142,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) partitions = board->parts; } - if (partitions && num_part > 0) - ret = add_mtd_partitions(mtd, partitions, num_part); - else - ret = add_mtd_device(mtd); -#else - ret = add_mtd_device(mtd); -#endif - + ret = mtd_device_register(mtd, partitions, num_part); if (ret) { nand_release(mtd); goto no_dev; diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 20bfe5f15afd..b1aa41b8a4eb 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -163,7 +163,7 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev) goto out_lpc; } - if (add_mtd_device(pasemi_nand_mtd)) { + if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n"); err = -ENODEV; goto out_lpc; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index caf5a736340a..633c04bf76f6 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -21,10 +21,8 @@ struct plat_nand_data { struct nand_chip chip; struct mtd_info mtd; void __iomem *io_base; -#ifdef CONFIG_MTD_PARTITIONS int nr_parts; struct mtd_partition *parts; -#endif }; /* @@ -101,13 +99,12 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) goto out; } -#ifdef CONFIG_MTD_PARTITIONS if (pdata->chip.part_probe_types) { err = parse_mtd_partitions(&data->mtd, pdata->chip.part_probe_types, &data->parts, 0); if (err > 0) { - add_mtd_partitions(&data->mtd, data->parts, err); + mtd_device_register(&data->mtd, data->parts, err); return 0; } } @@ -115,11 +112,10 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) pdata->chip.set_parts(data->mtd.size, &pdata->chip); if (pdata->chip.partitions) { data->parts = pdata->chip.partitions; - err = add_mtd_partitions(&data->mtd, data->parts, + err = mtd_device_register(&data->mtd, data->parts, pdata->chip.nr_partitions); } else -#endif - err = add_mtd_device(&data->mtd); + err = mtd_device_register(&data->mtd, NULL, 0); if (!err) return err; @@ -149,10 +145,8 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nand_release(&data->mtd); -#ifdef CONFIG_MTD_PARTITIONS if (data->parts && data->parts != pdata->chip.partitions) kfree(data->parts); -#endif if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); iounmap(data->io_base); diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index cc8658431851..3bbb796b451c 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -73,7 +73,6 @@ __setup("ppchameleon_fio_pbase=", ppchameleon_fio_pbase); __setup("ppchameleonevb_fio_pbase=", ppchameleonevb_fio_pbase); #endif -#ifdef CONFIG_MTD_PARTITIONS /* * Define static partitions for flash devices */ @@ -101,7 +100,6 @@ static struct mtd_partition partition_info_evb[] = { #define NUM_PARTITIONS 1 extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, const char *mtd_id); -#endif /* * hardware specific access to control-lines @@ -189,10 +187,8 @@ static int ppchameleonevb_device_ready(struct mtd_info *minfo) } #endif -#ifdef CONFIG_MTD_PARTITIONS const char *part_probes[] = { "cmdlinepart", NULL }; const char *part_probes_evb[] = { "cmdlinepart", NULL }; -#endif /* * Main initialization routine @@ -284,14 +280,13 @@ static int __init ppchameleonevb_init(void) this->chip_delay = NAND_SMALL_DELAY_US; #endif -#ifdef CONFIG_MTD_PARTITIONS ppchameleon_mtd->name = "ppchameleon-nand"; mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0); if (mtd_parts_nb > 0) part_type = "command line"; else mtd_parts_nb = 0; -#endif + if (mtd_parts_nb == 0) { if (ppchameleon_mtd->size == NAND_SMALL_SIZE) mtd_parts = partition_info_me; @@ -303,7 +298,7 @@ static int __init ppchameleonevb_init(void) /* Register the partitions */ printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb); + mtd_device_register(ppchameleon_mtd, mtd_parts, mtd_parts_nb); nand_evb_init: /**************************** @@ -385,14 +380,14 @@ static int __init ppchameleonevb_init(void) iounmap(ppchameleon_fio_base); return -ENXIO; } -#ifdef CONFIG_MTD_PARTITIONS + ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0); if (mtd_parts_nb > 0) part_type = "command line"; else mtd_parts_nb = 0; -#endif + if (mtd_parts_nb == 0) { mtd_parts = partition_info_evb; mtd_parts_nb = NUM_PARTITIONS; @@ -401,7 +396,7 @@ static int __init ppchameleonevb_init(void) /* Register the partitions */ printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb); + mtd_device_register(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ff0701276d65..1fb3b3a80581 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1119,10 +1119,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) clk_put(info->clk); if (mtd) { - del_mtd_device(mtd); -#ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(mtd); -#endif + mtd_device_unregister(mtd); kfree(mtd); } return 0; @@ -1149,7 +1146,6 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) return -ENODEV; } -#ifdef CONFIG_MTD_PARTITIONS if (mtd_has_cmdlinepart()) { const char *probes[] = { "cmdlinepart", NULL }; struct mtd_partition *parts; @@ -1158,13 +1154,10 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0); if (nr_parts) - return add_mtd_partitions(info->mtd, parts, nr_parts); + return mtd_device_register(info->mtd, parts, nr_parts); } - return add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); -#else - return 0; -#endif + return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts); } #ifdef CONFIG_PM diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 67440b5beef8..c9f9127ff770 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -580,7 +580,8 @@ static int __init rtc_from4_init(void) #endif /* Register the partitions */ - ret = add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + ret = mtd_device_register(rtc_from4_mtd, partition_info, + NUM_PARTITIONS); if (ret) goto err_3; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 33d832dddfdd..4405468f196b 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -55,7 +55,7 @@ static int hardware_ecc = 0; #endif #ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP -static int clock_stop = 1; +static const int clock_stop = 1; #else static const int clock_stop = 0; #endif @@ -96,6 +96,12 @@ enum s3c_cpu_type { TYPE_S3C2440, }; +enum s3c_nand_clk_state { + CLOCK_DISABLE = 0, + CLOCK_ENABLE, + CLOCK_SUSPEND, +}; + /* overview of the s3c2410 nand state */ /** @@ -111,6 +117,7 @@ enum s3c_cpu_type { * @mtd_count: The number of MTDs created from this controller. * @save_sel: The contents of @sel_reg to be saved over suspend. * @clk_rate: The clock rate from @clk. + * @clk_state: The current clock state. * @cpu_type: The exact type of this controller. */ struct s3c2410_nand_info { @@ -129,6 +136,7 @@ struct s3c2410_nand_info { int mtd_count; unsigned long save_sel; unsigned long clk_rate; + enum s3c_nand_clk_state clk_state; enum s3c_cpu_type cpu_type; @@ -159,11 +167,33 @@ static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) return dev->dev.platform_data; } -static inline int allow_clk_stop(struct s3c2410_nand_info *info) +static inline int allow_clk_suspend(struct s3c2410_nand_info *info) { return clock_stop; } +/** + * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock. + * @info: The controller instance. + * @new_state: State to which clock should be set. + */ +static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info, + enum s3c_nand_clk_state new_state) +{ + if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND) + return; + + if (info->clk_state == CLOCK_ENABLE) { + if (new_state != CLOCK_ENABLE) + clk_disable(info->clk); + } else { + if (new_state == CLOCK_ENABLE) + clk_enable(info->clk); + } + + info->clk_state = new_state; +} + /* timing calculations */ #define NS_IN_KHZ 1000000 @@ -333,8 +363,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) nmtd = this->priv; info = nmtd->info; - if (chip != -1 && allow_clk_stop(info)) - clk_enable(info->clk); + if (chip != -1) + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); cur = readl(info->sel_reg); @@ -356,8 +386,8 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) writel(cur, info->sel_reg); - if (chip == -1 && allow_clk_stop(info)) - clk_disable(info->clk); + if (chip == -1) + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } /* s3c2410_nand_hwcontrol @@ -694,8 +724,7 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) /* free the common resources */ if (info->clk != NULL && !IS_ERR(info->clk)) { - if (!allow_clk_stop(info)) - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); clk_put(info->clk); } @@ -715,7 +744,6 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_MTD_PARTITIONS const char *part_probes[] = { "cmdlinepart", NULL }; static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, @@ -725,7 +753,7 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, int nr_part = 0; if (set == NULL) - return add_mtd_device(&mtd->mtd); + return mtd_device_register(&mtd->mtd, NULL, 0); mtd->mtd.name = set->name; nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, &part_info, 0); @@ -735,19 +763,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, part_info = set->partitions; } - if (nr_part > 0 && part_info) - return add_mtd_partitions(&mtd->mtd, part_info, nr_part); - - return add_mtd_device(&mtd->mtd); -} -#else -static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *mtd, - struct s3c2410_nand_set *set) -{ - return add_mtd_device(&mtd->mtd); + return mtd_device_register(&mtd->mtd, part_info, nr_part); } -#endif /** * s3c2410_nand_init_chip - initialise a single instance of an chip @@ -947,7 +964,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) goto exit_error; } - clk_enable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); /* allocate and map the resource */ @@ -1026,9 +1043,9 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) goto exit_error; } - if (allow_clk_stop(info)) { + if (allow_clk_suspend(info)) { dev_info(&pdev->dev, "clock idle support enabled\n"); - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } pr_debug("initialised ok\n"); @@ -1059,8 +1076,7 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) writel(info->save_sel | info->sel_bit, info->sel_reg); - if (!allow_clk_stop(info)) - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_DISABLE); } return 0; @@ -1072,7 +1088,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) unsigned long sel; if (info) { - clk_enable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); s3c2410_nand_inithw(info); /* Restore the state of the nFCE line. */ @@ -1082,8 +1098,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) sel |= info->save_sel & info->sel_bit; writel(sel, info->sel_reg); - if (allow_clk_stop(info)) - clk_disable(info->clk); + s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND); } return 0; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 81bbb5ee148d..93b1f74321c2 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -867,7 +867,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) if (ret) goto err; - add_mtd_partitions(flctl_mtd, pdata->parts, pdata->nr_parts); + mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); return 0; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 54ec7542a7b7..19e24ed089ea 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -103,9 +103,7 @@ static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, return readb(sharpsl->io + ECCCNTR) != 0; } -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /* * Main initialization routine @@ -113,10 +111,8 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; static int __devinit sharpsl_nand_probe(struct platform_device *pdev) { struct nand_chip *this; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *sharpsl_partition_info; int nr_partitions; -#endif struct resource *r; int err = 0; struct sharpsl_nand *sharpsl; @@ -188,18 +184,14 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* Register the partitions */ sharpsl->mtd.name = "sharpsl-nand"; -#ifdef CONFIG_MTD_PARTITIONS nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0); if (nr_partitions <= 0) { nr_partitions = data->nr_partitions; sharpsl_partition_info = data->partitions; } - if (nr_partitions > 0) - err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions); - else -#endif - err = add_mtd_device(&sharpsl->mtd); + err = mtd_device_register(&sharpsl->mtd, sharpsl_partition_info, + nr_partitions); if (err) goto err_add; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 57cc80cd01a3..b6332e83b289 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -139,7 +139,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) if (ret) return ret; - return add_mtd_device(mtd); + return mtd_device_register(mtd, NULL, 0); } EXPORT_SYMBOL_GPL(sm_register_device); diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index a853548986f0..ca2d0555729e 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -155,9 +155,7 @@ static int socrates_nand_device_ready(struct mtd_info *mtd) return 1; } -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; -#endif /* * Probe for the NAND device. @@ -168,11 +166,8 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev) struct mtd_info *mtd; struct nand_chip *nand_chip; int res; - -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *partitions = NULL; int num_partitions = 0; -#endif /* Allocate memory for the device structure (and zero it) */ host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL); @@ -230,7 +225,6 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev) goto out; } -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); @@ -240,7 +234,6 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev) } #endif -#ifdef CONFIG_MTD_OF_PARTS if (num_partitions == 0) { num_partitions = of_mtd_parse_partitions(&ofdev->dev, ofdev->dev.of_node, @@ -250,19 +243,12 @@ static int __devinit socrates_nand_probe(struct platform_device *ofdev) goto release; } } -#endif - if (partitions && (num_partitions > 0)) - res = add_mtd_partitions(mtd, partitions, num_partitions); - else -#endif - res = add_mtd_device(mtd); + res = mtd_device_register(mtd, partitions, num_partitions); if (!res) return res; -#ifdef CONFIG_MTD_PARTITIONS release: -#endif nand_release(mtd); out: diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 0cc6d0acb8fe..bef76cd7c24c 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -149,7 +149,7 @@ static int __init spia_init(void) } /* Register the partitions */ - add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); + mtd_device_register(spia_mtd, partition_info, NUM_PARTITIONS); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index c004e474631b..11e8371b5683 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -381,10 +381,8 @@ static int tmio_probe(struct platform_device *dev) struct tmio_nand *tmio; struct mtd_info *mtd; struct nand_chip *nand_chip; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; int nbparts = 0; -#endif int retval; if (data == NULL) @@ -463,7 +461,6 @@ static int tmio_probe(struct platform_device *dev) goto err_scan; } /* Register the partitions */ -#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS nbparts = parse_mtd_partitions(mtd, part_probes, &parts, 0); #endif @@ -472,12 +469,7 @@ static int tmio_probe(struct platform_device *dev) nbparts = data->num_partitions; } - if (nbparts) - retval = add_mtd_partitions(mtd, parts, nbparts); - else -#endif - retval = add_mtd_device(mtd); - + retval = mtd_device_register(mtd, parts, nbparts); if (!retval) return retval; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index ca270a4881a4..bfba4e39a6c5 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -74,9 +74,7 @@ struct txx9ndfmc_drvdata { unsigned char hold; /* in gbusclock */ unsigned char spw; /* in gbusclock */ struct nand_hw_control hw_control; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts[MAX_TXX9NDFMC_DEV]; -#endif }; static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) @@ -289,9 +287,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) static int __init txx9ndfmc_probe(struct platform_device *dev) { struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; -#ifdef CONFIG_MTD_PARTITIONS static const char *probes[] = { "cmdlinepart", NULL }; -#endif int hold, spw; int i; struct txx9ndfmc_drvdata *drvdata; @@ -337,9 +333,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) struct txx9ndfmc_priv *txx9_priv; struct nand_chip *chip; struct mtd_info *mtd; -#ifdef CONFIG_MTD_PARTITIONS int nr_parts; -#endif if (!(plat->ch_mask & (1 << i))) continue; @@ -399,13 +393,9 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) } mtd->name = txx9_priv->mtdname; -#ifdef CONFIG_MTD_PARTITIONS nr_parts = parse_mtd_partitions(mtd, probes, &drvdata->parts[i], 0); - if (nr_parts > 0) - add_mtd_partitions(mtd, drvdata->parts[i], nr_parts); -#endif - add_mtd_device(mtd); + mtd_device_register(mtd, drvdata->parts[i], nr_parts); drvdata->mtds[i] = mtd; } @@ -431,9 +421,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) txx9_priv = chip->priv; nand_release(mtd); -#ifdef CONFIG_MTD_PARTITIONS kfree(drvdata->parts[i]); -#endif kfree(txx9_priv->mtdname); kfree(txx9_priv); } diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 4f426195f8db..772ad2966619 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -1,7 +1,6 @@ menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD - select MTD_PARTITIONS help This enables support for accessing all type of OneNAND flash devices. For further information see diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index ac08750748a3..2d70d354d846 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -30,9 +30,7 @@ */ #define DRIVER_NAME "onenand-flash" -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL, }; -#endif struct onenand_info { struct mtd_info mtd; @@ -75,15 +73,13 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) goto out_iounmap; } -#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) - add_mtd_partitions(&info->mtd, info->parts, err); + mtd_device_register(&info->mtd, info->parts, err); else if (err <= 0 && pdata && pdata->parts) - add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + mtd_device_register(&info->mtd, pdata->parts, pdata->nr_parts); else -#endif - err = add_mtd_device(&info->mtd); + err = mtd_device_register(&info->mtd, NULL, 0); platform_set_drvdata(pdev, info); @@ -108,11 +104,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (info) { - if (info->parts) - del_mtd_partitions(&info->mtd); - else - del_mtd_device(&info->mtd); - + mtd_device_unregister(&info->mtd); onenand_release(&info->mtd); release_mem_region(res->start, size); iounmap(info->onenand.base); diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 1fcb41adab07..a916dec29215 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -67,9 +67,7 @@ struct omap2_onenand { struct regulator *regulator; }; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL, }; -#endif static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data) { @@ -755,15 +753,13 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_regulator; -#ifdef CONFIG_MTD_PARTITIONS r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0); if (r > 0) - r = add_mtd_partitions(&c->mtd, c->parts, r); + r = mtd_device_register(&c->mtd, c->parts, r); else if (pdata->parts != NULL) - r = add_mtd_partitions(&c->mtd, pdata->parts, pdata->nr_parts); + r = mtd_device_register(&c->mtd, pdata->parts, pdata->nr_parts); else -#endif - r = add_mtd_device(&c->mtd); + r = mtd_device_register(&c->mtd, NULL, 0); if (r) goto err_release_onenand; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 56a8b2005bda..ac9e959802a7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -65,11 +65,11 @@ MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP" " : 2 -> 1st Block lock" " : 3 -> BOTH OTP Block and 1st Block lock"); -/** - * onenand_oob_128 - oob info for Flex-Onenand with 4KB page - * For now, we expose only 64 out of 80 ecc bytes +/* + * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes */ -static struct nand_ecclayout onenand_oob_128 = { +static struct nand_ecclayout flexonenand_oob_128 = { .eccbytes = 64, .eccpos = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, @@ -86,6 +86,35 @@ static struct nand_ecclayout onenand_oob_128 = { } }; +/* + * onenand_oob_128 - oob info for OneNAND with 4KB page + * + * Based on specification: + * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010 + * + * For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout) + * + * oobfree uses the spare area fields marked as + * "Managed by internal ECC logic for Logical Sector Number area" + */ +static struct nand_ecclayout onenand_oob_128 = { + .eccbytes = 64, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + 71, 72, 73, 74, 75, 76, 77, 78, 79, + 87, 88, 89, 90, 91, 92, 93, 94, 95, + 103, 104, 105, 106, 107, 108, 109, 110, 111, + 119 + }, + .oobfree = { + {2, 3}, {18, 3}, {34, 3}, {50, 3}, + {66, 3}, {82, 3}, {98, 3}, {114, 3} + } +}; + /** * onenand_oob_64 - oob info for large (2KB) page */ @@ -2424,7 +2453,7 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd, len -= block_size; addr += block_size; - if (addr == region_end) { + if (region && addr == region_end) { if (!len) break; region++; @@ -4018,8 +4047,13 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) */ switch (mtd->oobsize) { case 128: - this->ecclayout = &onenand_oob_128; - mtd->subpage_sft = 0; + if (FLEXONENAND(this)) { + this->ecclayout = &flexonenand_oob_128; + mtd->subpage_sft = 0; + } else { + this->ecclayout = &onenand_oob_128; + mtd->subpage_sft = 2; + } break; case 64: this->ecclayout = &onenand_oob_64; @@ -4108,12 +4142,8 @@ void onenand_release(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; -#ifdef CONFIG_MTD_PARTITIONS /* Deregister partitions */ - del_mtd_partitions (mtd); -#endif - /* Deregister the device */ - del_mtd_device (mtd); + mtd_device_unregister(mtd); /* Free bad block table memory, if allocated */ if (this->bbm) { diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index 5ef3bd547772..85399e3accda 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -539,7 +539,8 @@ static int __init onenand_sim_init(void) return -ENXIO; } - add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions)); + mtd_device_register(&info->mtd, info->parts, + ARRAY_SIZE(os_partitions)); return 0; } diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index a4c74a9ba430..3306b5b3c736 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -147,9 +147,7 @@ struct s3c_onenand { struct resource *dma_res; unsigned long phys_base; struct completion complete; -#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; -#endif }; #define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) @@ -159,9 +157,7 @@ struct s3c_onenand { static struct s3c_onenand *onenand; -#ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL, }; -#endif static inline int s3c_read_reg(int offset) { @@ -1021,15 +1017,13 @@ static int s3c_onenand_probe(struct platform_device *pdev) if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); -#ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0); if (err > 0) - add_mtd_partitions(mtd, onenand->parts, err); + mtd_device_register(mtd, onenand->parts, err); else if (err <= 0 && pdata && pdata->parts) - add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + mtd_device_register(mtd, pdata->parts, pdata->nr_parts); else -#endif - err = add_mtd_device(mtd); + err = mtd_device_register(mtd, NULL, 0); platform_set_drvdata(pdev, mtd); diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 9aa81584c8a2..941bc3c05d6e 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -365,7 +365,7 @@ static int gluebi_create(struct ubi_device_info *di, vi->vol_id); mutex_unlock(&devices_mutex); - if (add_mtd_device(mtd)) { + if (mtd_device_register(mtd, NULL, 0)) { err_msg("cannot add MTD device"); kfree(mtd->name); kfree(gluebi); @@ -407,7 +407,7 @@ static int gluebi_remove(struct ubi_volume_info *vi) return err; mtd = &gluebi->mtd; - err = del_mtd_device(mtd); + err = mtd_device_unregister(mtd); if (err) { err_msg("cannot remove fake MTD device %d, UBI device %d, " "volume %d, error %d", mtd->index, gluebi->ubi_num, @@ -524,7 +524,7 @@ static void __exit ubi_gluebi_exit(void) int err; struct mtd_info *mtd = &gluebi->mtd; - err = del_mtd_device(mtd); + err = mtd_device_unregister(mtd); if (err) err_msg("error %d while removing gluebi MTD device %d, " "UBI device %d, volume %d - ignoring", err, diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6141667c5fb7..17b4dd94da90 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -113,9 +113,11 @@ MODULE_PARM_DESC(max_bonds, "Max number of bonded devices"); module_param(tx_queues, int, 0); MODULE_PARM_DESC(tx_queues, "Max number of transmit queues (default = 16)"); module_param_named(num_grat_arp, num_peer_notif, int, 0644); -MODULE_PARM_DESC(num_grat_arp, "Number of peer notifications to send on failover event (alias of num_unsol_na)"); +MODULE_PARM_DESC(num_grat_arp, "Number of peer notifications to send on " + "failover event (alias of num_unsol_na)"); module_param_named(num_unsol_na, num_peer_notif, int, 0644); -MODULE_PARM_DESC(num_unsol_na, "Number of peer notifications to send on failover event (alias of num_grat_arp)"); +MODULE_PARM_DESC(num_unsol_na, "Number of peer notifications to send on " + "failover event (alias of num_grat_arp)"); module_param(miimon, int, 0); MODULE_PARM_DESC(miimon, "Link check interval in milliseconds"); module_param(updelay, int, 0); @@ -127,7 +129,7 @@ module_param(use_carrier, int, 0); MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; " "0 for off, 1 for on (default)"); module_param(mode, charp, 0); -MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, " +MODULE_PARM_DESC(mode, "Mode of operation; 0 for balance-rr, " "1 for active-backup, 2 for balance-xor, " "3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, " "6 for balance-alb"); @@ -142,27 +144,35 @@ MODULE_PARM_DESC(primary_reselect, "Reselect primary slave " "2 for only on active slave " "failure"); module_param(lacp_rate, charp, 0); -MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner " - "(slow/fast)"); +MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner; " + "0 for slow, 1 for fast"); module_param(ad_select, charp, 0); -MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic: stable (0, default), bandwidth (1), count (2)"); +MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic; " + "0 for stable (default), 1 for bandwidth, " + "2 for count"); module_param(xmit_hash_policy, charp, 0); -MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method: 0 for layer 2 (default)" - ", 1 for layer 3+4"); +MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; " + "0 for layer 2 (default), 1 for layer 3+4, " + "2 for layer 2+3"); module_param(arp_interval, int, 0); MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds"); module_param_array(arp_ip_target, charp, NULL, 0); MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form"); module_param(arp_validate, charp, 0); -MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all"); +MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes; " + "0 for none (default), 1 for active, " + "2 for backup, 3 for all"); module_param(fail_over_mac, charp, 0); -MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the same MAC. none (default), active or follow"); +MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to " + "the same MAC; 0 for none (default), " + "1 for active, 2 for follow"); module_param(all_slaves_active, int, 0); MODULE_PARM_DESC(all_slaves_active, "Keep all frames received on an interface" - "by setting active flag for all slaves. " + "by setting active flag for all slaves; " "0 for never (default), 1 for always."); module_param(resend_igmp, int, 0); -MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on link failure"); +MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on " + "link failure"); /*----------------------------- Global variables ----------------------------*/ diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 807b6bb200eb..29a4f06fbfcf 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1772,7 +1772,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) /* obtain emac clock from kernel */ emac_clk = clk_get(&pdev->dev, NULL); if (IS_ERR(emac_clk)) { - printk(KERN_ERR "DaVinci EMAC: Failed to get EMAC clock\n"); + dev_err(&pdev->dev, "failed to get EMAC clock\n"); return -EBUSY; } emac_bus_frequency = clk_get_rate(emac_clk); @@ -1780,7 +1780,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) ndev = alloc_etherdev(sizeof(struct emac_priv)); if (!ndev) { - printk(KERN_ERR "DaVinci EMAC: Error allocating net_device\n"); + dev_err(&pdev->dev, "error allocating net_device\n"); clk_put(emac_clk); return -ENOMEM; } @@ -1795,7 +1795,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (!pdata) { - printk(KERN_ERR "DaVinci EMAC: No platform data\n"); + dev_err(&pdev->dev, "no platform data\n"); return -ENODEV; } @@ -1814,7 +1814,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) /* Get EMAC platform data */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(emac_dev, "DaVinci EMAC: Error getting res\n"); + dev_err(&pdev->dev,"error getting res\n"); rc = -ENOENT; goto probe_quit; } @@ -1822,14 +1822,14 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; size = res->end - res->start + 1; if (!request_mem_region(res->start, size, ndev->name)) { - dev_err(emac_dev, "DaVinci EMAC: failed request_mem_region() for regs\n"); + dev_err(&pdev->dev, "failed request_mem_region() for regs\n"); rc = -ENXIO; goto probe_quit; } priv->remap_addr = ioremap(res->start, size); if (!priv->remap_addr) { - dev_err(emac_dev, "Unable to map IO\n"); + dev_err(&pdev->dev, "unable to map IO\n"); rc = -ENOMEM; release_mem_region(res->start, size); goto probe_quit; @@ -1863,7 +1863,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) priv->dma = cpdma_ctlr_create(&dma_params); if (!priv->dma) { - dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n"); + dev_err(&pdev->dev, "error initializing DMA\n"); rc = -ENOMEM; goto no_dma; } @@ -1879,7 +1879,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { - dev_err(emac_dev, "DaVinci EMAC: Error getting irq res\n"); + dev_err(&pdev->dev, "error getting irq res\n"); rc = -ENOENT; goto no_irq_res; } @@ -1888,8 +1888,8 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) if (!is_valid_ether_addr(priv->mac_addr)) { /* Use random MAC if none passed */ random_ether_addr(priv->mac_addr); - printk(KERN_WARNING "%s: using random MAC addr: %pM\n", - __func__, priv->mac_addr); + dev_warn(&pdev->dev, "using random MAC addr: %pM\n", + priv->mac_addr); } ndev->netdev_ops = &emac_netdev_ops; @@ -1902,7 +1902,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) SET_NETDEV_DEV(ndev, &pdev->dev); rc = register_netdev(ndev); if (rc) { - dev_err(emac_dev, "DaVinci EMAC: Error in register_netdev\n"); + dev_err(&pdev->dev, "error in register_netdev\n"); rc = -ENODEV; goto netdev_reg_err; } diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index a3c0dc9d8b98..9537aaa50c2f 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -69,7 +69,7 @@ static const char paranoia_str[] = KERN_ERR static const char bc_drvname[] = "baycom_epp"; static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_epp: version 0.7\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 5f5af9a606f8..279d2296290a 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -102,7 +102,7 @@ static const char bc_drvname[] = "baycom_par"; static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_par: version 0.9\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index 3e25f10cabd6..99cdce33df8b 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -92,7 +92,7 @@ static const char bc_drvname[] = "baycom_ser_fdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_ser_fdx: version 0.10\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 1686f6dcbbce..d92fe6ca788f 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -80,7 +80,7 @@ static const char bc_drvname[] = "baycom_ser_hdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; +"baycom_ser_hdx: version 0.10\n"; /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 5b37579e84b7..a4a3516b6bbf 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -749,7 +749,7 @@ EXPORT_SYMBOL(hdlcdrv_unregister); static int __init hdlcdrv_init_driver(void) { printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n"); - printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "hdlcdrv: version 0.8\n"); return 0; } diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index e646bfce2d84..b6304486f244 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -216,7 +216,7 @@ static void efx_mtd_remove_partition(struct efx_mtd_partition *part) int rc; for (;;) { - rc = del_mtd_device(&part->mtd); + rc = mtd_device_unregister(&part->mtd); if (rc != -EBUSY) break; ssleep(1); @@ -268,7 +268,7 @@ static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) part->mtd.write = efx_mtd->ops->write; part->mtd.sync = efx_mtd_sync; - if (add_mtd_device(&part->mtd)) + if (mtd_device_register(&part->mtd, NULL, 0)) goto fail; } @@ -280,7 +280,7 @@ fail: --part; efx_mtd_remove_partition(part); } - /* add_mtd_device() returns 1 if the MTD table is full */ + /* mtd_device_register() returns 1 if the MTD table is full */ return -ENOMEM; } diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 737b59f1a8dc..9617d3d0ee39 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -3242,8 +3242,7 @@ static inline void show_version(void) rcsdate++; tmp = strrchr(rcsdate, ' '); *tmp = '\0'; - printk(KERN_INFO "Cyclades-PC300 driver %s %s (built %s %s)\n", - rcsvers, rcsdate, __DATE__, __TIME__); + printk(KERN_INFO "Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate); } /* show_version */ static const struct net_device_ops cpc_netdev_ops = { diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 8b63a691a9ed..65200af29c52 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -670,7 +670,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); - if (depth != 1 || + if (depth != 1 || !data || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; @@ -679,16 +679,16 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, /* Retrieve command line */ p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) - strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE)); + strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); #ifdef CONFIG_CMDLINE #ifndef CONFIG_CMDLINE_FORCE if (p == NULL || l == 0 || (l == 1 && (*p) == 0)) #endif - strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); + strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ - pr_debug("Command line is: %s\n", cmd_line); + pr_debug("Command line is: %s\n", (char*)data); /* break now */ return 1; diff --git a/drivers/parport/parport_ip32.c b/drivers/parport/parport_ip32.c index d3d7809af8bf..0dc34f12f92e 100644 --- a/drivers/parport/parport_ip32.c +++ b/drivers/parport/parport_ip32.c @@ -2203,7 +2203,6 @@ static __exit void parport_ip32_unregister_port(struct parport *p) static int __init parport_ip32_init(void) { pr_info(PPIP32 "SGI IP32 built-in parallel port driver v0.6\n"); - pr_debug1(PPIP32 "Compiled on %s, %s\n", __DATE__, __TIME__); this_port = parport_ip32_probe_port(); return IS_ERR(this_port) ? PTR_ERR(this_port) : 0; } diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 52a462fc6b84..e57b50b38565 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -68,6 +68,13 @@ config BATTERY_DS2760 help Say Y here to enable support for batteries with ds2760 chip. +config BATTERY_DS2780 + tristate "DS2780 battery driver" + select W1 + select W1_SLAVE_DS2780 + help + Say Y here to enable support for batteries with ds2780 chip. + config BATTERY_DS2782 tristate "DS2782/DS2786 standalone gas-gauge" depends on I2C @@ -203,6 +210,15 @@ config CHARGER_ISP1704 Say Y to enable support for USB Charger Detection with ISP1707/ISP1704 USB transceivers. +config CHARGER_MAX8903 + tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power" + depends on GENERIC_HARDIRQS + help + Say Y to enable support for the MAX8903 DC-DC charger and sysfs. + The driver supports controlling charger-enable and current-limit + pins based on the status of charger connections with interrupt + handlers. + config CHARGER_TWL4030 tristate "OMAP TWL4030 BCI charger driver" depends on TWL4030_CORE diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 8385bfae8728..009a90fa8ac9 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o +obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o @@ -32,5 +33,6 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o +obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 59e68dbd028b..bb16f5b7e167 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -4,6 +4,7 @@ * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -76,7 +77,7 @@ struct bq27x00_reg_cache { int time_to_empty_avg; int time_to_full; int charge_full; - int charge_counter; + int cycle_count; int capacity; int flags; @@ -115,7 +116,7 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, }; @@ -267,7 +268,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); cache.charge_full = bq27x00_battery_read_lmd(di); - cache.charge_counter = bq27x00_battery_read_cyct(di); + cache.cycle_count = bq27x00_battery_read_cyct(di); if (!is_bq27500) cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false); @@ -496,8 +497,8 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ret = bq27x00_simple_value(di->charge_design_full, val); break; - case POWER_SUPPLY_PROP_CHARGE_COUNTER: - ret = bq27x00_simple_value(di->cache.charge_counter, val); + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = bq27x00_simple_value(di->cache.cycle_count, val); break; case POWER_SUPPLY_PROP_ENERGY_NOW: ret = bq27x00_battery_energy(di, val); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index e534290f3256..f2c9cc33c0f9 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -86,7 +86,11 @@ static int rated_capacities[] = { 920, /* NEC */ 1440, /* Samsung */ 1440, /* BYD */ +#ifdef CONFIG_MACH_H4700 + 1800, /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */ +#else 1440, /* Lishen */ +#endif 1440, /* NEC */ 2880, /* Samsung */ 2880, /* BYD */ @@ -186,7 +190,7 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di) scale[0] = di->full_active_uAh; for (i = 1; i < 5; i++) - scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; + scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i]; di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); di->full_active_uAh *= 1000; /* convert to µAh */ diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c new file mode 100644 index 000000000000..1fefe82e12e3 --- /dev/null +++ b/drivers/power/ds2780_battery.c @@ -0,0 +1,853 @@ +/* + * 1-wire client/driver for the Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on ds2760_battery and ds2782_battery drivers + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/param.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/idr.h> + +#include "../w1/w1.h" +#include "../w1/slaves/w1_ds2780.h" + +/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ +#define DS2780_CURRENT_UNITS 1563 +/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */ +#define DS2780_CHARGE_UNITS 6250 +/* Number of bytes in user EEPROM space */ +#define DS2780_USER_EEPROM_SIZE (DS2780_EEPROM_BLOCK0_END - \ + DS2780_EEPROM_BLOCK0_START + 1) +/* Number of bytes in parameter EEPROM space */ +#define DS2780_PARAM_EEPROM_SIZE (DS2780_EEPROM_BLOCK1_END - \ + DS2780_EEPROM_BLOCK1_START + 1) + +struct ds2780_device_info { + struct device *dev; + struct power_supply bat; + struct device *w1_dev; +}; + +enum current_types { + CURRENT_NOW, + CURRENT_AVG, +}; + +static const char model[] = "DS2780"; +static const char manufacturer[] = "Maxim/Dallas"; + +static inline struct ds2780_device_info *to_ds2780_device_info( + struct power_supply *psy) +{ + return container_of(psy, struct ds2780_device_info, bat); +} + +static inline struct power_supply *to_power_supply(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static inline int ds2780_read8(struct device *dev, u8 *val, int addr) +{ + return w1_ds2780_io(dev, val, addr, sizeof(u8), 0); +} + +static int ds2780_read16(struct device *dev, s16 *val, int addr) +{ + int ret; + u8 raw[2]; + + ret = w1_ds2780_io(dev, raw, addr, sizeof(u8) * 2, 0); + if (ret < 0) + return ret; + + *val = (raw[0] << 8) | raw[1]; + + return 0; +} + +static inline int ds2780_read_block(struct device *dev, u8 *val, int addr, + size_t count) +{ + return w1_ds2780_io(dev, val, addr, count, 0); +} + +static inline int ds2780_write(struct device *dev, u8 *val, int addr, + size_t count) +{ + return w1_ds2780_io(dev, val, addr, count, 1); +} + +static inline int ds2780_store_eeprom(struct device *dev, int addr) +{ + return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_COPY_DATA); +} + +static inline int ds2780_recall_eeprom(struct device *dev, int addr) +{ + return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_RECALL_DATA); +} + +static int ds2780_save_eeprom(struct ds2780_device_info *dev_info, int reg) +{ + int ret; + + ret = ds2780_store_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + ret = ds2780_recall_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + return 0; +} + +/* Set sense resistor value in mhos */ +static int ds2780_set_sense_register(struct ds2780_device_info *dev_info, + u8 conductance) +{ + int ret; + + ret = ds2780_write(dev_info->w1_dev, &conductance, + DS2780_RSNSP_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_RSNSP_REG); +} + +/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info, + u16 *rsgain) +{ + return ds2780_read16(dev_info->w1_dev, rsgain, DS2780_RSGAIN_MSB_REG); +} + +/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info, + u16 rsgain) +{ + int ret; + u8 raw[] = {rsgain >> 8, rsgain & 0xFF}; + + ret = ds2780_write(dev_info->w1_dev, raw, + DS2780_RSGAIN_MSB_REG, sizeof(u8) * 2); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_RSGAIN_MSB_REG); +} + +static int ds2780_get_voltage(struct ds2780_device_info *dev_info, + int *voltage_uV) +{ + int ret; + s16 voltage_raw; + + /* + * The voltage value is located in 10 bits across the voltage MSB + * and LSB registers in two's compliment form + * Sign bit of the voltage value is in bit 7 of the voltage MSB register + * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the + * voltage MSB register + * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the + * voltage LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &voltage_raw, + DS2780_VOLT_MSB_REG); + if (ret < 0) + return ret; + + /* + * DS2780 reports voltage in units of 4.88mV, but the battery class + * reports in units of uV, so convert by multiplying by 4880. + */ + *voltage_uV = (voltage_raw / 32) * 4880; + return 0; +} + +static int ds2780_get_temperature(struct ds2780_device_info *dev_info, + int *temperature) +{ + int ret; + s16 temperature_raw; + + /* + * The temperature value is located in 10 bits across the temperature + * MSB and LSB registers in two's compliment form + * Sign bit of the temperature value is in bit 7 of the temperature + * MSB register + * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the + * temperature MSB register + * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the + * temperature LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &temperature_raw, + DS2780_TEMP_MSB_REG); + if (ret < 0) + return ret; + + /* + * Temperature is measured in units of 0.125 degrees celcius, the + * power_supply class measures temperature in tenths of degrees + * celsius. The temperature value is stored as a 10 bit number, plus + * sign in the upper bits of a 16 bit register. + */ + *temperature = ((temperature_raw / 32) * 125) / 100; + return 0; +} + +static int ds2780_get_current(struct ds2780_device_info *dev_info, + enum current_types type, int *current_uA) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw, reg_msb; + + /* + * The units of measurement for current are dependent on the value of + * the sense resistor. + */ + ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -ENXIO; + } + sense_res = 1000 / sense_res_raw; + + if (type == CURRENT_NOW) + reg_msb = DS2780_CURRENT_MSB_REG; + else if (type == CURRENT_AVG) + reg_msb = DS2780_IAVG_MSB_REG; + else + return -EINVAL; + + /* + * The current value is located in 16 bits across the current MSB + * and LSB registers in two's compliment form + * Sign bit of the current value is in bit 7 of the current MSB register + * Bits 14 - 8 of the current value are in bits 6 - 0 of the current + * MSB register + * Bits 7 - 0 of the current value are in bits 7 - 0 of the current + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, reg_msb); + if (ret < 0) + return ret; + + *current_uA = current_raw * (DS2780_CURRENT_UNITS / sense_res); + return 0; +} + +static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info, + int *accumulated_current) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw; + + /* + * The units of measurement for accumulated current are dependent on + * the value of the sense resistor. + */ + ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -ENXIO; + } + sense_res = 1000 / sense_res_raw; + + /* + * The ACR value is located in 16 bits across the ACR MSB and + * LSB registers + * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR + * MSB register + * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, DS2780_ACR_MSB_REG); + if (ret < 0) + return ret; + + *accumulated_current = current_raw * (DS2780_CHARGE_UNITS / sense_res); + return 0; +} + +static int ds2780_get_capacity(struct ds2780_device_info *dev_info, + int *capacity) +{ + int ret; + u8 raw; + + ret = ds2780_read8(dev_info->w1_dev, &raw, DS2780_RARC_REG); + if (ret < 0) + return ret; + + *capacity = raw; + return raw; +} + +static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status) +{ + int ret, current_uA, capacity; + + ret = ds2780_get_current(dev_info, CURRENT_NOW, ¤t_uA); + if (ret < 0) + return ret; + + ret = ds2780_get_capacity(dev_info, &capacity); + if (ret < 0) + return ret; + + if (capacity == 100) + *status = POWER_SUPPLY_STATUS_FULL; + else if (current_uA == 0) + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (current_uA < 0) + *status = POWER_SUPPLY_STATUS_DISCHARGING; + else + *status = POWER_SUPPLY_STATUS_CHARGING; + + return 0; +} + +static int ds2780_get_charge_now(struct ds2780_device_info *dev_info, + int *charge_now) +{ + int ret; + u16 charge_raw; + + /* + * The RAAC value is located in 16 bits across the RAAC MSB and + * LSB registers + * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC + * MSB register + * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &charge_raw, DS2780_RAAC_MSB_REG); + if (ret < 0) + return ret; + + *charge_now = charge_raw * 1600; + return 0; +} + +static int ds2780_get_control_register(struct ds2780_device_info *dev_info, + u8 *control_reg) +{ + return ds2780_read8(dev_info->w1_dev, control_reg, DS2780_CONTROL_REG); +} + +static int ds2780_set_control_register(struct ds2780_device_info *dev_info, + u8 control_reg) +{ + int ret; + + ret = ds2780_write(dev_info->w1_dev, &control_reg, + DS2780_CONTROL_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_CONTROL_REG); +} + +static int ds2780_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = ds2780_get_voltage(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_TEMP: + ret = ds2780_get_temperature(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = model; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = manufacturer; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = ds2780_get_current(dev_info, CURRENT_NOW, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = ds2780_get_current(dev_info, CURRENT_AVG, &val->intval); + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = ds2780_get_status(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + ret = ds2780_get_capacity(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = ds2780_get_accumulated_current(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = ds2780_get_charge_now(dev_info, &val->intval); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property ds2780_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_NOW, +}; + +static ssize_t ds2780_get_pmod_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 control_reg; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + /* Get power mode */ + ret = ds2780_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + !!(control_reg & DS2780_CONTROL_REG_PMOD)); +} + +static ssize_t ds2780_set_pmod_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 control_reg, new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + /* Set power mode */ + ret = ds2780_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n"); + return -EINVAL; + } + + if (new_setting) + control_reg |= DS2780_CONTROL_REG_PMOD; + else + control_reg &= ~DS2780_CONTROL_REG_PMOD; + + ret = ds2780_set_control_register(dev_info, control_reg); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sense_resistor; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_read8(dev_info->w1_dev, &sense_resistor, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sense_resistor); + return ret; +} + +static ssize_t ds2780_set_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + ret = ds2780_set_sense_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_rsgain_setting(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 rsgain; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_get_rsgain_register(dev_info, &rsgain); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", rsgain); +} + +static ssize_t ds2780_set_rsgain_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u16 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou16(buf, 0, &new_setting); + if (ret < 0) + return ret; + + /* Gain can only be from 0 to 1.999 in steps of .001 */ + if (new_setting > 1999) { + dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n"); + return -EINVAL; + } + + ret = ds2780_set_rsgain_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_pio_pin(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sfr; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_read8(dev_info->w1_dev, &sfr, DS2780_SFR_REG); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC); + return ret; +} + +static ssize_t ds2780_set_pio_pin(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n"); + return -EINVAL; + } + + ret = ds2780_write(dev_info->w1_dev, &new_setting, + DS2780_SFR_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK1_END - + DS2780_EEPROM_BLOCK1_START + 1 - off); + + return ds2780_read_block(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK1_START + off, count); +} + +static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK1_END - + DS2780_EEPROM_BLOCK1_START + 1 - off); + + ret = ds2780_write(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK1_START + off, count); + if (ret < 0) + return ret; + + ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK1_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2780_param_eeprom_bin_attr = { + .attr = { + .name = "param_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1, + .read = ds2780_read_param_eeprom_bin, + .write = ds2780_write_param_eeprom_bin, +}; + +static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK0_END - + DS2780_EEPROM_BLOCK0_START + 1 - off); + + return ds2780_read_block(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK0_START + off, count); + +} + +static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK0_END - + DS2780_EEPROM_BLOCK0_START + 1 - off); + + ret = ds2780_write(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK0_START + off, count); + if (ret < 0) + return ret; + + ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK0_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2780_user_eeprom_bin_attr = { + .attr = { + .name = "user_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1, + .read = ds2780_read_user_eeprom_bin, + .write = ds2780_write_user_eeprom_bin, +}; + +static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled, + ds2780_set_pmod_enabled); +static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR, + ds2780_get_sense_resistor_value, ds2780_set_sense_resistor_value); +static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting, + ds2780_set_rsgain_setting); +static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin, + ds2780_set_pio_pin); + + +static struct attribute *ds2780_attributes[] = { + &dev_attr_pmod_enabled.attr, + &dev_attr_sense_resistor_value.attr, + &dev_attr_rsgain_setting.attr, + &dev_attr_pio_pin.attr, + NULL +}; + +static const struct attribute_group ds2780_attr_group = { + .attrs = ds2780_attributes, +}; + +static int __devinit ds2780_battery_probe(struct platform_device *pdev) +{ + int ret = 0; + struct ds2780_device_info *dev_info; + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + ret = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, dev_info); + + dev_info->dev = &pdev->dev; + dev_info->w1_dev = pdev->dev.parent; + dev_info->bat.name = dev_name(&pdev->dev); + dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; + dev_info->bat.properties = ds2780_battery_props; + dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props); + dev_info->bat.get_property = ds2780_battery_get_property; + + ret = power_supply_register(&pdev->dev, &dev_info->bat); + if (ret) { + dev_err(dev_info->dev, "failed to register battery\n"); + goto fail_free_info; + } + + ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + if (ret) { + dev_err(dev_info->dev, "failed to create sysfs group\n"); + goto fail_unregister; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2780_param_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create param eeprom bin file"); + goto fail_remove_group; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2780_user_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create user eeprom bin file"); + goto fail_remove_bin_file; + } + + return 0; + +fail_remove_bin_file: + sysfs_remove_bin_file(&dev_info->bat.dev->kobj, + &ds2780_param_eeprom_bin_attr); +fail_remove_group: + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); +fail_unregister: + power_supply_unregister(&dev_info->bat); +fail_free_info: + kfree(dev_info); +fail: + return ret; +} + +static int __devexit ds2780_battery_remove(struct platform_device *pdev) +{ + struct ds2780_device_info *dev_info = platform_get_drvdata(pdev); + + /* remove attributes */ + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + + power_supply_unregister(&dev_info->bat); + + kfree(dev_info); + return 0; +} + +MODULE_ALIAS("platform:ds2780-battery"); + +static struct platform_driver ds2780_battery_driver = { + .driver = { + .name = "ds2780-battery", + }, + .probe = ds2780_battery_probe, + .remove = ds2780_battery_remove, +}; + +static int __init ds2780_battery_init(void) +{ + return platform_driver_register(&ds2780_battery_driver); +} + +static void __exit ds2780_battery_exit(void) +{ + platform_driver_unregister(&ds2780_battery_driver); +} + +module_init(ds2780_battery_init); +module_exit(ds2780_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver"); diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 25b88ac1d44c..718f2c537827 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -161,12 +161,27 @@ static int __devexit gpio_charger_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int gpio_charger_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); + + power_supply_changed(&gpio_charger->charger); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, NULL, gpio_charger_resume); + static struct platform_driver gpio_charger_driver = { .probe = gpio_charger_probe, .remove = __devexit_p(gpio_charger_remove), .driver = { .name = "gpio-charger", .owner = THIS_MODULE, + .pm = &gpio_charger_pm_ops, }, }; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 2ad9b14a5ce3..f6d72b402a8e 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -33,6 +33,7 @@ #include <linux/usb/ulpi.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/power/isp1704_charger.h> /* Vendor specific Power Control register */ #define ISP1704_PWR_CTRL 0x3d @@ -71,6 +72,18 @@ struct isp1704_charger { }; /* + * Disable/enable the power from the isp1704 if a function for it + * has been provided with platform data. + */ +static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) +{ + struct isp1704_charger_data *board = isp->dev->platform_data; + + if (board->set_power) + board->set_power(on); +} + +/* * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB * chargers). * @@ -222,6 +235,9 @@ static void isp1704_charger_work(struct work_struct *data) mutex_lock(&lock); + if (event != USB_EVENT_NONE) + isp1704_charger_set_power(isp, 1); + switch (event) { case USB_EVENT_VBUS: isp->online = true; @@ -269,6 +285,8 @@ static void isp1704_charger_work(struct work_struct *data) */ if (isp->otg->gadget) usb_gadget_disconnect(isp->otg->gadget); + + isp1704_charger_set_power(isp, 0); break; case USB_EVENT_ENUMERATED: if (isp->present) @@ -394,6 +412,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) isp->dev = &pdev->dev; platform_set_drvdata(pdev, isp); + isp1704_charger_set_power(isp, 1); + ret = isp1704_test_ulpi(isp); if (ret < 0) goto fail1; @@ -434,6 +454,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) /* Detect charger if VBUS is valid (the cable was already plugged). */ ret = otg_io_read(isp->otg, ULPI_USB_INT_STS); + isp1704_charger_set_power(isp, 0); if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) { isp->event = USB_EVENT_VBUS; schedule_work(&isp->work); @@ -459,6 +480,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) otg_unregister_notifier(isp->otg, &isp->nb); power_supply_unregister(&isp->psy); otg_put_transceiver(isp->otg); + isp1704_charger_set_power(isp, 0); kfree(isp); return 0; diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c new file mode 100644 index 000000000000..33ff0e37809e --- /dev/null +++ b/drivers/power/max8903_charger.c @@ -0,0 +1,391 @@ +/* + * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/power_supply.h> +#include <linux/platform_device.h> +#include <linux/power/max8903_charger.h> + +struct max8903_data { + struct max8903_pdata *pdata; + struct device *dev; + struct power_supply psy; + bool fault; + bool usb_in; + bool ta_in; +}; + +static enum power_supply_property max8903_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, /* Charger status output */ + POWER_SUPPLY_PROP_ONLINE, /* External power source */ + POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ +}; + +static int max8903_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8903_data *data = container_of(psy, + struct max8903_data, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + if (data->pdata->chg) { + if (gpio_get_value(data->pdata->chg) == 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (data->usb_in || data->ta_in) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 0; + if (data->usb_in || data->ta_in) + val->intval = 1; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + if (data->fault) + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + default: + return -EINVAL; + } + return 0; +} + +static irqreturn_t max8903_dcin(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool ta_in; + enum power_supply_type old_type; + + ta_in = gpio_get_value(pdata->dok) ? false : true; + + if (ta_in == data->ta_in) + return IRQ_HANDLED; + + data->ta_in = ta_in; + + /* Set Current-Limit-Mode 1:DC 0:USB */ + if (pdata->dcm) + gpio_set_value(pdata->dcm, ta_in ? 1 : 0); + + /* Charger Enable / Disable (cen is negated) */ + if (pdata->cen) + gpio_set_value(pdata->cen, ta_in ? 0 : + (data->usb_in ? 0 : 1)); + + dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? + "Connected" : "Disconnected"); + + old_type = data->psy.type; + + if (data->ta_in) + data->psy.type = POWER_SUPPLY_TYPE_MAINS; + else if (data->usb_in) + data->psy.type = POWER_SUPPLY_TYPE_USB; + else + data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + + if (old_type != data->psy.type) + power_supply_changed(&data->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t max8903_usbin(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool usb_in; + enum power_supply_type old_type; + + usb_in = gpio_get_value(pdata->uok) ? false : true; + + if (usb_in == data->usb_in) + return IRQ_HANDLED; + + data->usb_in = usb_in; + + /* Do not touch Current-Limit-Mode */ + + /* Charger Enable / Disable (cen is negated) */ + if (pdata->cen) + gpio_set_value(pdata->cen, usb_in ? 0 : + (data->ta_in ? 0 : 1)); + + dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? + "Connected" : "Disconnected"); + + old_type = data->psy.type; + + if (data->ta_in) + data->psy.type = POWER_SUPPLY_TYPE_MAINS; + else if (data->usb_in) + data->psy.type = POWER_SUPPLY_TYPE_USB; + else + data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + + if (old_type != data->psy.type) + power_supply_changed(&data->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t max8903_fault(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool fault; + + fault = gpio_get_value(pdata->flt) ? false : true; + + if (fault == data->fault) + return IRQ_HANDLED; + + data->fault = fault; + + if (fault) + dev_err(data->dev, "Charger suffers a fault and stops.\n"); + else + dev_err(data->dev, "Charger recovered from a fault.\n"); + + return IRQ_HANDLED; +} + +static __devinit int max8903_probe(struct platform_device *pdev) +{ + struct max8903_data *data; + struct device *dev = &pdev->dev; + struct max8903_pdata *pdata = pdev->dev.platform_data; + int ret = 0; + int gpio; + int ta_in = 0; + int usb_in = 0; + + data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Cannot allocate memory.\n"); + return -ENOMEM; + } + data->pdata = pdata; + data->dev = dev; + platform_set_drvdata(pdev, data); + + if (pdata->dc_valid == false && pdata->usb_valid == false) { + dev_err(dev, "No valid power sources.\n"); + ret = -EINVAL; + goto err; + } + + if (pdata->dc_valid) { + if (pdata->dok && gpio_is_valid(pdata->dok) && + pdata->dcm && gpio_is_valid(pdata->dcm)) { + gpio = pdata->dok; /* PULL_UPed Interrupt */ + ta_in = gpio_get_value(gpio) ? 0 : 1; + + gpio = pdata->dcm; /* Output */ + gpio_set_value(gpio, ta_in); + } else { + dev_err(dev, "When DC is wired, DOK and DCM should" + " be wired as well.\n"); + ret = -EINVAL; + goto err; + } + } else { + if (pdata->dcm) { + if (gpio_is_valid(pdata->dcm)) + gpio_set_value(pdata->dcm, 0); + else { + dev_err(dev, "Invalid pin: dcm.\n"); + ret = -EINVAL; + goto err; + } + } + } + + if (pdata->usb_valid) { + if (pdata->uok && gpio_is_valid(pdata->uok)) { + gpio = pdata->uok; + usb_in = gpio_get_value(gpio) ? 0 : 1; + } else { + dev_err(dev, "When USB is wired, UOK should be wired." + "as well.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->cen) { + if (gpio_is_valid(pdata->cen)) { + gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); + } else { + dev_err(dev, "Invalid pin: cen.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->chg) { + if (!gpio_is_valid(pdata->chg)) { + dev_err(dev, "Invalid pin: chg.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->flt) { + if (!gpio_is_valid(pdata->flt)) { + dev_err(dev, "Invalid pin: flt.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->usus) { + if (!gpio_is_valid(pdata->usus)) { + dev_err(dev, "Invalid pin: usus.\n"); + ret = -EINVAL; + goto err; + } + } + + data->fault = false; + data->ta_in = ta_in; + data->usb_in = usb_in; + + data->psy.name = "max8903_charger"; + data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : + ((usb_in) ? POWER_SUPPLY_TYPE_USB : + POWER_SUPPLY_TYPE_BATTERY); + data->psy.get_property = max8903_get_property; + data->psy.properties = max8903_charger_props; + data->psy.num_properties = ARRAY_SIZE(max8903_charger_props); + + ret = power_supply_register(dev, &data->psy); + if (ret) { + dev_err(dev, "failed: power supply register.\n"); + goto err; + } + + if (pdata->dc_valid) { + ret = request_threaded_irq(gpio_to_irq(pdata->dok), + NULL, max8903_dcin, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 DC IN", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for DC (%d)\n", + gpio_to_irq(pdata->dok), ret); + goto err_psy; + } + } + + if (pdata->usb_valid) { + ret = request_threaded_irq(gpio_to_irq(pdata->uok), + NULL, max8903_usbin, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 USB IN", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for USB (%d)\n", + gpio_to_irq(pdata->uok), ret); + goto err_dc_irq; + } + } + + if (pdata->flt) { + ret = request_threaded_irq(gpio_to_irq(pdata->flt), + NULL, max8903_fault, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 Fault", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for Fault (%d)\n", + gpio_to_irq(pdata->flt), ret); + goto err_usb_irq; + } + } + + return 0; + +err_usb_irq: + if (pdata->usb_valid) + free_irq(gpio_to_irq(pdata->uok), data); +err_dc_irq: + if (pdata->dc_valid) + free_irq(gpio_to_irq(pdata->dok), data); +err_psy: + power_supply_unregister(&data->psy); +err: + kfree(data); + return ret; +} + +static __devexit int max8903_remove(struct platform_device *pdev) +{ + struct max8903_data *data = platform_get_drvdata(pdev); + + if (data) { + struct max8903_pdata *pdata = data->pdata; + + if (pdata->flt) + free_irq(gpio_to_irq(pdata->flt), data); + if (pdata->usb_valid) + free_irq(gpio_to_irq(pdata->uok), data); + if (pdata->dc_valid) + free_irq(gpio_to_irq(pdata->dok), data); + power_supply_unregister(&data->psy); + kfree(data); + } + + return 0; +} + +static struct platform_driver max8903_driver = { + .probe = max8903_probe, + .remove = __devexit_p(max8903_remove), + .driver = { + .name = "max8903-charger", + .owner = THIS_MODULE, + }, +}; + +static int __init max8903_init(void) +{ + return platform_driver_register(&max8903_driver); +} +module_init(max8903_init); + +static void __exit max8903_exit(void) +{ + platform_driver_unregister(&max8903_driver); +} +module_exit(max8903_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MAX8903 Charger Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_ALIAS("max8903-charger"); diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index 0cd9f67d33e5..b527c93bf2f3 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -3,6 +3,12 @@ * * Copyright 2010 Anton Vorontsov <cbouatmailru@gmail.com> * + * Dynamic module parameter code from the Virtual Battery Driver + * Copyright (C) 2008 Pylone, Inc. + * By: Masashi YOKOTA <yokota@pylone.jp> + * Originally found here: + * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2 + * * 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. @@ -15,8 +21,12 @@ #include <linux/delay.h> #include <linux/vermagic.h> -static int test_power_ac_online = 1; -static int test_power_battery_status = POWER_SUPPLY_STATUS_CHARGING; +static int ac_online = 1; +static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; +static int battery_health = POWER_SUPPLY_HEALTH_GOOD; +static int battery_present = 1; /* true */ +static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; +static int battery_capacity = 50; static int test_power_get_ac_property(struct power_supply *psy, enum power_supply_property psp, @@ -24,7 +34,7 @@ static int test_power_get_ac_property(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = test_power_ac_online; + val->intval = ac_online; break; default: return -EINVAL; @@ -47,22 +57,30 @@ static int test_power_get_battery_property(struct power_supply *psy, val->strval = UTS_RELEASE; break; case POWER_SUPPLY_PROP_STATUS: - val->intval = test_power_battery_status; + val->intval = battery_status; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; break; case POWER_SUPPLY_PROP_HEALTH: - val->intval = POWER_SUPPLY_HEALTH_GOOD; + val->intval = battery_health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery_present; break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + val->intval = battery_technology; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = 50; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = battery_capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = 100; break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: @@ -84,9 +102,11 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -118,6 +138,7 @@ static struct power_supply test_power_supplies[] = { }, }; + static int __init test_power_init(void) { int i; @@ -145,8 +166,8 @@ static void __exit test_power_exit(void) int i; /* Let's see how we handle changes... */ - test_power_ac_online = 0; - test_power_battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + ac_online = 0; + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) power_supply_changed(&test_power_supplies[i]); pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n", @@ -158,6 +179,241 @@ static void __exit test_power_exit(void) } module_exit(test_power_exit); + + +#define MAX_KEYLENGTH 256 +struct battery_property_map { + int value; + char const *key; +}; + +static struct battery_property_map map_ac_online[] = { + { 0, "on" }, + { 1, "off" }, + { -1, NULL }, +}; + +static struct battery_property_map map_status[] = { + { POWER_SUPPLY_STATUS_CHARGING, "charging" }, + { POWER_SUPPLY_STATUS_DISCHARGING, "discharging" }, + { POWER_SUPPLY_STATUS_NOT_CHARGING, "not-charging" }, + { POWER_SUPPLY_STATUS_FULL, "full" }, + { -1, NULL }, +}; + +static struct battery_property_map map_health[] = { + { POWER_SUPPLY_HEALTH_GOOD, "good" }, + { POWER_SUPPLY_HEALTH_OVERHEAT, "overheat" }, + { POWER_SUPPLY_HEALTH_DEAD, "dead" }, + { POWER_SUPPLY_HEALTH_OVERVOLTAGE, "overvoltage" }, + { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure" }, + { -1, NULL }, +}; + +static struct battery_property_map map_present[] = { + { 0, "false" }, + { 1, "true" }, + { -1, NULL }, +}; + +static struct battery_property_map map_technology[] = { + { POWER_SUPPLY_TECHNOLOGY_NiMH, "NiMH" }, + { POWER_SUPPLY_TECHNOLOGY_LION, "LION" }, + { POWER_SUPPLY_TECHNOLOGY_LIPO, "LIPO" }, + { POWER_SUPPLY_TECHNOLOGY_LiFe, "LiFe" }, + { POWER_SUPPLY_TECHNOLOGY_NiCd, "NiCd" }, + { POWER_SUPPLY_TECHNOLOGY_LiMn, "LiMn" }, + { -1, NULL }, +}; + + +static int map_get_value(struct battery_property_map *map, const char *key, + int def_val) +{ + char buf[MAX_KEYLENGTH]; + int cr; + + strncpy(buf, key, MAX_KEYLENGTH); + buf[MAX_KEYLENGTH-1] = '\0'; + + cr = strnlen(buf, MAX_KEYLENGTH) - 1; + if (buf[cr] == '\n') + buf[cr] = '\0'; + + while (map->key) { + if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0) + return map->value; + map++; + } + + return def_val; +} + + +static const char *map_get_key(struct battery_property_map *map, int value, + const char *def_key) +{ + while (map->key) { + if (map->value == value) + return map->key; + map++; + } + + return def_key; +} + +static int param_set_ac_online(const char *key, const struct kernel_param *kp) +{ + ac_online = map_get_value(map_ac_online, key, ac_online); + power_supply_changed(&test_power_supplies[0]); + return 0; +} + +static int param_get_ac_online(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_status(const char *key, + const struct kernel_param *kp) +{ + battery_status = map_get_value(map_status, key, battery_status); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_status(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_status, battery_status, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_health(const char *key, + const struct kernel_param *kp) +{ + battery_health = map_get_value(map_health, key, battery_health); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_health(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_health, battery_health, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_present(const char *key, + const struct kernel_param *kp) +{ + battery_present = map_get_value(map_present, key, battery_present); + power_supply_changed(&test_power_supplies[0]); + return 0; +} + +static int param_get_battery_present(char *buffer, + const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_present, battery_present, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_technology(const char *key, + const struct kernel_param *kp) +{ + battery_technology = map_get_value(map_technology, key, + battery_technology); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_technology(char *buffer, + const struct kernel_param *kp) +{ + strcpy(buffer, + map_get_key(map_technology, battery_technology, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_capacity(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_capacity = tmp; + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +#define param_get_battery_capacity param_get_int + + + +static struct kernel_param_ops param_ops_ac_online = { + .set = param_set_ac_online, + .get = param_get_ac_online, +}; + +static struct kernel_param_ops param_ops_battery_status = { + .set = param_set_battery_status, + .get = param_get_battery_status, +}; + +static struct kernel_param_ops param_ops_battery_present = { + .set = param_set_battery_present, + .get = param_get_battery_present, +}; + +static struct kernel_param_ops param_ops_battery_technology = { + .set = param_set_battery_technology, + .get = param_get_battery_technology, +}; + +static struct kernel_param_ops param_ops_battery_health = { + .set = param_set_battery_health, + .get = param_get_battery_health, +}; + +static struct kernel_param_ops param_ops_battery_capacity = { + .set = param_set_battery_capacity, + .get = param_get_battery_capacity, +}; + + +#define param_check_ac_online(name, p) __param_check(name, p, void); +#define param_check_battery_status(name, p) __param_check(name, p, void); +#define param_check_battery_present(name, p) __param_check(name, p, void); +#define param_check_battery_technology(name, p) __param_check(name, p, void); +#define param_check_battery_health(name, p) __param_check(name, p, void); +#define param_check_battery_capacity(name, p) __param_check(name, p, void); + + +module_param(ac_online, ac_online, 0644); +MODULE_PARM_DESC(ac_online, "AC charging state <on|off>"); + +module_param(battery_status, battery_status, 0644); +MODULE_PARM_DESC(battery_status, + "battery status <charging|discharging|not-charging|full>"); + +module_param(battery_present, battery_present, 0644); +MODULE_PARM_DESC(battery_present, + "battery presence state <good|overheat|dead|overvoltage|failure>"); + +module_param(battery_technology, battery_technology, 0644); +MODULE_PARM_DESC(battery_technology, + "battery technology <NiMH|LION|LIPO|LiFe|NiCd|LiMn>"); + +module_param(battery_health, battery_health, 0644); +MODULE_PARM_DESC(battery_health, + "battery health state <good|overheat|dead|overvoltage|failure>"); + +module_param(battery_capacity, battery_capacity, 0644); +MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); + + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index e5ced3a4c1ed..d119c38b3ff6 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -271,24 +271,33 @@ static int __devexit z2_batt_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) +static int z2_batt_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct z2_charger *charger = i2c_get_clientdata(client); flush_work_sync(&charger->bat_work); return 0; } -static int z2_batt_resume(struct i2c_client *client) +static int z2_batt_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct z2_charger *charger = i2c_get_clientdata(client); schedule_work(&charger->bat_work); return 0; } + +static const struct dev_pm_ops z2_battery_pm_ops = { + .suspend = z2_batt_suspend, + .resume = z2_batt_resume, +}; + +#define Z2_BATTERY_PM_OPS (&z2_battery_pm_ops) + #else -#define z2_batt_suspend NULL -#define z2_batt_resume NULL +#define Z2_BATTERY_PM_OPS (NULL) #endif static const struct i2c_device_id z2_batt_id[] = { @@ -301,11 +310,10 @@ static struct i2c_driver z2_batt_driver = { .driver = { .name = "z2-battery", .owner = THIS_MODULE, + .pm = Z2_BATTERY_PM_OPS }, .probe = z2_batt_probe, .remove = z2_batt_remove, - .suspend = z2_batt_suspend, - .resume = z2_batt_resume, .id_table = z2_batt_id, }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index f0b13a0d1851..d7ed20f293d7 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -297,5 +297,11 @@ config REGULATOR_TPS6524X serial interface currently supported on the sequencer serial port controller. +config REGULATOR_TPS65910 + tristate "TI TPS65910 Power Regulator" + depends on MFD_TPS65910 + help + This driver supports TPS65910 voltage regulator chips. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 165ff5371e9e..3932d2ec38f3 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o +obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0fae51c4845a..d3e38790906e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -158,6 +158,13 @@ static int regulator_check_consumers(struct regulator_dev *rdev, struct regulator *regulator; list_for_each_entry(regulator, &rdev->consumer_list, list) { + /* + * Assume consumers that didn't say anything are OK + * with anything in the constraint range. + */ + if (!regulator->min_uV && !regulator->max_uV) + continue; + if (*max_uV > regulator->max_uV) *max_uV = regulator->max_uV; if (*min_uV < regulator->min_uV) @@ -197,9 +204,9 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, } /* operating mode constraint check */ -static int regulator_check_mode(struct regulator_dev *rdev, int mode) +static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) { - switch (mode) { + switch (*mode) { case REGULATOR_MODE_FAST: case REGULATOR_MODE_NORMAL: case REGULATOR_MODE_IDLE: @@ -217,11 +224,17 @@ static int regulator_check_mode(struct regulator_dev *rdev, int mode) rdev_err(rdev, "operation not allowed\n"); return -EPERM; } - if (!(rdev->constraints->valid_modes_mask & mode)) { - rdev_err(rdev, "invalid mode %x\n", mode); - return -EINVAL; + + /* The modes are bitmasks, the most power hungry modes having + * the lowest values. If the requested mode isn't supported + * try higher modes. */ + while (*mode) { + if (rdev->constraints->valid_modes_mask & *mode) + return 0; + *mode /= 2; } - return 0; + + return -EINVAL; } /* dynamic regulator mode switching constraint check */ @@ -612,7 +625,7 @@ static void drms_uA_update(struct regulator_dev *rdev) output_uV, current_uA); /* check the new mode is allowed */ - err = regulator_check_mode(rdev, mode); + err = regulator_mode_constrain(rdev, &mode); if (err == 0) rdev->desc->ops->set_mode(rdev, mode); } @@ -718,6 +731,10 @@ static void print_constraints(struct regulator_dev *rdev) count += sprintf(buf + count, "at %d mV ", ret / 1000); } + if (constraints->uV_offset) + count += sprintf(buf, "%dmV offset ", + constraints->uV_offset / 1000); + if (constraints->min_uA && constraints->max_uA) { if (constraints->min_uA == constraints->max_uA) count += sprintf(buf + count, "%d mA ", @@ -1498,13 +1515,14 @@ static int _regulator_force_disable(struct regulator_dev *rdev, */ int regulator_force_disable(struct regulator *regulator) { + struct regulator_dev *rdev = regulator->rdev; struct regulator_dev *supply_rdev = NULL; int ret; - mutex_lock(®ulator->rdev->mutex); + mutex_lock(&rdev->mutex); regulator->uA_load = 0; - ret = _regulator_force_disable(regulator->rdev, &supply_rdev); - mutex_unlock(®ulator->rdev->mutex); + ret = _regulator_force_disable(rdev, &supply_rdev); + mutex_unlock(&rdev->mutex); if (supply_rdev) regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); @@ -1634,6 +1652,9 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); + min_uV += rdev->constraints->uV_offset; + max_uV += rdev->constraints->uV_offset; + if (rdev->desc->ops->set_voltage) { ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, &selector); @@ -1858,18 +1879,22 @@ EXPORT_SYMBOL_GPL(regulator_sync_voltage); static int _regulator_get_voltage(struct regulator_dev *rdev) { - int sel; + int sel, ret; if (rdev->desc->ops->get_voltage_sel) { sel = rdev->desc->ops->get_voltage_sel(rdev); if (sel < 0) return sel; - return rdev->desc->ops->list_voltage(rdev, sel); - } - if (rdev->desc->ops->get_voltage) - return rdev->desc->ops->get_voltage(rdev); - else + ret = rdev->desc->ops->list_voltage(rdev, sel); + } else if (rdev->desc->ops->get_voltage) { + ret = rdev->desc->ops->get_voltage(rdev); + } else { return -EINVAL; + } + + if (ret < 0) + return ret; + return ret - rdev->constraints->uV_offset; } /** @@ -2005,7 +2030,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode) } /* constraints check */ - ret = regulator_check_mode(rdev, mode); + ret = regulator_mode_constrain(rdev, &mode); if (ret < 0) goto out; @@ -2081,16 +2106,26 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) mutex_lock(&rdev->mutex); + /* + * first check to see if we can set modes at all, otherwise just + * tell the consumer everything is OK. + */ regulator->uA_load = uA_load; ret = regulator_check_drms(rdev); - if (ret < 0) + if (ret < 0) { + ret = 0; goto out; - ret = -EINVAL; + } - /* sanity check */ if (!rdev->desc->ops->get_optimum_mode) goto out; + /* + * we can actually do this so any errors are indicators of + * potential real failure. + */ + ret = -EINVAL; + /* get output voltage */ output_uV = _regulator_get_voltage(rdev); if (output_uV <= 0) { @@ -2116,7 +2151,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, output_uV, total_uA_load); - ret = regulator_check_mode(rdev, mode); + ret = regulator_mode_constrain(rdev, &mode); if (ret < 0) { rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", total_uA_load, input_uV, output_uV); @@ -2589,14 +2624,6 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, if (ret < 0) goto scrub; - /* set supply regulator if it exists */ - if (init_data->supply_regulator && init_data->supply_regulator_dev) { - dev_err(dev, - "Supply regulator specified by both name and dev\n"); - ret = -EINVAL; - goto scrub; - } - if (init_data->supply_regulator) { struct regulator_dev *r; int found = 0; @@ -2621,14 +2648,6 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, goto scrub; } - if (init_data->supply_regulator_dev) { - dev_warn(dev, "Uses supply_regulator_dev instead of regulator_supply\n"); - ret = set_supply(rdev, - dev_get_drvdata(init_data->supply_regulator_dev)); - if (ret < 0) - goto scrub; - } - /* add consumers devices */ for (i = 0; i < init_data->num_consumer_supplies; i++) { ret = set_consumer_device_supply(rdev, diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 77e0cfb30b23..10d5a1d9768e 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -267,7 +267,6 @@ static int max8997_get_enable_register(struct regulator_dev *rdev, default: /* Not controllable or not exists */ return -EINVAL; - break; } return 0; @@ -1033,11 +1032,11 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) /* For the safety, set max voltage before setting up */ for (i = 0; i < 8; i++) { - max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, max_buck1, 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, max_buck2, 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, max_buck5, 0x3f); } @@ -1114,13 +1113,13 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) /* Initialize all the DVS related BUCK registers */ for (i = 0; i < 8; i++) { - max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, max8997->buck1_vol[i], 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, max8997->buck2_vol[i], 0x3f); - max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1), + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, max8997->buck5_vol[i], 0x3f); } diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index f57e9c42fdb4..41a1495eec2b 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -732,13 +732,15 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) if (!pdata->buck1_set1) { printk(KERN_ERR "MAX8998 SET1 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck1_set1); - return -EIO; + ret = -EIO; + goto err_free_mem; } /* Check if SET2 is not equal to 0 */ if (!pdata->buck1_set2) { printk(KERN_ERR "MAX8998 SET2 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck1_set2); - return -EIO; + ret = -EIO; + goto err_free_mem; } gpio_request(pdata->buck1_set1, "MAX8998 BUCK1_SET1"); @@ -758,7 +760,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck1_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i); if (ret) - return ret; + goto err_free_mem; /* Set predefined value for BUCK1 register 2 */ i = 0; @@ -770,7 +772,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck1_vol[1] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE2, i); if (ret) - return ret; + goto err_free_mem; /* Set predefined value for BUCK1 register 3 */ i = 0; @@ -782,7 +784,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck1_vol[2] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE3, i); if (ret) - return ret; + goto err_free_mem; /* Set predefined value for BUCK1 register 4 */ i = 0; @@ -794,7 +796,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck1_vol[3] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE4, i); if (ret) - return ret; + goto err_free_mem; } @@ -803,7 +805,8 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) if (!pdata->buck2_set3) { printk(KERN_ERR "MAX8998 SET3 GPIO defined as 0 !\n"); WARN_ON(!pdata->buck2_set3); - return -EIO; + ret = -EIO; + goto err_free_mem; } gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3"); gpio_direction_output(pdata->buck2_set3, @@ -818,7 +821,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck2_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i); if (ret) - return ret; + goto err_free_mem; /* BUCK2 register 2 */ i = 0; @@ -830,7 +833,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) max8998->buck2_vol[1] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); if (ret) - return ret; + goto err_free_mem; } for (i = 0; i < pdata->num_regulators; i++) { @@ -860,6 +863,7 @@ err: if (rdev[i]) regulator_unregister(rdev[i]); +err_free_mem: kfree(max8998->rdev); kfree(max8998); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 1b8f7398a4a8..3285d41842f2 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -431,7 +431,8 @@ static int mc13892_sw_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); - int hi, value, val, mask, id = rdev_get_id(rdev); + int hi, value, mask, id = rdev_get_id(rdev); + u32 valread; int ret; dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", @@ -447,15 +448,16 @@ static int mc13892_sw_regulator_set_voltage(struct regulator_dev *rdev, mc13xxx_lock(priv->mc13xxx); ret = mc13xxx_reg_read(priv->mc13xxx, - mc13892_regulators[id].vsel_reg, &val); + mc13892_regulators[id].vsel_reg, &valread); if (ret) goto err; - hi = val & MC13892_SWITCHERS0_SWxHI; - if (value > 1375) + if (value > 1375000) hi = 1; - if (value < 1100) + else if (value < 1100000) hi = 0; + else + hi = valread & MC13892_SWITCHERS0_SWxHI; if (hi) { value = (value - 1100000) / 25000; @@ -464,8 +466,10 @@ static int mc13892_sw_regulator_set_voltage(struct regulator_dev *rdev, value = (value - 600000) / 25000; mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI; - ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].vsel_reg, - mask, value << mc13892_regulators[id].vsel_shift); + valread = (valread & ~mask) | + (value << mc13892_regulators[id].vsel_shift); + ret = mc13xxx_reg_write(priv->mc13xxx, mc13892_regulators[id].vsel_reg, + valread); err: mc13xxx_unlock(priv->mc13xxx); diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 2bb5de1f2421..bc27ab136378 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -174,7 +174,7 @@ static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev) dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val); - BUG_ON(val > mc13xxx_regulators[id].desc.n_voltages); + BUG_ON(val >= mc13xxx_regulators[id].desc.n_voltages); return mc13xxx_regulators[id].voltages[val]; } diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index a4d7f4540c18..1011873896dc 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -158,6 +158,7 @@ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) "failed to register regulator\n"); return ret; } + platform_set_drvdata(pdev, tps6105x); return 0; } diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 60a7ca5409e9..fbddc15e1811 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -466,7 +466,6 @@ static struct regulator_ops tps65023_ldo_ops = { static int __devinit tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) { - static int desc_id; const struct tps_info *info = (void *)id->driver_data; struct regulator_init_data *init_data; struct regulator_dev *rdev; @@ -499,7 +498,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->info[i] = info; tps->desc[i].name = info->name; - tps->desc[i].id = desc_id++; + tps->desc[i].id = i; tps->desc[i].n_voltages = num_voltages[i]; tps->desc[i].ops = (i > TPS65023_DCDC_3 ? &tps65023_ldo_ops : &tps65023_dcdc_ops); diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 064755290599..bfffabc21eda 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -553,7 +553,6 @@ static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); - static int desc_id; struct tps_info *info = &tps6507x_pmic_regs[0]; struct regulator_init_data *init_data; struct regulator_dev *rdev; @@ -598,7 +597,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) } tps->desc[i].name = info->name; - tps->desc[i].id = desc_id++; + tps->desc[i].id = i; tps->desc[i].n_voltages = num_voltages[i]; tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c new file mode 100644 index 000000000000..55dd4e6650db --- /dev/null +++ b/drivers/regulator/tps65910-regulator.c @@ -0,0 +1,993 @@ +/* + * tps65910.c -- TI tps65910 + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/mfd/tps65910.h> + +#define TPS65910_REG_VRTC 0 +#define TPS65910_REG_VIO 1 +#define TPS65910_REG_VDD1 2 +#define TPS65910_REG_VDD2 3 +#define TPS65910_REG_VDD3 4 +#define TPS65910_REG_VDIG1 5 +#define TPS65910_REG_VDIG2 6 +#define TPS65910_REG_VPLL 7 +#define TPS65910_REG_VDAC 8 +#define TPS65910_REG_VAUX1 9 +#define TPS65910_REG_VAUX2 10 +#define TPS65910_REG_VAUX33 11 +#define TPS65910_REG_VMMC 12 + +#define TPS65911_REG_VDDCTRL 4 +#define TPS65911_REG_LDO1 5 +#define TPS65911_REG_LDO2 6 +#define TPS65911_REG_LDO3 7 +#define TPS65911_REG_LDO4 8 +#define TPS65911_REG_LDO5 9 +#define TPS65911_REG_LDO6 10 +#define TPS65911_REG_LDO7 11 +#define TPS65911_REG_LDO8 12 + +#define TPS65910_NUM_REGULATOR 13 +#define TPS65910_SUPPLY_STATE_ENABLED 0x1 + +/* supported VIO voltages in milivolts */ +static const u16 VIO_VSEL_table[] = { + 1500, 1800, 2500, 3300, +}; + +/* VSEL tables for TPS65910 specific LDOs and dcdc's */ + +/* supported VDD3 voltages in milivolts */ +static const u16 VDD3_VSEL_table[] = { + 5000, +}; + +/* supported VDIG1 voltages in milivolts */ +static const u16 VDIG1_VSEL_table[] = { + 1200, 1500, 1800, 2700, +}; + +/* supported VDIG2 voltages in milivolts */ +static const u16 VDIG2_VSEL_table[] = { + 1000, 1100, 1200, 1800, +}; + +/* supported VPLL voltages in milivolts */ +static const u16 VPLL_VSEL_table[] = { + 1000, 1100, 1800, 2500, +}; + +/* supported VDAC voltages in milivolts */ +static const u16 VDAC_VSEL_table[] = { + 1800, 2600, 2800, 2850, +}; + +/* supported VAUX1 voltages in milivolts */ +static const u16 VAUX1_VSEL_table[] = { + 1800, 2500, 2800, 2850, +}; + +/* supported VAUX2 voltages in milivolts */ +static const u16 VAUX2_VSEL_table[] = { + 1800, 2800, 2900, 3300, +}; + +/* supported VAUX33 voltages in milivolts */ +static const u16 VAUX33_VSEL_table[] = { + 1800, 2000, 2800, 3300, +}; + +/* supported VMMC voltages in milivolts */ +static const u16 VMMC_VSEL_table[] = { + 1800, 2800, 3000, 3300, +}; + +struct tps_info { + const char *name; + unsigned min_uV; + unsigned max_uV; + u8 table_len; + const u16 *table; +}; + +static struct tps_info tps65910_regs[] = { + { + .name = "VRTC", + }, + { + .name = "VIO", + .min_uV = 1500000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VIO_VSEL_table), + .table = VIO_VSEL_table, + }, + { + .name = "VDD1", + .min_uV = 600000, + .max_uV = 4500000, + }, + { + .name = "VDD2", + .min_uV = 600000, + .max_uV = 4500000, + }, + { + .name = "VDD3", + .min_uV = 5000000, + .max_uV = 5000000, + .table_len = ARRAY_SIZE(VDD3_VSEL_table), + .table = VDD3_VSEL_table, + }, + { + .name = "VDIG1", + .min_uV = 1200000, + .max_uV = 2700000, + .table_len = ARRAY_SIZE(VDIG1_VSEL_table), + .table = VDIG1_VSEL_table, + }, + { + .name = "VDIG2", + .min_uV = 1000000, + .max_uV = 1800000, + .table_len = ARRAY_SIZE(VDIG2_VSEL_table), + .table = VDIG2_VSEL_table, + }, + { + .name = "VPLL", + .min_uV = 1000000, + .max_uV = 2500000, + .table_len = ARRAY_SIZE(VPLL_VSEL_table), + .table = VPLL_VSEL_table, + }, + { + .name = "VDAC", + .min_uV = 1800000, + .max_uV = 2850000, + .table_len = ARRAY_SIZE(VDAC_VSEL_table), + .table = VDAC_VSEL_table, + }, + { + .name = "VAUX1", + .min_uV = 1800000, + .max_uV = 2850000, + .table_len = ARRAY_SIZE(VAUX1_VSEL_table), + .table = VAUX1_VSEL_table, + }, + { + .name = "VAUX2", + .min_uV = 1800000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VAUX2_VSEL_table), + .table = VAUX2_VSEL_table, + }, + { + .name = "VAUX33", + .min_uV = 1800000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VAUX33_VSEL_table), + .table = VAUX33_VSEL_table, + }, + { + .name = "VMMC", + .min_uV = 1800000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VMMC_VSEL_table), + .table = VMMC_VSEL_table, + }, +}; + +static struct tps_info tps65911_regs[] = { + { + .name = "VIO", + .min_uV = 1500000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VIO_VSEL_table), + .table = VIO_VSEL_table, + }, + { + .name = "VDD1", + .min_uV = 600000, + .max_uV = 4500000, + }, + { + .name = "VDD2", + .min_uV = 600000, + .max_uV = 4500000, + }, + { + .name = "VDDCTRL", + .min_uV = 600000, + .max_uV = 1400000, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO2", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO3", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO4", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO5", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO6", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO7", + .min_uV = 1000000, + .max_uV = 3300000, + }, + { + .name = "LDO8", + .min_uV = 1000000, + .max_uV = 3300000, + }, +}; + +struct tps65910_reg { + struct regulator_desc desc[TPS65910_NUM_REGULATOR]; + struct tps65910 *mfd; + struct regulator_dev *rdev[TPS65910_NUM_REGULATOR]; + struct tps_info *info[TPS65910_NUM_REGULATOR]; + struct mutex mutex; + int mode; + int (*get_ctrl_reg)(int); +}; + +static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) +{ + u8 val; + int err; + + err = pmic->mfd->read(pmic->mfd, reg, 1, &val); + if (err) + return err; + + return val; +} + +static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val) +{ + return pmic->mfd->write(pmic->mfd, reg, 1, &val); +} + +static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg, + u8 set_mask, u8 clear_mask) +{ + int err, data; + + mutex_lock(&pmic->mutex); + + data = tps65910_read(pmic, reg); + if (data < 0) { + dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data &= ~clear_mask; + data |= set_mask; + err = tps65910_write(pmic, reg, data); + if (err) + dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&pmic->mutex); + return err; +} + +static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg) +{ + int data; + + mutex_lock(&pmic->mutex); + + data = tps65910_read(pmic, reg); + if (data < 0) + dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg); + + mutex_unlock(&pmic->mutex); + return data; +} + +static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val) +{ + int err; + + mutex_lock(&pmic->mutex); + + err = tps65910_write(pmic, reg, val); + if (err < 0) + dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&pmic->mutex); + return err; +} + +static int tps65910_get_ctrl_register(int id) +{ + switch (id) { + case TPS65910_REG_VRTC: + return TPS65910_VRTC; + case TPS65910_REG_VIO: + return TPS65910_VIO; + case TPS65910_REG_VDD1: + return TPS65910_VDD1; + case TPS65910_REG_VDD2: + return TPS65910_VDD2; + case TPS65910_REG_VDD3: + return TPS65910_VDD3; + case TPS65910_REG_VDIG1: + return TPS65910_VDIG1; + case TPS65910_REG_VDIG2: + return TPS65910_VDIG2; + case TPS65910_REG_VPLL: + return TPS65910_VPLL; + case TPS65910_REG_VDAC: + return TPS65910_VDAC; + case TPS65910_REG_VAUX1: + return TPS65910_VAUX1; + case TPS65910_REG_VAUX2: + return TPS65910_VAUX2; + case TPS65910_REG_VAUX33: + return TPS65910_VAUX33; + case TPS65910_REG_VMMC: + return TPS65910_VMMC; + default: + return -EINVAL; + } +} + +static int tps65911_get_ctrl_register(int id) +{ + switch (id) { + case TPS65910_REG_VRTC: + return TPS65910_VRTC; + case TPS65910_REG_VIO: + return TPS65910_VIO; + case TPS65910_REG_VDD1: + return TPS65910_VDD1; + case TPS65910_REG_VDD2: + return TPS65910_VDD2; + case TPS65911_REG_VDDCTRL: + return TPS65911_VDDCTRL; + case TPS65911_REG_LDO1: + return TPS65911_LDO1; + case TPS65911_REG_LDO2: + return TPS65911_LDO2; + case TPS65911_REG_LDO3: + return TPS65911_LDO3; + case TPS65911_REG_LDO4: + return TPS65911_LDO4; + case TPS65911_REG_LDO5: + return TPS65911_LDO5; + case TPS65911_REG_LDO6: + return TPS65911_LDO6; + case TPS65911_REG_LDO7: + return TPS65911_LDO7; + case TPS65911_REG_LDO8: + return TPS65911_LDO8; + default: + return -EINVAL; + } +} + +static int tps65910_is_enabled(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + value = tps65910_reg_read(pmic, reg); + if (value < 0) + return value; + + return value & TPS65910_SUPPLY_STATE_ENABLED; +} + +static int tps65910_enable(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct tps65910 *mfd = pmic->mfd; + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65910_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); +} + +static int tps65910_disable(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct tps65910 *mfd = pmic->mfd; + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); +} + + +static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct tps65910 *mfd = pmic->mfd; + int reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + return tps65910_modify_bits(pmic, reg, LDO_ST_ON_BIT, + LDO_ST_MODE_BIT); + case REGULATOR_MODE_IDLE: + value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; + return tps65910_set_bits(mfd, reg, value); + case REGULATOR_MODE_STANDBY: + return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT); + } + + return -EINVAL; +} + +static unsigned int tps65910_get_mode(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + value = tps65910_reg_read(pmic, reg); + if (value < 0) + return value; + + if (value & LDO_ST_ON_BIT) + return REGULATOR_MODE_STANDBY; + else if (value & LDO_ST_MODE_BIT) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int tps65910_get_voltage_dcdc(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev), voltage = 0; + int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0; + + switch (id) { + case TPS65910_REG_VDD1: + opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP); + mult = tps65910_reg_read(pmic, TPS65910_VDD1); + mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; + srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR); + sr = opvsel & VDD1_OP_CMD_MASK; + opvsel &= VDD1_OP_SEL_MASK; + srvsel &= VDD1_SR_SEL_MASK; + vselmax = 75; + break; + case TPS65910_REG_VDD2: + opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP); + mult = tps65910_reg_read(pmic, TPS65910_VDD2); + mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; + srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR); + sr = opvsel & VDD2_OP_CMD_MASK; + opvsel &= VDD2_OP_SEL_MASK; + srvsel &= VDD2_SR_SEL_MASK; + vselmax = 75; + break; + case TPS65911_REG_VDDCTRL: + opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP); + srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR); + sr = opvsel & VDDCTRL_OP_CMD_MASK; + opvsel &= VDDCTRL_OP_SEL_MASK; + srvsel &= VDDCTRL_SR_SEL_MASK; + vselmax = 64; + break; + } + + /* multiplier 0 == 1 but 2,3 normal */ + if (!mult) + mult=1; + + if (sr) { + /* normalise to valid range */ + if (srvsel < 3) + srvsel = 3; + if (srvsel > vselmax) + srvsel = vselmax; + srvsel -= 3; + + voltage = (srvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100; + } else { + + /* normalise to valid range*/ + if (opvsel < 3) + opvsel = 3; + if (opvsel > vselmax) + opvsel = vselmax; + opvsel -= 3; + + voltage = (opvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100; + } + + voltage *= mult; + + return voltage; +} + +static int tps65910_get_voltage(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, value, id = rdev_get_id(dev), voltage = 0; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + value = tps65910_reg_read(pmic, reg); + if (value < 0) + return value; + + switch (id) { + case TPS65910_REG_VIO: + case TPS65910_REG_VDIG1: + case TPS65910_REG_VDIG2: + case TPS65910_REG_VPLL: + case TPS65910_REG_VDAC: + case TPS65910_REG_VAUX1: + case TPS65910_REG_VAUX2: + case TPS65910_REG_VAUX33: + case TPS65910_REG_VMMC: + value &= LDO_SEL_MASK; + value >>= LDO_SEL_SHIFT; + break; + default: + return -EINVAL; + } + + voltage = pmic->info[id]->table[value] * 1000; + + return voltage; +} + +static int tps65910_get_voltage_vdd3(struct regulator_dev *dev) +{ + return 5 * 1000 * 1000; +} + +static int tps65911_get_voltage(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int step_mv, id = rdev_get_id(dev); + u8 value, reg; + + reg = pmic->get_ctrl_reg(id); + + value = tps65910_reg_read(pmic, reg); + + switch (id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + value &= LDO1_SEL_MASK; + value >>= LDO_SEL_SHIFT; + /* The first 5 values of the selector correspond to 1V */ + if (value < 5) + value = 0; + else + value -= 4; + + step_mv = 50; + break; + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + value &= LDO3_SEL_MASK; + value >>= LDO_SEL_SHIFT; + /* The first 3 values of the selector correspond to 1V */ + if (value < 3) + value = 0; + else + value -= 2; + + step_mv = 100; + break; + case TPS65910_REG_VIO: + return pmic->info[id]->table[value] * 1000; + break; + default: + return -EINVAL; + } + + return (LDO_MIN_VOLT + value * step_mv) * 1000; +} + +static int tps65910_set_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev), vsel; + int dcdc_mult = 0; + + switch (id) { + case TPS65910_REG_VDD1: + dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1; + if (dcdc_mult == 1) + dcdc_mult--; + vsel = (selector % VDD1_2_NUM_VOLTS) + 3; + + tps65910_modify_bits(pmic, TPS65910_VDD1, + (dcdc_mult << VDD1_VGAIN_SEL_SHIFT), + VDD1_VGAIN_SEL_MASK); + tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel); + break; + case TPS65910_REG_VDD2: + dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1; + if (dcdc_mult == 1) + dcdc_mult--; + vsel = (selector % VDD1_2_NUM_VOLTS) + 3; + + tps65910_modify_bits(pmic, TPS65910_VDD2, + (dcdc_mult << VDD2_VGAIN_SEL_SHIFT), + VDD1_VGAIN_SEL_MASK); + tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel); + break; + case TPS65911_REG_VDDCTRL: + vsel = selector; + tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel); + } + + return 0; +} + +static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (id) { + case TPS65910_REG_VIO: + case TPS65910_REG_VDIG1: + case TPS65910_REG_VDIG2: + case TPS65910_REG_VPLL: + case TPS65910_REG_VDAC: + case TPS65910_REG_VAUX1: + case TPS65910_REG_VAUX2: + case TPS65910_REG_VAUX33: + case TPS65910_REG_VMMC: + return tps65910_modify_bits(pmic, reg, + (selector << LDO_SEL_SHIFT), LDO_SEL_MASK); + } + + return -EINVAL; +} + +static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + return tps65910_modify_bits(pmic, reg, + (selector << LDO_SEL_SHIFT), LDO1_SEL_MASK); + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + case TPS65910_REG_VIO: + return tps65910_modify_bits(pmic, reg, + (selector << LDO_SEL_SHIFT), LDO3_SEL_MASK); + } + + return -EINVAL; +} + + +static int tps65910_list_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) +{ + int volt, mult = 1, id = rdev_get_id(dev); + + switch (id) { + case TPS65910_REG_VDD1: + case TPS65910_REG_VDD2: + mult = (selector / VDD1_2_NUM_VOLTS) + 1; + volt = VDD1_2_MIN_VOLT + + (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET; + case TPS65911_REG_VDDCTRL: + volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET); + } + + return volt * 100 * mult; +} + +static int tps65910_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev), voltage; + + if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC) + return -EINVAL; + + if (selector >= pmic->info[id]->table_len) + return -EINVAL; + else + voltage = pmic->info[id]->table[selector] * 1000; + + return voltage; +} + +static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int step_mv = 0, id = rdev_get_id(dev); + + switch(id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + /* The first 5 values of the selector correspond to 1V */ + if (selector < 5) + selector = 0; + else + selector -= 4; + + step_mv = 50; + break; + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + /* The first 3 values of the selector correspond to 1V */ + if (selector < 3) + selector = 0; + else + selector -= 2; + + step_mv = 100; + break; + case TPS65910_REG_VIO: + return pmic->info[id]->table[selector] * 1000; + default: + return -EINVAL; + } + + return (LDO_MIN_VOLT + selector * step_mv) * 1000; +} + +/* Regulator ops (except VRTC) */ +static struct regulator_ops tps65910_ops_dcdc = { + .is_enabled = tps65910_is_enabled, + .enable = tps65910_enable, + .disable = tps65910_disable, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage = tps65910_get_voltage_dcdc, + .set_voltage_sel = tps65910_set_voltage_dcdc, + .list_voltage = tps65910_list_voltage_dcdc, +}; + +static struct regulator_ops tps65910_ops_vdd3 = { + .is_enabled = tps65910_is_enabled, + .enable = tps65910_enable, + .disable = tps65910_disable, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage = tps65910_get_voltage_vdd3, + .list_voltage = tps65910_list_voltage, +}; + +static struct regulator_ops tps65910_ops = { + .is_enabled = tps65910_is_enabled, + .enable = tps65910_enable, + .disable = tps65910_disable, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage = tps65910_get_voltage, + .set_voltage_sel = tps65910_set_voltage, + .list_voltage = tps65910_list_voltage, +}; + +static struct regulator_ops tps65911_ops = { + .is_enabled = tps65910_is_enabled, + .enable = tps65910_enable, + .disable = tps65910_disable, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage = tps65911_get_voltage, + .set_voltage_sel = tps65911_set_voltage, + .list_voltage = tps65911_list_voltage, +}; + +static __devinit int tps65910_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct tps_info *info; + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct tps65910_reg *pmic; + struct tps65910_board *pmic_plat_data; + int i, err; + + pmic_plat_data = dev_get_platdata(tps65910->dev); + if (!pmic_plat_data) + return -EINVAL; + + reg_data = pmic_plat_data->tps65910_pmic_init_data; + + pmic = kzalloc(sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + mutex_init(&pmic->mutex); + pmic->mfd = tps65910; + platform_set_drvdata(pdev, pmic); + + /* Give control of all register to control port */ + tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL, + DEVCTRL_SR_CTL_I2C_SEL_MASK); + + switch(tps65910_chip_id(tps65910)) { + case TPS65910: + pmic->get_ctrl_reg = &tps65910_get_ctrl_register; + info = tps65910_regs; + case TPS65911: + pmic->get_ctrl_reg = &tps65911_get_ctrl_register; + info = tps65911_regs; + default: + pr_err("Invalid tps chip version\n"); + return -ENODEV; + } + + for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) { + /* Register the regulators */ + pmic->info[i] = info; + + pmic->desc[i].name = info->name; + pmic->desc[i].id = i; + pmic->desc[i].n_voltages = info->table_len; + + if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) { + pmic->desc[i].ops = &tps65910_ops_dcdc; + } else if (i == TPS65910_REG_VDD3) { + if (tps65910_chip_id(tps65910) == TPS65910) + pmic->desc[i].ops = &tps65910_ops_vdd3; + else + pmic->desc[i].ops = &tps65910_ops_dcdc; + } else { + if (tps65910_chip_id(tps65910) == TPS65910) + pmic->desc[i].ops = &tps65910_ops; + else + pmic->desc[i].ops = &tps65911_ops; + } + + pmic->desc[i].type = REGULATOR_VOLTAGE; + pmic->desc[i].owner = THIS_MODULE; + + rdev = regulator_register(&pmic->desc[i], + tps65910->dev, reg_data, pmic); + if (IS_ERR(rdev)) { + dev_err(tps65910->dev, + "failed to register %s regulator\n", + pdev->name); + err = PTR_ERR(rdev); + goto err; + } + + /* Save regulator for cleanup */ + pmic->rdev[i] = rdev; + } + return 0; + +err: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +static int __devexit tps65910_remove(struct platform_device *pdev) +{ + struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < TPS65910_NUM_REGULATOR; i++) + regulator_unregister(tps65910_reg->rdev[i]); + + kfree(tps65910_reg); + return 0; +} + +static struct platform_driver tps65910_driver = { + .driver = { + .name = "tps65910-pmic", + .owner = THIS_MODULE, + }, + .probe = tps65910_probe, + .remove = __devexit_p(tps65910_remove), +}; + +static int __init tps65910_init(void) +{ + return platform_driver_register(&tps65910_driver); +} +subsys_initcall(tps65910_init); + +static void __exit tps65910_cleanup(void) +{ + platform_driver_unregister(&tps65910_driver); +} +module_exit(tps65910_cleanup); + +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65910-pmic"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 6a292852a358..87fe0f75a56e 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -51,8 +51,13 @@ struct twlreg_info { u16 min_mV; u16 max_mV; + u8 flags; + /* used by regulator core */ struct regulator_desc desc; + + /* chip specific features */ + unsigned long features; }; @@ -70,12 +75,35 @@ struct twlreg_info { #define VREG_TRANS 1 #define VREG_STATE 2 #define VREG_VOLTAGE 3 +#define VREG_VOLTAGE_SMPS 4 /* TWL6030 Misc register offsets */ #define VREG_BC_ALL 1 #define VREG_BC_REF 2 #define VREG_BC_PROC 3 #define VREG_BC_CLK_RST 4 +/* TWL6030 LDO register values for CFG_STATE */ +#define TWL6030_CFG_STATE_OFF 0x00 +#define TWL6030_CFG_STATE_ON 0x01 +#define TWL6030_CFG_STATE_OFF2 0x02 +#define TWL6030_CFG_STATE_SLEEP 0x03 +#define TWL6030_CFG_STATE_GRP_SHIFT 5 +#define TWL6030_CFG_STATE_APP_SHIFT 2 +#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) +#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ + TWL6030_CFG_STATE_APP_SHIFT) + +/* Flags for SMPS Voltage reading */ +#define SMPS_OFFSET_EN BIT(0) +#define SMPS_EXTENDED_EN BIT(1) + +/* twl6025 SMPS EPROM values */ +#define TWL6030_SMPS_OFFSET 0xB0 +#define TWL6030_SMPS_MULT 0xB3 +#define SMPS_MULTOFFSET_SMPS4 BIT(0) +#define SMPS_MULTOFFSET_VIO BIT(1) +#define SMPS_MULTOFFSET_SMPS3 BIT(6) + static inline int twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) { @@ -118,21 +146,38 @@ static int twlreg_grp(struct regulator_dev *rdev) #define P2_GRP_6030 BIT(1) /* "peripherals" */ #define P1_GRP_6030 BIT(0) /* CPU/Linux */ -static int twlreg_is_enabled(struct regulator_dev *rdev) +static int twl4030reg_is_enabled(struct regulator_dev *rdev) { int state = twlreg_grp(rdev); if (state < 0) return state; - if (twl_class_is_4030()) - state &= P1_GRP_4030; + return state & P1_GRP_4030; +} + +static int twl6030reg_is_enabled(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0, val; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); + if (grp < 0) + return grp; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp &= P1_GRP_6030; else - state &= P1_GRP_6030; - return state; + grp = 1; + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + val = TWL6030_CFG_STATE_APP(val); + + return grp && (val == TWL6030_CFG_STATE_ON); } -static int twlreg_enable(struct regulator_dev *rdev) +static int twl4030reg_enable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); int grp; @@ -142,10 +187,7 @@ static int twlreg_enable(struct regulator_dev *rdev) if (grp < 0) return grp; - if (twl_class_is_4030()) - grp |= P1_GRP_4030; - else - grp |= P1_GRP_6030; + grp |= P1_GRP_4030; ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); @@ -154,29 +196,63 @@ static int twlreg_enable(struct regulator_dev *rdev) return ret; } -static int twlreg_disable(struct regulator_dev *rdev) +static int twl6030reg_enable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); + if (grp < 0) + return grp; + + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + grp << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_ON); + + udelay(info->delay); + + return ret; +} + +static int twl4030reg_disable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); int grp; + int ret; grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); if (grp < 0) return grp; - if (twl_class_is_4030()) - grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030); - else - grp &= ~(P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030); + grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030); - return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); + + return ret; } -static int twlreg_get_status(struct regulator_dev *rdev) +static int twl6030reg_disable(struct regulator_dev *rdev) { - int state = twlreg_grp(rdev); + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; + + /* For 6030, set the off state for all grps enabled */ + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + (grp) << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_OFF); + + return ret; +} - if (twl_class_is_6030()) - return 0; /* FIXME return for 6030 regulator */ +static int twl4030reg_get_status(struct regulator_dev *rdev) +{ + int state = twlreg_grp(rdev); if (state < 0) return state; @@ -190,15 +266,39 @@ static int twlreg_get_status(struct regulator_dev *rdev) : REGULATOR_STATUS_STANDBY; } -static int twlreg_set_mode(struct regulator_dev *rdev, unsigned mode) +static int twl6030reg_get_status(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int val; + + val = twlreg_grp(rdev); + if (val < 0) + return val; + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + + switch (TWL6030_CFG_STATE_APP(val)) { + case TWL6030_CFG_STATE_ON: + return REGULATOR_STATUS_NORMAL; + + case TWL6030_CFG_STATE_SLEEP: + return REGULATOR_STATUS_STANDBY; + + case TWL6030_CFG_STATE_OFF: + case TWL6030_CFG_STATE_OFF2: + default: + break; + } + + return REGULATOR_STATUS_OFF; +} + +static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) { struct twlreg_info *info = rdev_get_drvdata(rdev); unsigned message; int status; - if (twl_class_is_6030()) - return 0; /* FIXME return for 6030 regulator */ - /* We can only set the mode through state machine commands... */ switch (mode) { case REGULATOR_MODE_NORMAL: @@ -227,6 +327,36 @@ static int twlreg_set_mode(struct regulator_dev *rdev, unsigned mode) message & 0xff, TWL4030_PM_MASTER_PB_WORD_LSB); } +static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int val; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); + + if (grp < 0) + return grp; + + /* Compose the state register settings */ + val = grp << TWL6030_CFG_STATE_GRP_SHIFT; + /* We can only set the mode through state machine commands... */ + switch (mode) { + case REGULATOR_MODE_NORMAL: + val |= TWL6030_CFG_STATE_ON; + break; + case REGULATOR_MODE_STANDBY: + val |= TWL6030_CFG_STATE_SLEEP; + break; + + default: + return -EINVAL; + } + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); +} + /*----------------------------------------------------------------------*/ /* @@ -375,13 +505,13 @@ static struct regulator_ops twl4030ldo_ops = { .set_voltage = twl4030ldo_set_voltage, .get_voltage = twl4030ldo_get_voltage, - .enable = twlreg_enable, - .disable = twlreg_disable, - .is_enabled = twlreg_is_enabled, + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, - .set_mode = twlreg_set_mode, + .set_mode = twl4030reg_set_mode, - .get_status = twlreg_get_status, + .get_status = twl4030reg_get_status, }; static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) @@ -433,13 +563,13 @@ static struct regulator_ops twl6030ldo_ops = { .set_voltage = twl6030ldo_set_voltage, .get_voltage = twl6030ldo_get_voltage, - .enable = twlreg_enable, - .disable = twlreg_disable, - .is_enabled = twlreg_is_enabled, + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, - .set_mode = twlreg_set_mode, + .set_mode = twl6030reg_set_mode, - .get_status = twlreg_get_status, + .get_status = twl6030reg_get_status, }; /*----------------------------------------------------------------------*/ @@ -461,25 +591,242 @@ static int twlfixed_get_voltage(struct regulator_dev *rdev) return info->min_mV * 1000; } -static struct regulator_ops twlfixed_ops = { +static struct regulator_ops twl4030fixed_ops = { + .list_voltage = twlfixed_list_voltage, + + .get_voltage = twlfixed_get_voltage, + + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, + + .set_mode = twl4030reg_set_mode, + + .get_status = twl4030reg_get_status, +}; + +static struct regulator_ops twl6030fixed_ops = { .list_voltage = twlfixed_list_voltage, .get_voltage = twlfixed_get_voltage, - .enable = twlreg_enable, - .disable = twlreg_disable, - .is_enabled = twlreg_is_enabled, + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, - .set_mode = twlreg_set_mode, + .set_mode = twl6030reg_set_mode, - .get_status = twlreg_get_status, + .get_status = twl6030reg_get_status, }; static struct regulator_ops twl6030_fixed_resource = { - .enable = twlreg_enable, - .disable = twlreg_disable, - .is_enabled = twlreg_is_enabled, - .get_status = twlreg_get_status, + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + .get_status = twl6030reg_get_status, +}; + +/* + * SMPS status and control + */ + +static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + int voltage = 0; + + switch (info->flags) { + case SMPS_OFFSET_EN: + voltage = 100000; + /* fall through */ + case 0: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 1350 * 1000; + break; + case 59: + voltage = 1500 * 1000; + break; + case 60: + voltage = 1800 * 1000; + break; + case 61: + voltage = 1900 * 1000; + break; + case 62: + voltage = 2100 * 1000; + break; + default: + voltage += (600000 + (12500 * (index - 1))); + } + break; + case SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 2084 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (1852000 + (38600 * (index - 1))); + } + break; + case SMPS_OFFSET_EN | SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 4167 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (2161000 + (38600 * (index - 1))); + } + break; + } + + return voltage; +} + +static int +twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned int *selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = 0; + + switch (info->flags) { + case 0: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 600000) && (max_uV <= 1300000)) { + vsel = (min_uV - 600000) / 125; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (max_uV >= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (max_uV >= 1900000)) + vsel = 61; + else if ((min_uV > 1500000) && (max_uV >= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (max_uV >= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (max_uV >= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_OFFSET_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 700000) && (max_uV <= 1420000)) { + vsel = (min_uV - 700000) / 125; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (max_uV >= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (max_uV >= 1900000)) + vsel = 61; + else if ((min_uV > 1350000) && (max_uV >= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (max_uV >= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (max_uV >= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_EXTENDED_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { + vsel = (min_uV - 1852000) / 386; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + break; + case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 2161000) && (max_uV <= 4321000)) { + vsel = (min_uV - 1852000) / 386; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + break; + } + + *selector = vsel; + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, + vsel); +} + +static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS); +} + +static struct regulator_ops twlsmps_ops = { + .list_voltage = twl6030smps_list_voltage, + + .set_voltage = twl6030smps_set_voltage, + .get_voltage_sel = twl6030smps_get_voltage_sel, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, }; /*----------------------------------------------------------------------*/ @@ -487,11 +834,10 @@ static struct regulator_ops twl6030_fixed_resource = { #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ remap_conf) \ TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ - remap_conf, TWL4030) -#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ - remap_conf) \ + remap_conf, TWL4030, twl4030fixed_ops) +#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \ TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ - remap_conf, TWL6030) + 0x0, TWL6030, twl6030fixed_ops) #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \ .base = offset, \ @@ -510,13 +856,11 @@ static struct regulator_ops twl6030_fixed_resource = { }, \ } -#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num, \ - remap_conf) { \ +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ .base = offset, \ .id = num, \ .min_mV = min_mVolts, \ .max_mV = max_mVolts, \ - .remap = remap_conf, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ @@ -527,9 +871,23 @@ static struct regulator_ops twl6030_fixed_resource = { }, \ } +#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ + .base = offset, \ + .id = num, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6025_REG_##label, \ + .n_voltages = ((max_mVolts - min_mVolts)/100) + 1, \ + .ops = &twl6030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } #define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \ - family) { \ + family, operations) { \ .base = offset, \ .id = num, \ .min_mV = mVolts, \ @@ -539,17 +897,16 @@ static struct regulator_ops twl6030_fixed_resource = { .name = #label, \ .id = family##_REG_##label, \ .n_voltages = 1, \ - .ops = &twlfixed_ops, \ + .ops = &operations, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ }, \ } -#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay, remap_conf) { \ +#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \ .base = offset, \ .id = num, \ .delay = turnon_delay, \ - .remap = remap_conf, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ @@ -559,6 +916,21 @@ static struct regulator_ops twl6030_fixed_resource = { }, \ } +#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \ + .base = offset, \ + .id = num, \ + .min_mV = 600, \ + .max_mV = 2100, \ + .desc = { \ + .name = #label, \ + .id = TWL6025_REG_##label, \ + .n_voltages = 63, \ + .ops = &twlsmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + /* * We list regulators here if systems need some level of * software control over them after boot. @@ -589,19 +961,52 @@ static struct twlreg_info twl_regs[] = { /* 6030 REG with base as PMC Slave Misc : 0x0030 */ /* Turnon-delay and remap configuration values for 6030 are not verified since the specification is not public */ - TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1, 0x21), - TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2, 0x21), - TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3, 0x21), - TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4, 0x21), - TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5, 0x21), - TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7, 0x21), - TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x21), - TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x21), - TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x21), - TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21), - TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1), + TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2), + TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3), + TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4), + TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5), + TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7), + TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0), + TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0), + TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0), + TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0), + TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0), + + /* 6025 are renamed compared to 6030 versions */ + TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1), + TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2), + TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3), + TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4), + TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5), + TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7), + TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16), + TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17), + TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18), + + TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1), + TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2), + TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3), }; +static u8 twl_get_smps_offset(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_OFFSET); + return value; +} + +static u8 twl_get_smps_mult(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_MULT); + return value; +} + static int __devinit twlreg_probe(struct platform_device *pdev) { int i; @@ -623,6 +1028,9 @@ static int __devinit twlreg_probe(struct platform_device *pdev) if (!initdata) return -EINVAL; + /* copy the features into regulator data */ + info->features = (unsigned long)initdata->driver_data; + /* Constrain board-specific capabilities according to what * this driver and the chip itself can actually do. */ @@ -645,6 +1053,27 @@ static int __devinit twlreg_probe(struct platform_device *pdev) break; } + switch (pdev->id) { + case TWL6025_REG_SMPS3: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6025_REG_SMPS4: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6025_REG_VIO: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_OFFSET_EN; + break; + } + rdev = regulator_register(&info->desc, &pdev->dev, initdata, info); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "can't register %s, %ld\n", @@ -653,7 +1082,8 @@ static int __devinit twlreg_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); - twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, + if (twl_class_is_4030()) + twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, info->remap); /* NOTE: many regulators support short-circuit IRQs (presentable diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index e93453b1b978..a0982e809851 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -600,7 +600,6 @@ err: static __devexit int wm831x_buckv_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - struct wm831x *wm831x = dcdc->wm831x; platform_set_drvdata(pdev, NULL); @@ -776,7 +775,6 @@ err: static __devexit int wm831x_buckp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - struct wm831x *wm831x = dcdc->wm831x; platform_set_drvdata(pdev, NULL); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index b42d01cef35a..0f12c70bebc9 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -55,7 +55,7 @@ static int wm8400_ldo_list_voltage(struct regulator_dev *dev, return 1600000 + ((selector - 14) * 100000); } -static int wm8400_ldo_get_voltage(struct regulator_dev *dev) +static int wm8400_ldo_get_voltage_sel(struct regulator_dev *dev) { struct wm8400 *wm8400 = rdev_get_drvdata(dev); u16 val; @@ -63,7 +63,7 @@ static int wm8400_ldo_get_voltage(struct regulator_dev *dev) val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); val &= WM8400_LDO1_VSEL_MASK; - return wm8400_ldo_list_voltage(dev, val); + return val; } static int wm8400_ldo_set_voltage(struct regulator_dev *dev, @@ -104,7 +104,7 @@ static struct regulator_ops wm8400_ldo_ops = { .enable = wm8400_ldo_enable, .disable = wm8400_ldo_disable, .list_voltage = wm8400_ldo_list_voltage, - .get_voltage = wm8400_ldo_get_voltage, + .get_voltage_sel = wm8400_ldo_get_voltage_sel, .set_voltage = wm8400_ldo_set_voltage, }; @@ -145,7 +145,7 @@ static int wm8400_dcdc_list_voltage(struct regulator_dev *dev, return 850000 + (selector * 25000); } -static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) +static int wm8400_dcdc_get_voltage_sel(struct regulator_dev *dev) { struct wm8400 *wm8400 = rdev_get_drvdata(dev); u16 val; @@ -154,7 +154,7 @@ static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); val &= WM8400_DC1_VSEL_MASK; - return 850000 + (25000 * val); + return val; } static int wm8400_dcdc_set_voltage(struct regulator_dev *dev, @@ -261,7 +261,7 @@ static struct regulator_ops wm8400_dcdc_ops = { .enable = wm8400_dcdc_enable, .disable = wm8400_dcdc_disable, .list_voltage = wm8400_dcdc_list_voltage, - .get_voltage = wm8400_dcdc_get_voltage, + .get_voltage_sel = wm8400_dcdc_get_voltage_sel, .set_voltage = wm8400_dcdc_set_voltage, .get_mode = wm8400_dcdc_get_mode, .set_mode = wm8400_dcdc_set_mode, diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8e437e2f6281..f822e13dc04b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -361,12 +361,39 @@ config RTC_DRV_RX8025 This driver can also be built as a module. If so, the module will be called rtc-rx8025. +config RTC_DRV_EM3027 + tristate "EM Microelectronic EM3027" + help + If you say yes here you get support for the EM + Microelectronic EM3027 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-em3027. + +config RTC_DRV_RV3029C2 + tristate "Micro Crystal RTC" + help + If you say yes here you get support for the Micro Crystal + RV3029-C2 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3029c2. + endif # I2C comment "SPI RTC drivers" if SPI_MASTER +config RTC_DRV_M41T93 + tristate "ST M41T93" + help + If you say yes here you will get support for the + ST M41T93 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t93. + config RTC_DRV_M41T94 tristate "ST M41T94" help @@ -655,6 +682,14 @@ config RTC_DRV_WM8350 This driver can also be built as a module. If so, the module will be called "rtc-wm8350". +config RTC_DRV_SPEAR + tristate "SPEAR ST RTC" + depends on PLAT_SPEAR + default y + help + If you say Y here you will get support for the RTC found on + spear + config RTC_DRV_PCF50633 depends on MFD_PCF50633 tristate "NXP PCF50633 RTC" @@ -884,6 +919,13 @@ config RTC_DRV_PXA This RTC driver uses PXA RTC registers available since pxa27x series (RDxR, RYxR) instead of legacy RCNR, RTAR. +config RTC_DRV_VT8500 + tristate "VIA/WonderMedia 85xx SoC RTC" + depends on ARCH_VT8500 + help + If you say Y here you will get access to the real time clock + built into your VIA VT8500 SoC or its relatives. + config RTC_DRV_SUN4V bool "SUN4V Hypervisor RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 612f5a88a8ee..213d725f16d4 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o +obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o @@ -53,6 +54,7 @@ obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o +obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o @@ -82,12 +84,14 @@ obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o +obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o @@ -99,6 +103,7 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o +obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c new file mode 100644 index 000000000000..d8e1c2578553 --- /dev/null +++ b/drivers/rtc/rtc-em3027.c @@ -0,0 +1,161 @@ +/* + * An rtc/i2c driver for the EM Microelectronic EM3027 + * Copyright 2011 CompuLab, Ltd. + * + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on rtc-ds1672.c by Alessandro Zummo <a.zummo@towertech.it> + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +/* Registers */ +#define EM3027_REG_ON_OFF_CTRL 0x00 +#define EM3027_REG_IRQ_CTRL 0x01 +#define EM3027_REG_IRQ_FLAGS 0x02 +#define EM3027_REG_STATUS 0x03 +#define EM3027_REG_RST_CTRL 0x04 + +#define EM3027_REG_WATCH_SEC 0x08 +#define EM3027_REG_WATCH_MIN 0x09 +#define EM3027_REG_WATCH_HOUR 0x0a +#define EM3027_REG_WATCH_DATE 0x0b +#define EM3027_REG_WATCH_DAY 0x0c +#define EM3027_REG_WATCH_MON 0x0d +#define EM3027_REG_WATCH_YEAR 0x0e + +#define EM3027_REG_ALARM_SEC 0x10 +#define EM3027_REG_ALARM_MIN 0x11 +#define EM3027_REG_ALARM_HOUR 0x12 +#define EM3027_REG_ALARM_DATE 0x13 +#define EM3027_REG_ALARM_DAY 0x14 +#define EM3027_REG_ALARM_MON 0x15 +#define EM3027_REG_ALARM_YEAR 0x16 + +static struct i2c_driver em3027_driver; + +static int em3027_get_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + + unsigned char addr = EM3027_REG_WATCH_SEC; + unsigned char buf[7]; + + struct i2c_msg msgs[] = { + {client->addr, 0, 1, &addr}, /* setup read addr */ + {client->addr, I2C_M_RD, 7, buf}, /* read time/date */ + }; + + /* read time/date registers */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + tm->tm_sec = bcd2bin(buf[0]); + tm->tm_min = bcd2bin(buf[1]); + tm->tm_hour = bcd2bin(buf[2]); + tm->tm_mday = bcd2bin(buf[3]); + tm->tm_wday = bcd2bin(buf[4]); + tm->tm_mon = bcd2bin(buf[5]); + tm->tm_year = bcd2bin(buf[6]) + 100; + + return 0; +} + +static int em3027_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + + struct i2c_msg msg = { + client->addr, 0, 8, buf, /* write time/date */ + }; + + buf[0] = EM3027_REG_WATCH_SEC; + buf[1] = bin2bcd(tm->tm_sec); + buf[2] = bin2bcd(tm->tm_min); + buf[3] = bin2bcd(tm->tm_hour); + buf[4] = bin2bcd(tm->tm_mday); + buf[5] = bin2bcd(tm->tm_wday); + buf[6] = bin2bcd(tm->tm_mon); + buf[7] = bin2bcd(tm->tm_year % 100); + + /* write time/date registers */ + if ((i2c_transfer(client->adapter, &msg, 1)) != 1) { + dev_err(&client->dev, "%s: write error\n", __func__); + return -EIO; + } + + return 0; +} + +static const struct rtc_class_ops em3027_rtc_ops = { + .read_time = em3027_get_time, + .set_time = em3027_set_time, +}; + +static int em3027_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rtc_device *rtc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + rtc = rtc_device_register(em3027_driver.driver.name, &client->dev, + &em3027_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int em3027_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +static struct i2c_device_id em3027_id[] = { + { "em3027", 0 }, + { } +}; + +static struct i2c_driver em3027_driver = { + .driver = { + .name = "rtc-em3027", + }, + .probe = &em3027_probe, + .remove = &em3027_remove, + .id_table = em3027_id, +}; + +static int __init em3027_init(void) +{ + return i2c_add_driver(&em3027_driver); +} + +static void __exit em3027_exit(void) +{ + i2c_del_driver(&em3027_driver); +} + +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver"); +MODULE_LICENSE("GPL"); + +module_init(em3027_init); +module_exit(em3027_exit); diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c new file mode 100644 index 000000000000..1a84b3e227d1 --- /dev/null +++ b/drivers/rtc/rtc-m41t93.c @@ -0,0 +1,225 @@ +/* + * + * Driver for ST M41T93 SPI RTC + * + * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH + * + * 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. + */ + +#include <linux/bcd.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> + +#define M41T93_REG_SSEC 0 +#define M41T93_REG_ST_SEC 1 +#define M41T93_REG_MIN 2 +#define M41T93_REG_CENT_HOUR 3 +#define M41T93_REG_WDAY 4 +#define M41T93_REG_DAY 5 +#define M41T93_REG_MON 6 +#define M41T93_REG_YEAR 7 + + +#define M41T93_REG_ALM_HOUR_HT 0xc +#define M41T93_REG_FLAGS 0xf + +#define M41T93_FLAG_ST (1 << 7) +#define M41T93_FLAG_OF (1 << 2) +#define M41T93_FLAG_BL (1 << 4) +#define M41T93_FLAG_HT (1 << 6) + +static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data) +{ + u8 buf[2]; + + /* MSB must be '1' to write */ + buf[0] = addr | 0x80; + buf[1] = data; + + return spi_write(spi, buf, sizeof(buf)); +} + +static int m41t93_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */ + u8 * const data = &buf[1]; /* ptr to first data byte */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + if (tm->tm_year < 100) { + dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n"); + return -EINVAL; + } + + data[M41T93_REG_SSEC] = 0; + data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec); + data[M41T93_REG_MIN] = bin2bcd(tm->tm_min); + data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) | + ((tm->tm_year/100-1) << 6); + data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday); + data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1); + data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + return spi_write(spi, buf, sizeof(buf)); +} + + +static int m41t93_get_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + const u8 start_addr = 0; + u8 buf[8]; + int century_after_1900; + int tmp; + int ret = 0; + + /* Check status of clock. Two states must be considered: + 1. halt bit (HT) is set: the clock is running but update of readout + registers has been disabled due to power failure. This is normal + case after poweron. Time is valid after resetting HT bit. + 2. oscillator fail bit (OF) is set. Oscillator has be stopped and + time is invalid: + a) OF can be immeditely reset. + b) OF cannot be immediately reset: oscillator has to be restarted. + */ + tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT); + if (tmp < 0) + return tmp; + + if (tmp & M41T93_FLAG_HT) { + dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n"); + m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT, + tmp & ~M41T93_FLAG_HT); + } + + tmp = spi_w8r8(spi, M41T93_REG_FLAGS); + if (tmp < 0) + return tmp; + + if (tmp & M41T93_FLAG_OF) { + ret = -EINVAL; + dev_warn(&spi->dev, "OF bit is set, resetting.\n"); + m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF); + + tmp = spi_w8r8(spi, M41T93_REG_FLAGS); + if (tmp < 0) + return tmp; + else if (tmp & M41T93_FLAG_OF) { + u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST; + + dev_warn(&spi->dev, + "OF bit is still set, kickstarting clock.\n"); + m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + reset_osc &= ~M41T93_FLAG_ST; + m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + } + } + + if (tmp & M41T93_FLAG_BL) + dev_warn(&spi->dev, "BL bit is set, replace battery.\n"); + + /* read actual time/date */ + tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf)); + if (tmp < 0) + return tmp; + + tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]); + tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]); + tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]); + tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1; + tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1; + + century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1; + tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + return ret < 0 ? ret : rtc_valid_tm(tm); +} + + +static const struct rtc_class_ops m41t93_rtc_ops = { + .read_time = m41t93_get_time, + .set_time = m41t93_set_time, +}; + +static struct spi_driver m41t93_driver; + +static int __devinit m41t93_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int res; + + spi->bits_per_word = 8; + spi_setup(spi); + + res = spi_w8r8(spi, M41T93_REG_WDAY); + if (res < 0 || (res & 0xf8) != 0) { + dev_err(&spi->dev, "not found 0x%x.\n", res); + return -ENODEV; + } + + rtc = rtc_device_register(m41t93_driver.driver.name, + &spi->dev, &m41t93_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + dev_set_drvdata(&spi->dev, rtc); + + return 0; +} + + +static int __devexit m41t93_remove(struct spi_device *spi) +{ + struct rtc_device *rtc = platform_get_drvdata(spi); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +static struct spi_driver m41t93_driver = { + .driver = { + .name = "rtc-m41t93", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = m41t93_probe, + .remove = __devexit_p(m41t93_remove), +}; + +static __init int m41t93_init(void) +{ + return spi_register_driver(&m41t93_driver); +} +module_init(m41t93_init); + +static __exit void m41t93_exit(void) +{ + spi_unregister_driver(&m41t93_driver); +} +module_exit(m41t93_exit); + +MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>"); +MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t93"); diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index b2f096871a97..0cec5650d56a 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -380,7 +380,7 @@ cleanup1: cleanup0: dev_set_drvdata(dev, NULL); mrst_rtc.dev = NULL; - release_region(iomem->start, iomem->end + 1 - iomem->start); + release_mem_region(iomem->start, resource_size(iomem)); dev_err(dev, "rtc-mrst: unable to initialise\n"); return retval; } @@ -406,7 +406,7 @@ static void __devexit rtc_mrst_do_remove(struct device *dev) mrst->rtc = NULL; iomem = mrst->iomem; - release_region(iomem->start, iomem->end + 1 - iomem->start); + release_mem_region(iomem->start, resource_size(iomem)); mrst->iomem = NULL; mrst->dev = NULL; diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index d814417bee8c..39e41fbdf08b 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -55,12 +55,6 @@ static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { { MAX_PIE_FREQ, RTC_SAM7_BIT }, }; -/* Those are the bits from a classic RTC we want to mimic */ -#define RTC_IRQF 0x80 /* any of the following 3 is active */ -#define RTC_PF 0x40 /* Periodic interrupt */ -#define RTC_AF 0x20 /* Alarm interrupt */ -#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */ - #define MXC_RTC_TIME 0 #define MXC_RTC_ALARM 1 diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c index f90c574f9d05..0c423892923c 100644 --- a/drivers/rtc/rtc-pcf50633.c +++ b/drivers/rtc/rtc-pcf50633.c @@ -58,7 +58,6 @@ struct pcf50633_time { struct pcf50633_rtc { int alarm_enabled; - int second_enabled; int alarm_pending; struct pcf50633 *pcf; @@ -143,7 +142,7 @@ static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct pcf50633_rtc *rtc; struct pcf50633_time pcf_tm; - int second_masked, alarm_masked, ret = 0; + int alarm_masked, ret = 0; rtc = dev_get_drvdata(dev); @@ -162,11 +161,8 @@ static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm) pcf_tm.time[PCF50633_TI_SEC]); - second_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_SECOND); alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM); - if (!second_masked) - pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND); if (!alarm_masked) pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM); @@ -175,8 +171,6 @@ static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm) PCF50633_TI_EXTENT, &pcf_tm.time[0]); - if (!second_masked) - pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND); if (!alarm_masked) pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM); @@ -250,15 +244,8 @@ static void pcf50633_rtc_irq(int irq, void *data) { struct pcf50633_rtc *rtc = data; - switch (irq) { - case PCF50633_IRQ_ALARM: - rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); - rtc->alarm_pending = 1; - break; - case PCF50633_IRQ_SECOND: - rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); - break; - } + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + rtc->alarm_pending = 1; } static int __devinit pcf50633_rtc_probe(struct platform_device *pdev) @@ -282,9 +269,6 @@ static int __devinit pcf50633_rtc_probe(struct platform_device *pdev) pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM, pcf50633_rtc_irq, rtc); - pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_SECOND, - pcf50633_rtc_irq, rtc); - return 0; } @@ -295,7 +279,6 @@ static int __devexit pcf50633_rtc_remove(struct platform_device *pdev) rtc = platform_get_drvdata(pdev); pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM); - pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND); rtc_device_unregister(rtc->rtc_dev); kfree(rtc); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c new file mode 100644 index 000000000000..ea09ff211dc6 --- /dev/null +++ b/drivers/rtc/rtc-rv3029c2.c @@ -0,0 +1,454 @@ +/* + * Micro Crystal RV-3029C2 rtc class driver + * + * Author: Gregory Hermant <gregory.hermant@calao-systems.com> + * + * based on previously existing rtc class drivers + * + * 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. + * + * NOTE: Currently this driver only supports the bare minimum for read + * and write the RTC and alarms. The extra features provided by this chip + * (trickle charger, eeprom, T° compensation) are unavailable. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> + +/* Register map */ +/* control section */ +#define RV3029C2_ONOFF_CTRL 0x00 +#define RV3029C2_IRQ_CTRL 0x01 +#define RV3029C2_IRQ_CTRL_AIE (1 << 0) +#define RV3029C2_IRQ_FLAGS 0x02 +#define RV3029C2_IRQ_FLAGS_AF (1 << 0) +#define RV3029C2_STATUS 0x03 +#define RV3029C2_STATUS_VLOW1 (1 << 2) +#define RV3029C2_STATUS_VLOW2 (1 << 3) +#define RV3029C2_STATUS_SR (1 << 4) +#define RV3029C2_STATUS_PON (1 << 5) +#define RV3029C2_STATUS_EEBUSY (1 << 7) +#define RV3029C2_RST_CTRL 0x04 +#define RV3029C2_CONTROL_SECTION_LEN 0x05 + +/* watch section */ +#define RV3029C2_W_SEC 0x08 +#define RV3029C2_W_MINUTES 0x09 +#define RV3029C2_W_HOURS 0x0A +#define RV3029C2_REG_HR_12_24 (1<<6) /* 24h/12h mode */ +#define RV3029C2_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ +#define RV3029C2_W_DATE 0x0B +#define RV3029C2_W_DAYS 0x0C +#define RV3029C2_W_MONTHS 0x0D +#define RV3029C2_W_YEARS 0x0E +#define RV3029C2_WATCH_SECTION_LEN 0x07 + +/* alarm section */ +#define RV3029C2_A_SC 0x10 +#define RV3029C2_A_MN 0x11 +#define RV3029C2_A_HR 0x12 +#define RV3029C2_A_DT 0x13 +#define RV3029C2_A_DW 0x14 +#define RV3029C2_A_MO 0x15 +#define RV3029C2_A_YR 0x16 +#define RV3029C2_ALARM_SECTION_LEN 0x07 + +/* timer section */ +#define RV3029C2_TIMER_LOW 0x18 +#define RV3029C2_TIMER_HIGH 0x19 + +/* temperature section */ +#define RV3029C2_TEMP_PAGE 0x20 + +/* eeprom data section */ +#define RV3029C2_E2P_EEDATA1 0x28 +#define RV3029C2_E2P_EEDATA2 0x29 + +/* eeprom control section */ +#define RV3029C2_CONTROL_E2P_EECTRL 0x30 +#define RV3029C2_TRICKLE_1K (1<<0) /* 1K resistance */ +#define RV3029C2_TRICKLE_5K (1<<1) /* 5K resistance */ +#define RV3029C2_TRICKLE_20K (1<<2) /* 20K resistance */ +#define RV3029C2_TRICKLE_80K (1<<3) /* 80K resistance */ +#define RV3029C2_CONTROL_E2P_XTALOFFSET 0x31 +#define RV3029C2_CONTROL_E2P_QCOEF 0x32 +#define RV3029C2_CONTROL_E2P_TURNOVER 0x33 + +/* user ram section */ +#define RV3029C2_USR1_RAM_PAGE 0x38 +#define RV3029C2_USR1_SECTION_LEN 0x04 +#define RV3029C2_USR2_RAM_PAGE 0x3C +#define RV3029C2_USR2_SECTION_LEN 0x04 + +static int +rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf, + unsigned len) +{ + int ret; + + if ((reg > RV3029C2_USR1_RAM_PAGE + 7) || + (reg + len > RV3029C2_USR1_RAM_PAGE + 8)) + return -EINVAL; + + ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf); + if (ret < 0) + return ret; + if (ret < len) + return -EIO; + return 0; +} + +static int +rv3029c2_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[], + unsigned len) +{ + if ((reg > RV3029C2_USR1_RAM_PAGE + 7) || + (reg + len > RV3029C2_USR1_RAM_PAGE + 8)) + return -EINVAL; + + return i2c_smbus_write_i2c_block_data(client, reg, len, buf); +} + +static int +rv3029c2_i2c_get_sr(struct i2c_client *client, u8 *buf) +{ + int ret = rv3029c2_i2c_read_regs(client, RV3029C2_STATUS, buf, 1); + + if (ret < 0) + return -EIO; + dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + return 0; +} + +static int +rv3029c2_i2c_set_sr(struct i2c_client *client, u8 val) +{ + u8 buf[1]; + int sr; + + buf[0] = val; + sr = rv3029c2_i2c_write_regs(client, RV3029C2_STATUS, buf, 1); + dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + if (sr < 0) + return -EIO; + return 0; +} + +static int +rv3029c2_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) +{ + u8 buf[1]; + int ret; + u8 regs[RV3029C2_WATCH_SECTION_LEN] = { 0, }; + + ret = rv3029c2_i2c_get_sr(client, buf); + if (ret < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + ret = rv3029c2_i2c_read_regs(client, RV3029C2_W_SEC , regs, + RV3029C2_WATCH_SECTION_LEN); + if (ret < 0) { + dev_err(&client->dev, "%s: reading RTC section failed\n", + __func__); + return ret; + } + + tm->tm_sec = bcd2bin(regs[RV3029C2_W_SEC-RV3029C2_W_SEC]); + tm->tm_min = bcd2bin(regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[RV3029C2_W_HOURS-RV3029C2_W_SEC]; + if (_hr & RV3029C2_REG_HR_12_24) { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & RV3029C2_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } else /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + } + + tm->tm_mday = bcd2bin(regs[RV3029C2_W_DATE-RV3029C2_W_SEC]); + tm->tm_mon = bcd2bin(regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC]) - 1; + tm->tm_year = bcd2bin(regs[RV3029C2_W_YEARS-RV3029C2_W_SEC]) + 100; + tm->tm_wday = bcd2bin(regs[RV3029C2_W_DAYS-RV3029C2_W_SEC]) - 1; + + return 0; +} + +static int rv3029c2_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return rv3029c2_i2c_read_time(to_i2c_client(dev), tm); +} + +static int +rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) +{ + struct rtc_time *const tm = &alarm->time; + int ret; + u8 regs[8]; + + ret = rv3029c2_i2c_get_sr(client, regs); + if (ret < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + ret = rv3029c2_i2c_read_regs(client, RV3029C2_A_SC, regs, + RV3029C2_ALARM_SECTION_LEN); + + if (ret < 0) { + dev_err(&client->dev, "%s: reading alarm section failed\n", + __func__); + return ret; + } + + tm->tm_sec = bcd2bin(regs[RV3029C2_A_SC-RV3029C2_A_SC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3029C2_A_MN-RV3029C2_A_SC] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3029C2_A_HR-RV3029C2_A_SC] & 0x3f); + tm->tm_mday = bcd2bin(regs[RV3029C2_A_DT-RV3029C2_A_SC] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3029C2_A_MO-RV3029C2_A_SC] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[RV3029C2_A_YR-RV3029C2_A_SC] & 0x7f) + 100; + tm->tm_wday = bcd2bin(regs[RV3029C2_A_DW-RV3029C2_A_SC] & 0x07) - 1; + + return 0; +} + +static int +rv3029c2_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return rv3029c2_i2c_read_alarm(to_i2c_client(dev), alarm); +} + +static int rv3029c2_rtc_i2c_alarm_set_irq(struct i2c_client *client, + int enable) +{ + int ret; + u8 buf[1]; + + /* enable AIE irq */ + ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_CTRL, buf, 1); + if (ret < 0) { + dev_err(&client->dev, "can't read INT reg\n"); + return ret; + } + if (enable) + buf[0] |= RV3029C2_IRQ_CTRL_AIE; + else + buf[0] &= ~RV3029C2_IRQ_CTRL_AIE; + + ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_CTRL, buf, 1); + if (ret < 0) { + dev_err(&client->dev, "can't set INT reg\n"); + return ret; + } + + return 0; +} + +static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client, + struct rtc_wkalrm *alarm) +{ + struct rtc_time *const tm = &alarm->time; + int ret; + u8 regs[8]; + + /* + * The clock has an 8 bit wide bcd-coded register (they never learn) + * for the year. tm_year is an offset from 1900 and we are interested + * in the 2000-2099 range, so any value less than 100 is invalid. + */ + if (tm->tm_year < 100) + return -EINVAL; + + ret = rv3029c2_i2c_get_sr(client, regs); + if (ret < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + regs[RV3029C2_A_SC-RV3029C2_A_SC] = bin2bcd(tm->tm_sec & 0x7f); + regs[RV3029C2_A_MN-RV3029C2_A_SC] = bin2bcd(tm->tm_min & 0x7f); + regs[RV3029C2_A_HR-RV3029C2_A_SC] = bin2bcd(tm->tm_hour & 0x3f); + regs[RV3029C2_A_DT-RV3029C2_A_SC] = bin2bcd(tm->tm_mday & 0x3f); + regs[RV3029C2_A_MO-RV3029C2_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1); + regs[RV3029C2_A_DW-RV3029C2_A_SC] = bin2bcd((tm->tm_wday & 7) - 1); + regs[RV3029C2_A_YR-RV3029C2_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100); + + ret = rv3029c2_i2c_write_regs(client, RV3029C2_A_SC, regs, + RV3029C2_ALARM_SECTION_LEN); + if (ret < 0) + return ret; + + if (alarm->enabled) { + u8 buf[1]; + + /* clear AF flag */ + ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_FLAGS, + buf, 1); + if (ret < 0) { + dev_err(&client->dev, "can't read alarm flag\n"); + return ret; + } + buf[0] &= ~RV3029C2_IRQ_FLAGS_AF; + ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_FLAGS, + buf, 1); + if (ret < 0) { + dev_err(&client->dev, "can't set alarm flag\n"); + return ret; + } + /* enable AIE irq */ + ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1); + if (ret) + return ret; + + dev_dbg(&client->dev, "alarm IRQ armed\n"); + } else { + /* disable AIE irq */ + ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1); + if (ret) + return ret; + + dev_dbg(&client->dev, "alarm IRQ disabled\n"); + } + + return 0; +} + +static int rv3029c2_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return rv3029c2_rtc_i2c_set_alarm(to_i2c_client(dev), alarm); +} + +static int +rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm) +{ + u8 regs[8]; + int ret; + + /* + * The clock has an 8 bit wide bcd-coded register (they never learn) + * for the year. tm_year is an offset from 1900 and we are interested + * in the 2000-2099 range, so any value less than 100 is invalid. + */ + if (tm->tm_year < 100) + return -EINVAL; + + regs[RV3029C2_W_SEC-RV3029C2_W_SEC] = bin2bcd(tm->tm_sec); + regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC] = bin2bcd(tm->tm_min); + regs[RV3029C2_W_HOURS-RV3029C2_W_SEC] = bin2bcd(tm->tm_hour); + regs[RV3029C2_W_DATE-RV3029C2_W_SEC] = bin2bcd(tm->tm_mday); + regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC] = bin2bcd(tm->tm_mon+1); + regs[RV3029C2_W_DAYS-RV3029C2_W_SEC] = bin2bcd((tm->tm_wday & 7)+1); + regs[RV3029C2_W_YEARS-RV3029C2_W_SEC] = bin2bcd(tm->tm_year - 100); + + ret = rv3029c2_i2c_write_regs(client, RV3029C2_W_SEC, regs, + RV3029C2_WATCH_SECTION_LEN); + if (ret < 0) + return ret; + + ret = rv3029c2_i2c_get_sr(client, regs); + if (ret < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return ret; + } + /* clear PON bit */ + ret = rv3029c2_i2c_set_sr(client, (regs[0] & ~RV3029C2_STATUS_PON)); + if (ret < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return ret; + } + + return 0; +} + +static int rv3029c2_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return rv3029c2_i2c_set_time(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops rv3029c2_rtc_ops = { + .read_time = rv3029c2_rtc_read_time, + .set_time = rv3029c2_rtc_set_time, + .read_alarm = rv3029c2_rtc_read_alarm, + .set_alarm = rv3029c2_rtc_set_alarm, +}; + +static struct i2c_device_id rv3029c2_id[] = { + { "rv3029c2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv3029c2_id); + +static int __devinit +rv3029c2_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct rtc_device *rtc; + int rc = 0; + u8 buf[1]; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL)) + return -ENODEV; + + rtc = rtc_device_register(client->name, + &client->dev, &rv3029c2_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + rc = rv3029c2_i2c_get_sr(client, buf); + if (rc < 0) { + dev_err(&client->dev, "reading status failed\n"); + goto exit_unregister; + } + + return 0; + +exit_unregister: + rtc_device_unregister(rtc); + + return rc; +} + +static int __devexit rv3029c2_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + rtc_device_unregister(rtc); + + return 0; +} + +static struct i2c_driver rv3029c2_driver = { + .driver = { + .name = "rtc-rv3029c2", + }, + .probe = rv3029c2_probe, + .remove = __devexit_p(rv3029c2_remove), + .id_table = rv3029c2_id, +}; + +static int __init rv3029c2_init(void) +{ + return i2c_add_driver(&rv3029c2_driver); +} + +static void __exit rv3029c2_exit(void) +{ + i2c_del_driver(&rv3029c2_driver); +} + +module_init(rv3029c2_init); +module_exit(rv3029c2_exit); + +MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>"); +MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c new file mode 100644 index 000000000000..893bac2bb21b --- /dev/null +++ b/drivers/rtc/rtc-spear.c @@ -0,0 +1,534 @@ +/* + * drivers/rtc/rtc-spear.c + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar<rajeev-dlh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +/* RTC registers */ +#define TIME_REG 0x00 +#define DATE_REG 0x04 +#define ALARM_TIME_REG 0x08 +#define ALARM_DATE_REG 0x0C +#define CTRL_REG 0x10 +#define STATUS_REG 0x14 + +/* TIME_REG & ALARM_TIME_REG */ +#define SECONDS_UNITS (0xf<<0) /* seconds units position */ +#define SECONDS_TENS (0x7<<4) /* seconds tens position */ +#define MINUTES_UNITS (0xf<<8) /* minutes units position */ +#define MINUTES_TENS (0x7<<12) /* minutes tens position */ +#define HOURS_UNITS (0xf<<16) /* hours units position */ +#define HOURS_TENS (0x3<<20) /* hours tens position */ + +/* DATE_REG & ALARM_DATE_REG */ +#define DAYS_UNITS (0xf<<0) /* days units position */ +#define DAYS_TENS (0x3<<4) /* days tens position */ +#define MONTHS_UNITS (0xf<<8) /* months units position */ +#define MONTHS_TENS (0x1<<12) /* months tens position */ +#define YEARS_UNITS (0xf<<16) /* years units position */ +#define YEARS_TENS (0xf<<20) /* years tens position */ +#define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */ +#define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */ + +/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/ +#define SECOND_SHIFT 0x00 /* seconds units */ +#define MINUTE_SHIFT 0x08 /* minutes units position */ +#define HOUR_SHIFT 0x10 /* hours units position */ +#define MDAY_SHIFT 0x00 /* Month day shift */ +#define MONTH_SHIFT 0x08 /* Month shift */ +#define YEAR_SHIFT 0x10 /* Year shift */ + +#define SECOND_MASK 0x7F +#define MIN_MASK 0x7F +#define HOUR_MASK 0x3F +#define DAY_MASK 0x3F +#define MONTH_MASK 0x7F +#define YEAR_MASK 0xFFFF + +/* date reg equal to time reg, for debug only */ +#define TIME_BYP (1<<9) +#define INT_ENABLE (1<<31) /* interrupt enable */ + +/* STATUS_REG */ +#define CLK_UNCONNECTED (1<<0) +#define PEND_WR_TIME (1<<2) +#define PEND_WR_DATE (1<<3) +#define LOST_WR_TIME (1<<4) +#define LOST_WR_DATE (1<<5) +#define RTC_INT_MASK (1<<31) +#define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE) +#define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) + +struct spear_rtc_config { + struct clk *clk; + spinlock_t lock; + void __iomem *ioaddr; +}; + +static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&config->lock, flags); + val = readl(config->ioaddr + STATUS_REG); + val |= RTC_INT_MASK; + writel(val, config->ioaddr + STATUS_REG); + spin_unlock_irqrestore(&config->lock, flags); +} + +static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + + val = readl(config->ioaddr + CTRL_REG); + if (!(val & INT_ENABLE)) { + spear_rtc_clear_interrupt(config); + val |= INT_ENABLE; + writel(val, config->ioaddr + CTRL_REG); + } +} + +static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + + val = readl(config->ioaddr + CTRL_REG); + if (val & INT_ENABLE) { + val &= ~INT_ENABLE; + writel(val, config->ioaddr + CTRL_REG); + } +} + +static inline int is_write_complete(struct spear_rtc_config *config) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&config->lock, flags); + if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL) + ret = -EIO; + spin_unlock_irqrestore(&config->lock, flags); + + return ret; +} + +static void rtc_wait_not_busy(struct spear_rtc_config *config) +{ + int status, count = 0; + unsigned long flags; + + /* Assuming BUSY may stay active for 80 msec) */ + for (count = 0; count < 80; count++) { + spin_lock_irqsave(&config->lock, flags); + status = readl(config->ioaddr + STATUS_REG); + spin_unlock_irqrestore(&config->lock, flags); + if ((status & STATUS_BUSY) == 0) + break; + /* check status busy, after each msec */ + msleep(1); + } +} + +static irqreturn_t spear_rtc_irq(int irq, void *dev_id) +{ + struct rtc_device *rtc = (struct rtc_device *)dev_id; + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + unsigned long flags, events = 0; + unsigned int irq_data; + + spin_lock_irqsave(&config->lock, flags); + irq_data = readl(config->ioaddr + STATUS_REG); + spin_unlock_irqrestore(&config->lock, flags); + + if ((irq_data & RTC_INT_MASK)) { + spear_rtc_clear_interrupt(config); + events = RTC_IRQF | RTC_AF; + rtc_update_irq(rtc, 1, events); + return IRQ_HANDLED; + } else + return IRQ_NONE; + +} + +static int tm2bcd(struct rtc_time *tm) +{ + if (rtc_valid_tm(tm) != 0) + return -EINVAL; + tm->tm_sec = bin2bcd(tm->tm_sec); + tm->tm_min = bin2bcd(tm->tm_min); + tm->tm_hour = bin2bcd(tm->tm_hour); + tm->tm_mday = bin2bcd(tm->tm_mday); + tm->tm_mon = bin2bcd(tm->tm_mon + 1); + tm->tm_year = bin2bcd(tm->tm_year); + + return 0; +} + +static void bcd2tm(struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon) - 1; + /* epoch == 1900 */ + tm->tm_year = bcd2bin(tm->tm_year); +} + +/* + * spear_rtc_read_time - set the time + * @dev: rtc device in use + * @tm: holds date and time + * + * This function read time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + unsigned int time, date; + + /* we don't report wday/yday/isdst ... */ + rtc_wait_not_busy(config); + + time = readl(config->ioaddr + TIME_REG); + date = readl(config->ioaddr + DATE_REG); + tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; + tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; + tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; + tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; + tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; + tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; + + bcd2tm(tm); + return 0; +} + +/* + * spear_rtc_set_time - set the time + * @dev: rtc device in use + * @tm: holds date and time + * + * This function set time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + unsigned int time, date, err = 0; + + if (tm2bcd(tm) < 0) + return -EINVAL; + + rtc_wait_not_busy(config); + time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | + (tm->tm_hour << HOUR_SHIFT); + date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | + (tm->tm_year << YEAR_SHIFT); + writel(time, config->ioaddr + TIME_REG); + writel(date, config->ioaddr + DATE_REG); + err = is_write_complete(config); + if (err < 0) + return err; + + return 0; +} + +/* + * spear_rtc_read_alarm - read the alarm time + * @dev: rtc device in use + * @alm: holds alarm date and time + * + * This function read alarm time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + unsigned int time, date; + + rtc_wait_not_busy(config); + + time = readl(config->ioaddr + ALARM_TIME_REG); + date = readl(config->ioaddr + ALARM_DATE_REG); + alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; + alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; + alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; + alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; + alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; + alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; + + bcd2tm(&alm->time); + alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; + + return 0; +} + +/* + * spear_rtc_set_alarm - set the alarm time + * @dev: rtc device in use + * @alm: holds alarm date and time + * + * This function set alarm time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + unsigned int time, date, err = 0; + + if (tm2bcd(&alm->time) < 0) + return -EINVAL; + + rtc_wait_not_busy(config); + + time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << + MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); + date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << + MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); + + writel(time, config->ioaddr + ALARM_TIME_REG); + writel(date, config->ioaddr + ALARM_DATE_REG); + err = is_write_complete(config); + if (err < 0) + return err; + + if (alm->enabled) + spear_rtc_enable_interrupt(config); + else + spear_rtc_disable_interrupt(config); + + return 0; +} +static struct rtc_class_ops spear_rtc_ops = { + .read_time = spear_rtc_read_time, + .set_time = spear_rtc_set_time, + .read_alarm = spear_rtc_read_alarm, + .set_alarm = spear_rtc_set_alarm, +}; + +static int __devinit spear_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rtc_device *rtc; + struct spear_rtc_config *config; + unsigned int status = 0; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no resource defined\n"); + return -EBUSY; + } + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "rtc region already claimed\n"); + return -EBUSY; + } + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) { + dev_err(&pdev->dev, "out of memory\n"); + status = -ENOMEM; + goto err_release_region; + } + + config->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(config->clk)) { + status = PTR_ERR(config->clk); + goto err_kfree; + } + + status = clk_enable(config->clk); + if (status < 0) + goto err_clk_put; + + config->ioaddr = ioremap(res->start, resource_size(res)); + if (!config->ioaddr) { + dev_err(&pdev->dev, "ioremap fail\n"); + status = -ENOMEM; + goto err_disable_clock; + } + + spin_lock_init(&config->lock); + + rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "can't register RTC device, err %ld\n", + PTR_ERR(rtc)); + status = PTR_ERR(rtc); + goto err_iounmap; + } + + platform_set_drvdata(pdev, rtc); + dev_set_drvdata(&rtc->dev, config); + + /* alarm irqs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no update irq?\n"); + status = irq; + goto err_clear_platdata; + } + + status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc); + if (status) { + dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ + claimed\n", irq); + goto err_clear_platdata; + } + + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, 1); + + return 0; + +err_clear_platdata: + platform_set_drvdata(pdev, NULL); + dev_set_drvdata(&rtc->dev, NULL); + rtc_device_unregister(rtc); +err_iounmap: + iounmap(config->ioaddr); +err_disable_clock: + clk_disable(config->clk); +err_clk_put: + clk_put(config->clk); +err_kfree: + kfree(config); +err_release_region: + release_mem_region(res->start, resource_size(res)); + + return status; +} + +static int __devexit spear_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + int irq; + struct resource *res; + + /* leave rtc running, but disable irqs */ + spear_rtc_disable_interrupt(config); + device_init_wakeup(&pdev->dev, 0); + irq = platform_get_irq(pdev, 0); + if (irq) + free_irq(irq, pdev); + clk_disable(config->clk); + clk_put(config->clk); + iounmap(config->ioaddr); + kfree(config); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + dev_set_drvdata(&rtc->dev, NULL); + rtc_device_unregister(rtc); + + return 0; +} + +#ifdef CONFIG_PM + +static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + int irq; + + irq = platform_get_irq(pdev, 0); + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(irq); + else { + spear_rtc_disable_interrupt(config); + clk_disable(config->clk); + } + + return 0; +} + +static int spear_rtc_resume(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + int irq; + + irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(irq); + else { + clk_enable(config->clk); + spear_rtc_enable_interrupt(config); + } + + return 0; +} + +#else +#define spear_rtc_suspend NULL +#define spear_rtc_resume NULL +#endif + +static void spear_rtc_shutdown(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + + spear_rtc_disable_interrupt(config); + clk_disable(config->clk); +} + +static struct platform_driver spear_rtc_driver = { + .probe = spear_rtc_probe, + .remove = __devexit_p(spear_rtc_remove), + .suspend = spear_rtc_suspend, + .resume = spear_rtc_resume, + .shutdown = spear_rtc_shutdown, + .driver = { + .name = "rtc-spear", + }, +}; + +static int __init rtc_init(void) +{ + return platform_driver_register(&spear_rtc_driver); +} +module_init(rtc_init); + +static void __exit rtc_exit(void) +{ + platform_driver_unregister(&spear_rtc_driver); +} +module_exit(rtc_exit); + +MODULE_ALIAS("platform:rtc-spear"); +MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); +MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c new file mode 100644 index 000000000000..b8bc862903ae --- /dev/null +++ b/drivers/rtc/rtc-vt8500.c @@ -0,0 +1,366 @@ +/* + * drivers/rtc/rtc-vt8500.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * Based on rtc-pxa.c + * + * 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 <linux/module.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/bcd.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* + * Register definitions + */ +#define VT8500_RTC_TS 0x00 /* Time set */ +#define VT8500_RTC_DS 0x04 /* Date set */ +#define VT8500_RTC_AS 0x08 /* Alarm set */ +#define VT8500_RTC_CR 0x0c /* Control */ +#define VT8500_RTC_TR 0x10 /* Time read */ +#define VT8500_RTC_DR 0x14 /* Date read */ +#define VT8500_RTC_WS 0x18 /* Write status */ +#define VT8500_RTC_CL 0x20 /* Calibration */ +#define VT8500_RTC_IS 0x24 /* Interrupt status */ +#define VT8500_RTC_ST 0x28 /* Status */ + +#define INVALID_TIME_BIT (1 << 31) + +#define DATE_CENTURY_S 19 +#define DATE_YEAR_S 11 +#define DATE_YEAR_MASK (0xff << DATE_YEAR_S) +#define DATE_MONTH_S 6 +#define DATE_MONTH_MASK (0x1f << DATE_MONTH_S) +#define DATE_DAY_MASK 0x3f + +#define TIME_DOW_S 20 +#define TIME_DOW_MASK (0x07 << TIME_DOW_S) +#define TIME_HOUR_S 14 +#define TIME_HOUR_MASK (0x3f << TIME_HOUR_S) +#define TIME_MIN_S 7 +#define TIME_MIN_MASK (0x7f << TIME_MIN_S) +#define TIME_SEC_MASK 0x7f + +#define ALARM_DAY_S 20 +#define ALARM_DAY_MASK (0x3f << ALARM_DAY_S) + +#define ALARM_DAY_BIT (1 << 29) +#define ALARM_HOUR_BIT (1 << 28) +#define ALARM_MIN_BIT (1 << 27) +#define ALARM_SEC_BIT (1 << 26) + +#define ALARM_ENABLE_MASK (ALARM_DAY_BIT \ + | ALARM_HOUR_BIT \ + | ALARM_MIN_BIT \ + | ALARM_SEC_BIT) + +#define VT8500_RTC_CR_ENABLE (1 << 0) /* Enable RTC */ +#define VT8500_RTC_CR_24H (1 << 1) /* 24h time format */ +#define VT8500_RTC_CR_SM_ENABLE (1 << 2) /* Enable periodic irqs */ +#define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */ +#define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */ + +struct vt8500_rtc { + void __iomem *regbase; + struct resource *res; + int irq_alarm; + int irq_hz; + struct rtc_device *rtc; + spinlock_t lock; /* Protects this structure */ +}; + +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id) +{ + struct vt8500_rtc *vt8500_rtc = dev_id; + u32 isr; + unsigned long events = 0; + + spin_lock(&vt8500_rtc->lock); + + /* clear interrupt sources */ + isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS); + writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS); + + spin_unlock(&vt8500_rtc->lock); + + if (isr & 1) + events |= RTC_AF | RTC_IRQF; + + /* Only second/minute interrupts are supported */ + if (isr & 2) + events |= RTC_UF | RTC_IRQF; + + rtc_update_irq(vt8500_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + u32 date, time; + + date = readl(vt8500_rtc->regbase + VT8500_RTC_DR); + time = readl(vt8500_rtc->regbase + VT8500_RTC_TR); + + tm->tm_sec = bcd2bin(time & TIME_SEC_MASK); + tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S); + tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S); + tm->tm_mday = bcd2bin(date & DATE_DAY_MASK); + tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S); + tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S) + + ((date >> DATE_CENTURY_S) & 1 ? 200 : 100); + tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S; + + return 0; +} + +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + + if (tm->tm_year < 100) { + dev_warn(dev, "Only years 2000-2199 are supported by the " + "hardware!\n"); + return -EINVAL; + } + + writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S) + | (bin2bcd(tm->tm_mon) << DATE_MONTH_S) + | (bin2bcd(tm->tm_mday)), + vt8500_rtc->regbase + VT8500_RTC_DS); + writel((bin2bcd(tm->tm_wday) << TIME_DOW_S) + | (bin2bcd(tm->tm_hour) << TIME_HOUR_S) + | (bin2bcd(tm->tm_min) << TIME_MIN_S) + | (bin2bcd(tm->tm_sec)), + vt8500_rtc->regbase + VT8500_RTC_TS); + + return 0; +} + +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + u32 isr, alarm; + + alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS); + isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS); + + alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S); + alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S); + alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S); + alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK)); + + alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0; + + alrm->pending = (isr & 1) ? 1 : 0; + return rtc_valid_tm(&alrm->time); +} + +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + + writel((alrm->enabled ? ALARM_ENABLE_MASK : 0) + | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S) + | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S) + | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S) + | (bin2bcd(alrm->time.tm_sec)), + vt8500_rtc->regbase + VT8500_RTC_AS); + + return 0; +} + +static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS); + + if (enabled) + tmp |= ALARM_ENABLE_MASK; + else + tmp &= ~ALARM_ENABLE_MASK; + + writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS); + return 0; +} + +static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR); + + if (enabled) + tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE; + else + tmp &= ~VT8500_RTC_CR_SM_ENABLE; + + writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR); + return 0; +} + +static const struct rtc_class_ops vt8500_rtc_ops = { + .read_time = vt8500_rtc_read_time, + .set_time = vt8500_rtc_set_time, + .read_alarm = vt8500_rtc_read_alarm, + .set_alarm = vt8500_rtc_set_alarm, + .alarm_irq_enable = vt8500_alarm_irq_enable, + .update_irq_enable = vt8500_update_irq_enable, +}; + +static int __devinit vt8500_rtc_probe(struct platform_device *pdev) +{ + struct vt8500_rtc *vt8500_rtc; + int ret; + + vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL); + if (!vt8500_rtc) + return -ENOMEM; + + spin_lock_init(&vt8500_rtc->lock); + platform_set_drvdata(pdev, vt8500_rtc); + + vt8500_rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!vt8500_rtc->res) { + dev_err(&pdev->dev, "No I/O memory resource defined\n"); + ret = -ENXIO; + goto err_free; + } + + vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0); + if (vt8500_rtc->irq_alarm < 0) { + dev_err(&pdev->dev, "No alarm IRQ resource defined\n"); + ret = -ENXIO; + goto err_free; + } + + vt8500_rtc->irq_hz = platform_get_irq(pdev, 1); + if (vt8500_rtc->irq_hz < 0) { + dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n"); + ret = -ENXIO; + goto err_free; + } + + vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start, + resource_size(vt8500_rtc->res), + "vt8500-rtc"); + if (vt8500_rtc->res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto err_free; + } + + vt8500_rtc->regbase = ioremap(vt8500_rtc->res->start, + resource_size(vt8500_rtc->res)); + if (!vt8500_rtc->regbase) { + dev_err(&pdev->dev, "Unable to map RTC I/O memory\n"); + ret = -EBUSY; + goto err_release; + } + + /* Enable the second/minute interrupt generation and enable RTC */ + writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H + | VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC, + vt8500_rtc->regbase + VT8500_RTC_CR); + + vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev, + &vt8500_rtc_ops, THIS_MODULE); + if (IS_ERR(vt8500_rtc->rtc)) { + ret = PTR_ERR(vt8500_rtc->rtc); + dev_err(&pdev->dev, + "Failed to register RTC device -> %d\n", ret); + goto err_unmap; + } + + ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0, + "rtc 1Hz", vt8500_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", + vt8500_rtc->irq_hz, ret); + goto err_unreg; + } + + ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0, + "rtc alarm", vt8500_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", + vt8500_rtc->irq_alarm, ret); + goto err_free_hz; + } + + return 0; + +err_free_hz: + free_irq(vt8500_rtc->irq_hz, vt8500_rtc); +err_unreg: + rtc_device_unregister(vt8500_rtc->rtc); +err_unmap: + iounmap(vt8500_rtc->regbase); +err_release: + release_mem_region(vt8500_rtc->res->start, + resource_size(vt8500_rtc->res)); +err_free: + kfree(vt8500_rtc); + return ret; +} + +static int __devexit vt8500_rtc_remove(struct platform_device *pdev) +{ + struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev); + + free_irq(vt8500_rtc->irq_alarm, vt8500_rtc); + free_irq(vt8500_rtc->irq_hz, vt8500_rtc); + + rtc_device_unregister(vt8500_rtc->rtc); + + /* Disable alarm matching */ + writel(0, vt8500_rtc->regbase + VT8500_RTC_IS); + iounmap(vt8500_rtc->regbase); + release_mem_region(vt8500_rtc->res->start, + resource_size(vt8500_rtc->res)); + + kfree(vt8500_rtc); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver vt8500_rtc_driver = { + .probe = vt8500_rtc_probe, + .remove = __devexit_p(vt8500_rtc_remove), + .driver = { + .name = "vt8500-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init vt8500_rtc_init(void) +{ + return platform_driver_register(&vt8500_rtc_driver); +} +module_init(vt8500_rtc_init); + +static void __exit vt8500_rtc_exit(void) +{ + platform_driver_unregister(&vt8500_rtc_driver); +} +module_exit(vt8500_rtc_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vt8500-rtc"); diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 85dddb1e4126..46784b83c5c4 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -24,7 +24,7 @@ #include <asm/debug.h> #include <asm/ebcdic.h> #include <asm/io.h> -#include <asm/s390_ext.h> +#include <asm/irq.h> #include <asm/vtoc.h> #include <asm/diag.h> @@ -642,7 +642,7 @@ dasd_diag_init(void) } ASCEBC(dasd_diag_discipline.ebcname, 4); - ctl_set_bit(0, 9); + service_subclass_irq_register(); register_external_interrupt(0x2603, dasd_ext_handler); dasd_diag_discipline_pointer = &dasd_diag_discipline; return 0; @@ -652,7 +652,7 @@ static void __exit dasd_diag_cleanup(void) { unregister_external_interrupt(0x2603, dasd_ext_handler); - ctl_clear_bit(0, 9); + service_subclass_irq_unregister(); dasd_diag_discipline_pointer = NULL; } diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index b76c61f82485..eaa7e78186f9 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -19,7 +19,6 @@ #include <linux/suspend.h> #include <linux/completion.h> #include <linux/platform_device.h> -#include <asm/s390_ext.h> #include <asm/types.h> #include <asm/irq.h> @@ -885,12 +884,12 @@ sclp_check_interface(void) spin_unlock_irqrestore(&sclp_lock, flags); /* Enable service-signal interruption - needs to happen * with IRQs enabled. */ - ctl_set_bit(0, 9); + service_subclass_irq_register(); /* Wait for signal from interrupt or timeout */ sclp_sync_wait(); /* Disable service-signal interruption - needs to happen * with IRQs enabled. */ - ctl_clear_bit(0,9); + service_subclass_irq_unregister(); spin_lock_irqsave(&sclp_lock, flags); del_timer(&sclp_request_timer); if (sclp_init_req.status == SCLP_REQ_DONE && @@ -1070,7 +1069,7 @@ sclp_init(void) spin_unlock_irqrestore(&sclp_lock, flags); /* Enable service-signal external interruption - needs to happen with * IRQs enabled. */ - ctl_set_bit(0, 9); + service_subclass_irq_register(); sclp_init_mask(1); return 0; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 607998f0b7d8..aec60d55b10d 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -25,7 +25,6 @@ #include <asm/kvm_para.h> #include <asm/kvm_virtio.h> #include <asm/setup.h> -#include <asm/s390_ext.h> #include <asm/irq.h> #define VIRTIO_SUBCODE_64 0x0D00 @@ -441,7 +440,7 @@ static int __init kvm_devices_init(void) INIT_WORK(&hotplug_work, hotplug_devices); - ctl_set_bit(0, 9); + service_subclass_irq_register(); register_external_interrupt(0x2603, kvm_extint_handler); scan_devices(); diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 4ff26521d75f..3382475dc22d 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -59,7 +59,6 @@ #ifndef AAC_DRIVER_BRANCH #define AAC_DRIVER_BRANCH "" #endif -#define AAC_DRIVER_BUILD_DATE __DATE__ " " __TIME__ #define AAC_DRIVERNAME "aacraid" #ifdef AAC_DRIVER_BUILD @@ -67,7 +66,7 @@ #define str(x) _str(x) #define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION "[" str(AAC_DRIVER_BUILD) "]" AAC_DRIVER_BRANCH #else -#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION AAC_DRIVER_BRANCH " " AAC_DRIVER_BUILD_DATE +#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION AAC_DRIVER_BRANCH #endif MODULE_AUTHOR("Red Hat Inc and Adaptec"); diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 3b7e83d2dab4..d5ff142c93a2 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -486,7 +486,7 @@ static ssize_t asd_show_update_bios(struct device *dev, flash_error_table[i].reason); } -static DEVICE_ATTR(update_bios, S_IRUGO|S_IWUGO, +static DEVICE_ATTR(update_bios, S_IRUGO|S_IWUSR, asd_show_update_bios, asd_store_update_bios); static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha) diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index c1f72c49196f..6c7e0339dda4 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -56,6 +56,8 @@ BFA_TRC_FILE(CNA, IOC); #define bfa_ioc_map_port(__ioc) ((__ioc)->ioc_hwif->ioc_map_port(__ioc)) #define bfa_ioc_notify_fail(__ioc) \ ((__ioc)->ioc_hwif->ioc_notify_fail(__ioc)) +#define bfa_ioc_sync_start(__ioc) \ + ((__ioc)->ioc_hwif->ioc_sync_start(__ioc)) #define bfa_ioc_sync_join(__ioc) \ ((__ioc)->ioc_hwif->ioc_sync_join(__ioc)) #define bfa_ioc_sync_leave(__ioc) \ @@ -647,7 +649,7 @@ bfa_iocpf_sm_fwcheck(struct bfa_iocpf_s *iocpf, enum iocpf_event event) switch (event) { case IOCPF_E_SEMLOCKED: if (bfa_ioc_firmware_lock(ioc)) { - if (bfa_ioc_sync_complete(ioc)) { + if (bfa_ioc_sync_start(ioc)) { iocpf->retry_count = 0; bfa_ioc_sync_join(ioc); bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit); diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h index ec9cf08b0e7f..c85182a704fb 100644 --- a/drivers/scsi/bfa/bfa_ioc.h +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -263,6 +263,7 @@ struct bfa_ioc_hwif_s { bfa_boolean_t msix); void (*ioc_notify_fail) (struct bfa_ioc_s *ioc); void (*ioc_ownership_reset) (struct bfa_ioc_s *ioc); + bfa_boolean_t (*ioc_sync_start) (struct bfa_ioc_s *ioc); void (*ioc_sync_join) (struct bfa_ioc_s *ioc); void (*ioc_sync_leave) (struct bfa_ioc_s *ioc); void (*ioc_sync_ack) (struct bfa_ioc_s *ioc); diff --git a/drivers/scsi/bfa/bfa_ioc_cb.c b/drivers/scsi/bfa/bfa_ioc_cb.c index e4a0713185b6..89ae4c8f95a2 100644 --- a/drivers/scsi/bfa/bfa_ioc_cb.c +++ b/drivers/scsi/bfa/bfa_ioc_cb.c @@ -32,6 +32,7 @@ static void bfa_ioc_cb_map_port(struct bfa_ioc_s *ioc); static void bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix); static void bfa_ioc_cb_notify_fail(struct bfa_ioc_s *ioc); static void bfa_ioc_cb_ownership_reset(struct bfa_ioc_s *ioc); +static bfa_boolean_t bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc); static void bfa_ioc_cb_sync_join(struct bfa_ioc_s *ioc); static void bfa_ioc_cb_sync_leave(struct bfa_ioc_s *ioc); static void bfa_ioc_cb_sync_ack(struct bfa_ioc_s *ioc); @@ -53,6 +54,7 @@ bfa_ioc_set_cb_hwif(struct bfa_ioc_s *ioc) hwif_cb.ioc_isr_mode_set = bfa_ioc_cb_isr_mode_set; hwif_cb.ioc_notify_fail = bfa_ioc_cb_notify_fail; hwif_cb.ioc_ownership_reset = bfa_ioc_cb_ownership_reset; + hwif_cb.ioc_sync_start = bfa_ioc_cb_sync_start; hwif_cb.ioc_sync_join = bfa_ioc_cb_sync_join; hwif_cb.ioc_sync_leave = bfa_ioc_cb_sync_leave; hwif_cb.ioc_sync_ack = bfa_ioc_cb_sync_ack; @@ -195,6 +197,15 @@ bfa_ioc_cb_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix) } /* + * Synchronized IOC failure processing routines + */ +static bfa_boolean_t +bfa_ioc_cb_sync_start(struct bfa_ioc_s *ioc) +{ + return bfa_ioc_cb_sync_complete(ioc); +} + +/* * Cleanup hw semaphore and usecnt registers */ static void diff --git a/drivers/scsi/bfa/bfa_ioc_ct.c b/drivers/scsi/bfa/bfa_ioc_ct.c index 008d129ddfcd..93612520f0d2 100644 --- a/drivers/scsi/bfa/bfa_ioc_ct.c +++ b/drivers/scsi/bfa/bfa_ioc_ct.c @@ -41,6 +41,7 @@ static void bfa_ioc_ct_map_port(struct bfa_ioc_s *ioc); static void bfa_ioc_ct_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix); static void bfa_ioc_ct_notify_fail(struct bfa_ioc_s *ioc); static void bfa_ioc_ct_ownership_reset(struct bfa_ioc_s *ioc); +static bfa_boolean_t bfa_ioc_ct_sync_start(struct bfa_ioc_s *ioc); static void bfa_ioc_ct_sync_join(struct bfa_ioc_s *ioc); static void bfa_ioc_ct_sync_leave(struct bfa_ioc_s *ioc); static void bfa_ioc_ct_sync_ack(struct bfa_ioc_s *ioc); @@ -62,6 +63,7 @@ bfa_ioc_set_ct_hwif(struct bfa_ioc_s *ioc) hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set; hwif_ct.ioc_notify_fail = bfa_ioc_ct_notify_fail; hwif_ct.ioc_ownership_reset = bfa_ioc_ct_ownership_reset; + hwif_ct.ioc_sync_start = bfa_ioc_ct_sync_start; hwif_ct.ioc_sync_join = bfa_ioc_ct_sync_join; hwif_ct.ioc_sync_leave = bfa_ioc_ct_sync_leave; hwif_ct.ioc_sync_ack = bfa_ioc_ct_sync_ack; @@ -351,6 +353,30 @@ bfa_ioc_ct_ownership_reset(struct bfa_ioc_s *ioc) writel(1, ioc->ioc_regs.ioc_sem_reg); } +static bfa_boolean_t +bfa_ioc_ct_sync_start(struct bfa_ioc_s *ioc) +{ + uint32_t r32 = readl(ioc->ioc_regs.ioc_fail_sync); + uint32_t sync_reqd = bfa_ioc_ct_get_sync_reqd(r32); + + /* + * Driver load time. If the sync required bit for this PCI fn + * is set, it is due to an unclean exit by the driver for this + * PCI fn in the previous incarnation. Whoever comes here first + * should clean it up, no matter which PCI fn. + */ + + if (sync_reqd & bfa_ioc_ct_sync_pos(ioc)) { + writel(0, ioc->ioc_regs.ioc_fail_sync); + writel(1, ioc->ioc_regs.ioc_usage_reg); + writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate); + writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate); + return BFA_TRUE; + } + + return bfa_ioc_ct_sync_complete(ioc); +} + /* * Synchronized IOC failure processing routines */ diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index cfd59023227b..6bdd25a93db9 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -66,11 +66,11 @@ #define BD_SPLIT_SIZE 32768 /* min, max & default values for SQ/RQ/CQ size, configurable via' modparam */ -#define BNX2I_SQ_WQES_MIN 16 -#define BNX2I_570X_SQ_WQES_MAX 128 -#define BNX2I_5770X_SQ_WQES_MAX 512 -#define BNX2I_570X_SQ_WQES_DEFAULT 128 -#define BNX2I_5770X_SQ_WQES_DEFAULT 256 +#define BNX2I_SQ_WQES_MIN 16 +#define BNX2I_570X_SQ_WQES_MAX 128 +#define BNX2I_5770X_SQ_WQES_MAX 512 +#define BNX2I_570X_SQ_WQES_DEFAULT 128 +#define BNX2I_5770X_SQ_WQES_DEFAULT 128 #define BNX2I_570X_CQ_WQES_MAX 128 #define BNX2I_5770X_CQ_WQES_MAX 512 @@ -115,6 +115,7 @@ #define BNX2X_MAX_CQS 8 #define CNIC_ARM_CQE 1 +#define CNIC_ARM_CQE_FP 2 #define CNIC_DISARM_CQE 0 #define REG_RD(__hba, offset) \ @@ -666,7 +667,9 @@ enum { * after HBA reset is completed by bnx2i/cnic/bnx2 * modules * @state: tracks offload connection state machine - * @teardown_mode: indicates if conn teardown is abortive or orderly + * @timestamp: tracks the start time when the ep begins to connect + * @num_active_cmds: tracks the number of outstanding commands for this ep + * @ec_shift: the amount of shift as part of the event coal calc * @qp: QP information * @ids: contains chip allocated *context id* & driver assigned * *iscsi cid* @@ -685,6 +688,7 @@ struct bnx2i_endpoint { u32 state; unsigned long timestamp; int num_active_cmds; + u32 ec_shift; struct qp_info qp; struct ep_handles ids; diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index f0b89513faed..5c54a2d9b834 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -138,7 +138,6 @@ void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action) u16 next_index; u32 num_active_cmds; - /* Coalesce CQ entries only on 10G devices */ if (!test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) return; @@ -148,16 +147,19 @@ void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action) * interrupts and other unwanted results */ cq_db = (struct bnx2i_5771x_cq_db *) ep->qp.cq_pgtbl_virt; - if (cq_db->sqn[0] && cq_db->sqn[0] != 0xFFFF) - return; - if (action == CNIC_ARM_CQE) { + if (action != CNIC_ARM_CQE_FP) + if (cq_db->sqn[0] && cq_db->sqn[0] != 0xFFFF) + return; + + if (action == CNIC_ARM_CQE || action == CNIC_ARM_CQE_FP) { num_active_cmds = ep->num_active_cmds; if (num_active_cmds <= event_coal_min) next_index = 1; else next_index = event_coal_min + - (num_active_cmds - event_coal_min) / event_coal_div; + ((num_active_cmds - event_coal_min) >> + ep->ec_shift); if (!next_index) next_index = 1; cq_index = ep->qp.cqe_exp_seq_sn + next_index - 1; @@ -1274,6 +1276,7 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) iscsi_init.dummy_buffer_addr_hi = (u32) ((u64) hba->dummy_buf_dma >> 32); + hba->num_ccell = hba->max_sqes >> 1; hba->ctx_ccell_tasks = ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16)); iscsi_init.num_ccells_per_conn = hba->num_ccell; @@ -1934,7 +1937,6 @@ cqe_out: qp->cq_cons_idx++; } } - bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE); } /** @@ -1948,22 +1950,23 @@ cqe_out: static void bnx2i_fastpath_notification(struct bnx2i_hba *hba, struct iscsi_kcqe *new_cqe_kcqe) { - struct bnx2i_conn *conn; + struct bnx2i_conn *bnx2i_conn; u32 iscsi_cid; iscsi_cid = new_cqe_kcqe->iscsi_conn_id; - conn = bnx2i_get_conn_from_id(hba, iscsi_cid); + bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid); - if (!conn) { + if (!bnx2i_conn) { printk(KERN_ALERT "cid #%x not valid\n", iscsi_cid); return; } - if (!conn->ep) { + if (!bnx2i_conn->ep) { printk(KERN_ALERT "cid #%x - ep not bound\n", iscsi_cid); return; } - - bnx2i_process_new_cqes(conn); + bnx2i_process_new_cqes(bnx2i_conn); + bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE_FP); + bnx2i_process_new_cqes(bnx2i_conn); } diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 1d24a2819736..6adbdc34a9a5 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -244,7 +244,7 @@ void bnx2i_stop(void *handle) wait_event_interruptible_timeout(hba->eh_wait, (list_empty(&hba->ep_ofld_list) && list_empty(&hba->ep_destroy_list)), - 10 * HZ); + 2 * HZ); /* Wait for all endpoints to be torn down, Chip will be reset once * control returns to network driver. So it is required to cleanup and * release all connection resources before returning from this routine. diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 1809f9ccc4ce..041928b23cb0 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -379,6 +379,7 @@ static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba) { struct iscsi_endpoint *ep; struct bnx2i_endpoint *bnx2i_ep; + u32 ec_div; ep = iscsi_create_endpoint(sizeof(*bnx2i_ep)); if (!ep) { @@ -393,6 +394,11 @@ static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba) bnx2i_ep->ep_iscsi_cid = (u16) -1; bnx2i_ep->hba = hba; bnx2i_ep->hba_age = hba->age; + + ec_div = event_coal_div; + while (ec_div >>= 1) + bnx2i_ep->ec_shift += 1; + hba->ofld_conns_active++; init_waitqueue_head(&bnx2i_ep->ofld_wait); return ep; @@ -858,7 +864,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) mutex_init(&hba->net_dev_lock); init_waitqueue_head(&hba->eh_wait); if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { - hba->hba_shutdown_tmo = 20 * HZ; + hba->hba_shutdown_tmo = 30 * HZ; hba->conn_teardown_tmo = 20 * HZ; hba->conn_ctx_destroy_tmo = 6 * HZ; } else { /* 5706/5708/5709 */ @@ -1208,6 +1214,9 @@ static int bnx2i_task_xmit(struct iscsi_task *task) struct bnx2i_cmd *cmd = task->dd_data; struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr; + if (bnx2i_conn->ep->num_active_cmds + 1 > hba->max_sqes) + return -ENOMEM; + /* * If there is no scsi_cmnd this must be a mgmt task */ @@ -2156,7 +2165,7 @@ static struct scsi_host_template bnx2i_host_template = { .change_queue_depth = iscsi_change_queue_depth, .can_queue = 1024, .max_sectors = 127, - .cmd_per_lun = 32, + .cmd_per_lun = 24, .this_id = -1, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = ISCSI_MAX_BDS_PER_CMD, diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index cc23bd9480b2..155d7b9bdeae 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -137,6 +137,7 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled); static int fcoe_vport_disable(struct fc_vport *, bool disable); static void fcoe_set_vport_symbolic_name(struct fc_vport *); static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); +static int fcoe_validate_vport_create(struct fc_vport *); static struct libfc_function_template fcoe_libfc_fcn_templ = { .frame_send = fcoe_xmit, @@ -2351,6 +2352,17 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled) struct fcoe_interface *fcoe = port->priv; struct net_device *netdev = fcoe->netdev; struct fc_lport *vn_port; + int rc; + char buf[32]; + + rc = fcoe_validate_vport_create(vport); + if (rc) { + wwn_to_str(vport->port_name, buf, sizeof(buf)); + printk(KERN_ERR "fcoe: Failed to create vport, " + "WWPN (0x%s) already exists\n", + buf); + return rc; + } mutex_lock(&fcoe_config_mutex); vn_port = fcoe_if_create(fcoe, &vport->dev, 1); @@ -2497,3 +2509,49 @@ static void fcoe_set_port_id(struct fc_lport *lport, if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); } + +/** + * fcoe_validate_vport_create() - Validate a vport before creating it + * @vport: NPIV port to be created + * + * This routine is meant to add validation for a vport before creating it + * via fcoe_vport_create(). + * Current validations are: + * - WWPN supplied is unique for given lport + * + * +*/ +static int fcoe_validate_vport_create(struct fc_vport *vport) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port; + int rc = 0; + char buf[32]; + + mutex_lock(&n_port->lp_mutex); + + wwn_to_str(vport->port_name, buf, sizeof(buf)); + /* Check if the wwpn is not same as that of the lport */ + if (!memcmp(&n_port->wwpn, &vport->port_name, sizeof(u64))) { + FCOE_DBG("vport WWPN 0x%s is same as that of the " + "base port WWPN\n", buf); + rc = -EINVAL; + goto out; + } + + /* Check if there is any existing vport with same wwpn */ + list_for_each_entry(vn_port, &n_port->vports, list) { + if (!memcmp(&vn_port->wwpn, &vport->port_name, sizeof(u64))) { + FCOE_DBG("vport with given WWPN 0x%s already " + "exists\n", buf); + rc = -EINVAL; + break; + } + } + +out: + mutex_unlock(&n_port->lp_mutex); + + return rc; +} diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 408a6fd78fb4..c4a93993c0cf 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -99,4 +99,14 @@ static inline struct net_device *fcoe_netdev(const struct fc_lport *lport) ((struct fcoe_port *)lport_priv(lport))->priv)->netdev; } +static inline void wwn_to_str(u64 wwn, char *buf, int len) +{ + u8 wwpn[8]; + + u64_to_wwn(wwn, wwpn); + snprintf(buf, len, "%02x%02x%02x%02x%02x%02x%02x%02x", + wwpn[0], wwpn[1], wwpn[2], wwpn[3], + wwpn[4], wwpn[5], wwpn[6], wwpn[7]); +} + #endif /* _FCOE_H_ */ diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 229e4af5508a..c74c4b8e71ef 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1173,7 +1173,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, struct fc_lport *lport = fip->lp; struct fc_lport *vn_port = NULL; u32 desc_mask; - int is_vn_port = 0; + int num_vlink_desc; + int reset_phys_port = 0; + struct fip_vn_desc **vlink_desc_arr = NULL; LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); @@ -1183,70 +1185,73 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, /* * mask of required descriptors. Validating each one clears its bit. */ - desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | BIT(FIP_DT_VN_ID); + desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME); rlen = ntohs(fh->fip_dl_len) * FIP_BPW; desc = (struct fip_desc *)(fh + 1); + + /* + * Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen' + * before determining max Vx_Port descriptor but a buggy FCF could have + * omited either or both MAC Address and Name Identifier descriptors + */ + num_vlink_desc = rlen / sizeof(*vp); + if (num_vlink_desc) + vlink_desc_arr = kmalloc(sizeof(vp) * num_vlink_desc, + GFP_ATOMIC); + if (!vlink_desc_arr) + return; + num_vlink_desc = 0; + while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; if (dlen > rlen) - return; + goto err; /* Drop CVL if there are duplicate critical descriptors */ if ((desc->fip_dtype < 32) && + (desc->fip_dtype != FIP_DT_VN_ID) && !(desc_mask & 1U << desc->fip_dtype)) { LIBFCOE_FIP_DBG(fip, "Duplicate Critical " "Descriptors in FIP CVL\n"); - return; + goto err; } switch (desc->fip_dtype) { case FIP_DT_MAC: mp = (struct fip_mac_desc *)desc; if (dlen < sizeof(*mp)) - return; + goto err; if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac)) - return; + goto err; desc_mask &= ~BIT(FIP_DT_MAC); break; case FIP_DT_NAME: wp = (struct fip_wwn_desc *)desc; if (dlen < sizeof(*wp)) - return; + goto err; if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name) - return; + goto err; desc_mask &= ~BIT(FIP_DT_NAME); break; case FIP_DT_VN_ID: vp = (struct fip_vn_desc *)desc; if (dlen < sizeof(*vp)) - return; - if (compare_ether_addr(vp->fd_mac, - fip->get_src_addr(lport)) == 0 && - get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && - ntoh24(vp->fd_fc_id) == lport->port_id) { - desc_mask &= ~BIT(FIP_DT_VN_ID); - break; + goto err; + vlink_desc_arr[num_vlink_desc++] = vp; + vn_port = fc_vport_id_lookup(lport, + ntoh24(vp->fd_fc_id)); + if (vn_port && (vn_port == lport)) { + mutex_lock(&fip->ctlr_mutex); + per_cpu_ptr(lport->dev_stats, + get_cpu())->VLinkFailureCount++; + put_cpu(); + fcoe_ctlr_reset(fip); + mutex_unlock(&fip->ctlr_mutex); } - /* check if clr_vlink is for NPIV port */ - mutex_lock(&lport->lp_mutex); - list_for_each_entry(vn_port, &lport->vports, list) { - if (compare_ether_addr(vp->fd_mac, - fip->get_src_addr(vn_port)) == 0 && - (get_unaligned_be64(&vp->fd_wwpn) - == vn_port->wwpn) && - (ntoh24(vp->fd_fc_id) == - fc_host_port_id(vn_port->host))) { - desc_mask &= ~BIT(FIP_DT_VN_ID); - is_vn_port = 1; - break; - } - } - mutex_unlock(&lport->lp_mutex); - break; default: /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) - return; + goto err; break; } desc = (struct fip_desc *)((char *)desc + dlen); @@ -1256,26 +1261,68 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, /* * reset only if all required descriptors were present and valid. */ - if (desc_mask) { + if (desc_mask) LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", desc_mask); + else if (!num_vlink_desc) { + LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n"); + /* + * No Vx_Port description. Clear all NPIV ports, + * followed by physical port + */ + mutex_lock(&lport->lp_mutex); + list_for_each_entry(vn_port, &lport->vports, list) + fc_lport_reset(vn_port); + mutex_unlock(&lport->lp_mutex); + + mutex_lock(&fip->ctlr_mutex); + per_cpu_ptr(lport->dev_stats, + get_cpu())->VLinkFailureCount++; + put_cpu(); + fcoe_ctlr_reset(fip); + mutex_unlock(&fip->ctlr_mutex); + + fc_lport_reset(fip->lp); + fcoe_ctlr_solicit(fip, NULL); } else { - LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); + int i; - if (is_vn_port) - fc_lport_reset(vn_port); - else { - mutex_lock(&fip->ctlr_mutex); - per_cpu_ptr(lport->dev_stats, - get_cpu())->VLinkFailureCount++; - put_cpu(); - fcoe_ctlr_reset(fip); - mutex_unlock(&fip->ctlr_mutex); + LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); + for (i = 0; i < num_vlink_desc; i++) { + vp = vlink_desc_arr[i]; + vn_port = fc_vport_id_lookup(lport, + ntoh24(vp->fd_fc_id)); + if (!vn_port) + continue; + + /* + * 'port_id' is already validated, check MAC address and + * wwpn + */ + if (compare_ether_addr(fip->get_src_addr(vn_port), + vp->fd_mac) != 0 || + get_unaligned_be64(&vp->fd_wwpn) != + vn_port->wwpn) + continue; + + if (vn_port == lport) + /* + * Physical port, defer processing till all + * listed NPIV ports are cleared + */ + reset_phys_port = 1; + else /* NPIV port */ + fc_lport_reset(vn_port); + } + if (reset_phys_port) { fc_lport_reset(fip->lp); fcoe_ctlr_solicit(fip, NULL); } } + +err: + kfree(vlink_desc_arr); } /** diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index f81f77c8569e..41068e8748e7 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -544,16 +544,6 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) struct fcoe_transport *ft = NULL; enum fip_state fip_mode = (enum fip_state)(long)kp->arg; -#ifdef CONFIG_LIBFCOE_MODULE - /* - * Make sure the module has been initialized, and is not about to be - * removed. Module parameter sysfs files are writable before the - * module_init function is called and after module_exit. - */ - if (THIS_MODULE->state != MODULE_STATE_LIVE) - goto out_nodev; -#endif - mutex_lock(&ft_mutex); netdev = fcoe_if_to_netdev(buffer); @@ -618,16 +608,6 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) struct net_device *netdev = NULL; struct fcoe_transport *ft = NULL; -#ifdef CONFIG_LIBFCOE_MODULE - /* - * Make sure the module has been initialized, and is not about to be - * removed. Module parameter sysfs files are writable before the - * module_init function is called and after module_exit. - */ - if (THIS_MODULE->state != MODULE_STATE_LIVE) - goto out_nodev; -#endif - mutex_lock(&ft_mutex); netdev = fcoe_if_to_netdev(buffer); @@ -672,16 +652,6 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) struct net_device *netdev = NULL; struct fcoe_transport *ft = NULL; -#ifdef CONFIG_LIBFCOE_MODULE - /* - * Make sure the module has been initialized, and is not about to be - * removed. Module parameter sysfs files are writable before the - * module_init function is called and after module_exit. - */ - if (THIS_MODULE->state != MODULE_STATE_LIVE) - goto out_nodev; -#endif - mutex_lock(&ft_mutex); netdev = fcoe_if_to_netdev(buffer); @@ -720,16 +690,6 @@ static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) struct net_device *netdev = NULL; struct fcoe_transport *ft = NULL; -#ifdef CONFIG_LIBFCOE_MODULE - /* - * Make sure the module has been initialized, and is not about to be - * removed. Module parameter sysfs files are writable before the - * module_init function is called and after module_exit. - */ - if (THIS_MODULE->state != MODULE_STATE_LIVE) - goto out_nodev; -#endif - mutex_lock(&ft_mutex); netdev = fcoe_if_to_netdev(buffer); diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c index 92109b126391..112f1bec7756 100644 --- a/drivers/scsi/in2000.c +++ b/drivers/scsi/in2000.c @@ -2227,7 +2227,7 @@ static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start, bp = buf; *bp = '\0'; if (hd->proc & PR_VERSION) { - sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s", IN2000_VERSION, IN2000_DATE, __DATE__, __TIME__); + sprintf(tbuf, "\nVersion %s - %s.", IN2000_VERSION, IN2000_DATE); strcat(bp, tbuf); } if (hd->proc & PR_INFO) { diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 12868ca46110..888086c4e709 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -5149,21 +5149,21 @@ static irqreturn_t ipr_isr(int irq, void *devp) if (ipr_cmd != NULL) { /* Clear the PCI interrupt */ + num_hrrq = 0; do { writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32); int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32); } while (int_reg & IPR_PCII_HRRQ_UPDATED && num_hrrq++ < IPR_MAX_HRRQ_RETRIES); - if (int_reg & IPR_PCII_HRRQ_UPDATED) { - ipr_isr_eh(ioa_cfg, "Error clearing HRRQ"); - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - return IRQ_HANDLED; - } - } else if (rc == IRQ_NONE && irq_none == 0) { int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32); irq_none++; + } else if (num_hrrq == IPR_MAX_HRRQ_RETRIES && + int_reg & IPR_PCII_HRRQ_UPDATED) { + ipr_isr_eh(ioa_cfg, "Error clearing HRRQ"); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_HANDLED; } else break; } diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 911b2736cafa..b9cb8140b398 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -205,6 +205,7 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp) default: FC_DISC_DBG(disc, "Received an unsupported request, " "the opcode is (%x)\n", op); + fc_frame_free(fp); break; } } diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 77035a746f60..3b8a6451ea28 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1434,6 +1434,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { spin_lock_bh(&ep->ex_lock); + resp = ep->resp; rc = fc_exch_done_locked(ep); WARN_ON(fc_seq_exch(sp) != ep); spin_unlock_bh(&ep->ex_lock); @@ -1978,6 +1979,7 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport, spin_unlock_bh(&ep->ex_lock); return sp; err: + fc_fcp_ddp_done(fr_fsp(fp)); rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 2a3a4720a771..9cd2149519ac 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -312,7 +312,7 @@ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid) * DDP related resources for a fcp_pkt * @fsp: The FCP packet that DDP had been used on */ -static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp) +void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp) { struct fc_lport *lport; @@ -681,8 +681,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, error = lport->tt.seq_send(lport, seq, fp); if (error) { WARN_ON(1); /* send error should be rare */ - fc_fcp_retry_cmd(fsp); - return 0; + return error; } fp = NULL; } @@ -1673,7 +1672,8 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) FC_FCTL_REQ, 0); rec_tov = get_fsp_rec_tov(fsp); - seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL, + seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, + fc_fcp_pkt_destroy, fsp, jiffies_to_msecs(rec_tov)); if (!seq) goto retry; @@ -1720,7 +1720,6 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) return; } - fsp->recov_seq = NULL; switch (fc_frame_payload_op(fp)) { case ELS_LS_ACC: fsp->recov_retry = 0; @@ -1732,10 +1731,9 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) break; } fc_fcp_unlock_pkt(fsp); - fsp->lp->tt.exch_done(seq); out: + fsp->lp->tt.exch_done(seq); fc_frame_free(fp); - fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ } /** @@ -1747,8 +1745,6 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { if (fc_fcp_lock_pkt(fsp)) goto out; - fsp->lp->tt.exch_done(fsp->recov_seq); - fsp->recov_seq = NULL; switch (PTR_ERR(fp)) { case -FC_EX_TIMEOUT: if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) @@ -1764,7 +1760,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) } fc_fcp_unlock_pkt(fsp); out: - fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ + fsp->lp->tt.exch_done(fsp->recov_seq); } /** diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index fedc819d70c0..c7d071289af5 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -108,6 +108,7 @@ extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */ * Set up direct-data placement for this I/O request */ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid); +void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp); /* * Module setup functions diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 31fc21f4d831..db9238f2ecb8 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -99,19 +99,29 @@ static void sas_ata_task_done(struct sas_task *task) struct sas_ha_struct *sas_ha; enum ata_completion_errors ac; unsigned long flags; + struct ata_link *link; if (!qc) goto qc_already_gone; dev = qc->ap->private_data; sas_ha = dev->port->ha; + link = &dev->sata_dev.ap->link; spin_lock_irqsave(dev->sata_dev.ap->lock, flags); if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || ((stat->stat == SAM_STAT_CHECK_CONDITION && dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + + if (!link->sactive) { + qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + } else { + link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.tf.command); + if (unlikely(link->eh_info.err_mask)) + qc->flags |= ATA_QCFLAG_FAILED; + } + dev->sata_dev.sstatus = resp->sstatus; dev->sata_dev.serror = resp->serror; dev->sata_dev.scontrol = resp->scontrol; @@ -121,7 +131,13 @@ static void sas_ata_task_done(struct sas_task *task) SAS_DPRINTK("%s: SAS error %x\n", __func__, stat->stat); /* We saw a SAS error. Send a vague error. */ - qc->err_mask = ac; + if (!link->sactive) { + qc->err_mask = ac; + } else { + link->eh_info.err_mask |= AC_ERR_DEV; + qc->flags |= ATA_QCFLAG_FAILED; + } + dev->sata_dev.tf.feature = 0x04; /* status err */ dev->sata_dev.tf.command = ATA_ERR; } @@ -279,6 +295,44 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, return ret; } +static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct domain_device *dev = ap->private_data; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + int res = TMF_RESP_FUNC_FAILED; + int ret = 0; + + if (i->dft->lldd_ata_soft_reset) + res = i->dft->lldd_ata_soft_reset(dev); + + if (res != TMF_RESP_FUNC_COMPLETE) { + SAS_DPRINTK("%s: Unable to soft reset\n", __func__); + ret = -EAGAIN; + } + + switch (dev->sata_dev.command_set) { + case ATA_COMMAND_SET: + SAS_DPRINTK("%s: Found ATA device.\n", __func__); + *class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); + *class = ATA_DEV_ATAPI; + break; + default: + SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", + __func__, dev->sata_dev.command_set); + *class = ATA_DEV_UNKNOWN; + break; + } + + ap->cbl = ATA_CBL_SATA; + return ret; +} + static void sas_ata_post_internal(struct ata_queued_cmd *qc) { if (qc->flags & ATA_QCFLAG_FAILED) @@ -309,7 +363,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) static struct ata_port_operations sas_sata_ops = { .prereset = ata_std_prereset, - .softreset = NULL, + .softreset = sas_ata_soft_reset, .hardreset = sas_ata_hard_reset, .postreset = ata_std_postreset, .error_handler = ata_std_error_handler, diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 8b538bd1ff2b..14e21b5fb8ba 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -57,7 +57,7 @@ int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); void sas_shutdown_queue(struct sas_ha_struct *sas_ha); -void sas_deform_port(struct asd_sas_phy *phy); +void sas_deform_port(struct asd_sas_phy *phy, int gone); void sas_porte_bytes_dmaed(struct work_struct *work); void sas_porte_broadcast_rcvd(struct work_struct *work); diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index b459c4b635b1..e0f5018e9071 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -39,7 +39,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock, &phy->phy_events_pending); phy->error = 0; - sas_deform_port(phy); + sas_deform_port(phy, 1); } static void sas_phye_oob_done(struct work_struct *work) @@ -66,7 +66,7 @@ static void sas_phye_oob_error(struct work_struct *work) sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock, &phy->phy_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); if (!port && phy->enabled && i->dft->lldd_control_phy) { phy->error++; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 5257fdfe699a..42fd1f25b664 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -57,7 +57,7 @@ static void sas_form_port(struct asd_sas_phy *phy) if (port) { if (!phy_is_wideport_member(port, phy)) - sas_deform_port(phy); + sas_deform_port(phy, 0); else { SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", __func__, phy->id, phy->port->id, @@ -153,28 +153,31 @@ static void sas_form_port(struct asd_sas_phy *phy) * This is called when the physical link to the other phy has been * lost (on this phy), in Event thread context. We cannot delay here. */ -void sas_deform_port(struct asd_sas_phy *phy) +void sas_deform_port(struct asd_sas_phy *phy, int gone) { struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + struct domain_device *dev; unsigned long flags; if (!port) return; /* done by a phy event */ - if (port->port_dev) - port->port_dev->pathways--; + dev = port->port_dev; + if (dev) + dev->pathways--; if (port->num_phys == 1) { + if (dev && gone) + dev->gone = 1; sas_unregister_domain_devices(port); sas_port_delete(port->port); port->port = NULL; } else sas_port_delete_phy(port->port, phy->phy); - if (si->dft->lldd_port_deformed) si->dft->lldd_port_deformed(phy); @@ -244,7 +247,7 @@ void sas_porte_link_reset_err(struct work_struct *work) sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } void sas_porte_timer_event(struct work_struct *work) @@ -256,7 +259,7 @@ void sas_porte_timer_event(struct work_struct *work) sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } void sas_porte_hard_reset(struct work_struct *work) @@ -268,7 +271,7 @@ void sas_porte_hard_reset(struct work_struct *work) sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, &phy->port_events_pending); - sas_deform_port(phy); + sas_deform_port(phy, 1); } /* ---------- SAS port registration ---------- */ @@ -306,6 +309,6 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha) for (i = 0; i < sas_ha->num_phys; i++) if (sas_ha->sas_phy[i]->port) - sas_deform_port(sas_ha->sas_phy[i]); + sas_deform_port(sas_ha->sas_phy[i], 0); } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f6e189f40917..eeba76cdf774 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -207,6 +207,13 @@ static int sas_queuecommand_lck(struct scsi_cmnd *cmd, struct sas_ha_struct *sas_ha = dev->port->ha; struct sas_task *task; + /* If the device fell off, no sense in issuing commands */ + if (dev->gone) { + cmd->result = DID_BAD_TARGET << 16; + scsi_done(cmd); + goto out; + } + if (dev_is_sata(dev)) { unsigned long flags; @@ -216,13 +223,6 @@ static int sas_queuecommand_lck(struct scsi_cmnd *cmd, goto out; } - /* If the device fell off, no sense in issuing commands */ - if (dev->gone) { - cmd->result = DID_BAD_TARGET << 16; - scsi_done(cmd); - goto out; - } - res = -ENOMEM; task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 02d53d89534f..8ec2c86a49d4 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -41,6 +41,7 @@ struct lpfc_sli2_slim; downloads using bsg */ #define LPFC_DEFAULT_PROT_SG_SEG_CNT 4096 /* sg protection elements count */ #define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */ +#define LPFC_MAX_SGE_SIZE 0x80000000 /* Maximum data allowed in a SGE */ #define LPFC_MAX_PROT_SG_SEG_CNT 4096 /* prot sg element count per scsi cmd*/ #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ #define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */ @@ -486,6 +487,42 @@ struct unsol_rcv_ct_ctx { (1 << LPFC_USER_LINK_SPEED_AUTO)) #define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16" +enum nemb_type { + nemb_mse = 1, + nemb_hbd +}; + +enum mbox_type { + mbox_rd = 1, + mbox_wr +}; + +enum dma_type { + dma_mbox = 1, + dma_ebuf +}; + +enum sta_type { + sta_pre_addr = 1, + sta_pos_addr +}; + +struct lpfc_mbox_ext_buf_ctx { + uint32_t state; +#define LPFC_BSG_MBOX_IDLE 0 +#define LPFC_BSG_MBOX_HOST 1 +#define LPFC_BSG_MBOX_PORT 2 +#define LPFC_BSG_MBOX_DONE 3 +#define LPFC_BSG_MBOX_ABTS 4 + enum nemb_type nembType; + enum mbox_type mboxType; + uint32_t numBuf; + uint32_t mbxTag; + uint32_t seqNum; + struct lpfc_dmabuf *mbx_dmabuf; + struct list_head ext_dmabuf_list; +}; + struct lpfc_hba { /* SCSI interface function jump table entries */ int (*lpfc_new_scsi_buf) @@ -589,6 +626,7 @@ struct lpfc_hba { MAILBOX_t *mbox; uint32_t *mbox_ext; + struct lpfc_mbox_ext_buf_ctx mbox_ext_buf_ctx; uint32_t ha_copy; struct _PCB *pcb; struct _IOCB *IOCBs; @@ -659,6 +697,7 @@ struct lpfc_hba { uint32_t cfg_hostmem_hgp; uint32_t cfg_log_verbose; uint32_t cfg_aer_support; + uint32_t cfg_sriov_nr_virtfn; uint32_t cfg_iocb_cnt; uint32_t cfg_suppress_link_up; #define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */ @@ -706,7 +745,6 @@ struct lpfc_hba { uint32_t *hbq_get; /* Host mem address of HBQ get ptrs */ int brd_no; /* FC board number */ - char SerialNumber[32]; /* adapter Serial Number */ char OptionROMVersion[32]; /* adapter BIOS / Fcode version */ char ModelDesc[256]; /* Model Description */ @@ -778,6 +816,9 @@ struct lpfc_hba { uint16_t vpi_base; uint16_t vfi_base; unsigned long *vpi_bmask; /* vpi allocation table */ + uint16_t *vpi_ids; + uint16_t vpi_count; + struct list_head lpfc_vpi_blk_list; /* Data structure used by fabric iocb scheduler */ struct list_head fabric_iocb_list; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 8dcbf8fff673..135a53baa735 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -755,6 +755,73 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, } /** + * lpfc_sli4_pdev_reg_request - Request physical dev to perform a register acc + * @phba: lpfc_hba pointer. + * + * Description: + * Request SLI4 interface type-2 device to perform a physical register set + * access. + * + * Returns: + * zero for success + **/ +static ssize_t +lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) +{ + struct completion online_compl; + uint32_t reg_val; + int status = 0; + int rc; + + if (!phba->cfg_enable_hba_reset) + return -EIO; + + if ((phba->sli_rev < LPFC_SLI_REV4) || + (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2)) + return -EPERM; + + status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); + + if (status != 0) + return status; + + /* wait for the device to be quiesced before firmware reset */ + msleep(100); + + reg_val = readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PDEV_CTL_OFFSET); + + if (opcode == LPFC_FW_DUMP) + reg_val |= LPFC_FW_DUMP_REQUEST; + else if (opcode == LPFC_FW_RESET) + reg_val |= LPFC_CTL_PDEV_CTL_FRST; + else if (opcode == LPFC_DV_RESET) + reg_val |= LPFC_CTL_PDEV_CTL_DRST; + + writel(reg_val, phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PDEV_CTL_OFFSET); + /* flush */ + readl(phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET); + + /* delay driver action following IF_TYPE_2 reset */ + msleep(100); + + init_completion(&online_compl); + rc = lpfc_workq_post_event(phba, &status, &online_compl, + LPFC_EVT_ONLINE); + if (rc == 0) + return -ENOMEM; + + wait_for_completion(&online_compl); + + if (status != 0) + return -EIO; + + return 0; +} + +/** * lpfc_nport_evt_cnt_show - Return the number of nport events * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. @@ -848,6 +915,12 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr, return -EINVAL; else status = lpfc_do_offline(phba, LPFC_EVT_KILL); + else if (strncmp(buf, "dump", sizeof("dump") - 1) == 0) + status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_DUMP); + else if (strncmp(buf, "fw_reset", sizeof("fw_reset") - 1) == 0) + status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_RESET); + else if (strncmp(buf, "dv_reset", sizeof("dv_reset") - 1) == 0) + status = lpfc_sli4_pdev_reg_request(phba, LPFC_DV_RESET); else return -EINVAL; @@ -1322,6 +1395,102 @@ lpfc_dss_show(struct device *dev, struct device_attribute *attr, } /** + * lpfc_sriov_hw_max_virtfn_show - Return maximum number of virtual functions + * @dev: class converted to a Scsi_host structure. + * @attr: device attribute, not used. + * @buf: on return contains the formatted support level. + * + * Description: + * Returns the maximum number of virtual functions a physical function can + * support, 0 will be returned if called on virtual function. + * + * Returns: size of formatted string. + **/ +static ssize_t +lpfc_sriov_hw_max_virtfn_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct pci_dev *pdev = phba->pcidev; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t shdr_status, shdr_add_status; + LPFC_MBOXQ_t *mboxq; + struct lpfc_mbx_get_prof_cfg *get_prof_cfg; + struct lpfc_rsrc_desc_pcie *desc; + uint32_t max_nr_virtfn; + uint32_t desc_count; + int length, rc, i; + + if ((phba->sli_rev < LPFC_SLI_REV4) || + (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2)) + return -EPERM; + + if (!pdev->is_physfn) + return snprintf(buf, PAGE_SIZE, "%d\n", 0); + + mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mboxq) + return -ENOMEM; + + /* get the maximum number of virtfn support by physfn */ + length = (sizeof(struct lpfc_mbx_get_prof_cfg) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG, + length, LPFC_SLI4_MBX_EMBED); + shdr = (union lpfc_sli4_cfg_shdr *) + &mboxq->u.mqe.un.sli4_config.header.cfg_shdr; + bf_set(lpfc_mbox_hdr_pf_num, &shdr->request, + phba->sli4_hba.iov.pf_number + 1); + + get_prof_cfg = &mboxq->u.mqe.un.get_prof_cfg; + bf_set(lpfc_mbx_get_prof_cfg_prof_tp, &get_prof_cfg->u.request, + LPFC_CFG_TYPE_CURRENT_ACTIVE); + + rc = lpfc_sli_issue_mbox_wait(phba, mboxq, + lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG)); + + if (rc != MBX_TIMEOUT) { + /* check return status */ + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, + &shdr->response); + if (shdr_status || shdr_add_status || rc) + goto error_out; + + } else + goto error_out; + + desc_count = get_prof_cfg->u.response.prof_cfg.rsrc_desc_count; + + for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) { + desc = (struct lpfc_rsrc_desc_pcie *) + &get_prof_cfg->u.response.prof_cfg.desc[i]; + if (LPFC_RSRC_DESC_TYPE_PCIE == + bf_get(lpfc_rsrc_desc_pcie_type, desc)) { + max_nr_virtfn = bf_get(lpfc_rsrc_desc_pcie_nr_virtfn, + desc); + break; + } + } + + if (i < LPFC_RSRC_DESC_MAX_NUM) { + if (rc != MBX_TIMEOUT) + mempool_free(mboxq, phba->mbox_mem_pool); + return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn); + } + +error_out: + if (rc != MBX_TIMEOUT) + mempool_free(mboxq, phba->mbox_mem_pool); + return -EIO; +} + +/** * lpfc_param_show - Return a cfg attribute value in decimal * * Description: @@ -1762,6 +1931,8 @@ static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL); static DEVICE_ATTR(lpfc_fips_level, S_IRUGO, lpfc_fips_level_show, NULL); static DEVICE_ATTR(lpfc_fips_rev, S_IRUGO, lpfc_fips_rev_show, NULL); static DEVICE_ATTR(lpfc_dss, S_IRUGO, lpfc_dss_show, NULL); +static DEVICE_ATTR(lpfc_sriov_hw_max_virtfn, S_IRUGO, + lpfc_sriov_hw_max_virtfn_show, NULL); static char *lpfc_soft_wwn_key = "C99G71SL8032A"; @@ -3014,7 +3185,7 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR, * * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. - * @buf: containing the string "selective". + * @buf: containing enable or disable aer flag. * @count: unused variable. * * Description: @@ -3098,7 +3269,7 @@ lpfc_param_show(aer_support) /** * lpfc_aer_support_init - Set the initial adapters aer support flag * @phba: lpfc_hba pointer. - * @val: link speed value. + * @val: enable aer or disable aer flag. * * Description: * If val is in a valid range [0,1], then set the adapter's initial @@ -3137,7 +3308,7 @@ static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR, * lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device * @dev: class device that is converted into a Scsi_host. * @attr: device attribute, not used. - * @buf: containing the string "selective". + * @buf: containing flag 1 for aer cleanup state. * @count: unused variable. * * Description: @@ -3180,6 +3351,136 @@ lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL, lpfc_aer_cleanup_state); +/** + * lpfc_sriov_nr_virtfn_store - Enable the adapter for sr-iov virtual functions + * + * @dev: class device that is converted into a Scsi_host. + * @attr: device attribute, not used. + * @buf: containing the string the number of vfs to be enabled. + * @count: unused variable. + * + * Description: + * When this api is called either through user sysfs, the driver shall + * try to enable or disable SR-IOV virtual functions according to the + * following: + * + * If zero virtual function has been enabled to the physical function, + * the driver shall invoke the pci enable virtual function api trying + * to enable the virtual functions. If the nr_vfn provided is greater + * than the maximum supported, the maximum virtual function number will + * be used for invoking the api; otherwise, the nr_vfn provided shall + * be used for invoking the api. If the api call returned success, the + * actual number of virtual functions enabled will be set to the driver + * cfg_sriov_nr_virtfn; otherwise, -EINVAL shall be returned and driver + * cfg_sriov_nr_virtfn remains zero. + * + * If none-zero virtual functions have already been enabled to the + * physical function, as reflected by the driver's cfg_sriov_nr_virtfn, + * -EINVAL will be returned and the driver does nothing; + * + * If the nr_vfn provided is zero and none-zero virtual functions have + * been enabled, as indicated by the driver's cfg_sriov_nr_virtfn, the + * disabling virtual function api shall be invoded to disable all the + * virtual functions and driver's cfg_sriov_nr_virtfn shall be set to + * zero. Otherwise, if zero virtual function has been enabled, do + * nothing. + * + * Returns: + * length of the buf on success if val is in range the intended mode + * is supported. + * -EINVAL if val out of range or intended mode is not supported. + **/ +static ssize_t +lpfc_sriov_nr_virtfn_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; + struct lpfc_hba *phba = vport->phba; + struct pci_dev *pdev = phba->pcidev; + int val = 0, rc = -EINVAL; + + /* Sanity check on user data */ + if (!isdigit(buf[0])) + return -EINVAL; + if (sscanf(buf, "%i", &val) != 1) + return -EINVAL; + if (val < 0) + return -EINVAL; + + /* Request disabling virtual functions */ + if (val == 0) { + if (phba->cfg_sriov_nr_virtfn > 0) { + pci_disable_sriov(pdev); + phba->cfg_sriov_nr_virtfn = 0; + } + return strlen(buf); + } + + /* Request enabling virtual functions */ + if (phba->cfg_sriov_nr_virtfn > 0) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3018 There are %d virtual functions " + "enabled on physical function.\n", + phba->cfg_sriov_nr_virtfn); + return -EEXIST; + } + + if (val <= LPFC_MAX_VFN_PER_PFN) + phba->cfg_sriov_nr_virtfn = val; + else { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3019 Enabling %d virtual functions is not " + "allowed.\n", val); + return -EINVAL; + } + + rc = lpfc_sli_probe_sriov_nr_virtfn(phba, phba->cfg_sriov_nr_virtfn); + if (rc) { + phba->cfg_sriov_nr_virtfn = 0; + rc = -EPERM; + } else + rc = strlen(buf); + + return rc; +} + +static int lpfc_sriov_nr_virtfn = LPFC_DEF_VFN_PER_PFN; +module_param(lpfc_sriov_nr_virtfn, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(lpfc_sriov_nr_virtfn, "Enable PCIe device SR-IOV virtual fn"); +lpfc_param_show(sriov_nr_virtfn) + +/** + * lpfc_sriov_nr_virtfn_init - Set the initial sr-iov virtual function enable + * @phba: lpfc_hba pointer. + * @val: link speed value. + * + * Description: + * If val is in a valid range [0,255], then set the adapter's initial + * cfg_sriov_nr_virtfn field. If it's greater than the maximum, the maximum + * number shall be used instead. It will be up to the driver's probe_one + * routine to determine whether the device's SR-IOV is supported or not. + * + * Returns: + * zero if val saved. + * -EINVAL val out of range + **/ +static int +lpfc_sriov_nr_virtfn_init(struct lpfc_hba *phba, int val) +{ + if (val >= 0 && val <= LPFC_MAX_VFN_PER_PFN) { + phba->cfg_sriov_nr_virtfn = val; + return 0; + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3017 Enabling %d virtual functions is not " + "allowed.\n", val); + return -EINVAL; +} +static DEVICE_ATTR(lpfc_sriov_nr_virtfn, S_IRUGO | S_IWUSR, + lpfc_sriov_nr_virtfn_show, lpfc_sriov_nr_virtfn_store); + /* # lpfc_fcp_class: Determines FC class to use for the FCP protocol. # Value range is [2,3]. Default value is 3. @@ -3497,6 +3798,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_prot_sg_seg_cnt, &dev_attr_lpfc_aer_support, &dev_attr_lpfc_aer_state_cleanup, + &dev_attr_lpfc_sriov_nr_virtfn, &dev_attr_lpfc_suppress_link_up, &dev_attr_lpfc_iocb_cnt, &dev_attr_iocb_hw, @@ -3505,6 +3807,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_fips_level, &dev_attr_lpfc_fips_rev, &dev_attr_lpfc_dss, + &dev_attr_lpfc_sriov_hw_max_virtfn, NULL, }; @@ -3961,7 +4264,7 @@ static struct bin_attribute sysfs_mbox_attr = { .name = "mbox", .mode = S_IRUSR | S_IWUSR, }, - .size = MAILBOX_CMD_SIZE, + .size = MAILBOX_SYSFS_MAX, .read = sysfs_mbox_read, .write = sysfs_mbox_write, }; @@ -4705,6 +5008,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth); lpfc_hba_log_verbose_init(phba, lpfc_log_verbose); lpfc_aer_support_init(phba, lpfc_aer_support); + lpfc_sriov_nr_virtfn_init(phba, lpfc_sriov_nr_virtfn); lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up); lpfc_iocb_cnt_init(phba, lpfc_iocb_cnt); phba->cfg_enable_dss = 1; diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 853e5042f39c..7fb0ba4cbfa7 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -23,6 +23,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/list.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> @@ -79,8 +80,7 @@ struct lpfc_bsg_iocb { struct lpfc_bsg_mbox { LPFC_MBOXQ_t *pmboxq; MAILBOX_t *mb; - struct lpfc_dmabuf *rxbmp; /* for BIU diags */ - struct lpfc_dmabufext *dmp; /* for BIU diags */ + struct lpfc_dmabuf *dmabuffers; /* for BIU diags */ uint8_t *ext; /* extended mailbox data */ uint32_t mbOffset; /* from app */ uint32_t inExtWLen; /* from app */ @@ -332,6 +332,8 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) cmd->ulpLe = 1; cmd->ulpClass = CLASS3; cmd->ulpContext = ndlp->nlp_rpi; + if (phba->sli_rev == LPFC_SLI_REV4) + cmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; cmd->ulpOwner = OWN_CHIP; cmdiocbq->vport = phba->pport; cmdiocbq->context3 = bmp; @@ -1336,6 +1338,10 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, } icmd->un.ulpWord[3] = ndlp->nlp_rpi; + if (phba->sli_rev == LPFC_SLI_REV4) + icmd->ulpContext = + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; + /* The exchange is done, mark the entry as invalid */ phba->ct_ctx[tag].flags &= ~UNSOL_VALID; } else @@ -1463,11 +1469,91 @@ send_mgmt_rsp_exit: } /** - * lpfc_bsg_diag_mode - process a LPFC_BSG_VENDOR_DIAG_MODE bsg vendor command + * lpfc_bsg_diag_mode_enter - process preparing into device diag loopback mode + * @phba: Pointer to HBA context object. * @job: LPFC_BSG_VENDOR_DIAG_MODE * - * This function is responsible for placing a port into diagnostic loopback - * mode in order to perform a diagnostic loopback test. + * This function is responsible for preparing driver for diag loopback + * on device. + */ +static int +lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba, struct fc_bsg_job *job) +{ + struct lpfc_vport **vports; + struct Scsi_Host *shost; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + int i = 0; + + psli = &phba->sli; + if (!psli) + return -ENODEV; + + pring = &psli->ring[LPFC_FCP_RING]; + if (!pring) + return -ENODEV; + + if ((phba->link_state == LPFC_HBA_ERROR) || + (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || + (!(psli->sli_flag & LPFC_SLI_ACTIVE))) + return -EACCES; + + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { + shost = lpfc_shost_from_vport(vports[i]); + scsi_block_requests(shost); + } + lpfc_destroy_vport_work_array(phba, vports); + } else { + shost = lpfc_shost_from_vport(phba->pport); + scsi_block_requests(shost); + } + + while (pring->txcmplq_cnt) { + if (i++ > 500) /* wait up to 5 seconds */ + break; + msleep(10); + } + return 0; +} + +/** + * lpfc_bsg_diag_mode_exit - exit process from device diag loopback mode + * @phba: Pointer to HBA context object. + * @job: LPFC_BSG_VENDOR_DIAG_MODE + * + * This function is responsible for driver exit processing of setting up + * diag loopback mode on device. + */ +static void +lpfc_bsg_diag_mode_exit(struct lpfc_hba *phba) +{ + struct Scsi_Host *shost; + struct lpfc_vport **vports; + int i; + + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { + shost = lpfc_shost_from_vport(vports[i]); + scsi_unblock_requests(shost); + } + lpfc_destroy_vport_work_array(phba, vports); + } else { + shost = lpfc_shost_from_vport(phba->pport); + scsi_unblock_requests(shost); + } + return; +} + +/** + * lpfc_sli3_bsg_diag_loopback_mode - process an sli3 bsg vendor command + * @phba: Pointer to HBA context object. + * @job: LPFC_BSG_VENDOR_DIAG_MODE + * + * This function is responsible for placing an sli3 port into diagnostic + * loopback mode in order to perform a diagnostic loopback test. * All new scsi requests are blocked, a small delay is used to allow the * scsi requests to complete then the link is brought down. If the link is * is placed in loopback mode then scsi requests are again allowed @@ -1475,17 +1561,11 @@ send_mgmt_rsp_exit: * All of this is done in-line. */ static int -lpfc_bsg_diag_mode(struct fc_bsg_job *job) +lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job) { - struct Scsi_Host *shost = job->shost; - struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; - struct lpfc_hba *phba = vport->phba; struct diag_mode_set *loopback_mode; - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING]; uint32_t link_flags; uint32_t timeout; - struct lpfc_vport **vports; LPFC_MBOXQ_t *pmboxq; int mbxstatus; int i = 0; @@ -1494,53 +1574,33 @@ lpfc_bsg_diag_mode(struct fc_bsg_job *job) /* no data to return just the return code */ job->reply->reply_payload_rcv_len = 0; - if (job->request_len < - sizeof(struct fc_bsg_request) + sizeof(struct diag_mode_set)) { + if (job->request_len < sizeof(struct fc_bsg_request) + + sizeof(struct diag_mode_set)) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2738 Received DIAG MODE request below minimum " - "size\n"); + "2738 Received DIAG MODE request size:%d " + "below the minimum size:%d\n", + job->request_len, + (int)(sizeof(struct fc_bsg_request) + + sizeof(struct diag_mode_set))); rc = -EINVAL; goto job_error; } + rc = lpfc_bsg_diag_mode_enter(phba, job); + if (rc) + goto job_error; + + /* bring the link to diagnostic mode */ loopback_mode = (struct diag_mode_set *) job->request->rqst_data.h_vendor.vendor_cmd; link_flags = loopback_mode->type; timeout = loopback_mode->timeout * 100; - if ((phba->link_state == LPFC_HBA_ERROR) || - (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || - (!(psli->sli_flag & LPFC_SLI_ACTIVE))) { - rc = -EACCES; - goto job_error; - } - pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) { rc = -ENOMEM; - goto job_error; - } - - vports = lpfc_create_vport_work_array(phba); - if (vports) { - for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { - shost = lpfc_shost_from_vport(vports[i]); - scsi_block_requests(shost); - } - - lpfc_destroy_vport_work_array(phba, vports); - } else { - shost = lpfc_shost_from_vport(phba->pport); - scsi_block_requests(shost); + goto loopback_mode_exit; } - - while (pring->txcmplq_cnt) { - if (i++ > 500) /* wait up to 5 seconds */ - break; - - msleep(10); - } - memset((void *)pmboxq, 0, sizeof(LPFC_MBOXQ_t)); pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK; pmboxq->u.mb.mbxOwner = OWN_HOST; @@ -1594,22 +1654,186 @@ lpfc_bsg_diag_mode(struct fc_bsg_job *job) rc = -ENODEV; loopback_mode_exit: - vports = lpfc_create_vport_work_array(phba); - if (vports) { - for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { - shost = lpfc_shost_from_vport(vports[i]); - scsi_unblock_requests(shost); + lpfc_bsg_diag_mode_exit(phba); + + /* + * Let SLI layer release mboxq if mbox command completed after timeout. + */ + if (mbxstatus != MBX_TIMEOUT) + mempool_free(pmboxq, phba->mbox_mem_pool); + +job_error: + /* make error code available to userspace */ + job->reply->result = rc; + /* complete the job back to userspace if no error */ + if (rc == 0) + job->job_done(job); + return rc; +} + +/** + * lpfc_sli4_bsg_set_link_diag_state - set sli4 link diag state + * @phba: Pointer to HBA context object. + * @diag: Flag for set link to diag or nomral operation state. + * + * This function is responsible for issuing a sli4 mailbox command for setting + * link to either diag state or normal operation state. + */ +static int +lpfc_sli4_bsg_set_link_diag_state(struct lpfc_hba *phba, uint32_t diag) +{ + LPFC_MBOXQ_t *pmboxq; + struct lpfc_mbx_set_link_diag_state *link_diag_state; + uint32_t req_len, alloc_len; + int mbxstatus = MBX_SUCCESS, rc; + + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) + return -ENOMEM; + + req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE, + req_len, LPFC_SLI4_MBX_EMBED); + if (alloc_len != req_len) { + rc = -ENOMEM; + goto link_diag_state_set_out; + } + link_diag_state = &pmboxq->u.mqe.un.link_diag_state; + bf_set(lpfc_mbx_set_diag_state_link_num, &link_diag_state->u.req, + phba->sli4_hba.link_state.number); + bf_set(lpfc_mbx_set_diag_state_link_type, &link_diag_state->u.req, + phba->sli4_hba.link_state.type); + if (diag) + bf_set(lpfc_mbx_set_diag_state_diag, + &link_diag_state->u.req, 1); + else + bf_set(lpfc_mbx_set_diag_state_diag, + &link_diag_state->u.req, 0); + + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO); + + if ((mbxstatus == MBX_SUCCESS) && (pmboxq->u.mb.mbxStatus == 0)) + rc = 0; + else + rc = -ENODEV; + +link_diag_state_set_out: + if (pmboxq && (mbxstatus != MBX_TIMEOUT)) + mempool_free(pmboxq, phba->mbox_mem_pool); + + return rc; +} + +/** + * lpfc_sli4_bsg_diag_loopback_mode - process an sli4 bsg vendor command + * @phba: Pointer to HBA context object. + * @job: LPFC_BSG_VENDOR_DIAG_MODE + * + * This function is responsible for placing an sli4 port into diagnostic + * loopback mode in order to perform a diagnostic loopback test. + */ +static int +lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job) +{ + struct diag_mode_set *loopback_mode; + uint32_t link_flags, timeout, req_len, alloc_len; + struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback; + LPFC_MBOXQ_t *pmboxq = NULL; + int mbxstatus, i, rc = 0; + + /* no data to return just the return code */ + job->reply->reply_payload_rcv_len = 0; + + if (job->request_len < sizeof(struct fc_bsg_request) + + sizeof(struct diag_mode_set)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "3011 Received DIAG MODE request size:%d " + "below the minimum size:%d\n", + job->request_len, + (int)(sizeof(struct fc_bsg_request) + + sizeof(struct diag_mode_set))); + rc = -EINVAL; + goto job_error; + } + + rc = lpfc_bsg_diag_mode_enter(phba, job); + if (rc) + goto job_error; + + /* bring the link to diagnostic mode */ + loopback_mode = (struct diag_mode_set *) + job->request->rqst_data.h_vendor.vendor_cmd; + link_flags = loopback_mode->type; + timeout = loopback_mode->timeout * 100; + + rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1); + if (rc) + goto loopback_mode_exit; + + /* wait for link down before proceeding */ + i = 0; + while (phba->link_state != LPFC_LINK_DOWN) { + if (i++ > timeout) { + rc = -ETIMEDOUT; + goto loopback_mode_exit; + } + msleep(10); + } + /* set up loopback mode */ + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto loopback_mode_exit; + } + req_len = (sizeof(struct lpfc_mbx_set_link_diag_loopback) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK, + req_len, LPFC_SLI4_MBX_EMBED); + if (alloc_len != req_len) { + rc = -ENOMEM; + goto loopback_mode_exit; + } + link_diag_loopback = &pmboxq->u.mqe.un.link_diag_loopback; + bf_set(lpfc_mbx_set_diag_state_link_num, + &link_diag_loopback->u.req, phba->sli4_hba.link_state.number); + bf_set(lpfc_mbx_set_diag_state_link_type, + &link_diag_loopback->u.req, phba->sli4_hba.link_state.type); + if (link_flags == INTERNAL_LOOP_BACK) + bf_set(lpfc_mbx_set_diag_lpbk_type, + &link_diag_loopback->u.req, + LPFC_DIAG_LOOPBACK_TYPE_INTERNAL); + else + bf_set(lpfc_mbx_set_diag_lpbk_type, + &link_diag_loopback->u.req, + LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL); + + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO); + if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) + rc = -ENODEV; + else { + phba->link_flag |= LS_LOOPBACK_MODE; + /* wait for the link attention interrupt */ + msleep(100); + i = 0; + while (phba->link_state != LPFC_HBA_READY) { + if (i++ > timeout) { + rc = -ETIMEDOUT; + break; + } + msleep(10); } - lpfc_destroy_vport_work_array(phba, vports); - } else { - shost = lpfc_shost_from_vport(phba->pport); - scsi_unblock_requests(shost); } +loopback_mode_exit: + lpfc_bsg_diag_mode_exit(phba); + /* * Let SLI layer release mboxq if mbox command completed after timeout. */ - if (mbxstatus != MBX_TIMEOUT) + if (pmboxq && (mbxstatus != MBX_TIMEOUT)) mempool_free(pmboxq, phba->mbox_mem_pool); job_error: @@ -1622,6 +1846,234 @@ job_error: } /** + * lpfc_bsg_diag_loopback_mode - bsg vendor command for diag loopback mode + * @job: LPFC_BSG_VENDOR_DIAG_MODE + * + * This function is responsible for responding to check and dispatch bsg diag + * command from the user to proper driver action routines. + */ +static int +lpfc_bsg_diag_loopback_mode(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + int rc; + + shost = job->shost; + if (!shost) + return -ENODEV; + vport = (struct lpfc_vport *)job->shost->hostdata; + if (!vport) + return -ENODEV; + phba = vport->phba; + if (!phba) + return -ENODEV; + + if (phba->sli_rev < LPFC_SLI_REV4) + rc = lpfc_sli3_bsg_diag_loopback_mode(phba, job); + else if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_IF_TYPE_2) + rc = lpfc_sli4_bsg_diag_loopback_mode(phba, job); + else + rc = -ENODEV; + + return rc; + +} + +/** + * lpfc_sli4_bsg_diag_mode_end - sli4 bsg vendor command for ending diag mode + * @job: LPFC_BSG_VENDOR_DIAG_MODE_END + * + * This function is responsible for responding to check and dispatch bsg diag + * command from the user to proper driver action routines. + */ +static int +lpfc_sli4_bsg_diag_mode_end(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + int rc; + + shost = job->shost; + if (!shost) + return -ENODEV; + vport = (struct lpfc_vport *)job->shost->hostdata; + if (!vport) + return -ENODEV; + phba = vport->phba; + if (!phba) + return -ENODEV; + + if (phba->sli_rev < LPFC_SLI_REV4) + return -ENODEV; + if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) + return -ENODEV; + + rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0); + + if (!rc) + rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT); + + return rc; +} + +/** + * lpfc_sli4_bsg_link_diag_test - sli4 bsg vendor command for diag link test + * @job: LPFC_BSG_VENDOR_DIAG_LINK_TEST + * + * This function is to perform SLI4 diag link test request from the user + * applicaiton. + */ +static int +lpfc_sli4_bsg_link_diag_test(struct fc_bsg_job *job) +{ + struct Scsi_Host *shost; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + LPFC_MBOXQ_t *pmboxq; + struct sli4_link_diag *link_diag_test_cmd; + uint32_t req_len, alloc_len; + uint32_t timeout; + struct lpfc_mbx_run_link_diag_test *run_link_diag_test; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t shdr_status, shdr_add_status; + struct diag_status *diag_status_reply; + int mbxstatus, rc = 0; + + shost = job->shost; + if (!shost) { + rc = -ENODEV; + goto job_error; + } + vport = (struct lpfc_vport *)job->shost->hostdata; + if (!vport) { + rc = -ENODEV; + goto job_error; + } + phba = vport->phba; + if (!phba) { + rc = -ENODEV; + goto job_error; + } + + if (phba->sli_rev < LPFC_SLI_REV4) { + rc = -ENODEV; + goto job_error; + } + if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) { + rc = -ENODEV; + goto job_error; + } + + if (job->request_len < sizeof(struct fc_bsg_request) + + sizeof(struct sli4_link_diag)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "3013 Received LINK DIAG TEST request " + " size:%d below the minimum size:%d\n", + job->request_len, + (int)(sizeof(struct fc_bsg_request) + + sizeof(struct sli4_link_diag))); + rc = -EINVAL; + goto job_error; + } + + rc = lpfc_bsg_diag_mode_enter(phba, job); + if (rc) + goto job_error; + + link_diag_test_cmd = (struct sli4_link_diag *) + job->request->rqst_data.h_vendor.vendor_cmd; + timeout = link_diag_test_cmd->timeout * 100; + + rc = lpfc_sli4_bsg_set_link_diag_state(phba, 1); + + if (rc) + goto job_error; + + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto link_diag_test_exit; + } + + req_len = (sizeof(struct lpfc_mbx_set_link_diag_state) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + alloc_len = lpfc_sli4_config(phba, pmboxq, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE, + req_len, LPFC_SLI4_MBX_EMBED); + if (alloc_len != req_len) { + rc = -ENOMEM; + goto link_diag_test_exit; + } + run_link_diag_test = &pmboxq->u.mqe.un.link_diag_test; + bf_set(lpfc_mbx_run_diag_test_link_num, &run_link_diag_test->u.req, + phba->sli4_hba.link_state.number); + bf_set(lpfc_mbx_run_diag_test_link_type, &run_link_diag_test->u.req, + phba->sli4_hba.link_state.type); + bf_set(lpfc_mbx_run_diag_test_test_id, &run_link_diag_test->u.req, + link_diag_test_cmd->test_id); + bf_set(lpfc_mbx_run_diag_test_loops, &run_link_diag_test->u.req, + link_diag_test_cmd->loops); + bf_set(lpfc_mbx_run_diag_test_test_ver, &run_link_diag_test->u.req, + link_diag_test_cmd->test_version); + bf_set(lpfc_mbx_run_diag_test_err_act, &run_link_diag_test->u.req, + link_diag_test_cmd->error_action); + + mbxstatus = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); + + shdr = (union lpfc_sli4_cfg_shdr *) + &pmboxq->u.mqe.un.sli4_config.header.cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (shdr_status || shdr_add_status || mbxstatus) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "3010 Run link diag test mailbox failed with " + "mbx_status x%x status x%x, add_status x%x\n", + mbxstatus, shdr_status, shdr_add_status); + } + + diag_status_reply = (struct diag_status *) + job->reply->reply_data.vendor_reply.vendor_rsp; + + if (job->reply_len < + sizeof(struct fc_bsg_request) + sizeof(struct diag_status)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, + "3012 Received Run link diag test reply " + "below minimum size (%d): reply_len:%d\n", + (int)(sizeof(struct fc_bsg_request) + + sizeof(struct diag_status)), + job->reply_len); + rc = -EINVAL; + goto job_error; + } + + diag_status_reply->mbox_status = mbxstatus; + diag_status_reply->shdr_status = shdr_status; + diag_status_reply->shdr_add_status = shdr_add_status; + +link_diag_test_exit: + rc = lpfc_sli4_bsg_set_link_diag_state(phba, 0); + + if (pmboxq) + mempool_free(pmboxq, phba->mbox_mem_pool); + + lpfc_bsg_diag_mode_exit(phba); + +job_error: + /* make error code available to userspace */ + job->reply->result = rc; + /* complete the job back to userspace if no error */ + if (rc == 0) + job->job_done(job); + return rc; +} + +/** * lpfcdiag_loop_self_reg - obtains a remote port login id * @phba: Pointer to HBA context object * @rpi: Pointer to a remote port login id @@ -1851,6 +2303,86 @@ err_get_xri_exit: } /** + * lpfc_bsg_dma_page_alloc - allocate a bsg mbox page sized dma buffers + * @phba: Pointer to HBA context object + * + * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and. + * retruns the pointer to the buffer. + **/ +static struct lpfc_dmabuf * +lpfc_bsg_dma_page_alloc(struct lpfc_hba *phba) +{ + struct lpfc_dmabuf *dmabuf; + struct pci_dev *pcidev = phba->pcidev; + + /* allocate dma buffer struct */ + dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!dmabuf) + return NULL; + + INIT_LIST_HEAD(&dmabuf->list); + + /* now, allocate dma buffer */ + dmabuf->virt = dma_alloc_coherent(&pcidev->dev, BSG_MBOX_SIZE, + &(dmabuf->phys), GFP_KERNEL); + + if (!dmabuf->virt) { + kfree(dmabuf); + return NULL; + } + memset((uint8_t *)dmabuf->virt, 0, BSG_MBOX_SIZE); + + return dmabuf; +} + +/** + * lpfc_bsg_dma_page_free - free a bsg mbox page sized dma buffer + * @phba: Pointer to HBA context object. + * @dmabuf: Pointer to the bsg mbox page sized dma buffer descriptor. + * + * This routine just simply frees a dma buffer and its associated buffer + * descriptor referred by @dmabuf. + **/ +static void +lpfc_bsg_dma_page_free(struct lpfc_hba *phba, struct lpfc_dmabuf *dmabuf) +{ + struct pci_dev *pcidev = phba->pcidev; + + if (!dmabuf) + return; + + if (dmabuf->virt) + dma_free_coherent(&pcidev->dev, BSG_MBOX_SIZE, + dmabuf->virt, dmabuf->phys); + kfree(dmabuf); + return; +} + +/** + * lpfc_bsg_dma_page_list_free - free a list of bsg mbox page sized dma buffers + * @phba: Pointer to HBA context object. + * @dmabuf_list: Pointer to a list of bsg mbox page sized dma buffer descs. + * + * This routine just simply frees all dma buffers and their associated buffer + * descriptors referred by @dmabuf_list. + **/ +static void +lpfc_bsg_dma_page_list_free(struct lpfc_hba *phba, + struct list_head *dmabuf_list) +{ + struct lpfc_dmabuf *dmabuf, *next_dmabuf; + + if (list_empty(dmabuf_list)) + return; + + list_for_each_entry_safe(dmabuf, next_dmabuf, dmabuf_list, list) { + list_del_init(&dmabuf->list); + lpfc_bsg_dma_page_free(phba, dmabuf); + } + return; +} + +/** * diag_cmd_data_alloc - fills in a bde struct with dma buffers * @phba: Pointer to HBA context object * @bpl: Pointer to 64 bit bde structure @@ -2067,7 +2599,7 @@ err_post_rxbufs_exit: } /** - * lpfc_bsg_diag_test - with a port in loopback issues a Ct cmd to itself + * lpfc_bsg_diag_loopback_run - run loopback on a port by issue ct cmd to itself * @job: LPFC_BSG_VENDOR_DIAG_TEST fc_bsg_job * * This function receives a user data buffer to be transmitted and received on @@ -2086,7 +2618,7 @@ err_post_rxbufs_exit: * of loopback mode. **/ static int -lpfc_bsg_diag_test(struct fc_bsg_job *job) +lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; @@ -2411,7 +2943,7 @@ job_error: } /** - * lpfc_bsg_wake_mbox_wait - lpfc_bsg_issue_mbox mbox completion handler + * lpfc_bsg_issue_mbox_cmpl - lpfc_bsg_issue_mbox mbox completion handler * @phba: Pointer to HBA context object. * @pmboxq: Pointer to mailbox command. * @@ -2422,15 +2954,13 @@ job_error: * of the mailbox. **/ void -lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) +lpfc_bsg_issue_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) { struct bsg_job_data *dd_data; struct fc_bsg_job *job; - struct lpfc_mbx_nembed_cmd *nembed_sge; uint32_t size; unsigned long flags; - uint8_t *to; - uint8_t *from; + uint8_t *pmb, *pmb_buf; spin_lock_irqsave(&phba->ct_ev_lock, flags); dd_data = pmboxq->context1; @@ -2440,62 +2970,21 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) return; } - /* build the outgoing buffer to do an sg copy - * the format is the response mailbox followed by any extended - * mailbox data + /* + * The outgoing buffer is readily referred from the dma buffer, + * just need to get header part from mailboxq structure. */ - from = (uint8_t *)&pmboxq->u.mb; - to = (uint8_t *)dd_data->context_un.mbox.mb; - memcpy(to, from, sizeof(MAILBOX_t)); - if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS) { - /* copy the extended data if any, count is in words */ - if (dd_data->context_un.mbox.outExtWLen) { - from = (uint8_t *)dd_data->context_un.mbox.ext; - to += sizeof(MAILBOX_t); - size = dd_data->context_un.mbox.outExtWLen * - sizeof(uint32_t); - memcpy(to, from, size); - } else if (pmboxq->u.mb.mbxCommand == MBX_RUN_BIU_DIAG64) { - from = (uint8_t *)dd_data->context_un.mbox. - dmp->dma.virt; - to += sizeof(MAILBOX_t); - size = dd_data->context_un.mbox.dmp->size; - memcpy(to, from, size); - } else if ((phba->sli_rev == LPFC_SLI_REV4) && - (pmboxq->u.mb.mbxCommand == MBX_DUMP_MEMORY)) { - from = (uint8_t *)dd_data->context_un.mbox.dmp->dma. - virt; - to += sizeof(MAILBOX_t); - size = pmboxq->u.mb.un.varWords[5]; - memcpy(to, from, size); - } else if ((phba->sli_rev == LPFC_SLI_REV4) && - (pmboxq->u.mb.mbxCommand == MBX_SLI4_CONFIG)) { - nembed_sge = (struct lpfc_mbx_nembed_cmd *) - &pmboxq->u.mb.un.varWords[0]; - - from = (uint8_t *)dd_data->context_un.mbox.dmp->dma. - virt; - to += sizeof(MAILBOX_t); - size = nembed_sge->sge[0].length; - memcpy(to, from, size); - } else if (pmboxq->u.mb.mbxCommand == MBX_READ_EVENT_LOG) { - from = (uint8_t *)dd_data->context_un. - mbox.dmp->dma.virt; - to += sizeof(MAILBOX_t); - size = dd_data->context_un.mbox.dmp->size; - memcpy(to, from, size); - } - } + pmb = (uint8_t *)&pmboxq->u.mb; + pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb; + memcpy(pmb_buf, pmb, sizeof(MAILBOX_t)); - from = (uint8_t *)dd_data->context_un.mbox.mb; job = dd_data->context_un.mbox.set_job; if (job) { size = job->reply_payload.payload_len; job->reply->reply_payload_rcv_len = sg_copy_from_buffer(job->reply_payload.sg_list, - job->reply_payload.sg_cnt, - from, size); - job->reply->result = 0; + job->reply_payload.sg_cnt, + pmb_buf, size); /* need to hold the lock until we set job->dd_data to NULL * to hold off the timeout handler returning to the mid-layer * while we are still processing the job. @@ -2503,28 +2992,19 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) job->dd_data = NULL; dd_data->context_un.mbox.set_job = NULL; spin_unlock_irqrestore(&phba->ct_ev_lock, flags); - job->job_done(job); } else { dd_data->context_un.mbox.set_job = NULL; spin_unlock_irqrestore(&phba->ct_ev_lock, flags); } - kfree(dd_data->context_un.mbox.mb); mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool); - kfree(dd_data->context_un.mbox.ext); - if (dd_data->context_un.mbox.dmp) { - dma_free_coherent(&phba->pcidev->dev, - dd_data->context_un.mbox.dmp->size, - dd_data->context_un.mbox.dmp->dma.virt, - dd_data->context_un.mbox.dmp->dma.phys); - kfree(dd_data->context_un.mbox.dmp); - } - if (dd_data->context_un.mbox.rxbmp) { - lpfc_mbuf_free(phba, dd_data->context_un.mbox.rxbmp->virt, - dd_data->context_un.mbox.rxbmp->phys); - kfree(dd_data->context_un.mbox.rxbmp); - } + lpfc_bsg_dma_page_free(phba, dd_data->context_un.mbox.dmabuffers); kfree(dd_data); + + if (job) { + job->reply->result = 0; + job->job_done(job); + } return; } @@ -2619,6 +3099,1006 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, } /** + * lpfc_bsg_mbox_ext_cleanup - clean up context of multi-buffer mbox session + * @phba: Pointer to HBA context object. + * + * This is routine clean up and reset BSG handling of multi-buffer mbox + * command session. + **/ +static void +lpfc_bsg_mbox_ext_session_reset(struct lpfc_hba *phba) +{ + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) + return; + + /* free all memory, including dma buffers */ + lpfc_bsg_dma_page_list_free(phba, + &phba->mbox_ext_buf_ctx.ext_dmabuf_list); + lpfc_bsg_dma_page_free(phba, phba->mbox_ext_buf_ctx.mbx_dmabuf); + /* multi-buffer write mailbox command pass-through complete */ + memset((char *)&phba->mbox_ext_buf_ctx, 0, + sizeof(struct lpfc_mbox_ext_buf_ctx)); + INIT_LIST_HEAD(&phba->mbox_ext_buf_ctx.ext_dmabuf_list); + + return; +} + +/** + * lpfc_bsg_issue_mbox_ext_handle_job - job handler for multi-buffer mbox cmpl + * @phba: Pointer to HBA context object. + * @pmboxq: Pointer to mailbox command. + * + * This is routine handles BSG job for mailbox commands completions with + * multiple external buffers. + **/ +static struct fc_bsg_job * +lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) +{ + struct bsg_job_data *dd_data; + struct fc_bsg_job *job; + uint8_t *pmb, *pmb_buf; + unsigned long flags; + uint32_t size; + int rc = 0; + + spin_lock_irqsave(&phba->ct_ev_lock, flags); + dd_data = pmboxq->context1; + /* has the job already timed out? */ + if (!dd_data) { + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + job = NULL; + goto job_done_out; + } + + /* + * The outgoing buffer is readily referred from the dma buffer, + * just need to get header part from mailboxq structure. + */ + pmb = (uint8_t *)&pmboxq->u.mb; + pmb_buf = (uint8_t *)dd_data->context_un.mbox.mb; + memcpy(pmb_buf, pmb, sizeof(MAILBOX_t)); + + job = dd_data->context_un.mbox.set_job; + if (job) { + size = job->reply_payload.payload_len; + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + pmb_buf, size); + /* result for successful */ + job->reply->result = 0; + job->dd_data = NULL; + /* need to hold the lock util we set job->dd_data to NULL + * to hold off the timeout handler from midlayer to take + * any action. + */ + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2937 SLI_CONFIG ext-buffer maibox command " + "(x%x/x%x) complete bsg job done, bsize:%d\n", + phba->mbox_ext_buf_ctx.nembType, + phba->mbox_ext_buf_ctx.mboxType, size); + } else + spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + +job_done_out: + if (!job) + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2938 SLI_CONFIG ext-buffer maibox " + "command (x%x/x%x) failure, rc:x%x\n", + phba->mbox_ext_buf_ctx.nembType, + phba->mbox_ext_buf_ctx.mboxType, rc); + /* state change */ + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_DONE; + kfree(dd_data); + + return job; +} + +/** + * lpfc_bsg_issue_read_mbox_ext_cmpl - compl handler for multi-buffer read mbox + * @phba: Pointer to HBA context object. + * @pmboxq: Pointer to mailbox command. + * + * This is completion handler function for mailbox read commands with multiple + * external buffers. + **/ +static void +lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) +{ + struct fc_bsg_job *job; + + /* handle the BSG job with mailbox command */ + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS) + pmboxq->u.mb.mbxStatus = MBXERR_ERROR; + + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2939 SLI_CONFIG ext-buffer rd maibox command " + "complete, ctxState:x%x, mbxStatus:x%x\n", + phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); + + job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq); + + if (pmboxq->u.mb.mbxStatus || phba->mbox_ext_buf_ctx.numBuf == 1) + lpfc_bsg_mbox_ext_session_reset(phba); + + /* free base driver mailbox structure memory */ + mempool_free(pmboxq, phba->mbox_mem_pool); + + /* complete the bsg job if we have it */ + if (job) + job->job_done(job); + + return; +} + +/** + * lpfc_bsg_issue_write_mbox_ext_cmpl - cmpl handler for multi-buffer write mbox + * @phba: Pointer to HBA context object. + * @pmboxq: Pointer to mailbox command. + * + * This is completion handler function for mailbox write commands with multiple + * external buffers. + **/ +static void +lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) +{ + struct fc_bsg_job *job; + + /* handle the BSG job with the mailbox command */ + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_ABTS) + pmboxq->u.mb.mbxStatus = MBXERR_ERROR; + + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2940 SLI_CONFIG ext-buffer wr maibox command " + "complete, ctxState:x%x, mbxStatus:x%x\n", + phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); + + job = lpfc_bsg_issue_mbox_ext_handle_job(phba, pmboxq); + + /* free all memory, including dma buffers */ + mempool_free(pmboxq, phba->mbox_mem_pool); + lpfc_bsg_mbox_ext_session_reset(phba); + + /* complete the bsg job if we have it */ + if (job) + job->job_done(job); + + return; +} + +static void +lpfc_bsg_sli_cfg_dma_desc_setup(struct lpfc_hba *phba, enum nemb_type nemb_tp, + uint32_t index, struct lpfc_dmabuf *mbx_dmabuf, + struct lpfc_dmabuf *ext_dmabuf) +{ + struct lpfc_sli_config_mbox *sli_cfg_mbx; + + /* pointer to the start of mailbox command */ + sli_cfg_mbx = (struct lpfc_sli_config_mbox *)mbx_dmabuf->virt; + + if (nemb_tp == nemb_mse) { + if (index == 0) { + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_hi = + putPaddrHigh(mbx_dmabuf->phys + + sizeof(MAILBOX_t)); + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_lo = + putPaddrLow(mbx_dmabuf->phys + + sizeof(MAILBOX_t)); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2943 SLI_CONFIG(mse)[%d], " + "bufLen:%d, addrHi:x%x, addrLo:x%x\n", + index, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].buf_len, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_hi, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_lo); + } else { + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_hi = + putPaddrHigh(ext_dmabuf->phys); + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_lo = + putPaddrLow(ext_dmabuf->phys); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2944 SLI_CONFIG(mse)[%d], " + "bufLen:%d, addrHi:x%x, addrLo:x%x\n", + index, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].buf_len, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_hi, + sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[index].pa_lo); + } + } else { + if (index == 0) { + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_hi = + putPaddrHigh(mbx_dmabuf->phys + + sizeof(MAILBOX_t)); + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_lo = + putPaddrLow(mbx_dmabuf->phys + + sizeof(MAILBOX_t)); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "3007 SLI_CONFIG(hbd)[%d], " + "bufLen:%d, addrHi:x%x, addrLo:x%x\n", + index, + bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len, + &sli_cfg_mbx->un. + sli_config_emb1_subsys.hbd[index]), + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_hi, + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_lo); + + } else { + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_hi = + putPaddrHigh(ext_dmabuf->phys); + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_lo = + putPaddrLow(ext_dmabuf->phys); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "3008 SLI_CONFIG(hbd)[%d], " + "bufLen:%d, addrHi:x%x, addrLo:x%x\n", + index, + bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len, + &sli_cfg_mbx->un. + sli_config_emb1_subsys.hbd[index]), + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_hi, + sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[index].pa_lo); + } + } + return; +} + +/** + * lpfc_bsg_sli_cfg_mse_read_cmd_ext - sli_config non-embedded mailbox cmd read + * @phba: Pointer to HBA context object. + * @mb: Pointer to a BSG mailbox object. + * @nemb_tp: Enumerate of non-embedded mailbox command type. + * @dmabuff: Pointer to a DMA buffer descriptor. + * + * This routine performs SLI_CONFIG (0x9B) read mailbox command operation with + * non-embedded external bufffers. + **/ +static int +lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, + enum nemb_type nemb_tp, + struct lpfc_dmabuf *dmabuf) +{ + struct lpfc_sli_config_mbox *sli_cfg_mbx; + struct dfc_mbox_req *mbox_req; + struct lpfc_dmabuf *curr_dmabuf, *next_dmabuf; + uint32_t ext_buf_cnt, ext_buf_index; + struct lpfc_dmabuf *ext_dmabuf = NULL; + struct bsg_job_data *dd_data = NULL; + LPFC_MBOXQ_t *pmboxq = NULL; + MAILBOX_t *pmb; + uint8_t *pmbx; + int rc, i; + + mbox_req = + (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd; + + /* pointer to the start of mailbox command */ + sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt; + + if (nemb_tp == nemb_mse) { + ext_buf_cnt = bsg_bf_get(lpfc_mbox_hdr_mse_cnt, + &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr); + if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2945 Handled SLI_CONFIG(mse) rd, " + "ext_buf_cnt(%d) out of range(%d)\n", + ext_buf_cnt, + LPFC_MBX_SLI_CONFIG_MAX_MSE); + rc = -ERANGE; + goto job_error; + } + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2941 Handled SLI_CONFIG(mse) rd, " + "ext_buf_cnt:%d\n", ext_buf_cnt); + } else { + /* sanity check on interface type for support */ + if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) { + rc = -ENODEV; + goto job_error; + } + /* nemb_tp == nemb_hbd */ + ext_buf_cnt = sli_cfg_mbx->un.sli_config_emb1_subsys.hbd_count; + if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_HBD) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2946 Handled SLI_CONFIG(hbd) rd, " + "ext_buf_cnt(%d) out of range(%d)\n", + ext_buf_cnt, + LPFC_MBX_SLI_CONFIG_MAX_HBD); + rc = -ERANGE; + goto job_error; + } + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2942 Handled SLI_CONFIG(hbd) rd, " + "ext_buf_cnt:%d\n", ext_buf_cnt); + } + + /* reject non-embedded mailbox command with none external buffer */ + if (ext_buf_cnt == 0) { + rc = -EPERM; + goto job_error; + } else if (ext_buf_cnt > 1) { + /* additional external read buffers */ + for (i = 1; i < ext_buf_cnt; i++) { + ext_dmabuf = lpfc_bsg_dma_page_alloc(phba); + if (!ext_dmabuf) { + rc = -ENOMEM; + goto job_error; + } + list_add_tail(&ext_dmabuf->list, + &phba->mbox_ext_buf_ctx.ext_dmabuf_list); + } + } + + /* bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + rc = -ENOMEM; + goto job_error; + } + + /* mailbox command structure for base driver */ + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto job_error; + } + memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + + /* for the first external buffer */ + lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf); + + /* for the rest of external buffer descriptors if any */ + if (ext_buf_cnt > 1) { + ext_buf_index = 1; + list_for_each_entry_safe(curr_dmabuf, next_dmabuf, + &phba->mbox_ext_buf_ctx.ext_dmabuf_list, list) { + lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, + ext_buf_index, dmabuf, + curr_dmabuf); + ext_buf_index++; + } + } + + /* construct base driver mbox command */ + pmb = &pmboxq->u.mb; + pmbx = (uint8_t *)dmabuf->virt; + memcpy(pmb, pmbx, sizeof(*pmb)); + pmb->mbxOwner = OWN_HOST; + pmboxq->vport = phba->pport; + + /* multi-buffer handling context */ + phba->mbox_ext_buf_ctx.nembType = nemb_tp; + phba->mbox_ext_buf_ctx.mboxType = mbox_rd; + phba->mbox_ext_buf_ctx.numBuf = ext_buf_cnt; + phba->mbox_ext_buf_ctx.mbxTag = mbox_req->extMboxTag; + phba->mbox_ext_buf_ctx.seqNum = mbox_req->extSeqNum; + phba->mbox_ext_buf_ctx.mbx_dmabuf = dmabuf; + + /* callback for multi-buffer read mailbox command */ + pmboxq->mbox_cmpl = lpfc_bsg_issue_read_mbox_ext_cmpl; + + /* context fields to callback function */ + pmboxq->context1 = dd_data; + dd_data->type = TYPE_MBOX; + dd_data->context_un.mbox.pmboxq = pmboxq; + dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx; + dd_data->context_un.mbox.set_job = job; + job->dd_data = dd_data; + + /* state change */ + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT; + + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2947 Issued SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + return 1; + } + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2948 Failed to issue SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + rc = -EPIPE; + +job_error: + if (pmboxq) + mempool_free(pmboxq, phba->mbox_mem_pool); + lpfc_bsg_dma_page_list_free(phba, + &phba->mbox_ext_buf_ctx.ext_dmabuf_list); + kfree(dd_data); + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_IDLE; + return rc; +} + +/** + * lpfc_bsg_sli_cfg_write_cmd_ext - sli_config non-embedded mailbox cmd write + * @phba: Pointer to HBA context object. + * @mb: Pointer to a BSG mailbox object. + * @dmabuff: Pointer to a DMA buffer descriptor. + * + * This routine performs SLI_CONFIG (0x9B) write mailbox command operation with + * non-embedded external bufffers. + **/ +static int +lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, + enum nemb_type nemb_tp, + struct lpfc_dmabuf *dmabuf) +{ + struct dfc_mbox_req *mbox_req; + struct lpfc_sli_config_mbox *sli_cfg_mbx; + uint32_t ext_buf_cnt; + struct bsg_job_data *dd_data = NULL; + LPFC_MBOXQ_t *pmboxq = NULL; + MAILBOX_t *pmb; + uint8_t *mbx; + int rc = 0, i; + + mbox_req = + (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd; + + /* pointer to the start of mailbox command */ + sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt; + + if (nemb_tp == nemb_mse) { + ext_buf_cnt = bsg_bf_get(lpfc_mbox_hdr_mse_cnt, + &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr); + if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_MSE) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2953 Handled SLI_CONFIG(mse) wr, " + "ext_buf_cnt(%d) out of range(%d)\n", + ext_buf_cnt, + LPFC_MBX_SLI_CONFIG_MAX_MSE); + return -ERANGE; + } + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2949 Handled SLI_CONFIG(mse) wr, " + "ext_buf_cnt:%d\n", ext_buf_cnt); + } else { + /* sanity check on interface type for support */ + if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) + return -ENODEV; + /* nemb_tp == nemb_hbd */ + ext_buf_cnt = sli_cfg_mbx->un.sli_config_emb1_subsys.hbd_count; + if (ext_buf_cnt > LPFC_MBX_SLI_CONFIG_MAX_HBD) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2954 Handled SLI_CONFIG(hbd) wr, " + "ext_buf_cnt(%d) out of range(%d)\n", + ext_buf_cnt, + LPFC_MBX_SLI_CONFIG_MAX_HBD); + return -ERANGE; + } + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2950 Handled SLI_CONFIG(hbd) wr, " + "ext_buf_cnt:%d\n", ext_buf_cnt); + } + + if (ext_buf_cnt == 0) + return -EPERM; + + /* for the first external buffer */ + lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf); + + /* log for looking forward */ + for (i = 1; i < ext_buf_cnt; i++) { + if (nemb_tp == nemb_mse) + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2951 SLI_CONFIG(mse), buf[%d]-length:%d\n", + i, sli_cfg_mbx->un.sli_config_emb0_subsys. + mse[i].buf_len); + else + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2952 SLI_CONFIG(hbd), buf[%d]-length:%d\n", + i, bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len, + &sli_cfg_mbx->un.sli_config_emb1_subsys. + hbd[i])); + } + + /* multi-buffer handling context */ + phba->mbox_ext_buf_ctx.nembType = nemb_tp; + phba->mbox_ext_buf_ctx.mboxType = mbox_wr; + phba->mbox_ext_buf_ctx.numBuf = ext_buf_cnt; + phba->mbox_ext_buf_ctx.mbxTag = mbox_req->extMboxTag; + phba->mbox_ext_buf_ctx.seqNum = mbox_req->extSeqNum; + phba->mbox_ext_buf_ctx.mbx_dmabuf = dmabuf; + + if (ext_buf_cnt == 1) { + /* bsg tracking structure */ + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + rc = -ENOMEM; + goto job_error; + } + + /* mailbox command structure for base driver */ + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto job_error; + } + memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + pmb = &pmboxq->u.mb; + mbx = (uint8_t *)dmabuf->virt; + memcpy(pmb, mbx, sizeof(*pmb)); + pmb->mbxOwner = OWN_HOST; + pmboxq->vport = phba->pport; + + /* callback for multi-buffer read mailbox command */ + pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl; + + /* context fields to callback function */ + pmboxq->context1 = dd_data; + dd_data->type = TYPE_MBOX; + dd_data->context_un.mbox.pmboxq = pmboxq; + dd_data->context_un.mbox.mb = (MAILBOX_t *)mbx; + dd_data->context_un.mbox.set_job = job; + job->dd_data = dd_data; + + /* state change */ + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT; + + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2955 Issued SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + return 1; + } + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2956 Failed to issue SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + rc = -EPIPE; + } + +job_error: + if (pmboxq) + mempool_free(pmboxq, phba->mbox_mem_pool); + kfree(dd_data); + + return rc; +} + +/** + * lpfc_bsg_handle_sli_cfg_mbox - handle sli-cfg mailbox cmd with ext buffer + * @phba: Pointer to HBA context object. + * @mb: Pointer to a BSG mailbox object. + * @dmabuff: Pointer to a DMA buffer descriptor. + * + * This routine handles SLI_CONFIG (0x9B) mailbox command with non-embedded + * external bufffers, including both 0x9B with non-embedded MSEs and 0x9B + * with embedded sussystem 0x1 and opcodes with external HBDs. + **/ +static int +lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, + struct lpfc_dmabuf *dmabuf) +{ + struct lpfc_sli_config_mbox *sli_cfg_mbx; + uint32_t subsys; + uint32_t opcode; + int rc = SLI_CONFIG_NOT_HANDLED; + + /* state change */ + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_HOST; + + sli_cfg_mbx = (struct lpfc_sli_config_mbox *)dmabuf->virt; + + if (!bsg_bf_get(lpfc_mbox_hdr_emb, + &sli_cfg_mbx->un.sli_config_emb0_subsys.sli_config_hdr)) { + subsys = bsg_bf_get(lpfc_emb0_subcmnd_subsys, + &sli_cfg_mbx->un.sli_config_emb0_subsys); + opcode = bsg_bf_get(lpfc_emb0_subcmnd_opcode, + &sli_cfg_mbx->un.sli_config_emb0_subsys); + if (subsys == SLI_CONFIG_SUBSYS_FCOE) { + switch (opcode) { + case FCOE_OPCODE_READ_FCF: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2957 Handled SLI_CONFIG " + "subsys_fcoe, opcode:x%x\n", + opcode); + rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job, + nemb_mse, dmabuf); + break; + case FCOE_OPCODE_ADD_FCF: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2958 Handled SLI_CONFIG " + "subsys_fcoe, opcode:x%x\n", + opcode); + rc = lpfc_bsg_sli_cfg_write_cmd_ext(phba, job, + nemb_mse, dmabuf); + break; + default: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2959 Not handled SLI_CONFIG " + "subsys_fcoe, opcode:x%x\n", + opcode); + rc = SLI_CONFIG_NOT_HANDLED; + break; + } + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2977 Handled SLI_CONFIG " + "subsys:x%d, opcode:x%x\n", + subsys, opcode); + rc = SLI_CONFIG_NOT_HANDLED; + } + } else { + subsys = bsg_bf_get(lpfc_emb1_subcmnd_subsys, + &sli_cfg_mbx->un.sli_config_emb1_subsys); + opcode = bsg_bf_get(lpfc_emb1_subcmnd_opcode, + &sli_cfg_mbx->un.sli_config_emb1_subsys); + if (subsys == SLI_CONFIG_SUBSYS_COMN) { + switch (opcode) { + case COMN_OPCODE_READ_OBJECT: + case COMN_OPCODE_READ_OBJECT_LIST: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2960 Handled SLI_CONFIG " + "subsys_comn, opcode:x%x\n", + opcode); + rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job, + nemb_hbd, dmabuf); + break; + case COMN_OPCODE_WRITE_OBJECT: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2961 Handled SLI_CONFIG " + "subsys_comn, opcode:x%x\n", + opcode); + rc = lpfc_bsg_sli_cfg_write_cmd_ext(phba, job, + nemb_hbd, dmabuf); + break; + default: + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2962 Not handled SLI_CONFIG " + "subsys_comn, opcode:x%x\n", + opcode); + rc = SLI_CONFIG_NOT_HANDLED; + break; + } + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2978 Handled SLI_CONFIG " + "subsys:x%d, opcode:x%x\n", + subsys, opcode); + rc = SLI_CONFIG_NOT_HANDLED; + } + } + return rc; +} + +/** + * lpfc_bsg_mbox_ext_abort_req - request to abort mbox command with ext buffers + * @phba: Pointer to HBA context object. + * + * This routine is for requesting to abort a pass-through mailbox command with + * multiple external buffers due to error condition. + **/ +static void +lpfc_bsg_mbox_ext_abort(struct lpfc_hba *phba) +{ + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT) + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS; + else + lpfc_bsg_mbox_ext_session_reset(phba); + return; +} + +/** + * lpfc_bsg_read_ebuf_get - get the next mailbox read external buffer + * @phba: Pointer to HBA context object. + * @dmabuf: Pointer to a DMA buffer descriptor. + * + * This routine extracts the next mailbox read external buffer back to + * user space through BSG. + **/ +static int +lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job) +{ + struct lpfc_sli_config_mbox *sli_cfg_mbx; + struct lpfc_dmabuf *dmabuf; + uint8_t *pbuf; + uint32_t size; + uint32_t index; + + index = phba->mbox_ext_buf_ctx.seqNum; + phba->mbox_ext_buf_ctx.seqNum++; + + sli_cfg_mbx = (struct lpfc_sli_config_mbox *) + phba->mbox_ext_buf_ctx.mbx_dmabuf->virt; + + if (phba->mbox_ext_buf_ctx.nembType == nemb_mse) { + size = bsg_bf_get(lpfc_mbox_sli_config_mse_len, + &sli_cfg_mbx->un.sli_config_emb0_subsys.mse[index]); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2963 SLI_CONFIG (mse) ext-buffer rd get " + "buffer[%d], size:%d\n", index, size); + } else { + size = bsg_bf_get(lpfc_mbox_sli_config_ecmn_hbd_len, + &sli_cfg_mbx->un.sli_config_emb1_subsys.hbd[index]); + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2964 SLI_CONFIG (hbd) ext-buffer rd get " + "buffer[%d], size:%d\n", index, size); + } + if (list_empty(&phba->mbox_ext_buf_ctx.ext_dmabuf_list)) + return -EPIPE; + dmabuf = list_first_entry(&phba->mbox_ext_buf_ctx.ext_dmabuf_list, + struct lpfc_dmabuf, list); + list_del_init(&dmabuf->list); + pbuf = (uint8_t *)dmabuf->virt; + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + pbuf, size); + + lpfc_bsg_dma_page_free(phba, dmabuf); + + if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2965 SLI_CONFIG (hbd) ext-buffer rd mbox " + "command session done\n"); + lpfc_bsg_mbox_ext_session_reset(phba); + } + + job->reply->result = 0; + job->job_done(job); + + return SLI_CONFIG_HANDLED; +} + +/** + * lpfc_bsg_write_ebuf_set - set the next mailbox write external buffer + * @phba: Pointer to HBA context object. + * @dmabuf: Pointer to a DMA buffer descriptor. + * + * This routine sets up the next mailbox read external buffer obtained + * from user space through BSG. + **/ +static int +lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job, + struct lpfc_dmabuf *dmabuf) +{ + struct lpfc_sli_config_mbox *sli_cfg_mbx; + struct bsg_job_data *dd_data = NULL; + LPFC_MBOXQ_t *pmboxq = NULL; + MAILBOX_t *pmb; + enum nemb_type nemb_tp; + uint8_t *pbuf; + uint32_t size; + uint32_t index; + int rc; + + index = phba->mbox_ext_buf_ctx.seqNum; + phba->mbox_ext_buf_ctx.seqNum++; + nemb_tp = phba->mbox_ext_buf_ctx.nembType; + + sli_cfg_mbx = (struct lpfc_sli_config_mbox *) + phba->mbox_ext_buf_ctx.mbx_dmabuf->virt; + + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + rc = -ENOMEM; + goto job_error; + } + + pbuf = (uint8_t *)dmabuf->virt; + size = job->request_payload.payload_len; + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + pbuf, size); + + if (phba->mbox_ext_buf_ctx.nembType == nemb_mse) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2966 SLI_CONFIG (mse) ext-buffer wr set " + "buffer[%d], size:%d\n", + phba->mbox_ext_buf_ctx.seqNum, size); + + } else { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2967 SLI_CONFIG (hbd) ext-buffer wr set " + "buffer[%d], size:%d\n", + phba->mbox_ext_buf_ctx.seqNum, size); + + } + + /* set up external buffer descriptor and add to external buffer list */ + lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, index, + phba->mbox_ext_buf_ctx.mbx_dmabuf, + dmabuf); + list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list); + + if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2968 SLI_CONFIG ext-buffer wr all %d " + "ebuffers received\n", + phba->mbox_ext_buf_ctx.numBuf); + /* mailbox command structure for base driver */ + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!pmboxq) { + rc = -ENOMEM; + goto job_error; + } + memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); + pbuf = (uint8_t *)phba->mbox_ext_buf_ctx.mbx_dmabuf->virt; + pmb = &pmboxq->u.mb; + memcpy(pmb, pbuf, sizeof(*pmb)); + pmb->mbxOwner = OWN_HOST; + pmboxq->vport = phba->pport; + + /* callback for multi-buffer write mailbox command */ + pmboxq->mbox_cmpl = lpfc_bsg_issue_write_mbox_ext_cmpl; + + /* context fields to callback function */ + pmboxq->context1 = dd_data; + dd_data->type = TYPE_MBOX; + dd_data->context_un.mbox.pmboxq = pmboxq; + dd_data->context_un.mbox.mb = (MAILBOX_t *)pbuf; + dd_data->context_un.mbox.set_job = job; + job->dd_data = dd_data; + + /* state change */ + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_PORT; + + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2969 Issued SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + return 1; + } + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2970 Failed to issue SLI_CONFIG ext-buffer " + "maibox command, rc:x%x\n", rc); + rc = -EPIPE; + goto job_error; + } + + /* wait for additoinal external buffers */ + job->reply->result = 0; + job->job_done(job); + return SLI_CONFIG_HANDLED; + +job_error: + lpfc_bsg_dma_page_free(phba, dmabuf); + kfree(dd_data); + + return rc; +} + +/** + * lpfc_bsg_handle_sli_cfg_ebuf - handle ext buffer with sli-cfg mailbox cmd + * @phba: Pointer to HBA context object. + * @mb: Pointer to a BSG mailbox object. + * @dmabuff: Pointer to a DMA buffer descriptor. + * + * This routine handles the external buffer with SLI_CONFIG (0x9B) mailbox + * command with multiple non-embedded external buffers. + **/ +static int +lpfc_bsg_handle_sli_cfg_ebuf(struct lpfc_hba *phba, struct fc_bsg_job *job, + struct lpfc_dmabuf *dmabuf) +{ + int rc; + + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2971 SLI_CONFIG buffer (type:x%x)\n", + phba->mbox_ext_buf_ctx.mboxType); + + if (phba->mbox_ext_buf_ctx.mboxType == mbox_rd) { + if (phba->mbox_ext_buf_ctx.state != LPFC_BSG_MBOX_DONE) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2972 SLI_CONFIG rd buffer state " + "mismatch:x%x\n", + phba->mbox_ext_buf_ctx.state); + lpfc_bsg_mbox_ext_abort(phba); + return -EPIPE; + } + rc = lpfc_bsg_read_ebuf_get(phba, job); + if (rc == SLI_CONFIG_HANDLED) + lpfc_bsg_dma_page_free(phba, dmabuf); + } else { /* phba->mbox_ext_buf_ctx.mboxType == mbox_wr */ + if (phba->mbox_ext_buf_ctx.state != LPFC_BSG_MBOX_HOST) { + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2973 SLI_CONFIG wr buffer state " + "mismatch:x%x\n", + phba->mbox_ext_buf_ctx.state); + lpfc_bsg_mbox_ext_abort(phba); + return -EPIPE; + } + rc = lpfc_bsg_write_ebuf_set(phba, job, dmabuf); + } + return rc; +} + +/** + * lpfc_bsg_handle_sli_cfg_ext - handle sli-cfg mailbox with external buffer + * @phba: Pointer to HBA context object. + * @mb: Pointer to a BSG mailbox object. + * @dmabuff: Pointer to a DMA buffer descriptor. + * + * This routine checkes and handles non-embedded multi-buffer SLI_CONFIG + * (0x9B) mailbox commands and external buffers. + **/ +static int +lpfc_bsg_handle_sli_cfg_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, + struct lpfc_dmabuf *dmabuf) +{ + struct dfc_mbox_req *mbox_req; + int rc; + + mbox_req = + (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd; + + /* mbox command with/without single external buffer */ + if (mbox_req->extMboxTag == 0 && mbox_req->extSeqNum == 0) + return SLI_CONFIG_NOT_HANDLED; + + /* mbox command and first external buffer */ + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_IDLE) { + if (mbox_req->extSeqNum == 1) { + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2974 SLI_CONFIG mailbox: tag:%d, " + "seq:%d\n", mbox_req->extMboxTag, + mbox_req->extSeqNum); + rc = lpfc_bsg_handle_sli_cfg_mbox(phba, job, dmabuf); + return rc; + } else + goto sli_cfg_ext_error; + } + + /* + * handle additional external buffers + */ + + /* check broken pipe conditions */ + if (mbox_req->extMboxTag != phba->mbox_ext_buf_ctx.mbxTag) + goto sli_cfg_ext_error; + if (mbox_req->extSeqNum > phba->mbox_ext_buf_ctx.numBuf) + goto sli_cfg_ext_error; + if (mbox_req->extSeqNum != phba->mbox_ext_buf_ctx.seqNum + 1) + goto sli_cfg_ext_error; + + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2975 SLI_CONFIG mailbox external buffer: " + "extSta:x%x, tag:%d, seq:%d\n", + phba->mbox_ext_buf_ctx.state, mbox_req->extMboxTag, + mbox_req->extSeqNum); + rc = lpfc_bsg_handle_sli_cfg_ebuf(phba, job, dmabuf); + return rc; + +sli_cfg_ext_error: + /* all other cases, broken pipe */ + lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, + "2976 SLI_CONFIG mailbox broken pipe: " + "ctxSta:x%x, ctxNumBuf:%d " + "ctxTag:%d, ctxSeq:%d, tag:%d, seq:%d\n", + phba->mbox_ext_buf_ctx.state, + phba->mbox_ext_buf_ctx.numBuf, + phba->mbox_ext_buf_ctx.mbxTag, + phba->mbox_ext_buf_ctx.seqNum, + mbox_req->extMboxTag, mbox_req->extSeqNum); + + lpfc_bsg_mbox_ext_session_reset(phba); + + return -EPIPE; +} + +/** * lpfc_bsg_issue_mbox - issues a mailbox command on behalf of an app * @phba: Pointer to HBA context object. * @mb: Pointer to a mailbox object. @@ -2638,22 +4118,21 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */ MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */ /* a 4k buffer to hold the mb and extended data from/to the bsg */ - MAILBOX_t *mb = NULL; + uint8_t *pmbx = NULL; struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */ - uint32_t size; - struct lpfc_dmabuf *rxbmp = NULL; /* for biu diag */ - struct lpfc_dmabufext *dmp = NULL; /* for biu diag */ - struct ulp_bde64 *rxbpl = NULL; - struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *) - job->request->rqst_data.h_vendor.vendor_cmd; + struct lpfc_dmabuf *dmabuf = NULL; + struct dfc_mbox_req *mbox_req; struct READ_EVENT_LOG_VAR *rdEventLog; uint32_t transmit_length, receive_length, mode; + struct lpfc_mbx_sli4_config *sli4_config; struct lpfc_mbx_nembed_cmd *nembed_sge; struct mbox_header *header; struct ulp_bde64 *bde; uint8_t *ext = NULL; int rc = 0; uint8_t *from; + uint32_t size; + /* in case no data is transferred */ job->reply->reply_payload_rcv_len = 0; @@ -2665,6 +4144,18 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, goto job_done; } + /* + * Don't allow mailbox commands to be sent when blocked or when in + * the middle of discovery + */ + if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { + rc = -EAGAIN; + goto job_done; + } + + mbox_req = + (struct dfc_mbox_req *)job->request->rqst_data.h_vendor.vendor_cmd; + /* check if requested extended data lengths are valid */ if ((mbox_req->inExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t)) || (mbox_req->outExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t))) { @@ -2672,6 +4163,32 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, goto job_done; } + dmabuf = lpfc_bsg_dma_page_alloc(phba); + if (!dmabuf || !dmabuf->virt) { + rc = -ENOMEM; + goto job_done; + } + + /* Get the mailbox command or external buffer from BSG */ + pmbx = (uint8_t *)dmabuf->virt; + size = job->request_payload.payload_len; + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, pmbx, size); + + /* Handle possible SLI_CONFIG with non-embedded payloads */ + if (phba->sli_rev == LPFC_SLI_REV4) { + rc = lpfc_bsg_handle_sli_cfg_ext(phba, job, dmabuf); + if (rc == SLI_CONFIG_HANDLED) + goto job_cont; + if (rc) + goto job_done; + /* SLI_CONFIG_NOT_HANDLED for other mailbox commands */ + } + + rc = lpfc_bsg_check_cmd_access(phba, (MAILBOX_t *)pmbx, vport); + if (rc != 0) + goto job_done; /* must be negative */ + /* allocate our bsg tracking structure */ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); if (!dd_data) { @@ -2681,12 +4198,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, goto job_done; } - mb = kzalloc(BSG_MBOX_SIZE, GFP_KERNEL); - if (!mb) { - rc = -ENOMEM; - goto job_done; - } - pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) { rc = -ENOMEM; @@ -2694,17 +4205,8 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, } memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); - size = job->request_payload.payload_len; - sg_copy_to_buffer(job->request_payload.sg_list, - job->request_payload.sg_cnt, - mb, size); - - rc = lpfc_bsg_check_cmd_access(phba, mb, vport); - if (rc != 0) - goto job_done; /* must be negative */ - pmb = &pmboxq->u.mb; - memcpy(pmb, mb, sizeof(*pmb)); + memcpy(pmb, pmbx, sizeof(*pmb)); pmb->mbxOwner = OWN_HOST; pmboxq->vport = vport; @@ -2721,30 +4223,13 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, "0x%x while in stopped state.\n", pmb->mbxCommand); - /* Don't allow mailbox commands to be sent when blocked - * or when in the middle of discovery - */ - if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { - rc = -EAGAIN; - goto job_done; - } - /* extended mailbox commands will need an extended buffer */ if (mbox_req->inExtWLen || mbox_req->outExtWLen) { - ext = kzalloc(MAILBOX_EXT_SIZE, GFP_KERNEL); - if (!ext) { - rc = -ENOMEM; - goto job_done; - } - /* any data for the device? */ if (mbox_req->inExtWLen) { - from = (uint8_t *)mb; - from += sizeof(MAILBOX_t); - memcpy((uint8_t *)ext, from, - mbox_req->inExtWLen * sizeof(uint32_t)); + from = pmbx; + ext = from + sizeof(MAILBOX_t); } - pmboxq->context2 = ext; pmboxq->in_ext_byte_len = mbox_req->inExtWLen * sizeof(uint32_t); @@ -2768,46 +4253,17 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, rc = -ERANGE; goto job_done; } - - rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!rxbmp) { - rc = -ENOMEM; - goto job_done; - } - - rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - if (!rxbmp->virt) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&rxbmp->list); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - dmp = diag_cmd_data_alloc(phba, rxbpl, transmit_length, 0); - if (!dmp) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&dmp->dma.list); pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh = - putPaddrHigh(dmp->dma.phys); + putPaddrHigh(dmabuf->phys + sizeof(MAILBOX_t)); pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow = - putPaddrLow(dmp->dma.phys); + putPaddrLow(dmabuf->phys + sizeof(MAILBOX_t)); pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh = - putPaddrHigh(dmp->dma.phys + - pmb->un.varBIUdiag.un.s2. - xmit_bde64.tus.f.bdeSize); + putPaddrHigh(dmabuf->phys + sizeof(MAILBOX_t) + + pmb->un.varBIUdiag.un.s2.xmit_bde64.tus.f.bdeSize); pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow = - putPaddrLow(dmp->dma.phys + - pmb->un.varBIUdiag.un.s2. - xmit_bde64.tus.f.bdeSize); - - /* copy the transmit data found in the mailbox extension area */ - from = (uint8_t *)mb; - from += sizeof(MAILBOX_t); - memcpy((uint8_t *)dmp->dma.virt, from, transmit_length); + putPaddrLow(dmabuf->phys + sizeof(MAILBOX_t) + + pmb->un.varBIUdiag.un.s2.xmit_bde64.tus.f.bdeSize); } else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) { rdEventLog = &pmb->un.varRdEventLog; receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize; @@ -2823,33 +4279,10 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, /* mode zero uses a bde like biu diags command */ if (mode == 0) { - - /* rebuild the command for sli4 using our own buffers - * like we do for biu diags - */ - - rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!rxbmp) { - rc = -ENOMEM; - goto job_done; - } - - rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - if (rxbpl) { - INIT_LIST_HEAD(&rxbmp->list); - dmp = diag_cmd_data_alloc(phba, rxbpl, - receive_length, 0); - } - - if (!dmp) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&dmp->dma.list); - pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); - pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); + pmb->un.varWords[3] = putPaddrLow(dmabuf->phys + + sizeof(MAILBOX_t)); + pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys + + sizeof(MAILBOX_t)); } } else if (phba->sli_rev == LPFC_SLI_REV4) { if (pmb->mbxCommand == MBX_DUMP_MEMORY) { @@ -2860,36 +4293,14 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, /* receive length cannot be greater than mailbox * extension size */ - if ((receive_length == 0) || - (receive_length > MAILBOX_EXT_SIZE)) { + if (receive_length == 0) { rc = -ERANGE; goto job_done; } - - rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!rxbmp) { - rc = -ENOMEM; - goto job_done; - } - - rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - if (!rxbmp->virt) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&rxbmp->list); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - dmp = diag_cmd_data_alloc(phba, rxbpl, receive_length, - 0); - if (!dmp) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&dmp->dma.list); - pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); - pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); + pmb->un.varWords[3] = putPaddrLow(dmabuf->phys + + sizeof(MAILBOX_t)); + pmb->un.varWords[4] = putPaddrHigh(dmabuf->phys + + sizeof(MAILBOX_t)); } else if ((pmb->mbxCommand == MBX_UPDATE_CFG) && pmb->un.varUpdateCfg.co) { bde = (struct ulp_bde64 *)&pmb->un.varWords[4]; @@ -2899,102 +4310,53 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, rc = -ERANGE; goto job_done; } - - rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!rxbmp) { - rc = -ENOMEM; - goto job_done; - } - - rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - if (!rxbmp->virt) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&rxbmp->list); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - dmp = diag_cmd_data_alloc(phba, rxbpl, - bde->tus.f.bdeSize, 0); - if (!dmp) { - rc = -ENOMEM; - goto job_done; - } - - INIT_LIST_HEAD(&dmp->dma.list); - bde->addrHigh = putPaddrHigh(dmp->dma.phys); - bde->addrLow = putPaddrLow(dmp->dma.phys); - - /* copy the transmit data found in the mailbox - * extension area - */ - from = (uint8_t *)mb; - from += sizeof(MAILBOX_t); - memcpy((uint8_t *)dmp->dma.virt, from, - bde->tus.f.bdeSize); + bde->addrHigh = putPaddrHigh(dmabuf->phys + + sizeof(MAILBOX_t)); + bde->addrLow = putPaddrLow(dmabuf->phys + + sizeof(MAILBOX_t)); } else if (pmb->mbxCommand == MBX_SLI4_CONFIG) { - /* rebuild the command for sli4 using our own buffers - * like we do for biu diags - */ - header = (struct mbox_header *)&pmb->un.varWords[0]; - nembed_sge = (struct lpfc_mbx_nembed_cmd *) - &pmb->un.varWords[0]; - receive_length = nembed_sge->sge[0].length; - - /* receive length cannot be greater than mailbox - * extension size - */ - if ((receive_length == 0) || - (receive_length > MAILBOX_EXT_SIZE)) { - rc = -ERANGE; - goto job_done; - } - - rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!rxbmp) { - rc = -ENOMEM; - goto job_done; - } - - rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - if (!rxbmp->virt) { - rc = -ENOMEM; - goto job_done; - } + /* Handling non-embedded SLI_CONFIG mailbox command */ + sli4_config = &pmboxq->u.mqe.un.sli4_config; + if (!bf_get(lpfc_mbox_hdr_emb, + &sli4_config->header.cfg_mhdr)) { + /* rebuild the command for sli4 using our + * own buffers like we do for biu diags + */ + header = (struct mbox_header *) + &pmb->un.varWords[0]; + nembed_sge = (struct lpfc_mbx_nembed_cmd *) + &pmb->un.varWords[0]; + receive_length = nembed_sge->sge[0].length; + + /* receive length cannot be greater than + * mailbox extension size + */ + if ((receive_length == 0) || + (receive_length > MAILBOX_EXT_SIZE)) { + rc = -ERANGE; + goto job_done; + } - INIT_LIST_HEAD(&rxbmp->list); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - dmp = diag_cmd_data_alloc(phba, rxbpl, receive_length, - 0); - if (!dmp) { - rc = -ENOMEM; - goto job_done; + nembed_sge->sge[0].pa_hi = + putPaddrHigh(dmabuf->phys + + sizeof(MAILBOX_t)); + nembed_sge->sge[0].pa_lo = + putPaddrLow(dmabuf->phys + + sizeof(MAILBOX_t)); } - - INIT_LIST_HEAD(&dmp->dma.list); - nembed_sge->sge[0].pa_hi = putPaddrHigh(dmp->dma.phys); - nembed_sge->sge[0].pa_lo = putPaddrLow(dmp->dma.phys); - /* copy the transmit data found in the mailbox - * extension area - */ - from = (uint8_t *)mb; - from += sizeof(MAILBOX_t); - memcpy((uint8_t *)dmp->dma.virt, from, - header->cfg_mhdr.payload_length); } } - dd_data->context_un.mbox.rxbmp = rxbmp; - dd_data->context_un.mbox.dmp = dmp; + dd_data->context_un.mbox.dmabuffers = dmabuf; /* setup wake call as IOCB callback */ - pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait; + pmboxq->mbox_cmpl = lpfc_bsg_issue_mbox_cmpl; /* setup context field to pass wait_queue pointer to wake function */ pmboxq->context1 = dd_data; dd_data->type = TYPE_MBOX; dd_data->context_un.mbox.pmboxq = pmboxq; - dd_data->context_un.mbox.mb = mb; + dd_data->context_un.mbox.mb = (MAILBOX_t *)pmbx; dd_data->context_un.mbox.set_job = job; dd_data->context_un.mbox.ext = ext; dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset; @@ -3011,11 +4373,11 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, } /* job finished, copy the data */ - memcpy(mb, pmb, sizeof(*pmb)); + memcpy(pmbx, pmb, sizeof(*pmb)); job->reply->reply_payload_rcv_len = sg_copy_from_buffer(job->reply_payload.sg_list, - job->reply_payload.sg_cnt, - mb, size); + job->reply_payload.sg_cnt, + pmbx, size); /* not waiting mbox already done */ rc = 0; goto job_done; @@ -3027,22 +4389,12 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, job_done: /* common exit for error or job completed inline */ - kfree(mb); if (pmboxq) mempool_free(pmboxq, phba->mbox_mem_pool); - kfree(ext); - if (dmp) { - dma_free_coherent(&phba->pcidev->dev, - dmp->size, dmp->dma.virt, - dmp->dma.phys); - kfree(dmp); - } - if (rxbmp) { - lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys); - kfree(rxbmp); - } + lpfc_bsg_dma_page_free(phba, dmabuf); kfree(dd_data); +job_cont: return rc; } @@ -3055,37 +4407,28 @@ lpfc_bsg_mbox_cmd(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; + struct dfc_mbox_req *mbox_req; int rc = 0; - /* in case no data is transferred */ + /* mix-and-match backward compatibility */ job->reply->reply_payload_rcv_len = 0; if (job->request_len < sizeof(struct fc_bsg_request) + sizeof(struct dfc_mbox_req)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2737 Received MBOX_REQ request below " - "minimum size\n"); - rc = -EINVAL; - goto job_error; - } - - if (job->request_payload.payload_len != BSG_MBOX_SIZE) { - rc = -EINVAL; - goto job_error; - } - - if (job->reply_payload.payload_len != BSG_MBOX_SIZE) { - rc = -EINVAL; - goto job_error; - } - - if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { - rc = -EAGAIN; - goto job_error; + lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, + "2737 Mix-and-match backward compability " + "between MBOX_REQ old size:%d and " + "new request size:%d\n", + (int)(job->request_len - + sizeof(struct fc_bsg_request)), + (int)sizeof(struct dfc_mbox_req)); + mbox_req = (struct dfc_mbox_req *) + job->request->rqst_data.h_vendor.vendor_cmd; + mbox_req->extMboxTag = 0; + mbox_req->extSeqNum = 0; } rc = lpfc_bsg_issue_mbox(phba, job, vport); -job_error: if (rc == 0) { /* job done */ job->reply->result = 0; @@ -3416,10 +4759,16 @@ lpfc_bsg_hst_vendor(struct fc_bsg_job *job) rc = lpfc_bsg_send_mgmt_rsp(job); break; case LPFC_BSG_VENDOR_DIAG_MODE: - rc = lpfc_bsg_diag_mode(job); + rc = lpfc_bsg_diag_loopback_mode(job); + break; + case LPFC_BSG_VENDOR_DIAG_MODE_END: + rc = lpfc_sli4_bsg_diag_mode_end(job); + break; + case LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK: + rc = lpfc_bsg_diag_loopback_run(job); break; - case LPFC_BSG_VENDOR_DIAG_TEST: - rc = lpfc_bsg_diag_test(job); + case LPFC_BSG_VENDOR_LINK_DIAG_TEST: + rc = lpfc_sli4_bsg_link_diag_test(job); break; case LPFC_BSG_VENDOR_GET_MGMT_REV: rc = lpfc_bsg_get_dfc_rev(job); @@ -3538,6 +4887,8 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) /* the mbox completion handler can now be run */ spin_unlock_irqrestore(&phba->ct_ev_lock, flags); job->job_done(job); + if (phba->mbox_ext_buf_ctx.state == LPFC_BSG_MBOX_PORT) + phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS; break; case TYPE_MENLO: menlo = &dd_data->context_un.menlo; diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index b542aca6f5ae..c8c2b47ea886 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -24,15 +24,17 @@ * These are the vendor unique structures passed in using the bsg * FC_BSG_HST_VENDOR message code type. */ -#define LPFC_BSG_VENDOR_SET_CT_EVENT 1 -#define LPFC_BSG_VENDOR_GET_CT_EVENT 2 -#define LPFC_BSG_VENDOR_SEND_MGMT_RESP 3 -#define LPFC_BSG_VENDOR_DIAG_MODE 4 -#define LPFC_BSG_VENDOR_DIAG_TEST 5 -#define LPFC_BSG_VENDOR_GET_MGMT_REV 6 -#define LPFC_BSG_VENDOR_MBOX 7 -#define LPFC_BSG_VENDOR_MENLO_CMD 8 -#define LPFC_BSG_VENDOR_MENLO_DATA 9 +#define LPFC_BSG_VENDOR_SET_CT_EVENT 1 +#define LPFC_BSG_VENDOR_GET_CT_EVENT 2 +#define LPFC_BSG_VENDOR_SEND_MGMT_RESP 3 +#define LPFC_BSG_VENDOR_DIAG_MODE 4 +#define LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK 5 +#define LPFC_BSG_VENDOR_GET_MGMT_REV 6 +#define LPFC_BSG_VENDOR_MBOX 7 +#define LPFC_BSG_VENDOR_MENLO_CMD 8 +#define LPFC_BSG_VENDOR_MENLO_DATA 9 +#define LPFC_BSG_VENDOR_DIAG_MODE_END 10 +#define LPFC_BSG_VENDOR_LINK_DIAG_TEST 11 struct set_ct_event { uint32_t command; @@ -67,10 +69,25 @@ struct diag_mode_set { uint32_t timeout; }; +struct sli4_link_diag { + uint32_t command; + uint32_t timeout; + uint32_t test_id; + uint32_t loops; + uint32_t test_version; + uint32_t error_action; +}; + struct diag_mode_test { uint32_t command; }; +struct diag_status { + uint32_t mbox_status; + uint32_t shdr_status; + uint32_t shdr_add_status; +}; + #define LPFC_WWNN_TYPE 0 #define LPFC_WWPN_TYPE 1 @@ -92,11 +109,15 @@ struct get_mgmt_rev_reply { }; #define BSG_MBOX_SIZE 4096 /* mailbox command plus extended data */ + +/* BSG mailbox request header */ struct dfc_mbox_req { uint32_t command; uint32_t mbOffset; uint32_t inExtWLen; uint32_t outExtWLen; + uint32_t extMboxTag; + uint32_t extSeqNum; }; /* Used for menlo command or menlo data. The xri is only used for menlo data */ @@ -171,7 +192,7 @@ struct lpfc_sli_config_mse { #define lpfc_mbox_sli_config_mse_len_WORD buf_len }; -struct lpfc_sli_config_subcmd_hbd { +struct lpfc_sli_config_hbd { uint32_t buf_len; #define lpfc_mbox_sli_config_ecmn_hbd_len_SHIFT 0 #define lpfc_mbox_sli_config_ecmn_hbd_len_MASK 0xffffff @@ -194,21 +215,39 @@ struct lpfc_sli_config_hdr { uint32_t reserved5; }; -struct lpfc_sli_config_generic { +struct lpfc_sli_config_emb0_subsys { struct lpfc_sli_config_hdr sli_config_hdr; #define LPFC_MBX_SLI_CONFIG_MAX_MSE 19 struct lpfc_sli_config_mse mse[LPFC_MBX_SLI_CONFIG_MAX_MSE]; + uint32_t padding; + uint32_t word64; +#define lpfc_emb0_subcmnd_opcode_SHIFT 0 +#define lpfc_emb0_subcmnd_opcode_MASK 0xff +#define lpfc_emb0_subcmnd_opcode_WORD word64 +#define lpfc_emb0_subcmnd_subsys_SHIFT 8 +#define lpfc_emb0_subcmnd_subsys_MASK 0xff +#define lpfc_emb0_subcmnd_subsys_WORD word64 +/* Subsystem FCOE (0x0C) OpCodes */ +#define SLI_CONFIG_SUBSYS_FCOE 0x0C +#define FCOE_OPCODE_READ_FCF 0x08 +#define FCOE_OPCODE_ADD_FCF 0x09 }; -struct lpfc_sli_config_subcmnd { +struct lpfc_sli_config_emb1_subsys { struct lpfc_sli_config_hdr sli_config_hdr; uint32_t word6; -#define lpfc_subcmnd_opcode_SHIFT 0 -#define lpfc_subcmnd_opcode_MASK 0xff -#define lpfc_subcmnd_opcode_WORD word6 -#define lpfc_subcmnd_subsys_SHIFT 8 -#define lpfc_subcmnd_subsys_MASK 0xff -#define lpfc_subcmnd_subsys_WORD word6 +#define lpfc_emb1_subcmnd_opcode_SHIFT 0 +#define lpfc_emb1_subcmnd_opcode_MASK 0xff +#define lpfc_emb1_subcmnd_opcode_WORD word6 +#define lpfc_emb1_subcmnd_subsys_SHIFT 8 +#define lpfc_emb1_subcmnd_subsys_MASK 0xff +#define lpfc_emb1_subcmnd_subsys_WORD word6 +/* Subsystem COMN (0x01) OpCodes */ +#define SLI_CONFIG_SUBSYS_COMN 0x01 +#define COMN_OPCODE_READ_OBJECT 0xAB +#define COMN_OPCODE_WRITE_OBJECT 0xAC +#define COMN_OPCODE_READ_OBJECT_LIST 0xAD +#define COMN_OPCODE_DELETE_OBJECT 0xAE uint32_t timeout; uint32_t request_length; uint32_t word9; @@ -222,8 +261,8 @@ struct lpfc_sli_config_subcmnd { uint32_t rd_offset; uint32_t obj_name[26]; uint32_t hbd_count; -#define LPFC_MBX_SLI_CONFIG_MAX_HBD 10 - struct lpfc_sli_config_subcmd_hbd hbd[LPFC_MBX_SLI_CONFIG_MAX_HBD]; +#define LPFC_MBX_SLI_CONFIG_MAX_HBD 8 + struct lpfc_sli_config_hbd hbd[LPFC_MBX_SLI_CONFIG_MAX_HBD]; }; struct lpfc_sli_config_mbox { @@ -235,7 +274,11 @@ struct lpfc_sli_config_mbox { #define lpfc_mqe_command_MASK 0x000000FF #define lpfc_mqe_command_WORD word0 union { - struct lpfc_sli_config_generic sli_config_generic; - struct lpfc_sli_config_subcmnd sli_config_subcmnd; + struct lpfc_sli_config_emb0_subsys sli_config_emb0_subsys; + struct lpfc_sli_config_emb1_subsys sli_config_emb1_subsys; } un; }; + +/* driver only */ +#define SLI_CONFIG_NOT_HANDLED 0 +#define SLI_CONFIG_HANDLED 1 diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index f0b332f4eedb..fc20c247f36b 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -55,6 +55,8 @@ void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *); void lpfc_supported_pages(struct lpfcMboxq *); void lpfc_pc_sli4_params(struct lpfcMboxq *); int lpfc_pc_sli4_params_get(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_sli4_mbox_rsrc_extent(struct lpfc_hba *, struct lpfcMboxq *, + uint16_t, uint16_t, bool); int lpfc_get_sli4_parameters(struct lpfc_hba *, LPFC_MBOXQ_t *); struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t); void lpfc_cleanup_rcv_buffers(struct lpfc_vport *); @@ -171,6 +173,7 @@ void lpfc_delayed_disc_tmo(unsigned long); void lpfc_delayed_disc_timeout_handler(struct lpfc_vport *); int lpfc_config_port_prep(struct lpfc_hba *); +void lpfc_update_vport_wwn(struct lpfc_vport *vport); int lpfc_config_port_post(struct lpfc_hba *); int lpfc_hba_down_prep(struct lpfc_hba *); int lpfc_hba_down_post(struct lpfc_hba *); @@ -365,6 +368,10 @@ extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t, uint32_t, uint32_t); extern struct lpfc_hbq_init *lpfc_hbq_defs[]; +/* SLI4 if_type 2 externs. */ +int lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *); +int lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *); + /* externs BlockGuard */ extern char *_dump_buf_data; extern unsigned long _dump_buf_data_order; @@ -429,3 +436,6 @@ void lpfc_cleanup_wt_rrqs(struct lpfc_hba *); void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *); struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, uint32_t); +int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *); +/* functions to support SR-IOV */ +int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index d9edfd90d7ff..779b88e1469d 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -352,6 +352,8 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, icmd->ulpLe = 1; icmd->ulpClass = CLASS3; icmd->ulpContext = ndlp->nlp_rpi; + if (phba->sli_rev == LPFC_SLI_REV4) + icmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) { /* For GEN_REQUEST64_CR, use the RPI */ diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index c93fca058603..ffe82d169b40 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1665,7 +1665,8 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, /* Get fast-path complete queue information */ len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "Fast-path FCP CQ information:\n"); - for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) { + fcp_qidx = 0; + do { len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "Associated EQID[%02d]:\n", phba->sli4_hba.fcp_cq[fcp_qidx]->assoc_qid); @@ -1678,7 +1679,7 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, phba->sli4_hba.fcp_cq[fcp_qidx]->entry_size, phba->sli4_hba.fcp_cq[fcp_qidx]->host_index, phba->sli4_hba.fcp_cq[fcp_qidx]->hba_index); - } + } while (++fcp_qidx < phba->cfg_fcp_eq_count); len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); /* Get mailbox queue information */ @@ -2012,7 +2013,8 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, goto pass_check; } /* FCP complete queue */ - for (qidx = 0; qidx < phba->cfg_fcp_eq_count; qidx++) { + qidx = 0; + do { if (phba->sli4_hba.fcp_cq[qidx]->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( @@ -2024,7 +2026,7 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, phba->sli4_hba.fcp_cq[qidx]; goto pass_check; } - } + } while (++qidx < phba->cfg_fcp_eq_count); goto error_out; break; case LPFC_IDIAG_MQ: diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index e2c452467c8b..32a084534f3e 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -250,7 +250,7 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp, icmd->un.elsreq64.myID = vport->fc_myDID; /* For ELS_REQUEST64_CR, use the VPI by default */ - icmd->ulpContext = vport->vpi + phba->vpi_base; + icmd->ulpContext = phba->vpi_ids[vport->vpi]; icmd->ulpCt_h = 0; /* The CT field must be 0=INVALID_RPI for the ECHO cmd */ if (elscmd == ELS_CMD_ECHO) @@ -454,6 +454,7 @@ lpfc_issue_reg_vfi(struct lpfc_vport *vport) rc = -ENOMEM; goto fail_free_dmabuf; } + mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mboxq) { rc = -ENOMEM; @@ -6585,6 +6586,26 @@ lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi) { struct lpfc_vport *vport; unsigned long flags; + int i; + + /* The physical ports are always vpi 0 - translate is unnecessary. */ + if (vpi > 0) { + /* + * Translate the physical vpi to the logical vpi. The + * vport stores the logical vpi. + */ + for (i = 0; i < phba->max_vpi; i++) { + if (vpi == phba->vpi_ids[i]) + break; + } + + if (i >= phba->max_vpi) { + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "2936 Could not find Vport mapped " + "to vpi %d\n", vpi); + return NULL; + } + } spin_lock_irqsave(&phba->hbalock, flags); list_for_each_entry(vport, &phba->port_list, listentry) { @@ -6641,8 +6662,9 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, vport = phba->pport; else vport = lpfc_find_vport_by_vpid(phba, - icmd->unsli3.rcvsli3.vpi - phba->vpi_base); + icmd->unsli3.rcvsli3.vpi); } + /* If there are no BDEs associated * with this IOCB, there is nothing to do. */ @@ -7222,7 +7244,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, elsiocb->iocb.ulpCt_h = (SLI4_CT_VPI >> 1) & 1; elsiocb->iocb.ulpCt_l = SLI4_CT_VPI & 1 ; /* Set the ulpContext to the vpi */ - elsiocb->iocb.ulpContext = vport->vpi + phba->vpi_base; + elsiocb->iocb.ulpContext = phba->vpi_ids[vport->vpi]; } else { /* For FDISC, Let FDISC rsp set the NPortID for this VPI */ icmd->ulpCt_h = 1; diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 7a35df5e2038..18d0dbfda2bc 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -881,7 +881,7 @@ lpfc_linkdown(struct lpfc_hba *phba) /* Clean up any firmware default rpi's */ mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mb) { - lpfc_unreg_did(phba, 0xffff, 0xffffffff, mb); + lpfc_unreg_did(phba, 0xffff, LPFC_UNREG_ALL_DFLT_RPIS, mb); mb->vport = vport; mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; if (lpfc_sli_issue_mbox(phba, mb, MBX_NOWAIT) @@ -2690,16 +2690,7 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) memcpy((uint8_t *) &vport->fc_sparam, (uint8_t *) mp->virt, sizeof (struct serv_parm)); - if (phba->cfg_soft_wwnn) - u64_to_wwn(phba->cfg_soft_wwnn, - vport->fc_sparam.nodeName.u.wwn); - if (phba->cfg_soft_wwpn) - u64_to_wwn(phba->cfg_soft_wwpn, - vport->fc_sparam.portName.u.wwn); - memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName, - sizeof(vport->fc_nodename)); - memcpy(&vport->fc_portname, &vport->fc_sparam.portName, - sizeof(vport->fc_portname)); + lpfc_update_vport_wwn(vport); if (vport->port_type == LPFC_PHYSICAL_PORT) { memcpy(&phba->wwnn, &vport->fc_nodename, sizeof(phba->wwnn)); memcpy(&phba->wwpn, &vport->fc_portname, sizeof(phba->wwnn)); @@ -3430,7 +3421,8 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) return; } - ndlp->nlp_rpi = mb->un.varWords[0]; + if (phba->sli_rev < LPFC_SLI_REV4) + ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); @@ -3504,7 +3496,8 @@ out: return; } - ndlp->nlp_rpi = mb->un.varWords[0]; + if (phba->sli_rev < LPFC_SLI_REV4) + ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); @@ -3591,7 +3584,6 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (ndlp->nlp_type & NLP_FCP_INITIATOR) rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; - if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) fc_remote_port_rolechg(rport, rport_ids.roles); @@ -4106,11 +4098,16 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *mbox; int rc; + uint16_t rpi; if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { - lpfc_unreg_login(phba, vport->vpi, ndlp->nlp_rpi, mbox); + /* SLI4 ports require the physical rpi value. */ + rpi = ndlp->nlp_rpi; + if (phba->sli_rev == LPFC_SLI_REV4) + rpi = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; + lpfc_unreg_login(phba, vport->vpi, rpi, mbox); mbox->vport = vport; mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); @@ -4179,7 +4176,8 @@ lpfc_unreg_all_rpis(struct lpfc_vport *vport) mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { - lpfc_unreg_login(phba, vport->vpi, 0xffff, mbox); + lpfc_unreg_login(phba, vport->vpi, LPFC_UNREG_ALL_RPIS_VPORT, + mbox); mbox->vport = vport; mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; mbox->context1 = NULL; @@ -4203,7 +4201,8 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport) mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { - lpfc_unreg_did(phba, vport->vpi, 0xffffffff, mbox); + lpfc_unreg_did(phba, vport->vpi, LPFC_UNREG_ALL_DFLT_RPIS, + mbox); mbox->vport = vport; mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; mbox->context1 = NULL; @@ -4653,10 +4652,7 @@ lpfc_disc_start(struct lpfc_vport *vport) if (num_sent) return; - /* - * For SLI3, cmpl_reg_vpi will set port_state to READY, and - * continue discovery. - */ + /* Register the VPI for SLI3, NON-NPIV only. */ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) && !(vport->fc_flag & FC_PT2PT) && !(vport->fc_flag & FC_RSCN_MODE) && @@ -4943,7 +4939,7 @@ restart_disc: if (phba->sli_rev < LPFC_SLI_REV4) { if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) lpfc_issue_reg_vpi(phba, vport); - else { /* NPIV Not enabled */ + else { lpfc_issue_clear_la(phba, vport); vport->port_state = LPFC_VPORT_READY; } @@ -5069,7 +5065,8 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) pmb->context1 = NULL; pmb->context2 = NULL; - ndlp->nlp_rpi = mb->un.varWords[0]; + if (phba->sli_rev < LPFC_SLI_REV4) + ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); @@ -5354,6 +5351,17 @@ lpfc_fcf_inuse(struct lpfc_hba *phba) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { shost = lpfc_shost_from_vport(vports[i]); spin_lock_irq(shost->host_lock); + /* + * IF the CVL_RCVD bit is not set then we have sent the + * flogi. + * If dev_loss fires while we are waiting we do not want to + * unreg the fcf. + */ + if (!(vports[i]->fc_flag & FC_VPORT_CVL_RCVD)) { + spin_unlock_irq(shost->host_lock); + ret = 1; + goto out; + } list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) { if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport && (ndlp->rport->roles & FC_RPORT_ROLE_FCP_TARGET)) { diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 86b6f7e6686a..9059524cf225 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -64,6 +64,8 @@ #define SLI3_IOCB_CMD_SIZE 128 #define SLI3_IOCB_RSP_SIZE 64 +#define LPFC_UNREG_ALL_RPIS_VPORT 0xffff +#define LPFC_UNREG_ALL_DFLT_RPIS 0xffffffff /* vendor ID used in SCSI netlink calls */ #define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX) @@ -903,6 +905,8 @@ struct RRQ { /* Structure is in Big Endian format */ #define rrq_rxid_WORD rrq_exchg }; +#define LPFC_MAX_VFN_PER_PFN 255 /* Maximum VFs allowed per ARI */ +#define LPFC_DEF_VFN_PER_PFN 0 /* Default VFs due to platform limitation*/ struct RTV_RSP { /* Structure is in Big Endian format */ uint32_t ratov; @@ -1199,7 +1203,9 @@ typedef struct { #define PCI_DEVICE_ID_BALIUS 0xe131 #define PCI_DEVICE_ID_PROTEUS_PF 0xe180 #define PCI_DEVICE_ID_LANCER_FC 0xe200 +#define PCI_DEVICE_ID_LANCER_FC_VF 0xe208 #define PCI_DEVICE_ID_LANCER_FCOE 0xe260 +#define PCI_DEVICE_ID_LANCER_FCOE_VF 0xe268 #define PCI_DEVICE_ID_SAT_SMB 0xf011 #define PCI_DEVICE_ID_SAT_MID 0xf015 #define PCI_DEVICE_ID_RFLY 0xf095 @@ -3021,7 +3027,7 @@ typedef struct { #define MAILBOX_EXT_SIZE (MAILBOX_EXT_WSIZE * sizeof(uint32_t)) #define MAILBOX_HBA_EXT_OFFSET 0x100 /* max mbox xmit size is a page size for sysfs IO operations */ -#define MAILBOX_MAX_XMIT_SIZE PAGE_SIZE +#define MAILBOX_SYSFS_MAX 4096 typedef union { uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; /* first word is type/ diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 4dff668ebdad..11e26a26b5d1 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -170,6 +170,25 @@ struct lpfc_sli_intf { #define LPFC_PCI_FUNC3 3 #define LPFC_PCI_FUNC4 4 +/* SLI4 interface type-2 control register offsets */ +#define LPFC_CTL_PORT_SEM_OFFSET 0x400 +#define LPFC_CTL_PORT_STA_OFFSET 0x404 +#define LPFC_CTL_PORT_CTL_OFFSET 0x408 +#define LPFC_CTL_PORT_ER1_OFFSET 0x40C +#define LPFC_CTL_PORT_ER2_OFFSET 0x410 +#define LPFC_CTL_PDEV_CTL_OFFSET 0x414 + +/* Some SLI4 interface type-2 PDEV_CTL register bits */ +#define LPFC_CTL_PDEV_CTL_DRST 0x00000001 +#define LPFC_CTL_PDEV_CTL_FRST 0x00000002 +#define LPFC_CTL_PDEV_CTL_DD 0x00000004 +#define LPFC_CTL_PDEV_CTL_LC 0x00000008 +#define LPFC_CTL_PDEV_CTL_FRL_ALL 0x00 +#define LPFC_CTL_PDEV_CTL_FRL_FC_FCOE 0x10 +#define LPFC_CTL_PDEV_CTL_FRL_NIC 0x20 + +#define LPFC_FW_DUMP_REQUEST (LPFC_CTL_PDEV_CTL_DD | LPFC_CTL_PDEV_CTL_FRST) + /* Active interrupt test count */ #define LPFC_ACT_INTR_CNT 4 @@ -210,9 +229,26 @@ struct ulp_bde64 { struct lpfc_sli4_flags { uint32_t word0; -#define lpfc_fip_flag_SHIFT 0 -#define lpfc_fip_flag_MASK 0x00000001 -#define lpfc_fip_flag_WORD word0 +#define lpfc_idx_rsrc_rdy_SHIFT 0 +#define lpfc_idx_rsrc_rdy_MASK 0x00000001 +#define lpfc_idx_rsrc_rdy_WORD word0 +#define LPFC_IDX_RSRC_RDY 1 +#define lpfc_xri_rsrc_rdy_SHIFT 1 +#define lpfc_xri_rsrc_rdy_MASK 0x00000001 +#define lpfc_xri_rsrc_rdy_WORD word0 +#define LPFC_XRI_RSRC_RDY 1 +#define lpfc_rpi_rsrc_rdy_SHIFT 2 +#define lpfc_rpi_rsrc_rdy_MASK 0x00000001 +#define lpfc_rpi_rsrc_rdy_WORD word0 +#define LPFC_RPI_RSRC_RDY 1 +#define lpfc_vpi_rsrc_rdy_SHIFT 3 +#define lpfc_vpi_rsrc_rdy_MASK 0x00000001 +#define lpfc_vpi_rsrc_rdy_WORD word0 +#define LPFC_VPI_RSRC_RDY 1 +#define lpfc_vfi_rsrc_rdy_SHIFT 4 +#define lpfc_vfi_rsrc_rdy_MASK 0x00000001 +#define lpfc_vfi_rsrc_rdy_WORD word0 +#define LPFC_VFI_RSRC_RDY 1 }; struct sli4_bls_rsp { @@ -739,6 +775,12 @@ union lpfc_sli4_cfg_shdr { #define lpfc_mbox_hdr_version_SHIFT 0 #define lpfc_mbox_hdr_version_MASK 0x000000FF #define lpfc_mbox_hdr_version_WORD word9 +#define lpfc_mbox_hdr_pf_num_SHIFT 16 +#define lpfc_mbox_hdr_pf_num_MASK 0x000000FF +#define lpfc_mbox_hdr_pf_num_WORD word9 +#define lpfc_mbox_hdr_vh_num_SHIFT 24 +#define lpfc_mbox_hdr_vh_num_MASK 0x000000FF +#define lpfc_mbox_hdr_vh_num_WORD word9 #define LPFC_Q_CREATE_VERSION_2 2 #define LPFC_Q_CREATE_VERSION_1 1 #define LPFC_Q_CREATE_VERSION_0 0 @@ -766,12 +808,22 @@ union lpfc_sli4_cfg_shdr { } response; }; -/* Mailbox structures */ +/* Mailbox Header structures. + * struct mbox_header is defined for first generation SLI4_CFG mailbox + * calls deployed for BE-based ports. + * + * struct sli4_mbox_header is defined for second generation SLI4 + * ports that don't deploy the SLI4_CFG mechanism. + */ struct mbox_header { struct lpfc_sli4_cfg_mhdr cfg_mhdr; union lpfc_sli4_cfg_shdr cfg_shdr; }; +#define LPFC_EXTENT_LOCAL 0 +#define LPFC_TIMEOUT_DEFAULT 0 +#define LPFC_EXTENT_VERSION_DEFAULT 0 + /* Subsystem Definitions */ #define LPFC_MBOX_SUBSYSTEM_COMMON 0x1 #define LPFC_MBOX_SUBSYSTEM_FCOE 0xC @@ -794,6 +846,13 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A #define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D #define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A +#define LPFC_MBOX_OPCODE_GET_RSRC_EXTENT_INFO 0x9A +#define LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT 0x9B +#define LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT 0x9C +#define LPFC_MBOX_OPCODE_DEALLOC_RSRC_EXTENT 0x9D +#define LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG 0xA0 +#define LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG 0xA4 +#define LPFC_MBOX_OPCODE_WRITE_OBJECT 0xAC #define LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS 0xB5 /* FCoE Opcodes */ @@ -808,6 +867,8 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A #define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B #define LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF 0x10 +#define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE 0x22 +#define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK 0x23 /* Mailbox command structures */ struct eq_context { @@ -1210,6 +1271,187 @@ struct lpfc_mbx_mq_destroy { } u; }; +/* Start Gen 2 SLI4 Mailbox definitions: */ + +/* Define allocate-ready Gen 2 SLI4 FCoE Resource Extent Types. */ +#define LPFC_RSC_TYPE_FCOE_VFI 0x20 +#define LPFC_RSC_TYPE_FCOE_VPI 0x21 +#define LPFC_RSC_TYPE_FCOE_RPI 0x22 +#define LPFC_RSC_TYPE_FCOE_XRI 0x23 + +struct lpfc_mbx_get_rsrc_extent_info { + struct mbox_header header; + union { + struct { + uint32_t word4; +#define lpfc_mbx_get_rsrc_extent_info_type_SHIFT 0 +#define lpfc_mbx_get_rsrc_extent_info_type_MASK 0x0000FFFF +#define lpfc_mbx_get_rsrc_extent_info_type_WORD word4 + } req; + struct { + uint32_t word4; +#define lpfc_mbx_get_rsrc_extent_info_cnt_SHIFT 0 +#define lpfc_mbx_get_rsrc_extent_info_cnt_MASK 0x0000FFFF +#define lpfc_mbx_get_rsrc_extent_info_cnt_WORD word4 +#define lpfc_mbx_get_rsrc_extent_info_size_SHIFT 16 +#define lpfc_mbx_get_rsrc_extent_info_size_MASK 0x0000FFFF +#define lpfc_mbx_get_rsrc_extent_info_size_WORD word4 + } rsp; + } u; +}; + +struct lpfc_id_range { + uint32_t word5; +#define lpfc_mbx_rsrc_id_word4_0_SHIFT 0 +#define lpfc_mbx_rsrc_id_word4_0_MASK 0x0000FFFF +#define lpfc_mbx_rsrc_id_word4_0_WORD word5 +#define lpfc_mbx_rsrc_id_word4_1_SHIFT 16 +#define lpfc_mbx_rsrc_id_word4_1_MASK 0x0000FFFF +#define lpfc_mbx_rsrc_id_word4_1_WORD word5 +}; + +struct lpfc_mbx_set_link_diag_state { + struct mbox_header header; + union { + struct { + uint32_t word0; +#define lpfc_mbx_set_diag_state_diag_SHIFT 0 +#define lpfc_mbx_set_diag_state_diag_MASK 0x00000001 +#define lpfc_mbx_set_diag_state_diag_WORD word0 +#define lpfc_mbx_set_diag_state_link_num_SHIFT 16 +#define lpfc_mbx_set_diag_state_link_num_MASK 0x0000003F +#define lpfc_mbx_set_diag_state_link_num_WORD word0 +#define lpfc_mbx_set_diag_state_link_type_SHIFT 22 +#define lpfc_mbx_set_diag_state_link_type_MASK 0x00000003 +#define lpfc_mbx_set_diag_state_link_type_WORD word0 + } req; + struct { + uint32_t word0; + } rsp; + } u; +}; + +struct lpfc_mbx_set_link_diag_loopback { + struct mbox_header header; + union { + struct { + uint32_t word0; +#define lpfc_mbx_set_diag_lpbk_type_SHIFT 0 +#define lpfc_mbx_set_diag_lpbk_type_MASK 0x00000001 +#define lpfc_mbx_set_diag_lpbk_type_WORD word0 +#define LPFC_DIAG_LOOPBACK_TYPE_DISABLE 0x0 +#define LPFC_DIAG_LOOPBACK_TYPE_INTERNAL 0x1 +#define LPFC_DIAG_LOOPBACK_TYPE_EXTERNAL 0x2 +#define lpfc_mbx_set_diag_lpbk_link_num_SHIFT 16 +#define lpfc_mbx_set_diag_lpbk_link_num_MASK 0x0000003F +#define lpfc_mbx_set_diag_lpbk_link_num_WORD word0 +#define lpfc_mbx_set_diag_lpbk_link_type_SHIFT 22 +#define lpfc_mbx_set_diag_lpbk_link_type_MASK 0x00000003 +#define lpfc_mbx_set_diag_lpbk_link_type_WORD word0 + } req; + struct { + uint32_t word0; + } rsp; + } u; +}; + +struct lpfc_mbx_run_link_diag_test { + struct mbox_header header; + union { + struct { + uint32_t word0; +#define lpfc_mbx_run_diag_test_link_num_SHIFT 16 +#define lpfc_mbx_run_diag_test_link_num_MASK 0x0000003F +#define lpfc_mbx_run_diag_test_link_num_WORD word0 +#define lpfc_mbx_run_diag_test_link_type_SHIFT 22 +#define lpfc_mbx_run_diag_test_link_type_MASK 0x00000003 +#define lpfc_mbx_run_diag_test_link_type_WORD word0 + uint32_t word1; +#define lpfc_mbx_run_diag_test_test_id_SHIFT 0 +#define lpfc_mbx_run_diag_test_test_id_MASK 0x0000FFFF +#define lpfc_mbx_run_diag_test_test_id_WORD word1 +#define lpfc_mbx_run_diag_test_loops_SHIFT 16 +#define lpfc_mbx_run_diag_test_loops_MASK 0x0000FFFF +#define lpfc_mbx_run_diag_test_loops_WORD word1 + uint32_t word2; +#define lpfc_mbx_run_diag_test_test_ver_SHIFT 0 +#define lpfc_mbx_run_diag_test_test_ver_MASK 0x0000FFFF +#define lpfc_mbx_run_diag_test_test_ver_WORD word2 +#define lpfc_mbx_run_diag_test_err_act_SHIFT 16 +#define lpfc_mbx_run_diag_test_err_act_MASK 0x000000FF +#define lpfc_mbx_run_diag_test_err_act_WORD word2 + } req; + struct { + uint32_t word0; + } rsp; + } u; +}; + +/* + * struct lpfc_mbx_alloc_rsrc_extents: + * A mbox is generically 256 bytes long. An SLI4_CONFIG mailbox requires + * 6 words of header + 4 words of shared subcommand header + + * 1 words of Extent-Opcode-specific header = 11 words or 44 bytes total. + * + * An embedded version of SLI4_CONFIG therefore has 256 - 44 = 212 bytes + * for extents payload. + * + * 212/2 (bytes per extent) = 106 extents. + * 106/2 (extents per word) = 53 words. + * lpfc_id_range id is statically size to 53. + * + * This mailbox definition is used for ALLOC or GET_ALLOCATED + * extent ranges. For ALLOC, the type and cnt are required. + * For GET_ALLOCATED, only the type is required. + */ +struct lpfc_mbx_alloc_rsrc_extents { + struct mbox_header header; + union { + struct { + uint32_t word4; +#define lpfc_mbx_alloc_rsrc_extents_type_SHIFT 0 +#define lpfc_mbx_alloc_rsrc_extents_type_MASK 0x0000FFFF +#define lpfc_mbx_alloc_rsrc_extents_type_WORD word4 +#define lpfc_mbx_alloc_rsrc_extents_cnt_SHIFT 16 +#define lpfc_mbx_alloc_rsrc_extents_cnt_MASK 0x0000FFFF +#define lpfc_mbx_alloc_rsrc_extents_cnt_WORD word4 + } req; + struct { + uint32_t word4; +#define lpfc_mbx_rsrc_cnt_SHIFT 0 +#define lpfc_mbx_rsrc_cnt_MASK 0x0000FFFF +#define lpfc_mbx_rsrc_cnt_WORD word4 + struct lpfc_id_range id[53]; + } rsp; + } u; +}; + +/* + * This is the non-embedded version of ALLOC or GET RSRC_EXTENTS. Word4 in this + * structure shares the same SHIFT/MASK/WORD defines provided in the + * mbx_alloc_rsrc_extents and mbx_get_alloc_rsrc_extents, word4, provided in + * the structures defined above. This non-embedded structure provides for the + * maximum number of extents supported by the port. + */ +struct lpfc_mbx_nembed_rsrc_extent { + union lpfc_sli4_cfg_shdr cfg_shdr; + uint32_t word4; + struct lpfc_id_range id; +}; + +struct lpfc_mbx_dealloc_rsrc_extents { + struct mbox_header header; + struct { + uint32_t word4; +#define lpfc_mbx_dealloc_rsrc_extents_type_SHIFT 0 +#define lpfc_mbx_dealloc_rsrc_extents_type_MASK 0x0000FFFF +#define lpfc_mbx_dealloc_rsrc_extents_type_WORD word4 + } req; + +}; + +/* Start SLI4 FCoE specific mbox structures. */ + struct lpfc_mbx_post_hdr_tmpl { struct mbox_header header; uint32_t word10; @@ -1229,7 +1471,7 @@ struct sli4_sge { /* SLI-4 */ uint32_t word2; #define lpfc_sli4_sge_offset_SHIFT 0 /* Offset of buffer - Not used*/ -#define lpfc_sli4_sge_offset_MASK 0x00FFFFFF +#define lpfc_sli4_sge_offset_MASK 0x1FFFFFFF #define lpfc_sli4_sge_offset_WORD word2 #define lpfc_sli4_sge_last_SHIFT 31 /* Last SEG in the SGL sets this flag !! */ @@ -1773,61 +2015,31 @@ struct lpfc_mbx_read_rev { struct lpfc_mbx_read_config { uint32_t word1; -#define lpfc_mbx_rd_conf_max_bbc_SHIFT 0 -#define lpfc_mbx_rd_conf_max_bbc_MASK 0x000000FF -#define lpfc_mbx_rd_conf_max_bbc_WORD word1 -#define lpfc_mbx_rd_conf_init_bbc_SHIFT 8 -#define lpfc_mbx_rd_conf_init_bbc_MASK 0x000000FF -#define lpfc_mbx_rd_conf_init_bbc_WORD word1 +#define lpfc_mbx_rd_conf_extnts_inuse_SHIFT 31 +#define lpfc_mbx_rd_conf_extnts_inuse_MASK 0x00000001 +#define lpfc_mbx_rd_conf_extnts_inuse_WORD word1 uint32_t word2; -#define lpfc_mbx_rd_conf_nport_did_SHIFT 0 -#define lpfc_mbx_rd_conf_nport_did_MASK 0x00FFFFFF -#define lpfc_mbx_rd_conf_nport_did_WORD word2 #define lpfc_mbx_rd_conf_topology_SHIFT 24 #define lpfc_mbx_rd_conf_topology_MASK 0x000000FF #define lpfc_mbx_rd_conf_topology_WORD word2 - uint32_t word3; -#define lpfc_mbx_rd_conf_ao_SHIFT 0 -#define lpfc_mbx_rd_conf_ao_MASK 0x00000001 -#define lpfc_mbx_rd_conf_ao_WORD word3 -#define lpfc_mbx_rd_conf_bb_scn_SHIFT 8 -#define lpfc_mbx_rd_conf_bb_scn_MASK 0x0000000F -#define lpfc_mbx_rd_conf_bb_scn_WORD word3 -#define lpfc_mbx_rd_conf_cbb_scn_SHIFT 12 -#define lpfc_mbx_rd_conf_cbb_scn_MASK 0x0000000F -#define lpfc_mbx_rd_conf_cbb_scn_WORD word3 -#define lpfc_mbx_rd_conf_mc_SHIFT 29 -#define lpfc_mbx_rd_conf_mc_MASK 0x00000001 -#define lpfc_mbx_rd_conf_mc_WORD word3 + uint32_t rsvd_3; uint32_t word4; #define lpfc_mbx_rd_conf_e_d_tov_SHIFT 0 #define lpfc_mbx_rd_conf_e_d_tov_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_e_d_tov_WORD word4 - uint32_t word5; -#define lpfc_mbx_rd_conf_lp_tov_SHIFT 0 -#define lpfc_mbx_rd_conf_lp_tov_MASK 0x0000FFFF -#define lpfc_mbx_rd_conf_lp_tov_WORD word5 + uint32_t rsvd_5; uint32_t word6; #define lpfc_mbx_rd_conf_r_a_tov_SHIFT 0 #define lpfc_mbx_rd_conf_r_a_tov_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_r_a_tov_WORD word6 - uint32_t word7; -#define lpfc_mbx_rd_conf_r_t_tov_SHIFT 0 -#define lpfc_mbx_rd_conf_r_t_tov_MASK 0x000000FF -#define lpfc_mbx_rd_conf_r_t_tov_WORD word7 - uint32_t word8; -#define lpfc_mbx_rd_conf_al_tov_SHIFT 0 -#define lpfc_mbx_rd_conf_al_tov_MASK 0x0000000F -#define lpfc_mbx_rd_conf_al_tov_WORD word8 + uint32_t rsvd_7; + uint32_t rsvd_8; uint32_t word9; #define lpfc_mbx_rd_conf_lmt_SHIFT 0 #define lpfc_mbx_rd_conf_lmt_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_lmt_WORD word9 - uint32_t word10; -#define lpfc_mbx_rd_conf_max_alpa_SHIFT 0 -#define lpfc_mbx_rd_conf_max_alpa_MASK 0x000000FF -#define lpfc_mbx_rd_conf_max_alpa_WORD word10 - uint32_t word11_rsvd; + uint32_t rsvd_10; + uint32_t rsvd_11; uint32_t word12; #define lpfc_mbx_rd_conf_xri_base_SHIFT 0 #define lpfc_mbx_rd_conf_xri_base_MASK 0x0000FFFF @@ -1857,9 +2069,6 @@ struct lpfc_mbx_read_config { #define lpfc_mbx_rd_conf_vfi_count_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_vfi_count_WORD word15 uint32_t word16; -#define lpfc_mbx_rd_conf_fcfi_base_SHIFT 0 -#define lpfc_mbx_rd_conf_fcfi_base_MASK 0x0000FFFF -#define lpfc_mbx_rd_conf_fcfi_base_WORD word16 #define lpfc_mbx_rd_conf_fcfi_count_SHIFT 16 #define lpfc_mbx_rd_conf_fcfi_count_MASK 0x0000FFFF #define lpfc_mbx_rd_conf_fcfi_count_WORD word16 @@ -2169,6 +2378,12 @@ struct lpfc_sli4_parameters { #define cfg_fcoe_SHIFT 0 #define cfg_fcoe_MASK 0x00000001 #define cfg_fcoe_WORD word12 +#define cfg_ext_SHIFT 1 +#define cfg_ext_MASK 0x00000001 +#define cfg_ext_WORD word12 +#define cfg_hdrr_SHIFT 2 +#define cfg_hdrr_MASK 0x00000001 +#define cfg_hdrr_WORD word12 #define cfg_phwq_SHIFT 15 #define cfg_phwq_MASK 0x00000001 #define cfg_phwq_WORD word12 @@ -2198,6 +2413,145 @@ struct lpfc_mbx_get_sli4_parameters { struct lpfc_sli4_parameters sli4_parameters; }; +struct lpfc_rscr_desc_generic { +#define LPFC_RSRC_DESC_WSIZE 18 + uint32_t desc[LPFC_RSRC_DESC_WSIZE]; +}; + +struct lpfc_rsrc_desc_pcie { + uint32_t word0; +#define lpfc_rsrc_desc_pcie_type_SHIFT 0 +#define lpfc_rsrc_desc_pcie_type_MASK 0x000000ff +#define lpfc_rsrc_desc_pcie_type_WORD word0 +#define LPFC_RSRC_DESC_TYPE_PCIE 0x40 + uint32_t word1; +#define lpfc_rsrc_desc_pcie_pfnum_SHIFT 0 +#define lpfc_rsrc_desc_pcie_pfnum_MASK 0x000000ff +#define lpfc_rsrc_desc_pcie_pfnum_WORD word1 + uint32_t reserved; + uint32_t word3; +#define lpfc_rsrc_desc_pcie_sriov_sta_SHIFT 0 +#define lpfc_rsrc_desc_pcie_sriov_sta_MASK 0x000000ff +#define lpfc_rsrc_desc_pcie_sriov_sta_WORD word3 +#define lpfc_rsrc_desc_pcie_pf_sta_SHIFT 8 +#define lpfc_rsrc_desc_pcie_pf_sta_MASK 0x000000ff +#define lpfc_rsrc_desc_pcie_pf_sta_WORD word3 +#define lpfc_rsrc_desc_pcie_pf_type_SHIFT 16 +#define lpfc_rsrc_desc_pcie_pf_type_MASK 0x000000ff +#define lpfc_rsrc_desc_pcie_pf_type_WORD word3 + uint32_t word4; +#define lpfc_rsrc_desc_pcie_nr_virtfn_SHIFT 0 +#define lpfc_rsrc_desc_pcie_nr_virtfn_MASK 0x0000ffff +#define lpfc_rsrc_desc_pcie_nr_virtfn_WORD word4 +}; + +struct lpfc_rsrc_desc_fcfcoe { + uint32_t word0; +#define lpfc_rsrc_desc_fcfcoe_type_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_type_MASK 0x000000ff +#define lpfc_rsrc_desc_fcfcoe_type_WORD word0 +#define LPFC_RSRC_DESC_TYPE_FCFCOE 0x43 + uint32_t word1; +#define lpfc_rsrc_desc_fcfcoe_vfnum_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_vfnum_MASK 0x000000ff +#define lpfc_rsrc_desc_fcfcoe_vfnum_WORD word1 +#define lpfc_rsrc_desc_fcfcoe_pfnum_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_pfnum_MASK 0x000007ff +#define lpfc_rsrc_desc_fcfcoe_pfnum_WORD word1 + uint32_t word2; +#define lpfc_rsrc_desc_fcfcoe_rpi_cnt_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_rpi_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_rpi_cnt_WORD word2 +#define lpfc_rsrc_desc_fcfcoe_xri_cnt_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_xri_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_xri_cnt_WORD word2 + uint32_t word3; +#define lpfc_rsrc_desc_fcfcoe_wq_cnt_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_wq_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_wq_cnt_WORD word3 +#define lpfc_rsrc_desc_fcfcoe_rq_cnt_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_rq_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_rq_cnt_WORD word3 + uint32_t word4; +#define lpfc_rsrc_desc_fcfcoe_cq_cnt_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_cq_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_cq_cnt_WORD word4 +#define lpfc_rsrc_desc_fcfcoe_vpi_cnt_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_vpi_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_vpi_cnt_WORD word4 + uint32_t word5; +#define lpfc_rsrc_desc_fcfcoe_fcfi_cnt_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_fcfi_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_fcfi_cnt_WORD word5 +#define lpfc_rsrc_desc_fcfcoe_vfi_cnt_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_vfi_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_vfi_cnt_WORD word5 + uint32_t word6; + uint32_t word7; + uint32_t word8; + uint32_t word9; + uint32_t word10; + uint32_t word11; + uint32_t word12; + uint32_t word13; +#define lpfc_rsrc_desc_fcfcoe_lnk_nr_SHIFT 0 +#define lpfc_rsrc_desc_fcfcoe_lnk_nr_MASK 0x0000003f +#define lpfc_rsrc_desc_fcfcoe_lnk_nr_WORD word13 +#define lpfc_rsrc_desc_fcfcoe_lnk_tp_SHIFT 6 +#define lpfc_rsrc_desc_fcfcoe_lnk_tp_MASK 0x00000003 +#define lpfc_rsrc_desc_fcfcoe_lnk_tp_WORD word13 +#define lpfc_rsrc_desc_fcfcoe_lmc_SHIFT 8 +#define lpfc_rsrc_desc_fcfcoe_lmc_MASK 0x00000001 +#define lpfc_rsrc_desc_fcfcoe_lmc_WORD word13 +#define lpfc_rsrc_desc_fcfcoe_lld_SHIFT 9 +#define lpfc_rsrc_desc_fcfcoe_lld_MASK 0x00000001 +#define lpfc_rsrc_desc_fcfcoe_lld_WORD word13 +#define lpfc_rsrc_desc_fcfcoe_eq_cnt_SHIFT 16 +#define lpfc_rsrc_desc_fcfcoe_eq_cnt_MASK 0x0000ffff +#define lpfc_rsrc_desc_fcfcoe_eq_cnt_WORD word13 +}; + +struct lpfc_func_cfg { +#define LPFC_RSRC_DESC_MAX_NUM 2 + uint32_t rsrc_desc_count; + struct lpfc_rscr_desc_generic desc[LPFC_RSRC_DESC_MAX_NUM]; +}; + +struct lpfc_mbx_get_func_cfg { + struct mbox_header header; +#define LPFC_CFG_TYPE_PERSISTENT_OVERRIDE 0x0 +#define LPFC_CFG_TYPE_FACTURY_DEFAULT 0x1 +#define LPFC_CFG_TYPE_CURRENT_ACTIVE 0x2 + struct lpfc_func_cfg func_cfg; +}; + +struct lpfc_prof_cfg { +#define LPFC_RSRC_DESC_MAX_NUM 2 + uint32_t rsrc_desc_count; + struct lpfc_rscr_desc_generic desc[LPFC_RSRC_DESC_MAX_NUM]; +}; + +struct lpfc_mbx_get_prof_cfg { + struct mbox_header header; +#define LPFC_CFG_TYPE_PERSISTENT_OVERRIDE 0x0 +#define LPFC_CFG_TYPE_FACTURY_DEFAULT 0x1 +#define LPFC_CFG_TYPE_CURRENT_ACTIVE 0x2 + union { + struct { + uint32_t word10; +#define lpfc_mbx_get_prof_cfg_prof_id_SHIFT 0 +#define lpfc_mbx_get_prof_cfg_prof_id_MASK 0x000000ff +#define lpfc_mbx_get_prof_cfg_prof_id_WORD word10 +#define lpfc_mbx_get_prof_cfg_prof_tp_SHIFT 8 +#define lpfc_mbx_get_prof_cfg_prof_tp_MASK 0x00000003 +#define lpfc_mbx_get_prof_cfg_prof_tp_WORD word10 + } request; + struct { + struct lpfc_prof_cfg prof_cfg; + } response; + } u; +}; + /* Mailbox Completion Queue Error Messages */ #define MB_CQE_STATUS_SUCCESS 0x0 #define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1 @@ -2206,6 +2560,29 @@ struct lpfc_mbx_get_sli4_parameters { #define MB_CEQ_STATUS_QUEUE_FLUSHING 0x4 #define MB_CQE_STATUS_DMA_FAILED 0x5 +#define LPFC_MBX_WR_CONFIG_MAX_BDE 8 +struct lpfc_mbx_wr_object { + struct mbox_header header; + union { + struct { + uint32_t word4; +#define lpfc_wr_object_eof_SHIFT 31 +#define lpfc_wr_object_eof_MASK 0x00000001 +#define lpfc_wr_object_eof_WORD word4 +#define lpfc_wr_object_write_length_SHIFT 0 +#define lpfc_wr_object_write_length_MASK 0x00FFFFFF +#define lpfc_wr_object_write_length_WORD word4 + uint32_t write_offset; + uint32_t object_name[26]; + uint32_t bde_count; + struct ulp_bde64 bde[LPFC_MBX_WR_CONFIG_MAX_BDE]; + } request; + struct { + uint32_t actual_write_length; + } response; + } u; +}; + /* mailbox queue entry structure */ struct lpfc_mqe { uint32_t word0; @@ -2241,6 +2618,9 @@ struct lpfc_mqe { struct lpfc_mbx_cq_destroy cq_destroy; struct lpfc_mbx_wq_destroy wq_destroy; struct lpfc_mbx_rq_destroy rq_destroy; + struct lpfc_mbx_get_rsrc_extent_info rsrc_extent_info; + struct lpfc_mbx_alloc_rsrc_extents alloc_rsrc_extents; + struct lpfc_mbx_dealloc_rsrc_extents dealloc_rsrc_extents; struct lpfc_mbx_post_sgl_pages post_sgl_pages; struct lpfc_mbx_nembed_cmd nembed_cmd; struct lpfc_mbx_read_rev read_rev; @@ -2252,7 +2632,13 @@ struct lpfc_mqe { struct lpfc_mbx_supp_pages supp_pages; struct lpfc_mbx_pc_sli4_params sli4_params; struct lpfc_mbx_get_sli4_parameters get_sli4_parameters; + struct lpfc_mbx_set_link_diag_state link_diag_state; + struct lpfc_mbx_set_link_diag_loopback link_diag_loopback; + struct lpfc_mbx_run_link_diag_test link_diag_test; + struct lpfc_mbx_get_func_cfg get_func_cfg; + struct lpfc_mbx_get_prof_cfg get_prof_cfg; struct lpfc_mbx_nop nop; + struct lpfc_mbx_wr_object wr_object; } un; }; @@ -2458,7 +2844,7 @@ struct lpfc_bmbx_create { #define SGL_ALIGN_SZ 64 #define SGL_PAGE_SIZE 4096 /* align SGL addr on a size boundary - adjust address up */ -#define NO_XRI ((uint16_t)-1) +#define NO_XRI 0xffff struct wqe_common { uint32_t word6; @@ -2798,9 +3184,28 @@ union lpfc_wqe { struct gen_req64_wqe gen_req; }; +#define LPFC_GROUP_OJECT_MAGIC_NUM 0xfeaa0001 +#define LPFC_FILE_TYPE_GROUP 0xf7 +#define LPFC_FILE_ID_GROUP 0xa2 +struct lpfc_grp_hdr { + uint32_t size; + uint32_t magic_number; + uint32_t word2; +#define lpfc_grp_hdr_file_type_SHIFT 24 +#define lpfc_grp_hdr_file_type_MASK 0x000000FF +#define lpfc_grp_hdr_file_type_WORD word2 +#define lpfc_grp_hdr_id_SHIFT 16 +#define lpfc_grp_hdr_id_MASK 0x000000FF +#define lpfc_grp_hdr_id_WORD word2 + uint8_t rev_name[128]; +}; + #define FCP_COMMAND 0x0 #define FCP_COMMAND_DATA_OUT 0x1 #define ELS_COMMAND_NON_FIP 0xC #define ELS_COMMAND_FIP 0xD #define OTHER_COMMAND 0x8 +#define LPFC_FW_DUMP 1 +#define LPFC_FW_RESET 2 +#define LPFC_DV_RESET 3 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 7dda036a1af3..148b98ddbb1d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -30,6 +30,7 @@ #include <linux/ctype.h> #include <linux/aer.h> #include <linux/slab.h> +#include <linux/firmware.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -211,7 +212,6 @@ lpfc_config_port_prep(struct lpfc_hba *phba) lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_KERNEL); if (!lpfc_vpd_data) goto out_free_mbox; - do { lpfc_dump_mem(phba, pmb, offset, DMP_REGION_VPD); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); @@ -309,6 +309,45 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) } /** + * lpfc_update_vport_wwn - Updates the fc_nodename, fc_portname, + * cfg_soft_wwnn, cfg_soft_wwpn + * @vport: pointer to lpfc vport data structure. + * + * + * Return codes + * None. + **/ +void +lpfc_update_vport_wwn(struct lpfc_vport *vport) +{ + /* If the soft name exists then update it using the service params */ + if (vport->phba->cfg_soft_wwnn) + u64_to_wwn(vport->phba->cfg_soft_wwnn, + vport->fc_sparam.nodeName.u.wwn); + if (vport->phba->cfg_soft_wwpn) + u64_to_wwn(vport->phba->cfg_soft_wwpn, + vport->fc_sparam.portName.u.wwn); + + /* + * If the name is empty or there exists a soft name + * then copy the service params name, otherwise use the fc name + */ + if (vport->fc_nodename.u.wwn[0] == 0 || vport->phba->cfg_soft_wwnn) + memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName, + sizeof(struct lpfc_name)); + else + memcpy(&vport->fc_sparam.nodeName, &vport->fc_nodename, + sizeof(struct lpfc_name)); + + if (vport->fc_portname.u.wwn[0] == 0 || vport->phba->cfg_soft_wwpn) + memcpy(&vport->fc_portname, &vport->fc_sparam.portName, + sizeof(struct lpfc_name)); + else + memcpy(&vport->fc_sparam.portName, &vport->fc_portname, + sizeof(struct lpfc_name)); +} + +/** * lpfc_config_port_post - Perform lpfc initialization after config port * @phba: pointer to lpfc hba data structure. * @@ -377,17 +416,7 @@ lpfc_config_port_post(struct lpfc_hba *phba) lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); pmb->context1 = NULL; - - if (phba->cfg_soft_wwnn) - u64_to_wwn(phba->cfg_soft_wwnn, - vport->fc_sparam.nodeName.u.wwn); - if (phba->cfg_soft_wwpn) - u64_to_wwn(phba->cfg_soft_wwpn, - vport->fc_sparam.portName.u.wwn); - memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName, - sizeof (struct lpfc_name)); - memcpy(&vport->fc_portname, &vport->fc_sparam.portName, - sizeof (struct lpfc_name)); + lpfc_update_vport_wwn(vport); /* Update the fc_host data structures with new wwn. */ fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn); @@ -573,7 +602,6 @@ lpfc_config_port_post(struct lpfc_hba *phba) /* Clear all pending interrupts */ writel(0xffffffff, phba->HAregaddr); readl(phba->HAregaddr); /* flush */ - phba->link_state = LPFC_HBA_ERROR; if (rc != MBX_BUSY) mempool_free(pmb, phba->mbox_mem_pool); @@ -1755,7 +1783,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) && descp && descp[0] != '\0') return; - if (phba->lmt & LMT_10Gb) + if (phba->lmt & LMT_16Gb) + max_speed = 16; + else if (phba->lmt & LMT_10Gb) max_speed = 10; else if (phba->lmt & LMT_8Gb) max_speed = 8; @@ -1922,12 +1952,13 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LANCER_FC: - oneConnect = 1; - m = (typeof(m)){"Undefined", "PCIe", "Fibre Channel Adapter"}; + case PCI_DEVICE_ID_LANCER_FC_VF: + m = (typeof(m)){"LPe16000", "PCIe", "Fibre Channel Adapter"}; break; case PCI_DEVICE_ID_LANCER_FCOE: + case PCI_DEVICE_ID_LANCER_FCOE_VF: oneConnect = 1; - m = (typeof(m)){"Undefined", "PCIe", "FCoE"}; + m = (typeof(m)){"OCe50100", "PCIe", "FCoE"}; break; default: m = (typeof(m)){"Unknown", "", ""}; @@ -1936,7 +1967,8 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) if (mdp && mdp[0] == '\0') snprintf(mdp, 79,"%s", m.name); - /* oneConnect hba requires special processing, they are all initiators + /* + * oneConnect hba requires special processing, they are all initiators * and we put the port number on the end */ if (descp && descp[0] == '\0') { @@ -2656,6 +2688,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) kfree(io); phba->total_iocbq_bufs--; } + spin_unlock_irq(&phba->hbalock); return 0; } @@ -3612,6 +3645,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, "2718 Clear Virtual Link Received for VPI 0x%x" " tag 0x%x\n", acqe_fip->index, acqe_fip->event_tag); + vport = lpfc_find_vport_by_vpid(phba, acqe_fip->index - phba->vpi_base); ndlp = lpfc_sli4_perform_vport_cvl(vport); @@ -3935,6 +3969,10 @@ lpfc_enable_pci_dev(struct lpfc_hba *phba) pci_try_set_mwi(pdev); pci_save_state(pdev); + /* PCIe EEH recovery on powerpc platforms needs fundamental reset */ + if (pci_find_capability(pdev, PCI_CAP_ID_EXP)) + pdev->needs_freset = 1; + return 0; out_disable_device: @@ -3997,6 +4035,36 @@ lpfc_reset_hba(struct lpfc_hba *phba) } /** + * lpfc_sli_probe_sriov_nr_virtfn - Enable a number of sr-iov virtual functions + * @phba: pointer to lpfc hba data structure. + * @nr_vfn: number of virtual functions to be enabled. + * + * This function enables the PCI SR-IOV virtual functions to a physical + * function. It invokes the PCI SR-IOV api with the @nr_vfn provided to + * enable the number of virtual functions to the physical function. As + * not all devices support SR-IOV, the return code from the pci_enable_sriov() + * API call does not considered as an error condition for most of the device. + **/ +int +lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn) +{ + struct pci_dev *pdev = phba->pcidev; + int rc; + + rc = pci_enable_sriov(pdev, nr_vfn); + if (rc) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "2806 Failed to enable sriov on this device " + "with vfn number nr_vf:%d, rc:%d\n", + nr_vfn, rc); + } else + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "2807 Successful enable sriov on this device " + "with vfn number nr_vf:%d\n", nr_vfn); + return rc; +} + +/** * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev. * @phba: pointer to lpfc hba data structure. * @@ -4011,6 +4079,7 @@ static int lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) { struct lpfc_sli *psli; + int rc; /* * Initialize timers used by driver @@ -4085,6 +4154,23 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) if (lpfc_mem_alloc(phba, BPL_ALIGN_SZ)) return -ENOMEM; + /* + * Enable sr-iov virtual functions if supported and configured + * through the module parameter. + */ + if (phba->cfg_sriov_nr_virtfn > 0) { + rc = lpfc_sli_probe_sriov_nr_virtfn(phba, + phba->cfg_sriov_nr_virtfn); + if (rc) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "2808 Requested number of SR-IOV " + "virtual functions (%d) is not " + "supported\n", + phba->cfg_sriov_nr_virtfn); + phba->cfg_sriov_nr_virtfn = 0; + } + } + return 0; } @@ -4161,6 +4247,14 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) phba->fcf.redisc_wait.data = (unsigned long)phba; /* + * Control structure for handling external multi-buffer mailbox + * command pass-through. + */ + memset((uint8_t *)&phba->mbox_ext_buf_ctx, 0, + sizeof(struct lpfc_mbox_ext_buf_ctx)); + INIT_LIST_HEAD(&phba->mbox_ext_buf_ctx.ext_dmabuf_list); + + /* * We need to do a READ_CONFIG mailbox command here before * calling lpfc_get_cfgparam. For VFs this will report the * MAX_XRI, MAX_VPI, MAX_RPI, MAX_IOCB, and MAX_VFI settings. @@ -4233,7 +4327,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) spin_lock_init(&phba->sli4_hba.abts_sgl_list_lock); /* - * Initialize dirver internal slow-path work queues + * Initialize driver internal slow-path work queues */ /* Driver internel slow-path CQ Event pool */ @@ -4249,6 +4343,12 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* Receive queue CQ Event work queue list */ INIT_LIST_HEAD(&phba->sli4_hba.sp_unsol_work_queue); + /* Initialize extent block lists. */ + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_rpi_blk_list); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_xri_blk_list); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_vfi_blk_list); + INIT_LIST_HEAD(&phba->lpfc_vpi_blk_list); + /* Initialize the driver internal SLI layer lists. */ lpfc_sli_setup(phba); lpfc_sli_queue_setup(phba); @@ -4323,9 +4423,19 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) } /* * Get sli4 parameters that override parameters from Port capabilities. - * If this call fails it is not a critical error so continue loading. + * If this call fails, it isn't critical unless the SLI4 parameters come + * back in conflict. */ - lpfc_get_sli4_parameters(phba, mboxq); + rc = lpfc_get_sli4_parameters(phba, mboxq); + if (rc) { + if (phba->sli4_hba.extents_in_use && + phba->sli4_hba.rpi_hdrs_in_use) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2999 Unsupported SLI4 Parameters " + "Extents and RPI headers enabled.\n"); + goto out_free_bsmbx; + } + } mempool_free(mboxq, phba->mbox_mem_pool); /* Create all the SLI4 queues */ rc = lpfc_sli4_queue_create(phba); @@ -4350,7 +4460,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) "1430 Failed to initialize sgl list.\n"); goto out_free_sgl_list; } - rc = lpfc_sli4_init_rpi_hdrs(phba); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -4366,6 +4475,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2759 Failed allocate memory for FCF round " "robin failover bmask\n"); + rc = -ENOMEM; goto out_remove_rpi_hdrs; } @@ -4375,6 +4485,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2572 Failed allocate memory for fast-path " "per-EQ handle array\n"); + rc = -ENOMEM; goto out_free_fcf_rr_bmask; } @@ -4384,9 +4495,27 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2573 Failed allocate memory for msi-x " "interrupt vector entries\n"); + rc = -ENOMEM; goto out_free_fcp_eq_hdl; } + /* + * Enable sr-iov virtual functions if supported and configured + * through the module parameter. + */ + if (phba->cfg_sriov_nr_virtfn > 0) { + rc = lpfc_sli_probe_sriov_nr_virtfn(phba, + phba->cfg_sriov_nr_virtfn); + if (rc) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "3020 Requested number of SR-IOV " + "virtual functions (%d) is not " + "supported\n", + phba->cfg_sriov_nr_virtfn); + phba->cfg_sriov_nr_virtfn = 0; + } + } + return rc; out_free_fcp_eq_hdl: @@ -4449,6 +4578,9 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) lpfc_sli4_cq_event_release_all(phba); lpfc_sli4_cq_event_pool_destroy(phba); + /* Release resource identifiers. */ + lpfc_sli4_dealloc_resource_identifiers(phba); + /* Free the bsmbx region. */ lpfc_destroy_bootstrap_mbox(phba); @@ -4649,6 +4781,7 @@ lpfc_init_iocb_list(struct lpfc_hba *phba, int iocb_count) "Unloading driver.\n", __func__); goto out_free_iocbq; } + iocbq_entry->sli4_lxritag = NO_XRI; iocbq_entry->sli4_xritag = NO_XRI; spin_lock_irq(&phba->hbalock); @@ -4746,7 +4879,7 @@ lpfc_init_sgl_list(struct lpfc_hba *phba) els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "2400 lpfc_init_sgl_list els %d.\n", + "2400 ELS XRI count %d.\n", els_xri_cnt); /* Initialize and populate the sglq list per host/VF. */ INIT_LIST_HEAD(&phba->sli4_hba.lpfc_sgl_list); @@ -4779,7 +4912,6 @@ lpfc_init_sgl_list(struct lpfc_hba *phba) phba->sli4_hba.scsi_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; phba->sli4_hba.scsi_xri_cnt = 0; - phba->sli4_hba.lpfc_scsi_psb_array = kzalloc((sizeof(struct lpfc_scsi_buf *) * phba->sli4_hba.scsi_xri_max), GFP_KERNEL); @@ -4802,13 +4934,6 @@ lpfc_init_sgl_list(struct lpfc_hba *phba) goto out_free_mem; } - sglq_entry->sli4_xritag = lpfc_sli4_next_xritag(phba); - if (sglq_entry->sli4_xritag == NO_XRI) { - kfree(sglq_entry); - printk(KERN_ERR "%s: failed to allocate XRI.\n" - "Unloading driver.\n", __func__); - goto out_free_mem; - } sglq_entry->buff_type = GEN_BUFF_TYPE; sglq_entry->virt = lpfc_mbuf_alloc(phba, 0, &sglq_entry->phys); if (sglq_entry->virt == NULL) { @@ -4857,24 +4982,20 @@ int lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba) { int rc = 0; - int longs; - uint16_t rpi_count; struct lpfc_rpi_hdr *rpi_hdr; INIT_LIST_HEAD(&phba->sli4_hba.lpfc_rpi_hdr_list); - /* - * Provision an rpi bitmask range for discovery. The total count - * is the difference between max and base + 1. + * If the SLI4 port supports extents, posting the rpi header isn't + * required. Set the expected maximum count and let the actual value + * get set when extents are fully allocated. */ - rpi_count = phba->sli4_hba.max_cfg_param.rpi_base + - phba->sli4_hba.max_cfg_param.max_rpi - 1; - - longs = ((rpi_count) + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->sli4_hba.rpi_bmask = kzalloc(longs * sizeof(unsigned long), - GFP_KERNEL); - if (!phba->sli4_hba.rpi_bmask) - return -ENOMEM; + if (!phba->sli4_hba.rpi_hdrs_in_use) { + phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.max_rpi; + return rc; + } + if (phba->sli4_hba.extents_in_use) + return -EIO; rpi_hdr = lpfc_sli4_create_rpi_hdr(phba); if (!rpi_hdr) { @@ -4908,11 +5029,28 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba) struct lpfc_rpi_hdr *rpi_hdr; uint32_t rpi_count; + /* + * If the SLI4 port supports extents, posting the rpi header isn't + * required. Set the expected maximum count and let the actual value + * get set when extents are fully allocated. + */ + if (!phba->sli4_hba.rpi_hdrs_in_use) + return NULL; + if (phba->sli4_hba.extents_in_use) + return NULL; + + /* The limit on the logical index is just the max_rpi count. */ rpi_limit = phba->sli4_hba.max_cfg_param.rpi_base + - phba->sli4_hba.max_cfg_param.max_rpi - 1; + phba->sli4_hba.max_cfg_param.max_rpi - 1; spin_lock_irq(&phba->hbalock); - curr_rpi_range = phba->sli4_hba.next_rpi; + /* + * Establish the starting RPI in this header block. The starting + * rpi is normalized to a zero base because the physical rpi is + * port based. + */ + curr_rpi_range = phba->sli4_hba.next_rpi - + phba->sli4_hba.max_cfg_param.rpi_base; spin_unlock_irq(&phba->hbalock); /* @@ -4925,6 +5063,8 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba) else rpi_count = LPFC_RPI_HDR_COUNT; + if (!rpi_count) + return NULL; /* * First allocate the protocol header region for the port. The * port expects a 4KB DMA-mapped memory region that is 4K aligned. @@ -4957,12 +5097,14 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba) rpi_hdr->len = LPFC_HDR_TEMPLATE_SIZE; rpi_hdr->page_count = 1; spin_lock_irq(&phba->hbalock); - rpi_hdr->start_rpi = phba->sli4_hba.next_rpi; + + /* The rpi_hdr stores the logical index only. */ + rpi_hdr->start_rpi = curr_rpi_range; list_add_tail(&rpi_hdr->list, &phba->sli4_hba.lpfc_rpi_hdr_list); /* - * The next_rpi stores the next module-64 rpi value to post - * in any subsequent rpi memory region postings. + * The next_rpi stores the next logical module-64 rpi value used + * to post physical rpis in subsequent rpi postings. */ phba->sli4_hba.next_rpi += rpi_count; spin_unlock_irq(&phba->hbalock); @@ -4981,15 +5123,18 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba) * @phba: pointer to lpfc hba data structure. * * This routine is invoked to remove all memory resources allocated - * to support rpis. This routine presumes the caller has released all - * rpis consumed by fabric or port logins and is prepared to have - * the header pages removed. + * to support rpis for SLI4 ports not supporting extents. This routine + * presumes the caller has released all rpis consumed by fabric or port + * logins and is prepared to have the header pages removed. **/ void lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba) { struct lpfc_rpi_hdr *rpi_hdr, *next_rpi_hdr; + if (!phba->sli4_hba.rpi_hdrs_in_use) + goto exit; + list_for_each_entry_safe(rpi_hdr, next_rpi_hdr, &phba->sli4_hba.lpfc_rpi_hdr_list, list) { list_del(&rpi_hdr->list); @@ -4998,9 +5143,9 @@ lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba) kfree(rpi_hdr->dmabuf); kfree(rpi_hdr); } - - phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base; - memset(phba->sli4_hba.rpi_bmask, 0, sizeof(*phba->sli4_hba.rpi_bmask)); + exit: + /* There are no rpis available to the port now. */ + phba->sli4_hba.next_rpi = 0; } /** @@ -5487,7 +5632,8 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba) /* Final checks. The port status should be clean. */ if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, ®_data.word0) || - bf_get(lpfc_sliport_status_err, ®_data)) { + (bf_get(lpfc_sliport_status_err, ®_data) && + !bf_get(lpfc_sliport_status_rn, ®_data))) { phba->work_status[0] = readl(phba->sli4_hba.u.if_type2. ERR1regaddr); @@ -5741,7 +5887,12 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) { LPFC_MBOXQ_t *pmb; struct lpfc_mbx_read_config *rd_config; - uint32_t rc = 0; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t shdr_status, shdr_add_status; + struct lpfc_mbx_get_func_cfg *get_func_cfg; + struct lpfc_rsrc_desc_fcfcoe *desc; + uint32_t desc_count; + int length, i, rc = 0; pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) { @@ -5763,6 +5914,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) rc = -EIO; } else { rd_config = &pmb->u.mqe.un.rd_config; + phba->sli4_hba.extents_in_use = + bf_get(lpfc_mbx_rd_conf_extnts_inuse, rd_config); phba->sli4_hba.max_cfg_param.max_xri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config); phba->sli4_hba.max_cfg_param.xri_base = @@ -5781,8 +5934,6 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) bf_get(lpfc_mbx_rd_conf_vfi_base, rd_config); phba->sli4_hba.max_cfg_param.max_fcfi = bf_get(lpfc_mbx_rd_conf_fcfi_count, rd_config); - phba->sli4_hba.max_cfg_param.fcfi_base = - bf_get(lpfc_mbx_rd_conf_fcfi_base, rd_config); phba->sli4_hba.max_cfg_param.max_eq = bf_get(lpfc_mbx_rd_conf_eq_count, rd_config); phba->sli4_hba.max_cfg_param.max_rq = @@ -5800,11 +5951,13 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0; phba->max_vports = phba->max_vpi; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "2003 cfg params XRI(B:%d M:%d), " + "2003 cfg params Extents? %d " + "XRI(B:%d M:%d), " "VPI(B:%d M:%d) " "VFI(B:%d M:%d) " "RPI(B:%d M:%d) " - "FCFI(B:%d M:%d)\n", + "FCFI(Count:%d)\n", + phba->sli4_hba.extents_in_use, phba->sli4_hba.max_cfg_param.xri_base, phba->sli4_hba.max_cfg_param.max_xri, phba->sli4_hba.max_cfg_param.vpi_base, @@ -5813,10 +5966,11 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->sli4_hba.max_cfg_param.max_vfi, phba->sli4_hba.max_cfg_param.rpi_base, phba->sli4_hba.max_cfg_param.max_rpi, - phba->sli4_hba.max_cfg_param.fcfi_base, phba->sli4_hba.max_cfg_param.max_fcfi); } - mempool_free(pmb, phba->mbox_mem_pool); + + if (rc) + goto read_cfg_out; /* Reset the DFT_HBA_Q_DEPTH to the max xri */ if (phba->cfg_hba_queue_depth > @@ -5825,6 +5979,65 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->cfg_hba_queue_depth = phba->sli4_hba.max_cfg_param.max_xri - lpfc_sli4_get_els_iocb_cnt(phba); + + if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) + goto read_cfg_out; + + /* get the pf# and vf# for SLI4 if_type 2 port */ + length = (sizeof(struct lpfc_mbx_get_func_cfg) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, pmb, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG, + length, LPFC_SLI4_MBX_EMBED); + + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); + shdr = (union lpfc_sli4_cfg_shdr *) + &pmb->u.mqe.un.sli4_config.header.cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (rc || shdr_status || shdr_add_status) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "3026 Mailbox failed , mbxCmd x%x " + "GET_FUNCTION_CONFIG, mbxStatus x%x\n", + bf_get(lpfc_mqe_command, &pmb->u.mqe), + bf_get(lpfc_mqe_status, &pmb->u.mqe)); + rc = -EIO; + goto read_cfg_out; + } + + /* search for fc_fcoe resrouce descriptor */ + get_func_cfg = &pmb->u.mqe.un.get_func_cfg; + desc_count = get_func_cfg->func_cfg.rsrc_desc_count; + + for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) { + desc = (struct lpfc_rsrc_desc_fcfcoe *) + &get_func_cfg->func_cfg.desc[i]; + if (LPFC_RSRC_DESC_TYPE_FCFCOE == + bf_get(lpfc_rsrc_desc_pcie_type, desc)) { + phba->sli4_hba.iov.pf_number = + bf_get(lpfc_rsrc_desc_fcfcoe_pfnum, desc); + phba->sli4_hba.iov.vf_number = + bf_get(lpfc_rsrc_desc_fcfcoe_vfnum, desc); + break; + } + } + + if (i < LPFC_RSRC_DESC_MAX_NUM) + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "3027 GET_FUNCTION_CONFIG: pf_number:%d, " + "vf_number:%d\n", phba->sli4_hba.iov.pf_number, + phba->sli4_hba.iov.vf_number); + else { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "3028 GET_FUNCTION_CONFIG: failed to find " + "Resrouce Descriptor:x%x\n", + LPFC_RSRC_DESC_TYPE_FCFCOE); + rc = -EIO; + } + +read_cfg_out: + mempool_free(pmb, phba->mbox_mem_pool); return rc; } @@ -6229,8 +6442,10 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) phba->sli4_hba.mbx_cq = NULL; /* Release FCP response complete queue */ - for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) + fcp_qidx = 0; + do lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_qidx]); + while (++fcp_qidx < phba->cfg_fcp_eq_count); kfree(phba->sli4_hba.fcp_cq); phba->sli4_hba.fcp_cq = NULL; @@ -6353,16 +6568,24 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) phba->sli4_hba.sp_eq->queue_id); /* Set up fast-path FCP Response Complete Queue */ - for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) { + fcp_cqidx = 0; + do { if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0526 Fast-path FCP CQ (%d) not " "allocated\n", fcp_cqidx); goto out_destroy_fcp_cq; } - rc = lpfc_cq_create(phba, phba->sli4_hba.fcp_cq[fcp_cqidx], - phba->sli4_hba.fp_eq[fcp_cqidx], - LPFC_WCQ, LPFC_FCP); + if (phba->cfg_fcp_eq_count) + rc = lpfc_cq_create(phba, + phba->sli4_hba.fcp_cq[fcp_cqidx], + phba->sli4_hba.fp_eq[fcp_cqidx], + LPFC_WCQ, LPFC_FCP); + else + rc = lpfc_cq_create(phba, + phba->sli4_hba.fcp_cq[fcp_cqidx], + phba->sli4_hba.sp_eq, + LPFC_WCQ, LPFC_FCP); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0527 Failed setup of fast-path FCP " @@ -6371,12 +6594,15 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) } lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "2588 FCP CQ setup: cq[%d]-id=%d, " - "parent eq[%d]-id=%d\n", + "parent %seq[%d]-id=%d\n", fcp_cqidx, phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id, + (phba->cfg_fcp_eq_count) ? "" : "sp_", fcp_cqidx, - phba->sli4_hba.fp_eq[fcp_cqidx]->queue_id); - } + (phba->cfg_fcp_eq_count) ? + phba->sli4_hba.fp_eq[fcp_cqidx]->queue_id : + phba->sli4_hba.sp_eq->queue_id); + } while (++fcp_cqidx < phba->cfg_fcp_eq_count); /* * Set up all the Work Queues (WQs) @@ -6445,7 +6671,9 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) fcp_cq_index, phba->sli4_hba.fcp_cq[fcp_cq_index]->queue_id); /* Round robin FCP Work Queue's Completion Queue assignment */ - fcp_cq_index = ((fcp_cq_index + 1) % phba->cfg_fcp_eq_count); + if (phba->cfg_fcp_eq_count) + fcp_cq_index = ((fcp_cq_index + 1) % + phba->cfg_fcp_eq_count); } /* @@ -6827,6 +7055,8 @@ lpfc_pci_function_reset(struct lpfc_hba *phba) if (rdy_chk < 1000) break; } + /* delay driver action following IF_TYPE_2 function reset */ + msleep(100); break; case LPFC_SLI_INTF_IF_TYPE_1: default: @@ -7419,11 +7649,15 @@ enable_msix_vectors: /* * Assign MSI-X vectors to interrupt handlers */ - - /* The first vector must associated to slow-path handler for MQ */ - rc = request_irq(phba->sli4_hba.msix_entries[0].vector, - &lpfc_sli4_sp_intr_handler, IRQF_SHARED, - LPFC_SP_DRIVER_HANDLER_NAME, phba); + if (vectors > 1) + rc = request_irq(phba->sli4_hba.msix_entries[0].vector, + &lpfc_sli4_sp_intr_handler, IRQF_SHARED, + LPFC_SP_DRIVER_HANDLER_NAME, phba); + else + /* All Interrupts need to be handled by one EQ */ + rc = request_irq(phba->sli4_hba.msix_entries[0].vector, + &lpfc_sli4_intr_handler, IRQF_SHARED, + LPFC_DRIVER_NAME, phba); if (rc) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0485 MSI-X slow-path request_irq failed " @@ -7765,6 +7999,7 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) { int wait_cnt = 0; LPFC_MBOXQ_t *mboxq; + struct pci_dev *pdev = phba->pcidev; lpfc_stop_hba_timers(phba); phba->sli4_hba.intr_enable = 0; @@ -7804,6 +8039,10 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) /* Disable PCI subsystem interrupt */ lpfc_sli4_disable_intr(phba); + /* Disable SR-IOV if enabled */ + if (phba->cfg_sriov_nr_virtfn) + pci_disable_sriov(pdev); + /* Stop kthread signal shall trigger work_done one more time */ kthread_stop(phba->worker_thread); @@ -7878,6 +8117,11 @@ lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) sli4_params->hdr_pp_align = bf_get(hdr_pp_align, &mqe->un.sli4_params); sli4_params->sgl_pages_max = bf_get(sgl_pages, &mqe->un.sli4_params); sli4_params->sgl_pp_align = bf_get(sgl_pp_align, &mqe->un.sli4_params); + + /* Make sure that sge_supp_len can be handled by the driver */ + if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE) + sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE; + return rc; } @@ -7902,6 +8146,13 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) int length; struct lpfc_sli4_parameters *mbx_sli4_parameters; + /* + * By default, the driver assumes the SLI4 port requires RPI + * header postings. The SLI4_PARAM response will correct this + * assumption. + */ + phba->sli4_hba.rpi_hdrs_in_use = 1; + /* Read the port's SLI4 Config Parameters */ length = (sizeof(struct lpfc_mbx_get_sli4_parameters) - sizeof(struct lpfc_sli4_cfg_mhdr)); @@ -7938,6 +8189,13 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) mbx_sli4_parameters); sli4_params->sgl_pp_align = bf_get(cfg_sgl_pp_align, mbx_sli4_parameters); + phba->sli4_hba.extents_in_use = bf_get(cfg_ext, mbx_sli4_parameters); + phba->sli4_hba.rpi_hdrs_in_use = bf_get(cfg_hdrr, mbx_sli4_parameters); + + /* Make sure that sge_supp_len can be handled by the driver */ + if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE) + sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE; + return 0; } @@ -8173,6 +8431,10 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev) lpfc_debugfs_terminate(vport); + /* Disable SR-IOV if enabled */ + if (phba->cfg_sriov_nr_virtfn) + pci_disable_sriov(pdev); + /* Disable interrupt */ lpfc_sli_disable_intr(phba); @@ -8565,6 +8827,97 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba) } /** + * lpfc_write_firmware - attempt to write a firmware image to the port + * @phba: pointer to lpfc hba data structure. + * @fw: pointer to firmware image returned from request_firmware. + * + * returns the number of bytes written if write is successful. + * returns a negative error value if there were errors. + * returns 0 if firmware matches currently active firmware on port. + **/ +int +lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) +{ + char fwrev[32]; + struct lpfc_grp_hdr *image = (struct lpfc_grp_hdr *)fw->data; + struct list_head dma_buffer_list; + int i, rc = 0; + struct lpfc_dmabuf *dmabuf, *next; + uint32_t offset = 0, temp_offset = 0; + + INIT_LIST_HEAD(&dma_buffer_list); + if ((image->magic_number != LPFC_GROUP_OJECT_MAGIC_NUM) || + (bf_get(lpfc_grp_hdr_file_type, image) != LPFC_FILE_TYPE_GROUP) || + (bf_get(lpfc_grp_hdr_id, image) != LPFC_FILE_ID_GROUP) || + (image->size != fw->size)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3022 Invalid FW image found. " + "Magic:%d Type:%x ID:%x\n", + image->magic_number, + bf_get(lpfc_grp_hdr_file_type, image), + bf_get(lpfc_grp_hdr_id, image)); + return -EINVAL; + } + lpfc_decode_firmware_rev(phba, fwrev, 1); + if (strncmp(fwrev, image->rev_name, strnlen(fwrev, 16))) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3023 Updating Firmware. Current Version:%s " + "New Version:%s\n", + fwrev, image->rev_name); + for (i = 0; i < LPFC_MBX_WR_CONFIG_MAX_BDE; i++) { + dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), + GFP_KERNEL); + if (!dmabuf) { + rc = -ENOMEM; + goto out; + } + dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, + SLI4_PAGE_SIZE, + &dmabuf->phys, + GFP_KERNEL); + if (!dmabuf->virt) { + kfree(dmabuf); + rc = -ENOMEM; + goto out; + } + list_add_tail(&dmabuf->list, &dma_buffer_list); + } + while (offset < fw->size) { + temp_offset = offset; + list_for_each_entry(dmabuf, &dma_buffer_list, list) { + if (offset + SLI4_PAGE_SIZE > fw->size) { + temp_offset += fw->size - offset; + memcpy(dmabuf->virt, + fw->data + temp_offset, + fw->size - offset); + break; + } + temp_offset += SLI4_PAGE_SIZE; + memcpy(dmabuf->virt, fw->data + temp_offset, + SLI4_PAGE_SIZE); + } + rc = lpfc_wr_object(phba, &dma_buffer_list, + (fw->size - offset), &offset); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3024 Firmware update failed. " + "%d\n", rc); + goto out; + } + } + rc = offset; + } +out: + list_for_each_entry_safe(dmabuf, next, &dma_buffer_list, list) { + list_del(&dmabuf->list); + dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, + dmabuf->virt, dmabuf->phys); + kfree(dmabuf); + } + return rc; +} + +/** * lpfc_pci_probe_one_s4 - PCI probe func to reg SLI-4 device to PCI subsys * @pdev: pointer to PCI device * @pid: pointer to PCI device identifier @@ -8591,6 +8944,10 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) int error; uint32_t cfg_mode, intr_mode; int mcnt; + int adjusted_fcp_eq_count; + int fcp_qidx; + const struct firmware *fw; + uint8_t file_name[16]; /* Allocate memory for HBA structure */ phba = lpfc_hba_alloc(pdev); @@ -8688,11 +9045,25 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) error = -ENODEV; goto out_free_sysfs_attr; } - /* Default to single FCP EQ for non-MSI-X */ + /* Default to single EQ for non-MSI-X */ if (phba->intr_type != MSIX) - phba->cfg_fcp_eq_count = 1; - else if (phba->sli4_hba.msix_vec_nr < phba->cfg_fcp_eq_count) - phba->cfg_fcp_eq_count = phba->sli4_hba.msix_vec_nr - 1; + adjusted_fcp_eq_count = 0; + else if (phba->sli4_hba.msix_vec_nr < + phba->cfg_fcp_eq_count + 1) + adjusted_fcp_eq_count = phba->sli4_hba.msix_vec_nr - 1; + else + adjusted_fcp_eq_count = phba->cfg_fcp_eq_count; + /* Free unused EQs */ + for (fcp_qidx = adjusted_fcp_eq_count; + fcp_qidx < phba->cfg_fcp_eq_count; + fcp_qidx++) { + lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_qidx]); + /* do not delete the first fcp_cq */ + if (fcp_qidx) + lpfc_sli4_queue_free( + phba->sli4_hba.fcp_cq[fcp_qidx]); + } + phba->cfg_fcp_eq_count = adjusted_fcp_eq_count; /* Set up SLI-4 HBA */ if (lpfc_sli4_hba_setup(phba)) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -8731,6 +9102,14 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Perform post initialization setup */ lpfc_post_init_setup(phba); + /* check for firmware upgrade or downgrade */ + snprintf(file_name, 16, "%s.grp", phba->ModelName); + error = request_firmware(&fw, file_name, &phba->pcidev->dev); + if (!error) { + lpfc_write_firmware(phba, fw); + release_firmware(fw); + } + /* Check if there are static vports to be created. */ lpfc_create_static_vport(phba); @@ -9498,6 +9877,10 @@ static struct pci_device_id lpfc_id_table[] = { PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE, PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC_VF, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE_VF, + PCI_ANY_ID, PCI_ANY_ID, }, { 0 } }; diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index e6ce9033f85e..556767028353 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -610,7 +610,8 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi) mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys); mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys); - mb->un.varRdSparm.vpi = vpi + phba->vpi_base; + if (phba->sli_rev >= LPFC_SLI_REV3) + mb->un.varRdSparm.vpi = phba->vpi_ids[vpi]; /* save address for completion */ pmb->context1 = mp; @@ -643,9 +644,10 @@ lpfc_unreg_did(struct lpfc_hba * phba, uint16_t vpi, uint32_t did, memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); mb->un.varUnregDID.did = did; - if (vpi != 0xffff) - vpi += phba->vpi_base; mb->un.varUnregDID.vpi = vpi; + if ((vpi != 0xffff) && + (phba->sli_rev == LPFC_SLI_REV4)) + mb->un.varUnregDID.vpi = phba->vpi_ids[vpi]; mb->mbxCommand = MBX_UNREG_D_ID; mb->mbxOwner = OWN_HOST; @@ -738,12 +740,10 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did, memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); mb->un.varRegLogin.rpi = 0; - if (phba->sli_rev == LPFC_SLI_REV4) { - mb->un.varRegLogin.rpi = rpi; - if (mb->un.varRegLogin.rpi == LPFC_RPI_ALLOC_ERROR) - return 1; - } - mb->un.varRegLogin.vpi = vpi + phba->vpi_base; + if (phba->sli_rev == LPFC_SLI_REV4) + mb->un.varRegLogin.rpi = phba->sli4_hba.rpi_ids[rpi]; + if (phba->sli_rev >= LPFC_SLI_REV3) + mb->un.varRegLogin.vpi = phba->vpi_ids[vpi]; mb->un.varRegLogin.did = did; mb->mbxOwner = OWN_HOST; /* Get a buffer to hold NPorts Service Parameters */ @@ -757,7 +757,7 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did, lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, "0302 REG_LOGIN: no buffers, VPI:%d DID:x%x, " "rpi x%x\n", vpi, did, rpi); - return (1); + return 1; } INIT_LIST_HEAD(&mp->list); sparam = mp->virt; @@ -773,7 +773,7 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did, mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys); mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys); - return (0); + return 0; } /** @@ -789,6 +789,9 @@ lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did, * * This routine prepares the mailbox command for unregistering remote port * login. + * + * For SLI4 ports, the rpi passed to this function must be the physical + * rpi value, not the logical index. **/ void lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi, @@ -799,9 +802,10 @@ lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi, mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); - mb->un.varUnregLogin.rpi = (uint16_t) rpi; + mb->un.varUnregLogin.rpi = rpi; mb->un.varUnregLogin.rsvd1 = 0; - mb->un.varUnregLogin.vpi = vpi + phba->vpi_base; + if (phba->sli_rev >= LPFC_SLI_REV3) + mb->un.varUnregLogin.vpi = phba->vpi_ids[vpi]; mb->mbxCommand = MBX_UNREG_LOGIN; mb->mbxOwner = OWN_HOST; @@ -825,9 +829,16 @@ lpfc_sli4_unreg_all_rpis(struct lpfc_vport *vport) mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { - lpfc_unreg_login(phba, vport->vpi, - vport->vpi + phba->vpi_base, mbox); - mbox->u.mb.un.varUnregLogin.rsvd1 = 0x4000 ; + /* + * For SLI4 functions, the rpi field is overloaded for + * the vport context unreg all. This routine passes + * 0 for the rpi field in lpfc_unreg_login for compatibility + * with SLI3 and then overrides the rpi field with the + * expected value for SLI4. + */ + lpfc_unreg_login(phba, vport->vpi, phba->vpi_ids[vport->vpi], + mbox); + mbox->u.mb.un.varUnregLogin.rsvd1 = 0x4000; mbox->vport = vport; mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; mbox->context1 = NULL; @@ -865,9 +876,13 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb) if ((phba->sli_rev == LPFC_SLI_REV4) && !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) mb->un.varRegVpi.upd = 1; - mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base; + + mb->un.varRegVpi.vpi = phba->vpi_ids[vport->vpi]; mb->un.varRegVpi.sid = vport->fc_myDID; - mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base; + if (phba->sli_rev == LPFC_SLI_REV4) + mb->un.varRegVpi.vfi = phba->sli4_hba.vfi_ids[vport->vfi]; + else + mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base; memcpy(mb->un.varRegVpi.wwn, &vport->fc_portname, sizeof(struct lpfc_name)); mb->un.varRegVpi.wwn[0] = cpu_to_le32(mb->un.varRegVpi.wwn[0]); @@ -901,10 +916,10 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb) MAILBOX_t *mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); - if (phba->sli_rev < LPFC_SLI_REV4) - mb->un.varUnregVpi.vpi = vpi + phba->vpi_base; - else - mb->un.varUnregVpi.sli4_vpi = vpi + phba->vpi_base; + if (phba->sli_rev == LPFC_SLI_REV3) + mb->un.varUnregVpi.vpi = phba->vpi_ids[vpi]; + else if (phba->sli_rev >= LPFC_SLI_REV4) + mb->un.varUnregVpi.sli4_vpi = phba->vpi_ids[vpi]; mb->mbxCommand = MBX_UNREG_VPI; mb->mbxOwner = OWN_HOST; @@ -1735,12 +1750,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, return length; } - /* Setup for the none-embedded mbox command */ + /* Setup for the non-embedded mbox command */ pcount = (SLI4_PAGE_ALIGN(length))/SLI4_PAGE_SIZE; pcount = (pcount > LPFC_SLI4_MBX_SGE_MAX_PAGES) ? LPFC_SLI4_MBX_SGE_MAX_PAGES : pcount; /* Allocate record for keeping SGE virtual addresses */ - mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt), + mbox->sge_array = kzalloc(sizeof(struct lpfc_mbx_nembed_sge_virt), GFP_KERNEL); if (!mbox->sge_array) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, @@ -1790,12 +1805,87 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, /* The sub-header is in DMA memory, which needs endian converstion */ if (cfg_shdr) lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr, - sizeof(union lpfc_sli4_cfg_shdr)); - + sizeof(union lpfc_sli4_cfg_shdr)); return alloc_len; } /** + * lpfc_sli4_mbox_rsrc_extent - Initialize the opcode resource extent. + * @phba: pointer to lpfc hba data structure. + * @mbox: pointer to an allocated lpfc mbox resource. + * @exts_count: the number of extents, if required, to allocate. + * @rsrc_type: the resource extent type. + * @emb: true if LPFC_SLI4_MBX_EMBED. false if LPFC_SLI4_MBX_NEMBED. + * + * This routine completes the subcommand header for SLI4 resource extent + * mailbox commands. It is called after lpfc_sli4_config. The caller must + * pass an allocated mailbox and the attributes required to initialize the + * mailbox correctly. + * + * Return: the actual length of the mbox command allocated. + **/ +int +lpfc_sli4_mbox_rsrc_extent(struct lpfc_hba *phba, struct lpfcMboxq *mbox, + uint16_t exts_count, uint16_t rsrc_type, bool emb) +{ + uint8_t opcode = 0; + struct lpfc_mbx_nembed_rsrc_extent *n_rsrc_extnt = NULL; + void *virtaddr = NULL; + + /* Set up SLI4 ioctl command header fields */ + if (emb == LPFC_SLI4_MBX_NEMBED) { + /* Get the first SGE entry from the non-embedded DMA memory */ + virtaddr = mbox->sge_array->addr[0]; + if (virtaddr == NULL) + return 1; + n_rsrc_extnt = (struct lpfc_mbx_nembed_rsrc_extent *) virtaddr; + } + + /* + * The resource type is common to all extent Opcodes and resides in the + * same position. + */ + if (emb == LPFC_SLI4_MBX_EMBED) + bf_set(lpfc_mbx_alloc_rsrc_extents_type, + &mbox->u.mqe.un.alloc_rsrc_extents.u.req, + rsrc_type); + else { + /* This is DMA data. Byteswap is required. */ + bf_set(lpfc_mbx_alloc_rsrc_extents_type, + n_rsrc_extnt, rsrc_type); + lpfc_sli_pcimem_bcopy(&n_rsrc_extnt->word4, + &n_rsrc_extnt->word4, + sizeof(uint32_t)); + } + + /* Complete the initialization for the particular Opcode. */ + opcode = lpfc_sli4_mbox_opcode_get(phba, mbox); + switch (opcode) { + case LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT: + if (emb == LPFC_SLI4_MBX_EMBED) + bf_set(lpfc_mbx_alloc_rsrc_extents_cnt, + &mbox->u.mqe.un.alloc_rsrc_extents.u.req, + exts_count); + else + bf_set(lpfc_mbx_alloc_rsrc_extents_cnt, + n_rsrc_extnt, exts_count); + break; + case LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT: + case LPFC_MBOX_OPCODE_GET_RSRC_EXTENT_INFO: + case LPFC_MBOX_OPCODE_DEALLOC_RSRC_EXTENT: + /* Initialization is complete.*/ + break; + default: + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "2929 Resource Extent Opcode x%x is " + "unsupported\n", opcode); + return 1; + } + + return 0; +} + +/** * lpfc_sli4_mbox_opcode_get - Get the opcode from a sli4 mailbox command * @phba: pointer to lpfc hba data structure. * @mbox: pointer to lpfc mbox command. @@ -1939,9 +2029,12 @@ lpfc_init_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport) bf_set(lpfc_init_vfi_vr, init_vfi, 1); bf_set(lpfc_init_vfi_vt, init_vfi, 1); bf_set(lpfc_init_vfi_vp, init_vfi, 1); - bf_set(lpfc_init_vfi_vfi, init_vfi, vport->vfi + vport->phba->vfi_base); - bf_set(lpfc_init_vpi_vpi, init_vfi, vport->vpi + vport->phba->vpi_base); - bf_set(lpfc_init_vfi_fcfi, init_vfi, vport->phba->fcf.fcfi); + bf_set(lpfc_init_vfi_vfi, init_vfi, + vport->phba->sli4_hba.vfi_ids[vport->vfi]); + bf_set(lpfc_init_vpi_vpi, init_vfi, + vport->phba->vpi_ids[vport->vpi]); + bf_set(lpfc_init_vfi_fcfi, init_vfi, + vport->phba->fcf.fcfi); } /** @@ -1964,9 +2057,10 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys) reg_vfi = &mbox->u.mqe.un.reg_vfi; bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_VFI); bf_set(lpfc_reg_vfi_vp, reg_vfi, 1); - bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base); + bf_set(lpfc_reg_vfi_vfi, reg_vfi, + vport->phba->sli4_hba.vfi_ids[vport->vfi]); bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi); - bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base); + bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->phba->vpi_ids[vport->vpi]); memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name)); reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]); reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]); @@ -1997,9 +2091,9 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi) memset(mbox, 0, sizeof(*mbox)); bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VPI); bf_set(lpfc_init_vpi_vpi, &mbox->u.mqe.un.init_vpi, - vpi + phba->vpi_base); + phba->vpi_ids[vpi]); bf_set(lpfc_init_vpi_vfi, &mbox->u.mqe.un.init_vpi, - phba->pport->vfi + phba->vfi_base); + phba->sli4_hba.vfi_ids[phba->pport->vfi]); } /** @@ -2019,7 +2113,7 @@ lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport) memset(mbox, 0, sizeof(*mbox)); bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI); bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, - vport->vfi + vport->phba->vfi_base); + vport->phba->sli4_hba.vfi_ids[vport->vfi]); } /** @@ -2131,12 +2225,14 @@ lpfc_unreg_fcfi(struct lpfcMboxq *mbox, uint16_t fcfi) void lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp) { + struct lpfc_hba *phba = ndlp->phba; struct lpfc_mbx_resume_rpi *resume_rpi; memset(mbox, 0, sizeof(*mbox)); resume_rpi = &mbox->u.mqe.un.resume_rpi; bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_RESUME_RPI); - bf_set(lpfc_resume_rpi_index, resume_rpi, ndlp->nlp_rpi); + bf_set(lpfc_resume_rpi_index, resume_rpi, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); bf_set(lpfc_resume_rpi_ii, resume_rpi, RESUME_INDEX_RPI); resume_rpi->event_tag = ndlp->phba->fc_eventTag; } diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index cbb48ee8b0bb..10d5b5e41499 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -62,7 +62,6 @@ int lpfc_mem_alloc(struct lpfc_hba *phba, int align) { struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; - int longs; int i; if (phba->sli_rev == LPFC_SLI_REV4) @@ -138,17 +137,8 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align) phba->lpfc_hrb_pool = NULL; phba->lpfc_drb_pool = NULL; } - /* vpi zero is reserved for the physical port so add 1 to max */ - longs = ((phba->max_vpi + 1) + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), GFP_KERNEL); - if (!phba->vpi_bmask) - goto fail_free_dbq_pool; return 0; - - fail_free_dbq_pool: - pci_pool_destroy(phba->lpfc_drb_pool); - phba->lpfc_drb_pool = NULL; fail_free_hrb_pool: pci_pool_destroy(phba->lpfc_hrb_pool); phba->lpfc_hrb_pool = NULL; @@ -191,9 +181,6 @@ lpfc_mem_free(struct lpfc_hba *phba) int i; struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; - /* Free VPI bitmask memory */ - kfree(phba->vpi_bmask); - /* Free HBQ pools */ lpfc_sli_hbqbuf_free_all(phba); if (phba->lpfc_drb_pool) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 0d92d4205ea6..2ddd02f7c603 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -350,11 +350,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb; - /* - * Need to unreg_login if we are already in one of these states and - * change to NPR state. This will block the port until after the ACC - * completes and the reg_login is issued and completed. - */ + /* no need to reg_login if we are already in one of these states */ switch (ndlp->nlp_state) { case NLP_STE_NPR_NODE: if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) @@ -363,9 +359,8 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, case NLP_STE_PRLI_ISSUE: case NLP_STE_UNMAPPED_NODE: case NLP_STE_MAPPED_NODE: - lpfc_unreg_rpi(vport, ndlp); - ndlp->nlp_prev_state = ndlp->nlp_state; - lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL); + return 1; } if ((vport->fc_flag & FC_PT2PT) && @@ -657,6 +652,7 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_unreg_rpi(vport, ndlp); return 0; } + /** * lpfc_release_rpi - Release a RPI by issuing unreg_login mailbox cmd. * @phba : Pointer to lpfc_hba structure. @@ -1399,8 +1395,11 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, if (mb->mbxStatus) { /* RegLogin failed */ lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, - "0246 RegLogin failed Data: x%x x%x x%x\n", - did, mb->mbxStatus, vport->port_state); + "0246 RegLogin failed Data: x%x x%x x%x x%x " + "x%x\n", + did, mb->mbxStatus, vport->port_state, + mb->un.varRegLogin.vpi, + mb->un.varRegLogin.rpi); /* * If RegLogin failed due to lack of HBA resources do not * retry discovery. @@ -1424,7 +1423,10 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, return ndlp->nlp_state; } - ndlp->nlp_rpi = mb->un.varWords[0]; + /* SLI4 ports have preallocated logical rpis. */ + if (vport->phba->sli_rev < LPFC_SLI_REV4) + ndlp->nlp_rpi = mb->un.varWords[0]; + ndlp->nlp_flag |= NLP_RPI_REGISTERED; /* Only if we are not a fabric nport do we issue PRLI */ @@ -2025,7 +2027,9 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport, MAILBOX_t *mb = &pmb->u.mb; if (!mb->mbxStatus) { - ndlp->nlp_rpi = mb->un.varWords[0]; + /* SLI4 ports have preallocated logical rpis. */ + if (vport->phba->sli_rev < LPFC_SLI_REV4) + ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; } else { if (ndlp->nlp_flag & NLP_NODEV_REMOVE) { diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 84e4481b2406..3ccc97496ebf 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -743,7 +743,14 @@ lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba) if (bcnt == 0) continue; /* Now, post the SCSI buffer list sgls as a block */ - status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt); + if (!phba->sli4_hba.extents_in_use) + status = lpfc_sli4_post_scsi_sgl_block(phba, + &sblist, + bcnt); + else + status = lpfc_sli4_post_scsi_sgl_blk_ext(phba, + &sblist, + bcnt); /* Reset SCSI buffer count for next round of posting */ bcnt = 0; while (!list_empty(&sblist)) { @@ -787,7 +794,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) dma_addr_t pdma_phys_fcp_cmd; dma_addr_t pdma_phys_fcp_rsp; dma_addr_t pdma_phys_bpl, pdma_phys_bpl1; - uint16_t iotag, last_xritag = NO_XRI; + uint16_t iotag, last_xritag = NO_XRI, lxri = 0; int status = 0, index; int bcnt; int non_sequential_xri = 0; @@ -823,13 +830,15 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) break; } - psb->cur_iocbq.sli4_xritag = lpfc_sli4_next_xritag(phba); - if (psb->cur_iocbq.sli4_xritag == NO_XRI) { + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; } + psb->cur_iocbq.sli4_lxritag = lxri; + psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; if (last_xritag != NO_XRI && psb->cur_iocbq.sli4_xritag != (last_xritag+1)) { non_sequential_xri = 1; @@ -861,6 +870,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd)); + sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 0); sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd)); @@ -869,6 +879,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) /* Setup the physical region for the FCP RSP */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp)); + sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp)); @@ -914,7 +925,21 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) } } if (bcnt) { - status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt); + if (!phba->sli4_hba.extents_in_use) + status = lpfc_sli4_post_scsi_sgl_block(phba, + &sblist, + bcnt); + else + status = lpfc_sli4_post_scsi_sgl_blk_ext(phba, + &sblist, + bcnt); + + if (status) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "3021 SCSI SGL post error %d\n", + status); + bcnt = 0; + } /* Reset SCSI buffer count for next round of posting */ while (!list_empty(&sblist)) { list_remove_head(&sblist, psb, struct lpfc_scsi_buf, @@ -2081,6 +2106,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) dma_len = sg_dma_len(sgel); sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr)); sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr)); + sgl->word2 = le32_to_cpu(sgl->word2); if ((num_bde + 1) == nseg) bf_set(lpfc_sli4_sge_last, sgl, 1); else @@ -2794,6 +2820,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, * of the scsi_cmnd request_buffer */ piocbq->iocb.ulpContext = pnode->nlp_rpi; + if (phba->sli_rev == LPFC_SLI_REV4) + piocbq->iocb.ulpContext = + phba->sli4_hba.rpi_ids[pnode->nlp_rpi]; if (pnode->nlp_fcp_info & NLP_FCP_2_DEVICE) piocbq->iocb.ulpFCP2Rcvy = 1; else @@ -2807,7 +2836,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, } /** - * lpfc_scsi_prep_task_mgmt_cmnd - Convert SLI3 scsi TM cmd to FCP info unit + * lpfc_scsi_prep_task_mgmt_cmd - Convert SLI3 scsi TM cmd to FCP info unit * @vport: The virtual port for which this call is being executed. * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure. * @lun: Logical unit number. @@ -2851,6 +2880,10 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport, lpfc_fcpcmd_to_iocb(piocb->unsli3.fcp_ext.icd, fcp_cmnd); piocb->ulpCommand = CMD_FCP_ICMND64_CR; piocb->ulpContext = ndlp->nlp_rpi; + if (vport->phba->sli_rev == LPFC_SLI_REV4) { + piocb->ulpContext = + vport->phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; + } if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { piocb->ulpFCP2Rcvy = 1; } @@ -3405,9 +3438,10 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "0702 Issue %s to TGT %d LUN %d " - "rpi x%x nlp_flag x%x\n", + "rpi x%x nlp_flag x%x Data: x%x x%x\n", lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id, - pnode->nlp_rpi, pnode->nlp_flag); + pnode->nlp_rpi, pnode->nlp_flag, iocbq->sli4_xritag, + iocbq->iocb_flag); status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING, iocbq, iocbqrsp, lpfc_cmd->timeout); @@ -3419,10 +3453,12 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, ret = FAILED; lpfc_cmd->status = IOSTAT_DRIVER_REJECT; lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, - "0727 TMF %s to TGT %d LUN %d failed (%d, %d)\n", + "0727 TMF %s to TGT %d LUN %d failed (%d, %d) " + "iocb_flag x%x\n", lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id, iocbqrsp->iocb.ulpStatus, - iocbqrsp->iocb.un.ulpWord[4]); + iocbqrsp->iocb.un.ulpWord[4], + iocbq->iocb_flag); } else if (status == IOCB_BUSY) ret = FAILED; else diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index fd5835e1c039..98999bbd8cbf 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -65,6 +65,9 @@ static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *, struct lpfc_iocbq *); static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *, struct hbq_dmabuf *); +static int lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *, struct lpfc_queue *, + struct lpfc_cqe *); + static IOCB_t * lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) { @@ -456,7 +459,6 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba) struct lpfc_iocbq * iocbq = NULL; list_remove_head(lpfc_iocb_list, iocbq, struct lpfc_iocbq, list); - if (iocbq) phba->iocb_cnt++; if (phba->iocb_cnt > phba->iocb_max) @@ -479,13 +481,10 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba) static struct lpfc_sglq * __lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag) { - uint16_t adj_xri; struct lpfc_sglq *sglq; - adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri) - return NULL; - sglq = phba->sli4_hba.lpfc_sglq_active_list[adj_xri]; - phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = NULL; + + sglq = phba->sli4_hba.lpfc_sglq_active_list[xritag]; + phba->sli4_hba.lpfc_sglq_active_list[xritag] = NULL; return sglq; } @@ -504,12 +503,9 @@ __lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag) struct lpfc_sglq * __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag) { - uint16_t adj_xri; struct lpfc_sglq *sglq; - adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri) - return NULL; - sglq = phba->sli4_hba.lpfc_sglq_active_list[adj_xri]; + + sglq = phba->sli4_hba.lpfc_sglq_active_list[xritag]; return sglq; } @@ -532,7 +528,6 @@ static int __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, uint16_t xritag, uint16_t rxid, uint16_t send_rrq) { - uint16_t adj_xri; struct lpfc_node_rrq *rrq; int empty; uint32_t did = 0; @@ -553,21 +548,19 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, /* * set the active bit even if there is no mem available. */ - adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (NLP_CHK_FREE_REQ(ndlp)) goto out; if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING)) goto out; - if (test_and_set_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) + if (test_and_set_bit(xritag, ndlp->active_rrqs.xri_bitmap)) goto out; rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL); if (rrq) { rrq->send_rrq = send_rrq; - rrq->xritag = xritag; + rrq->xritag = phba->sli4_hba.xri_ids[xritag]; rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1); rrq->ndlp = ndlp; rrq->nlp_DID = ndlp->nlp_DID; @@ -603,7 +596,6 @@ lpfc_clr_rrq_active(struct lpfc_hba *phba, uint16_t xritag, struct lpfc_node_rrq *rrq) { - uint16_t adj_xri; struct lpfc_nodelist *ndlp = NULL; if ((rrq->vport) && NLP_CHK_NODE_ACT(rrq->ndlp)) @@ -619,8 +611,7 @@ lpfc_clr_rrq_active(struct lpfc_hba *phba, if (!ndlp) goto out; - adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (test_and_clear_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) { + if (test_and_clear_bit(xritag, ndlp->active_rrqs.xri_bitmap)) { rrq->send_rrq = 0; rrq->xritag = 0; rrq->rrq_stop_time = 0; @@ -796,12 +787,9 @@ int lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, uint16_t xritag) { - uint16_t adj_xri; - - adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; if (!ndlp) return 0; - if (test_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) + if (test_bit(xritag, ndlp->active_rrqs.xri_bitmap)) return 1; else return 0; @@ -841,7 +829,7 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, * @piocb: Pointer to the iocbq. * * This function is called with hbalock held. This function - * Gets a new driver sglq object from the sglq list. If the + * gets a new driver sglq object from the sglq list. If the * list is not empty then it is successful, it returns pointer to the newly * allocated sglq object else it returns NULL. **/ @@ -851,7 +839,6 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list; struct lpfc_sglq *sglq = NULL; struct lpfc_sglq *start_sglq = NULL; - uint16_t adj_xri; struct lpfc_scsi_buf *lpfc_cmd; struct lpfc_nodelist *ndlp; int found = 0; @@ -870,8 +857,6 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) while (!found) { if (!sglq) return NULL; - adj_xri = sglq->sli4_xritag - - phba->sli4_hba.max_cfg_param.xri_base; if (lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) { /* This xri has an rrq outstanding for this DID. * put it back in the list and get another xri. @@ -888,7 +873,7 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) } sglq->ndlp = ndlp; found = 1; - phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq; + phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq; sglq->state = SGL_ALLOCATED; } return sglq; @@ -944,7 +929,8 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) if (iocbq->sli4_xritag == NO_XRI) sglq = NULL; else - sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag); + sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_lxritag); + if (sglq) { if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) && (sglq->state != SGL_XRI_ABORTED)) { @@ -971,6 +957,7 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) * Clean all volatile data fields, preserve iotag and node struct. */ memset((char *)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean); + iocbq->sli4_lxritag = NO_XRI; iocbq->sli4_xritag = NO_XRI; list_add_tail(&iocbq->list, &phba->lpfc_iocb_list); } @@ -2113,7 +2100,7 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) pmb->u.mb.mbxCommand == MBX_REG_LOGIN64 && !pmb->u.mb.mbxStatus) { rpi = pmb->u.mb.un.varWords[0]; - vpi = pmb->u.mb.un.varRegLogin.vpi - phba->vpi_base; + vpi = pmb->u.mb.un.varRegLogin.vpi; lpfc_unreg_login(phba, vpi, rpi, pmb); pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); @@ -3881,8 +3868,10 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) list_del_init(&phba->sli4_hba.els_cq->list); for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++) list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list); - for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++) + qindx = 0; + do list_del_init(&phba->sli4_hba.fcp_cq[qindx]->list); + while (++qindx < phba->cfg_fcp_eq_count); spin_unlock_irq(&phba->hbalock); /* Now physically reset the device */ @@ -4318,6 +4307,7 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode) continue; } else if (rc) break; + phba->link_state = LPFC_INIT_MBX_CMDS; lpfc_config_port(phba, pmb); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); @@ -4421,7 +4411,8 @@ int lpfc_sli_hba_setup(struct lpfc_hba *phba) { uint32_t rc; - int mode = 3; + int mode = 3, i; + int longs; switch (lpfc_sli_mode) { case 2: @@ -4491,6 +4482,35 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba) if (rc) goto lpfc_sli_hba_setup_error; + /* Initialize VPIs. */ + if (phba->sli_rev == LPFC_SLI_REV3) { + /* + * The VPI bitmask and physical ID array are allocated + * and initialized once only - at driver load. A port + * reset doesn't need to reinitialize this memory. + */ + if ((phba->vpi_bmask == NULL) && (phba->vpi_ids == NULL)) { + longs = (phba->max_vpi + BITS_PER_LONG) / BITS_PER_LONG; + phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), + GFP_KERNEL); + if (!phba->vpi_bmask) { + rc = -ENOMEM; + goto lpfc_sli_hba_setup_error; + } + + phba->vpi_ids = kzalloc( + (phba->max_vpi+1) * sizeof(uint16_t), + GFP_KERNEL); + if (!phba->vpi_ids) { + kfree(phba->vpi_bmask); + rc = -ENOMEM; + goto lpfc_sli_hba_setup_error; + } + for (i = 0; i < phba->max_vpi; i++) + phba->vpi_ids[i] = i; + } + } + /* Init HBQs */ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { rc = lpfc_sli_hbq_setup(phba); @@ -4677,9 +4697,11 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM); lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM); - for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) + fcp_eqidx = 0; + do lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx], LPFC_QUEUE_REARM); + while (++fcp_eqidx < phba->cfg_fcp_eq_count); lpfc_sli4_eq_release(phba->sli4_hba.sp_eq, LPFC_QUEUE_REARM); for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) lpfc_sli4_eq_release(phba->sli4_hba.fp_eq[fcp_eqidx], @@ -4687,6 +4709,803 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) } /** + * lpfc_sli4_get_avail_extnt_rsrc - Get available resource extent count. + * @phba: Pointer to HBA context object. + * @type: The resource extent type. + * + * This function allocates all SLI4 resource identifiers. + **/ +static int +lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type, + uint16_t *extnt_count, uint16_t *extnt_size) +{ + int rc = 0; + uint32_t length; + uint32_t mbox_tmo; + struct lpfc_mbx_get_rsrc_extent_info *rsrc_info; + LPFC_MBOXQ_t *mbox; + + mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + /* Find out how many extents are available for this resource type */ + length = (sizeof(struct lpfc_mbx_get_rsrc_extent_info) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_GET_RSRC_EXTENT_INFO, + length, LPFC_SLI4_MBX_EMBED); + + /* Send an extents count of 0 - the GET doesn't use it. */ + rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, 0, type, + LPFC_SLI4_MBX_EMBED); + if (unlikely(rc)) { + rc = -EIO; + goto err_exit; + } + + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + if (unlikely(rc)) { + rc = -EIO; + goto err_exit; + } + + rsrc_info = &mbox->u.mqe.un.rsrc_extent_info; + if (bf_get(lpfc_mbox_hdr_status, + &rsrc_info->header.cfg_shdr.response)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT, + "2930 Failed to get resource extents " + "Status 0x%x Add'l Status 0x%x\n", + bf_get(lpfc_mbox_hdr_status, + &rsrc_info->header.cfg_shdr.response), + bf_get(lpfc_mbox_hdr_add_status, + &rsrc_info->header.cfg_shdr.response)); + rc = -EIO; + goto err_exit; + } + + *extnt_count = bf_get(lpfc_mbx_get_rsrc_extent_info_cnt, + &rsrc_info->u.rsp); + *extnt_size = bf_get(lpfc_mbx_get_rsrc_extent_info_size, + &rsrc_info->u.rsp); + err_exit: + mempool_free(mbox, phba->mbox_mem_pool); + return rc; +} + +/** + * lpfc_sli4_chk_avail_extnt_rsrc - Check for available SLI4 resource extents. + * @phba: Pointer to HBA context object. + * @type: The extent type to check. + * + * This function reads the current available extents from the port and checks + * if the extent count or extent size has changed since the last access. + * Callers use this routine post port reset to understand if there is a + * extent reprovisioning requirement. + * + * Returns: + * -Error: error indicates problem. + * 1: Extent count or size has changed. + * 0: No changes. + **/ +static int +lpfc_sli4_chk_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type) +{ + uint16_t curr_ext_cnt, rsrc_ext_cnt; + uint16_t size_diff, rsrc_ext_size; + int rc = 0; + struct lpfc_rsrc_blks *rsrc_entry; + struct list_head *rsrc_blk_list = NULL; + + size_diff = 0; + curr_ext_cnt = 0; + rc = lpfc_sli4_get_avail_extnt_rsrc(phba, type, + &rsrc_ext_cnt, + &rsrc_ext_size); + if (unlikely(rc)) + return -EIO; + + switch (type) { + case LPFC_RSC_TYPE_FCOE_RPI: + rsrc_blk_list = &phba->sli4_hba.lpfc_rpi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_VPI: + rsrc_blk_list = &phba->lpfc_vpi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_XRI: + rsrc_blk_list = &phba->sli4_hba.lpfc_xri_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_VFI: + rsrc_blk_list = &phba->sli4_hba.lpfc_vfi_blk_list; + break; + default: + break; + } + + list_for_each_entry(rsrc_entry, rsrc_blk_list, list) { + curr_ext_cnt++; + if (rsrc_entry->rsrc_size != rsrc_ext_size) + size_diff++; + } + + if (curr_ext_cnt != rsrc_ext_cnt || size_diff != 0) + rc = 1; + + return rc; +} + +/** + * lpfc_sli4_cfg_post_extnts - + * @phba: Pointer to HBA context object. + * @extnt_cnt - number of available extents. + * @type - the extent type (rpi, xri, vfi, vpi). + * @emb - buffer to hold either MBX_EMBED or MBX_NEMBED operation. + * @mbox - pointer to the caller's allocated mailbox structure. + * + * This function executes the extents allocation request. It also + * takes care of the amount of memory needed to allocate or get the + * allocated extents. It is the caller's responsibility to evaluate + * the response. + * + * Returns: + * -Error: Error value describes the condition found. + * 0: if successful + **/ +static int +lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt, + uint16_t type, bool *emb, LPFC_MBOXQ_t *mbox) +{ + int rc = 0; + uint32_t req_len; + uint32_t emb_len; + uint32_t alloc_len, mbox_tmo; + + /* Calculate the total requested length of the dma memory */ + req_len = *extnt_cnt * sizeof(uint16_t); + + /* + * Calculate the size of an embedded mailbox. The uint32_t + * accounts for extents-specific word. + */ + emb_len = sizeof(MAILBOX_t) - sizeof(struct mbox_header) - + sizeof(uint32_t); + + /* + * Presume the allocation and response will fit into an embedded + * mailbox. If not true, reconfigure to a non-embedded mailbox. + */ + *emb = LPFC_SLI4_MBX_EMBED; + if (req_len > emb_len) { + req_len = *extnt_cnt * sizeof(uint16_t) + + sizeof(union lpfc_sli4_cfg_shdr) + + sizeof(uint32_t); + *emb = LPFC_SLI4_MBX_NEMBED; + } + + alloc_len = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_ALLOC_RSRC_EXTENT, + req_len, *emb); + if (alloc_len < req_len) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "9000 Allocated DMA memory size (x%x) is " + "less than the requested DMA memory " + "size (x%x)\n", alloc_len, req_len); + return -ENOMEM; + } + rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, *extnt_cnt, type, *emb); + if (unlikely(rc)) + return -EIO; + + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + + if (unlikely(rc)) + rc = -EIO; + return rc; +} + +/** + * lpfc_sli4_alloc_extent - Allocate an SLI4 resource extent. + * @phba: Pointer to HBA context object. + * @type: The resource extent type to allocate. + * + * This function allocates the number of elements for the specified + * resource type. + **/ +static int +lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) +{ + bool emb = false; + uint16_t rsrc_id_cnt, rsrc_cnt, rsrc_size; + uint16_t rsrc_id, rsrc_start, j, k; + uint16_t *ids; + int i, rc; + unsigned long longs; + unsigned long *bmask; + struct lpfc_rsrc_blks *rsrc_blks; + LPFC_MBOXQ_t *mbox; + uint32_t length; + struct lpfc_id_range *id_array = NULL; + void *virtaddr = NULL; + struct lpfc_mbx_nembed_rsrc_extent *n_rsrc; + struct lpfc_mbx_alloc_rsrc_extents *rsrc_ext; + struct list_head *ext_blk_list; + + rc = lpfc_sli4_get_avail_extnt_rsrc(phba, type, + &rsrc_cnt, + &rsrc_size); + if (unlikely(rc)) + return -EIO; + + if ((rsrc_cnt == 0) || (rsrc_size == 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT, + "3009 No available Resource Extents " + "for resource type 0x%x: Count: 0x%x, " + "Size 0x%x\n", type, rsrc_cnt, + rsrc_size); + return -ENOMEM; + } + + lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_INIT, + "2903 Available Resource Extents " + "for resource type 0x%x: Count: 0x%x, " + "Size 0x%x\n", type, rsrc_cnt, + rsrc_size); + + mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + rc = lpfc_sli4_cfg_post_extnts(phba, &rsrc_cnt, type, &emb, mbox); + if (unlikely(rc)) { + rc = -EIO; + goto err_exit; + } + + /* + * Figure out where the response is located. Then get local pointers + * to the response data. The port does not guarantee to respond to + * all extents counts request so update the local variable with the + * allocated count from the port. + */ + if (emb == LPFC_SLI4_MBX_EMBED) { + rsrc_ext = &mbox->u.mqe.un.alloc_rsrc_extents; + id_array = &rsrc_ext->u.rsp.id[0]; + rsrc_cnt = bf_get(lpfc_mbx_rsrc_cnt, &rsrc_ext->u.rsp); + } else { + virtaddr = mbox->sge_array->addr[0]; + n_rsrc = (struct lpfc_mbx_nembed_rsrc_extent *) virtaddr; + rsrc_cnt = bf_get(lpfc_mbx_rsrc_cnt, n_rsrc); + id_array = &n_rsrc->id; + } + + longs = ((rsrc_cnt * rsrc_size) + BITS_PER_LONG - 1) / BITS_PER_LONG; + rsrc_id_cnt = rsrc_cnt * rsrc_size; + + /* + * Based on the resource size and count, correct the base and max + * resource values. + */ + length = sizeof(struct lpfc_rsrc_blks); + switch (type) { + case LPFC_RSC_TYPE_FCOE_RPI: + phba->sli4_hba.rpi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.rpi_bmask)) { + rc = -ENOMEM; + goto err_exit; + } + phba->sli4_hba.rpi_ids = kzalloc(rsrc_id_cnt * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.rpi_ids)) { + kfree(phba->sli4_hba.rpi_bmask); + rc = -ENOMEM; + goto err_exit; + } + + /* + * The next_rpi was initialized with the maximum available + * count but the port may allocate a smaller number. Catch + * that case and update the next_rpi. + */ + phba->sli4_hba.next_rpi = rsrc_id_cnt; + + /* Initialize local ptrs for common extent processing later. */ + bmask = phba->sli4_hba.rpi_bmask; + ids = phba->sli4_hba.rpi_ids; + ext_blk_list = &phba->sli4_hba.lpfc_rpi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_VPI: + phba->vpi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->vpi_bmask)) { + rc = -ENOMEM; + goto err_exit; + } + phba->vpi_ids = kzalloc(rsrc_id_cnt * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->vpi_ids)) { + kfree(phba->vpi_bmask); + rc = -ENOMEM; + goto err_exit; + } + + /* Initialize local ptrs for common extent processing later. */ + bmask = phba->vpi_bmask; + ids = phba->vpi_ids; + ext_blk_list = &phba->lpfc_vpi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_XRI: + phba->sli4_hba.xri_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.xri_bmask)) { + rc = -ENOMEM; + goto err_exit; + } + phba->sli4_hba.xri_ids = kzalloc(rsrc_id_cnt * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.xri_ids)) { + kfree(phba->sli4_hba.xri_bmask); + rc = -ENOMEM; + goto err_exit; + } + + /* Initialize local ptrs for common extent processing later. */ + bmask = phba->sli4_hba.xri_bmask; + ids = phba->sli4_hba.xri_ids; + ext_blk_list = &phba->sli4_hba.lpfc_xri_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_VFI: + phba->sli4_hba.vfi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.vfi_bmask)) { + rc = -ENOMEM; + goto err_exit; + } + phba->sli4_hba.vfi_ids = kzalloc(rsrc_id_cnt * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.vfi_ids)) { + kfree(phba->sli4_hba.vfi_bmask); + rc = -ENOMEM; + goto err_exit; + } + + /* Initialize local ptrs for common extent processing later. */ + bmask = phba->sli4_hba.vfi_bmask; + ids = phba->sli4_hba.vfi_ids; + ext_blk_list = &phba->sli4_hba.lpfc_vfi_blk_list; + break; + default: + /* Unsupported Opcode. Fail call. */ + id_array = NULL; + bmask = NULL; + ids = NULL; + ext_blk_list = NULL; + goto err_exit; + } + + /* + * Complete initializing the extent configuration with the + * allocated ids assigned to this function. The bitmask serves + * as an index into the array and manages the available ids. The + * array just stores the ids communicated to the port via the wqes. + */ + for (i = 0, j = 0, k = 0; i < rsrc_cnt; i++) { + if ((i % 2) == 0) + rsrc_id = bf_get(lpfc_mbx_rsrc_id_word4_0, + &id_array[k]); + else + rsrc_id = bf_get(lpfc_mbx_rsrc_id_word4_1, + &id_array[k]); + + rsrc_blks = kzalloc(length, GFP_KERNEL); + if (unlikely(!rsrc_blks)) { + rc = -ENOMEM; + kfree(bmask); + kfree(ids); + goto err_exit; + } + rsrc_blks->rsrc_start = rsrc_id; + rsrc_blks->rsrc_size = rsrc_size; + list_add_tail(&rsrc_blks->list, ext_blk_list); + rsrc_start = rsrc_id; + if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) + phba->sli4_hba.scsi_xri_start = rsrc_start + + lpfc_sli4_get_els_iocb_cnt(phba); + + while (rsrc_id < (rsrc_start + rsrc_size)) { + ids[j] = rsrc_id; + rsrc_id++; + j++; + } + /* Entire word processed. Get next word.*/ + if ((i % 2) == 1) + k++; + } + err_exit: + lpfc_sli4_mbox_cmd_free(phba, mbox); + return rc; +} + +/** + * lpfc_sli4_dealloc_extent - Deallocate an SLI4 resource extent. + * @phba: Pointer to HBA context object. + * @type: the extent's type. + * + * This function deallocates all extents of a particular resource type. + * SLI4 does not allow for deallocating a particular extent range. It + * is the caller's responsibility to release all kernel memory resources. + **/ +static int +lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type) +{ + int rc; + uint32_t length, mbox_tmo = 0; + LPFC_MBOXQ_t *mbox; + struct lpfc_mbx_dealloc_rsrc_extents *dealloc_rsrc; + struct lpfc_rsrc_blks *rsrc_blk, *rsrc_blk_next; + + mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + /* + * This function sends an embedded mailbox because it only sends the + * the resource type. All extents of this type are released by the + * port. + */ + length = (sizeof(struct lpfc_mbx_dealloc_rsrc_extents) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_DEALLOC_RSRC_EXTENT, + length, LPFC_SLI4_MBX_EMBED); + + /* Send an extents count of 0 - the dealloc doesn't use it. */ + rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, 0, type, + LPFC_SLI4_MBX_EMBED); + if (unlikely(rc)) { + rc = -EIO; + goto out_free_mbox; + } + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, mbox_tmo); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + if (unlikely(rc)) { + rc = -EIO; + goto out_free_mbox; + } + + dealloc_rsrc = &mbox->u.mqe.un.dealloc_rsrc_extents; + if (bf_get(lpfc_mbox_hdr_status, + &dealloc_rsrc->header.cfg_shdr.response)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT, + "2919 Failed to release resource extents " + "for type %d - Status 0x%x Add'l Status 0x%x. " + "Resource memory not released.\n", + type, + bf_get(lpfc_mbox_hdr_status, + &dealloc_rsrc->header.cfg_shdr.response), + bf_get(lpfc_mbox_hdr_add_status, + &dealloc_rsrc->header.cfg_shdr.response)); + rc = -EIO; + goto out_free_mbox; + } + + /* Release kernel memory resources for the specific type. */ + switch (type) { + case LPFC_RSC_TYPE_FCOE_VPI: + kfree(phba->vpi_bmask); + kfree(phba->vpi_ids); + bf_set(lpfc_vpi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + list_for_each_entry_safe(rsrc_blk, rsrc_blk_next, + &phba->lpfc_vpi_blk_list, list) { + list_del_init(&rsrc_blk->list); + kfree(rsrc_blk); + } + break; + case LPFC_RSC_TYPE_FCOE_XRI: + kfree(phba->sli4_hba.xri_bmask); + kfree(phba->sli4_hba.xri_ids); + bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + list_for_each_entry_safe(rsrc_blk, rsrc_blk_next, + &phba->sli4_hba.lpfc_xri_blk_list, list) { + list_del_init(&rsrc_blk->list); + kfree(rsrc_blk); + } + break; + case LPFC_RSC_TYPE_FCOE_VFI: + kfree(phba->sli4_hba.vfi_bmask); + kfree(phba->sli4_hba.vfi_ids); + bf_set(lpfc_vfi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + list_for_each_entry_safe(rsrc_blk, rsrc_blk_next, + &phba->sli4_hba.lpfc_vfi_blk_list, list) { + list_del_init(&rsrc_blk->list); + kfree(rsrc_blk); + } + break; + case LPFC_RSC_TYPE_FCOE_RPI: + /* RPI bitmask and physical id array are cleaned up earlier. */ + list_for_each_entry_safe(rsrc_blk, rsrc_blk_next, + &phba->sli4_hba.lpfc_rpi_blk_list, list) { + list_del_init(&rsrc_blk->list); + kfree(rsrc_blk); + } + break; + default: + break; + } + + bf_set(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + + out_free_mbox: + mempool_free(mbox, phba->mbox_mem_pool); + return rc; +} + +/** + * lpfc_sli4_alloc_resource_identifiers - Allocate all SLI4 resource extents. + * @phba: Pointer to HBA context object. + * + * This function allocates all SLI4 resource identifiers. + **/ +int +lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) +{ + int i, rc, error = 0; + uint16_t count, base; + unsigned long longs; + + if (phba->sli4_hba.extents_in_use) { + /* + * The port supports resource extents. The XRI, VPI, VFI, RPI + * resource extent count must be read and allocated before + * provisioning the resource id arrays. + */ + if (bf_get(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags) == + LPFC_IDX_RSRC_RDY) { + /* + * Extent-based resources are set - the driver could + * be in a port reset. Figure out if any corrective + * actions need to be taken. + */ + rc = lpfc_sli4_chk_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_VFI); + if (rc != 0) + error++; + rc = lpfc_sli4_chk_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_VPI); + if (rc != 0) + error++; + rc = lpfc_sli4_chk_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_XRI); + if (rc != 0) + error++; + rc = lpfc_sli4_chk_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_RPI); + if (rc != 0) + error++; + + /* + * It's possible that the number of resources + * provided to this port instance changed between + * resets. Detect this condition and reallocate + * resources. Otherwise, there is no action. + */ + if (error) { + lpfc_printf_log(phba, KERN_INFO, + LOG_MBOX | LOG_INIT, + "2931 Detected extent resource " + "change. Reallocating all " + "extents.\n"); + rc = lpfc_sli4_dealloc_extent(phba, + LPFC_RSC_TYPE_FCOE_VFI); + rc = lpfc_sli4_dealloc_extent(phba, + LPFC_RSC_TYPE_FCOE_VPI); + rc = lpfc_sli4_dealloc_extent(phba, + LPFC_RSC_TYPE_FCOE_XRI); + rc = lpfc_sli4_dealloc_extent(phba, + LPFC_RSC_TYPE_FCOE_RPI); + } else + return 0; + } + + rc = lpfc_sli4_alloc_extent(phba, LPFC_RSC_TYPE_FCOE_VFI); + if (unlikely(rc)) + goto err_exit; + + rc = lpfc_sli4_alloc_extent(phba, LPFC_RSC_TYPE_FCOE_VPI); + if (unlikely(rc)) + goto err_exit; + + rc = lpfc_sli4_alloc_extent(phba, LPFC_RSC_TYPE_FCOE_RPI); + if (unlikely(rc)) + goto err_exit; + + rc = lpfc_sli4_alloc_extent(phba, LPFC_RSC_TYPE_FCOE_XRI); + if (unlikely(rc)) + goto err_exit; + bf_set(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags, + LPFC_IDX_RSRC_RDY); + return rc; + } else { + /* + * The port does not support resource extents. The XRI, VPI, + * VFI, RPI resource ids were determined from READ_CONFIG. + * Just allocate the bitmasks and provision the resource id + * arrays. If a port reset is active, the resources don't + * need any action - just exit. + */ + if (bf_get(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags) == + LPFC_IDX_RSRC_RDY) + return 0; + + /* RPIs. */ + count = phba->sli4_hba.max_cfg_param.max_rpi; + base = phba->sli4_hba.max_cfg_param.rpi_base; + longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; + phba->sli4_hba.rpi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.rpi_bmask)) { + rc = -ENOMEM; + goto err_exit; + } + phba->sli4_hba.rpi_ids = kzalloc(count * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.rpi_ids)) { + rc = -ENOMEM; + goto free_rpi_bmask; + } + + for (i = 0; i < count; i++) + phba->sli4_hba.rpi_ids[i] = base + i; + + /* VPIs. */ + count = phba->sli4_hba.max_cfg_param.max_vpi; + base = phba->sli4_hba.max_cfg_param.vpi_base; + longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; + phba->vpi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->vpi_bmask)) { + rc = -ENOMEM; + goto free_rpi_ids; + } + phba->vpi_ids = kzalloc(count * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->vpi_ids)) { + rc = -ENOMEM; + goto free_vpi_bmask; + } + + for (i = 0; i < count; i++) + phba->vpi_ids[i] = base + i; + + /* XRIs. */ + count = phba->sli4_hba.max_cfg_param.max_xri; + base = phba->sli4_hba.max_cfg_param.xri_base; + longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; + phba->sli4_hba.xri_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.xri_bmask)) { + rc = -ENOMEM; + goto free_vpi_ids; + } + phba->sli4_hba.xri_ids = kzalloc(count * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.xri_ids)) { + rc = -ENOMEM; + goto free_xri_bmask; + } + + for (i = 0; i < count; i++) + phba->sli4_hba.xri_ids[i] = base + i; + + /* VFIs. */ + count = phba->sli4_hba.max_cfg_param.max_vfi; + base = phba->sli4_hba.max_cfg_param.vfi_base; + longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; + phba->sli4_hba.vfi_bmask = kzalloc(longs * + sizeof(unsigned long), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.vfi_bmask)) { + rc = -ENOMEM; + goto free_xri_ids; + } + phba->sli4_hba.vfi_ids = kzalloc(count * + sizeof(uint16_t), + GFP_KERNEL); + if (unlikely(!phba->sli4_hba.vfi_ids)) { + rc = -ENOMEM; + goto free_vfi_bmask; + } + + for (i = 0; i < count; i++) + phba->sli4_hba.vfi_ids[i] = base + i; + + /* + * Mark all resources ready. An HBA reset doesn't need + * to reset the initialization. + */ + bf_set(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags, + LPFC_IDX_RSRC_RDY); + return 0; + } + + free_vfi_bmask: + kfree(phba->sli4_hba.vfi_bmask); + free_xri_ids: + kfree(phba->sli4_hba.xri_ids); + free_xri_bmask: + kfree(phba->sli4_hba.xri_bmask); + free_vpi_ids: + kfree(phba->vpi_ids); + free_vpi_bmask: + kfree(phba->vpi_bmask); + free_rpi_ids: + kfree(phba->sli4_hba.rpi_ids); + free_rpi_bmask: + kfree(phba->sli4_hba.rpi_bmask); + err_exit: + return rc; +} + +/** + * lpfc_sli4_dealloc_resource_identifiers - Deallocate all SLI4 resource extents. + * @phba: Pointer to HBA context object. + * + * This function allocates the number of elements for the specified + * resource type. + **/ +int +lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *phba) +{ + if (phba->sli4_hba.extents_in_use) { + lpfc_sli4_dealloc_extent(phba, LPFC_RSC_TYPE_FCOE_VPI); + lpfc_sli4_dealloc_extent(phba, LPFC_RSC_TYPE_FCOE_RPI); + lpfc_sli4_dealloc_extent(phba, LPFC_RSC_TYPE_FCOE_XRI); + lpfc_sli4_dealloc_extent(phba, LPFC_RSC_TYPE_FCOE_VFI); + } else { + kfree(phba->vpi_bmask); + kfree(phba->vpi_ids); + bf_set(lpfc_vpi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + kfree(phba->sli4_hba.xri_bmask); + kfree(phba->sli4_hba.xri_ids); + bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + kfree(phba->sli4_hba.vfi_bmask); + kfree(phba->sli4_hba.vfi_ids); + bf_set(lpfc_vfi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + bf_set(lpfc_idx_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); + } + + return 0; +} + +/** * lpfc_sli4_hba_setup - SLI4 device intialization PCI function * @phba: Pointer to HBA context object. * @@ -4708,10 +5527,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) struct lpfc_vport *vport = phba->pport; struct lpfc_dmabuf *mp; - /* - * TODO: Why does this routine execute these task in a different - * order from probe? - */ /* Perform a PCI function reset to start from clean */ rc = lpfc_pci_function_reset(phba); if (unlikely(rc)) @@ -4740,7 +5555,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) * to read FCoE param config regions */ if (lpfc_sli4_read_fcoe_params(phba, mboxq)) - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT, + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_INIT, "2570 Failed to read FCoE parameters\n"); /* Issue READ_REV to collect vpd and FW information. */ @@ -4873,6 +5688,18 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) phba->sli3_options |= (LPFC_SLI3_NPIV_ENABLED | LPFC_SLI3_HBQ_ENABLED); spin_unlock_irq(&phba->hbalock); + /* + * Allocate all resources (xri,rpi,vpi,vfi) now. Subsequent + * calls depends on these resources to complete port setup. + */ + rc = lpfc_sli4_alloc_resource_identifiers(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "2920 Failed to alloc Resource IDs " + "rc = x%x\n", rc); + goto out_free_mbox; + } + /* Read the port's service parameters. */ rc = lpfc_read_sparam(phba, mboxq, vport->vpi); if (rc) { @@ -4906,35 +5733,37 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) goto out_free_mbox; } - if (phba->cfg_soft_wwnn) - u64_to_wwn(phba->cfg_soft_wwnn, - vport->fc_sparam.nodeName.u.wwn); - if (phba->cfg_soft_wwpn) - u64_to_wwn(phba->cfg_soft_wwpn, - vport->fc_sparam.portName.u.wwn); - memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName, - sizeof(struct lpfc_name)); - memcpy(&vport->fc_portname, &vport->fc_sparam.portName, - sizeof(struct lpfc_name)); + lpfc_update_vport_wwn(vport); /* Update the fc_host data structures with new wwn. */ fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn); fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn); /* Register SGL pool to the device using non-embedded mailbox command */ - rc = lpfc_sli4_post_sgl_list(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "0582 Error %d during sgl post operation\n", - rc); - rc = -ENODEV; - goto out_free_mbox; + if (!phba->sli4_hba.extents_in_use) { + rc = lpfc_sli4_post_els_sgl_list(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "0582 Error %d during els sgl post " + "operation\n", rc); + rc = -ENODEV; + goto out_free_mbox; + } + } else { + rc = lpfc_sli4_post_els_sgl_list_ext(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "2560 Error %d during els sgl post " + "operation\n", rc); + rc = -ENODEV; + goto out_free_mbox; + } } /* Register SCSI SGL pool to the device */ rc = lpfc_sli4_repost_scsi_sgl_list(phba); if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI, + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "0383 Error %d during scsi sgl post " "operation\n", rc); /* Some Scsi buffers were moved to the abort scsi list */ @@ -5747,10 +6576,15 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe, sizeof(struct lpfc_mcqe)); mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe); - - /* Prefix the mailbox status with range x4000 to note SLI4 status. */ + /* + * When the CQE status indicates a failure and the mailbox status + * indicates success then copy the CQE status into the mailbox status + * (and prefix it with x4000). + */ if (mcqe_status != MB_CQE_STATUS_SUCCESS) { - bf_set(lpfc_mqe_status, mb, LPFC_MBX_ERROR_RANGE | mcqe_status); + if (bf_get(lpfc_mqe_status, mb) == MBX_SUCCESS) + bf_set(lpfc_mqe_status, mb, + (LPFC_MBX_ERROR_RANGE | mcqe_status)); rc = MBXERR_ERROR; } else lpfc_sli4_swap_str(phba, mboxq); @@ -5819,7 +6653,7 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, else rc = -EIO; if (rc != MBX_SUCCESS) - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI, "(%d):2541 Mailbox command x%x " "(x%x) cannot issue Data: x%x x%x\n", mboxq->vport ? mboxq->vport->vpi : 0, @@ -6307,6 +7141,7 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, sgl->addr_hi = bpl->addrHigh; sgl->addr_lo = bpl->addrLow; + sgl->word2 = le32_to_cpu(sgl->word2); if ((i+1) == numBdes) bf_set(lpfc_sli4_sge_last, sgl, 1); else @@ -6343,6 +7178,7 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, cpu_to_le32(icmd->un.genreq64.bdl.addrHigh); sgl->addr_lo = cpu_to_le32(icmd->un.genreq64.bdl.addrLow); + sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = @@ -6474,7 +7310,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK) >> LPFC_FIP_ELS_ID_SHIFT); } - bf_set(wqe_temp_rpi, &wqe->els_req.wqe_com, ndlp->nlp_rpi); + bf_set(wqe_temp_rpi, &wqe->els_req.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); bf_set(wqe_els_id, &wqe->els_req.wqe_com, els_id); bf_set(wqe_dbde, &wqe->els_req.wqe_com, 1); bf_set(wqe_iod, &wqe->els_req.wqe_com, LPFC_WQE_IOD_READ); @@ -6623,14 +7460,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, iocbq->iocb.ulpContext); if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l) bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com, - iocbq->vport->vpi + phba->vpi_base); + phba->vpi_ids[iocbq->vport->vpi]); bf_set(wqe_dbde, &wqe->xmit_els_rsp.wqe_com, 1); bf_set(wqe_iod, &wqe->xmit_els_rsp.wqe_com, LPFC_WQE_IOD_WRITE); bf_set(wqe_qosd, &wqe->xmit_els_rsp.wqe_com, 1); bf_set(wqe_lenloc, &wqe->xmit_els_rsp.wqe_com, LPFC_WQE_LENLOC_WORD3); bf_set(wqe_ebde_cnt, &wqe->xmit_els_rsp.wqe_com, 0); - bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp, ndlp->nlp_rpi); + bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); command_type = OTHER_COMMAND; break; case CMD_CLOSE_XRI_CN: @@ -6729,6 +7567,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, return IOCB_ERROR; break; } + bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag); bf_set(wqe_reqtag, &wqe->generic.wqe_com, iocbq->iotag); wqe->generic.wqe_com.abort_tag = abort_tag; @@ -6776,7 +7615,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, return IOCB_BUSY; } } else { - sglq = __lpfc_sli_get_sglq(phba, piocb); + sglq = __lpfc_sli_get_sglq(phba, piocb); if (!sglq) { if (!(flag & SLI_IOCB_RET_IOCB)) { __lpfc_sli_ringtx_put(phba, @@ -6789,11 +7628,11 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, } } } else if (piocb->iocb_flag & LPFC_IO_FCP) { - sglq = NULL; /* These IO's already have an XRI and - * a mapped sgl. - */ + /* These IO's already have an XRI and a mapped sgl. */ + sglq = NULL; } else { - /* This is a continuation of a commandi,(CX) so this + /* + * This is a continuation of a commandi,(CX) so this * sglq is on the active list */ sglq = __lpfc_get_active_sglq(phba, piocb->sli4_xritag); @@ -6802,8 +7641,8 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, } if (sglq) { + piocb->sli4_lxritag = sglq->sli4_lxritag; piocb->sli4_xritag = sglq->sli4_xritag; - if (NO_XRI == lpfc_sli4_bpl2sgl(phba, piocb, sglq)) return IOCB_ERROR; } @@ -9799,7 +10638,12 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) break; case LPFC_WCQ: while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, cqe); + if (cq->subtype == LPFC_FCP) + workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, + cqe); + else + workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, + cqe); if (!(++ecount % LPFC_GET_QE_REL_INT)) lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); } @@ -11446,6 +12290,7 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox; int rc; uint32_t shdr_status, shdr_add_status; + uint32_t mbox_tmo; union lpfc_sli4_cfg_shdr *shdr; if (xritag == NO_XRI) { @@ -11479,8 +12324,10 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba, cpu_to_le32(putPaddrHigh(pdma_phys_addr1)); if (!phba->sli4_hba.intr_enable) rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); - else - rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } /* The IOCTL status is embedded in the mailbox subheader. */ shdr = (union lpfc_sli4_cfg_shdr *) &post_sgl_pages->header.cfg_shdr; shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); @@ -11498,6 +12345,76 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba, } /** + * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to post rpi header templates to the + * port for those SLI4 ports that do not support extents. This routine + * posts a PAGE_SIZE memory region to the port to hold up to + * PAGE_SIZE modulo 64 rpi context headers. This is an initialization routine + * and should be called only when interrupts are disabled. + * + * Return codes + * 0 - successful + * -ERROR - otherwise. + */ +uint16_t +lpfc_sli4_alloc_xri(struct lpfc_hba *phba) +{ + unsigned long xri; + + /* + * Fetch the next logical xri. Because this index is logical, + * the driver starts at 0 each time. + */ + spin_lock_irq(&phba->hbalock); + xri = find_next_zero_bit(phba->sli4_hba.xri_bmask, + phba->sli4_hba.max_cfg_param.max_xri, 0); + if (xri >= phba->sli4_hba.max_cfg_param.max_xri) { + spin_unlock_irq(&phba->hbalock); + return NO_XRI; + } else { + set_bit(xri, phba->sli4_hba.xri_bmask); + phba->sli4_hba.max_cfg_param.xri_used++; + phba->sli4_hba.xri_count++; + } + + spin_unlock_irq(&phba->hbalock); + return xri; +} + +/** + * lpfc_sli4_free_xri - Release an xri for reuse. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to release an xri to the pool of + * available rpis maintained by the driver. + **/ +void +__lpfc_sli4_free_xri(struct lpfc_hba *phba, int xri) +{ + if (test_and_clear_bit(xri, phba->sli4_hba.xri_bmask)) { + phba->sli4_hba.xri_count--; + phba->sli4_hba.max_cfg_param.xri_used--; + } +} + +/** + * lpfc_sli4_free_xri - Release an xri for reuse. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to release an xri to the pool of + * available rpis maintained by the driver. + **/ +void +lpfc_sli4_free_xri(struct lpfc_hba *phba, int xri) +{ + spin_lock_irq(&phba->hbalock); + __lpfc_sli4_free_xri(phba, xri); + spin_unlock_irq(&phba->hbalock); +} + +/** * lpfc_sli4_next_xritag - Get an xritag for the io * @phba: Pointer to HBA context object. * @@ -11510,30 +12427,23 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba, uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *phba) { - uint16_t xritag; + uint16_t xri_index; - spin_lock_irq(&phba->hbalock); - xritag = phba->sli4_hba.next_xri; - if ((xritag != (uint16_t) -1) && xritag < - (phba->sli4_hba.max_cfg_param.max_xri - + phba->sli4_hba.max_cfg_param.xri_base)) { - phba->sli4_hba.next_xri++; - phba->sli4_hba.max_cfg_param.xri_used++; - spin_unlock_irq(&phba->hbalock); - return xritag; - } - spin_unlock_irq(&phba->hbalock); - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, + xri_index = lpfc_sli4_alloc_xri(phba); + if (xri_index != NO_XRI) + return xri_index; + + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2004 Failed to allocate XRI.last XRITAG is %d" " Max XRI is %d, Used XRI is %d\n", - phba->sli4_hba.next_xri, + xri_index, phba->sli4_hba.max_cfg_param.max_xri, phba->sli4_hba.max_cfg_param.xri_used); - return -1; + return NO_XRI; } /** - * lpfc_sli4_post_sgl_list - post a block of sgl list to the firmware. + * lpfc_sli4_post_els_sgl_list - post a block of ELS sgls to the port. * @phba: pointer to lpfc hba data structure. * * This routine is invoked to post a block of driver's sgl pages to the @@ -11542,7 +12452,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba) * stopped. **/ int -lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) +lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba) { struct lpfc_sglq *sglq_entry; struct lpfc_mbx_post_uembed_sgl_page1 *sgl; @@ -11551,7 +12461,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) LPFC_MBOXQ_t *mbox; uint32_t reqlen, alloclen, pg_pairs; uint32_t mbox_tmo; - uint16_t xritag_start = 0; + uint16_t xritag_start = 0, lxri = 0; int els_xri_cnt, rc = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; @@ -11568,11 +12478,8 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) return -ENOMEM; } mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (!mbox) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2560 Failed to allocate mbox cmd memory\n"); + if (!mbox) return -ENOMEM; - } /* Allocate DMA memory and set up the non-embedded mailbox command */ alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, @@ -11587,15 +12494,30 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) lpfc_sli4_mbox_cmd_free(phba, mbox); return -ENOMEM; } - /* Get the first SGE entry from the non-embedded DMA memory */ - viraddr = mbox->sge_array->addr[0]; - /* Set up the SGL pages in the non-embedded DMA pages */ + viraddr = mbox->sge_array->addr[0]; sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr; sgl_pg_pairs = &sgl->sgl_pg_pairs; for (pg_pairs = 0; pg_pairs < els_xri_cnt; pg_pairs++) { sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[pg_pairs]; + + /* + * Assign the sglq a physical xri only if the driver has not + * initialized those resources. A port reset only needs + * the sglq's posted. + */ + if (bf_get(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags) != + LPFC_XRI_RSRC_RDY) { + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + lpfc_sli4_mbox_cmd_free(phba, mbox); + return -ENOMEM; + } + sglq_entry->sli4_lxritag = lxri; + sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + } + /* Set up the sge entry */ sgl_pg_pairs->sgl_pg0_addr_lo = cpu_to_le32(putPaddrLow(sglq_entry->phys)); @@ -11605,16 +12527,17 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) cpu_to_le32(putPaddrLow(0)); sgl_pg_pairs->sgl_pg1_addr_hi = cpu_to_le32(putPaddrHigh(0)); + /* Keep the first xritag on the list */ if (pg_pairs == 0) xritag_start = sglq_entry->sli4_xritag; sgl_pg_pairs++; } + + /* Complete initialization and perform endian conversion. */ bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt); - /* Perform endian conversion if necessary */ sgl->word0 = cpu_to_le32(sgl->word0); - if (!phba->sli4_hba.intr_enable) rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); else { @@ -11633,6 +12556,181 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) shdr_status, shdr_add_status, rc); rc = -ENXIO; } + + if (rc == 0) + bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, + LPFC_XRI_RSRC_RDY); + return rc; +} + +/** + * lpfc_sli4_post_els_sgl_list_ext - post a block of ELS sgls to the port. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to post a block of driver's sgl pages to the + * HBA using non-embedded mailbox command. No Lock is held. This routine + * is only called when the driver is loading and after all IO has been + * stopped. + **/ +int +lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba) +{ + struct lpfc_sglq *sglq_entry; + struct lpfc_mbx_post_uembed_sgl_page1 *sgl; + struct sgl_page_pairs *sgl_pg_pairs; + void *viraddr; + LPFC_MBOXQ_t *mbox; + uint32_t reqlen, alloclen, index; + uint32_t mbox_tmo; + uint16_t rsrc_start, rsrc_size, els_xri_cnt; + uint16_t xritag_start = 0, lxri = 0; + struct lpfc_rsrc_blks *rsrc_blk; + int cnt, ttl_cnt, rc = 0; + int loop_cnt; + uint32_t shdr_status, shdr_add_status; + union lpfc_sli4_cfg_shdr *shdr; + + /* The number of sgls to be posted */ + els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); + + reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) + + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); + if (reqlen > SLI4_PAGE_SIZE) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "2989 Block sgl registration required DMA " + "size (%d) great than a page\n", reqlen); + return -ENOMEM; + } + + cnt = 0; + ttl_cnt = 0; + list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list, + list) { + rsrc_start = rsrc_blk->rsrc_start; + rsrc_size = rsrc_blk->rsrc_size; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3014 Working ELS Extent start %d, cnt %d\n", + rsrc_start, rsrc_size); + + loop_cnt = min(els_xri_cnt, rsrc_size); + if (ttl_cnt + loop_cnt >= els_xri_cnt) { + loop_cnt = els_xri_cnt - ttl_cnt; + ttl_cnt = els_xri_cnt; + } + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + /* + * Allocate DMA memory and set up the non-embedded mailbox + * command. + */ + alloclen = lpfc_sli4_config(phba, mbox, + LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, + reqlen, LPFC_SLI4_MBX_NEMBED); + if (alloclen < reqlen) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2987 Allocated DMA memory size (%d) " + "is less than the requested DMA memory " + "size (%d)\n", alloclen, reqlen); + lpfc_sli4_mbox_cmd_free(phba, mbox); + return -ENOMEM; + } + + /* Set up the SGL pages in the non-embedded DMA pages */ + viraddr = mbox->sge_array->addr[0]; + sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr; + sgl_pg_pairs = &sgl->sgl_pg_pairs; + + /* + * The starting resource may not begin at zero. Control + * the loop variants via the block resource parameters, + * but handle the sge pointers with a zero-based index + * that doesn't get reset per loop pass. + */ + for (index = rsrc_start; + index < rsrc_start + loop_cnt; + index++) { + sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[cnt]; + + /* + * Assign the sglq a physical xri only if the driver + * has not initialized those resources. A port reset + * only needs the sglq's posted. + */ + if (bf_get(lpfc_xri_rsrc_rdy, + &phba->sli4_hba.sli4_flags) != + LPFC_XRI_RSRC_RDY) { + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + lpfc_sli4_mbox_cmd_free(phba, mbox); + rc = -ENOMEM; + goto err_exit; + } + sglq_entry->sli4_lxritag = lxri; + sglq_entry->sli4_xritag = + phba->sli4_hba.xri_ids[lxri]; + } + + /* Set up the sge entry */ + sgl_pg_pairs->sgl_pg0_addr_lo = + cpu_to_le32(putPaddrLow(sglq_entry->phys)); + sgl_pg_pairs->sgl_pg0_addr_hi = + cpu_to_le32(putPaddrHigh(sglq_entry->phys)); + sgl_pg_pairs->sgl_pg1_addr_lo = + cpu_to_le32(putPaddrLow(0)); + sgl_pg_pairs->sgl_pg1_addr_hi = + cpu_to_le32(putPaddrHigh(0)); + + /* Track the starting physical XRI for the mailbox. */ + if (index == rsrc_start) + xritag_start = sglq_entry->sli4_xritag; + sgl_pg_pairs++; + cnt++; + } + + /* Complete initialization and perform endian conversion. */ + rsrc_blk->rsrc_used += loop_cnt; + bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); + bf_set(lpfc_post_sgl_pages_xricnt, sgl, loop_cnt); + sgl->word0 = cpu_to_le32(sgl->word0); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3015 Post ELS Extent SGL, start %d, " + "cnt %d, used %d\n", + xritag_start, loop_cnt, rsrc_blk->rsrc_used); + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, + &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, + &shdr->response); + if (rc != MBX_TIMEOUT) + lpfc_sli4_mbox_cmd_free(phba, mbox); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2988 POST_SGL_BLOCK mailbox " + "command failed status x%x " + "add_status x%x mbx status x%x\n", + shdr_status, shdr_add_status, rc); + rc = -ENXIO; + goto err_exit; + } + if (ttl_cnt >= els_xri_cnt) + break; + } + + err_exit: + if (rc == 0) + bf_set(lpfc_xri_rsrc_rdy, &phba->sli4_hba.sli4_flags, + LPFC_XRI_RSRC_RDY); return rc; } @@ -11693,6 +12791,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist, lpfc_sli4_mbox_cmd_free(phba, mbox); return -ENOMEM; } + /* Get the first SGE entry from the non-embedded DMA memory */ viraddr = mbox->sge_array->addr[0]; @@ -11748,6 +12847,169 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist, } /** + * lpfc_sli4_post_scsi_sgl_blk_ext - post a block of scsi sgls to the port. + * @phba: pointer to lpfc hba data structure. + * @sblist: pointer to scsi buffer list. + * @count: number of scsi buffers on the list. + * + * This routine is invoked to post a block of @count scsi sgl pages from a + * SCSI buffer list @sblist to the HBA using non-embedded mailbox command. + * No Lock is held. + * + **/ +int +lpfc_sli4_post_scsi_sgl_blk_ext(struct lpfc_hba *phba, struct list_head *sblist, + int cnt) +{ + struct lpfc_scsi_buf *psb = NULL; + struct lpfc_mbx_post_uembed_sgl_page1 *sgl; + struct sgl_page_pairs *sgl_pg_pairs; + void *viraddr; + LPFC_MBOXQ_t *mbox; + uint32_t reqlen, alloclen, pg_pairs; + uint32_t mbox_tmo; + uint16_t xri_start = 0, scsi_xri_start; + uint16_t rsrc_range; + int rc = 0, avail_cnt; + uint32_t shdr_status, shdr_add_status; + dma_addr_t pdma_phys_bpl1; + union lpfc_sli4_cfg_shdr *shdr; + struct lpfc_rsrc_blks *rsrc_blk; + uint32_t xri_cnt = 0; + + /* Calculate the total requested length of the dma memory */ + reqlen = cnt * sizeof(struct sgl_page_pairs) + + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); + if (reqlen > SLI4_PAGE_SIZE) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "2932 Block sgl registration required DMA " + "size (%d) great than a page\n", reqlen); + return -ENOMEM; + } + + /* + * The use of extents requires the driver to post the sgl headers + * in multiple postings to meet the contiguous resource assignment. + */ + psb = list_prepare_entry(psb, sblist, list); + scsi_xri_start = phba->sli4_hba.scsi_xri_start; + list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list, + list) { + rsrc_range = rsrc_blk->rsrc_start + rsrc_blk->rsrc_size; + if (rsrc_range < scsi_xri_start) + continue; + else if (rsrc_blk->rsrc_used >= rsrc_blk->rsrc_size) + continue; + else + avail_cnt = rsrc_blk->rsrc_size - rsrc_blk->rsrc_used; + + reqlen = (avail_cnt * sizeof(struct sgl_page_pairs)) + + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); + /* + * Allocate DMA memory and set up the non-embedded mailbox + * command. The mbox is used to post an SGL page per loop + * but the DMA memory has a use-once semantic so the mailbox + * is used and freed per loop pass. + */ + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2933 Failed to allocate mbox cmd " + "memory\n"); + return -ENOMEM; + } + alloclen = lpfc_sli4_config(phba, mbox, + LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, + reqlen, + LPFC_SLI4_MBX_NEMBED); + if (alloclen < reqlen) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2934 Allocated DMA memory size (%d) " + "is less than the requested DMA memory " + "size (%d)\n", alloclen, reqlen); + lpfc_sli4_mbox_cmd_free(phba, mbox); + return -ENOMEM; + } + + /* Get the first SGE entry from the non-embedded DMA memory */ + viraddr = mbox->sge_array->addr[0]; + + /* Set up the SGL pages in the non-embedded DMA pages */ + sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr; + sgl_pg_pairs = &sgl->sgl_pg_pairs; + + /* pg_pairs tracks posted SGEs per loop iteration. */ + pg_pairs = 0; + list_for_each_entry_continue(psb, sblist, list) { + /* Set up the sge entry */ + sgl_pg_pairs->sgl_pg0_addr_lo = + cpu_to_le32(putPaddrLow(psb->dma_phys_bpl)); + sgl_pg_pairs->sgl_pg0_addr_hi = + cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl)); + if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) + pdma_phys_bpl1 = psb->dma_phys_bpl + + SGL_PAGE_SIZE; + else + pdma_phys_bpl1 = 0; + sgl_pg_pairs->sgl_pg1_addr_lo = + cpu_to_le32(putPaddrLow(pdma_phys_bpl1)); + sgl_pg_pairs->sgl_pg1_addr_hi = + cpu_to_le32(putPaddrHigh(pdma_phys_bpl1)); + /* Keep the first xri for this extent. */ + if (pg_pairs == 0) + xri_start = psb->cur_iocbq.sli4_xritag; + sgl_pg_pairs++; + pg_pairs++; + xri_cnt++; + + /* + * Track two exit conditions - the loop has constructed + * all of the caller's SGE pairs or all available + * resource IDs in this extent are consumed. + */ + if ((xri_cnt == cnt) || (pg_pairs >= avail_cnt)) + break; + } + rsrc_blk->rsrc_used += pg_pairs; + bf_set(lpfc_post_sgl_pages_xri, sgl, xri_start); + bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "3016 Post SCSI Extent SGL, start %d, cnt %d " + "blk use %d\n", + xri_start, pg_pairs, rsrc_blk->rsrc_used); + /* Perform endian conversion if necessary */ + sgl->word0 = cpu_to_le32(sgl->word0); + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, + &shdr->response); + if (rc != MBX_TIMEOUT) + lpfc_sli4_mbox_cmd_free(phba, mbox); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2935 POST_SGL_BLOCK mailbox command " + "failed status x%x add_status x%x " + "mbx status x%x\n", + shdr_status, shdr_add_status, rc); + return -ENXIO; + } + + /* Post only what is requested. */ + if (xri_cnt >= cnt) + break; + } + return rc; +} + +/** * lpfc_fc_frame_check - Check that this frame is a valid frame to handle * @phba: pointer to lpfc_hba struct that the frame was received on * @fc_hdr: A pointer to the FC Header data (In Big Endian Format) @@ -12137,6 +13399,28 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba, } /** + * lpfc_sli4_xri_inrange - check xri is in range of xris owned by driver. + * @phba: Pointer to HBA context object. + * @xri: xri id in transaction. + * + * This function validates the xri maps to the known range of XRIs allocated an + * used by the driver. + **/ +static uint16_t +lpfc_sli4_xri_inrange(struct lpfc_hba *phba, + uint16_t xri) +{ + int i; + + for (i = 0; i < phba->sli4_hba.max_cfg_param.max_xri; i++) { + if (xri == phba->sli4_hba.xri_ids[i]) + return i; + } + return NO_XRI; +} + + +/** * lpfc_sli4_seq_abort_rsp - bls rsp to sequence abort * @phba: Pointer to HBA context object. * @fc_hdr: pointer to a FC frame header. @@ -12169,9 +13453,7 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba, "SID:x%x\n", oxid, sid); return; } - if (rxid >= phba->sli4_hba.max_cfg_param.xri_base - && rxid <= (phba->sli4_hba.max_cfg_param.max_xri - + phba->sli4_hba.max_cfg_param.xri_base)) + if (lpfc_sli4_xri_inrange(phba, rxid)) lpfc_set_rrq_active(phba, ndlp, rxid, oxid, 0); /* Allocate buffer for rsp iocb */ @@ -12194,12 +13476,13 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba, icmd->ulpBdeCount = 0; icmd->ulpLe = 1; icmd->ulpClass = CLASS3; - icmd->ulpContext = ndlp->nlp_rpi; + icmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; ctiocb->context1 = ndlp; ctiocb->iocb_cmpl = NULL; ctiocb->vport = phba->pport; ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_rsp_cmpl; + ctiocb->sli4_lxritag = NO_XRI; ctiocb->sli4_xritag = NO_XRI; /* If the oxid maps to the FCP XRI range or if it is out of range, @@ -12380,8 +13663,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS; first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX; first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id); - first_iocbq->iocb.unsli3.rcvsli3.vpi = - vport->vpi + vport->phba->vpi_base; + /* iocbq is prepped for internal consumption. Logical vpi. */ + first_iocbq->iocb.unsli3.rcvsli3.vpi = vport->vpi; /* put the first buffer into the first IOCBq */ first_iocbq->context2 = &seq_dmabuf->dbuf; first_iocbq->context3 = NULL; @@ -12461,7 +13744,7 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport, &phba->sli.ring[LPFC_ELS_RING], iocbq, fc_hdr->fh_r_ctl, fc_hdr->fh_type)) - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2540 Ring %d handler: unexpected Rctl " "x%x Type x%x received\n", LPFC_ELS_RING, @@ -12558,9 +13841,24 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba) { struct lpfc_rpi_hdr *rpi_page; uint32_t rc = 0; + uint16_t lrpi = 0; + + /* SLI4 ports that support extents do not require RPI headers. */ + if (!phba->sli4_hba.rpi_hdrs_in_use) + goto exit; + if (phba->sli4_hba.extents_in_use) + return -EIO; - /* Post all rpi memory regions to the port. */ list_for_each_entry(rpi_page, &phba->sli4_hba.lpfc_rpi_hdr_list, list) { + /* + * Assign the rpi headers a physical rpi only if the driver + * has not initialized those resources. A port reset only + * needs the headers posted. + */ + if (bf_get(lpfc_rpi_rsrc_rdy, &phba->sli4_hba.sli4_flags) != + LPFC_RPI_RSRC_RDY) + rpi_page->start_rpi = phba->sli4_hba.rpi_ids[lrpi]; + rc = lpfc_sli4_post_rpi_hdr(phba, rpi_page); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -12571,6 +13869,9 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba) } } + exit: + bf_set(lpfc_rpi_rsrc_rdy, &phba->sli4_hba.sli4_flags, + LPFC_RPI_RSRC_RDY); return rc; } @@ -12594,10 +13895,15 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page) LPFC_MBOXQ_t *mboxq; struct lpfc_mbx_post_hdr_tmpl *hdr_tmpl; uint32_t rc = 0; - uint32_t mbox_tmo; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + /* SLI4 ports that support extents do not require RPI headers. */ + if (!phba->sli4_hba.rpi_hdrs_in_use) + return rc; + if (phba->sli4_hba.extents_in_use) + return -EIO; + /* The port is notified of the header region via a mailbox command. */ mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mboxq) { @@ -12609,16 +13915,19 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page) /* Post all rpi memory regions to the port. */ hdr_tmpl = &mboxq->u.mqe.un.hdr_tmpl; - mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE, LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE, sizeof(struct lpfc_mbx_post_hdr_tmpl) - sizeof(struct lpfc_sli4_cfg_mhdr), LPFC_SLI4_MBX_EMBED); - bf_set(lpfc_mbx_post_hdr_tmpl_page_cnt, - hdr_tmpl, rpi_page->page_count); + + + /* Post the physical rpi to the port for this rpi header. */ bf_set(lpfc_mbx_post_hdr_tmpl_rpi_offset, hdr_tmpl, rpi_page->start_rpi); + bf_set(lpfc_mbx_post_hdr_tmpl_page_cnt, + hdr_tmpl, rpi_page->page_count); + hdr_tmpl->rpi_paddr_lo = putPaddrLow(rpi_page->dmabuf->phys); hdr_tmpl->rpi_paddr_hi = putPaddrHigh(rpi_page->dmabuf->phys); rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); @@ -12653,22 +13962,21 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page) int lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) { - int rpi; - uint16_t max_rpi, rpi_base, rpi_limit; - uint16_t rpi_remaining; + unsigned long rpi; + uint16_t max_rpi, rpi_limit; + uint16_t rpi_remaining, lrpi = 0; struct lpfc_rpi_hdr *rpi_hdr; max_rpi = phba->sli4_hba.max_cfg_param.max_rpi; - rpi_base = phba->sli4_hba.max_cfg_param.rpi_base; rpi_limit = phba->sli4_hba.next_rpi; /* - * The valid rpi range is not guaranteed to be zero-based. Start - * the search at the rpi_base as reported by the port. + * Fetch the next logical rpi. Because this index is logical, + * the driver starts at 0 each time. */ spin_lock_irq(&phba->hbalock); - rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, rpi_base); - if (rpi >= rpi_limit || rpi < rpi_base) + rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, 0); + if (rpi >= rpi_limit) rpi = LPFC_RPI_ALLOC_ERROR; else { set_bit(rpi, phba->sli4_hba.rpi_bmask); @@ -12678,7 +13986,7 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) /* * Don't try to allocate more rpi header regions if the device limit - * on available rpis max has been exhausted. + * has been exhausted. */ if ((rpi == LPFC_RPI_ALLOC_ERROR) && (phba->sli4_hba.rpi_count >= max_rpi)) { @@ -12687,13 +13995,21 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) } /* + * RPI header postings are not required for SLI4 ports capable of + * extents. + */ + if (!phba->sli4_hba.rpi_hdrs_in_use) { + spin_unlock_irq(&phba->hbalock); + return rpi; + } + + /* * If the driver is running low on rpi resources, allocate another * page now. Note that the next_rpi value is used because * it represents how many are actually in use whereas max_rpi notes * how many are supported max by the device. */ - rpi_remaining = phba->sli4_hba.next_rpi - rpi_base - - phba->sli4_hba.rpi_count; + rpi_remaining = phba->sli4_hba.next_rpi - phba->sli4_hba.rpi_count; spin_unlock_irq(&phba->hbalock); if (rpi_remaining < LPFC_RPI_LOW_WATER_MARK) { rpi_hdr = lpfc_sli4_create_rpi_hdr(phba); @@ -12702,6 +14018,8 @@ lpfc_sli4_alloc_rpi(struct lpfc_hba *phba) "2002 Error Could not grow rpi " "count\n"); } else { + lrpi = rpi_hdr->start_rpi; + rpi_hdr->start_rpi = phba->sli4_hba.rpi_ids[lrpi]; lpfc_sli4_post_rpi_hdr(phba, rpi_hdr); } } @@ -12751,6 +14069,8 @@ void lpfc_sli4_remove_rpis(struct lpfc_hba *phba) { kfree(phba->sli4_hba.rpi_bmask); + kfree(phba->sli4_hba.rpi_ids); + bf_set(lpfc_rpi_rsrc_rdy, &phba->sli4_hba.sli4_flags, 0); } /** @@ -13490,6 +14810,96 @@ out: } /** + * lpfc_wr_object - write an object to the firmware + * @phba: HBA structure that indicates port to create a queue on. + * @dmabuf_list: list of dmabufs to write to the port. + * @size: the total byte value of the objects to write to the port. + * @offset: the current offset to be used to start the transfer. + * + * This routine will create a wr_object mailbox command to send to the port. + * the mailbox command will be constructed using the dma buffers described in + * @dmabuf_list to create a list of BDEs. This routine will fill in as many + * BDEs that the imbedded mailbox can support. The @offset variable will be + * used to indicate the starting offset of the transfer and will also return + * the offset after the write object mailbox has completed. @size is used to + * determine the end of the object and whether the eof bit should be set. + * + * Return 0 is successful and offset will contain the the new offset to use + * for the next write. + * Return negative value for error cases. + **/ +int +lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list, + uint32_t size, uint32_t *offset) +{ + struct lpfc_mbx_wr_object *wr_object; + LPFC_MBOXQ_t *mbox; + int rc = 0, i = 0; + uint32_t shdr_status, shdr_add_status; + uint32_t mbox_tmo; + union lpfc_sli4_cfg_shdr *shdr; + struct lpfc_dmabuf *dmabuf; + uint32_t written = 0; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_WRITE_OBJECT, + sizeof(struct lpfc_mbx_wr_object) - + sizeof(struct lpfc_sli4_cfg_mhdr), LPFC_SLI4_MBX_EMBED); + + wr_object = (struct lpfc_mbx_wr_object *)&mbox->u.mqe.un.wr_object; + wr_object->u.request.write_offset = *offset; + sprintf((uint8_t *)wr_object->u.request.object_name, "/"); + wr_object->u.request.object_name[0] = + cpu_to_le32(wr_object->u.request.object_name[0]); + bf_set(lpfc_wr_object_eof, &wr_object->u.request, 0); + list_for_each_entry(dmabuf, dmabuf_list, list) { + if (i >= LPFC_MBX_WR_CONFIG_MAX_BDE || written >= size) + break; + wr_object->u.request.bde[i].addrLow = putPaddrLow(dmabuf->phys); + wr_object->u.request.bde[i].addrHigh = + putPaddrHigh(dmabuf->phys); + if (written + SLI4_PAGE_SIZE >= size) { + wr_object->u.request.bde[i].tus.f.bdeSize = + (size - written); + written += (size - written); + bf_set(lpfc_wr_object_eof, &wr_object->u.request, 1); + } else { + wr_object->u.request.bde[i].tus.f.bdeSize = + SLI4_PAGE_SIZE; + written += SLI4_PAGE_SIZE; + } + i++; + } + wr_object->u.request.bde_count = i; + bf_set(lpfc_wr_object_write_length, &wr_object->u.request, written); + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + /* The IOCTL status is embedded in the mailbox subheader. */ + shdr = (union lpfc_sli4_cfg_shdr *) &wr_object->header.cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (rc != MBX_TIMEOUT) + mempool_free(mbox, phba->mbox_mem_pool); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3025 Write Object mailbox failed with " + "status x%x add_status x%x, mbx status x%x\n", + shdr_status, shdr_add_status, rc); + rc = -ENXIO; + } else + *offset += wr_object->u.response.actual_write_length; + return rc; +} + +/** * lpfc_cleanup_pending_mbox - Free up vport discovery mailbox commands. * @vport: pointer to vport data structure. * @@ -13644,7 +15054,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) * never happen */ sglq = __lpfc_clear_active_sglq(phba, - sglq->sli4_xritag); + sglq->sli4_lxritag); spin_unlock_irqrestore(&phba->hbalock, iflags); lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "2823 txq empty and txq_cnt is %d\n ", @@ -13656,6 +15066,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) /* The xri and iocb resources secured, * attempt to issue request */ + piocbq->sli4_lxritag = sglq->sli4_lxritag; piocbq->sli4_xritag = sglq->sli4_xritag; if (NO_XRI == lpfc_sli4_bpl2sgl(phba, piocbq, sglq)) fail_msg = "to convert bpl to sgl"; diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 453577c21c14..a0075b0af142 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -52,6 +52,7 @@ struct lpfc_iocbq { struct list_head clist; struct list_head dlist; uint16_t iotag; /* pre-assigned IO tag */ + uint16_t sli4_lxritag; /* logical pre-assigned XRI. */ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */ struct lpfc_cq_event cq_event; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 1a3cbf88f2ce..4b1703554a26 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -310,7 +310,6 @@ struct lpfc_max_cfg_param { uint16_t vfi_base; uint16_t vfi_used; uint16_t max_fcfi; - uint16_t fcfi_base; uint16_t fcfi_used; uint16_t max_eq; uint16_t max_rq; @@ -365,6 +364,11 @@ struct lpfc_pc_sli4_params { uint8_t rqv; }; +struct lpfc_iov { + uint32_t pf_number; + uint32_t vf_number; +}; + /* SLI4 HBA data structure entries */ struct lpfc_sli4_hba { void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for @@ -444,10 +448,13 @@ struct lpfc_sli4_hba { uint32_t intr_enable; struct lpfc_bmbx bmbx; struct lpfc_max_cfg_param max_cfg_param; + uint16_t extents_in_use; /* must allocate resource extents. */ + uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */ uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */ uint16_t next_rpi; uint16_t scsi_xri_max; uint16_t scsi_xri_cnt; + uint16_t scsi_xri_start; struct list_head lpfc_free_sgl_list; struct list_head lpfc_sgl_list; struct lpfc_sglq **lpfc_els_sgl_array; @@ -458,7 +465,17 @@ struct lpfc_sli4_hba { struct lpfc_sglq **lpfc_sglq_active_list; struct list_head lpfc_rpi_hdr_list; unsigned long *rpi_bmask; + uint16_t *rpi_ids; uint16_t rpi_count; + struct list_head lpfc_rpi_blk_list; + unsigned long *xri_bmask; + uint16_t *xri_ids; + uint16_t xri_count; + struct list_head lpfc_xri_blk_list; + unsigned long *vfi_bmask; + uint16_t *vfi_ids; + uint16_t vfi_count; + struct list_head lpfc_vfi_blk_list; struct lpfc_sli4_flags sli4_flags; struct list_head sp_queue_event; struct list_head sp_cqe_event_pool; @@ -467,6 +484,7 @@ struct lpfc_sli4_hba { struct list_head sp_els_xri_aborted_work_queue; struct list_head sp_unsol_work_queue; struct lpfc_sli4_link link_state; + struct lpfc_iov iov; spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */ }; @@ -490,6 +508,7 @@ struct lpfc_sglq { enum lpfc_sgl_state state; struct lpfc_nodelist *ndlp; /* ndlp associated with IO */ uint16_t iotag; /* pre-assigned IO tag */ + uint16_t sli4_lxritag; /* logical pre-assigned xri. */ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */ struct sli4_sge *sgl; /* pre-assigned SGL */ void *virt; /* virtual address. */ @@ -504,6 +523,13 @@ struct lpfc_rpi_hdr { uint32_t start_rpi; }; +struct lpfc_rsrc_blks { + struct list_head list; + uint16_t rsrc_start; + uint16_t rsrc_size; + uint16_t rsrc_used; +}; + /* * SLI4 specific function prototypes */ @@ -543,8 +569,11 @@ int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t); int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *); uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *); int lpfc_sli4_post_async_mbox(struct lpfc_hba *); -int lpfc_sli4_post_sgl_list(struct lpfc_hba *phba); +int lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba); +int lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba); int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int); +int lpfc_sli4_post_scsi_sgl_blk_ext(struct lpfc_hba *, struct list_head *, + int); struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *); struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *); void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *); diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 30ba5440c67a..1feb551a57bc 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -83,7 +83,7 @@ inline void lpfc_vport_set_state(struct lpfc_vport *vport, static int lpfc_alloc_vpi(struct lpfc_hba *phba) { - int vpi; + unsigned long vpi; spin_lock_irq(&phba->hbalock); /* Start at bit 1 because vpi zero is reserved for the physical port */ diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 046dcc672ec1..7370c084b178 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -33,9 +33,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.05.34-rc1" -#define MEGASAS_RELDATE "Feb. 24, 2011" -#define MEGASAS_EXT_VERSION "Thu. Feb. 24 17:00:00 PDT 2011" +#define MEGASAS_VERSION "00.00.05.38-rc1" +#define MEGASAS_RELDATE "May. 11, 2011" +#define MEGASAS_EXT_VERSION "Wed. May. 11 17:00:00 PDT 2011" /* * Device IDs @@ -76,8 +76,8 @@ #define MFI_STATE_READY 0xB0000000 #define MFI_STATE_OPERATIONAL 0xC0000000 #define MFI_STATE_FAULT 0xF0000000 -#define MFI_RESET_REQUIRED 0x00000001 - +#define MFI_RESET_REQUIRED 0x00000001 +#define MFI_RESET_ADAPTER 0x00000002 #define MEGAMFI_FRAME_SIZE 64 /* diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 89c623ebadbc..2d8cdce7b2f5 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FILE: megaraid_sas_base.c - * Version : v00.00.05.34-rc1 + * Version : v00.00.05.38-rc1 * * Authors: LSI Corporation * Sreenivas Bagalkote @@ -437,15 +437,18 @@ megasas_read_fw_status_reg_ppc(struct megasas_register_set __iomem * regs) static int megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) { - u32 status; + u32 status, mfiStatus = 0; + /* * Check if it is our interrupt */ status = readl(®s->outbound_intr_status); - if (!(status & MFI_REPLY_1078_MESSAGE_INTERRUPT)) { - return 0; - } + if (status & MFI_REPLY_1078_MESSAGE_INTERRUPT) + mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; + + if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) + mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; /* * Clear the interrupt by writing back the same value @@ -455,8 +458,9 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) /* Dummy readl to force pci flush */ readl(®s->outbound_doorbell_clear); - return 1; + return mfiStatus; } + /** * megasas_fire_cmd_ppc - Sends command to the FW * @frame_phys_addr : Physical address of cmd @@ -477,17 +481,6 @@ megasas_fire_cmd_ppc(struct megasas_instance *instance, } /** - * megasas_adp_reset_ppc - For controller reset - * @regs: MFI register set - */ -static int -megasas_adp_reset_ppc(struct megasas_instance *instance, - struct megasas_register_set __iomem *regs) -{ - return 0; -} - -/** * megasas_check_reset_ppc - For controller reset check * @regs: MFI register set */ @@ -495,8 +488,12 @@ static int megasas_check_reset_ppc(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) + return 1; + return 0; } + static struct megasas_instance_template megasas_instance_template_ppc = { .fire_cmd = megasas_fire_cmd_ppc, @@ -504,7 +501,7 @@ static struct megasas_instance_template megasas_instance_template_ppc = { .disable_intr = megasas_disable_intr_ppc, .clear_intr = megasas_clear_intr_ppc, .read_fw_status_reg = megasas_read_fw_status_reg_ppc, - .adp_reset = megasas_adp_reset_ppc, + .adp_reset = megasas_adp_reset_xscale, .check_reset = megasas_check_reset_ppc, .service_isr = megasas_isr, .tasklet = megasas_complete_cmd_dpc, @@ -620,6 +617,9 @@ static int megasas_check_reset_skinny(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) + return 1; + return 0; } @@ -3454,7 +3454,7 @@ static int megasas_init_fw(struct megasas_instance *instance) { u32 max_sectors_1; u32 max_sectors_2; - u32 tmp_sectors; + u32 tmp_sectors, msix_enable; struct megasas_register_set __iomem *reg_set; struct megasas_ctrl_info *ctrl_info; unsigned long bar_list; @@ -3507,6 +3507,13 @@ static int megasas_init_fw(struct megasas_instance *instance) if (megasas_transition_to_ready(instance)) goto fail_ready_state; + /* Check if MSI-X is supported while in ready state */ + msix_enable = (instance->instancet->read_fw_status_reg(reg_set) & + 0x4000000) >> 0x1a; + if (msix_enable && !msix_disable && + !pci_enable_msix(instance->pdev, &instance->msixentry, 1)) + instance->msi_flag = 1; + /* Get operational params, sge flags, send init cmd to controller */ if (instance->instancet->init_adapter(instance)) goto fail_init_adapter; @@ -4076,14 +4083,6 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) else INIT_WORK(&instance->work_init, process_fw_state_change_wq); - /* Try to enable MSI-X */ - if ((instance->pdev->device != PCI_DEVICE_ID_LSI_SAS1078R) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_SAS1078DE) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_VERDE_ZCR) && - !msix_disable && !pci_enable_msix(instance->pdev, - &instance->msixentry, 1)) - instance->msi_flag = 1; - /* * Initialize MFI Firmware */ @@ -4116,6 +4115,14 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) megasas_mgmt_info.max_index++; /* + * Register with SCSI mid-layer + */ + if (megasas_io_attach(instance)) + goto fail_io_attach; + + instance->unload = 0; + + /* * Initiate AEN (Asynchronous Event Notification) */ if (megasas_start_aen(instance)) { @@ -4123,13 +4130,6 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) goto fail_start_aen; } - /* - * Register with SCSI mid-layer - */ - if (megasas_io_attach(instance)) - goto fail_io_attach; - - instance->unload = 0; return 0; fail_start_aen: @@ -4332,10 +4332,6 @@ megasas_resume(struct pci_dev *pdev) if (megasas_set_dma_mask(pdev)) goto fail_set_dma_mask; - /* Now re-enable MSI-X */ - if (instance->msi_flag) - pci_enable_msix(instance->pdev, &instance->msixentry, 1); - /* * Initialize MFI Firmware */ @@ -4348,6 +4344,10 @@ megasas_resume(struct pci_dev *pdev) if (megasas_transition_to_ready(instance)) goto fail_ready_state; + /* Now re-enable MSI-X */ + if (instance->msi_flag) + pci_enable_msix(instance->pdev, &instance->msixentry, 1); + switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_FUSION: { @@ -4384,12 +4384,6 @@ megasas_resume(struct pci_dev *pdev) instance->instancet->enable_intr(instance->reg_set); - /* - * Initiate AEN (Asynchronous Event Notification) - */ - if (megasas_start_aen(instance)) - printk(KERN_ERR "megasas: Start AEN failed\n"); - /* Initialize the cmd completion timer */ if (poll_mode_io) megasas_start_timer(instance, &instance->io_completion_timer, @@ -4397,6 +4391,12 @@ megasas_resume(struct pci_dev *pdev) MEGASAS_COMPLETION_TIMER_INTERVAL); instance->unload = 0; + /* + * Initiate AEN (Asynchronous Event Notification) + */ + if (megasas_start_aen(instance)) + printk(KERN_ERR "megasas: Start AEN failed\n"); + return 0; fail_irq: @@ -4527,6 +4527,11 @@ static void megasas_shutdown(struct pci_dev *pdev) instance->unload = 1; megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); + instance->instancet->disable_intr(instance->reg_set); + free_irq(instance->msi_flag ? instance->msixentry.vector : + instance->pdev->irq, instance); + if (instance->msi_flag) + pci_disable_msix(instance->pdev); } /** diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 145a8cffb1fa..f13e7abd345a 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -696,22 +696,6 @@ fail_get_cmd: } /* - * megasas_return_cmd_for_smid - Returns a cmd_fusion for a SMID - * @instance: Adapter soft state - * - */ -void -megasas_return_cmd_for_smid(struct megasas_instance *instance, u16 smid) -{ - struct fusion_context *fusion; - struct megasas_cmd_fusion *cmd; - - fusion = instance->ctrl_context; - cmd = fusion->cmd_list[smid - 1]; - megasas_return_cmd_fusion(instance, cmd); -} - -/* * megasas_get_ld_map_info - Returns FW's ld_map structure * @instance: Adapter soft state * @pend: Pend the command or not @@ -1153,7 +1137,7 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len, u64 start_blk = io_info->pdBlock; u8 *cdb = io_request->CDB.CDB32; u32 num_blocks = io_info->numBlocks; - u8 opcode, flagvals, groupnum, control; + u8 opcode = 0, flagvals = 0, groupnum = 0, control = 0; /* Check if T10 PI (DIF) is enabled for this LD */ ld = MR_TargetIdToLdGet(io_info->ldTgtId, local_map_ptr); @@ -1235,7 +1219,46 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len, cdb[8] = (u8)(num_blocks & 0xff); cdb[7] = (u8)((num_blocks >> 8) & 0xff); + io_request->IoFlags = 10; /* Specify 10-byte cdb */ cdb_len = 10; + } else if ((cdb_len < 16) && (start_blk > 0xffffffff)) { + /* Convert to 16 byte CDB for large LBA's */ + switch (cdb_len) { + case 6: + opcode = cdb[0] == READ_6 ? READ_16 : WRITE_16; + control = cdb[5]; + break; + case 10: + opcode = + cdb[0] == READ_10 ? READ_16 : WRITE_16; + flagvals = cdb[1]; + groupnum = cdb[6]; + control = cdb[9]; + break; + case 12: + opcode = + cdb[0] == READ_12 ? READ_16 : WRITE_16; + flagvals = cdb[1]; + groupnum = cdb[10]; + control = cdb[11]; + break; + } + + memset(cdb, 0, sizeof(io_request->CDB.CDB32)); + + cdb[0] = opcode; + cdb[1] = flagvals; + cdb[14] = groupnum; + cdb[15] = control; + + /* Transfer length */ + cdb[13] = (u8)(num_blocks & 0xff); + cdb[12] = (u8)((num_blocks >> 8) & 0xff); + cdb[11] = (u8)((num_blocks >> 16) & 0xff); + cdb[10] = (u8)((num_blocks >> 24) & 0xff); + + io_request->IoFlags = 16; /* Specify 16-byte cdb */ + cdb_len = 16; } /* Normal case, just load LBA here */ @@ -2026,17 +2049,11 @@ int megasas_reset_fusion(struct Scsi_Host *shost) struct fusion_context *fusion; struct megasas_cmd *cmd_mfi; union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; - u32 host_diag, abs_state; + u32 host_diag, abs_state, status_reg, reset_adapter; instance = (struct megasas_instance *)shost->hostdata; fusion = instance->ctrl_context; - mutex_lock(&instance->reset_mutex); - set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); - instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; - instance->instancet->disable_intr(instance->reg_set); - msleep(1000); - if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { printk(KERN_WARNING "megaraid_sas: Hardware critical error, " "returning FAILED.\n"); @@ -2044,6 +2061,12 @@ int megasas_reset_fusion(struct Scsi_Host *shost) goto out; } + mutex_lock(&instance->reset_mutex); + set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); + instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; + instance->instancet->disable_intr(instance->reg_set); + msleep(1000); + /* First try waiting for commands to complete */ if (megasas_wait_for_outstanding_fusion(instance)) { printk(KERN_WARNING "megaraid_sas: resetting fusion " @@ -2060,7 +2083,12 @@ int megasas_reset_fusion(struct Scsi_Host *shost) } } - if (instance->disableOnlineCtrlReset == 1) { + status_reg = instance->instancet->read_fw_status_reg( + instance->reg_set); + abs_state = status_reg & MFI_STATE_MASK; + reset_adapter = status_reg & MFI_RESET_ADAPTER; + if (instance->disableOnlineCtrlReset || + (abs_state == MFI_STATE_FAULT && !reset_adapter)) { /* Reset not supported, kill adapter */ printk(KERN_WARNING "megaraid_sas: Reset not supported" ", killing adapter.\n"); @@ -2089,6 +2117,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost) /* Check that the diag write enable (DRWE) bit is on */ host_diag = readl(&instance->reg_set->fusion_host_diag); + retry = 0; while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) { msleep(100); host_diag = @@ -2126,7 +2155,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost) abs_state = instance->instancet->read_fw_status_reg( - instance->reg_set); + instance->reg_set) & MFI_STATE_MASK; retry = 0; while ((abs_state <= MFI_STATE_FW_INIT) && @@ -2134,7 +2163,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost) msleep(100); abs_state = instance->instancet->read_fw_status_reg( - instance->reg_set); + instance->reg_set) & MFI_STATE_MASK; } if (abs_state <= MFI_STATE_FW_INIT) { printk(KERN_WARNING "megaraid_sas: firmware " diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 2a3c05f6db8b..dcc289c25459 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -69,11 +69,11 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "08.100.00.01" +#define MPT2SAS_DRIVER_VERSION "08.100.00.02" #define MPT2SAS_MAJOR_VERSION 08 #define MPT2SAS_MINOR_VERSION 100 #define MPT2SAS_BUILD_VERSION 00 -#define MPT2SAS_RELEASE_VERSION 01 +#define MPT2SAS_RELEASE_VERSION 02 /* * Set MPT2SAS_SG_DEPTH value based on user input. diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index f12e02358d6d..a7dbc6825f5f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -113,6 +113,7 @@ struct sense_info { }; +#define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC) #define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) /** @@ -121,6 +122,7 @@ struct sense_info { * @work: work object (ioc->fault_reset_work_q) * @cancel_pending_work: flag set during reset handling * @ioc: per adapter object + * @device_handle: device handle * @VF_ID: virtual function id * @VP_ID: virtual port id * @ignore: flag meaning this event has been marked to ignore @@ -134,6 +136,7 @@ struct fw_event_work { u8 cancel_pending_work; struct delayed_work delayed_work; struct MPT2SAS_ADAPTER *ioc; + u16 device_handle; u8 VF_ID; u8 VP_ID; u8 ignore; @@ -3499,6 +3502,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) switch (prot_type) { case SCSI_PROT_DIF_TYPE1: + case SCSI_PROT_DIF_TYPE2: /* * enable ref/guard checking @@ -3511,13 +3515,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) cpu_to_be32(scsi_get_lba(scmd)); break; - case SCSI_PROT_DIF_TYPE2: - - eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - break; - case SCSI_PROT_DIF_TYPE3: /* @@ -4047,17 +4044,75 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, #endif /** - * _scsih_smart_predicted_fault - illuminate Fault LED + * _scsih_turn_on_fault_led - illuminate Fault LED * @ioc: per adapter object * @handle: device handle + * Context: process * * Return nothing. */ static void -_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SepReply_t mpi_reply; Mpi2SepRequest_t mpi_request; + + memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); + mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; + mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; + mpi_request.SlotStatus = + cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); + mpi_request.DevHandle = cpu_to_le16(handle); + mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; + if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, + &mpi_request)) != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "enclosure_processor: " + "ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo))); + return; + } +} + +/** + * _scsih_send_event_to_turn_on_fault_led - fire delayed event + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_send_event_to_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_TURN_ON_FAULT_LED; + fw_event->device_handle = handle; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); +} + +/** + * _scsih_smart_predicted_fault - process smart errors + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ struct scsi_target *starget; struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2EventNotificationReply_t *event_reply; @@ -4084,30 +4139,8 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) starget_printk(KERN_WARNING, starget, "predicted fault\n"); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) { - memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; - mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; - mpi_request.SlotStatus = - cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); - mpi_request.DevHandle = cpu_to_le16(handle); - mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; - if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "enclosure_processor: ioc_status (0x%04x), " - "loginfo(0x%08x)\n", ioc->name, - le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); - return; - } - } + if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) + _scsih_send_event_to_turn_on_fault_led(ioc, handle); /* insert into event log */ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + @@ -6753,6 +6786,9 @@ _firmware_event_work(struct work_struct *work) } switch (fw_event->event) { + case MPT2SAS_TURN_ON_FAULT_LED: + _scsih_turn_on_fault_led(ioc, fw_event->device_handle); + break; case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: _scsih_sas_topology_change_event(ioc, fw_event); break; diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 58f5be4740e9..de0b1a704fb5 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -4698,12 +4698,14 @@ static int __os_scsi_tape_open(struct inode * inode, struct file * filp) break; if ((SRpnt->sense[2] & 0x0f) == UNIT_ATTENTION) { + int j; + STp->pos_unknown = 0; STp->partition = STp->new_partition = 0; if (STp->can_partitions) STp->nbr_partitions = 1; /* This guess will be updated later if necessary */ - for (i=0; i < ST_NBR_PARTITIONS; i++) { - STps = &(STp->ps[i]); + for (j = 0; j < ST_NBR_PARTITIONS; j++) { + STps = &(STp->ps[j]); STps->rw = ST_IDLE; STps->eof = ST_NOEOF; STps->at_sm = 0; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 7f636b118287..fca6a8953070 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -4252,8 +4252,8 @@ static ssize_t pmcraid_show_drv_version( char *buf ) { - return snprintf(buf, PAGE_SIZE, "version: %s, build date: %s\n", - PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE); + return snprintf(buf, PAGE_SIZE, "version: %s\n", + PMCRAID_DRIVER_VERSION); } static struct device_attribute pmcraid_driver_version_attr = { @@ -6096,9 +6096,8 @@ static int __init pmcraid_init(void) dev_t dev; int error; - pmcraid_info("%s Device Driver version: %s %s\n", - PMCRAID_DRIVER_NAME, - PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE); + pmcraid_info("%s Device Driver version: %s\n", + PMCRAID_DRIVER_NAME, PMCRAID_DRIVER_VERSION); error = alloc_chrdev_region(&dev, 0, PMCRAID_MAX_ADAPTERS, diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 34e4c915002e..f920baf3ff24 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -43,7 +43,6 @@ #define PMCRAID_DRIVER_NAME "PMC MaxRAID" #define PMCRAID_DEVFILE "pmcsas" #define PMCRAID_DRIVER_VERSION "1.0.3" -#define PMCRAID_DRIVER_DATE __DATE__ #define PMCRAID_FW_VERSION_1 0x002 diff --git a/drivers/scsi/qla4xxx/Makefile b/drivers/scsi/qla4xxx/Makefile index 0339ff03a535..252523d7847e 100644 --- a/drivers/scsi/qla4xxx/Makefile +++ b/drivers/scsi/qla4xxx/Makefile @@ -1,5 +1,5 @@ qla4xxx-y := ql4_os.o ql4_init.o ql4_mbx.o ql4_iocb.o ql4_isr.o \ - ql4_nx.o ql4_nvram.o ql4_dbg.o + ql4_nx.o ql4_nvram.o ql4_dbg.o ql4_attr.o obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx.o diff --git a/drivers/scsi/qla4xxx/ql4_attr.c b/drivers/scsi/qla4xxx/ql4_attr.c new file mode 100644 index 000000000000..864d018631c0 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_attr.c @@ -0,0 +1,69 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2011 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" +#include "ql4_glbl.h" +#include "ql4_dbg.h" + +/* Scsi_Host attributes. */ +static ssize_t +qla4xxx_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); + + if (is_qla8022(ha)) + return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", + ha->firmware_version[0], + ha->firmware_version[1], + ha->patch_number, ha->build_number); + else + return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d.%02d\n", + ha->firmware_version[0], + ha->firmware_version[1], + ha->patch_number, ha->build_number); +} + +static ssize_t +qla4xxx_serial_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); + return snprintf(buf, PAGE_SIZE, "%s\n", ha->serial_number); +} + +static ssize_t +qla4xxx_iscsi_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); + return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->iscsi_major, + ha->iscsi_minor); +} + +static ssize_t +qla4xxx_optrom_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev)); + return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d.%02d\n", + ha->bootload_major, ha->bootload_minor, + ha->bootload_patch, ha->bootload_build); +} + +static DEVICE_ATTR(fw_version, S_IRUGO, qla4xxx_fw_version_show, NULL); +static DEVICE_ATTR(serial_num, S_IRUGO, qla4xxx_serial_num_show, NULL); +static DEVICE_ATTR(iscsi_version, S_IRUGO, qla4xxx_iscsi_version_show, NULL); +static DEVICE_ATTR(optrom_version, S_IRUGO, qla4xxx_optrom_version_show, NULL); + +struct device_attribute *qla4xxx_host_attrs[] = { + &dev_attr_fw_version, + &dev_attr_serial_num, + &dev_attr_iscsi_version, + &dev_attr_optrom_version, + NULL, +}; diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 4757878d59dd..473c5c872b39 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -115,7 +115,7 @@ #define INVALID_ENTRY 0xFFFF #define MAX_CMDS_TO_RISC 1024 #define MAX_SRBS MAX_CMDS_TO_RISC -#define MBOX_AEN_REG_COUNT 5 +#define MBOX_AEN_REG_COUNT 8 #define MAX_INIT_RETRIES 5 /* @@ -368,7 +368,6 @@ struct scsi_qla_host { #define AF_INIT_DONE 1 /* 0x00000002 */ #define AF_MBOX_COMMAND 2 /* 0x00000004 */ #define AF_MBOX_COMMAND_DONE 3 /* 0x00000008 */ -#define AF_DPC_SCHEDULED 5 /* 0x00000020 */ #define AF_INTERRUPTS_ON 6 /* 0x00000040 */ #define AF_GET_CRASH_RECORD 7 /* 0x00000080 */ #define AF_LINK_UP 8 /* 0x00000100 */ @@ -584,6 +583,14 @@ struct scsi_qla_host { uint32_t nx_reset_timeout; struct completion mbx_intr_comp; + + /* --- From About Firmware --- */ + uint16_t iscsi_major; + uint16_t iscsi_minor; + uint16_t bootload_major; + uint16_t bootload_minor; + uint16_t bootload_patch; + uint16_t bootload_build; }; static inline int is_ipv4_enabled(struct scsi_qla_host *ha) diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 31e2bf97198c..01082aa77098 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -690,6 +690,29 @@ struct mbx_sys_info { uint8_t reserved[12]; /* 34-3f */ }; +struct about_fw_info { + uint16_t fw_major; /* 00 - 01 */ + uint16_t fw_minor; /* 02 - 03 */ + uint16_t fw_patch; /* 04 - 05 */ + uint16_t fw_build; /* 06 - 07 */ + uint8_t fw_build_date[16]; /* 08 - 17 ASCII String */ + uint8_t fw_build_time[16]; /* 18 - 27 ASCII String */ + uint8_t fw_build_user[16]; /* 28 - 37 ASCII String */ + uint16_t fw_load_source; /* 38 - 39 */ + /* 1 = Flash Primary, + 2 = Flash Secondary, + 3 = Host Download + */ + uint8_t reserved1[6]; /* 3A - 3F */ + uint16_t iscsi_major; /* 40 - 41 */ + uint16_t iscsi_minor; /* 42 - 43 */ + uint16_t bootload_major; /* 44 - 45 */ + uint16_t bootload_minor; /* 46 - 47 */ + uint16_t bootload_patch; /* 48 - 49 */ + uint16_t bootload_build; /* 4A - 4B */ + uint8_t reserved2[180]; /* 4C - FF */ +}; + struct crash_record { uint16_t fw_major_version; /* 00 - 01 */ uint16_t fw_minor_version; /* 02 - 03 */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index cc53e3fbd78c..a53a256c1f8d 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -61,7 +61,7 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha); int qla4xxx_add_sess(struct ddb_entry *); void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry); int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host *ha); -int qla4xxx_get_fw_version(struct scsi_qla_host * ha); +int qla4xxx_about_firmware(struct scsi_qla_host *ha); void qla4xxx_interrupt_service_routine(struct scsi_qla_host *ha, uint32_t intr_status); int qla4xxx_init_rings(struct scsi_qla_host *ha); @@ -139,4 +139,5 @@ extern int ql4xextended_error_logging; extern int ql4xdontresethba; extern int ql4xenablemsix; +extern struct device_attribute *qla4xxx_host_attrs[]; #endif /* _QLA4x_GBL_H */ diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 48e2241ddaf4..42ed5db2d530 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -1275,7 +1275,7 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, if (ha->isp_ops->start_firmware(ha) == QLA_ERROR) goto exit_init_hba; - if (qla4xxx_get_fw_version(ha) == QLA_ERROR) + if (qla4xxx_about_firmware(ha) == QLA_ERROR) goto exit_init_hba; if (ha->isp_ops->get_sys_info(ha) == QLA_ERROR) diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 2f40ac761cd4..0e72921c752d 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -25,9 +25,14 @@ static void qla4xxx_copy_sense(struct scsi_qla_host *ha, memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); sense_len = le16_to_cpu(sts_entry->senseDataByteCnt); - if (sense_len == 0) + if (sense_len == 0) { + DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%d: %s:" + " sense len 0\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, __func__)); + ha->status_srb = NULL; return; - + } /* Save total available sense length, * not to exceed cmd's sense buffer size */ sense_len = min_t(uint16_t, sense_len, SCSI_SENSE_BUFFERSIZE); @@ -541,6 +546,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */ case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR: case MBOX_ASTS_SUBNET_STATE_CHANGE: + case MBOX_ASTS_DUPLICATE_IP: /* No action */ DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no, mbox_status)); @@ -593,11 +599,13 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, mbox_sts[i]; /* print debug message */ - DEBUG2(printk("scsi%ld: AEN[%d] %04x queued" - " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n", - ha->host_no, ha->aen_in, mbox_sts[0], - mbox_sts[1], mbox_sts[2], mbox_sts[3], - mbox_sts[4])); + DEBUG2(printk("scsi%ld: AEN[%d] %04x queued " + "mb1:0x%x mb2:0x%x mb3:0x%x " + "mb4:0x%x mb5:0x%x\n", + ha->host_no, ha->aen_in, + mbox_sts[0], mbox_sts[1], + mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5])); /* advance pointer */ ha->aen_in++; diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index d78b58dc5011..fce8289e9752 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -86,22 +86,8 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, msleep(10); } - /* To prevent overwriting mailbox registers for a command that has - * not yet been serviced, check to see if an active command - * (AEN, IOCB, etc.) is interrupting, then service it. - * ----------------------------------------------------------------- - */ spin_lock_irqsave(&ha->hardware_lock, flags); - if (!is_qla8022(ha)) { - intr_status = readl(&ha->reg->ctrl_status); - if (intr_status & CSR_SCSI_PROCESSOR_INTR) { - /* Service existing interrupt */ - ha->isp_ops->interrupt_service_routine(ha, intr_status); - clear_bit(AF_MBOX_COMMAND_DONE, &ha->flags); - } - } - ha->mbox_status_count = outCount; for (i = 0; i < outCount; i++) ha->mbox_status[i] = 0; @@ -1057,38 +1043,65 @@ int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, } /** - * qla4xxx_get_fw_version - gets firmware version + * qla4xxx_about_firmware - gets FW, iscsi draft and boot loader version * @ha: Pointer to host adapter structure. * - * Retrieves the firmware version on HBA. In QLA4010, mailboxes 2 & 3 may - * hold an address for data. Make sure that we write 0 to those mailboxes, - * if unused. + * Retrieves the FW version, iSCSI draft version & bootloader version of HBA. + * Mailboxes 2 & 3 may hold an address for data. Make sure that we write 0 to + * those mailboxes, if unused. **/ -int qla4xxx_get_fw_version(struct scsi_qla_host * ha) +int qla4xxx_about_firmware(struct scsi_qla_host *ha) { + struct about_fw_info *about_fw = NULL; + dma_addr_t about_fw_dma; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_ERROR; + + about_fw = dma_alloc_coherent(&ha->pdev->dev, + sizeof(struct about_fw_info), + &about_fw_dma, GFP_KERNEL); + if (!about_fw) { + DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Unable to alloc memory " + "for about_fw\n", __func__)); + return status; + } - /* Get firmware version. */ + memset(about_fw, 0, sizeof(struct about_fw_info)); memset(&mbox_cmd, 0, sizeof(mbox_cmd)); memset(&mbox_sts, 0, sizeof(mbox_sts)); mbox_cmd[0] = MBOX_CMD_ABOUT_FW; - - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0], &mbox_sts[0]) != - QLA_SUCCESS) { - DEBUG2(printk("scsi%ld: %s: MBOX_CMD_ABOUT_FW failed w/ " - "status %04X\n", ha->host_no, __func__, mbox_sts[0])); - return QLA_ERROR; + mbox_cmd[2] = LSDW(about_fw_dma); + mbox_cmd[3] = MSDW(about_fw_dma); + mbox_cmd[4] = sizeof(struct about_fw_info); + + status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, MBOX_REG_COUNT, + &mbox_cmd[0], &mbox_sts[0]); + if (status != QLA_SUCCESS) { + DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: MBOX_CMD_ABOUT_FW " + "failed w/ status %04X\n", __func__, + mbox_sts[0])); + goto exit_about_fw; } - /* Save firmware version information. */ - ha->firmware_version[0] = mbox_sts[1]; - ha->firmware_version[1] = mbox_sts[2]; - ha->patch_number = mbox_sts[3]; - ha->build_number = mbox_sts[4]; + /* Save version information. */ + ha->firmware_version[0] = le16_to_cpu(about_fw->fw_major); + ha->firmware_version[1] = le16_to_cpu(about_fw->fw_minor); + ha->patch_number = le16_to_cpu(about_fw->fw_patch); + ha->build_number = le16_to_cpu(about_fw->fw_build); + ha->iscsi_major = le16_to_cpu(about_fw->iscsi_major); + ha->iscsi_minor = le16_to_cpu(about_fw->iscsi_minor); + ha->bootload_major = le16_to_cpu(about_fw->bootload_major); + ha->bootload_minor = le16_to_cpu(about_fw->bootload_minor); + ha->bootload_patch = le16_to_cpu(about_fw->bootload_patch); + ha->bootload_build = le16_to_cpu(about_fw->bootload_build); + status = QLA_SUCCESS; - return QLA_SUCCESS; +exit_about_fw: + dma_free_coherent(&ha->pdev->dev, sizeof(struct about_fw_info), + about_fw, about_fw_dma); + return status; } static int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 03e522b2fe0b..fdfe27b38698 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -964,12 +964,26 @@ qla4_8xxx_pinit_from_rom(struct scsi_qla_host *ha, int verbose) /* Halt all the indiviual PEGs and other blocks of the ISP */ qla4_8xxx_rom_lock(ha); - /* mask all niu interrupts */ + /* disable all I2Q */ + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x10, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x14, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x18, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x1c, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x20, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_I2Q + 0x24, 0x0); + + /* disable all niu interrupts */ qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x40, 0xff); /* disable xge rx/tx */ qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x70000, 0x00); /* disable xg1 rx/tx */ qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x80000, 0x00); + /* disable sideband mac */ + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x90000, 0x00); + /* disable ap0 mac */ + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0xa0000, 0x00); + /* disable ap1 mac */ + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0xb0000, 0x00); /* halt sre */ val = qla4_8xxx_rd_32(ha, QLA82XX_CRB_SRE + 0x1000); @@ -984,6 +998,7 @@ qla4_8xxx_pinit_from_rom(struct scsi_qla_host *ha, int verbose) qla4_8xxx_wr_32(ha, QLA82XX_CRB_TIMER + 0x10, 0x0); qla4_8xxx_wr_32(ha, QLA82XX_CRB_TIMER + 0x18, 0x0); qla4_8xxx_wr_32(ha, QLA82XX_CRB_TIMER + 0x100, 0x0); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_TIMER + 0x200, 0x0); /* halt pegs */ qla4_8xxx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c, 1); @@ -991,9 +1006,9 @@ qla4_8xxx_pinit_from_rom(struct scsi_qla_host *ha, int verbose) qla4_8xxx_wr_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c, 1); qla4_8xxx_wr_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c, 1); qla4_8xxx_wr_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c, 1); + msleep(5); /* big hammer */ - msleep(1000); if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) /* don't reset CAM block on reset */ qla4_8xxx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index c22f2a764d9d..f2364ec59f03 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -124,6 +124,7 @@ static struct scsi_host_template qla4xxx_driver_template = { .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, + .shost_attrs = qla4xxx_host_attrs, }; static struct iscsi_transport qla4xxx_iscsi_transport = { @@ -412,8 +413,7 @@ void qla4xxx_mark_all_devices_missing(struct scsi_qla_host *ha) static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry, - struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) + struct scsi_cmnd *cmd) { struct srb *srb; @@ -427,7 +427,6 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, srb->cmd = cmd; srb->flags = 0; CMD_SP(cmd) = (void *)srb; - cmd->scsi_done = done; return srb; } @@ -458,9 +457,8 @@ void qla4xxx_srb_compl(struct kref *ref) /** * qla4xxx_queuecommand - scsi layer issues scsi command to driver. + * @host: scsi host * @cmd: Pointer to Linux's SCSI command structure - * @done_fn: Function that the driver calls to notify the SCSI mid-layer - * that the command has been processed. * * Remarks: * This routine is invoked by Linux to send a SCSI command to the driver. @@ -470,10 +468,9 @@ void qla4xxx_srb_compl(struct kref *ref) * completion handling). Unfortunely, it sometimes calls the scheduler * in interrupt context which is a big NO! NO!. **/ -static int qla4xxx_queuecommand_lck(struct scsi_cmnd *cmd, - void (*done)(struct scsi_cmnd *)) +static int qla4xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { - struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + struct scsi_qla_host *ha = to_qla_host(host); struct ddb_entry *ddb_entry = cmd->device->hostdata; struct iscsi_cls_session *sess = ddb_entry->sess; struct srb *srb; @@ -515,37 +512,29 @@ static int qla4xxx_queuecommand_lck(struct scsi_cmnd *cmd, test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags)) goto qc_host_busy; - spin_unlock_irq(ha->host->host_lock); - - srb = qla4xxx_get_new_srb(ha, ddb_entry, cmd, done); + srb = qla4xxx_get_new_srb(ha, ddb_entry, cmd); if (!srb) - goto qc_host_busy_lock; + goto qc_host_busy; rval = qla4xxx_send_command_to_isp(ha, srb); if (rval != QLA_SUCCESS) goto qc_host_busy_free_sp; - spin_lock_irq(ha->host->host_lock); return 0; qc_host_busy_free_sp: qla4xxx_srb_free_dma(ha, srb); mempool_free(srb, ha->srb_mempool); -qc_host_busy_lock: - spin_lock_irq(ha->host->host_lock); - qc_host_busy: return SCSI_MLQUEUE_HOST_BUSY; qc_fail_command: - done(cmd); + cmd->scsi_done(cmd); return 0; } -static DEF_SCSI_QCMD(qla4xxx_queuecommand) - /** * qla4xxx_mem_free - frees memory allocated to adapter * @ha: Pointer to host adapter structure. @@ -679,7 +668,27 @@ static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha) if (ha->seconds_since_last_heartbeat == 2) { ha->seconds_since_last_heartbeat = 0; halt_status = qla4_8xxx_rd_32(ha, - QLA82XX_PEG_HALT_STATUS1); + QLA82XX_PEG_HALT_STATUS1); + + ql4_printk(KERN_INFO, ha, + "scsi(%ld): %s, Dumping hw/fw registers:\n " + " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2:" + " 0x%x,\n PEG_NET_0_PC: 0x%x, PEG_NET_1_PC:" + " 0x%x,\n PEG_NET_2_PC: 0x%x, PEG_NET_3_PC:" + " 0x%x,\n PEG_NET_4_PC: 0x%x\n", + ha->host_no, __func__, halt_status, + qla4_8xxx_rd_32(ha, + QLA82XX_PEG_HALT_STATUS2), + qla4_8xxx_rd_32(ha, QLA82XX_CRB_PEG_NET_0 + + 0x3c), + qla4_8xxx_rd_32(ha, QLA82XX_CRB_PEG_NET_1 + + 0x3c), + qla4_8xxx_rd_32(ha, QLA82XX_CRB_PEG_NET_2 + + 0x3c), + qla4_8xxx_rd_32(ha, QLA82XX_CRB_PEG_NET_3 + + 0x3c), + qla4_8xxx_rd_32(ha, QLA82XX_CRB_PEG_NET_4 + + 0x3c)); /* Since we cannot change dev_state in interrupt * context, set appropriate DPC flag then wakeup @@ -715,7 +724,7 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) /* don't poll if reset is going on */ if (!(test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) || test_bit(DPC_RESET_HA, &ha->dpc_flags) || - test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags))) { + test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags))) { if (dev_state == QLA82XX_DEV_NEED_RESET && !test_bit(DPC_RESET_HA, &ha->dpc_flags)) { if (!ql4xdontresethba) { @@ -839,7 +848,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha) } /* Wakeup the dpc routine for this adapter, if needed. */ - if ((start_dpc || + if (start_dpc || test_bit(DPC_RESET_HA, &ha->dpc_flags) || test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags) || test_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags) || @@ -849,9 +858,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha) test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) || test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags) || test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags) || - test_bit(DPC_AEN, &ha->dpc_flags)) && - !test_bit(AF_DPC_SCHEDULED, &ha->flags) && - ha->dpc_thread) { + test_bit(DPC_AEN, &ha->dpc_flags)) { DEBUG2(printk("scsi%ld: %s: scheduling dpc routine" " - dpc flags = 0x%lx\n", ha->host_no, __func__, ha->dpc_flags)); @@ -1241,11 +1248,8 @@ static void qla4xxx_relogin_all_devices(struct scsi_qla_host *ha) void qla4xxx_wake_dpc(struct scsi_qla_host *ha) { - if (ha->dpc_thread && - !test_bit(AF_DPC_SCHEDULED, &ha->flags)) { - set_bit(AF_DPC_SCHEDULED, &ha->flags); + if (ha->dpc_thread) queue_work(ha->dpc_thread, &ha->dpc_work); - } } /** @@ -1272,12 +1276,12 @@ static void qla4xxx_do_dpc(struct work_struct *work) /* Initialization not yet finished. Don't do anything yet. */ if (!test_bit(AF_INIT_DONE, &ha->flags)) - goto do_dpc_exit; + return; if (test_bit(AF_EEH_BUSY, &ha->flags)) { DEBUG2(printk(KERN_INFO "scsi%ld: %s: flags = %lx\n", ha->host_no, __func__, ha->flags)); - goto do_dpc_exit; + return; } if (is_qla8022(ha)) { @@ -1384,8 +1388,6 @@ dpc_post_reset_ha: } } -do_dpc_exit: - clear_bit(AF_DPC_SCHEDULED, &ha->flags); } /** diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index 603155769407..610492877253 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,4 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.02.00-k6" +#define QLA4XXX_DRIVER_VERSION "5.02.00-k7" diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index abea2cf05c2e..a4b9cdbaaa0b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -50,6 +50,8 @@ #define BUS_RESET_SETTLE_TIME (10) #define HOST_RESET_SETTLE_TIME (10) +static int scsi_eh_try_stu(struct scsi_cmnd *scmd); + /* called with shost->host_lock held */ void scsi_eh_wakeup(struct Scsi_Host *shost) { @@ -947,6 +949,48 @@ retry_tur: } /** + * scsi_eh_test_devices - check if devices are responding from error recovery. + * @cmd_list: scsi commands in error recovery. + * @work_q: queue for commands which still need more error recovery + * @done_q: queue for commands which are finished + * @try_stu: boolean on if a STU command should be tried in addition to TUR. + * + * Decription: + * Tests if devices are in a working state. Commands to devices now in + * a working state are sent to the done_q while commands to devices which + * are still failing to respond are returned to the work_q for more + * processing. + **/ +static int scsi_eh_test_devices(struct list_head *cmd_list, + struct list_head *work_q, + struct list_head *done_q, int try_stu) +{ + struct scsi_cmnd *scmd, *next; + struct scsi_device *sdev; + int finish_cmds; + + while (!list_empty(cmd_list)) { + scmd = list_entry(cmd_list->next, struct scsi_cmnd, eh_entry); + sdev = scmd->device; + + finish_cmds = !scsi_device_online(scmd->device) || + (try_stu && !scsi_eh_try_stu(scmd) && + !scsi_eh_tur(scmd)) || + !scsi_eh_tur(scmd); + + list_for_each_entry_safe(scmd, next, cmd_list, eh_entry) + if (scmd->device == sdev) { + if (finish_cmds) + scsi_eh_finish_cmd(scmd, done_q); + else + list_move_tail(&scmd->eh_entry, work_q); + } + } + return list_empty(work_q); +} + + +/** * scsi_eh_abort_cmds - abort pending commands. * @work_q: &list_head for pending commands. * @done_q: &list_head for processed commands. @@ -962,6 +1006,7 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, struct list_head *done_q) { struct scsi_cmnd *scmd, *next; + LIST_HEAD(check_list); int rtn; list_for_each_entry_safe(scmd, next, work_q, eh_entry) { @@ -973,11 +1018,10 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, rtn = scsi_try_to_abort_cmd(scmd->device->host->hostt, scmd); if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD; - if (!scsi_device_online(scmd->device) || - rtn == FAST_IO_FAIL || - !scsi_eh_tur(scmd)) { + if (rtn == FAST_IO_FAIL) scsi_eh_finish_cmd(scmd, done_q); - } + else + list_move_tail(&scmd->eh_entry, &check_list); } else SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting" " cmd failed:" @@ -986,7 +1030,7 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, scmd)); } - return list_empty(work_q); + return scsi_eh_test_devices(&check_list, work_q, done_q, 0); } /** @@ -1137,6 +1181,7 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, struct list_head *done_q) { LIST_HEAD(tmp_list); + LIST_HEAD(check_list); list_splice_init(work_q, &tmp_list); @@ -1161,9 +1206,9 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, if (scmd_id(scmd) != id) continue; - if ((rtn == SUCCESS || rtn == FAST_IO_FAIL) - && (!scsi_device_online(scmd->device) || - rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd))) + if (rtn == SUCCESS) + list_move_tail(&scmd->eh_entry, &check_list); + else if (rtn == FAST_IO_FAIL) scsi_eh_finish_cmd(scmd, done_q); else /* push back on work queue for further processing */ @@ -1171,7 +1216,7 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, } } - return list_empty(work_q); + return scsi_eh_test_devices(&check_list, work_q, done_q, 0); } /** @@ -1185,6 +1230,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, struct list_head *done_q) { struct scsi_cmnd *scmd, *chan_scmd, *next; + LIST_HEAD(check_list); unsigned int channel; int rtn; @@ -1216,12 +1262,14 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, rtn = scsi_try_bus_reset(chan_scmd); if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { - if (channel == scmd_channel(scmd)) - if (!scsi_device_online(scmd->device) || - rtn == FAST_IO_FAIL || - !scsi_eh_tur(scmd)) + if (channel == scmd_channel(scmd)) { + if (rtn == FAST_IO_FAIL) scsi_eh_finish_cmd(scmd, done_q); + else + list_move_tail(&scmd->eh_entry, + &check_list); + } } } else { SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BRST" @@ -1230,7 +1278,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, channel)); } } - return list_empty(work_q); + return scsi_eh_test_devices(&check_list, work_q, done_q, 0); } /** @@ -1242,6 +1290,7 @@ static int scsi_eh_host_reset(struct list_head *work_q, struct list_head *done_q) { struct scsi_cmnd *scmd, *next; + LIST_HEAD(check_list); int rtn; if (!list_empty(work_q)) { @@ -1252,12 +1301,10 @@ static int scsi_eh_host_reset(struct list_head *work_q, , current->comm)); rtn = scsi_try_host_reset(scmd); - if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { + if (rtn == SUCCESS) { + list_splice_init(work_q, &check_list); + } else if (rtn == FAST_IO_FAIL) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { - if (!scsi_device_online(scmd->device) || - rtn == FAST_IO_FAIL || - (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) || - !scsi_eh_tur(scmd)) scsi_eh_finish_cmd(scmd, done_q); } } else { @@ -1266,7 +1313,7 @@ static int scsi_eh_host_reset(struct list_head *work_q, current->comm)); } } - return list_empty(work_q); + return scsi_eh_test_devices(&check_list, work_q, done_q, 1); } /** diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c index b587289cfacb..2bea4f0b684a 100644 --- a/drivers/scsi/scsi_trace.c +++ b/drivers/scsi/scsi_trace.c @@ -59,6 +59,10 @@ scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", (unsigned long long)lba, (unsigned long long)txlen, cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + trace_seq_putc(p, 0); return ret; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index bd0806e64e85..953773cb26d9 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -490,7 +490,8 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) unsigned int max_blocks = 0; q->limits.discard_zeroes_data = sdkp->lbprz; - q->limits.discard_alignment = sdkp->unmap_alignment; + q->limits.discard_alignment = sdkp->unmap_alignment * + logical_block_size; q->limits.discard_granularity = max(sdkp->physical_block_size, sdkp->unmap_granularity * logical_block_size); @@ -2021,16 +2022,26 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) int dbd; int modepage; + int first_len; struct scsi_mode_data data; struct scsi_sense_hdr sshdr; int old_wce = sdkp->WCE; int old_rcd = sdkp->RCD; int old_dpofua = sdkp->DPOFUA; - if (sdp->skip_ms_page_8) - goto defaults; - - if (sdp->type == TYPE_RBC) { + first_len = 4; + if (sdp->skip_ms_page_8) { + if (sdp->type == TYPE_RBC) + goto defaults; + else { + if (sdp->skip_ms_page_3f) + goto defaults; + modepage = 0x3F; + if (sdp->use_192_bytes_for_3f) + first_len = 192; + dbd = 0; + } + } else if (sdp->type == TYPE_RBC) { modepage = 6; dbd = 8; } else { @@ -2039,13 +2050,15 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) } /* cautiously ask */ - res = sd_do_mode_sense(sdp, dbd, modepage, buffer, 4, &data, &sshdr); + res = sd_do_mode_sense(sdp, dbd, modepage, buffer, first_len, + &data, &sshdr); if (!scsi_status_is_good(res)) goto bad_sense; if (!data.header_length) { modepage = 6; + first_len = 0; sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response\n"); } @@ -2058,30 +2071,61 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) */ if (len < 3) goto bad_sense; - if (len > 20) - len = 20; - - /* Take headers and block descriptors into account */ - len += data.header_length + data.block_descriptor_length; - if (len > SD_BUF_SIZE) - goto bad_sense; + else if (len > SD_BUF_SIZE) { + sd_printk(KERN_NOTICE, sdkp, "Truncating mode parameter " + "data from %d to %d bytes\n", len, SD_BUF_SIZE); + len = SD_BUF_SIZE; + } + if (modepage == 0x3F && sdp->use_192_bytes_for_3f) + len = 192; /* Get the data */ - res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); + if (len > first_len) + res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, + &data, &sshdr); if (scsi_status_is_good(res)) { int offset = data.header_length + data.block_descriptor_length; - if (offset >= SD_BUF_SIZE - 2) { - sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response\n"); - goto defaults; + while (offset < len) { + u8 page_code = buffer[offset] & 0x3F; + u8 spf = buffer[offset] & 0x40; + + if (page_code == 8 || page_code == 6) { + /* We're interested only in the first 3 bytes. + */ + if (len - offset <= 2) { + sd_printk(KERN_ERR, sdkp, "Incomplete " + "mode parameter data\n"); + goto defaults; + } else { + modepage = page_code; + goto Page_found; + } + } else { + /* Go to the next page */ + if (spf && len - offset > 3) + offset += 4 + (buffer[offset+2] << 8) + + buffer[offset+3]; + else if (!spf && len - offset > 1) + offset += 2 + buffer[offset+1]; + else { + sd_printk(KERN_ERR, sdkp, "Incomplete " + "mode parameter data\n"); + goto defaults; + } + } } - if ((buffer[offset] & 0x3f) != modepage) { + if (modepage == 0x3F) { + sd_printk(KERN_ERR, sdkp, "No Caching mode page " + "present\n"); + goto defaults; + } else if ((buffer[offset] & 0x3f) != modepage) { sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); goto defaults; } - + Page_found: if (modepage == 8) { sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c index 9f4b58b7daad..7e22b737dfd8 100644 --- a/drivers/scsi/ultrastor.c +++ b/drivers/scsi/ultrastor.c @@ -307,7 +307,7 @@ static inline int find_and_clear_bit_16(unsigned long *field) "0: bsfw %1,%w0\n\t" "btr %0,%1\n\t" "jnc 0b" - : "=&r" (rv), "=m" (*field) :); + : "=&r" (rv), "+m" (*field) :); return rv; } diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 97ae716134d0..c0ee4ea28a19 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -2051,8 +2051,7 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, for (i = 0; i < MAX_SETUP_ARGS; i++) printk("%s,", setup_args[i]); printk("\n"); - printk(" Version %s - %s, Compiled %s at %s\n", - WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__); + printk(" Version %s - %s\n", WD33C93_VERSION, WD33C93_DATE); } int @@ -2132,8 +2131,8 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off bp = buf; *bp = '\0'; if (hd->proc & PR_VERSION) { - sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s", - WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__); + sprintf(tbuf, "\nVersion %s - %s.", + WD33C93_VERSION, WD33C93_DATE); strcat(bp, tbuf); } if (hd->proc & PR_INFO) { diff --git a/drivers/staging/generic_serial/rio/rioinit.c b/drivers/staging/generic_serial/rio/rioinit.c index 24a282bb89d4..fb62b383f1de 100644 --- a/drivers/staging/generic_serial/rio/rioinit.c +++ b/drivers/staging/generic_serial/rio/rioinit.c @@ -381,7 +381,7 @@ struct rioVersion *RIOVersid(void) { strlcpy(stVersion.version, "RIO driver for linux V1.0", sizeof(stVersion.version)); - strlcpy(stVersion.buildDate, __DATE__, + strlcpy(stVersion.buildDate, "Aug 15 2010", sizeof(stVersion.buildDate)); return &stVersion; diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index aed4e464d31c..dee2a2c909f5 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -31,7 +31,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_cmnd.h> -#include <scsi/libsas.h> /* For TASK_ATTR_* */ +#include <scsi/scsi_tcq.h> #include <target/target_core_base.h> #include <target/target_core_transport.h> @@ -95,17 +95,17 @@ static struct se_cmd *tcm_loop_allocate_core_cmd( if (sc->device->tagged_supported) { switch (sc->tag) { case HEAD_OF_QUEUE_TAG: - sam_task_attr = TASK_ATTR_HOQ; + sam_task_attr = MSG_HEAD_TAG; break; case ORDERED_QUEUE_TAG: - sam_task_attr = TASK_ATTR_ORDERED; + sam_task_attr = MSG_ORDERED_TAG; break; default: - sam_task_attr = TASK_ATTR_SIMPLE; + sam_task_attr = MSG_SIMPLE_TAG; break; } } else - sam_task_attr = TASK_ATTR_SIMPLE; + sam_task_attr = MSG_SIMPLE_TAG; /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure @@ -379,7 +379,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, - DMA_NONE, TASK_ATTR_SIMPLE, + DMA_NONE, MSG_SIMPLE_TAG, &tl_cmd->tl_sense_buf[0]); /* * Allocate the LUN_RESET TMR @@ -939,18 +939,6 @@ static u16 tcm_loop_get_fabric_sense_len(void) return 0; } -static u64 tcm_loop_pack_lun(unsigned int lun) -{ - u64 result; - - /* LSB of lun into byte 1 big-endian */ - result = ((lun & 0xff) << 8); - /* use flat space addressing method */ - result |= 0x40 | ((lun >> 8) & 0x3f); - - return cpu_to_le64(result); -} - static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) { switch (tl_hba->tl_proto_id) { @@ -1481,7 +1469,6 @@ static int tcm_loop_register_configfs(void) fabric->tf_ops.set_fabric_sense_len = &tcm_loop_set_fabric_sense_len; fabric->tf_ops.get_fabric_sense_len = &tcm_loop_get_fabric_sense_len; fabric->tf_ops.is_state_remove = &tcm_loop_is_state_remove; - fabric->tf_ops.pack_lun = &tcm_loop_pack_lun; tf_cg = &fabric->tf_group; /* diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index a5f44a6e6e1d..ee6fad979b50 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -497,10 +497,6 @@ static int target_fabric_tf_ops_check( printk(KERN_ERR "Missing tfo->is_state_remove()\n"); return -EINVAL; } - if (!(tfo->pack_lun)) { - printk(KERN_ERR "Missing tfo->pack_lun()\n"); - return -EINVAL; - } /* * We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn() * tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index d25e20829012..8407f9ca2b31 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -38,6 +38,7 @@ #include <net/sock.h> #include <net/tcp.h> #include <scsi/scsi.h> +#include <scsi/scsi_device.h> #include <target/target_core_base.h> #include <target/target_core_device.h> @@ -150,13 +151,13 @@ out: { struct se_device *dev = se_lun->lun_se_dev; - spin_lock(&dev->stats_lock); + spin_lock_irq(&dev->stats_lock); dev->num_cmds++; if (se_cmd->data_direction == DMA_TO_DEVICE) dev->write_bytes += se_cmd->data_length; else if (se_cmd->data_direction == DMA_FROM_DEVICE) dev->read_bytes += se_cmd->data_length; - spin_unlock(&dev->stats_lock); + spin_unlock_irq(&dev->stats_lock); } /* @@ -658,8 +659,7 @@ int transport_core_report_lun_response(struct se_cmd *se_cmd) struct se_session *se_sess = SE_SESS(se_cmd); struct se_task *se_task; unsigned char *buf = (unsigned char *)T_TASK(se_cmd)->t_task_buf; - u32 cdb_offset = 0, lun_count = 0, offset = 8; - u64 i, lun; + u32 cdb_offset = 0, lun_count = 0, offset = 8, i; list_for_each_entry(se_task, &T_TASK(se_cmd)->t_task_list, t_list) break; @@ -675,15 +675,7 @@ int transport_core_report_lun_response(struct se_cmd *se_cmd) * a $FABRIC_MOD. In that case, report LUN=0 only. */ if (!(se_sess)) { - lun = 0; - buf[offset++] = ((lun >> 56) & 0xff); - buf[offset++] = ((lun >> 48) & 0xff); - buf[offset++] = ((lun >> 40) & 0xff); - buf[offset++] = ((lun >> 32) & 0xff); - buf[offset++] = ((lun >> 24) & 0xff); - buf[offset++] = ((lun >> 16) & 0xff); - buf[offset++] = ((lun >> 8) & 0xff); - buf[offset++] = (lun & 0xff); + int_to_scsilun(0, (struct scsi_lun *)&buf[offset]); lun_count = 1; goto done; } @@ -703,15 +695,8 @@ int transport_core_report_lun_response(struct se_cmd *se_cmd) if ((cdb_offset + 8) >= se_cmd->data_length) continue; - lun = cpu_to_be64(CMD_TFO(se_cmd)->pack_lun(deve->mapped_lun)); - buf[offset++] = ((lun >> 56) & 0xff); - buf[offset++] = ((lun >> 48) & 0xff); - buf[offset++] = ((lun >> 40) & 0xff); - buf[offset++] = ((lun >> 32) & 0xff); - buf[offset++] = ((lun >> 24) & 0xff); - buf[offset++] = ((lun >> 16) & 0xff); - buf[offset++] = ((lun >> 8) & 0xff); - buf[offset++] = (lun & 0xff); + int_to_scsilun(deve->mapped_lun, (struct scsi_lun *)&buf[offset]); + offset += 8; cdb_offset += 8; } spin_unlock_irq(&SE_NODE_ACL(se_sess)->device_list_lock); diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 7ff6a35f26ac..331d423fd0e0 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -41,7 +41,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> -#include <scsi/libsas.h> /* For TASK_ATTR_* */ +#include <scsi/scsi_tcq.h> #include <target/target_core_base.h> #include <target/target_core_device.h> @@ -911,7 +911,7 @@ static int pscsi_do_task(struct se_task *task) * descriptor */ blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, pt->pscsi_req, - (task->task_se_cmd->sam_task_attr == TASK_ATTR_HOQ), + (task->task_se_cmd->sam_task_attr == MSG_HEAD_TAG), pscsi_req_done); return PYX_TRANSPORT_SENT_TO_TRANSPORT; diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 4a109835e420..59b8b9c5ad72 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -55,7 +55,8 @@ struct se_tmr_req *core_tmr_alloc_req( { struct se_tmr_req *tmr; - tmr = kmem_cache_zalloc(se_tmr_req_cache, GFP_KERNEL); + tmr = kmem_cache_zalloc(se_tmr_req_cache, (in_interrupt()) ? + GFP_ATOMIC : GFP_KERNEL); if (!(tmr)) { printk(KERN_ERR "Unable to allocate struct se_tmr_req\n"); return ERR_PTR(-ENOMEM); @@ -398,9 +399,9 @@ int core_tmr_lun_reset( printk(KERN_INFO "LUN_RESET: SCSI-2 Released reservation\n"); } - spin_lock(&dev->stats_lock); + spin_lock_irq(&dev->stats_lock); dev->num_resets++; - spin_unlock(&dev->stats_lock); + spin_unlock_irq(&dev->stats_lock); DEBUG_LR("LUN_RESET: %s for [%s] Complete\n", (preempt_and_abort_list) ? "Preempt" : "TMR", diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index b9d3501bdd91..4dafeb8b5638 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -42,7 +42,7 @@ #include <net/tcp.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> -#include <scsi/libsas.h> /* For TASK_ATTR_* */ +#include <scsi/scsi_tcq.h> #include <target/target_core_base.h> #include <target/target_core_device.h> @@ -762,7 +762,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) transport_all_task_dev_remove_state(cmd); spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags); - transport_free_dev_tasks(cmd); check_lun: spin_lock_irqsave(&lun->lun_cmd_lock, flags); @@ -1075,7 +1074,7 @@ static inline int transport_add_task_check_sam_attr( * head of the struct se_device->execute_task_list, and task_prev * after that for each subsequent task */ - if (task->task_se_cmd->sam_task_attr == TASK_ATTR_HOQ) { + if (task->task_se_cmd->sam_task_attr == MSG_HEAD_TAG) { list_add(&task->t_execute_list, (task_prev != NULL) ? &task_prev->t_execute_list : @@ -1195,6 +1194,7 @@ transport_get_task_from_execute_queue(struct se_device *dev) break; list_del(&task->t_execute_list); + atomic_set(&task->task_execute_queue, 0); atomic_dec(&dev->execute_tasks); return task; @@ -1210,8 +1210,14 @@ void transport_remove_task_from_execute_queue( { unsigned long flags; + if (atomic_read(&task->task_execute_queue) == 0) { + dump_stack(); + return; + } + spin_lock_irqsave(&dev->execute_task_lock, flags); list_del(&task->t_execute_list); + atomic_set(&task->task_execute_queue, 0); atomic_dec(&dev->execute_tasks); spin_unlock_irqrestore(&dev->execute_task_lock, flags); } @@ -1867,7 +1873,7 @@ static int transport_check_alloc_task_attr(struct se_cmd *cmd) if (SE_DEV(cmd)->dev_task_attr_type != SAM_TASK_ATTR_EMULATED) return 0; - if (cmd->sam_task_attr == TASK_ATTR_ACA) { + if (cmd->sam_task_attr == MSG_ACA_TAG) { DEBUG_STA("SAM Task Attribute ACA" " emulation is not supported\n"); return -1; @@ -2058,6 +2064,13 @@ int transport_generic_handle_tmr( } EXPORT_SYMBOL(transport_generic_handle_tmr); +void transport_generic_free_cmd_intr( + struct se_cmd *cmd) +{ + transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR); +} +EXPORT_SYMBOL(transport_generic_free_cmd_intr); + static int transport_stop_tasks_for_cmd(struct se_cmd *cmd) { struct se_task *task, *task_tmp; @@ -2504,7 +2517,7 @@ static inline int transport_execute_task_attr(struct se_cmd *cmd) * Check for the existence of HEAD_OF_QUEUE, and if true return 1 * to allow the passed struct se_cmd list of tasks to the front of the list. */ - if (cmd->sam_task_attr == TASK_ATTR_HOQ) { + if (cmd->sam_task_attr == MSG_HEAD_TAG) { atomic_inc(&SE_DEV(cmd)->dev_hoq_count); smp_mb__after_atomic_inc(); DEBUG_STA("Added HEAD_OF_QUEUE for CDB:" @@ -2512,7 +2525,7 @@ static inline int transport_execute_task_attr(struct se_cmd *cmd) T_TASK(cmd)->t_task_cdb[0], cmd->se_ordered_id); return 1; - } else if (cmd->sam_task_attr == TASK_ATTR_ORDERED) { + } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { spin_lock(&SE_DEV(cmd)->ordered_cmd_lock); list_add_tail(&cmd->se_ordered_list, &SE_DEV(cmd)->ordered_cmd_list); @@ -3411,7 +3424,7 @@ static int transport_generic_cmd_sequencer( * See spc4r17 section 5.3 */ if (SE_DEV(cmd)->dev_task_attr_type == SAM_TASK_ATTR_EMULATED) - cmd->sam_task_attr = TASK_ATTR_HOQ; + cmd->sam_task_attr = MSG_HEAD_TAG; cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB; break; case READ_BUFFER: @@ -3619,7 +3632,7 @@ static int transport_generic_cmd_sequencer( * See spc4r17 section 5.3 */ if (SE_DEV(cmd)->dev_task_attr_type == SAM_TASK_ATTR_EMULATED) - cmd->sam_task_attr = TASK_ATTR_HOQ; + cmd->sam_task_attr = MSG_HEAD_TAG; cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB; break; default: @@ -3777,21 +3790,21 @@ static void transport_complete_task_attr(struct se_cmd *cmd) struct se_cmd *cmd_p, *cmd_tmp; int new_active_tasks = 0; - if (cmd->sam_task_attr == TASK_ATTR_SIMPLE) { + if (cmd->sam_task_attr == MSG_SIMPLE_TAG) { atomic_dec(&dev->simple_cmds); smp_mb__after_atomic_dec(); dev->dev_cur_ordered_id++; DEBUG_STA("Incremented dev->dev_cur_ordered_id: %u for" " SIMPLE: %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id); - } else if (cmd->sam_task_attr == TASK_ATTR_HOQ) { + } else if (cmd->sam_task_attr == MSG_HEAD_TAG) { atomic_dec(&dev->dev_hoq_count); smp_mb__after_atomic_dec(); dev->dev_cur_ordered_id++; DEBUG_STA("Incremented dev_cur_ordered_id: %u for" " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id); - } else if (cmd->sam_task_attr == TASK_ATTR_ORDERED) { + } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { spin_lock(&dev->ordered_cmd_lock); list_del(&cmd->se_ordered_list); atomic_dec(&dev->dev_ordered_sync); @@ -3824,7 +3837,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) new_active_tasks++; spin_lock(&dev->delayed_cmd_lock); - if (cmd_p->sam_task_attr == TASK_ATTR_ORDERED) + if (cmd_p->sam_task_attr == MSG_ORDERED_TAG) break; } spin_unlock(&dev->delayed_cmd_lock); @@ -4776,18 +4789,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) sg_end_cur->page_link &= ~0x02; sg_chain(sg_head, task_sg_num, sg_head_cur); - sg_count += (task->task_sg_num + 1); - } else sg_count += task->task_sg_num; + task_sg_num = (task->task_sg_num + 1); + } else { + sg_chain(sg_head, task_sg_num, sg_head_cur); + sg_count += task->task_sg_num; + task_sg_num = task->task_sg_num; + } sg_head = sg_head_cur; sg_link = sg_link_cur; - task_sg_num = task->task_sg_num; continue; } sg_head = sg_first = &task->task_sg[0]; sg_link = &task->task_sg[task->task_sg_num]; - task_sg_num = task->task_sg_num; /* * Check for single task.. */ @@ -4798,9 +4813,12 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) */ sg_end = &task->task_sg[task->task_sg_num - 1]; sg_end->page_link &= ~0x02; - sg_count += (task->task_sg_num + 1); - } else sg_count += task->task_sg_num; + task_sg_num = (task->task_sg_num + 1); + } else { + sg_count += task->task_sg_num; + task_sg_num = task->task_sg_num; + } } /* * Setup the starting pointer and total t_tasks_sg_linked_no including @@ -4809,21 +4827,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) T_TASK(cmd)->t_tasks_sg_chained = sg_first; T_TASK(cmd)->t_tasks_sg_chained_no = sg_count; - DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and" - " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained, + DEBUG_CMD_M("Setup cmd: %p T_TASK(cmd)->t_tasks_sg_chained: %p and" + " t_tasks_sg_chained_no: %u\n", cmd, T_TASK(cmd)->t_tasks_sg_chained, T_TASK(cmd)->t_tasks_sg_chained_no); for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg, T_TASK(cmd)->t_tasks_sg_chained_no, i) { - DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n", - sg, sg_page(sg), sg->length, sg->offset); + DEBUG_CMD_M("SG[%d]: %p page: %p length: %d offset: %d, magic: 0x%08x\n", + i, sg, sg_page(sg), sg->length, sg->offset, sg->sg_magic); if (sg_is_chain(sg)) DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg); if (sg_is_last(sg)) DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg); } - } EXPORT_SYMBOL(transport_do_task_sg_chain); @@ -5297,6 +5314,8 @@ void transport_generic_free_cmd( if (wait_for_tasks && cmd->transport_wait_for_tasks) cmd->transport_wait_for_tasks(cmd, 0, 0); + transport_free_dev_tasks(cmd); + transport_generic_remove(cmd, release_to_pool, session_reinstatement); } @@ -6132,6 +6151,9 @@ get_cmd: case TRANSPORT_REMOVE: transport_generic_remove(cmd, 1, 0); break; + case TRANSPORT_FREE_CMD_INTR: + transport_generic_free_cmd(cmd, 0, 1, 0); + break; case TRANSPORT_PROCESS_TMR: transport_generic_do_tmr(cmd); break; diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 49e51778f733..c056a1132ae1 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -35,6 +35,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_tcq.h> #include <scsi/libfc.h> #include <scsi/fc_encode.h> @@ -592,8 +593,25 @@ static void ft_send_cmd(struct ft_cmd *cmd) case FCP_CFL_WRDATA | FCP_CFL_RDDATA: goto err; /* TBD not supported by tcm_fc yet */ } + /* + * Locate the SAM Task Attr from fc_pri_ta + */ + switch (fcp->fc_pri_ta & FCP_PTA_MASK) { + case FCP_PTA_HEADQ: + task_attr = MSG_HEAD_TAG; + break; + case FCP_PTA_ORDERED: + task_attr = MSG_ORDERED_TAG; + break; + case FCP_PTA_ACA: + task_attr = MSG_ACA_TAG; + break; + case FCP_PTA_SIMPLE: /* Fallthrough */ + default: + task_attr = MSG_SIMPLE_TAG; + } + - /* FCP_PTA_ maps 1:1 to TASK_ATTR_ */ task_attr = fcp->fc_pri_ta & FCP_PTA_MASK; data_len = ntohl(fcp->fc_dl); cmd->cdb = fcp->fc_cdb; diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index fcdbbffe88cc..84e868c255dd 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -519,13 +519,6 @@ static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) return tpg->index; } -static u64 ft_pack_lun(unsigned int index) -{ - WARN_ON(index >= 256); - /* Caller wants this byte-swapped */ - return cpu_to_le64((index & 0xff) << 8); -} - static struct target_core_fabric_ops ft_fabric_ops = { .get_fabric_name = ft_get_fabric_name, .get_fabric_proto_ident = fc_get_fabric_proto_ident, @@ -564,7 +557,6 @@ static struct target_core_fabric_ops ft_fabric_ops = { .get_fabric_sense_len = ft_get_fabric_sense_len, .set_fabric_sense_len = ft_set_fabric_sense_len, .is_state_remove = ft_is_state_remove, - .pack_lun = ft_pack_lun, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index bfa05e801823..c0e8f2eeb886 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -4096,8 +4096,7 @@ static int __init cy_init(void) if (!cy_serial_driver) goto err; - printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n", - __DATE__, __TIME__); + printk(KERN_INFO "Cyclades driver " CY_VERSION "\n"); /* Initialize the tty_driver structure */ diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index b1aecc7bb32a..fd347ff34d07 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -61,8 +61,7 @@ #include <linux/delay.h> -#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \ - __DATE__ " " __TIME__ ")" +#define VERSION_STRING DRIVER_DESC " 2.1d" /* Macros definitions */ diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 652bdac8ce8e..6d5d6e679fc7 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1420,7 +1420,7 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, port->flags = UPF_BOOT_AUTOCONF; port->ops = &atmel_pops; port->fifosize = 1; - port->line = pdev->id; + port->line = data->num; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index bea5c215460c..84db7321cce8 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -907,9 +907,10 @@ static int m32r_sio_request_port(struct uart_port *port) return ret; } -static void m32r_sio_config_port(struct uart_port *port, int flags) +static void m32r_sio_config_port(struct uart_port *port, int unused) { struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 3f2e07011a48..cfb5aa72b196 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -100,6 +100,7 @@ struct twl6030_usb { u8 linkstat; u8 asleep; bool irq_enabled; + unsigned long features; }; #define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg) @@ -204,6 +205,12 @@ static int twl6030_start_srp(struct otg_transceiver *x) static int twl6030_usb_ldo_init(struct twl6030_usb *twl) { + char *regulator_name; + + if (twl->features & TWL6025_SUBCLASS) + regulator_name = "ldousb"; + else + regulator_name = "vusb"; /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); @@ -214,7 +221,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl) /* Program MISC2 register and set bit VUSB_IN_VBAT */ twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); - twl->usb3v3 = regulator_get(twl->dev, "vusb"); + twl->usb3v3 = regulator_get(twl->dev, regulator_name); if (IS_ERR(twl->usb3v3)) return -ENODEV; @@ -409,6 +416,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->dev = &pdev->dev; twl->irq1 = platform_get_irq(pdev, 0); twl->irq2 = platform_get_irq(pdev, 1); + twl->features = pdata->features; twl->otg.dev = twl->dev; twl->otg.label = "twl6030"; twl->otg.set_host = twl6030_set_host; diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index ea39336addfb..f70bd63b0187 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -16,6 +16,7 @@ #include <linux/fb.h> #include <linux/delay.h> +#include <linux/uaccess.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 7c608c5ccf84..00d615d7aa21 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -42,7 +42,7 @@ config W1_MASTER_MXC config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" - depends on W1 && ARM && HAVE_CLK + depends on W1 help Say Y here to enable the DS1WM 1-wire driver, such as that in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 0855d6cce3c1..ad57593d224a 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -33,6 +33,7 @@ #define DS1WM_INT 0x02 /* R/W interrupt status */ #define DS1WM_INT_EN 0x03 /* R/W interrupt enable */ #define DS1WM_CLKDIV 0x04 /* R/W 5 bits of divisor and pre-scale */ +#define DS1WM_CNTRL 0x05 /* R/W master control register (not used yet) */ #define DS1WM_CMD_1W_RESET (1 << 0) /* force reset on 1-wire bus */ #define DS1WM_CMD_SRA (1 << 1) /* enable Search ROM accelerator mode */ @@ -56,6 +57,7 @@ #define DS1WM_INTEN_ERSRF (1 << 5) /* enable rx shift register full int */ #define DS1WM_INTEN_DQO (1 << 6) /* enable direct bus driving ops */ +#define DS1WM_INTEN_NOT_IAS (~DS1WM_INTEN_IAS) /* all but INTR active state */ #define DS1WM_TIMEOUT (HZ * 5) @@ -63,41 +65,50 @@ static struct { unsigned long freq; unsigned long divisor; } freq[] = { - { 4000000, 0x8 }, - { 5000000, 0x2 }, - { 6000000, 0x5 }, - { 7000000, 0x3 }, - { 8000000, 0xc }, - { 10000000, 0x6 }, - { 12000000, 0x9 }, - { 14000000, 0x7 }, - { 16000000, 0x10 }, - { 20000000, 0xa }, - { 24000000, 0xd }, - { 28000000, 0xb }, - { 32000000, 0x14 }, - { 40000000, 0xe }, - { 48000000, 0x11 }, - { 56000000, 0xf }, - { 64000000, 0x18 }, - { 80000000, 0x12 }, - { 96000000, 0x15 }, - { 112000000, 0x13 }, - { 128000000, 0x1c }, + { 1000000, 0x80 }, + { 2000000, 0x84 }, + { 3000000, 0x81 }, + { 4000000, 0x88 }, + { 5000000, 0x82 }, + { 6000000, 0x85 }, + { 7000000, 0x83 }, + { 8000000, 0x8c }, + { 10000000, 0x86 }, + { 12000000, 0x89 }, + { 14000000, 0x87 }, + { 16000000, 0x90 }, + { 20000000, 0x8a }, + { 24000000, 0x8d }, + { 28000000, 0x8b }, + { 32000000, 0x94 }, + { 40000000, 0x8e }, + { 48000000, 0x91 }, + { 56000000, 0x8f }, + { 64000000, 0x98 }, + { 80000000, 0x92 }, + { 96000000, 0x95 }, + { 112000000, 0x93 }, + { 128000000, 0x9c }, +/* you can continue this table, consult the OPERATION - CLOCK DIVISOR + section of the ds1wm spec sheet. */ }; struct ds1wm_data { - void __iomem *map; - int bus_shift; /* # of shifts to calc register offsets */ + void __iomem *map; + int bus_shift; /* # of shifts to calc register offsets */ struct platform_device *pdev; - const struct mfd_cell *cell; - int irq; - int active_high; - int slave_present; - void *reset_complete; - void *read_complete; - void *write_complete; - u8 read_byte; /* last byte received */ + const struct mfd_cell *cell; + int irq; + int slave_present; + void *reset_complete; + void *read_complete; + void *write_complete; + int read_error; + /* last byte received */ + u8 read_byte; + /* byte to write that makes all intr disabled, */ + /* considering active_state (IAS) (optimization) */ + u8 int_en_reg_none; }; static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg, @@ -115,23 +126,39 @@ static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg) static irqreturn_t ds1wm_isr(int isr, void *data) { struct ds1wm_data *ds1wm_data = data; - u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT); + u8 intr; + u8 inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN); + /* if no bits are set in int enable register (except the IAS) + than go no further, reading the regs below has side effects */ + if (!(inten & DS1WM_INTEN_NOT_IAS)) + return IRQ_NONE; - ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1; + ds1wm_write_register(ds1wm_data, + DS1WM_INT_EN, ds1wm_data->int_en_reg_none); - if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) - complete(ds1wm_data->reset_complete); + /* this read action clears the INTR and certain flags in ds1wm */ + intr = ds1wm_read_register(ds1wm_data, DS1WM_INT); - if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) - complete(ds1wm_data->write_complete); + ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1; + if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) { + inten &= ~DS1WM_INTEN_ETMT; + complete(ds1wm_data->write_complete); + } if (intr & DS1WM_INT_RBF) { + /* this read clears the RBF flag */ ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data, - DS1WM_DATA); + DS1WM_DATA); + inten &= ~DS1WM_INTEN_ERBF; if (ds1wm_data->read_complete) complete(ds1wm_data->read_complete); } + if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) { + inten &= ~DS1WM_INTEN_EPD; + complete(ds1wm_data->reset_complete); + } + ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, inten); return IRQ_HANDLED; } @@ -142,33 +169,19 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data) ds1wm_data->reset_complete = &reset_done; + /* enable Presence detect only */ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD | - (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0)); + ds1wm_data->int_en_reg_none); ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET); timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT); ds1wm_data->reset_complete = NULL; if (!timeleft) { - dev_err(&ds1wm_data->pdev->dev, "reset failed\n"); + dev_err(&ds1wm_data->pdev->dev, "reset failed, timed out\n"); return 1; } - /* Wait for the end of the reset. According to the specs, the time - * from when the interrupt is asserted to the end of the reset is: - * tRSTH - tPDH - tPDL - tPDI - * 625 us - 60 us - 240 us - 100 ns = 324.9 us - * - * We'll wait a bit longer just to be sure. - * Was udelay(500), but if it is going to busywait the cpu that long, - * might as well come back later. - */ - msleep(1); - - ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, - DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD | - (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0)); - if (!ds1wm_data->slave_present) { dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n"); return 1; @@ -179,26 +192,47 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data) static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data) { + unsigned long timeleft; DECLARE_COMPLETION_ONSTACK(write_done); ds1wm_data->write_complete = &write_done; + ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, + ds1wm_data->int_en_reg_none | DS1WM_INTEN_ETMT); + ds1wm_write_register(ds1wm_data, DS1WM_DATA, data); - wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT); + timeleft = wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT); + ds1wm_data->write_complete = NULL; + if (!timeleft) { + dev_err(&ds1wm_data->pdev->dev, "write failed, timed out\n"); + return -ETIMEDOUT; + } return 0; } -static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data) +static u8 ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data) { + unsigned long timeleft; + u8 intEnable = DS1WM_INTEN_ERBF | ds1wm_data->int_en_reg_none; DECLARE_COMPLETION_ONSTACK(read_done); + + ds1wm_read_register(ds1wm_data, DS1WM_DATA); + ds1wm_data->read_complete = &read_done; + ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, intEnable); - ds1wm_write(ds1wm_data, write_data); - wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT); - ds1wm_data->read_complete = NULL; + ds1wm_write_register(ds1wm_data, DS1WM_DATA, write_data); + timeleft = wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT); + ds1wm_data->read_complete = NULL; + if (!timeleft) { + dev_err(&ds1wm_data->pdev->dev, "read failed, timed out\n"); + ds1wm_data->read_error = -ETIMEDOUT; + return 0xFF; + } + ds1wm_data->read_error = 0; return ds1wm_data->read_byte; } @@ -206,8 +240,8 @@ static int ds1wm_find_divisor(int gclk) { int i; - for (i = 0; i < ARRAY_SIZE(freq); i++) - if (gclk <= freq[i].freq) + for (i = ARRAY_SIZE(freq)-1; i >= 0; --i) + if (gclk >= freq[i].freq) return freq[i].divisor; return 0; @@ -222,6 +256,8 @@ static void ds1wm_up(struct ds1wm_data *ds1wm_data) ds1wm_data->cell->enable(ds1wm_data->pdev); divisor = ds1wm_find_divisor(plat->clock_rate); + dev_dbg(&ds1wm_data->pdev->dev, + "found divisor 0x%x for clock %d\n", divisor, plat->clock_rate); if (divisor == 0) { dev_err(&ds1wm_data->pdev->dev, "no suitable divisor for %dHz clock\n", @@ -242,7 +278,7 @@ static void ds1wm_down(struct ds1wm_data *ds1wm_data) /* Disable interrupts. */ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, - ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0); + ds1wm_data->int_en_reg_none); if (ds1wm_data->cell->disable) ds1wm_data->cell->disable(ds1wm_data->pdev); @@ -279,41 +315,121 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, { struct ds1wm_data *ds1wm_data = data; int i; - unsigned long long rom_id; - - /* XXX We need to iterate for multiple devices per the DS1WM docs. - * See http://www.maxim-ic.com/appnotes.cfm/appnote_number/120. */ - if (ds1wm_reset(ds1wm_data)) - return; - - ds1wm_write(ds1wm_data, search_type); - ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); - - for (rom_id = 0, i = 0; i < 16; i++) { - - unsigned char resp, r, d; - - resp = ds1wm_read(ds1wm_data, 0x00); - - r = ((resp & 0x02) >> 1) | - ((resp & 0x08) >> 2) | - ((resp & 0x20) >> 3) | - ((resp & 0x80) >> 4); - - d = ((resp & 0x01) >> 0) | - ((resp & 0x04) >> 1) | - ((resp & 0x10) >> 2) | - ((resp & 0x40) >> 3); - - rom_id |= (unsigned long long) r << (i * 4); - - } - dev_dbg(&ds1wm_data->pdev->dev, "found 0x%08llX\n", rom_id); - - ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); - ds1wm_reset(ds1wm_data); - - slave_found(master_dev, rom_id); + int ms_discrep_bit = -1; + u64 r = 0; /* holds the progress of the search */ + u64 r_prime, d; + unsigned slaves_found = 0; + unsigned int pass = 0; + + dev_dbg(&ds1wm_data->pdev->dev, "search begin\n"); + while (true) { + ++pass; + if (pass > 100) { + dev_dbg(&ds1wm_data->pdev->dev, + "too many attempts (100), search aborted\n"); + return; + } + + if (ds1wm_reset(ds1wm_data)) { + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d reset error (or no slaves)\n", pass); + break; + } + + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d r : %0#18llx writing SEARCH_ROM\n", pass, r); + ds1wm_write(ds1wm_data, search_type); + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d entering ASM\n", pass); + ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d begining nibble loop\n", pass); + + r_prime = 0; + d = 0; + /* we work one nibble at a time */ + /* each nibble is interleaved to form a byte */ + for (i = 0; i < 16; i++) { + + unsigned char resp, _r, _r_prime, _d; + + _r = (r >> (4*i)) & 0xf; + _r = ((_r & 0x1) << 1) | + ((_r & 0x2) << 2) | + ((_r & 0x4) << 3) | + ((_r & 0x8) << 4); + + /* writes _r, then reads back: */ + resp = ds1wm_read(ds1wm_data, _r); + + if (ds1wm_data->read_error) { + dev_err(&ds1wm_data->pdev->dev, + "pass: %d nibble: %d read error\n", pass, i); + break; + } + + _r_prime = ((resp & 0x02) >> 1) | + ((resp & 0x08) >> 2) | + ((resp & 0x20) >> 3) | + ((resp & 0x80) >> 4); + + _d = ((resp & 0x01) >> 0) | + ((resp & 0x04) >> 1) | + ((resp & 0x10) >> 2) | + ((resp & 0x40) >> 3); + + r_prime |= (unsigned long long) _r_prime << (i * 4); + d |= (unsigned long long) _d << (i * 4); + + } + if (ds1wm_data->read_error) { + dev_err(&ds1wm_data->pdev->dev, + "pass: %d read error, retrying\n", pass); + break; + } + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d r\': %0#18llx d:%0#18llx\n", + pass, r_prime, d); + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d nibble loop complete, exiting ASM\n", pass); + ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d resetting bus\n", pass); + ds1wm_reset(ds1wm_data); + if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) { + dev_err(&ds1wm_data->pdev->dev, + "pass: %d bus error, retrying\n", pass); + continue; /* start over */ + } + + + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d found %0#18llx\n", pass, r_prime); + slave_found(master_dev, r_prime); + ++slaves_found; + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d complete, preparing next pass\n", pass); + + /* any discrepency found which we already choose the + '1' branch is now is now irrelevant we reveal the + next branch with this: */ + d &= ~r; + /* find last bit set, i.e. the most signif. bit set */ + ms_discrep_bit = fls64(d) - 1; + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d new d:%0#18llx MS discrep bit:%d\n", + pass, d, ms_discrep_bit); + + /* prev_ms_discrep_bit = ms_discrep_bit; + prepare for next ROM search: */ + if (ms_discrep_bit == -1) + break; + + r = (r & ~(~0ull << (ms_discrep_bit))) | 1 << ms_discrep_bit; + } /* end while true */ + dev_dbg(&ds1wm_data->pdev->dev, + "pass: %d total: %d search done ms d bit pos: %d\n", pass, + slaves_found, ms_discrep_bit); } /* --------------------------------------------------------------------- */ @@ -373,15 +489,15 @@ static int ds1wm_probe(struct platform_device *pdev) goto err1; } ds1wm_data->irq = res->start; - ds1wm_data->active_high = plat->active_high; + ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); if (res->flags & IORESOURCE_IRQ_HIGHEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); if (res->flags & IORESOURCE_IRQ_LOWEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); - ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED, - "ds1wm", ds1wm_data); + ret = request_irq(ds1wm_data->irq, ds1wm_isr, + IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data); if (ret) goto err1; @@ -468,5 +584,6 @@ module_exit(ds1wm_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " - "Matt Reimer <mreimer@vpop.net>"); + "Matt Reimer <mreimer@vpop.net>," + "Jean-Francois Dagenais <dagenaisj@sonatest.com>"); MODULE_DESCRIPTION("DS1WM w1 busmaster driver"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index f0c909625bd1..d0cb01b42012 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -16,6 +16,13 @@ config W1_SLAVE_SMEM Say Y here if you want to connect 1-wire simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. +config W1_SLAVE_DS2408 + tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" + help + Say Y here if you want to use a 1-wire + + DS2408 8-Channel Addressable Switch device support + config W1_SLAVE_DS2423 tristate "Counter 1-wire device (DS2423)" select CRC16 @@ -61,6 +68,19 @@ config W1_SLAVE_DS2760 If you are unsure, say N. +config W1_SLAVE_DS2780 + tristate "Dallas 2780 battery monitor chip" + depends on W1 + help + If you enable this you will have the DS2780 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + config W1_SLAVE_BQ27000 tristate "BQ27000 slave support" depends on W1 diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 3c76350a24f7..1f31e9fb0b25 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,8 +4,10 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o +obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o +obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c new file mode 100644 index 000000000000..c37781899d90 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2408.c @@ -0,0 +1,402 @@ +/* + * w1_ds2408.c - w1 family 29 (DS2408) driver + * + * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>"); +MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); + + +#define W1_F29_RETRIES 3 + +#define W1_F29_REG_LOGIG_STATE 0x88 /* R */ +#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */ +#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */ +#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */ +#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */ +#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */ + +#define W1_F29_FUNC_READ_PIO_REGS 0xF0 +#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5 +#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A +/* also used to write the control/status reg (0x8D): */ +#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC +#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3 + +#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA + +static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) +{ + u8 wrbuf[3]; + dev_dbg(&sl->dev, + "Reading with slave: %p, reg addr: %0#4x, buff addr: %p", + sl, (unsigned int)address, buf); + + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->mutex); + return -EIO; + } + + wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS; + wrbuf[1] = address; + wrbuf[2] = 0; + w1_write_block(sl->master, wrbuf, 3); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + return 1; +} + +static ssize_t w1_f29_read_state( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); +} + +static ssize_t w1_f29_read_output( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_OUTPUT_LATCH_STATE, buf); +} + +static ssize_t w1_f29_read_activity( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_ACTIVITY_LATCH_STATE, buf); +} + +static ssize_t w1_f29_read_cond_search_mask( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + dev_dbg(&kobj_to_w1_slave(kobj)->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); +} + +static ssize_t w1_f29_read_cond_search_polarity( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_COND_SEARCH_POL_SELECT, buf); +} + +static ssize_t w1_f29_read_status_control( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + if (count != 1 || off != 0) + return -EFAULT; + return _read_reg(kobj_to_w1_slave(kobj), + W1_F29_REG_CONTROL_AND_STATUS, buf); +} + + + + +static ssize_t w1_f29_write_output( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[3]; + u8 readBack; + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + readBack = w1_read_8(sl->master); + /* here the master could read another byte which + would be the PIO reg (the actual pin logic state) + since in this driver we don't know which pins are + in and outs, there's no value to read the state and + compare. with (*buf) so end this command abruptly: */ + if (w1_reset_resume_command(sl->master)) + goto error; + + if (readBack != 0xAA) { + /* try again, the slave is ready for a command */ + continue; + } + + /* go read back the output latches */ + /* (the direct effect of the write above) */ + w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; + w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE; + w1_buf[2] = 0; + w1_write_block(sl->master, w1_buf, 3); + /* read the result of the READ_PIO_REGS command */ + if (w1_read_8(sl->master) == *buf) { + /* success! */ + mutex_unlock(&sl->master->mutex); + dev_dbg(&sl->dev, + "mutex unlocked, retries:%d", retries); + return 1; + } + } +error: + mutex_unlock(&sl->master->mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + + return -EIO; +} + + +/** + * Writing to the activity file resets the activity latches. + */ +static ssize_t w1_f29_write_activity( + struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + mutex_lock(&sl->master->mutex); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES); + if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->mutex); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->mutex); + return -EIO; +} + +static ssize_t w1_f29_write_status_control( + struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, + loff_t off, + size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[4]; + unsigned int retries = W1_F29_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + mutex_lock(&sl->master->mutex); + + if (w1_reset_select_slave(sl)) + goto error; + + while (retries--) { + w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG; + w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; + w1_buf[2] = 0; + w1_buf[3] = *buf; + + w1_write_block(sl->master, w1_buf, 4); + if (w1_reset_resume_command(sl->master)) + goto error; + + w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; + w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; + w1_buf[2] = 0; + + w1_write_block(sl->master, w1_buf, 3); + if (w1_read_8(sl->master) == *buf) { + /* success! */ + mutex_unlock(&sl->master->mutex); + return 1; + } + } +error: + mutex_unlock(&sl->master->mutex); + + return -EIO; +} + + + +#define NB_SYSFS_BIN_FILES 6 +static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { + { + .attr = { + .name = "state", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f29_read_state, + }, + { + .attr = { + .name = "output", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = 1, + .read = w1_f29_read_output, + .write = w1_f29_write_output, + }, + { + .attr = { + .name = "activity", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f29_read_activity, + .write = w1_f29_write_activity, + }, + { + .attr = { + .name = "cond_search_mask", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f29_read_cond_search_mask, + .write = 0, + }, + { + .attr = { + .name = "cond_search_polarity", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f29_read_cond_search_polarity, + .write = 0, + }, + { + .attr = { + .name = "status_control", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = 1, + .read = w1_f29_read_status_control, + .write = w1_f29_write_status_control, + } +}; + +static int w1_f29_add_slave(struct w1_slave *sl) +{ + int err = 0; + int i; + + for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) + err = sysfs_create_bin_file( + &sl->dev.kobj, + &(w1_f29_sysfs_bin_files[i])); + if (err) + while (--i >= 0) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f29_sysfs_bin_files[i])); + return err; +} + +static void w1_f29_remove_slave(struct w1_slave *sl) +{ + int i; + for (i = NB_SYSFS_BIN_FILES; i <= 0; --i) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f29_sysfs_bin_files[i])); +} + +static struct w1_family_ops w1_f29_fops = { + .add_slave = w1_f29_add_slave, + .remove_slave = w1_f29_remove_slave, +}; + +static struct w1_family w1_family_29 = { + .fid = W1_FAMILY_DS2408, + .fops = &w1_f29_fops, +}; + +static int __init w1_f29_init(void) +{ + return w1_register_family(&w1_family_29); +} + +static void __exit w1_f29_exit(void) +{ + w1_unregister_family(&w1_family_29); +} + +module_init(w1_f29_init); +module_exit(w1_f29_exit); diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c new file mode 100644 index 000000000000..274c8f38303f --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.c @@ -0,0 +1,217 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2780.h" + +int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -ENODEV; + + mutex_lock(&sl->master->mutex); + + if (addr > DS2780_DATA_SIZE || addr < 0) { + count = 0; + goto out; + } + count = min_t(int, count, DS2780_DATA_SIZE - addr); + + if (w1_reset_select_slave(sl) == 0) { + if (io) { + w1_write_8(sl->master, W1_DS2780_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + /* XXX w1_write_block returns void, not n_written */ + } else { + w1_write_8(sl->master, W1_DS2780_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } + } + +out: + mutex_unlock(&sl->master->mutex); + + return count; +} +EXPORT_SYMBOL(w1_ds2780_io); + +int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } + + mutex_unlock(&sl->master->mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2780_eeprom_cmd); + +static ssize_t w1_ds2780_read_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2780_io(dev, buf, off, count, 0); +} + +static struct bin_attribute w1_ds2780_bin_attr = { + .attr = { + .name = "w1_slave", + .mode = S_IRUGO, + }, + .size = DS2780_DATA_SIZE, + .read = w1_ds2780_read_bin, +}; + +static DEFINE_IDR(bat_idr); +static DEFINE_MUTEX(bat_idr_lock); + +static int new_bat_id(void) +{ + int ret; + + while (1) { + int id; + + ret = idr_pre_get(&bat_idr, GFP_KERNEL); + if (ret == 0) + return -ENOMEM; + + mutex_lock(&bat_idr_lock); + ret = idr_get_new(&bat_idr, NULL, &id); + mutex_unlock(&bat_idr_lock); + + if (ret == 0) { + ret = id & MAX_ID_MASK; + break; + } else if (ret == -EAGAIN) { + continue; + } else { + break; + } + } + + return ret; +} + +static void release_bat_id(int id) +{ + mutex_lock(&bat_idr_lock); + idr_remove(&bat_idr, id); + mutex_unlock(&bat_idr_lock); +} + +static int w1_ds2780_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = new_bat_id(); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2780-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr); + if (ret) + goto bin_attr_failed; + + dev_set_drvdata(&sl->dev, pdev); + + return 0; + +bin_attr_failed: +pdev_add_failed: + platform_device_unregister(pdev); +pdev_alloc_failed: + release_bat_id(id); +noid: + return ret; +} + +static void w1_ds2780_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + release_bat_id(id); + sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr); +} + +static struct w1_family_ops w1_ds2780_fops = { + .add_slave = w1_ds2780_add_slave, + .remove_slave = w1_ds2780_remove_slave, +}; + +static struct w1_family w1_ds2780_family = { + .fid = W1_FAMILY_DS2780, + .fops = &w1_ds2780_fops, +}; + +static int __init w1_ds2780_init(void) +{ + idr_init(&bat_idr); + return w1_register_family(&w1_ds2780_family); +} + +static void __exit w1_ds2780_exit(void) +{ + w1_unregister_family(&w1_ds2780_family); + idr_destroy(&bat_idr); +} + +module_init(w1_ds2780_init); +module_exit(w1_ds2780_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC"); diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h new file mode 100644 index 000000000000..a1fba79eb1b5 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.h @@ -0,0 +1,129 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * 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. + * + */ + +#ifndef _W1_DS2780_H +#define _W1_DS2780_H + +/* Function commands */ +#define W1_DS2780_READ_DATA 0x69 +#define W1_DS2780_WRITE_DATA 0x6C +#define W1_DS2780_COPY_DATA 0x48 +#define W1_DS2780_RECALL_DATA 0xB8 +#define W1_DS2780_LOCK 0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2780_STATUS_REG 0x01 +#define DS2780_RAAC_MSB_REG 0x02 +#define DS2780_RAAC_LSB_REG 0x03 +#define DS2780_RSAC_MSB_REG 0x04 +#define DS2780_RSAC_LSB_REG 0x05 +#define DS2780_RARC_REG 0x06 +#define DS2780_RSRC_REG 0x07 +#define DS2780_IAVG_MSB_REG 0x08 +#define DS2780_IAVG_LSB_REG 0x09 +#define DS2780_TEMP_MSB_REG 0x0A +#define DS2780_TEMP_LSB_REG 0x0B +#define DS2780_VOLT_MSB_REG 0x0C +#define DS2780_VOLT_LSB_REG 0x0D +#define DS2780_CURRENT_MSB_REG 0x0E +#define DS2780_CURRENT_LSB_REG 0x0F +#define DS2780_ACR_MSB_REG 0x10 +#define DS2780_ACR_LSB_REG 0x11 +#define DS2780_ACRL_MSB_REG 0x12 +#define DS2780_ACRL_LSB_REG 0x13 +#define DS2780_AS_REG 0x14 +#define DS2780_SFR_REG 0x15 +#define DS2780_FULL_MSB_REG 0x16 +#define DS2780_FULL_LSB_REG 0x17 +#define DS2780_AE_MSB_REG 0x18 +#define DS2780_AE_LSB_REG 0x19 +#define DS2780_SE_MSB_REG 0x1A +#define DS2780_SE_LSB_REG 0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2780_EEPROM_REG 0x1F +#define DS2780_EEPROM_BLOCK0_START 0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2780_EEPROM_BLOCK0_END 0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2780_EEPROM_BLOCK1_START 0x60 +#define DS2780_CONTROL_REG 0x60 +#define DS2780_AB_REG 0x61 +#define DS2780_AC_MSB_REG 0x62 +#define DS2780_AC_LSB_REG 0x63 +#define DS2780_VCHG_REG 0x64 +#define DS2780_IMIN_REG 0x65 +#define DS2780_VAE_REG 0x66 +#define DS2780_IAE_REG 0x67 +#define DS2780_AE_40_REG 0x68 +#define DS2780_RSNSP_REG 0x69 +#define DS2780_FULL_40_MSB_REG 0x6A +#define DS2780_FULL_40_LSB_REG 0x6B +#define DS2780_FULL_3040_SLOPE_REG 0x6C +#define DS2780_FULL_2030_SLOPE_REG 0x6D +#define DS2780_FULL_1020_SLOPE_REG 0x6E +#define DS2780_FULL_0010_SLOPE_REG 0x6F +#define DS2780_AE_3040_SLOPE_REG 0x70 +#define DS2780_AE_2030_SLOPE_REG 0x71 +#define DS2780_AE_1020_SLOPE_REG 0x72 +#define DS2780_AE_0010_SLOPE_REG 0x73 +#define DS2780_SE_3040_SLOPE_REG 0x74 +#define DS2780_SE_2030_SLOPE_REG 0x75 +#define DS2780_SE_1020_SLOPE_REG 0x76 +#define DS2780_SE_0010_SLOPE_REG 0x77 +#define DS2780_RSGAIN_MSB_REG 0x78 +#define DS2780_RSGAIN_LSB_REG 0x79 +#define DS2780_RSTC_REG 0x7A +#define DS2780_FRSGAIN_MSB_REG 0x7B +#define DS2780_FRSGAIN_LSB_REG 0x7C +#define DS2780_EEPROM_BLOCK1_END 0x7C +/* Register 0x7D - 0xFF Reserved */ + +/* Number of valid register addresses */ +#define DS2780_DATA_SIZE 0x80 + +/* Status register bits */ +#define DS2780_STATUS_REG_CHGTF (1 << 7) +#define DS2780_STATUS_REG_AEF (1 << 6) +#define DS2780_STATUS_REG_SEF (1 << 5) +#define DS2780_STATUS_REG_LEARNF (1 << 4) +/* Bit 3 Reserved */ +#define DS2780_STATUS_REG_UVF (1 << 2) +#define DS2780_STATUS_REG_PORF (1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2780_CONTROL_REG_UVEN (1 << 6) +#define DS2780_CONTROL_REG_PMOD (1 << 5) +#define DS2780_CONTROL_REG_RNAOP (1 << 4) +/* Bit 0 - 3 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2780_SFR_REG_PIOSC (1 << 0) + +/* EEPROM register bits */ +#define DS2780_EEPROM_REG_EEC (1 << 7) +#define DS2780_EEPROM_REG_LOCK (1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2780_EEPROM_REG_BL1 (1 << 1) +#define DS2780_EEPROM_REG_BL0 (1 << 0) + +extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, + int io); +extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2780_H */ diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index b7b5014ff714..10606c822756 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -827,7 +827,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) mutex_unlock(&w1_mlock); } -static void w1_slave_found(struct w1_master *dev, u64 rn) +void w1_slave_found(struct w1_master *dev, u64 rn) { struct w1_slave *sl; struct w1_reg_num *tmp; @@ -933,14 +933,15 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb } } -void w1_search_process(struct w1_master *dev, u8 search_type) +void w1_search_process_cb(struct w1_master *dev, u8 search_type, + w1_slave_found_callback cb) { struct w1_slave *sl, *sln; list_for_each_entry(sl, &dev->slist, w1_slave_entry) clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); - w1_search_devices(dev, search_type, w1_slave_found); + w1_search_devices(dev, search_type, cb); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) @@ -953,6 +954,11 @@ void w1_search_process(struct w1_master *dev, u8 search_type) dev->search_count--; } +static void w1_search_process(struct w1_master *dev, u8 search_type) +{ + w1_search_process_cb(dev, search_type, w1_slave_found); +} + int w1_process(void *data) { struct w1_master *dev = (struct w1_master *) data; diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index d8a9709f3449..1ce23fc6186c 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -55,6 +55,7 @@ struct w1_reg_num #define W1_READ_ROM 0x33 #define W1_READ_PSUPPLY 0xB4 #define W1_MATCH_ROM 0x55 +#define W1_RESUME_CMD 0xA5 #define W1_SLAVE_ACTIVE 0 @@ -193,7 +194,9 @@ void w1_destroy_master_attributes(struct w1_master *master); void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); struct w1_slave *w1_search_slave(struct w1_reg_num *id); -void w1_search_process(struct w1_master *dev, u8 search_type); +void w1_slave_found(struct w1_master *dev, u64 rn); +void w1_search_process_cb(struct w1_master *dev, u8 search_type, + w1_slave_found_callback cb); struct w1_master *w1_search_master_id(u32 id); /* Disconnect and reconnect devices in the given family. Used for finding @@ -213,6 +216,7 @@ void w1_write_block(struct w1_master *, const u8 *, int); void w1_touch_block(struct w1_master *, u8 *, int); u8 w1_read_block(struct w1_master *, u8 *, int); int w1_reset_select_slave(struct w1_slave *sl); +int w1_reset_resume_command(struct w1_master *); void w1_next_pullup(struct w1_master *, int); static inline struct w1_slave* dev_to_w1_slave(struct device *dev) diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index f3b636d7cafe..97479ae70b9c 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -34,8 +34,10 @@ #define W1_THERM_DS1822 0x22 #define W1_EEPROM_DS2433 0x23 #define W1_THERM_DS18B20 0x28 +#define W1_FAMILY_DS2408 0x29 #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 +#define W1_FAMILY_DS2780 0x32 #define MAXNAMELEN 32 diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 3ebe9726a9e5..8e8b64cfafb6 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -390,6 +390,32 @@ int w1_reset_select_slave(struct w1_slave *sl) EXPORT_SYMBOL_GPL(w1_reset_select_slave); /** + * When the workflow with a slave amongst many requires several + * successive commands a reset between each, this function is similar + * to doing a reset then a match ROM for the last matched ROM. The + * advantage being that the matched ROM step is skipped in favor of the + * resume command. The slave must support the command of course. + * + * If the bus has only one slave, traditionnaly the match ROM is skipped + * and a "SKIP ROM" is done for efficiency. On multi-slave busses, this + * doesn't work of course, but the resume command is the next best thing. + * + * The w1 master lock must be held. + * + * @param dev the master device + */ +int w1_reset_resume_command(struct w1_master *dev) +{ + if (w1_reset_bus(dev)) + return -1; + + /* This will make only the last matched slave perform a skip ROM. */ + w1_write_8(dev, W1_RESUME_CMD); + return 0; +} +EXPORT_SYMBOL_GPL(w1_reset_resume_command); + +/** * Put out a strong pull-up of the specified duration after the next write * operation. Not all hardware supports strong pullups. Hardware that * doesn't support strong pullups will sleep for the given time after the diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 7e667bc77ef2..55aabd927c60 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -55,6 +55,9 @@ static void w1_send_slave(struct w1_master *dev, u64 rn) struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); int avail; + /* update kernel slave list */ + w1_slave_found(dev, rn); + avail = dev->priv_size - cmd->len; if (avail > 8) { @@ -85,7 +88,7 @@ static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, dev->priv = msg; dev->priv_size = avail; - w1_search_devices(dev, search_type, w1_send_slave); + w1_search_process_cb(dev, search_type, w1_send_slave); msg->ack = 0; cn_netlink_send(msg, 0, GFP_KERNEL); |